mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-09 15:34:31 +08:00
194 lines
6.5 KiB
Python
194 lines
6.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
from types import SimpleNamespace
|
||
|
||
from app.chain.transfer import JobManager, TransferChain
|
||
from app.core.config import settings
|
||
from app.schemas import FileItem, TransferTask
|
||
from app.schemas.types import MediaType
|
||
|
||
|
||
class _FakeMeta:
|
||
"""构造最小可用的剧集元数据。"""
|
||
|
||
def __init__(self, episode: int, season: int = 1):
|
||
"""初始化剧集编号相关字段。"""
|
||
self.name = "Test Show"
|
||
self.title = f"Test Show S{season:02d}E{episode:02d}"
|
||
self.year = "2026"
|
||
self.type = MediaType.TV
|
||
self.begin_season = season
|
||
self.end_season = None
|
||
self.total_season = 1
|
||
self.begin_episode = episode
|
||
self.end_episode = None
|
||
self.total_episode = 1
|
||
self.episode_list = [episode]
|
||
self.season_episode = f"S{season:02d}E{episode:02d}"
|
||
self.part = None
|
||
|
||
@property
|
||
def season(self):
|
||
"""返回季字符串。"""
|
||
return f"S{self.begin_season:02d}"
|
||
|
||
@property
|
||
def episode(self):
|
||
"""返回集字符串。"""
|
||
return f"E{self.begin_episode:02d}"
|
||
|
||
def to_dict(self):
|
||
"""返回元数据字典。"""
|
||
return {
|
||
"title": self.title,
|
||
"name": self.name,
|
||
"year": self.year,
|
||
"type": self.type.value,
|
||
"begin_season": self.begin_season,
|
||
"end_season": self.end_season,
|
||
"total_season": self.total_season,
|
||
"begin_episode": self.begin_episode,
|
||
"end_episode": self.end_episode,
|
||
"total_episode": self.total_episode,
|
||
"season_episode": self.season_episode,
|
||
"episode_list": self.episode_list,
|
||
"part": self.part,
|
||
}
|
||
|
||
|
||
def _make_chain() -> TransferChain:
|
||
"""构造跳过初始化的 TransferChain,仅带作业视图。"""
|
||
chain = object.__new__(TransferChain)
|
||
chain.jobview = JobManager()
|
||
chain._media_exts = settings.RMT_MEDIAEXT
|
||
chain._subtitle_exts = settings.RMT_SUBEXT
|
||
chain._audio_exts = settings.RMT_AUDIOEXT
|
||
chain._allowed_exts = (
|
||
chain._media_exts + chain._audio_exts + chain._subtitle_exts
|
||
)
|
||
chain._success_target_files = {}
|
||
chain._scrape_batches = {}
|
||
return chain
|
||
|
||
|
||
def _make_task(episode: int, download_hash: str, downloader: str) -> TransferTask:
|
||
"""构造带下载器信息的整理任务。"""
|
||
name = f"Test.Show.S01E{episode:02d}.mkv"
|
||
task = TransferTask(
|
||
fileitem=FileItem(
|
||
storage="local",
|
||
path=f"/downloads/Test Show/{name}",
|
||
type="file",
|
||
name=name,
|
||
basename=name.removesuffix(".mkv"),
|
||
extension="mkv",
|
||
size=1024,
|
||
),
|
||
meta=_FakeMeta(episode),
|
||
)
|
||
task.download_hash = download_hash
|
||
task.downloader = downloader
|
||
return task
|
||
|
||
|
||
def _mark(chain: TransferChain, download_hash: str, downloader: str):
|
||
"""调用私有的打标签方法。"""
|
||
chain._TransferChain__mark_torrent_completed_if_done(download_hash, downloader)
|
||
|
||
|
||
def _finish_task(chain: TransferChain, task: TransferTask):
|
||
"""将任务登记并流转到完成状态。"""
|
||
assert chain.jobview.add_task(task)
|
||
chain.jobview.running_task(task)
|
||
chain.jobview.finish_task(task)
|
||
|
||
|
||
def test_mark_skips_tag_when_torrent_still_downloading():
|
||
"""种子未下载完成时(多集种子先完成单集),不得设置已整理标签(#6009)。"""
|
||
chain = _make_chain()
|
||
completed = []
|
||
chain.transfer_completed = lambda **kwargs: completed.append(kwargs)
|
||
chain.list_torrents = lambda **kwargs: [SimpleNamespace(progress=52.3)]
|
||
_finish_task(chain, _make_task(1, "hash1", "qbittorrent"))
|
||
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
|
||
assert completed == []
|
||
|
||
|
||
def test_mark_tags_when_torrent_completed():
|
||
"""种子已下载完成且任务全部结束时,正常设置已整理标签。"""
|
||
chain = _make_chain()
|
||
completed = []
|
||
chain.transfer_completed = lambda **kwargs: completed.append(kwargs)
|
||
chain.list_torrents = lambda **kwargs: [SimpleNamespace(progress=100)]
|
||
_finish_task(chain, _make_task(1, "hash1", "qbittorrent"))
|
||
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
|
||
assert completed == [{"hashs": "hash1", "downloader": "qbittorrent"}]
|
||
|
||
|
||
def test_mark_short_circuits_downloader_query_when_jobview_not_done():
|
||
"""作业视图还有未结束任务时,不应产生任何下载器查询。"""
|
||
chain = _make_chain()
|
||
queries = []
|
||
chain.transfer_completed = lambda **kwargs: None
|
||
chain.list_torrents = lambda **kwargs: queries.append(kwargs) or []
|
||
task = _make_task(1, "hash1", "qbittorrent")
|
||
assert chain.jobview.add_task(task)
|
||
chain.jobview.running_task(task)
|
||
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
|
||
assert queries == []
|
||
|
||
|
||
def test_mark_skips_tag_when_torrent_not_found():
|
||
"""下载器中查不到种子时不打标签,留待定时轮询兜底。"""
|
||
chain = _make_chain()
|
||
completed = []
|
||
chain.transfer_completed = lambda **kwargs: completed.append(kwargs)
|
||
chain.list_torrents = lambda **kwargs: []
|
||
_finish_task(chain, _make_task(1, "hash1", "qbittorrent"))
|
||
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
|
||
assert completed == []
|
||
|
||
|
||
def test_mark_skips_tag_when_list_torrents_raises():
|
||
"""查询下载器异常时不打标签且不向上抛出。"""
|
||
chain = _make_chain()
|
||
completed = []
|
||
chain.transfer_completed = lambda **kwargs: completed.append(kwargs)
|
||
|
||
def _raise(**_kwargs):
|
||
raise RuntimeError("downloader unreachable")
|
||
|
||
chain.list_torrents = _raise
|
||
_finish_task(chain, _make_task(1, "hash1", "qbittorrent"))
|
||
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
|
||
assert completed == []
|
||
|
||
|
||
def test_multi_episode_torrent_tags_only_after_last_episode():
|
||
"""#6009 回归:E01 先整理完不打标签,种子整体下载完成后才打标签。"""
|
||
chain = _make_chain()
|
||
completed = []
|
||
chain.transfer_completed = lambda **kwargs: completed.append(kwargs)
|
||
progress = {"value": 60}
|
||
chain.list_torrents = lambda **kwargs: [SimpleNamespace(progress=progress["value"])]
|
||
|
||
# E01 下载完成并整理,此时种子整体仍在下载
|
||
_finish_task(chain, _make_task(1, "hash1", "qbittorrent"))
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
assert completed == []
|
||
|
||
# E02 下载完成并整理,种子整体到 100%
|
||
progress["value"] = 100
|
||
_finish_task(chain, _make_task(2, "hash1", "qbittorrent"))
|
||
_mark(chain, "hash1", "qbittorrent")
|
||
assert completed == [{"hashs": "hash1", "downloader": "qbittorrent"}]
|