mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-09 09:26:55 +08:00
fix(download): satisfy source classification gates
This commit is contained in:
@@ -4,7 +4,7 @@ import anyio
|
|||||||
from fastapi import Body, Depends
|
from fastapi import Body, Depends
|
||||||
|
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.api.dependencies.auth import get_current_active_user, get_current_active_manage_user
|
from app.api.dependencies.auth import get_current_active_manage_user, get_current_active_user
|
||||||
from app.api.dependencies.site import get_site_sync_query_service
|
from app.api.dependencies.site import get_site_sync_query_service
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.api.response import (
|
from app.api.response import (
|
||||||
@@ -401,7 +401,7 @@ async def update_task(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post( # type: ignore[misc]
|
||||||
"/{hashString}/classify-source",
|
"/{hashString}/classify-source",
|
||||||
summary="识别并归类已有下载任务",
|
summary="识别并归类已有下载任务",
|
||||||
response_model=_SchemaResponse[_SchemaDownloadSourceClassificationData],
|
response_model=_SchemaResponse[_SchemaDownloadSourceClassificationData],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional, cast
|
||||||
|
|
||||||
from app.application.classification.reference import (
|
from app.application.classification.reference import (
|
||||||
apply_persisted_classification_snapshot,
|
apply_persisted_classification_snapshot,
|
||||||
@@ -40,8 +40,13 @@ def _history_media(history: DownloadHistorySnapshot) -> MediaInfo | MusicInfo:
|
|||||||
if media_type == MediaType.MUSIC:
|
if media_type == MediaType.MUSIC:
|
||||||
note = history.note
|
note = history.note
|
||||||
music_note = note.get("music") if isinstance(note, dict) else None
|
music_note = note.get("music") if isinstance(note, dict) else None
|
||||||
media_payload = music_note.get("media") if isinstance(music_note, dict) else None
|
if isinstance(music_note, dict):
|
||||||
if isinstance(media_payload, dict) and music_note.get("version") == 1:
|
media_payload = music_note.get("media")
|
||||||
|
music_version = music_note.get("version")
|
||||||
|
else:
|
||||||
|
media_payload = None
|
||||||
|
music_version = None
|
||||||
|
if isinstance(media_payload, dict) and music_version == 1:
|
||||||
media: MediaInfo | MusicInfo = MusicInfo.from_dict(media_payload)
|
media: MediaInfo | MusicInfo = MusicInfo.from_dict(media_payload)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -57,12 +62,13 @@ def _history_media(history: DownloadHistorySnapshot) -> MediaInfo | MusicInfo:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
media = MediaInfo(
|
media = MediaInfo(
|
||||||
media_source=history.media_source,
|
|
||||||
media_id=history.media_id,
|
|
||||||
type=media_type,
|
type=media_type,
|
||||||
title=history.title,
|
title=history.title or "",
|
||||||
year=history.year,
|
year=history.year or "",
|
||||||
)
|
)
|
||||||
|
if history.media_source and history.media_id:
|
||||||
|
media.media_source = history.media_source
|
||||||
|
media.media_id = history.media_id
|
||||||
|
|
||||||
snapshot = persisted_classification_snapshot(
|
snapshot = persisted_classification_snapshot(
|
||||||
category_id=history.media_category_id,
|
category_id=history.media_category_id,
|
||||||
@@ -71,7 +77,10 @@ def _history_media(history: DownloadHistorySnapshot) -> MediaInfo | MusicInfo:
|
|||||||
policy_revision=history.classification_policy_revision,
|
policy_revision=history.classification_policy_revision,
|
||||||
source=history.classification_source,
|
source=history.classification_source,
|
||||||
)
|
)
|
||||||
return apply_persisted_classification_snapshot(media, snapshot) or media
|
return cast(
|
||||||
|
MediaInfo | MusicInfo,
|
||||||
|
apply_persisted_classification_snapshot(media, snapshot) or media,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def resolve_download_source_classification(
|
def resolve_download_source_classification(
|
||||||
@@ -93,13 +102,16 @@ def resolve_download_source_classification(
|
|||||||
)
|
)
|
||||||
if manual_path not in helper.classification_category_paths(media.type):
|
if manual_path not in helper.classification_category_paths(media.type):
|
||||||
raise ValueError("手动指定的媒体分类不存在、已停用或与媒体类型不匹配")
|
raise ValueError("手动指定的媒体分类不存在、已停用或与媒体类型不匹配")
|
||||||
media = apply_persisted_classification_snapshot(
|
media = cast(
|
||||||
media,
|
MediaInfo | MusicInfo,
|
||||||
persisted_classification_snapshot(
|
apply_persisted_classification_snapshot(
|
||||||
category_path=manual_path,
|
media,
|
||||||
source="manual",
|
persisted_classification_snapshot(
|
||||||
),
|
category_path=manual_path,
|
||||||
) or media
|
source="manual",
|
||||||
|
),
|
||||||
|
) or media,
|
||||||
|
)
|
||||||
directory = helper.get_download_dir_by_task_path(media, current_save_path)
|
directory = helper.get_download_dir_by_task_path(media, current_save_path)
|
||||||
if not directory or not directory.download_path:
|
if not directory or not directory.download_path:
|
||||||
raise ValueError("当前保存目录不在已配置的资源目录中")
|
raise ValueError("当前保存目录不在已配置的资源目录中")
|
||||||
|
|||||||
@@ -108,7 +108,13 @@ def _download_root(current: PurePosixPath, media_type: str, category: str) -> tu
|
|||||||
continue
|
continue
|
||||||
if directory.media_category and directory.media_category != category:
|
if directory.media_category and directory.media_category != category:
|
||||||
continue
|
continue
|
||||||
candidates.append((int(current.is_relative_to(root)), -directory.priority, len(root.parts), directory, root))
|
candidates.append((
|
||||||
|
int(current.is_relative_to(root)),
|
||||||
|
-int(directory.priority or 0),
|
||||||
|
len(root.parts),
|
||||||
|
directory,
|
||||||
|
root,
|
||||||
|
))
|
||||||
if not candidates:
|
if not candidates:
|
||||||
raise ValueError("没有找到匹配识别结果的本地资源目录")
|
raise ValueError("没有找到匹配识别结果的本地资源目录")
|
||||||
_, _, _, directory, root = max(candidates, key=lambda item: item[:3])
|
_, _, _, directory, root = max(candidates, key=lambda item: item[:3])
|
||||||
@@ -261,6 +267,8 @@ def organize_existing_source(hash_value: str, request: Any, chain: Any, media_ch
|
|||||||
if request.mode == "manual":
|
if request.mode == "manual":
|
||||||
target = _manual_target(str(request.target_path))
|
target = _manual_target(str(request.target_path))
|
||||||
else:
|
else:
|
||||||
|
if category is None:
|
||||||
|
raise ValueError("识别结果缺少可用的媒体类别")
|
||||||
_, target = _download_root(current, media_type, category)
|
_, target = _download_root(current, media_type, category)
|
||||||
target = PurePosixPath(validate_download_save_path(target.as_posix()))
|
target = PurePosixPath(validate_download_save_path(target.as_posix()))
|
||||||
|
|
||||||
@@ -294,6 +302,8 @@ def organize_existing_source(hash_value: str, request: Any, chain: Any, media_ch
|
|||||||
if not expected:
|
if not expected:
|
||||||
raise ValueError("任务路径或识别计划已变化,请重新预览后确认")
|
raise ValueError("任务路径或识别计划已变化,请重新预览后确认")
|
||||||
if rename_required:
|
if rename_required:
|
||||||
|
if current_root_name is None or proposed_root_name is None:
|
||||||
|
raise ValueError("根目录重命名计划不完整,请重新预览")
|
||||||
renamed = _rename_qb_root(
|
renamed = _rename_qb_root(
|
||||||
chain,
|
chain,
|
||||||
downloader,
|
downloader,
|
||||||
@@ -312,6 +322,8 @@ def organize_existing_source(hash_value: str, request: Any, chain: Any, media_ch
|
|||||||
relocated = bool(result.get("save_path"))
|
relocated = bool(result.get("save_path"))
|
||||||
if not relocated:
|
if not relocated:
|
||||||
if renamed:
|
if renamed:
|
||||||
|
if current_root_name is None or proposed_root_name is None:
|
||||||
|
raise ValueError("根目录重命名计划不完整,无法自动回滚")
|
||||||
rolled_back = _rename_qb_root(
|
rolled_back = _rename_qb_root(
|
||||||
chain,
|
chain,
|
||||||
downloader,
|
downloader,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
from typing import Literal, Optional
|
from typing import Literal, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, model_validator as _model_validator
|
from pydantic import BaseModel, Field
|
||||||
|
from pydantic import model_validator as _model_validator
|
||||||
|
|
||||||
from app.schemas.types import MediaSource as _MediaSource, MusicTargetEntityType as _MusicTargetEntityType
|
from app.schemas.types import MediaSource as _MediaSource
|
||||||
|
from app.schemas.types import MusicTargetEntityType as _MusicTargetEntityType
|
||||||
|
|
||||||
|
|
||||||
class DownloadTask(BaseModel):
|
class DownloadTask(BaseModel):
|
||||||
@@ -78,7 +80,7 @@ class DownloadTaskUpdateData(BaseModel): # type: ignore[misc]
|
|||||||
results: list[DownloadTaskMutationResult] = Field(default_factory=list, description="各修改动作结果")
|
results: list[DownloadTaskMutationResult] = Field(default_factory=list, description="各修改动作结果")
|
||||||
|
|
||||||
|
|
||||||
class DownloadSourceClassificationRequest(BaseModel):
|
class DownloadSourceClassificationRequest(BaseModel): # type: ignore[misc]
|
||||||
"""已有任务的识别、归类与种子根目录重命名请求。"""
|
"""已有任务的识别、归类与种子根目录重命名请求。"""
|
||||||
|
|
||||||
downloader: Optional[str] = None
|
downloader: Optional[str] = None
|
||||||
@@ -97,8 +99,8 @@ class DownloadSourceClassificationRequest(BaseModel):
|
|||||||
expected_content_path: Optional[str] = None
|
expected_content_path: Optional[str] = None
|
||||||
expected_root_name: Optional[str] = None
|
expected_root_name: Optional[str] = None
|
||||||
|
|
||||||
@_model_validator(mode="after")
|
@_model_validator(mode="after") # type: ignore[misc]
|
||||||
def validate_mode_and_identity(self):
|
def validate_mode_and_identity(self) -> "DownloadSourceClassificationRequest":
|
||||||
"""手动模式必须给出目录,ID 不能脱离其所属数据源。"""
|
"""手动模式必须给出目录,ID 不能脱离其所属数据源。"""
|
||||||
if self.mode == "manual" and not str(self.target_path or "").strip():
|
if self.mode == "manual" and not str(self.target_path or "").strip():
|
||||||
raise ValueError("手动指定目录模式必须填写目标路径")
|
raise ValueError("手动指定目录模式必须填写目标路径")
|
||||||
@@ -107,7 +109,7 @@ class DownloadSourceClassificationRequest(BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class DownloadSourceClassificationData(BaseModel):
|
class DownloadSourceClassificationData(BaseModel): # type: ignore[misc]
|
||||||
"""识别与资源目录变更计划,包含可审计的执行结果。"""
|
"""识别与资源目录变更计划,包含可审计的执行结果。"""
|
||||||
|
|
||||||
hash: str
|
hash: str
|
||||||
|
|||||||
@@ -734,7 +734,7 @@
|
|||||||
"owner": "downloader-operation",
|
"owner": "downloader-operation",
|
||||||
"path": "/api/v1/download/{hashString}/classify-source",
|
"path": "/api/v1/download/{hashString}/classify-source",
|
||||||
"reason": "Low-level provider behavior is exposed by the self-describing provider Skill; high-level MoviePilot operations remain in moviepilot-api.",
|
"reason": "Low-level provider behavior is exposed by the self-describing provider Skill; high-level MoviePilot operations remain in moviepilot-api.",
|
||||||
"summary": "按媒体类别重新定位资源目录",
|
"summary": "识别并归类已有下载任务",
|
||||||
"tags": [
|
"tags": [
|
||||||
"download"
|
"download"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
| `POST` | `/api/v1/download/subtitle` | download | `provider-skill` | downloader-operation | 下载字幕 |
|
| `POST` | `/api/v1/download/subtitle` | download | `provider-skill` | downloader-operation | 下载字幕 |
|
||||||
| `DELETE` | `/api/v1/download/{hashString}` | download | `provider-skill` | downloader-operation | 删除下载任务 |
|
| `DELETE` | `/api/v1/download/{hashString}` | download | `provider-skill` | downloader-operation | 删除下载任务 |
|
||||||
| `PATCH` | `/api/v1/download/{hashString}` | download | `provider-skill` | downloader-operation | 高级更新下载任务 |
|
| `PATCH` | `/api/v1/download/{hashString}` | download | `provider-skill` | downloader-operation | 高级更新下载任务 |
|
||||||
| `POST` | `/api/v1/download/{hashString}/classify-source` | download | `provider-skill` | downloader-operation | 按媒体类别重新定位资源目录 |
|
| `POST` | `/api/v1/download/{hashString}/classify-source` | download | `provider-skill` | downloader-operation | 识别并归类已有下载任务 |
|
||||||
| `DELETE` | `/api/v1/history/download` | history | `gateway` | download.history.delete | 删除下载历史记录 |
|
| `DELETE` | `/api/v1/history/download` | history | `gateway` | download.history.delete | 删除下载历史记录 |
|
||||||
| `GET` | `/api/v1/history/download` | history | `gateway` | download.history.list | 查询下载历史记录 |
|
| `GET` | `/api/v1/history/download` | history | `gateway` | download.history.list | 查询下载历史记录 |
|
||||||
| `DELETE` | `/api/v1/history/transfer` | history | `gateway` | transfer.history.delete | 删除整理记录 |
|
| `DELETE` | `/api/v1/history/transfer` | history | `gateway` | transfer.history.delete | 删除整理记录 |
|
||||||
|
|||||||
+30
-5
@@ -1074,8 +1074,8 @@
|
|||||||
"runtime_only": true
|
"runtime_only": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"edge_count": 8302,
|
"edge_count": 8325,
|
||||||
"edge_sha256": "49000047cece20aa2bd7f1d06916072d06b832f0f2d929693f995206f9a29b84",
|
"edge_sha256": "4ecae8adac9a9defb900d6125d2f22dade1bd6c60545f39d77dd2845db22a028",
|
||||||
"edges": [
|
"edges": [
|
||||||
"app -> app.foundation",
|
"app -> app.foundation",
|
||||||
"app -> app.foundation.environment",
|
"app -> app.foundation.environment",
|
||||||
@@ -2215,6 +2215,7 @@
|
|||||||
"app.api.endpoints.download -> app.application.configuration",
|
"app.api.endpoints.download -> app.application.configuration",
|
||||||
"app.api.endpoints.download -> app.application.directory",
|
"app.api.endpoints.download -> app.application.directory",
|
||||||
"app.api.endpoints.download -> app.application.download",
|
"app.api.endpoints.download -> app.application.download",
|
||||||
|
"app.api.endpoints.download -> app.application.download.organization",
|
||||||
"app.api.endpoints.download -> app.application.download.tasks",
|
"app.api.endpoints.download -> app.application.download.tasks",
|
||||||
"app.api.endpoints.download -> app.application.security",
|
"app.api.endpoints.download -> app.application.security",
|
||||||
"app.api.endpoints.download -> app.application.security.url",
|
"app.api.endpoints.download -> app.application.security.url",
|
||||||
@@ -3153,8 +3154,30 @@
|
|||||||
"app.application.directory -> app.schemas.file",
|
"app.application.directory -> app.schemas.file",
|
||||||
"app.application.directory -> app.schemas.system",
|
"app.application.directory -> app.schemas.system",
|
||||||
"app.application.directory -> app.schemas.types",
|
"app.application.directory -> app.schemas.types",
|
||||||
|
"app.application.download.classification -> app.application",
|
||||||
|
"app.application.download.classification -> app.application.classification",
|
||||||
|
"app.application.download.classification -> app.application.classification.reference",
|
||||||
|
"app.application.download.classification -> app.application.directory",
|
||||||
|
"app.application.download.classification -> app.application.history",
|
||||||
|
"app.application.download.classification -> app.domain",
|
||||||
|
"app.application.download.classification -> app.domain.classification",
|
||||||
|
"app.application.download.classification -> app.domain.classification.validation",
|
||||||
|
"app.application.download.classification -> app.domain.context",
|
||||||
|
"app.application.download.classification -> app.schemas",
|
||||||
|
"app.application.download.classification -> app.schemas.transfer",
|
||||||
|
"app.application.download.classification -> app.schemas.types",
|
||||||
"app.application.download.failures -> app.schemas",
|
"app.application.download.failures -> app.schemas",
|
||||||
"app.application.download.failures -> app.schemas.types",
|
"app.application.download.failures -> app.schemas.types",
|
||||||
|
"app.application.download.organization -> app.application",
|
||||||
|
"app.application.download.organization -> app.application.configuration",
|
||||||
|
"app.application.download.organization -> app.application.directory",
|
||||||
|
"app.application.download.organization -> app.domain",
|
||||||
|
"app.application.download.organization -> app.domain.meta",
|
||||||
|
"app.application.download.organization -> app.domain.meta.metabase",
|
||||||
|
"app.application.download.organization -> app.domain.meta.metamusic",
|
||||||
|
"app.application.download.organization -> app.domain.metainfo",
|
||||||
|
"app.application.download.organization -> app.schemas",
|
||||||
|
"app.application.download.organization -> app.schemas.types",
|
||||||
"app.application.download.selection -> app.domain",
|
"app.application.download.selection -> app.domain",
|
||||||
"app.application.download.selection -> app.domain.context",
|
"app.application.download.selection -> app.domain.context",
|
||||||
"app.application.download.selection -> app.schemas",
|
"app.application.download.selection -> app.schemas",
|
||||||
@@ -4070,8 +4093,6 @@
|
|||||||
"app.chain.download.submission -> app.schemas.message",
|
"app.chain.download.submission -> app.schemas.message",
|
||||||
"app.chain.download.submission -> app.schemas.types",
|
"app.chain.download.submission -> app.schemas.types",
|
||||||
"app.chain.download.subtitle -> app.application",
|
"app.chain.download.subtitle -> app.application",
|
||||||
"app.chain.download.subtitle -> app.application.classification",
|
|
||||||
"app.chain.download.subtitle -> app.application.classification.reference",
|
|
||||||
"app.chain.download.subtitle -> app.application.configuration",
|
"app.chain.download.subtitle -> app.application.configuration",
|
||||||
"app.chain.download.subtitle -> app.application.directory",
|
"app.chain.download.subtitle -> app.application.directory",
|
||||||
"app.chain.download.subtitle -> app.application.torrent",
|
"app.chain.download.subtitle -> app.application.torrent",
|
||||||
@@ -8227,6 +8248,8 @@
|
|||||||
"app.schemas.dashboard -> app.runtime.localization",
|
"app.schemas.dashboard -> app.runtime.localization",
|
||||||
"app.schemas.dashboard -> app.schemas",
|
"app.schemas.dashboard -> app.schemas",
|
||||||
"app.schemas.dashboard -> app.schemas.common",
|
"app.schemas.dashboard -> app.schemas.common",
|
||||||
|
"app.schemas.download -> app.schemas",
|
||||||
|
"app.schemas.download -> app.schemas.types",
|
||||||
"app.schemas.event -> app.schemas",
|
"app.schemas.event -> app.schemas",
|
||||||
"app.schemas.event -> app.schemas.category",
|
"app.schemas.event -> app.schemas.category",
|
||||||
"app.schemas.event -> app.schemas.common",
|
"app.schemas.event -> app.schemas.common",
|
||||||
@@ -9380,7 +9403,7 @@
|
|||||||
"app.workflow.actions.transfer_file -> app.workflow",
|
"app.workflow.actions.transfer_file -> app.workflow",
|
||||||
"app.workflow.actions.transfer_file -> app.workflow.actions"
|
"app.workflow.actions.transfer_file -> app.workflow.actions"
|
||||||
],
|
],
|
||||||
"module_count": 980,
|
"module_count": 982,
|
||||||
"modules": [
|
"modules": [
|
||||||
"app",
|
"app",
|
||||||
"app.adapters",
|
"app.adapters",
|
||||||
@@ -9603,7 +9626,9 @@
|
|||||||
"app.application.directory",
|
"app.application.directory",
|
||||||
"app.application.download",
|
"app.application.download",
|
||||||
"app.application.download.admission",
|
"app.application.download.admission",
|
||||||
|
"app.application.download.classification",
|
||||||
"app.application.download.failures",
|
"app.application.download.failures",
|
||||||
|
"app.application.download.organization",
|
||||||
"app.application.download.selection",
|
"app.application.download.selection",
|
||||||
"app.application.download.tasks",
|
"app.application.download.tasks",
|
||||||
"app.application.downloader",
|
"app.application.downloader",
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ from unittest.mock import MagicMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import app.application.download.classification as classification_module
|
|
||||||
import app.api.endpoints.download as download_endpoint
|
import app.api.endpoints.download as download_endpoint
|
||||||
|
import app.application.download.classification as classification_module
|
||||||
|
from app.application.directory import DirectoryHelper
|
||||||
from app.application.download.classification import (
|
from app.application.download.classification import (
|
||||||
DownloadSourceClassificationPlan,
|
DownloadSourceClassificationPlan,
|
||||||
DownloadSourceClassificationService,
|
DownloadSourceClassificationService,
|
||||||
resolve_download_source_classification,
|
resolve_download_source_classification,
|
||||||
)
|
)
|
||||||
from app.application.directory import DirectoryHelper
|
|
||||||
from app.application.history import DownloadHistorySnapshot
|
from app.application.history import DownloadHistorySnapshot
|
||||||
from app.domain.context import MusicInfo
|
from app.domain.context import MusicInfo
|
||||||
from app.schemas.system import TransferDirectoryConf
|
|
||||||
from app.schemas.download import DownloadSourceClassificationRequest
|
from app.schemas.download import DownloadSourceClassificationRequest
|
||||||
|
from app.schemas.system import TransferDirectoryConf
|
||||||
from app.schemas.transfer import DownloaderTorrent
|
from app.schemas.transfer import DownloaderTorrent
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
@@ -226,7 +226,8 @@ async def test_classify_source_endpoint_preserves_preview_mode(monkeypatch):
|
|||||||
download_history_repository=SimpleNamespace(get_by_hash=MagicMock()),
|
download_history_repository=SimpleNamespace(get_by_hash=MagicMock()),
|
||||||
update_torrent=MagicMock(),
|
update_torrent=MagicMock(),
|
||||||
)
|
)
|
||||||
plan = MagicMock(
|
media_chain = object()
|
||||||
|
organize = MagicMock(
|
||||||
return_value={
|
return_value={
|
||||||
"hash": HASH,
|
"hash": HASH,
|
||||||
"downloader": "qb-main",
|
"downloader": "qb-main",
|
||||||
@@ -237,25 +238,21 @@ async def test_classify_source_endpoint_preserves_preview_mode(monkeypatch):
|
|||||||
"executed": False,
|
"executed": False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
monkeypatch.setattr(download_endpoint, "DownloadChain", lambda: chain)
|
payload = DownloadSourceClassificationRequest(
|
||||||
monkeypatch.setattr(
|
downloader="qb-main",
|
||||||
download_endpoint,
|
execute=False,
|
||||||
"DownloadSourceClassificationService",
|
|
||||||
lambda **_kwargs: SimpleNamespace(plan=plan),
|
|
||||||
)
|
)
|
||||||
|
monkeypatch.setattr(download_endpoint, "DownloadChain", lambda: chain)
|
||||||
|
monkeypatch.setattr(download_endpoint, "MediaChain", lambda: media_chain)
|
||||||
|
monkeypatch.setattr(download_endpoint, "organize_existing_source", organize)
|
||||||
|
|
||||||
response = await download_endpoint.classify_source(
|
response = await download_endpoint.classify_source(
|
||||||
HASH,
|
HASH,
|
||||||
DownloadSourceClassificationRequest(downloader="qb-main", execute=False),
|
payload,
|
||||||
SimpleNamespace(),
|
SimpleNamespace(),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.success is True
|
assert response.success is True
|
||||||
assert response.data["target_save_path"] == "/downloads/Album"
|
assert response.data["target_save_path"] == "/downloads/Album"
|
||||||
assert response.data["executed"] is False
|
assert response.data["executed"] is False
|
||||||
plan.assert_called_once_with(
|
organize.assert_called_once_with(HASH, payload, chain, media_chain)
|
||||||
hash_value=HASH,
|
|
||||||
downloader="qb-main",
|
|
||||||
execute=False,
|
|
||||||
media_category=None,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import sys
|
|||||||
import unittest
|
import unittest
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import ModuleType, SimpleNamespace as NS
|
from types import ModuleType
|
||||||
|
from types import SimpleNamespace as NS
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user