fix: create temp directory for subtitle API downloads

This commit is contained in:
jxxghp
2026-06-10 07:07:33 +08:00
parent c4602070b1
commit 829d7944b0
4 changed files with 93 additions and 109 deletions

View File

@@ -148,6 +148,7 @@ class DownloadChain(ChainBase):
return []
saved_files = []
settings.TEMP_PATH.mkdir(parents=True, exist_ok=True)
temp_file = settings.TEMP_PATH / file_name
temp_extract_dir = temp_file.with_name(temp_file.stem)
try:

View File

@@ -202,18 +202,25 @@ class SubtitleModule(_ModuleBase):
fileURI = FileURI.from_uri(download_dir.as_posix())
storage = fileURI.storage
download_dir = Path(fileURI.path)
target_dir = download_dir / folder_name if folder_name else download_dir
for _ in range(30):
found = storageChain.get_file_item(storage, target_dir)
found = storageChain.get_file_item(storage, download_dir / folder_name)
if found:
working_dir_item = found
break
time.sleep(1)
# 下载器可能还未创建保存目录,字幕保存前需要按完整目标路径补齐目录
# 目录仍然不存在,且有文件夹名,则创建目录
if not working_dir_item and folder_name:
parent_dir_item = storageChain.get_folder(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:
working_dir_item = storageChain.get_folder(storage, target_dir)
if not working_dir_item:
logger.error(f"下载目录不存在,无法保存字幕:{target_dir}")
logger.error(f"下载目录不存在,无法保存字幕:{download_dir / folder_name}")
return
# 读取网站代码
sublink_list = self._get_subtitle_links(torrent)

View File

@@ -4,9 +4,10 @@ from unittest.mock import MagicMock
import app.chain.download as download_module
from app.chain.download import DownloadChain
from app.core.context import Context, MediaInfo, TorrentInfo
from app.core.config import settings
from app.core.context import Context, MediaInfo, SubtitleInfo, TorrentInfo
from app.core.metainfo import MetaInfo
from app.schemas import NotExistMediaInfo
from app.schemas import FileItem, NotExistMediaInfo
from app.schemas.types import MediaType
@@ -42,6 +43,50 @@ class _FakeThreadHelper:
self.submitted.append((func, args, kwargs))
class _FakeSubtitleStorageChain:
"""
模拟字幕 API 保存文件时使用的存储链。
"""
def __init__(self):
"""
初始化上传记录。
"""
self.uploaded_files = []
def get_folder(self, storage, path):
"""
模拟目标目录存在或已创建。
"""
return FileItem(storage=storage, type="dir", path=path.as_posix(), name=path.name)
def get_file_item(self, _storage, _path):
"""
模拟目标字幕文件不存在。
"""
return None
def upload_file(self, fileitem, path):
"""
记录上传的临时字幕文件。
"""
self.uploaded_files.append(path)
return FileItem(
storage=fileitem.storage,
type="file",
path=(Path(fileitem.path) / path.name).as_posix(),
name=path.name,
)
class _FakeSubtitleResponse:
"""
模拟字幕 API 下载响应。
"""
content = b"subtitle-content"
def test_download_single_submits_download_added_to_background(monkeypatch):
"""
添加下载成功后,站点字幕等后处理应提交到后台,不能阻塞下载接口返回。
@@ -99,6 +144,38 @@ def test_download_single_submits_download_added_to_background(monkeypatch):
)
def test_save_subtitle_response_creates_missing_temp_directory(monkeypatch, tmp_path):
"""
下载字幕 API 保存响应前应自动创建缺失的临时目录。
"""
storage_chain = _FakeSubtitleStorageChain()
temp_path = tmp_path / "missing-temp"
assert not temp_path.exists()
monkeypatch.setattr(
download_module,
"settings",
SimpleNamespace(TEMP_PATH=temp_path, RMT_SUBEXT=settings.RMT_SUBEXT),
)
monkeypatch.setattr(download_module, "StorageChain", lambda: storage_chain)
chain = DownloadChain.__new__(DownloadChain)
subtitle = SubtitleInfo(
title="Demo Movie",
enclosure="https://example.test/subtitle.srt",
file_name="Demo.Movie.zh-cn.srt",
)
saved_files = chain._save_subtitle_response(
subtitle=subtitle,
response=_FakeSubtitleResponse(),
target_dir=Path("/downloads"),
)
assert temp_path.exists()
assert saved_files == ["/downloads/Demo.Movie.zh-cn.srt"]
assert storage_chain.uploaded_files
class _FakeBatchTorrentHelper:
"""
为批量下载测试提供稳定排序和种子文件集数解析。

View File

@@ -1,66 +1,9 @@
from pathlib import Path
from lxml import etree
from app.core.context import Context, TorrentInfo
from app.modules.subtitle import SubtitleModule
from app.schemas.file import FileItem
class _FakeStorageChain:
"""
模拟字幕下载使用的存储链,记录目录查询与创建行为。
"""
def __init__(self):
"""
初始化调用记录。
"""
self.file_item_paths = []
self.created_paths = []
self.uploaded_files = []
def get_file_item(self, storage, path):
"""
记录文件项查询,模拟目标目录和字幕文件都不存在。
"""
self.file_item_paths.append((storage, path))
return None
def get_folder(self, storage, path):
"""
记录递归创建目录请求,并返回创建后的目录项。
"""
self.created_paths.append((storage, path))
return FileItem(storage=storage, type="dir", path=path.as_posix(), name=path.name)
def upload_file(self, fileitem, path):
"""
记录上传请求,并返回上传后的文件项。
"""
self.uploaded_files.append((fileitem, path))
return FileItem(
storage=fileitem.storage,
type="file",
path=(path.parent / path.name).as_posix(),
name=path.name,
)
class _FakeResponse:
"""
构造字幕下载响应对象。
"""
status_code = 200
content = b"subtitle"
headers = {"Content-Disposition": 'attachment; filename="example.srt"'}
def test_parse_subtitle_links_filters_detail_page_action_links():
"""
详情页解析字幕链接时应过滤上传和外部搜索动作链接。
"""
html_text = """
<tr>
<td class="rowhead" valign="top">字幕</td>
@@ -100,47 +43,3 @@ def test_parse_subtitle_links_filters_detail_page_action_links():
assert links == [
"https://audiences.me/downloadsubs.php?torrentid=621182&subid=2148"
]
def test_download_added_creates_missing_target_directory(monkeypatch):
"""
下载字幕时目标目录不存在,应按完整下载目录自动创建后再保存字幕。
"""
storage_chain = _FakeStorageChain()
module = SubtitleModule()
context = Context(
torrent_info=TorrentInfo(
page_url="https://example.test/details.php?id=1",
site_cookie="cookie",
site_ua="ua",
)
)
monkeypatch.setattr("app.modules.subtitle.StorageChain", lambda: storage_chain)
monkeypatch.setattr(
"app.modules.subtitle.TorrentHelper.get_fileinfo_from_torrent_content",
lambda self, content: ("Example Folder", []),
)
monkeypatch.setattr(
"app.modules.subtitle.TorrentHelper.get_url_filename",
lambda response, url: "example.srt",
)
monkeypatch.setattr(
"app.modules.subtitle.RequestUtils",
lambda **kwargs: type(
"FakeRequest",
(),
{"get_res": lambda self, url: _FakeResponse()},
)(),
)
monkeypatch.setattr("app.modules.subtitle.time.sleep", lambda seconds: None)
monkeypatch.setattr(module, "_get_subtitle_links", lambda torrent: ["https://example.test/subtitle.srt"])
module.download_added(
context=context,
download_dir=Path("/downloads"),
torrent_content=b"torrent",
)
assert storage_chain.created_paths == [("local", Path("/downloads/Example Folder"))]
assert storage_chain.uploaded_files