feat:订阅支持通用mediaid

This commit is contained in:
jxxghp
2025-02-06 19:19:43 +08:00
parent 247b3b24a1
commit f9bddcb406
11 changed files with 146 additions and 31 deletions
+24 -8
View File
@@ -7,9 +7,11 @@ from app import schemas
from app.chain.media import MediaChain from app.chain.media import MediaChain
from app.core.config import settings from app.core.config import settings
from app.core.context import Context from app.core.context import Context
from app.core.event import eventmanager
from app.core.metainfo import MetaInfo, MetaInfoPath from app.core.metainfo import MetaInfo, MetaInfoPath
from app.core.security import verify_token, verify_apitoken from app.core.security import verify_token, verify_apitoken
from app.schemas import MediaType from app.schemas import MediaType, MediaRecognizeConvertEventData
from app.schemas.types import ChainEventType
router = APIRouter() router = APIRouter()
@@ -138,18 +140,32 @@ def media_info(mediaid: str, type_name: str,
根据媒体ID查询themoviedb或豆瓣媒体信息,type_name: 电影/电视剧 根据媒体ID查询themoviedb或豆瓣媒体信息,type_name: 电影/电视剧
""" """
mtype = MediaType(type_name) mtype = MediaType(type_name)
tmdbid, doubanid, bangumiid = None, None, None mediainfo = None
if mediaid.startswith("tmdb:"): if mediaid.startswith("tmdb:"):
tmdbid = int(mediaid[5:]) mediainfo = MediaChain().recognize_media(tmdbid=int(mediaid[5:]), mtype=mtype)
elif mediaid.startswith("douban:"): elif mediaid.startswith("douban:"):
doubanid = mediaid[7:] mediainfo = MediaChain().recognize_media(doubanid=mediaid[7:], mtype=mtype)
elif mediaid.startswith("bangumi:"): elif mediaid.startswith("bangumi:"):
bangumiid = int(mediaid[8:]) mediainfo = MediaChain().recognize_media(bangumiid=int(mediaid[8:]), mtype=mtype)
if not tmdbid and not doubanid and not bangumiid: else:
return schemas.MediaInfo() # 广播事件解析媒体信息
event_data = MediaRecognizeConvertEventData(
mediaid=mediaid,
convert_type=settings.RECOGNIZE_SOURCE
)
event = eventmanager.send_event(ChainEventType.MediaRecognizeConvert, event_data)
# 使用事件返回的上下文数据
if event and event.event_data:
event_data: MediaRecognizeConvertEventData = event.event_data
if event_data.media_dict:
new_id = event_data.media_dict.get("id")
if event_data.convert_type == "themoviedb":
mediainfo = MediaChain().recognize_media(tmdbid=new_id, mtype=mtype)
elif event_data.convert_type == "douban":
mediainfo = MediaChain().recognize_media(doubanid=new_id, mtype=mtype)
# 识别 # 识别
mediainfo = MediaChain().recognize_media(tmdbid=tmdbid, doubanid=doubanid, bangumiid=bangumiid, mtype=mtype)
if mediainfo: if mediainfo:
MediaChain().obtain_images(mediainfo) MediaChain().obtain_images(mediainfo)
return mediainfo.to_dict() return mediainfo.to_dict()
return schemas.MediaInfo() return schemas.MediaInfo()
+1 -1
View File
@@ -97,7 +97,7 @@ def search_by_id(mediaid: str,
if event_data.convert_type == "themoviedb": if event_data.convert_type == "themoviedb":
torrents = SearchChain().search_by_id(tmdbid=search_id, torrents = SearchChain().search_by_id(tmdbid=search_id,
mtype=mtype, area=area, season=season) mtype=mtype, area=area, season=season)
else: elif event_data.convert_type == "douban":
torrents = SearchChain().search_by_id(doubanid=search_id, torrents = SearchChain().search_by_id(doubanid=search_id,
mtype=mtype, area=area, season=season) mtype=mtype, area=area, season=season)
else: else:
+9 -1
View File
@@ -82,6 +82,7 @@ def create_subscribe(
season=subscribe_in.season, season=subscribe_in.season,
doubanid=subscribe_in.doubanid, doubanid=subscribe_in.doubanid,
bangumiid=subscribe_in.bangumiid, bangumiid=subscribe_in.bangumiid,
mediaid=subscribe_in.mediaid,
username=current_user.name, username=current_user.name,
best_version=subscribe_in.best_version, best_version=subscribe_in.best_version,
save_path=subscribe_in.save_path, save_path=subscribe_in.save_path,
@@ -171,7 +172,6 @@ def subscribe_mediaid(
""" """
根据 TMDBID/豆瓣ID/BangumiId 查询订阅 tmdb:/douban: 根据 TMDBID/豆瓣ID/BangumiId 查询订阅 tmdb:/douban:
""" """
result = None
title_check = False title_check = False
if mediaid.startswith("tmdb:"): if mediaid.startswith("tmdb:"):
tmdbid = mediaid[5:] tmdbid = mediaid[5:]
@@ -192,6 +192,10 @@ def subscribe_mediaid(
result = Subscribe.get_by_bangumiid(db, int(bangumiid)) result = Subscribe.get_by_bangumiid(db, int(bangumiid))
if not result and title: if not result and title:
title_check = True title_check = True
else:
result = Subscribe.get_by_mediaid(db, mediaid)
if not result and title:
title_check = True
# 使用名称检查订阅 # 使用名称检查订阅
if title_check and title: if title_check and title:
meta = MetaInfo(title) meta = MetaInfo(title)
@@ -311,6 +315,10 @@ def delete_subscribe_by_mediaid(
subscribe = Subscribe().get_by_doubanid(db, doubanid) subscribe = Subscribe().get_by_doubanid(db, doubanid)
if subscribe: if subscribe:
delete_subscribes.append(subscribe) delete_subscribes.append(subscribe)
else:
subscribe = Subscribe().get_by_mediaid(db, mediaid)
if subscribe:
delete_subscribes.append(subscribe)
for subscribe in delete_subscribes: for subscribe in delete_subscribes:
Subscribe().delete(db, subscribe.id) Subscribe().delete(db, subscribe.id)
# 发送事件 # 发送事件
+1 -1
View File
@@ -37,7 +37,7 @@ class SearchChain(ChainBase):
def search_by_id(self, tmdbid: int = None, doubanid: str = None, def search_by_id(self, tmdbid: int = None, doubanid: str = None,
mtype: MediaType = None, area: str = "title", season: int = None) -> List[Context]: mtype: MediaType = None, area: str = "title", season: int = None) -> List[Context]:
""" """
根据TMDBID/豆瓣ID搜索资源,精确匹配,但不不过滤本地存在的资源 根据TMDBID/豆瓣ID搜索资源,精确匹配,不过滤本地存在的资源
:param tmdbid: TMDB ID :param tmdbid: TMDB ID
:param doubanid: 豆瓣 ID :param doubanid: 豆瓣 ID
:param mtype: 媒体,电影 or 电视剧 :param mtype: 媒体,电影 or 电视剧
+49 -9
View File
@@ -28,7 +28,8 @@ from app.helper.message import MessageHelper
from app.helper.subscribe import SubscribeHelper from app.helper.subscribe import SubscribeHelper
from app.helper.torrent import TorrentHelper from app.helper.torrent import TorrentHelper
from app.log import logger from app.log import logger
from app.schemas.types import MediaType, SystemConfigKey, MessageChannel, NotificationType, EventType from app.schemas import MediaRecognizeConvertEventData
from app.schemas.types import MediaType, SystemConfigKey, MessageChannel, NotificationType, EventType, ChainEventType
from app.utils.singleton import Singleton from app.utils.singleton import Singleton
@@ -58,6 +59,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
tmdbid: int = None, tmdbid: int = None,
doubanid: str = None, doubanid: str = None,
bangumiid: int = None, bangumiid: int = None,
mediaid: str = None,
season: int = None, season: int = None,
channel: MessageChannel = None, channel: MessageChannel = None,
source: str = None, source: str = None,
@@ -69,7 +71,29 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
""" """
识别媒体信息并添加订阅 识别媒体信息并添加订阅
""" """
def __get_event_meida(_mediaid: str, _meta: MetaBase) -> Optional[MediaInfo]:
"""
广播事件解析媒体信息
"""
event_data = MediaRecognizeConvertEventData(
mediaid=_mediaid,
convert_type=settings.RECOGNIZE_SOURCE
)
event = eventmanager.send_event(ChainEventType.MediaRecognizeConvert, event_data)
# 使用事件返回的上下文数据
if event and event.event_data:
event_data: MediaRecognizeConvertEventData = event.event_data
if event_data.media_dict:
new_id = event_data.media_dict.get("id")
if event_data.convert_type == "themoviedb":
return self.mediachain.recognize_media(meta=_meta, tmdbid=new_id)
elif event_data.convert_type == "douban":
return self.mediachain.recognize_media(meta=_meta, doubanid=new_id)
return None
logger.info(f'开始添加订阅,标题:{title} ...') logger.info(f'开始添加订阅,标题:{title} ...')
mediainfo = None mediainfo = None
metainfo = MetaInfo(title) metainfo = MetaInfo(title)
if year: if year:
@@ -82,27 +106,41 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
# 识别媒体信息 # 识别媒体信息
if settings.RECOGNIZE_SOURCE == "themoviedb": if settings.RECOGNIZE_SOURCE == "themoviedb":
# TMDB识别模式 # TMDB识别模式
if not tmdbid and doubanid: if not tmdbid:
# 将豆瓣信息转换为TMDB信息 if doubanid:
tmdbinfo = self.mediachain.get_tmdbinfo_by_doubanid(doubanid=doubanid, mtype=mtype) # 将豆瓣信息转换为TMDB信息
if tmdbinfo: tmdbinfo = self.mediachain.get_tmdbinfo_by_doubanid(doubanid=doubanid, mtype=mtype)
mediainfo = MediaInfo(tmdb_info=tmdbinfo) if tmdbinfo:
mediainfo = MediaInfo(tmdb_info=tmdbinfo)
elif mediaid:
# 未知前缀,广播事件解析媒体信息
mediainfo = __get_event_meida(mediaid, metainfo)
else: else:
# 识别TMDB信息,不使用缓存 # 使用TMDBID识别
mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, tmdbid=tmdbid, cache=False) mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, tmdbid=tmdbid, cache=False)
else: else:
# 豆瓣识别模式,不使用缓存 if doubanid:
mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, doubanid=doubanid, cache=False) # 豆瓣识别模式,不使用缓存
mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, doubanid=doubanid, cache=False)
elif mediaid:
# 未知前缀,广播事件解析媒体信息
mediainfo = __get_event_meida(mediaid, metainfo)
if mediainfo: if mediainfo:
# 豆瓣标题处理 # 豆瓣标题处理
meta = MetaInfo(mediainfo.title) meta = MetaInfo(mediainfo.title)
mediainfo.title = meta.name mediainfo.title = meta.name
if not season: if not season:
season = meta.begin_season season = meta.begin_season
# 使用名称识别兜底
if not mediainfo:
mediainfo = self.recognize_media(meta=metainfo, mtype=mtype)
# 识别失败 # 识别失败
if not mediainfo: if not mediainfo:
logger.warn(f'未识别到媒体信息,标题:{title}tmdbid{tmdbid}doubanid{doubanid}') logger.warn(f'未识别到媒体信息,标题:{title}tmdbid{tmdbid}doubanid{doubanid}')
return None, "未识别到媒体信息" return None, "未识别到媒体信息"
# 总集数 # 总集数
if mediainfo.type == MediaType.TV: if mediainfo.type == MediaType.TV:
if not season: if not season:
@@ -137,6 +175,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
else: else:
# 避免season为0的问题 # 避免season为0的问题
season = None season = None
# 更新媒体图片 # 更新媒体图片
self.obtain_images(mediainfo=mediainfo) self.obtain_images(mediainfo=mediainfo)
# 合并信息 # 合并信息
@@ -144,6 +183,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
mediainfo.douban_id = doubanid mediainfo.douban_id = doubanid
if bangumiid: if bangumiid:
mediainfo.bangumi_id = bangumiid mediainfo.bangumi_id = bangumiid
# 添加订阅 # 添加订阅
kwargs.update({ kwargs.update({
'quality': self.__get_default_subscribe_config(mediainfo.type, "quality") if not kwargs.get( 'quality': self.__get_default_subscribe_config(mediainfo.type, "quality") if not kwargs.get(
+21 -8
View File
@@ -24,6 +24,7 @@ class Subscribe(Base):
tvdbid = Column(Integer) tvdbid = Column(Integer)
doubanid = Column(String, index=True) doubanid = Column(String, index=True)
bangumiid = Column(Integer, index=True) bangumiid = Column(Integer, index=True)
mediaid = Column(String, index=True)
# 季号 # 季号
season = Column(Integer) season = Column(Integer)
# 海报 # 海报
@@ -107,6 +108,14 @@ class Subscribe(Base):
result = db.query(Subscribe).filter(Subscribe.state.in_(states)).all() result = db.query(Subscribe).filter(Subscribe.state.in_(states)).all()
return list(result) return list(result)
@staticmethod
@db_query
def get_by_title(db: Session, title: str, season: int = None):
if season:
return db.query(Subscribe).filter(Subscribe.name == title,
Subscribe.season == season).first()
return db.query(Subscribe).filter(Subscribe.name == title).first()
@staticmethod @staticmethod
@db_query @db_query
def get_by_tmdbid(db: Session, tmdbid: int, season: int = None): def get_by_tmdbid(db: Session, tmdbid: int, season: int = None):
@@ -117,14 +126,6 @@ class Subscribe(Base):
result = db.query(Subscribe).filter(Subscribe.tmdbid == tmdbid).all() result = db.query(Subscribe).filter(Subscribe.tmdbid == tmdbid).all()
return list(result) return list(result)
@staticmethod
@db_query
def get_by_title(db: Session, title: str, season: int = None):
if season:
return db.query(Subscribe).filter(Subscribe.name == title,
Subscribe.season == season).first()
return db.query(Subscribe).filter(Subscribe.name == title).first()
@staticmethod @staticmethod
@db_query @db_query
def get_by_doubanid(db: Session, doubanid: str): def get_by_doubanid(db: Session, doubanid: str):
@@ -135,6 +136,11 @@ class Subscribe(Base):
def get_by_bangumiid(db: Session, bangumiid: int): def get_by_bangumiid(db: Session, bangumiid: int):
return db.query(Subscribe).filter(Subscribe.bangumiid == bangumiid).first() return db.query(Subscribe).filter(Subscribe.bangumiid == bangumiid).first()
@staticmethod
@db_query
def get_by_mediaid(db: Session, mediaid: str):
return db.query(Subscribe).filter(Subscribe.mediaid == mediaid).first()
@db_update @db_update
def delete_by_tmdbid(self, db: Session, tmdbid: int, season: int): def delete_by_tmdbid(self, db: Session, tmdbid: int, season: int):
subscrbies = self.get_by_tmdbid(db, tmdbid, season) subscrbies = self.get_by_tmdbid(db, tmdbid, season)
@@ -149,6 +155,13 @@ class Subscribe(Base):
subscribe.delete(db, subscribe.id) subscribe.delete(db, subscribe.id)
return True return True
@db_update
def delete_by_mediaid(self, db: Session, mediaid: str):
subscribe = self.get_by_mediaid(db, mediaid)
if subscribe:
subscribe.delete(db, subscribe.id)
return True
@staticmethod @staticmethod
@db_query @db_query
def list_by_username(db: Session, username: str, state: str = None, mtype: str = None): def list_by_username(db: Session, username: str, state: str = None, mtype: str = None):
+1
View File
@@ -22,6 +22,7 @@ class SubscribeHistory(Base):
tvdbid = Column(Integer) tvdbid = Column(Integer)
doubanid = Column(String, index=True) doubanid = Column(String, index=True)
bangumiid = Column(Integer, index=True) bangumiid = Column(Integer, index=True)
mediaid = Column(String, index=True)
# 季号 # 季号
season = Column(Integer) season = Column(Integer)
# 海报 # 海报
+6 -2
View File
@@ -79,8 +79,6 @@ class MediaInfo(BaseModel):
title_year: Optional[str] = None title_year: Optional[str] = None
# 当前指定季,如有 # 当前指定季,如有
season: Optional[int] = None season: Optional[int] = None
# 合集等id
collection_id: Optional[int] = None
# TMDB ID # TMDB ID
tmdb_id: Optional[int] = None tmdb_id: Optional[int] = None
# IMDB ID # IMDB ID
@@ -91,6 +89,12 @@ class MediaInfo(BaseModel):
douban_id: Optional[str] = None douban_id: Optional[str] = None
# Bangumi ID # Bangumi ID
bangumi_id: Optional[int] = None bangumi_id: Optional[int] = None
# 合集ID
collection_id: Optional[int] = None
# 其它媒体ID前缀
mediaid_prefix: Optional[str] = None
# 其它媒体ID值
media_id: Optional[str] = None
# 媒体原语种 # 媒体原语种
original_language: Optional[str] = None original_language: Optional[str] = None
# 媒体原发行标题 # 媒体原发行标题
+1 -1
View File
@@ -268,7 +268,7 @@ class MediaRecognizeConvertEventData(ChainEventData):
convert_type (str): 转换类型 仅支持:themoviedb/douban,需要转换为对应的媒体数据并返回 convert_type (str): 转换类型 仅支持:themoviedb/douban,需要转换为对应的媒体数据并返回
# 输出参数 # 输出参数
media_dict (dict): TheMovieDb豆瓣的媒体数据 media_dict (dict): TheMovieDb/豆瓣的媒体数据
""" """
# 输入参数 # 输入参数
mediaid: str = Field(..., description="媒体ID") mediaid: str = Field(..., description="媒体ID")
+1
View File
@@ -16,6 +16,7 @@ class Subscribe(BaseModel):
tmdbid: Optional[int] = None tmdbid: Optional[int] = None
doubanid: Optional[str] = None doubanid: Optional[str] = None
bangumiid: Optional[int] = None bangumiid: Optional[int] = None
mediaid: Optional[str] = None
# 季号 # 季号
season: Optional[int] = None season: Optional[int] = None
# 海报 # 海报
+32
View File
@@ -0,0 +1,32 @@
"""2.1.0
Revision ID: ca5461f314f2
Revises: 55390f1f77c1
Create Date: 2025-02-06 18:28:00.644571
"""
import contextlib
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import sqlite
# revision identifiers, used by Alembic.
revision = 'ca5461f314f2'
down_revision = '55390f1f77c1'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# 订阅增加mediaid
with contextlib.suppress(Exception):
op.add_column('subscribe', sa.Column('mediaid', sa.String(), nullable=True))
op.create_index('ix_subscribe_mediaid', 'subscribe', ['mediaid'], unique=False)
op.add_column('subscribehistory', sa.Column('mediaid', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
pass