mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-01 04:47:03 +08:00
233 lines
8.2 KiB
Python
233 lines
8.2 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding:utf-8 -*-
|
|
"""
|
|
验证蓝光目录识别、转移入口与元数据刮削。
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from typing import Optional
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from app.application.history import TransferHistorySnapshot
|
|
from app.chain.scraping import ScrapingChain
|
|
from app.chain.storage import StorageChain
|
|
from app.chain.transfer.facade import TransferChain
|
|
from app.domain.context import MediaInfo
|
|
from app.domain.metainfo import MetaInfoPath
|
|
from app.runtime.events import Event
|
|
from app.runtime.log import logger
|
|
from app.schemas.file import FileItem
|
|
from app.schemas.types import EventType
|
|
from tests.cases.files import bluray_files
|
|
|
|
|
|
@pytest.fixture
|
|
def bluray_context():
|
|
"""
|
|
构造独立的蓝光文件树与转移历史,供路径归并和刮削断言使用。
|
|
|
|
:return: 含根目录、路径索引与历史记录的测试上下文
|
|
"""
|
|
bluray_context = SimpleNamespace()
|
|
bluray_context.history = []
|
|
bluray_context.root = FileItem(path="/", name="", type="dir", extension="", size=0)
|
|
bluray_context.items = {bluray_context.root.path: bluray_context.root}
|
|
|
|
def __build_child(parent: FileItem, files: list[tuple[str, list | int]]):
|
|
"""
|
|
递归构造目录子项并建立路径索引,不创建真实文件。
|
|
"""
|
|
parent.children = []
|
|
for name, children in files:
|
|
sep = "" if parent.path.endswith("/") else "/"
|
|
file_item = FileItem(
|
|
path=f"{parent.path}{sep}{name}",
|
|
name=name,
|
|
extension=Path(name).suffix[1:],
|
|
basename=Path(name).stem,
|
|
type="file" if isinstance(children, int) else "dir",
|
|
size=children if isinstance(children, int) else 0,
|
|
)
|
|
parent.children.append(file_item)
|
|
bluray_context.items[file_item.path] = file_item
|
|
if isinstance(children, list):
|
|
__build_child(file_item, children)
|
|
|
|
__build_child(bluray_context.root, bluray_files)
|
|
|
|
return bluray_context
|
|
|
|
|
|
def _test_do_transfer(bluray_context):
|
|
"""
|
|
验证各级蓝光路径归并到原盘目录,普通视频保留文件路径。
|
|
"""
|
|
|
|
def __test_do_transfer(path: str):
|
|
bluray_context.history.clear()
|
|
TransferChain().do_transfer(
|
|
force=False,
|
|
background=False,
|
|
fileitem=StorageChain().get_file_item(None, Path(path)),
|
|
)
|
|
return bluray_context.history
|
|
|
|
assert [
|
|
"/FOLDER/Digimon/Digimon BluRay (2055)",
|
|
"/FOLDER/Digimon/Digimon BluRay (2099)",
|
|
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4",
|
|
] == __test_do_transfer("/FOLDER/Digimon")
|
|
|
|
assert ["/FOLDER/Digimon/Digimon BluRay (2055)"] == __test_do_transfer("/FOLDER/Digimon/Digimon BluRay (2055)")
|
|
|
|
assert ["/FOLDER/Digimon/Digimon BluRay (2055)"] == __test_do_transfer("/FOLDER/Digimon/Digimon BluRay (2055)/BDMV")
|
|
|
|
assert ["/FOLDER/Digimon/Digimon BluRay (2055)"] == __test_do_transfer(
|
|
"/FOLDER/Digimon/Digimon BluRay (2055)/BDMV/STREAM"
|
|
)
|
|
|
|
assert ["/FOLDER/Digimon/Digimon BluRay (2055)"] == __test_do_transfer(
|
|
"/FOLDER/Digimon/Digimon BluRay (2055)/BDMV/STREAM/00001.m2ts"
|
|
)
|
|
|
|
assert ["/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4"] == __test_do_transfer("/FOLDER/Digimon/Digimon (2199)")
|
|
|
|
assert ["/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4"] == __test_do_transfer(
|
|
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4"
|
|
)
|
|
|
|
assert ["/FOLDER/Pokemon.2029.mp4"] == __test_do_transfer("/FOLDER/Pokemon.2029.mp4")
|
|
|
|
assert [
|
|
"/FOLDER/Digimon/Digimon BluRay (2055)",
|
|
"/FOLDER/Digimon/Digimon BluRay (2099)",
|
|
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4",
|
|
"/FOLDER/Pokemon BluRay (2016)",
|
|
"/FOLDER/Pokemon BluRay (2021)",
|
|
"/FOLDER/Pokemon (2028)/Pokemon.2028.mkv",
|
|
"/FOLDER/Pokemon.2029.mp4",
|
|
"/FOLDER/Pokemon.2039.mp4",
|
|
"/FOLDER/Pokemon (2031)/Pokemon (2031).mp4",
|
|
] == __test_do_transfer("/")
|
|
|
|
|
|
def _test_scrape_metadata(mock_metadata_nfo):
|
|
"""
|
|
验证手动和事件刮削对蓝光原盘及普通电影生成正确数量的元数据。
|
|
"""
|
|
|
|
def __test_scrape_metadata(path: str, excepted_nfo_count: int = 1):
|
|
"""
|
|
分别验证手动和自动刮削的元数据生成次数。
|
|
|
|
:param path: 模拟文件树中的电影或目录路径
|
|
:param excepted_nfo_count: 每种刮削入口应生成的元数据数量
|
|
"""
|
|
fileitem = StorageChain().get_file_item(None, Path(path))
|
|
meta = MetaInfoPath(Path(fileitem.path))
|
|
mediainfo = MediaInfo(tmdb_info={"id": 1, "title": "Test"})
|
|
|
|
# 测试手动刮削
|
|
logger.debug(f"测试手动刮削 {path}")
|
|
mock_metadata_nfo.reset_mock()
|
|
ScrapingChain().scrape_metadata(fileitem=fileitem, meta=meta, mediainfo=mediainfo, overwrite=True)
|
|
# 确保调用了指定次数的metadata_nfo
|
|
assert mock_metadata_nfo.call_count == excepted_nfo_count
|
|
|
|
# 测试自动刮削
|
|
logger.debug(f"测试自动刮削 {path}")
|
|
mock_metadata_nfo.reset_mock()
|
|
ScrapingChain().scrape_metadata_event(
|
|
Event(
|
|
event_type=EventType.MetadataScrape,
|
|
event_data={
|
|
"meta": meta,
|
|
"mediainfo": mediainfo,
|
|
"fileitem": fileitem,
|
|
"file_list": [fileitem.path],
|
|
"overwrite": False,
|
|
},
|
|
)
|
|
)
|
|
# 调用了指定次数的metadata_nfo
|
|
assert mock_metadata_nfo.call_count == excepted_nfo_count
|
|
|
|
# 刮削原盘目录
|
|
__test_scrape_metadata("/FOLDER/Digimon/Digimon BluRay (2099)")
|
|
# 刮削电影文件
|
|
__test_scrape_metadata("/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4")
|
|
# 刮削电影目录
|
|
__test_scrape_metadata("/FOLDER", excepted_nfo_count=2)
|
|
|
|
|
|
# 避免真实图片请求
|
|
@patch("app.chain.scraping.ScrapingChain.metadata_img", return_value=None)
|
|
@patch("app.chain.transfer.records.resolve_history")
|
|
@patch("app.chain.storage.StorageChain.list_files")
|
|
@patch("app.chain.storage.StorageChain.get_parent_item")
|
|
@patch("app.chain.storage.StorageChain.get_file_item")
|
|
def test_bluray_transfer_and_scraping(
|
|
mock_get_file_item,
|
|
mock_get_parent_item,
|
|
mock_list_files,
|
|
mock_resolve_history,
|
|
mock_metadata_img,
|
|
bluray_context,
|
|
) -> None:
|
|
"""
|
|
验证模拟蓝光文件树的转移与刮削过程,不访问真实文件和图片服务。
|
|
"""
|
|
|
|
def get_file_item(storage: str, path: Path):
|
|
"""
|
|
从内存文件树返回路径对应的文件项。
|
|
"""
|
|
path_posix = path.as_posix()
|
|
return bluray_context.items.get(path_posix)
|
|
|
|
def get_parent_item(fileitem: FileItem):
|
|
"""
|
|
返回内存文件树中的父目录。
|
|
"""
|
|
return get_file_item(None, Path(fileitem.path).parent)
|
|
|
|
def list_files(fileitem: FileItem, recursion: bool = False):
|
|
"""
|
|
列出模拟目录子项,递归模式按路径前缀收集所有后代。
|
|
"""
|
|
if fileitem.type != "dir":
|
|
return None
|
|
if recursion:
|
|
result = []
|
|
file_path = f"{fileitem.path}/"
|
|
for path, item in bluray_context.items.items():
|
|
if path.startswith(file_path):
|
|
result.append(item)
|
|
return result
|
|
else:
|
|
return fileitem.children
|
|
|
|
def resolve_history(
|
|
src: str,
|
|
storage: Optional[str] = None,
|
|
transfer_history_oper=None,
|
|
):
|
|
"""
|
|
记录被解析的源路径并返回成功历史,避免读取真实数据库。
|
|
"""
|
|
bluray_context.history.append(src)
|
|
return TransferHistorySnapshot(id=1, status=True)
|
|
|
|
mock_get_file_item.side_effect = get_file_item
|
|
mock_get_parent_item.side_effect = get_parent_item
|
|
mock_list_files.side_effect = list_files
|
|
mock_resolve_history.side_effect = resolve_history
|
|
|
|
_test_do_transfer(bluray_context)
|
|
|
|
with patch("app.chain.scraping.ScrapingChain.metadata_nfo", return_value=None) as mock:
|
|
_test_scrape_metadata(mock_metadata_nfo=mock)
|