refactor(chain): _mixins.py 更名 _transfer.py,统一功能域 mixin 命名

TransferChain 的 7 个功能域 mixin 集合文件按域更名 _transfer.py,
与 _recognition/_messaging/_interaction/_music 的单域命名约定对齐;
同步更新 transfer.py 导入、9 个测试文件 patch 目标与架构文档。
另含 chain 基类导入排序优化与未使用导入清理(copy/datetime 已随
dispatch 方法迁入 _messaging.py)。
This commit is contained in:
jxxghp
2026-08-16 17:25:49 +08:00
parent 5810f6bc2b
commit 3e417c1ac5
13 changed files with 63 additions and 65 deletions
+7 -9
View File
@@ -1,25 +1,26 @@
from __future__ import annotations from __future__ import annotations
import copy
import inspect import inspect
import pickle import pickle
import traceback import traceback
from abc import ABCMeta from abc import ABCMeta
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Optional, Any, Tuple, List, Set, Union, Dict from typing import Optional, Any, Tuple, List, Set, Union, Dict
from fastapi.concurrency import run_in_threadpool from fastapi.concurrency import run_in_threadpool
from app.runtime.cache import FileCache, AsyncFileCache from app.application.messaging.message import MessageHelper, MessageQueueManager
from app.chain._messaging import MessageProcessingMixin, NotificationMixin
from app.chain._recognition import RecognitionMixin
from app.db.oper.message import MessageOper
from app.domain.context import Context, MediaInfo, SubtitleInfo, TorrentInfo from app.domain.context import Context, MediaInfo, SubtitleInfo, TorrentInfo
from app.runtime.events import EventManager
from app.domain.meta.metabase import MetaBase from app.domain.meta.metabase import MetaBase
from app.foundation.reflection import ObjectUtils
from app.runtime.cache import FileCache, AsyncFileCache
from app.runtime.events import EventManager
from app.runtime.extensions.module_manager import ModuleManager from app.runtime.extensions.module_manager import ModuleManager
from app.runtime.extensions.plugin_manager import PluginManager from app.runtime.extensions.plugin_manager import PluginManager
from app.db.oper.message import MessageOper
from app.application.messaging.message import MessageHelper, MessageQueueManager
from app.runtime.log import logger from app.runtime.log import logger
from app.schemas import ( from app.schemas import (
RateLimitExceededException, RateLimitExceededException,
@@ -41,9 +42,6 @@ from app.schemas.types import (
MediaImageType, MediaImageType,
EventType, EventType,
) )
from app.foundation.reflection import ObjectUtils
from app.chain._messaging import MessageProcessingMixin, NotificationMixin
from app.chain._recognition import RecognitionMixin
class ChainBase(RecognitionMixin, MessageProcessingMixin, NotificationMixin, class ChainBase(RecognitionMixin, MessageProcessingMixin, NotificationMixin,
+1 -1
View File
@@ -57,7 +57,7 @@ from app.schemas.types import (
from app.runtime.reload import ConfigReloadMixin from app.runtime.reload import ConfigReloadMixin
from app.application.transfer import (FailedRetryScheduler, JobManager, from app.application.transfer import (FailedRetryScheduler, JobManager,
TransferQueue, TransferTask, job_lock) TransferQueue, TransferTask, job_lock)
from app.chain._mixins import (EpisodeFormatMixin, FailedRetryMixin, from app.chain._transfer import (EpisodeFormatMixin, FailedRetryMixin,
FileFilterMixin, FileKeyMixin, FileFilterMixin, FileKeyMixin,
HistoryMatchMixin, ManualHistoryMixin, HistoryMatchMixin, ManualHistoryMixin,
ScrapeBatchMixin) ScrapeBatchMixin)
+1 -1
View File
@@ -219,7 +219,7 @@ Underscore-prefixed files in `app/chain/` are feature-domain mixins for
slash-command delegation for `remote_list` / `parse_callback` / slash-command delegation for `remote_list` / `parse_callback` /
`handle_callback_interaction` / `handle_text_interaction`), `_music.py` `handle_callback_interaction` / `handle_text_interaction`), `_music.py`
(`MusicSubscribeMixin`, the music single/album subscribe domain mixed into (`MusicSubscribeMixin`, the music single/album subscribe domain mixed into
`SubscribeChain`) and `_mixins.py` (TransferChain feature mixins). A concrete chain that exposes slash-command `SubscribeChain`) and `_transfer.py` (TransferChain feature mixins). A concrete chain that exposes slash-command
interaction inherits `InteractionChainMixin`, injects its handler class via interaction inherits `InteractionChainMixin`, injects its handler class via
`_interaction_handler_type` and implements only `_interaction_handler`; it must `_interaction_handler_type` and implements only `_interaction_handler`; it must
not re-export application-layer interaction managers. not re-export application-layer interaction managers.
+2 -2
View File
@@ -722,7 +722,7 @@ def test_transfer_chain_recommend_episode_format_passes_helper_data(monkeypatch)
lambda item: [sample], lambda item: [sample],
) )
monkeypatch.setattr( monkeypatch.setattr(
"app.chain._mixins.EpisodeFormatRuleHelper.recommend", "app.chain._transfer.EpisodeFormatRuleHelper.recommend",
lambda self, rules, sample_files: (True, "", helper_data), lambda self, rules, sample_files: (True, "", helper_data),
) )
@@ -777,7 +777,7 @@ def test_transfer_chain_recommend_episode_format_uses_selected_fileitems(monkeyp
lambda: [], lambda: [],
) )
monkeypatch.setattr( monkeypatch.setattr(
"app.chain._mixins.EpisodeFormatRuleHelper.recommend", "app.chain._transfer.EpisodeFormatRuleHelper.recommend",
lambda self, rules, sample_files: (True, "", { lambda self, rules, sample_files: (True, "", {
**helper_data, **helper_data,
"received_samples": [item.name for item in sample_files], "received_samples": [item.name for item in sample_files],
+4 -4
View File
@@ -62,7 +62,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: history_oper, lambda: history_oper,
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: history_oper) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: history_oper)
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -72,7 +72,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -82,7 +82,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.StorageChain", "app.chain.transfer.StorageChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -93,7 +93,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del
or True, or True,
), ),
) )
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace(
exists=lambda current_fileitem: True, exists=lambda current_fileitem: True,
delete_media_file=lambda current_fileitem: deleted.append( delete_media_file=lambda current_fileitem: deleted.append(
("target", current_fileitem.path) ("target", current_fileitem.path)
+3 -3
View File
@@ -68,7 +68,7 @@ def test_music_retry_restores_history_entity_namespace(tmp_path, monkeypatch):
title="叶惠美", title="叶惠美",
) )
monkeypatch.setattr("app.chain.transfer.MediaChain", lambda: media_chain) monkeypatch.setattr("app.chain.transfer.MediaChain", lambda: media_chain)
monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: media_chain) monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: media_chain)
result = TransferChain()._recognize_music_retry_media( result = TransferChain()._recognize_music_retry_media(
history, history,
@@ -538,7 +538,7 @@ def test_success_file_aggregation_is_isolated_between_music_jobs_in_same_directo
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(), lambda: SimpleNamespace(),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace()) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace())
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.add_transfer_success", "app.chain.transfer.add_transfer_success",
lambda **kwargs: SimpleNamespace(id=1), lambda **kwargs: SimpleNamespace(id=1),
@@ -784,7 +784,7 @@ def test_downloader_process_forwards_music_history_type(tmp_path, monkeypatch):
), ),
) )
monkeypatch.setattr("app.chain.transfer.MediaChain", lambda: media_chain) monkeypatch.setattr("app.chain.transfer.MediaChain", lambda: media_chain)
monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: media_chain) monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: media_chain)
monkeypatch.setattr(chain, "do_transfer", Mock(return_value=(True, ""))) monkeypatch.setattr(chain, "do_transfer", Mock(return_value=(True, "")))
monkeypatch.setattr(chain, "run_module", run_module) monkeypatch.setattr(chain, "run_module", run_module)
+1 -1
View File
@@ -7,7 +7,7 @@
""" """
from types import SimpleNamespace from types import SimpleNamespace
import app.chain._mixins as mixins_module import app.chain._transfer as mixins_module
from app.chain.transfer import TransferChain from app.chain.transfer import TransferChain
+2 -2
View File
@@ -150,7 +150,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
"app.chain.transfer.TransferHistoryOper" "app.chain.transfer.TransferHistoryOper"
) as history_oper_cls, patch( ) as history_oper_cls, patch(
# mixin 中按自身模块命名空间解析 TransferHistoryOper,需同步镜像 # mixin 中按自身模块命名空间解析 TransferHistoryOper,需同步镜像
"app.chain._mixins.TransferHistoryOper" "app.chain._transfer.TransferHistoryOper"
) as mixins_history_oper_cls, patch( ) as mixins_history_oper_cls, patch(
"app.chain.transfer.asyncio.run_coroutine_threadsafe", "app.chain.transfer.asyncio.run_coroutine_threadsafe",
side_effect=_close_pending_coro, side_effect=_close_pending_coro,
@@ -224,7 +224,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
"app.chain.transfer.TransferHistoryOper" "app.chain.transfer.TransferHistoryOper"
) as history_oper_cls, patch( ) as history_oper_cls, patch(
# mixin 中按自身模块命名空间解析 TransferHistoryOper,需同步镜像 # mixin 中按自身模块命名空间解析 TransferHistoryOper,需同步镜像
"app.chain._mixins.TransferHistoryOper" "app.chain._transfer.TransferHistoryOper"
) as mixins_history_oper_cls, patch( ) as mixins_history_oper_cls, patch(
"app.application.agent._agent_manager.run_background_prompt", "app.application.agent._agent_manager.run_background_prompt",
side_effect=fake_run_background_prompt, side_effect=fake_run_background_prompt,
+5 -5
View File
@@ -31,7 +31,7 @@ def test_enabled_cleanup_skips_filesystem_detection():
开关开启时应保持旧行为且不产生额外文件系统检测 开关开启时应保持旧行为且不产生额外文件系统检测
""" """
with patch( with patch(
"app.chain._mixins.SystemUtils.is_network_filesystem" "app.chain._transfer.SystemUtils.is_network_filesystem"
) as is_network_filesystem: ) as is_network_filesystem:
should_delete = ( should_delete = (
TransferChain._should_delete_empty_source_directories( TransferChain._should_delete_empty_source_directories(
@@ -50,7 +50,7 @@ def test_disabled_cleanup_keeps_mounted_local_source_directories():
开关关闭时应保留网络或 FUSE 挂载的本地源目录 开关关闭时应保留网络或 FUSE 挂载的本地源目录
""" """
with patch( with patch(
"app.chain._mixins.SystemUtils.is_network_filesystem", "app.chain._transfer.SystemUtils.is_network_filesystem",
return_value=True, return_value=True,
) as is_network_filesystem: ) as is_network_filesystem:
should_delete = ( should_delete = (
@@ -72,7 +72,7 @@ def test_disabled_cleanup_still_deletes_ordinary_local_source_directories():
开关关闭时普通本地文件系统仍应删除空目录 开关关闭时普通本地文件系统仍应删除空目录
""" """
with patch( with patch(
"app.chain._mixins.SystemUtils.is_network_filesystem", "app.chain._transfer.SystemUtils.is_network_filesystem",
return_value=False, return_value=False,
): ):
should_delete = ( should_delete = (
@@ -91,7 +91,7 @@ def test_disabled_cleanup_does_not_change_remote_storage_cleanup():
开关关闭时非本地存储仍应执行原有空目录清理 开关关闭时非本地存储仍应执行原有空目录清理
""" """
with patch( with patch(
"app.chain._mixins.SystemUtils.is_network_filesystem" "app.chain._transfer.SystemUtils.is_network_filesystem"
) as is_network_filesystem: ) as is_network_filesystem:
should_delete = ( should_delete = (
TransferChain._should_delete_empty_source_directories( TransferChain._should_delete_empty_source_directories(
@@ -111,7 +111,7 @@ def test_mounted_filesystem_detection_is_cached_by_source_directory():
""" """
mounted_filesystem_cache = {} mounted_filesystem_cache = {}
with patch( with patch(
"app.chain._mixins.SystemUtils.is_network_filesystem", "app.chain._transfer.SystemUtils.is_network_filesystem",
return_value=True, return_value=True,
) as is_network_filesystem: ) as is_network_filesystem:
for _ in range(2): for _ in range(2):
+6 -6
View File
@@ -107,7 +107,7 @@ def test_conflicting_download_history_recognizes_movie_by_file_meta(monkeypatch)
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None), lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.MediaChain", "app.chain.transfer.MediaChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -118,7 +118,7 @@ def test_conflicting_download_history_recognizes_movie_by_file_meta(monkeypatch)
supplement_tmdb_info=lambda media, _meta: media, supplement_tmdb_info=lambda media, _meta: media,
), ),
) )
monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: SimpleNamespace(
recognize_media=lambda **kwargs: pytest.fail("不应按合集历史 ID 识别"), recognize_media=lambda **kwargs: pytest.fail("不应按合集历史 ID 识别"),
recognize_by_meta=lambda meta, obtain_images: ( recognize_by_meta=lambda meta, obtain_images: (
recognized_meta.append(meta) or fallback_media recognized_meta.append(meta) or fallback_media
@@ -190,16 +190,16 @@ def test_movie_collection_conflict_only_drops_automatic_media(
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr("app.chain.transfer.DownloadHistoryOper", lambda: history_oper) monkeypatch.setattr("app.chain.transfer.DownloadHistoryOper", lambda: history_oper)
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: history_oper) monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: history_oper)
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr("app.chain.transfer.StorageChain", lambda: SimpleNamespace()) monkeypatch.setattr("app.chain.transfer.StorageChain", lambda: SimpleNamespace())
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace()) monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace())
monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda *args, **kwargs: file_meta) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda *args, **kwargs: file_meta)
# 用真 MediaInfo 而非 SimpleNamespace:它会被装进 TransferTask.mediainfo # 用真 MediaInfo 而非 SimpleNamespace:它会被装进 TransferTask.mediainfo
+29 -29
View File
@@ -142,7 +142,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch):
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -152,7 +152,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch):
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -162,7 +162,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch):
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr("app.chain.transfer.MetaInfoPath", fake_meta_info_path) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", fake_meta_info_path)
state, errmsg = TransferChain.do_transfer( state, errmsg = TransferChain.do_transfer(
@@ -239,7 +239,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch):
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -249,7 +249,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch):
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -259,7 +259,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch):
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.StorageChain", "app.chain.transfer.StorageChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -270,7 +270,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch):
], ],
), ),
) )
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace(
get_parent_item=lambda fileitem: parent_fileitem, get_parent_item=lambda fileitem: parent_fileitem,
list_files=lambda fileitem, recursion=False: [ list_files=lambda fileitem, recursion=False: [
main_fileitem, main_fileitem,
@@ -354,7 +354,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -364,7 +364,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -374,7 +374,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.StorageChain", "app.chain.transfer.StorageChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -382,7 +382,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch
list_files=fake_list_files, list_files=fake_list_files,
), ),
) )
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace(
get_parent_item=lambda fileitem: parent_fileitem, get_parent_item=lambda fileitem: parent_fileitem,
list_files=fake_list_files, list_files=fake_list_files,
)) ))
@@ -452,7 +452,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch):
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -462,7 +462,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch):
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -472,7 +472,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch):
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1)) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1))
state, errmsg = TransferChain.do_transfer( state, errmsg = TransferChain.do_transfer(
@@ -539,7 +539,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch):
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -549,7 +549,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch):
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -559,7 +559,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch):
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1)) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1))
state, errmsg = TransferChain.do_transfer( state, errmsg = TransferChain.do_transfer(
@@ -635,7 +635,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -645,7 +645,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -655,7 +655,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.StorageChain", "app.chain.transfer.StorageChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -666,7 +666,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat
], ],
), ),
) )
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace(
get_parent_item=lambda fileitem: parent_fileitem, get_parent_item=lambda fileitem: parent_fileitem,
list_files=lambda fileitem, recursion=False: [ list_files=lambda fileitem, recursion=False: [
main_fileitem, main_fileitem,
@@ -731,7 +731,7 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.DownloadHistoryOper", "app.chain.transfer.DownloadHistoryOper",
lambda: SimpleNamespace( lambda: SimpleNamespace(
@@ -741,7 +741,7 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp
get_by_path=lambda path: None, get_by_path=lambda path: None,
), ),
) )
monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace(
get_by_hash=lambda download_hash: None, get_by_hash=lambda download_hash: None,
get_file_by_fullpath=lambda fullpath: None, get_file_by_fullpath=lambda fullpath: None,
get_files_by_savepath=lambda savepath: [], get_files_by_savepath=lambda savepath: [],
@@ -751,14 +751,14 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.StorageChain", "app.chain.transfer.StorageChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True,
), ),
) )
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace(
delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True,
)) ))
monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1)) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1))
@@ -799,14 +799,14 @@ def test_cleanup_dest_fileitem_is_kept_when_episode_format_matches_nothing(monke
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.StorageChain", "app.chain.transfer.StorageChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True,
), ),
) )
monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace(
delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True,
)) ))
@@ -841,7 +841,7 @@ def test_episode_format_matched_but_filtered_by_size_returns_failure(monkeypatch
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
state, errmsg = TransferChain.do_transfer( state, errmsg = TransferChain.do_transfer(
chain, chain,
@@ -882,7 +882,7 @@ def test_candidate_collection_checks_continue_callback(monkeypatch):
"app.chain.transfer.SystemConfigOper", "app.chain.transfer.SystemConfigOper",
lambda: SimpleNamespace(get=lambda key: None), lambda: SimpleNamespace(get=lambda key: None),
) )
monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None))
state, errmsg = TransferChain.do_transfer( state, errmsg = TransferChain.do_transfer(
chain, chain,
+2 -2
View File
@@ -79,14 +79,14 @@ def test_transfer_stops_when_automatic_category_has_no_tmdb_result(monkeypatch)
"app.chain.transfer.TransferHistoryOper", "app.chain.transfer.TransferHistoryOper",
lambda: SimpleNamespace(), lambda: SimpleNamespace(),
) )
monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace()) monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace())
monkeypatch.setattr( monkeypatch.setattr(
"app.chain.transfer.MediaChain", "app.chain.transfer.MediaChain",
lambda: SimpleNamespace( lambda: SimpleNamespace(
supplement_tmdb_info=lambda media, _meta: media, supplement_tmdb_info=lambda media, _meta: media,
), ),
) )
monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: SimpleNamespace( monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: SimpleNamespace(
supplement_tmdb_info=lambda media, _meta: media, supplement_tmdb_info=lambda media, _meta: media,
)) ))
task = TransferTask( task = TransferTask(