support path_mapping for downloader

This commit is contained in:
stkevintan
2025-12-08 14:00:39 +08:00
parent 63a890e85d
commit 378ba51f4d
7 changed files with 42 additions and 15 deletions

View File

@@ -707,19 +707,17 @@ class ChainBase(metaclass=ABCMeta):
cookie=cookie, episodes=episodes, category=category, label=label,
downloader=downloader)
def download_added(self, context: Context, download_dir: Path, storage: str, torrent_content: Union[str, bytes] = None) -> None:
def download_added(self, context: Context, download_dir: Path, torrent_content: Union[str, bytes] = None) -> None:
"""
添加下载任务成功后,从站点下载字幕,保存到下载目录
:param context: 上下文,包括识别信息、媒体信息、种子信息
:param download_dir: 下载目录
:param storage: 存储类型
:param torrent_content: 种子内容如果有则直接使用该内容否则从context中获取种子文件路径
:return: None该方法可被多个模块同时处理
"""
return self.run_module("download_added", context=context,
torrent_content=torrent_content,
download_dir=download_dir,
storage=storage)
download_dir=download_dir)
def list_torrents(self, status: TorrentStatus = None,
hashs: Union[list, str] = None,

View File

@@ -19,7 +19,7 @@ from app.db.mediaserver_oper import MediaServerOper
from app.helper.directory import DirectoryHelper
from app.helper.torrent import TorrentHelper
from app.log import logger
from app.schemas import ExistMediaInfo, NotExistMediaInfo, DownloadingTorrent, Notification, ResourceSelectionEventData, \
from app.schemas import ExistMediaInfo, FileURI, NotExistMediaInfo, DownloadingTorrent, Notification, ResourceSelectionEventData, \
ResourceDownloadEventData
from app.schemas.types import MediaType, TorrentStatus, EventType, MessageChannel, NotificationType, ContentType, \
ChainEventType
@@ -235,10 +235,7 @@ class DownloadChain(ChainBase):
storage = 'local'
# 下载目录
if save_path:
uri = schemas.FileURI.from_uri(save_path)
# 下载目录使用自定义的
download_dir = Path(uri.path)
storage = uri.storage
download_dir = Path(save_path)
else:
# 根据媒体信息查询下载目录配置
dir_info = DirectoryHelper().get_dir(_media, include_unsorted=True)
@@ -263,6 +260,8 @@ class DownloadChain(ChainBase):
self.messagehelper.put(f"{_media.type.value} {_media.title_year} 未找到下载目录!",
title="下载失败", role="system")
return None
fileURI = FileURI(storage=storage, path=download_dir.as_posix())
download_dir = Path(fileURI.uri)
# 添加下载
result: Optional[tuple] = self.download(content=torrent_content,
@@ -362,7 +361,7 @@ class DownloadChain(ChainBase):
username=username,
)
# 下载成功后处理
self.download_added(context=context, download_dir=download_dir, storage=storage, torrent_content=torrent_content)
self.download_added(context=context, download_dir=download_dir, torrent_content=torrent_content)
# 广播事件
self.eventmanager.send_event(EventType.DownloadAdded, {
"hash": _hash,

View File

@@ -1,5 +1,6 @@
from abc import abstractmethod, ABCMeta
from typing import Generic, Tuple, Union, TypeVar, Type, Dict, Optional, Callable
from pathlib import Path
from app.helper.service import ServiceConfigHelper
from app.schemas import Notification, NotificationConf, MediaServerConf, DownloaderConf
@@ -290,6 +291,30 @@ class _DownloaderBase(ServiceBase[TService, DownloaderConf]):
重置默认配置名称
"""
self._default_config_name = None
def normalize_path(self, path: Path, downloader: Optional[str]) -> str:
"""
根据下载器配置和路径映射,规范化下载路径
:param path: 原始路径
:param conf: 下载器配置
:return: 规范化后的路径
"""
dir = path.as_posix()
conf = self.get_config(downloader)
if conf and conf.path_mapping:
for (src, dst) in conf.path_mapping:
src = Path(src.strip()).as_posix()
dst = Path(dst.strip()).as_posix()
if dir.startswith(src):
dir = dir.replace(src, dst, 1)
break
# 处理存储协议前缀
for s in StorageSchema:
prefix = f"{s.value}:"
if dir.startswith(prefix):
return dir[len(prefix):]
return dir
class _MediaServerBase(ServiceBase[TService, MediaServerConf]):

View File

@@ -150,7 +150,7 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
# 添加任务
state = server.add_torrent(
content=content,
download_dir=download_dir.as_posix(),
download_dir=self.normalize_path(download_dir, downloader),
is_paused=is_paused,
tag=tags,
cookie=cookie,

View File

@@ -11,7 +11,7 @@ from app.core.context import Context
from app.helper.torrent import TorrentHelper
from app.log import logger
from app.modules import _ModuleBase
from app.schemas.file import FileItem
from app.schemas.file import FileURI
from app.schemas.types import ModuleType, OtherModulesType
from app.utils.http import RequestUtils
from app.utils.string import StringUtils
@@ -65,12 +65,11 @@ class SubtitleModule(_ModuleBase):
def test(self):
pass
def download_added(self, context: Context, download_dir: Path, storage: str, torrent_content: Union[str, bytes] = None):
def download_added(self, context: Context, download_dir: Path, torrent_content: Union[str, bytes] = None):
"""
添加下载任务成功后,从站点下载字幕,保存到下载目录
:param context: 上下文,包括识别信息、媒体信息、种子信息
:param download_dir: 下载目录
:param storage: 存储类型
:param torrent_content: 种子内容,如果是种子文件,则为文件内容,否则为种子字符串
:return: None该方法可被多个模块同时处理
"""
@@ -93,6 +92,10 @@ class SubtitleModule(_ModuleBase):
storageChain = StorageChain()
# 等待目录存在
working_dir_item = None
# split download_dir into storage and path
fileURI = FileURI.from_uri(download_dir.as_posix())
storage = fileURI.storage
download_dir = Path(fileURI.path)
for _ in range(30):
found = storageChain.get_file_item(storage, download_dir / folder_name)
if found:

View File

@@ -151,7 +151,7 @@ class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
# 添加任务
torrent = server.add_torrent(
content=content,
download_dir=download_dir.as_posix(),
download_dir=self.normalize_path(download_dir, downloader),
is_paused=is_paused,
labels=labels,
cookie=cookie

View File

@@ -51,6 +51,8 @@ class DownloaderConf(BaseModel):
config: Optional[dict] = Field(default_factory=dict)
# 是否启用
enabled: Optional[bool] = False
# 路径映射
path_mapping: Optional[list[tuple[str, str]]] = Field(default_factory=list)
class NotificationConf(BaseModel):