mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-06 07:09:09 +08:00
fix(workflow): 修复添加下载的媒体上下文类型 #6931
This commit is contained in:
@@ -1,12 +1,20 @@
|
||||
from dataclasses import fields
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from app.chain.download import DownloadChain
|
||||
from app.chain.media import MediaChain
|
||||
from app.domain.context import Context as DownloadContext
|
||||
from app.domain.context import MediaInfo as DownloadMediaInfo
|
||||
from app.domain.context import MusicInfo as DownloadMusicInfo
|
||||
from app.domain.context import TorrentInfo as DownloadTorrentInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.context import Context as WorkflowTorrentContext
|
||||
from app.schemas.types import MediaType
|
||||
from app.schemas.workflow import ActionContext, ActionParams, DownloadTask
|
||||
from app.workflow.actions import BaseAction
|
||||
@@ -34,6 +42,7 @@ class AddDownloadAction(BaseAction):
|
||||
}
|
||||
|
||||
def __init__(self, action_id: str):
|
||||
"""初始化下载结果集合和动作错误状态。"""
|
||||
super().__init__(action_id)
|
||||
self._added_downloads = []
|
||||
self._has_error = False
|
||||
@@ -44,11 +53,12 @@ class AddDownloadAction(BaseAction):
|
||||
|
||||
@property
|
||||
def success(self) -> bool:
|
||||
"""返回本次动作是否未遇到下载失败。"""
|
||||
return not self._has_error
|
||||
|
||||
def execute(self, workflow_id: int, params: dict, context: ActionContext) -> ActionContext:
|
||||
"""
|
||||
将上下文中的torrents添加到下载任务中
|
||||
将工作流资源还原为下载链所需的领域上下文并添加下载任务。
|
||||
"""
|
||||
params = AddDownloadParams(**params)
|
||||
_started = False
|
||||
@@ -71,10 +81,11 @@ class AddDownloadAction(BaseAction):
|
||||
self._has_error = True
|
||||
logger.warning(f"{t.torrent_info.title} 未识别到媒体信息,无法下载")
|
||||
continue
|
||||
download_context = self._to_domain_context(t)
|
||||
if params.only_lack:
|
||||
exists_info = DownloadChain().media_exists(t.media_info)
|
||||
exists_info = DownloadChain().media_exists(download_context.media_info)
|
||||
if exists_info:
|
||||
if t.media_info.type == MediaType.MOVIE:
|
||||
if download_context.media_info.type == MediaType.MOVIE:
|
||||
# 电影
|
||||
logger.warning(f"{t.torrent_info.title} 媒体库中已存在,跳过")
|
||||
continue
|
||||
@@ -99,7 +110,7 @@ class AddDownloadAction(BaseAction):
|
||||
continue
|
||||
|
||||
_started = True
|
||||
did = DownloadChain().download_single(context=t,
|
||||
did = DownloadChain().download_single(context=download_context,
|
||||
downloader=params.downloader,
|
||||
save_path=params.save_path,
|
||||
label=params.labels)
|
||||
@@ -118,3 +129,67 @@ class AddDownloadAction(BaseAction):
|
||||
|
||||
self.job_done(f"已添加 {len(self._added_downloads)} 个下载任务")
|
||||
return context
|
||||
|
||||
@staticmethod
|
||||
def _to_domain_context(context: WorkflowTorrentContext) -> DownloadContext:
|
||||
"""把工作流传输模型转换为下载链使用的领域上下文和媒体类型。"""
|
||||
if context.media_info is None or context.torrent_info is None:
|
||||
raise ValueError("工作流下载上下文缺少媒体或种子信息")
|
||||
|
||||
media_info = context.media_info
|
||||
if isinstance(media_info, (DownloadMediaInfo, DownloadMusicInfo)):
|
||||
domain_media_info = media_info
|
||||
else:
|
||||
media_data = media_info.model_dump()
|
||||
if media_data.get("type") == MediaType.MUSIC.value:
|
||||
domain_media_info = DownloadMusicInfo.from_dict(media_data)
|
||||
else:
|
||||
domain_media_info = DownloadMediaInfo()
|
||||
domain_media_info.from_dict(media_data)
|
||||
|
||||
torrent_info = context.torrent_info
|
||||
if isinstance(torrent_info, DownloadTorrentInfo):
|
||||
domain_torrent_info = torrent_info
|
||||
else:
|
||||
domain_torrent_info = DownloadTorrentInfo()
|
||||
domain_torrent_info.from_dict(torrent_info.model_dump())
|
||||
|
||||
meta_info = context.meta_info
|
||||
if isinstance(meta_info, MetaBase):
|
||||
domain_meta_info = meta_info
|
||||
elif meta_info is None:
|
||||
domain_meta_info = None
|
||||
else:
|
||||
meta_data = meta_info.model_dump()
|
||||
meta_type = meta_data.get("type")
|
||||
if meta_type == MediaType.MUSIC.value:
|
||||
domain_meta_info = MetaMusic.from_dict(meta_data)
|
||||
else:
|
||||
domain_meta_info = MetaInfo(
|
||||
title=meta_data.get("org_string") or meta_data.get("title") or torrent_info.title or "",
|
||||
subtitle=meta_data.get("subtitle"),
|
||||
mtype=MediaType(meta_type) if meta_type else None,
|
||||
)
|
||||
for item in fields(domain_meta_info):
|
||||
if item.name not in meta_data:
|
||||
continue
|
||||
value = meta_data[item.name]
|
||||
if item.name == "type" and value:
|
||||
value = MediaType(value)
|
||||
setattr(domain_meta_info, item.name, value)
|
||||
if meta_data.get("name"):
|
||||
domain_meta_info.name = meta_data["name"]
|
||||
|
||||
return DownloadContext(
|
||||
meta_info=domain_meta_info,
|
||||
media_info=domain_media_info,
|
||||
torrent_info=domain_torrent_info,
|
||||
resource_source=context.resource_source or "unknown",
|
||||
match_source=context.match_source or "unknown",
|
||||
candidate_recognized=bool(context.candidate_recognized),
|
||||
media_info_is_target=bool(context.media_info_is_target),
|
||||
match_status=context.match_status,
|
||||
match_reason=context.match_reason,
|
||||
confirmed_full_coverage=bool(context.confirmed_full_coverage),
|
||||
music_track_keys=list(context.music_track_keys) if context.music_track_keys else None,
|
||||
)
|
||||
|
||||
@@ -252,8 +252,10 @@ def test_workflow_manager_list_actions_exposes_contract():
|
||||
|
||||
|
||||
def test_add_download_only_lack_handles_schema_metainfo(monkeypatch):
|
||||
"""添加下载动作开启仅下载缺失资源时应支持 Schema MetaInfo 的 season_list 属性并正确过滤已存在剧集。"""
|
||||
"""仅下载缺失资源时应兼容工作流 MetaInfo 并向下载链传递领域上下文。"""
|
||||
import app.workflow.actions.add_download as add_download_module
|
||||
from app.domain.context import MediaInfo as DomainMediaInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.schemas.context import (
|
||||
Context as SchemaContext,
|
||||
)
|
||||
@@ -272,11 +274,21 @@ def test_add_download_only_lack_handles_schema_metainfo(monkeypatch):
|
||||
downloaded = []
|
||||
|
||||
class FakeDownloadChain:
|
||||
"""模拟媒体库检查和下载提交,并验证工作流对象已转换为领域类型。"""
|
||||
|
||||
def media_exists(self, mediainfo):
|
||||
"""返回已存在剧集,并检查传入媒体使用领域类型。"""
|
||||
assert isinstance(mediainfo, DomainMediaInfo)
|
||||
assert mediainfo.type is MediaType.TV
|
||||
# 模拟媒体库已存在第 1 季的第 1、2 集
|
||||
return SimpleNamespace(seasons={1: [1, 2]})
|
||||
|
||||
def download_single(self, context=None, **kwargs):
|
||||
"""记录提交的种子名称,并验证下载链拿到领域上下文。"""
|
||||
assert isinstance(context, Context)
|
||||
assert isinstance(context.media_info, DomainMediaInfo)
|
||||
assert isinstance(context.meta_info, MetaBase)
|
||||
assert isinstance(context.torrent_info, TorrentInfo)
|
||||
downloaded.append(context.torrent_info.title)
|
||||
return f"hash-{context.torrent_info.title}"
|
||||
|
||||
@@ -325,3 +337,29 @@ def test_add_download_only_lack_handles_schema_metainfo(monkeypatch):
|
||||
assert downloaded == ["Show S01E03", "Show Ep04"]
|
||||
assert len(result.downloads) == 2
|
||||
assert [d.download_id for d in result.downloads] == ["hash-Show S01E03", "hash-Show Ep04"]
|
||||
|
||||
|
||||
def test_add_download_converts_workflow_music_context_to_domain_types():
|
||||
"""添加下载动作应将音乐工作流模型恢复为下载链使用的领域对象。"""
|
||||
from app.domain.context import MusicInfo as DomainMusicInfo
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.schemas.context import Context as SchemaContext
|
||||
from app.schemas.context import TorrentInfo as SchemaTorrentInfo
|
||||
from app.schemas.music import MusicInfo as SchemaMusicInfo
|
||||
from app.schemas.music import MusicMeta
|
||||
from app.schemas.types import MediaType
|
||||
from app.workflow.actions.add_download import AddDownloadAction
|
||||
|
||||
context = SchemaContext(
|
||||
meta_info=MusicMeta(title="Track", artists=["Artist"]),
|
||||
media_info=SchemaMusicInfo(title="Track", artists=["Artist"]),
|
||||
torrent_info=SchemaTorrentInfo(title="Track", site=1),
|
||||
)
|
||||
|
||||
result = AddDownloadAction._to_domain_context(context)
|
||||
|
||||
assert isinstance(result, Context)
|
||||
assert isinstance(result.meta_info, MetaMusic)
|
||||
assert isinstance(result.media_info, DomainMusicInfo)
|
||||
assert result.media_info.type is MediaType.MUSIC
|
||||
assert isinstance(result.torrent_info, TorrentInfo)
|
||||
|
||||
Reference in New Issue
Block a user