diff --git a/app/chain/scraping.py b/app/chain/scraping.py index 9fae5bdb2..0faec14af 100644 --- a/app/chain/scraping.py +++ b/app/chain/scraping.py @@ -788,6 +788,17 @@ class ScrapingChain(ChainBase, ConfigReloadMixin, metaclass=Singleton): all_dirs.add(current_path) current_path = current_path.parent + if ( + getattr(mediainfo, "type", None) == MediaType.TV + and root_path.parent != root_path + and ( + root_path.name in self.runtime_config.season_zero_names + or MetaInfo(root_path.name).begin_season is not None + ) + ): + # 整理事件可能以季目录为根,补回剧集根目录才能触发电视剧分支。 + all_dirs.add(root_path.parent) + logger.debug(f"共收集到 {len(all_dirs)} 个目录") # 2. 初始化一遍子目录,但不处理文件 diff --git a/tests/test_scrape_metadata_event.py b/tests/test_scrape_metadata_event.py new file mode 100644 index 000000000..42c9ddeb5 --- /dev/null +++ b/tests/test_scrape_metadata_event.py @@ -0,0 +1,75 @@ +from collections.abc import Generator +from pathlib import Path +from unittest.mock import MagicMock + +import pytest + +from app import schemas +from app.chain.scraping import ScrapingChain +from app.domain.context import MediaInfo +from app.domain.metainfo import MetaInfo +from app.runtime.events import Event +from app.schemas.types import EventType, MediaType + + +@pytest.fixture +def scraping_chain() -> Generator[ScrapingChain, None, None]: + """构造隔离的刮削链,避免单例状态影响事件测试。""" + key = (ScrapingChain, (), frozenset()) + previous = ScrapingChain._instances.pop(key, None) + chain = ScrapingChain() + chain.storagechain = MagicMock() + try: + yield chain + finally: + ScrapingChain._instances.pop(key, None) + if previous is not None: + ScrapingChain._instances[key] = previous + + +def test_tv_season_scrape_event_includes_series_root( + scraping_chain: ScrapingChain, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """电视剧整理事件以季目录为根时也应刮削剧集根目录。""" + season_item = schemas.FileItem( + path="/tv/Show/Season 1", + name="Season 1", + type="dir", + storage="local", + ) + episode_path = "/tv/Show/Season 1/S01E01.mkv" + scraping_chain.storagechain.get_item.return_value = season_item + scraping_chain.storagechain.is_bluray_folder.return_value = False + + def get_file_item(storage: str, path: Path) -> schemas.FileItem: + """构造事件目录收集所需的文件项。""" + item_path = Path(path) + return schemas.FileItem( + storage=storage, + path=item_path.as_posix(), + name=item_path.name, + type="file" if item_path.suffix else "dir", + ) + + scraping_chain.storagechain.get_file_item.side_effect = get_file_item + scrape_metadata = MagicMock() + monkeypatch.setattr(scraping_chain, "scrape_metadata", scrape_metadata) + + scraping_chain.scrape_metadata_event( + Event( + event_type=EventType.MetadataScrape, + event_data={ + "fileitem": season_item, + "file_list": [episode_path], + "meta": MetaInfo("Show S01E01"), + "mediainfo": MediaInfo(type=MediaType.TV), + }, + ) + ) + + assert [call.kwargs["fileitem"].path for call in scrape_metadata.call_args_list] == [ + "/tv/Show", + "/tv/Show/Season 1", + episode_path, + ]