refactor(media): unify media identity and chain responsibilities

This commit is contained in:
jxxghp
2026-08-12 18:31:09 +08:00
parent cbb969e0ff
commit ca32922a7f
155 changed files with 8371 additions and 4526 deletions

View File

@@ -8,7 +8,13 @@ from app.helper.scraper import MediaScraperHelper
from app.log import logger
from app.modules import _ModuleBase
from app.modules.anilist.anilist import AniListApi
from app.schemas.types import MediaRecognizeType, MediaSource, MediaType, ModuleType
from app.schemas.types import (
MediaRecognizeType,
MediaSource,
MediaSourceSelection,
MediaType,
ModuleType,
)
from app.utils.media import is_media_source_enabled
@@ -327,7 +333,7 @@ class AniListModule(_ModuleBase):
return None
def search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索 AniList 动画媒体信息。
@@ -336,7 +342,7 @@ class AniListModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 统一媒体信息列表
"""
if not is_media_source_enabled(media_source, "anilist"):
if not is_media_source_enabled(media_source, MediaSource.AniList):
return None
if not meta or not meta.name:
return []
@@ -347,7 +353,7 @@ class AniListModule(_ModuleBase):
]
async def async_search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
异步搜索 AniList 动画媒体信息。
@@ -356,7 +362,7 @@ class AniListModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 统一媒体信息列表
"""
if not is_media_source_enabled(media_source, "anilist"):
if not is_media_source_enabled(media_source, MediaSource.AniList):
return None
if not meta or not meta.name:
return []

View File

@@ -8,7 +8,13 @@ from app.helper.scraper import MediaScraperHelper
from app.log import logger
from app.modules import _ModuleBase
from app.modules.bangumi.bangumi import BangumiApi
from app.schemas.types import MediaRecognizeType, MediaSource, MediaType, ModuleType
from app.schemas.types import (
MediaRecognizeType,
MediaSource,
MediaSourceSelection,
MediaType,
ModuleType,
)
from app.utils.http import RequestUtils
from app.utils.media import is_media_source_enabled
@@ -221,7 +227,7 @@ class BangumiModule(_ModuleBase):
return None
def search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息
@@ -229,7 +235,7 @@ class BangumiModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 媒体信息
"""
if not is_media_source_enabled(media_source, "bangumi"):
if not is_media_source_enabled(media_source, MediaSource.Bangumi):
return None
if not meta.name:
return []
@@ -241,7 +247,7 @@ class BangumiModule(_ModuleBase):
return []
async def async_search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息(异步版本)
@@ -249,7 +255,7 @@ class BangumiModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 媒体信息
"""
if not is_media_source_enabled(media_source, "bangumi"):
if not is_media_source_enabled(media_source, MediaSource.Bangumi):
return None
if not meta.name:
return []

View File

