From f9bddcb4069e409e6eed0d8a13b0fa569abd1a53 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 6 Feb 2025 19:19:43 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E8=AE=A2=E9=98=85=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=9A=E7=94=A8mediaid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/media.py | 32 ++++++++++---- app/api/endpoints/search.py | 2 +- app/api/endpoints/subscribe.py | 10 ++++- app/chain/search.py | 2 +- app/chain/subscribe.py | 58 +++++++++++++++++++++---- app/db/models/subscribe.py | 29 +++++++++---- app/db/models/subscribehistory.py | 1 + app/schemas/context.py | 8 +++- app/schemas/event.py | 2 +- app/schemas/subscribe.py | 1 + database/versions/ca5461f314f2_2_1_0.py | 32 ++++++++++++++ 11 files changed, 146 insertions(+), 31 deletions(-) create mode 100644 database/versions/ca5461f314f2_2_1_0.py diff --git a/app/api/endpoints/media.py b/app/api/endpoints/media.py index 6903fb0c..4f0dc9f3 100644 --- a/app/api/endpoints/media.py +++ b/app/api/endpoints/media.py @@ -7,9 +7,11 @@ from app import schemas from app.chain.media import MediaChain from app.core.config import settings from app.core.context import Context +from app.core.event import eventmanager from app.core.metainfo import MetaInfo, MetaInfoPath 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() @@ -138,18 +140,32 @@ def media_info(mediaid: str, type_name: str, 根据媒体ID查询themoviedb或豆瓣媒体信息,type_name: 电影/电视剧 """ mtype = MediaType(type_name) - tmdbid, doubanid, bangumiid = None, None, None + mediainfo = None if mediaid.startswith("tmdb:"): - tmdbid = int(mediaid[5:]) + mediainfo = MediaChain().recognize_media(tmdbid=int(mediaid[5:]), mtype=mtype) elif mediaid.startswith("douban:"): - doubanid = mediaid[7:] + mediainfo = MediaChain().recognize_media(doubanid=mediaid[7:], mtype=mtype) elif mediaid.startswith("bangumi:"): - bangumiid = int(mediaid[8:]) - if not tmdbid and not doubanid and not bangumiid: - return schemas.MediaInfo() + mediainfo = MediaChain().recognize_media(bangumiid=int(mediaid[8:]), mtype=mtype) + else: + # 广播事件解析媒体信息 + 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: MediaChain().obtain_images(mediainfo) return mediainfo.to_dict() + return schemas.MediaInfo() diff --git a/app/api/endpoints/search.py b/app/api/endpoints/search.py index 54121247..31950334 100644 --- a/app/api/endpoints/search.py +++ b/app/api/endpoints/search.py @@ -97,7 +97,7 @@ def search_by_id(mediaid: str, if event_data.convert_type == "themoviedb": torrents = SearchChain().search_by_id(tmdbid=search_id, mtype=mtype, area=area, season=season) - else: + elif event_data.convert_type == "douban": torrents = SearchChain().search_by_id(doubanid=search_id, mtype=mtype, area=area, season=season) else: diff --git a/app/api/endpoints/subscribe.py b/app/api/endpoints/subscribe.py index b84365e0..04a351d8 100644 --- a/app/api/endpoints/subscribe.py +++ b/app/api/endpoints/subscribe.py @@ -82,6 +82,7 @@ def create_subscribe( season=subscribe_in.season, doubanid=subscribe_in.doubanid, bangumiid=subscribe_in.bangumiid, + mediaid=subscribe_in.mediaid, username=current_user.name, best_version=subscribe_in.best_version, save_path=subscribe_in.save_path, @@ -171,7 +172,6 @@ def subscribe_mediaid( """ 根据 TMDBID/豆瓣ID/BangumiId 查询订阅 tmdb:/douban: """ - result = None title_check = False if mediaid.startswith("tmdb:"): tmdbid = mediaid[5:] @@ -192,6 +192,10 @@ def subscribe_mediaid( result = Subscribe.get_by_bangumiid(db, int(bangumiid)) if not result and title: title_check = True + else: + result = Subscribe.get_by_mediaid(db, mediaid) + if not result and title: + title_check = True # 使用名称检查订阅 if title_check and title: meta = MetaInfo(title) @@ -311,6 +315,10 @@ def delete_subscribe_by_mediaid( subscribe = Subscribe().get_by_doubanid(db, doubanid) if subscribe: delete_subscribes.append(subscribe) + else: + subscribe = Subscribe().get_by_mediaid(db, mediaid) + if subscribe: + delete_subscribes.append(subscribe) for subscribe in delete_subscribes: Subscribe().delete(db, subscribe.id) # 发送事件 diff --git a/app/chain/search.py b/app/chain/search.py index f7a819ac..56304f93 100644 --- a/app/chain/search.py +++ b/app/chain/search.py @@ -37,7 +37,7 @@ class SearchChain(ChainBase): def search_by_id(self, tmdbid: int = None, doubanid: str = None, mtype: MediaType = None, area: str = "title", season: int = None) -> List[Context]: """ - 根据TMDBID/豆瓣ID搜索资源,精确匹配,但不不过滤本地存在的资源 + 根据TMDBID/豆瓣ID搜索资源,精确匹配,不过滤本地存在的资源 :param tmdbid: TMDB ID :param doubanid: 豆瓣 ID :param mtype: 媒体,电影 or 电视剧 diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 2f7b641c..5df4065b 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -28,7 +28,8 @@ from app.helper.message import MessageHelper from app.helper.subscribe import SubscribeHelper from app.helper.torrent import TorrentHelper 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 @@ -58,6 +59,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton): tmdbid: int = None, doubanid: str = None, bangumiid: int = None, + mediaid: str = None, season: int = None, channel: MessageChannel = 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} ...') + mediainfo = None metainfo = MetaInfo(title) if year: @@ -82,27 +106,41 @@ class SubscribeChain(ChainBase, metaclass=Singleton): # 识别媒体信息 if settings.RECOGNIZE_SOURCE == "themoviedb": # TMDB识别模式 - if not tmdbid and doubanid: - # 将豆瓣信息转换为TMDB信息 - tmdbinfo = self.mediachain.get_tmdbinfo_by_doubanid(doubanid=doubanid, mtype=mtype) - if tmdbinfo: - mediainfo = MediaInfo(tmdb_info=tmdbinfo) + if not tmdbid: + if doubanid: + # 将豆瓣信息转换为TMDB信息 + tmdbinfo = self.mediachain.get_tmdbinfo_by_doubanid(doubanid=doubanid, mtype=mtype) + if tmdbinfo: + mediainfo = MediaInfo(tmdb_info=tmdbinfo) + elif mediaid: + # 未知前缀,广播事件解析媒体信息 + mediainfo = __get_event_meida(mediaid, metainfo) else: - # 识别TMDB信息,不使用缓存 + # 使用TMDBID识别 mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, tmdbid=tmdbid, cache=False) else: - # 豆瓣识别模式,不使用缓存 - mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, doubanid=doubanid, cache=False) + if doubanid: + # 豆瓣识别模式,不使用缓存 + mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, doubanid=doubanid, cache=False) + elif mediaid: + # 未知前缀,广播事件解析媒体信息 + mediainfo = __get_event_meida(mediaid, metainfo) if mediainfo: # 豆瓣标题处理 meta = MetaInfo(mediainfo.title) mediainfo.title = meta.name if not season: season = meta.begin_season + + # 使用名称识别兜底 + if not mediainfo: + mediainfo = self.recognize_media(meta=metainfo, mtype=mtype) + # 识别失败 if not mediainfo: logger.warn(f'未识别到媒体信息,标题:{title},tmdbid:{tmdbid},doubanid:{doubanid}') return None, "未识别到媒体信息" + # 总集数 if mediainfo.type == MediaType.TV: if not season: @@ -137,6 +175,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton): else: # 避免season为0的问题 season = None + # 更新媒体图片 self.obtain_images(mediainfo=mediainfo) # 合并信息 @@ -144,6 +183,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton): mediainfo.douban_id = doubanid if bangumiid: mediainfo.bangumi_id = bangumiid + # 添加订阅 kwargs.update({ 'quality': self.__get_default_subscribe_config(mediainfo.type, "quality") if not kwargs.get( diff --git a/app/db/models/subscribe.py b/app/db/models/subscribe.py index 7632d853..c406ee5f 100644 --- a/app/db/models/subscribe.py +++ b/app/db/models/subscribe.py @@ -24,6 +24,7 @@ class Subscribe(Base): tvdbid = Column(Integer) doubanid = Column(String, index=True) bangumiid = Column(Integer, index=True) + mediaid = Column(String, index=True) # 季号 season = Column(Integer) # 海报 @@ -107,6 +108,14 @@ class Subscribe(Base): result = db.query(Subscribe).filter(Subscribe.state.in_(states)).all() 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 @db_query 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() 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 @db_query def get_by_doubanid(db: Session, doubanid: str): @@ -135,6 +136,11 @@ class Subscribe(Base): def get_by_bangumiid(db: Session, bangumiid: int): 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 def delete_by_tmdbid(self, db: Session, tmdbid: int, season: int): subscrbies = self.get_by_tmdbid(db, tmdbid, season) @@ -149,6 +155,13 @@ class Subscribe(Base): subscribe.delete(db, subscribe.id) 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 @db_query def list_by_username(db: Session, username: str, state: str = None, mtype: str = None): diff --git a/app/db/models/subscribehistory.py b/app/db/models/subscribehistory.py index 7c95e1e2..2abdb4b3 100644 --- a/app/db/models/subscribehistory.py +++ b/app/db/models/subscribehistory.py @@ -22,6 +22,7 @@ class SubscribeHistory(Base): tvdbid = Column(Integer) doubanid = Column(String, index=True) bangumiid = Column(Integer, index=True) + mediaid = Column(String, index=True) # 季号 season = Column(Integer) # 海报 diff --git a/app/schemas/context.py b/app/schemas/context.py index cef29256..7f9c9357 100644 --- a/app/schemas/context.py +++ b/app/schemas/context.py @@ -79,8 +79,6 @@ class MediaInfo(BaseModel): title_year: Optional[str] = None # 当前指定季,如有 season: Optional[int] = None - # 合集等id - collection_id: Optional[int] = None # TMDB ID tmdb_id: Optional[int] = None # IMDB ID @@ -91,6 +89,12 @@ class MediaInfo(BaseModel): douban_id: Optional[str] = None # Bangumi ID 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 # 媒体原发行标题 diff --git a/app/schemas/event.py b/app/schemas/event.py index c7c4932b..1eabe712 100644 --- a/app/schemas/event.py +++ b/app/schemas/event.py @@ -268,7 +268,7 @@ class MediaRecognizeConvertEventData(ChainEventData): convert_type (str): 转换类型 仅支持:themoviedb/douban,需要转换为对应的媒体数据并返回 # 输出参数 - media_dict (dict): TheMovieDb或豆瓣的媒体数据 + media_dict (dict): TheMovieDb/豆瓣的媒体数据 """ # 输入参数 mediaid: str = Field(..., description="媒体ID") diff --git a/app/schemas/subscribe.py b/app/schemas/subscribe.py index 5c717b85..eb67c9a1 100644 --- a/app/schemas/subscribe.py +++ b/app/schemas/subscribe.py @@ -16,6 +16,7 @@ class Subscribe(BaseModel): tmdbid: Optional[int] = None doubanid: Optional[str] = None bangumiid: Optional[int] = None + mediaid: Optional[str] = None # 季号 season: Optional[int] = None # 海报 diff --git a/database/versions/ca5461f314f2_2_1_0.py b/database/versions/ca5461f314f2_2_1_0.py new file mode 100644 index 00000000..8193b33d --- /dev/null +++ b/database/versions/ca5461f314f2_2_1_0.py @@ -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