Merge pull request #5197 from stkevintan/default-samba

This commit is contained in:
jxxghp
2025-11-28 19:42:43 +08:00
committed by GitHub
4 changed files with 63 additions and 23 deletions
+4 -2
View File
@@ -707,17 +707,19 @@ class ChainBase(metaclass=ABCMeta):
cookie=cookie, episodes=episodes, category=category, label=label, cookie=cookie, episodes=episodes, category=category, label=label,
downloader=downloader) downloader=downloader)
def download_added(self, context: Context, download_dir: Path, torrent_content: Union[str, bytes] = None) -> None: def download_added(self, context: Context, download_dir: Path, storage: str, torrent_content: Union[str, bytes] = None) -> None:
""" """
添加下载任务成功后,从站点下载字幕,保存到下载目录 添加下载任务成功后,从站点下载字幕,保存到下载目录
:param context: 上下文,包括识别信息、媒体信息、种子信息 :param context: 上下文,包括识别信息、媒体信息、种子信息
:param download_dir: 下载目录 :param download_dir: 下载目录
:param storage: 存储类型
:param torrent_content: 种子内容,如果有则直接使用该内容,否则从context中获取种子文件路径 :param torrent_content: 种子内容,如果有则直接使用该内容,否则从context中获取种子文件路径
:return: None,该方法可被多个模块同时处理 :return: None,该方法可被多个模块同时处理
""" """
return self.run_module("download_added", context=context, return self.run_module("download_added", context=context,
torrent_content=torrent_content, torrent_content=torrent_content,
download_dir=download_dir) download_dir=download_dir,
storage=storage)
def list_torrents(self, status: TorrentStatus = None, def list_torrents(self, status: TorrentStatus = None,
hashs: Union[list, str] = None, hashs: Union[list, str] = None,
+7 -2
View File
@@ -232,13 +232,18 @@ class DownloadChain(ChainBase):
# 获取种子文件的文件夹名和文件清单 # 获取种子文件的文件夹名和文件清单
_folder_name, _file_list = TorrentHelper().get_fileinfo_from_torrent_content(torrent_content) _folder_name, _file_list = TorrentHelper().get_fileinfo_from_torrent_content(torrent_content)
storage = 'local'
# 下载目录 # 下载目录
if save_path: if save_path:
# 下载目录使用自定义的 # 下载目录使用自定义的
download_dir = Path(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
else: else:
# 根据媒体信息查询下载目录配置 # 根据媒体信息查询下载目录配置
dir_info = DirectoryHelper().get_dir(_media, storage="local", include_unsorted=True) dir_info = DirectoryHelper().get_dir(_media, include_unsorted=True)
storage = dir_info.storage if dir_info else storage
# 拼装子目录 # 拼装子目录
if dir_info: if dir_info:
# 一级目录 # 一级目录
@@ -358,7 +363,7 @@ class DownloadChain(ChainBase):
username=username, username=username,
) )
# 下载成功后处理 # 下载成功后处理
self.download_added(context=context, download_dir=download_dir, torrent_content=torrent_content) self.download_added(context=context, download_dir=download_dir, storage=storage, torrent_content=torrent_content)
# 广播事件 # 广播事件
self.eventmanager.send_event(EventType.DownloadAdded, { self.eventmanager.send_event(EventType.DownloadAdded, {
"hash": _hash, "hash": _hash,
+21 -8
View File
@@ -1,6 +1,6 @@
import re import re
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import List, Optional, Tuple
from app import schemas from app import schemas
from app.core.context import MediaInfo from app.core.context import MediaInfo
@@ -51,7 +51,7 @@ class DirectoryHelper:
""" """
return [d for d in self.get_library_dirs() if d.library_storage == "local"] return [d for d in self.get_library_dirs() if d.library_storage == "local"]
def get_dir(self, media: MediaInfo, include_unsorted: Optional[bool] = False, def get_dir(self, media: Optional[MediaInfo], include_unsorted: Optional[bool] = False,
storage: Optional[str] = None, src_path: Path = None, storage: Optional[str] = None, src_path: Path = None,
target_storage: Optional[str] = None, dest_path: Path = None target_storage: Optional[str] = None, dest_path: Path = None
) -> Optional[schemas.TransferDirectoryConf]: ) -> Optional[schemas.TransferDirectoryConf]:
@@ -64,11 +64,8 @@ class DirectoryHelper:
:param src_path: 源目录,有值时直接匹配 :param src_path: 源目录,有值时直接匹配
:param dest_path: 目标目录,有值时直接匹配 :param dest_path: 目标目录,有值时直接匹配
""" """
# 处理类型
if not media:
return None
# 电影/电视剧 # 电影/电视剧
media_type = media.type.value media_type = media.type.value if media else None
dirs = self.get_dirs() dirs = self.get_dirs()
# 如果存在源目录,并源目录为任一下载目录的子目录时,则进行源目录匹配,否则,允许源目录按同盘优先的逻辑匹配 # 如果存在源目录,并源目录为任一下载目录的子目录时,则进行源目录匹配,否则,允许源目录按同盘优先的逻辑匹配
@@ -93,7 +90,7 @@ class DirectoryHelper:
if dest_path and dest_path != Path(d.library_path): if dest_path and dest_path != Path(d.library_path):
continue continue
# 目录类型为全部的,符合条件 # 目录类型为全部的,符合条件
if not d.media_type: if not media_type or not d.media_type:
matched_dirs.append(d) matched_dirs.append(d)
continue continue
# 目录类型相等,目录类别为全部,符合条件 # 目录类型相等,目录类别为全部,符合条件
@@ -109,11 +106,27 @@ class DirectoryHelper:
# 优先源目录同盘 # 优先源目录同盘
for matched_dir in matched_dirs: for matched_dir in matched_dirs:
matched_path = Path(matched_dir.download_path) matched_path = Path(matched_dir.download_path)
if SystemUtils.is_same_disk(matched_path, src_path): if self._is_same_source((src_path, storage or "local"), (matched_path, matched_dir.library_storage)):
return matched_dir return matched_dir
return matched_dirs[0] return matched_dirs[0]
return None return None
@staticmethod
def _is_same_source(src: Tuple[Path, str], tar: Tuple[Path, str]) -> bool:
"""
判断源目录和目标目录是否在同一存储盘
:param src: 源目录路径和存储类型
:param tar: 目标目录路径和存储类型
:return: 是否在同一存储盘
"""
src_path, src_storage = src
tar_path, tar_storage = tar
if "local" == tar_storage == src_storage:
return SystemUtils.is_same_disk(src_path, tar_path)
# 网络存储,直接比较类型
return src_storage == tar_storage
@staticmethod @staticmethod
def get_media_root_path(rename_format: str, rename_path: Path) -> Optional[Path]: def get_media_root_path(rename_format: str, rename_path: Path) -> Optional[Path]:
""" """
+31 -11
View File
@@ -5,11 +5,13 @@ from typing import Tuple, Union
from lxml import etree from lxml import etree
from app.chain.storage import StorageChain
from app.core.config import settings from app.core.config import settings
from app.core.context import Context from app.core.context import Context
from app.helper.torrent import TorrentHelper from app.helper.torrent import TorrentHelper
from app.log import logger from app.log import logger
from app.modules import _ModuleBase from app.modules import _ModuleBase
from app.schemas.file import FileItem
from app.schemas.types import ModuleType, OtherModulesType from app.schemas.types import ModuleType, OtherModulesType
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
from app.utils.string import StringUtils from app.utils.string import StringUtils
@@ -63,11 +65,12 @@ class SubtitleModule(_ModuleBase):
def test(self): def test(self):
pass pass
def download_added(self, context: Context, download_dir: Path, torrent_content: Union[str, bytes] = None): def download_added(self, context: Context, download_dir: Path, storage: str, torrent_content: Union[str, bytes] = None):
""" """
添加下载任务成功后,从站点下载字幕,保存到下载目录 添加下载任务成功后,从站点下载字幕,保存到下载目录
:param context: 上下文,包括识别信息、媒体信息、种子信息 :param context: 上下文,包括识别信息、媒体信息、种子信息
:param download_dir: 下载目录 :param download_dir: 下载目录
:param storage: 存储类型
:param torrent_content: 种子内容,如果是种子文件,则为文件内容,否则为种子字符串 :param torrent_content: 种子内容,如果是种子文件,则为文件内容,否则为种子字符串
:return: None,该方法可被多个模块同时处理 :return: None,该方法可被多个模块同时处理
""" """
@@ -87,15 +90,29 @@ class SubtitleModule(_ModuleBase):
# 获取种子信息 # 获取种子信息
folder_name, _ = TorrentHelper().get_fileinfo_from_torrent_content(torrent_content) folder_name, _ = TorrentHelper().get_fileinfo_from_torrent_content(torrent_content)
# 文件保存目录,如果是单文件种子,则folder_name是空,此时文件保存目录就是下载目录 # 文件保存目录,如果是单文件种子,则folder_name是空,此时文件保存目录就是下载目录
download_dir = download_dir / folder_name storageChain = StorageChain()
# 等待目录存在 # 等待目录存在
working_dir_item = None
for _ in range(30): for _ in range(30):
if download_dir.exists(): found = storageChain.get_file_item(storage, download_dir / folder_name)
if found:
working_dir_item = found
break break
time.sleep(1) time.sleep(1)
# 目录仍然不存在,且有文件夹名,则创建目录 # 目录仍然不存在,且有文件夹名,则创建目录
if not download_dir.exists() and folder_name: if not working_dir_item and folder_name:
download_dir.mkdir(parents=True, exist_ok=True) parent_dir_item = storageChain.get_file_item(storage, download_dir)
if parent_dir_item:
working_dir_item = storageChain.create_folder(
parent_dir_item,
folder_name
)
else:
logger.error(f"下载根目录不存在,无法创建字幕文件夹:{download_dir}")
return
if not working_dir_item:
logger.error(f"下载目录不存在,无法保存字幕:{download_dir / folder_name}")
return
# 读取网站代码 # 读取网站代码
request = RequestUtils(cookies=torrent.site_cookie, ua=torrent.site_ua) request = RequestUtils(cookies=torrent.site_cookie, ua=torrent.site_ua)
res = request.get_res(torrent.page_url) res = request.get_res(torrent.page_url)
@@ -144,12 +161,12 @@ class SubtitleModule(_ModuleBase):
shutil.unpack_archive(zip_file, zip_path, format='zip') shutil.unpack_archive(zip_file, zip_path, format='zip')
# 遍历转移文件 # 遍历转移文件
for sub_file in SystemUtils.list_files(zip_path, settings.RMT_SUBEXT): for sub_file in SystemUtils.list_files(zip_path, settings.RMT_SUBEXT):
target_sub_file = download_dir / sub_file.name target_sub_file = Path(working_dir_item.path) / Path(sub_file.name)
if target_sub_file.exists(): if storageChain.get_file_item(storage, target_sub_file):
logger.info(f"字幕文件已存在:{target_sub_file}") logger.info(f"字幕文件已存在:{target_sub_file}")
continue continue
logger.info(f"转移字幕 {sub_file}{target_sub_file} ...") logger.info(f"转移字幕 {sub_file}{target_sub_file} ...")
SystemUtils.copy(sub_file, target_sub_file) storageChain.upload_file(working_dir_item, sub_file)
# 删除临时文件 # 删除临时文件
try: try:
shutil.rmtree(zip_path) shutil.rmtree(zip_path)
@@ -160,9 +177,12 @@ class SubtitleModule(_ModuleBase):
sub_file = settings.TEMP_PATH / file_name sub_file = settings.TEMP_PATH / file_name
# 保存 # 保存
sub_file.write_bytes(ret.content) sub_file.write_bytes(ret.content)
target_sub_file = download_dir / sub_file.name target_sub_file = Path(working_dir_item.path) / Path(sub_file.name)
logger.info(f"转移字幕 {sub_file}{target_sub_file}") if storageChain.get_file_item(storage, target_sub_file):
SystemUtils.copy(sub_file, target_sub_file) logger.info(f"字幕文件已存在:{target_sub_file}")
continue
logger.info(f"转移字幕 {sub_file}{target_sub_file} ...")
storageChain.upload_file(working_dir_item, sub_file)
else: else:
logger.error(f"下载字幕文件失败:{sublink}") logger.error(f"下载字幕文件失败:{sublink}")
continue continue