diff --git a/app/agent/tools/impl/add_download.py b/app/agent/tools/impl/add_download.py index 21dcf245..baaf3bfa 100644 --- a/app/agent/tools/impl/add_download.py +++ b/app/agent/tools/impl/add_download.py @@ -25,7 +25,7 @@ class AddDownloadInput(BaseModel): downloader: Optional[str] = Field(None, description="Name of the downloader to use (optional, uses default if not specified)") save_path: Optional[str] = Field(None, - description="Directory path where the downloaded files should be saved (optional, uses default path if not specified)") + description="Directory path where the downloaded files should be saved. Using `:` for remote storage. e.g. rclone:/MP, smb:/server/share/Movies. (optional, uses default path if not specified)") labels: Optional[str] = Field(None, description="Comma-separated list of labels/tags to assign to the download (optional, e.g., 'movie,hd,bluray')") diff --git a/app/api/endpoints/download.py b/app/api/endpoints/download.py index 23a137dc..243e2d3c 100644 --- a/app/api/endpoints/download.py +++ b/app/api/endpoints/download.py @@ -68,6 +68,7 @@ def add( tmdbid: Annotated[int | None, Body()] = None, doubanid: Annotated[str | None, Body()] = None, downloader: Annotated[str | None, Body()] = None, + # 保存路径, 支持:, 如rclone:/MP, smb:/server/share/Movies等 save_path: Annotated[str | None, Body()] = None, current_user: User = Depends(get_current_active_user)) -> Any: """ @@ -92,6 +93,7 @@ def add( media_info=mediainfo, torrent_info=torrentinfo ) + did = DownloadChain().download_single(context=context, username=current_user.name, downloader=downloader, save_path=save_path, source="Manual") if not did: diff --git a/app/chain/download.py b/app/chain/download.py index 8d2e370d..f687228f 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -162,7 +162,7 @@ class DownloadChain(ChainBase): :param channel: 通知渠道 :param source: 来源(消息通知、Subscribe、Manual等) :param downloader: 下载器 - :param save_path: 保存路径 + :param save_path: 保存路径, 支持:, 如rclone:/MP, smb:/server/share/Movies等 :param userid: 用户ID :param username: 调用下载的用户名/插件名 :param label: 自定义标签 @@ -235,11 +235,10 @@ class DownloadChain(ChainBase): storage = 'local' # 下载目录 if save_path: + uri = schemas.FileURI.from_uri(save_path) # 下载目录使用自定义的 - download_dir = Path(save_path) - # Check if the download_dir matches any configured dirs - dir_info = DirectoryHelper().get_dir(dest_path=download_dir) - storage = dir_info.storage if dir_info else storage + download_dir = Path(uri.path) + storage = uri.storage else: # 根据媒体信息查询下载目录配置 dir_info = DirectoryHelper().get_dir(_media, include_unsorted=True) @@ -405,7 +404,7 @@ class DownloadChain(ChainBase): 根据缺失数据,自动种子列表中组合择优下载 :param contexts: 资源上下文列表 :param no_exists: 缺失的剧集信息 - :param save_path: 保存路径 + :param save_path: 保存路径, 支持:, 如rclone:/MP, smb:/server/share/Movies等 :param channel: 通知渠道 :param source: 来源(消息通知、订阅、手工下载等) :param userid: 用户ID diff --git a/app/modules/qbittorrent/__init__.py b/app/modules/qbittorrent/__init__.py index 3afc91ce..c36160c5 100644 --- a/app/modules/qbittorrent/__init__.py +++ b/app/modules/qbittorrent/__init__.py @@ -150,7 +150,7 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]): # 添加任务 state = server.add_torrent( content=content, - download_dir=str(download_dir), + download_dir= download_dir.as_posix(), is_paused=is_paused, tag=tags, cookie=cookie, diff --git a/app/modules/transmission/__init__.py b/app/modules/transmission/__init__.py index a00fba82..def26677 100644 --- a/app/modules/transmission/__init__.py +++ b/app/modules/transmission/__init__.py @@ -151,7 +151,7 @@ class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]): # 添加任务 torrent = server.add_torrent( content=content, - download_dir=str(download_dir), + download_dir=download_dir.as_posix(), is_paused=is_paused, labels=labels, cookie=cookie diff --git a/app/schemas/file.py b/app/schemas/file.py index d6095e10..3bd970cd 100644 --- a/app/schemas/file.py +++ b/app/schemas/file.py @@ -1,15 +1,36 @@ from typing import Optional +from pathlib import Path from pydantic import BaseModel, Field +from app.schemas.types import StorageSchema -class FileItem(BaseModel): - # 存储类型 - storage: Optional[str] = Field(default="local") - # 类型 dir/file - type: Optional[str] = None +class FileURI(BaseModel): # 文件路径 path: Optional[str] = "/" + # 存储类型 + storage: Optional[str] = Field(default="local") + + @property + def uri(self) -> str: + return self.path if self.storage == "local" else f"{self.storage}:{self.path}" + + @classmethod + def from_uri(cls, uri: str) -> "FileURI": + storage, path = 'local', uri + for s in StorageSchema: + protocol = f"{s.value}:" + if uri.startswith(protocol): + path = uri[len(protocol):] + storage = s.value + break + + path = Path(path).as_posix() + return cls(storage=storage, path=path) + +class FileItem(FileURI): + # 类型 dir/file + type: Optional[str] = None # 文件名 name: Optional[str] = None # 文件名 @@ -46,3 +67,4 @@ class StorageUsage(BaseModel): class StorageTransType(BaseModel): # 传输类型 transtype: Optional[dict] = Field(default_factory=dict) + diff --git a/app/workflow/actions/add_download.py b/app/workflow/actions/add_download.py index 73def935..5c4a58bd 100644 --- a/app/workflow/actions/add_download.py +++ b/app/workflow/actions/add_download.py @@ -16,7 +16,7 @@ class AddDownloadParams(ActionParams): 添加下载资源参数 """ downloader: Optional[str] = Field(default=None, description="下载器") - save_path: Optional[str] = Field(default=None, description="保存路径") + save_path: Optional[str] = Field(default=None, description="保存路径, 支持:, 如rclone:/MP, smb:/server/share/Movies等") labels: Optional[str] = Field(default=None, description="标签(,分隔)") only_lack: Optional[bool] = Field(default=False, description="仅下载缺失的资源")