diff --git a/app/chain/media.py b/app/chain/media.py index 3a5f086f5..04a42d371 100644 --- a/app/chain/media.py +++ b/app/chain/media.py @@ -623,12 +623,11 @@ class MediaChain(ChainBase, metaclass=Singleton): if mediainfo.tmdb_id and mediainfo.tmdb_info and mediainfo.genre_ids: return mediainfo tmdb_meta = self._build_tmdb_supplement_meta(mediainfo, metainfo) - tmdb_module = self.modulemanager.get_running_module("TheMovieDbModule") - if not tmdb_module: - logger.warn("TMDB 模块未启用,无法补充 TMDB 辅助信息") - return mediainfo + # 按 source 路由走统一 dispatch:宿主识别模块对非自身来源都会快速返回 None, + # 指定 TMDB 来源时只有 TheMovieDbModule 会应答,链层无需点名具体模块 try: - tmdb_media = tmdb_module.recognize_media( + tmdb_media = self.run_module( + "recognize_media", meta=tmdb_meta, mtype=mediainfo.type, media_source=MediaSource.TMDB, diff --git a/docs/refactor/backend-architecture-review.md b/docs/refactor/backend-architecture-review.md index 56366ca53..010a31982 100644 --- a/docs/refactor/backend-architecture-review.md +++ b/docs/refactor/backend-architecture-review.md @@ -62,6 +62,14 @@ chain 层零 `app.db` / `app.modules` 内部直连,domain 与 chain 层配置 * `scraping.py:584-598` 自行聚合 `metadata_img` 多模块结果 (dispatcher 已有 aggregation contract 可表达)。 +> 处理进展(2026-08-24):`media.py` 的 TMDB 补充已改为按 source 路由的 +> `run_module("recognize_media")` 统一调度(宿主识别模块对非自身来源快速返回 None, +> 与 `_recognition.py` 既有模式一致)。`scraping.py` 的 `metadata_img` 合并经复核 +> **不能直接替换**:手写循环是"按键合并、宿主模块限定",而 dispatcher 现有聚合只有 +> 整体短路或后值覆盖,且 dispatch 全局插件优先会让第三方插件图片覆盖内置源图片。 +> 如需收口,须先做显式架构决策:新增"按键填充"聚合模式并提供调用方可控的 +> provider 排序策略,否则维持现状是行为最安全的选择。 + ## 四、全局状态:"通道已建、存量过半" | 全局点 | 现状 | 建议 | diff --git a/tests/test_tmdb_auxiliary.py b/tests/test_tmdb_auxiliary.py index 08931fd13..d3aa9c547 100644 --- a/tests/test_tmdb_auxiliary.py +++ b/tests/test_tmdb_auxiliary.py @@ -1,8 +1,10 @@ from types import SimpleNamespace +from unittest.mock import Mock from app.chain.media import MediaChain from app.domain.context import MediaInfo from app.domain.metainfo import MetaInfo +from app.runtime.extensions.module.dispatcher import ModuleInvocationDispatcher from app.schemas.types import MediaSource, MediaType @@ -13,19 +15,35 @@ class _FakeTmdbModule: """保存测试需要返回的 TMDB 媒体信息。""" self.result = result - def recognize_media(self, **_kwargs): - """同步返回固定 TMDB 媒体信息。""" + def get_name(self) -> str: + """返回模块展示名。""" + return "FakeTmdbModule" + + def get_priority(self) -> int: + """返回模块调度优先级。""" + return 0 + + def recognize_media(self, **kwargs): + """与宿主识别模块一致:只应答 TMDB 来源的请求。""" + if kwargs.get("media_source") != MediaSource.TMDB: + return None return self.result def _make_chain(tmdb_media: MediaInfo) -> MediaChain: - """构造不加载真实模块的媒体处理链。""" - chain = object.__new__(MediaChain) + """构造经真实 dispatch 算法路由、但不加载真实模块的媒体处理链。""" module = _FakeTmdbModule(tmdb_media) - chain.modulemanager = SimpleNamespace( - get_running_module=lambda module_id: ( - module if module_id == "TheMovieDbModule" else None - ) + module_manager = Mock() + module_manager.get_running_modules.return_value = [module] + plugin_manager = Mock() + plugin_manager.get_plugin_modules.return_value = {} + chain = object.__new__(MediaChain) + chain._module_dispatcher = ModuleInvocationDispatcher( + module_catalog=module_manager, + plugin_catalog=plugin_manager, + plugin_error_handler=lambda *args, **kwargs: None, + system_error_handler=lambda *args, **kwargs: None, + rate_limit_handler=lambda *args, **kwargs: None, ) return chain