diff --git a/app/modules/_base/media_auxiliary.py b/app/modules/_base/media_auxiliary.py index 178bc416c..c29e9ecaa 100644 --- a/app/modules/_base/media_auxiliary.py +++ b/app/modules/_base/media_auxiliary.py @@ -21,8 +21,9 @@ class MediaAuxiliaryProviderMixin: def _build_auxiliary_meta( mediainfo: MediaInfo, metainfo: Optional[MetaBase] = None, - ) -> MetaInfo: + ) -> MetaBase: """根据主识别结果构造不携带原来源身份的标题查询参数。""" + # MetaInfo 是工厂函数,实际返回 MetaBase 及其子类实例 meta = MetaInfo(mediainfo.title or getattr(metainfo, "name", None) or "") if not meta.en_name: meta.en_name = mediainfo.en_title or getattr(metainfo, "en_name", None) diff --git a/app/modules/anilist/__init__.py b/app/modules/anilist/__init__.py index e084d9728..4258dce2e 100644 --- a/app/modules/anilist/__init__.py +++ b/app/modules/anilist/__init__.py @@ -1,16 +1,15 @@ from typing import List, Optional, Tuple, Union -from app.runtime.settings import RuntimeSettingsCompat +from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin +from app.runtime.settings import get_runtime_setting from app.schemas.context import MediaPerson as _SchemaMediaPerson -settings = RuntimeSettingsCompat() from app.domain.context import MediaInfo from app.domain.media import is_media_source_enabled from app.domain.meta.metabase import MetaBase from app.domain.scraper import MediaScraperHelper from app.modules import _ModuleBase from app.modules.anilist.anilist import AniListApi -from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin from app.runtime.log import logger from app.schemas.types import ( MediaRecognizeType, @@ -78,7 +77,7 @@ class AniListModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param media_source: 请求级识别数据源 :return: 是否启用 AniList 识别 """ - return (media_source or settings.RECOGNIZE_SOURCE) == MediaSource.AniList + return (media_source or get_runtime_setting("RECOGNIZE_SOURCE")) == MediaSource.AniList @staticmethod def _media_type(info: dict) -> MediaType: @@ -571,7 +570,7 @@ class AniListModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param episode: 集号 :return: NFO XML 文本 """ - scrape_source = mediainfo.scrape_source or settings.SCRAP_SOURCE + scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE") if scrape_source != "anilist": return None return self.scraper.get_metadata_nfo(mediainfo, season=season, episode=episode) @@ -590,7 +589,7 @@ class AniListModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param episode: 集号 :return: 图片文件名与下载地址映射 """ - scrape_source = mediainfo.scrape_source or settings.SCRAP_SOURCE + scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE") if scrape_source != "anilist": return None return self.scraper.get_metadata_img(mediainfo, season=season, episode=episode) diff --git a/app/modules/bangumi/__init__.py b/app/modules/bangumi/__init__.py index 287a31728..2d35eb1dd 100644 --- a/app/modules/bangumi/__init__.py +++ b/app/modules/bangumi/__init__.py @@ -1,10 +1,10 @@ from dataclasses import dataclass from typing import Any, List, Optional, Tuple, Union -from app.runtime.settings import RuntimeSettingsCompat +from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin +from app.runtime.settings import get_runtime_setting from app.schemas.context import MediaPerson as _SchemaMediaPerson -settings = RuntimeSettingsCompat() from app.adapters.network.http import RequestUtils from app.domain.context import MediaInfo from app.domain.media import is_media_source_enabled @@ -12,7 +12,6 @@ from app.domain.meta.metabase import MetaBase from app.domain.scraper import MediaScraperHelper from app.modules import _ModuleBase from app.modules.bangumi.bangumi import BangumiApi -from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin from app.runtime.log import logger from app.schemas.types import ( MediaRecognizeType, @@ -45,7 +44,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase): """ 初始化Bangumi客户端 """ - self._config = BangumiConfigSnapshot(proxy=settings.PROXY) + self._config = BangumiConfigSnapshot(proxy=get_runtime_setting("PROXY")) self.bangumiapi = BangumiApi() self.scraper = MediaScraperHelper() @@ -127,7 +126,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase): return None bangumiid = int(media_id) if media_id is not None else None if not bangumiid and ( - not meta or (media_source or settings.RECOGNIZE_SOURCE) != MediaSource.Bangumi + not meta or (media_source or get_runtime_setting("RECOGNIZE_SOURCE")) != MediaSource.Bangumi ): return None @@ -176,7 +175,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase): return None bangumiid = int(media_id) if media_id is not None else None if not bangumiid and ( - not meta or (media_source or settings.RECOGNIZE_SOURCE) != MediaSource.Bangumi + not meta or (media_source or get_runtime_setting("RECOGNIZE_SOURCE")) != MediaSource.Bangumi ): return None @@ -317,7 +316,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param episode: 集号 :return: NFO XML文本 """ - scrape_source = mediainfo.scrape_source or settings.SCRAP_SOURCE + scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE") if scrape_source != "bangumi": return None return self.scraper.get_metadata_nfo(mediainfo, season=season, episode=episode) @@ -336,7 +335,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param episode: 集号 :return: 图片文件名与下载地址映射 """ - scrape_source = mediainfo.scrape_source or settings.SCRAP_SOURCE + scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE") if scrape_source != "bangumi": return None return self.scraper.get_metadata_img(mediainfo, season=season, episode=episode) diff --git a/app/modules/douban/__init__.py b/app/modules/douban/__init__.py index 5e1ca5524..0243dcd3c 100644 --- a/app/modules/douban/__init__.py +++ b/app/modules/douban/__init__.py @@ -3,10 +3,10 @@ from typing import Any, List, Optional, Tuple, Union import cn2an -from app.runtime.settings import RuntimeSettingsCompat +from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin +from app.runtime.settings import get_runtime_setting from app.schemas.context import MediaPerson as _SchemaMediaPerson -settings = RuntimeSettingsCompat() from app.adapters.network.http import RequestUtils from app.domain.context import ( MediaInfo, @@ -21,7 +21,6 @@ from app.foundation.text import convert as zhconv_convert from app.modules import _ModuleBase from app.modules.douban.apiv2 import DoubanApi from app.modules.douban.scraper import DoubanScraper -from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin from app.runtime.execution import retry from app.runtime.log import logger from app.runtime.rate import rate_limit_exponential @@ -707,7 +706,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase): if ( meta and not doubanid - and (kwargs.get("media_source") or settings.RECOGNIZE_SOURCE) != "douban" + and (kwargs.get("media_source") or get_runtime_setting("RECOGNIZE_SOURCE")) != "douban" ): return None @@ -778,7 +777,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase): if ( meta and not doubanid - and (kwargs.get("media_source") or settings.RECOGNIZE_SOURCE) != "douban" + and (kwargs.get("media_source") or get_runtime_setting("RECOGNIZE_SOURCE")) != "douban" ): return None @@ -1685,7 +1684,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param mediainfo: 媒体信息 :param season: 季号 """ - if (mediainfo.scrape_source or settings.SCRAP_SOURCE) != "douban": + if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "douban": return None return self.scraper.get_metadata_nfo(mediainfo=mediainfo, season=season) @@ -1696,7 +1695,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param season: 季号 :param episode: 集号 """ - if (mediainfo.scrape_source or settings.SCRAP_SOURCE) != "douban": + if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "douban": return None return self.scraper.get_metadata_img(mediainfo=mediainfo, season=season, episode=episode) @@ -1707,7 +1706,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param mediainfo: 媒体信息 :return: None 表示不处理,MediaInfo 表示继续处理 """ - if mediainfo.media_source != MediaSource.Douban and settings.RECOGNIZE_SOURCE != "douban": + if mediainfo.media_source != MediaSource.Douban and get_runtime_setting("RECOGNIZE_SOURCE") != "douban": return None if not mediainfo.douban_id: return None diff --git a/app/modules/themoviedb/__init__.py b/app/modules/themoviedb/__init__.py index d17b3756b..7d416dd0f 100644 --- a/app/modules/themoviedb/__init__.py +++ b/app/modules/themoviedb/__init__.py @@ -3,19 +3,18 @@ from typing import Dict, List, Optional, Tuple, Union import cn2an -from app.runtime.settings import RuntimeSettingsCompat +from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin +from app.runtime.settings import get_runtime_setting from app.schemas.context import MediaPerson as _SchemaMediaPerson from app.schemas.tmdb import TmdbEpisode as _SchemaTmdbEpisode from app.schemas.tmdb import TmdbSeason as _SchemaTmdbSeason -settings = RuntimeSettingsCompat() from app.adapters.network.http import RequestUtils from app.domain.context import MediaInfo from app.domain.media import is_media_source_enabled, is_media_source_selected from app.domain.meta.metabase import MetaBase from app.foundation.text import convert as zhconv_convert from app.modules import _ModuleBase -from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin from app.modules.themoviedb.category import CategoryHelper from app.modules.themoviedb.scraper import TmdbScraper from app.modules.themoviedb.tmdb_cache import TmdbCache @@ -95,13 +94,13 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): """ 测试模块连接性 """ - ret = RequestUtils(ua=settings.NORMAL_USER_AGENT, proxies=settings.PROXY).get_res( - f"https://{settings.TMDB_API_DOMAIN}/3/movie/550?api_key={settings.TMDB_API_KEY}") + ret = RequestUtils(ua=get_runtime_setting("NORMAL_USER_AGENT"), proxies=get_runtime_setting("PROXY")).get_res( + f"https://{get_runtime_setting("TMDB_API_DOMAIN")}/3/movie/550?api_key={get_runtime_setting("TMDB_API_KEY")}") if ret and ret.status_code == 200: return True, "" elif ret: - return False, f"无法连接 {settings.TMDB_API_DOMAIN},错误码:{ret.status_code}" - return False, f"{settings.TMDB_API_DOMAIN} 网络连接失败" + return False, f"无法连接 {get_runtime_setting("TMDB_API_DOMAIN")},错误码:{ret.status_code}" + return False, f"{get_runtime_setting("TMDB_API_DOMAIN")} 网络连接失败" def init_setting(self) -> Tuple[str, Union[str, bool]]: pass @@ -123,7 +122,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): if not tmdbid and not meta: return False - selected_source = normalize_media_source(media_source or settings.RECOGNIZE_SOURCE) + selected_source = normalize_media_source(media_source or get_runtime_setting("RECOGNIZE_SOURCE")) if meta and not tmdbid and selected_source != MediaSource.TMDB: return False @@ -974,7 +973,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param season: 季号 :param episode: 集号 """ - if (mediainfo.scrape_source or settings.SCRAP_SOURCE) != "themoviedb": + if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "themoviedb": return None return self.scraper.get_metadata_nfo(meta=meta, mediainfo=mediainfo, season=season, episode=episode) @@ -986,7 +985,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param season: 季号 :param episode: 集号 """ - if (mediainfo.scrape_source or settings.SCRAP_SOURCE) != "themoviedb": + if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "themoviedb": return None return self.scraper.get_metadata_img(mediainfo=mediainfo, season=season, episode=episode) @@ -1107,7 +1106,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): :param mediainfo: 媒体信息 :return: None 表示不处理,MediaInfo 表示继续处理 """ - if mediainfo.media_source != "themoviedb" and settings.RECOGNIZE_SOURCE != "themoviedb": + if mediainfo.media_source != "themoviedb" and get_runtime_setting("RECOGNIZE_SOURCE") != "themoviedb": return None if not mediainfo.tmdb_id: return mediainfo @@ -1148,15 +1147,15 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): # 背景图 if not mediainfo.backdrop_path: if image_path := cls._pick_best_tmdb_image(images.get("backdrops")): - mediainfo.backdrop_path = settings.TMDB_IMAGE_URL(image_path) + mediainfo.backdrop_path = get_runtime_setting("TMDB_IMAGE_URL")(image_path) # 标志 if not mediainfo.logo_path: if image_path := cls._pick_best_tmdb_image(images.get("logos")): - mediainfo.logo_path = settings.TMDB_IMAGE_URL(image_path) + mediainfo.logo_path = get_runtime_setting("TMDB_IMAGE_URL")(image_path) # 海报 if not mediainfo.poster_path: if image_path := cls._pick_best_tmdb_image(images.get("posters")): - mediainfo.poster_path = settings.TMDB_IMAGE_URL(image_path) + mediainfo.poster_path = get_runtime_setting("TMDB_IMAGE_URL")(image_path) return mediainfo def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]: @@ -1246,7 +1245,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase): image_path = seasoninfo.get(image_type.value) if image_path: - return settings.TMDB_IMAGE_URL(image_path, image_prefix) + return get_runtime_setting("TMDB_IMAGE_URL")(image_path, image_prefix) return None def tmdb_movie_similar(self, tmdbid: int) -> List[MediaInfo]: diff --git a/app/modules/thetvdb/__init__.py b/app/modules/thetvdb/__init__.py index 7fd2bdb78..fc69b75f7 100644 --- a/app/modules/thetvdb/__init__.py +++ b/app/modules/thetvdb/__init__.py @@ -1,15 +1,14 @@ from threading import Lock from typing import Optional, Tuple, Union, cast -from app.runtime.settings import RuntimeSettingsCompat - -settings = RuntimeSettingsCompat() from app.domain.context import MediaInfo from app.domain.media import is_media_source_enabled from app.domain.meta.metabase import MetaBase +from app.runtime.execution import run_in_threadpool +from app.runtime.settings import get_runtime_setting + from app.modules import _ModuleBase from app.modules.thetvdb import tvdb_v4_official -from app.runtime.execution import run_in_threadpool from app.runtime.log import logger from app.schemas.types import ( MediaRecognizeType, @@ -39,11 +38,11 @@ class TheTvDbModule(_ModuleBase): action = "刷新" if is_retry else "创建" logger.info(f"开始{action}TVDB登录会话...") try: - if not settings.TVDB_V4_API_KEY: + if not get_runtime_setting("TVDB_V4_API_KEY"): raise ConnectionError("TVDB API Key 未配置,无法初始化会话。") - self.tvdb = tvdb_v4_official.TVDB(apikey=settings.TVDB_V4_API_KEY, - pin=settings.TVDB_V4_API_PIN, - proxy=settings.PROXY, + self.tvdb = tvdb_v4_official.TVDB(apikey=get_runtime_setting("TVDB_V4_API_KEY"), + pin=get_runtime_setting("TVDB_V4_API_PIN"), + proxy=get_runtime_setting("PROXY"), timeout=self.__timeout) if self.tvdb: logger.info(f"TVDB登录会话{action}成功。")