@@ -21,6 +21,7 @@ from app.schemas.types import (
MUSIC_ENTITY_ALBUM,
MUSIC_ENTITY_RECORDING,
MediaSource,
MediaSourceSelection,
MediaType,
ModuleType,
MediaRecognizeType,
@@ -63,7 +64,7 @@ class DoubanModule(_ModuleBase):
return "豆瓣"
@staticmethod
def get_music_source() -> str:
def get_music_source() -> MediaSource:
"""返回音乐识别使用的数据源标识。"""
return DoubanModule._music_source
@@ -92,7 +93,7 @@ class DoubanModule(_ModuleBase):
self,
meta: MetaMusic,
limit: int = 20,
media_source: Optional[str] = None,
media_source: Optional[MediaSourceSelection] = None,
) -> Optional[List[MusicInfo]]:
"""按请求来源搜索豆瓣音乐专辑,并转换为统一音乐候选。"""
if not is_media_source_selected(media_source, self._music_source):
@@ -105,7 +106,7 @@ class DoubanModule(_ModuleBase):
def recognize_music(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -130,7 +131,11 @@ class DoubanModule(_ModuleBase):
)
return album.to_music_info()
def music_album(self, media_source: str, media_id: str) -> Optional[MusicAlbumInfo]:
def music_album(
self,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicAlbumInfo]:
"""按豆瓣音乐专辑 ID 获取标准化专辑详情和曲目。"""
if media_source != self._music_source or not media_id:
return None
@@ -139,7 +144,7 @@ class DoubanModule(_ModuleBase):
def music_discover(
self,
media_source: str,
media_source: MediaSource,
page: int = 1,
count: int = 30,
entity: str = MUSIC_ENTITY_ALBUM,
@@ -187,7 +192,7 @@ class DoubanModule(_ModuleBase):
def music_album_related(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
count: int = 24,
) -> Optional[List[MusicInfo]]:
@@ -204,7 +209,7 @@ class DoubanModule(_ModuleBase):
def _recognize_music_media(
self,
meta: Optional[MetaMusic],
media_source: Optional[str],
media_source: Optional[MediaSource],
media_id: Optional[str],
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -248,7 +253,7 @@ class DoubanModule(_ModuleBase):
async def _async_recognize_music_media(
self,
meta: Optional[MetaMusic],
media_source: Optional[str],
media_source: Optional[MediaSource],
media_id: Optional[str],
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -385,7 +390,7 @@ class DoubanModule(_ModuleBase):
title = cls._douban_music_text(target.get("title") or target.get("name"))
if not media_id or not title:
continue
artists = cls._douban_music_artists(target)
artists = cls._douban_music_search_artists(target)
release_date = cls._douban_music_date(target)
cover_url = cls._douban_music_cover(target)
candidate = MusicInfo(
@@ -551,6 +556,20 @@ class DoubanModule(_ModuleBase):
artists.append(text)
return artists
@classmethod
def _douban_music_search_artists(cls, info: dict[str, Any]) -> List[str]:
"""提取搜索候选艺术家,缺少结构化字段时回退到卡片副标题首段。"""
artists = cls._douban_music_artists(info)
if artists:
return artists
subtitle = cls._douban_music_text(info.get("card_subtitle"))
if not subtitle:
return []
artist = re.split(r"\s+/\s+", subtitle, maxsplit=1)[0].strip()
if not artist or re.fullmatch(r"\d{4}(?:-\d{1,2}(?:-\d{1,2})?)?", artist):
return []
return [artist]
@classmethod
def _douban_music_cover(cls, info: dict[str, Any]) -> Optional[str]:
"""从豆瓣多种图片字段中提取清晰封面。"""
@@ -1425,7 +1444,7 @@ class DoubanModule(_ModuleBase):
return []
def search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息
@@ -1433,7 +1452,7 @@ class DoubanModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 媒体信息
"""
if not is_media_source_enabled(media_source, "douban"):
if not is_media_source_enabled(media_source, MediaSource.Douban):
return None
if not meta.name:
return []
@@ -1444,7 +1463,7 @@ class DoubanModule(_ModuleBase):
return self._build_search_medias_result(meta, result.get("items"))
async def async_search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息(异步版本)
@@ -1452,7 +1471,7 @@ class DoubanModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 媒体信息
"""
if not is_media_source_enabled(media_source, "douban"):
if not is_media_source_enabled(media_source, MediaSource.Douban):
return None
if not meta.name:
return []
@@ -1463,7 +1482,7 @@ class DoubanModule(_ModuleBase):
return self._build_search_medias_result(meta, result.get("items"))
def search_persons(
self, name: str, media_source: Optional[MediaSource] = None
self, name: str, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaPerson]]:
"""
搜索人物信息
@@ -1471,7 +1490,7 @@ class DoubanModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 人物信息列表
"""
if not is_media_source_enabled(media_source, "douban"):
if not is_media_source_enabled(media_source, MediaSource.Douban):
return None
if not name:
return []
@@ -1488,7 +1507,7 @@ class DoubanModule(_ModuleBase):
return []
async def async_search_persons(
self, name: str, media_source: Optional[MediaSource] = None
self, name: str, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaPerson]]:
"""
搜索人物信息(异步版本)
@@ -1496,7 +1515,7 @@ class DoubanModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 人物信息列表
"""
if not is_media_source_enabled(media_source, "douban"):
if not is_media_source_enabled(media_source, MediaSource.Douban):
return None
if not name:
return []

View File

@@ -1150,7 +1150,9 @@ class Emby:
eventItem.item_id = message.get('Item', {}).get('Id')
eventItem.item_path = message.get('Item', {}).get('Path')
eventItem.tmdb_id = message.get('Item', {}).get('ProviderIds', {}).get('Tmdb')
eventItem.media_source, eventItem.media_id = MediaServerIdentityHelper.from_provider_ids(
message.get('Item', {}).get('ProviderIds')
)
if message.get('Item', {}).get('Overview') and len(message.get('Item', {}).get('Overview')) > 100:
eventItem.overview = str(message.get('Item', {}).get('Overview'))[:100] + "..."
else:
@@ -1171,7 +1173,9 @@ class Emby:
eventItem.item_type = message.get("item_type")
eventItem.item_name = message.get("item_name")
eventItem.item_path = message.get("item_path")
eventItem.tmdb_id = message.get("tmdb_id")
eventItem.media_source, eventItem.media_id = MediaServerIdentityHelper.from_provider_ids({
"tmdb_id": message.get("tmdb_id"),
})
eventItem.season_id = message.get("season_id")
eventItem.episode_id = message.get("episode_id")

View File

@@ -825,7 +825,11 @@ class Jellyfin:
channel="jellyfin"
)
eventItem.item_id = message.get('ItemId')
eventItem.tmdb_id = message.get('Provider_tmdb')
eventItem.media_source, eventItem.media_id = MediaServerIdentityHelper.from_provider_ids({
key.removeprefix("Provider_"): value
for key, value in message.items()
if key.startswith("Provider_")
})
eventItem.overview = message.get('Overview')
eventItem.item_favorite = message.get('Favorite')
eventItem.save_reason = message.get('SaveReason')

View File

@@ -24,6 +24,7 @@ from app.schemas.types import (
MUSIC_ENTITY_RECORDING,
MediaRecognizeType,
MediaSource,
MediaSourceSelection,
MediaType,
ModuleType,
)
@@ -123,7 +124,7 @@ class MusicBrainzModule(_ModuleBase):
return "MusicBrainz"
@staticmethod
def get_music_source() -> str:
def get_music_source() -> MediaSource:
"""返回音乐识别使用的数据源标识。"""
return MusicBrainzModule._source
@@ -146,7 +147,7 @@ class MusicBrainzModule(_ModuleBase):
self,
meta: MetaMusic,
limit: int = 20,
media_source: Optional[str] = None,
media_source: Optional[MediaSourceSelection] = None,
) -> Optional[list[MusicInfo]]:
"""搜索单曲、专辑和艺术家,并交错返回可浏览的 MusicBrainz 候选。"""
if not is_media_source_selected(media_source, self._source):
@@ -747,7 +748,7 @@ class MusicBrainzModule(_ModuleBase):
self,
meta: MetaBase = None,
mtype: MediaType = None,
media_source: Optional[str] = None,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
**kwargs,
) -> Optional[MusicInfo]:
@@ -846,7 +847,7 @@ class MusicBrainzModule(_ModuleBase):
self,
meta: MetaBase = None,
mtype: MediaType = None,
media_source: Optional[str] = None,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
**kwargs,
) -> Optional[MusicInfo]:
@@ -902,7 +903,12 @@ class MusicBrainzModule(_ModuleBase):
return result
@classmethod
def _select_candidate(cls, meta: MetaMusic, candidates: Iterable[MusicInfo], media_source: str) -> Optional[MusicInfo]:
def _select_candidate(
cls,
meta: MetaMusic,
candidates: Iterable[MusicInfo],
media_source: MediaSource,
) -> Optional[MusicInfo]:
"""按标题、艺术家和专辑匹配度选择最可信的搜索候选。"""
normalized_source = cls._normalize_text(media_source).casefold()
# 资源标题携带的音质标记先剥离,再与候选曲名比对;
@@ -1144,7 +1150,7 @@ class MusicBrainzModule(_ModuleBase):
def recognize_music(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -1169,7 +1175,7 @@ class MusicBrainzModule(_ModuleBase):
async def async_recognize_music(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -1193,7 +1199,7 @@ class MusicBrainzModule(_ModuleBase):
async def _async_music_album(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicAlbumInfo]:
"""异步按 MusicBrainz Release Group ID 获取专辑详情及曲目。"""
@@ -1218,7 +1224,11 @@ class MusicBrainzModule(_ModuleBase):
)
return album
def music_album(self, media_source: str, media_id: str) -> Optional[MusicAlbumInfo]:
def music_album(
self,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicAlbumInfo]:
"""按 MusicBrainz Release Group ID 获取标准化专辑详情及曲目。"""
if media_source != self._source or not media_id:
return None
@@ -1238,7 +1248,11 @@ class MusicBrainzModule(_ModuleBase):
album.tracks = self._album_tracks(album, payload.get("releases") or [])
return album
def music_artist(self, media_source: str, media_id: str) -> Optional[MusicArtistInfo]:
def music_artist(
self,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicArtistInfo]:
"""按 MusicBrainz Artist ID 获取标准化艺术家详情。"""
if media_source != self._source or not media_id:
return None
@@ -1250,7 +1264,7 @@ class MusicBrainzModule(_ModuleBase):
def music_artist_albums(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
page: int = 1,
count: int = 30,
@@ -1281,7 +1295,7 @@ class MusicBrainzModule(_ModuleBase):
def music_artist_related(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
count: int = 24,
) -> list[MusicArtistInfo]:

View File

@@ -15,6 +15,7 @@ from app.schemas.types import (
MUSIC_ENTITY_RECORDING,
MediaRecognizeType,
MediaSource,
MediaSourceSelection,
MediaType,
ModuleType,
)
@@ -50,7 +51,7 @@ class TheAudioDbModule(_ModuleBase):
return "TheAudioDB"
@staticmethod
def get_music_source() -> str:
def get_music_source() -> MediaSource:
"""返回音乐识别使用的数据源标识。"""
return TheAudioDbModule._source
@@ -73,7 +74,7 @@ class TheAudioDbModule(_ModuleBase):
self,
meta: MetaMusic,
limit: int = 20,
media_source: Optional[str] = None,
media_source: Optional[MediaSourceSelection] = None,
) -> Optional[list[MusicInfo]]:
"""按请求来源搜索 TheAudioDB 单曲、专辑和艺术家。"""
if not is_media_source_selected(media_source, self._source):
@@ -93,7 +94,7 @@ class TheAudioDbModule(_ModuleBase):
self,
meta: MetaBase = None,
mtype: MediaType = None,
media_source: Optional[str] = None,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
**kwargs,
) -> Optional[MusicInfo]:
@@ -131,7 +132,7 @@ class TheAudioDbModule(_ModuleBase):
self,
meta: MetaBase = None,
mtype: MediaType = None,
media_source: Optional[str] = None,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
**kwargs,
) -> Optional[MusicInfo]:
@@ -171,7 +172,7 @@ class TheAudioDbModule(_ModuleBase):
def recognize_music(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -190,7 +191,7 @@ class TheAudioDbModule(_ModuleBase):
async def async_recognize_music(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> Optional[MusicInfo]:
@@ -209,7 +210,7 @@ class TheAudioDbModule(_ModuleBase):
async def _async_music_album(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicAlbumInfo]:
"""异步按 TheAudioDB 专辑 ID 获取标准化专辑详情和曲目。"""
@@ -228,7 +229,11 @@ class TheAudioDbModule(_ModuleBase):
]
return album
def music_album(self, media_source: str, media_id: str) -> Optional[MusicAlbumInfo]:
def music_album(
self,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicAlbumInfo]:
"""按 TheAudioDB 专辑 ID 获取标准化专辑详情和曲目。"""
if media_source != self._source or not media_id:
return None
@@ -245,7 +250,11 @@ class TheAudioDbModule(_ModuleBase):
]
return album
def music_artist(self, media_source: str, media_id: str) -> Optional[MusicArtistInfo]:
def music_artist(
self,
media_source: MediaSource,
media_id: str,
) -> Optional[MusicArtistInfo]:
"""按 TheAudioDB 艺术家 ID 获取标准化艺术家详情。"""
if media_source != self._source or not media_id:
return None
@@ -255,7 +264,7 @@ class TheAudioDbModule(_ModuleBase):
def music_artist_albums(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
page: int = 1,
count: int = 30,
@@ -277,7 +286,7 @@ class TheAudioDbModule(_ModuleBase):
def music_album_related(
self,
media_source: str,
media_source: MediaSource,
media_id: str,
count: int = 24,
) -> Optional[list[MusicInfo]]:
@@ -304,11 +313,10 @@ class TheAudioDbModule(_ModuleBase):
def _search_tracks(self, meta: MetaMusic) -> list[MusicInfo]:
"""使用曲名和艺术家搜索 TheAudioDB 单曲。"""
title = meta.title
if not title:
artist = meta.artists[0] if meta.artists else meta.album_artist
if not title or not artist:
return []
params = {"t": title}
if meta.artists:
params["s"] = meta.artists[0]
params = {"t": title, "s": artist}
payload = self._request_json("searchtrack.php", params)
return [
info
@@ -319,11 +327,10 @@ class TheAudioDbModule(_ModuleBase):
async def _async_search_tracks(self, meta: MetaMusic) -> list[MusicInfo]:
"""异步使用曲名和艺术家搜索 TheAudioDB 单曲。"""
title = meta.title
if not title:
artist = meta.artists[0] if meta.artists else meta.album_artist
if not title or not artist:
return []
params = {"t": title}
if meta.artists:
params["s"] = meta.artists[0]
params = {"t": title, "s": artist}
payload = await self._async_request_json("searchtrack.php", params)
return [
info
@@ -334,11 +341,10 @@ class TheAudioDbModule(_ModuleBase):
def _search_albums(self, meta: MetaMusic) -> list[MusicAlbumInfo]:
"""使用专辑名和艺术家搜索 TheAudioDB 专辑。"""
album_name = meta.album or meta.title
if not album_name:
artist = meta.artists[0] if meta.artists else meta.album_artist
if not album_name or not artist:
return []
params = {"a": album_name}
if meta.artists:
params["s"] = meta.artists[0]
params = {"a": album_name, "s": artist}
payload = self._request_json("searchalbum.php", params)
return [self._album_to_info(item) for item in self._entities(payload, "album", "albums")]
@@ -348,11 +354,10 @@ class TheAudioDbModule(_ModuleBase):
) -> list[MusicAlbumInfo]:
"""异步使用专辑名和艺术家搜索 TheAudioDB 专辑。"""
album_name = meta.album or meta.title
if not album_name:
artist = meta.artists[0] if meta.artists else meta.album_artist
if not album_name or not artist:
return []
params = {"a": album_name}
if meta.artists:
params["s"] = meta.artists[0]
params = {"a": album_name, "s": artist}
payload = await self._async_request_json("searchalbum.php", params)
return [
self._album_to_info(item)
@@ -564,14 +569,25 @@ class TheAudioDbModule(_ModuleBase):
url=f"{cls._base_url}/{api_key}/{endpoint}",
params=params or {},
)
if not response or response.status_code != 200:
if response is None:
return None
try:
payload = response.json()
except ValueError as err:
logger.error(f"TheAudioDB 响应解析失败:{str(err)}")
return None
return payload if isinstance(payload, dict) else None
if response.status_code != 200:
return None
diagnostic = cls._response_diagnostic(response, endpoint)
if getattr(response, "content", None) in (b"", ""):
logger.warning(f"TheAudioDB 返回空响应:{diagnostic}")
return None
try:
payload = response.json()
except (TypeError, ValueError) as err:
logger.warning(
f"TheAudioDB 响应解析失败:{diagnostic},错误:{str(err)}"
)
return None
return payload if isinstance(payload, dict) else None
finally:
response.close()
@classmethod
@cached(
@@ -603,13 +619,31 @@ class TheAudioDbModule(_ModuleBase):
try:
if response.status_code != 200:
return None
payload = response.json()
except ValueError as err:
logger.error(f"TheAudioDB 响应解析失败:{str(err)}")
return None
diagnostic = cls._response_diagnostic(response, endpoint)
if getattr(response, "content", None) in (b"", ""):
logger.warning(f"TheAudioDB 返回空响应:{diagnostic}")
return None
try:
payload = response.json()
except (TypeError, ValueError) as err:
logger.warning(
f"TheAudioDB 响应解析失败:{diagnostic},错误:{str(err)}"
)
return None
return payload if isinstance(payload, dict) else None
finally:
await response.aclose()
return payload if isinstance(payload, dict) else None
@staticmethod
def _response_diagnostic(response: Any, endpoint: str) -> str:
"""生成不包含 API Key 的 TheAudioDB 响应诊断摘要。"""
headers = getattr(response, "headers", {}) or {}
content_type = headers.get("Content-Type", "") if hasattr(headers, "get") else ""
body = str(getattr(response, "text", "") or "").replace("\n", " ")[:200]
return (
f"endpoint={endpoint}, HTTP={getattr(response, 'status_code', '')}, "
f"Content-Type={content_type}, body={body!r}"
)
@staticmethod
def _entities(

View File

@@ -18,11 +18,16 @@ from app.schemas.types import (
MediaImageType,
MediaRecognizeType,
MediaSource,
MediaSourceSelection,
MediaType,
ModuleType,
)
from app.utils.http import RequestUtils
from app.utils.media import is_media_source_enabled, is_media_source_selected
from app.utils.media import (
is_media_source_enabled,
is_media_source_selected,
normalize_media_source,
)
from app.utils.zhconv import convert as zhconv_convert
@@ -102,7 +107,7 @@ class TheMovieDbModule(_ModuleBase):
def _validate_recognize_params(
meta: MetaBase,
tmdbid: Optional[int],
media_source: Optional[str] = None,
media_source: Optional[MediaSource] = None,
) -> bool:
"""
验证识别参数
@@ -115,7 +120,8 @@ class TheMovieDbModule(_ModuleBase):
if not tmdbid and not meta:
return False
if meta and not tmdbid and (media_source or settings.RECOGNIZE_SOURCE) != "themoviedb":
selected_source = normalize_media_source(media_source or settings.RECOGNIZE_SOURCE)
if meta and not tmdbid and selected_source != MediaSource.TMDB:
return False
if meta and not meta.name and not tmdbid:
@@ -769,7 +775,7 @@ class TheMovieDbModule(_ModuleBase):
}
def search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息
@@ -777,7 +783,7 @@ class TheMovieDbModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 媒体信息列表
"""
if not is_media_source_enabled(media_source, "themoviedb"):
if not is_media_source_enabled(media_source, MediaSource.TMDB):
return None
if not meta.name:
return []
@@ -801,7 +807,7 @@ class TheMovieDbModule(_ModuleBase):
return self._build_search_medias_result(meta, results)
def search_persons(
self, name: str, media_source: Optional[MediaSource] = None
self, name: str, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[schemas.MediaPerson]]:
"""
搜索人物信息
@@ -809,7 +815,7 @@ class TheMovieDbModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 人物信息列表
"""
if not is_media_source_enabled(media_source, "themoviedb"):
if not is_media_source_enabled(media_source, MediaSource.TMDB):
return None
if not name:
return []
@@ -819,7 +825,7 @@ class TheMovieDbModule(_ModuleBase):
return []
async def async_search_persons(
self, name: str, media_source: Optional[MediaSource] = None
self, name: str, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[schemas.MediaPerson]]:
"""
异步搜索人物信息
@@ -827,7 +833,7 @@ class TheMovieDbModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 人物信息列表
"""
if not is_media_source_enabled(media_source, "themoviedb"):
if not is_media_source_enabled(media_source, MediaSource.TMDB):
return None
if not name:
return []
@@ -837,7 +843,7 @@ class TheMovieDbModule(_ModuleBase):
return []
def search_collections(
self, name: str, media_source: Optional[MediaSource] = None
self, name: str, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索集合信息
@@ -845,7 +851,7 @@ class TheMovieDbModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 合集信息列表
"""
if media_source and not is_media_source_selected(media_source, "themoviedb"):
if media_source and not is_media_source_selected(media_source, MediaSource.TMDB):
return None
if not name:
return []
@@ -855,7 +861,7 @@ class TheMovieDbModule(_ModuleBase):
return []
async def async_search_collections(
self, name: str, media_source: Optional[MediaSource] = None
self, name: str, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
异步搜索集合信息
@@ -863,7 +869,7 @@ class TheMovieDbModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 合集信息列表
"""
if media_source and not is_media_source_selected(media_source, "themoviedb"):
if media_source and not is_media_source_selected(media_source, MediaSource.TMDB):
return None
if not name:
return []
@@ -1251,7 +1257,7 @@ class TheMovieDbModule(_ModuleBase):
# 异步方法
async def async_search_medias(
self, meta: MetaBase, media_source: Optional[MediaSource] = None
self, meta: MetaBase, media_source: Optional[MediaSourceSelection] = None
) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息(异步版本)
@@ -1259,7 +1265,7 @@ class TheMovieDbModule(_ModuleBase):
:param media_source: 请求级搜索数据源
:return: 媒体信息列表
"""
if not is_media_source_enabled(media_source, "themoviedb"):
if not is_media_source_enabled(media_source, MediaSource.TMDB):
return None
if not meta.name:
return []

View File

@@ -1026,7 +1026,9 @@ class ZSpace:
event_item.item_id = message.get('Item', {}).get('Id')
event_item.item_path = message.get('Item', {}).get('Path')
event_item.tmdb_id = message.get('Item', {}).get('ProviderIds', {}).get('Tmdb')
event_item.media_source, event_item.media_id = MediaServerIdentityHelper.from_provider_ids(
message.get('Item', {}).get('ProviderIds')
)
if message.get('Item', {}).get('Overview') and len(message.get('Item', {}).get('Overview')) > 100:
event_item.overview = str(message.get('Item', {}).get('Overview'))[:100] + "..."
else:
@@ -1047,7 +1049,9 @@ class ZSpace:
event_item.item_type = message.get("item_type")
event_item.item_name = message.get("item_name")
event_item.item_path = message.get("item_path")
event_item.tmdb_id = message.get("tmdb_id")
event_item.media_source, event_item.media_id = MediaServerIdentityHelper.from_provider_ids({
"tmdb_id": message.get("tmdb_id"),
})
event_item.season_id = message.get("season_id")
event_item.episode_id = message.get("episode_id")