fix: normalize downloader return paths

Fixes #5773
This commit is contained in:
jxxghp
2026-05-15 18:15:42 +08:00
parent 51229204c9
commit 1f49f9b454
5 changed files with 428 additions and 18 deletions

View File

@@ -291,7 +291,38 @@ class _DownloaderBase(ServiceBase[TService, DownloaderConf]):
重置默认配置名称
"""
self._default_config_name = None
@staticmethod
def __replace_path_prefix(path: Union[Path, str], source: str, target: str) -> Optional[str]:
"""
按完整路径段替换路径前缀,避免 /media 误匹配 /media2 这类相邻目录。
"""
if not source or not source.strip() or not target or not target.strip():
return None
path_text = Path(path).as_posix()
source_path = Path(source.strip()).as_posix()
target_path = Path(target.strip()).as_posix()
if path_text == source_path:
return target_path
source_prefix = f"{source_path.rstrip('/')}/"
if path_text.startswith(source_prefix):
suffix = path_text[len(source_prefix):]
return (Path(target_path) / suffix).as_posix()
return None
@staticmethod
def __strip_storage_prefix(path: str) -> str:
"""
去掉存储协议前缀 if any下载器无法识别本地存储协议。
"""
for s in StorageSchema:
prefix = f"{s.value}:"
if path.startswith(prefix):
return path[len(prefix):]
return path
def normalize_path(self, path: Path, downloader: Optional[str]) -> str:
"""
根据下载器配置和路径映射,规范化下载路径
@@ -300,21 +331,33 @@ class _DownloaderBase(ServiceBase[TService, DownloaderConf]):
:param downloader: 下载器名称
:return: 规范化后发送给下载器的路径
"""
dir = path.as_posix()
normalized_path = path.as_posix()
conf = self.get_config(downloader)
if conf and conf.path_mapping:
for (storage_path, download_path) in conf.path_mapping:
storage_path = Path(storage_path.strip()).as_posix()
download_path = Path(download_path.strip()).as_posix()
if dir.startswith(storage_path):
dir = dir.replace(storage_path, download_path, 1)
mapped_path = self.__replace_path_prefix(normalized_path, storage_path, download_path)
if mapped_path:
normalized_path = mapped_path
break
# 去掉存储协议前缀 if any, 下载器无法识别
for s in StorageSchema:
prefix = f"{s.value}:"
if dir.startswith(prefix):
return dir[len(prefix):]
return dir
return self.__strip_storage_prefix(normalized_path)
def normalize_return_path(self, path: Path, downloader: Optional[str]) -> str:
"""
将下载器返回的路径反向映射为 MoviePilot 可访问的存储路径。
:param path: 下载器返回的路径
:param downloader: 下载器名称
:return: MoviePilot 可访问的路径
"""
normalized_path = path.as_posix()
conf = self.get_config(downloader)
if conf and conf.path_mapping:
for (storage_path, download_path) in conf.path_mapping:
mapped_path = self.__replace_path_prefix(normalized_path, download_path, storage_path)
if mapped_path:
normalized_path = mapped_path
break
return self.__strip_storage_prefix(normalized_path)
class _MediaServerBase(ServiceBase[TService, MediaServerConf]):

View File

@@ -268,7 +268,7 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
ret_torrents.append(TransferTorrent(
downloader=name,
title=torrent.get('name'),
path=torrent_path,
path=Path(self.normalize_return_path(torrent_path, name)),
hash=torrent.get('hash'),
size=torrent.get('total_size'),
tags=torrent.get('tags'),
@@ -296,7 +296,7 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
ret_torrents.append(TransferTorrent(
downloader=name,
title=torrent.get('name'),
path=torrent_path,
path=Path(self.normalize_return_path(torrent_path, name)),
hash=torrent.get('hash'),
tags=torrent.get('tags')
))

View File

@@ -319,7 +319,7 @@ class RtorrentModule(_ModuleBase, _DownloaderBase[Rtorrent]):
TransferTorrent(
downloader=name,
title=torrent.get("name"),
path=torrent_path,
path=Path(self.normalize_return_path(torrent_path, name)),
hash=torrent.get("hash"),
size=torrent.get("total_size"),
tags=torrent.get("tags"),
@@ -355,7 +355,7 @@ class RtorrentModule(_ModuleBase, _DownloaderBase[Rtorrent]):
TransferTorrent(
downloader=name,
title=torrent.get("name"),
path=torrent_path,
path=Path(self.normalize_return_path(torrent_path, name)),
hash=torrent.get("hash"),
tags=torrent.get("tags"),
)

View File

@@ -249,10 +249,11 @@ class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
torrents, _ = server.get_torrents(ids=hashs, tags=settings.TORRENT_TAG) or []
try:
for torrent in torrents:
torrent_path = Path(torrent.download_dir) / torrent.name
ret_torrents.append(TransferTorrent(
downloader=name,
title=torrent.name,
path=Path(torrent.download_dir) / torrent.name,
path=Path(self.normalize_return_path(torrent_path, name)),
hash=torrent.hashString,
size=torrent.total_size,
tags=",".join(torrent.labels or []),
@@ -276,10 +277,11 @@ class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
if not path:
logger.debug(f"未获取到 {torrent.name} 下载保存路径")
continue
torrent_path = Path(torrent.download_dir) / torrent.name
ret_torrents.append(TransferTorrent(
downloader=name,
title=torrent.name,
path=Path(torrent.download_dir) / torrent.name,
path=Path(self.normalize_return_path(torrent_path, name)),
hash=torrent.hashString,
tags=",".join(torrent.labels or []),
progress=torrent.progress,