mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 12:06:51 +08:00
refactor: expand chain runtime config snapshot
This commit is contained in:
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Protocol
|
||||
from typing import Any, Optional, Protocol
|
||||
|
||||
|
||||
class SystemConfigReader(Protocol):
|
||||
@@ -80,6 +80,12 @@ class ChainRuntimeConfig:
|
||||
"""Chain 在一次宿主生命周期内使用的基础配置快照。"""
|
||||
|
||||
media_extensions: tuple[str, ...]
|
||||
superuser: str = "admin"
|
||||
media_recognize_share: bool = False
|
||||
auxiliary_auth_enable: bool = False
|
||||
global_image_cache: bool = False
|
||||
auto_download_user: Optional[str] = None
|
||||
resource_url: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
|
||||
+10
-7
@@ -13,7 +13,6 @@ from app.domain.context import Context, MediaInfo, MusicInfo, TorrentInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.foundation.identity import normalize_internal_user_id
|
||||
from app.application.messaging.message import MessageTemplateHelper
|
||||
from app.runtime.config import settings
|
||||
from app.runtime.extensions.service_config import ServiceConfigHelper
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.message import MessageResponse
|
||||
@@ -176,7 +175,9 @@ class NotificationMixin:
|
||||
# 仅发送管理员
|
||||
logger.info(f"{send_message.mtype} 的消息已设置发送给管理员")
|
||||
# 读取管理员消息IDS
|
||||
send_message.targets = useroper.get_settings(settings.SUPERUSER)
|
||||
send_message.targets = useroper.get_settings(
|
||||
self.runtime_config.superuser
|
||||
)
|
||||
admin_sended = True
|
||||
elif action == "user" and send_message.username:
|
||||
# 发送对应用户
|
||||
@@ -196,7 +197,7 @@ class NotificationMixin:
|
||||
)
|
||||
# 读取管理员消息IDS
|
||||
send_message.targets = useroper.get_settings(
|
||||
settings.SUPERUSER
|
||||
self.runtime_config.superuser
|
||||
)
|
||||
admin_sended = True
|
||||
else:
|
||||
@@ -205,7 +206,7 @@ class NotificationMixin:
|
||||
f"用户 {send_message.username} 不存在,消息无法发送到对应用户"
|
||||
)
|
||||
continue
|
||||
elif send_message.username == settings.SUPERUSER:
|
||||
elif send_message.username == self.runtime_config.superuser:
|
||||
# 管理员同名已发送
|
||||
admin_sended = True
|
||||
else:
|
||||
@@ -292,7 +293,9 @@ class NotificationMixin:
|
||||
# 仅发送管理员
|
||||
logger.info(f"{send_message.mtype} 的消息已设置发送给管理员")
|
||||
# 读取管理员消息IDS
|
||||
send_message.targets = useroper.get_settings(settings.SUPERUSER)
|
||||
send_message.targets = useroper.get_settings(
|
||||
self.runtime_config.superuser
|
||||
)
|
||||
admin_sended = True
|
||||
elif action == "user" and send_message.username:
|
||||
# 发送对应用户
|
||||
@@ -312,7 +315,7 @@ class NotificationMixin:
|
||||
)
|
||||
# 读取管理员消息IDS
|
||||
send_message.targets = useroper.get_settings(
|
||||
settings.SUPERUSER
|
||||
self.runtime_config.superuser
|
||||
)
|
||||
admin_sended = True
|
||||
else:
|
||||
@@ -321,7 +324,7 @@ class NotificationMixin:
|
||||
f"用户 {send_message.username} 不存在,消息无法发送到对应用户"
|
||||
)
|
||||
continue
|
||||
elif send_message.username == settings.SUPERUSER:
|
||||
elif send_message.username == self.runtime_config.superuser:
|
||||
# 管理员同名已发送
|
||||
admin_sended = True
|
||||
else:
|
||||
|
||||
@@ -15,7 +15,6 @@ from app.domain.context import MediaInfo, MusicInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.runtime.cache import fresh, async_fresh
|
||||
from app.runtime.config import settings
|
||||
from app.runtime.events import Event
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||
@@ -24,8 +23,8 @@ from app.schemas.types import ChainEventType, MediaSource, MediaType, SystemConf
|
||||
|
||||
class RecognitionMixin:
|
||||
|
||||
@staticmethod
|
||||
def _can_use_media_recognize_share(
|
||||
self,
|
||||
meta: Optional[MetaBase],
|
||||
media_source: Optional[MediaSource],
|
||||
media_id: Optional[str],
|
||||
@@ -34,7 +33,7 @@ class RecognitionMixin:
|
||||
仅在名称识别场景下使用共享识别,显式ID识别不再重复回查
|
||||
"""
|
||||
return bool(
|
||||
settings.MEDIA_RECOGNIZE_SHARE
|
||||
self.runtime_config.media_recognize_share
|
||||
and meta
|
||||
and not media_source
|
||||
and not media_id
|
||||
@@ -515,4 +514,3 @@ class RecognitionMixin:
|
||||
f"({plugin_info.media_source}:{plugin_info.media_id})"
|
||||
)
|
||||
return plugin_info
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ from app.domain import title as title_rules
|
||||
from app.domain.context import Context, MediaInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.foundation import url as url_tools
|
||||
from app.runtime.config import settings
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.download import DownloadDirectory
|
||||
from app.schemas.file import FileURI
|
||||
@@ -1130,7 +1129,7 @@ class MediaInteractionChain(ChainBase):
|
||||
source=source,
|
||||
title=title,
|
||||
userid=userid,
|
||||
link=settings.MP_DOMAIN("#/resource"),
|
||||
link=self.runtime_config.resource_url,
|
||||
buttons=buttons,
|
||||
original_message_id=original_message_id,
|
||||
original_chat_id=original_chat_id,
|
||||
@@ -1533,12 +1532,11 @@ class MediaInteractionChain(ChainBase):
|
||||
for sea, no_exist in season_map.items()
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _should_auto_download(userid: Union[str, int]) -> bool:
|
||||
def _should_auto_download(self, userid: Union[str, int]) -> bool:
|
||||
"""
|
||||
判断当前用户是否命中自动下载名单。
|
||||
"""
|
||||
auto_download_user = settings.AUTO_DOWNLOAD_USER
|
||||
auto_download_user = self.runtime_config.auto_download_user
|
||||
return bool(
|
||||
auto_download_user
|
||||
and (
|
||||
|
||||
@@ -8,7 +8,7 @@ from app.chain.douban import DoubanChain
|
||||
from app.chain.listenbrainz import ListenBrainzChain
|
||||
from app.chain.tmdb import TmdbChain
|
||||
from app.runtime.cache import cached, fresh
|
||||
from app.runtime.config import settings, global_vars
|
||||
from app.runtime.config import global_vars
|
||||
from app.domain.context import MusicInfo
|
||||
from app.application.image import ImageHelper
|
||||
from app.runtime.log import logger
|
||||
@@ -272,7 +272,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
|
||||
:param datas: 数据列表
|
||||
:param progress_callback: 定时服务进度更新回调
|
||||
"""
|
||||
if not settings.GLOBAL_IMAGE_CACHE:
|
||||
if not self.runtime_config.global_image_cache:
|
||||
return
|
||||
|
||||
total_num = len(datas)
|
||||
|
||||
+2
-3
@@ -3,7 +3,6 @@ from dataclasses import dataclass
|
||||
from typing import Any, Literal, Optional, Tuple, Union
|
||||
|
||||
from app.chain import ChainBase
|
||||
from app.runtime.config import settings
|
||||
from app.application.security.token import get_password_hash, verify_password
|
||||
from app.application.chain.data import UserPortProxy as UserOper
|
||||
from app.runtime.log import logger
|
||||
@@ -73,7 +72,7 @@ class UserChain(ChainBase):
|
||||
return True, user_or_message
|
||||
else:
|
||||
# 用户不存在或密码错误,考虑辅助认证
|
||||
if settings.AUXILIARY_AUTH_ENABLE:
|
||||
if self.runtime_config.auxiliary_auth_enable:
|
||||
logger.warning("密码认证失败,尝试通过外部服务进行辅助认证 ...")
|
||||
aux_success, aux_user_or_message = self.auxiliary_authenticate(credentials=credentials)
|
||||
if aux_success:
|
||||
@@ -91,7 +90,7 @@ class UserChain(ChainBase):
|
||||
return False, PASSWORD_INVALID_CREDENTIALS_MESSAGE
|
||||
elif credentials.grant_type == "authorization_code":
|
||||
# 处理其他认证类型的分支
|
||||
if settings.AUXILIARY_AUTH_ENABLE:
|
||||
if self.runtime_config.auxiliary_auth_enable:
|
||||
aux_success, aux_user_or_message = self.auxiliary_authenticate(credentials=credentials)
|
||||
if aux_success:
|
||||
return True, aux_user_or_message
|
||||
|
||||
@@ -207,14 +207,20 @@ def _build_scheduler_runtime_config() -> SchedulerRuntimeConfig:
|
||||
|
||||
|
||||
def _build_chain_runtime_config() -> ChainRuntimeConfig:
|
||||
"""构建 Chain 通用媒体文件后缀配置快照。"""
|
||||
"""构建 Chain 在本次实例生命周期内使用的部署配置快照。"""
|
||||
return ChainRuntimeConfig(
|
||||
media_extensions=tuple(
|
||||
settings.RMT_MEDIAEXT
|
||||
+ settings.DOWNLOAD_TMPEXT
|
||||
+ settings.RMT_SUBEXT
|
||||
+ settings.RMT_AUDIOEXT
|
||||
)
|
||||
),
|
||||
superuser=settings.SUPERUSER,
|
||||
media_recognize_share=settings.MEDIA_RECOGNIZE_SHARE,
|
||||
auxiliary_auth_enable=settings.AUXILIARY_AUTH_ENABLE,
|
||||
global_image_cache=settings.GLOBAL_IMAGE_CACHE,
|
||||
auto_download_user=settings.AUTO_DOWNLOAD_USER,
|
||||
resource_url=settings.MP_DOMAIN("#/resource"),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -567,6 +567,9 @@ app/api/dependencies/ # 按领域拆分依赖工厂
|
||||
- 登录、仪表板和整理历史 API 不再直接导入 `settings`;`Scheduler` 已清除全部直接 `settings` 访问,
|
||||
用户认证配置改走 `SystemConfigService`;`StorageChain` 的媒体后缀改走 Chain snapshot。canonical 配置债务
|
||||
从 169/15 降到 164 个 settings import 文件/14 个 SystemConfigOper 构造点。
|
||||
- Chain snapshot 继续覆盖超级用户、共享识别、辅助认证、全局图片缓存、自动下载用户和资源页链接;
|
||||
消息、识别、交互、推荐和用户链的 5 个直接 `settings` 导入被移除,当前低水位进一步降到
|
||||
161 个 settings import 文件,插件 SDK 与兼容入口未改。
|
||||
- 直接调用 endpoint 和显式构造 `ChainRuntimeContext` 的旧测试/兼容入口仍有 fallback;正式 FastAPI 与
|
||||
Startup 路径始终使用 HostRuntime 注入。插件 SDK 的 `app.sdk.config.settings`、动态 API 返回和事件字段未改。
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"root": "app"
|
||||
},
|
||||
"settings_imports": {
|
||||
"count": 166,
|
||||
"count": 161,
|
||||
"files": [
|
||||
"app/adapters/cache/backends.py",
|
||||
"app/adapters/cache/redis.py",
|
||||
@@ -69,14 +69,10 @@
|
||||
"app/application/security/token.py",
|
||||
"app/application/security/url.py",
|
||||
"app/application/torrent.py",
|
||||
"app/chain/_messaging.py",
|
||||
"app/chain/_recognition.py",
|
||||
"app/chain/_transfer.py",
|
||||
"app/chain/download.py",
|
||||
"app/chain/interaction.py",
|
||||
"app/chain/media.py",
|
||||
"app/chain/message.py",
|
||||
"app/chain/recommend.py",
|
||||
"app/chain/scraping.py",
|
||||
"app/chain/search.py",
|
||||
"app/chain/site.py",
|
||||
@@ -84,7 +80,6 @@
|
||||
"app/chain/system.py",
|
||||
"app/chain/torrents.py",
|
||||
"app/chain/transfer.py",
|
||||
"app/chain/user.py",
|
||||
"app/cli.py",
|
||||
"app/db/base.py",
|
||||
"app/db/engine.py",
|
||||
|
||||
+2
-6
@@ -13,8 +13,8 @@
|
||||
"runtime_to_db": [],
|
||||
"workflow_to_db": []
|
||||
},
|
||||
"edge_count": 6364,
|
||||
"edge_sha256": "0f241a46ed27309c2071d4c99f00ba067ad02b3974581cc206ddec4c522a982c",
|
||||
"edge_count": 6360,
|
||||
"edge_sha256": "09c923d8b167a889e22829c320e05f8801c62400f672b1386d6524c55b064564",
|
||||
"edges": [
|
||||
"app -> app.runtime",
|
||||
"app -> app.runtime.compat",
|
||||
@@ -2851,7 +2851,6 @@
|
||||
"app.chain._messaging -> app.foundation",
|
||||
"app.chain._messaging -> app.foundation.identity",
|
||||
"app.chain._messaging -> app.runtime",
|
||||
"app.chain._messaging -> app.runtime.config",
|
||||
"app.chain._messaging -> app.runtime.extensions",
|
||||
"app.chain._messaging -> app.runtime.extensions.service_config",
|
||||
"app.chain._messaging -> app.runtime.log",
|
||||
@@ -2891,7 +2890,6 @@
|
||||
"app.chain._recognition -> app.domain.meta.metamusic",
|
||||
"app.chain._recognition -> app.runtime",
|
||||
"app.chain._recognition -> app.runtime.cache",
|
||||
"app.chain._recognition -> app.runtime.config",
|
||||
"app.chain._recognition -> app.runtime.events",
|
||||
"app.chain._recognition -> app.runtime.log",
|
||||
"app.chain._recognition -> app.schemas",
|
||||
@@ -3015,7 +3013,6 @@
|
||||
"app.chain.interaction -> app.foundation",
|
||||
"app.chain.interaction -> app.foundation.url",
|
||||
"app.chain.interaction -> app.runtime",
|
||||
"app.chain.interaction -> app.runtime.config",
|
||||
"app.chain.interaction -> app.runtime.log",
|
||||
"app.chain.interaction -> app.schemas",
|
||||
"app.chain.interaction -> app.schemas.download",
|
||||
@@ -3366,7 +3363,6 @@
|
||||
"app.chain.user -> app.application.security.token",
|
||||
"app.chain.user -> app.chain",
|
||||
"app.chain.user -> app.runtime",
|
||||
"app.chain.user -> app.runtime.config",
|
||||
"app.chain.user -> app.runtime.log",
|
||||
"app.chain.user -> app.schemas",
|
||||
"app.chain.user -> app.schemas.event",
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""配置快照与窄读写端口测试。"""
|
||||
|
||||
import asyncio
|
||||
from dataclasses import FrozenInstanceError
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from app.application.configuration import (
|
||||
ApiRuntimeConfig,
|
||||
ChainRuntimeConfig,
|
||||
@@ -78,5 +81,19 @@ def test_api_runtime_provider_returns_frozen_snapshot_per_request() -> None:
|
||||
|
||||
assert before_reload.ai_agent_enable is False
|
||||
assert after_reload.ai_agent_enable is True
|
||||
configure_runtime_configuration,
|
||||
get_api_runtime_config_snapshot,
|
||||
|
||||
|
||||
def test_chain_runtime_config_is_an_instance_scoped_frozen_snapshot() -> None:
|
||||
"""Chain 配置应随实例固定,避免同一业务调用中途读取到 reload 后的新值。"""
|
||||
snapshot = ChainRuntimeConfig(
|
||||
media_extensions=(".mkv",),
|
||||
superuser="root",
|
||||
media_recognize_share=True,
|
||||
resource_url="https://example.test/#/resource",
|
||||
)
|
||||
|
||||
assert snapshot.superuser == "root"
|
||||
assert snapshot.media_recognize_share is True
|
||||
assert snapshot.resource_url == "https://example.test/#/resource"
|
||||
with pytest.raises(FrozenInstanceError):
|
||||
snapshot.superuser = "changed" # type: ignore[misc]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
共享识别成功后回填本地缓存、音乐识别上报/查询载荷,以及命中缓存不重复上报等场景。
|
||||
"""
|
||||
import asyncio
|
||||
from dataclasses import replace
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from app.chain import ChainBase
|
||||
@@ -41,6 +42,14 @@ def _tmdb_media(
|
||||
)
|
||||
|
||||
|
||||
def _enable_media_recognize_share(chain: ChainBase) -> None:
|
||||
"""为单个链实例启用共享识别配置快照。"""
|
||||
chain.runtime_config = replace(
|
||||
chain.runtime_config,
|
||||
media_recognize_share=True,
|
||||
)
|
||||
|
||||
|
||||
def test_report_shared_result_after_local_recognize_success():
|
||||
"""本地识别成功后应上报共享识别结果。"""
|
||||
chain = ChainBase()
|
||||
@@ -64,6 +73,7 @@ def test_report_shared_result_after_local_recognize_success():
|
||||
def test_query_shared_result_when_local_recognize_failed():
|
||||
"""本地识别失败后应回查共享识别结果,并按共享ID再次识别。"""
|
||||
chain = ChainBase()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = _build_meta("测试剧集")
|
||||
shared_media = _tmdb_media("测试剧集", 200, MediaType.TV, year="2024")
|
||||
|
||||
@@ -109,6 +119,7 @@ def test_query_shared_result_when_local_recognize_failed():
|
||||
def test_async_query_shared_result_when_local_recognize_failed():
|
||||
"""异步识别失败后也应回查共享识别结果。"""
|
||||
chain = ChainBase()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = _build_meta("测试异步剧集")
|
||||
shared_media = _tmdb_media("测试异步剧集", 300, MediaType.TV, year="2025")
|
||||
async_run_module = AsyncMock(side_effect=[None, shared_media])
|
||||
@@ -157,6 +168,7 @@ def test_async_query_shared_result_when_local_recognize_failed():
|
||||
def test_backfill_local_cache_after_shared_recognize_success():
|
||||
"""共享识别后二次本地识别成功时,应回填原始名称对应的本地识别缓存。"""
|
||||
chain = ChainBase()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = _build_meta("测试缓存回填", MediaType.MOVIE)
|
||||
shared_media = MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
@@ -304,6 +316,7 @@ def test_report_shared_result_with_distinct_keyword_meta():
|
||||
def test_query_shared_result_with_distinct_keyword_meta():
|
||||
"""本地识别失败后应按辅助前名称回查共享结果。"""
|
||||
chain = ChainBase()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = _build_meta("辅助识别后的名称", MediaType.TV)
|
||||
meta.year = "2024"
|
||||
share_meta = _build_meta("辅助识别前的名称", MediaType.UNKNOWN)
|
||||
@@ -594,6 +607,7 @@ def test_chain_recognize_media_reports_music_share_result():
|
||||
def test_chain_recognize_media_queries_music_share_when_local_failed():
|
||||
"""音乐本地识别失败后应回查共享识别并按数据源原生 ID 二次识别。"""
|
||||
chain = MediaChain()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = MetaMusic(title="晴天", artists=["周杰伦"])
|
||||
music = _music_info()
|
||||
|
||||
@@ -641,6 +655,7 @@ def test_chain_recognize_media_queries_music_share_when_local_failed():
|
||||
def test_chain_recognize_media_queries_music_share_after_local_fallback():
|
||||
"""本地标签兜底没有远端身份时,仍应通过共享结果补成标准音乐身份。"""
|
||||
chain = MediaChain()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = MetaMusic(title="晴天", artists=["周杰伦"])
|
||||
fallback = MusicInfo(title="晴天", artists=["周杰伦"])
|
||||
music = _music_info()
|
||||
@@ -669,9 +684,6 @@ def test_chain_recognize_media_queries_music_share_after_local_fallback():
|
||||
), patch.object(
|
||||
chain,
|
||||
"_update_local_recognize_cache",
|
||||
), patch(
|
||||
"app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE",
|
||||
True,
|
||||
):
|
||||
result = chain.recognize_media(meta=meta, cache=False)
|
||||
|
||||
@@ -687,6 +699,7 @@ def test_chain_recognize_media_queries_music_share_after_local_fallback():
|
||||
def test_chain_async_recognize_media_queries_music_share_after_local_fallback():
|
||||
"""异步音乐识别也必须在返回本地兜底前尝试共享身份补全。"""
|
||||
chain = MediaChain()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = MetaMusic(title="晴天", artists=["周杰伦"])
|
||||
fallback = MusicInfo(title="晴天", artists=["周杰伦"])
|
||||
music = _music_info()
|
||||
@@ -717,9 +730,6 @@ def test_chain_async_recognize_media_queries_music_share_after_local_fallback():
|
||||
chain,
|
||||
"_async_update_local_recognize_cache",
|
||||
new=AsyncMock(),
|
||||
), patch(
|
||||
"app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE",
|
||||
True,
|
||||
):
|
||||
result = await chain.async_recognize_media(meta=meta, cache=False)
|
||||
return result, query_share, recognize_source
|
||||
@@ -745,6 +755,7 @@ def test_chain_async_recognize_media_queries_music_share_after_local_fallback():
|
||||
def test_chain_recognize_media_skips_music_report_for_fallback_result():
|
||||
"""共享也未命中时保留音乐标签兜底,且不把无身份结果上报。"""
|
||||
chain = MediaChain()
|
||||
_enable_media_recognize_share(chain)
|
||||
meta = MetaMusic(title="未知曲目", artists=["未知艺术家"])
|
||||
fallback = MusicInfo(title="未知曲目", artists=["未知艺术家"])
|
||||
|
||||
@@ -753,9 +764,7 @@ def test_chain_recognize_media_skips_music_report_for_fallback_result():
|
||||
return_value=None,
|
||||
) as query_mock, patch(
|
||||
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share"
|
||||
) as report_mock, patch(
|
||||
"app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE", True
|
||||
):
|
||||
) as report_mock:
|
||||
result = chain.recognize_media(meta=meta, cache=False)
|
||||
|
||||
assert result is fallback
|
||||
|
||||
@@ -41,6 +41,7 @@ def _bare_chain() -> ChainBase:
|
||||
"""构造不执行初始化的识别链实例,并挂上无插件响应的事件管理器桩。"""
|
||||
chain = object.__new__(ChainBase)
|
||||
chain.eventmanager = Mock(check=Mock(return_value=False))
|
||||
chain.runtime_config = SimpleNamespace(media_recognize_share=True)
|
||||
return chain
|
||||
|
||||
|
||||
@@ -57,7 +58,6 @@ def test_sync_shared_recognize_success_increments_persisted_count(monkeypatch):
|
||||
type=MediaType.MOVIE,
|
||||
)
|
||||
increment = _mock_counter(monkeypatch)
|
||||
monkeypatch.setattr("app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE", True)
|
||||
monkeypatch.setattr(chain, "run_module", Mock(side_effect=[None, media]))
|
||||
monkeypatch.setattr(chain, "_update_local_recognize_cache", Mock())
|
||||
monkeypatch.setattr(
|
||||
@@ -86,7 +86,6 @@ def test_sync_shared_result_without_local_match_does_not_increment(monkeypatch):
|
||||
chain = _bare_chain()
|
||||
meta = _build_meta("共享识别失败电影")
|
||||
increment = _mock_counter(monkeypatch)
|
||||
monkeypatch.setattr("app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE", True)
|
||||
monkeypatch.setattr(chain, "run_module", Mock(side_effect=[None, None]))
|
||||
monkeypatch.setattr(
|
||||
MoviePilotServerHelper,
|
||||
@@ -122,7 +121,6 @@ def test_async_shared_recognize_success_increments_persisted_count(monkeypatch):
|
||||
type=MediaType.MOVIE,
|
||||
)
|
||||
increment = _mock_counter(monkeypatch)
|
||||
monkeypatch.setattr("app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE", True)
|
||||
monkeypatch.setattr(
|
||||
chain,
|
||||
"async_run_module",
|
||||
|
||||
Reference in New Issue
Block a user