mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-08 17:08:35 +08:00
fix: create temp directory for subtitle API downloads
This commit is contained in:
@@ -148,6 +148,7 @@ class DownloadChain(ChainBase):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
saved_files = []
|
saved_files = []
|
||||||
|
settings.TEMP_PATH.mkdir(parents=True, exist_ok=True)
|
||||||
temp_file = settings.TEMP_PATH / file_name
|
temp_file = settings.TEMP_PATH / file_name
|
||||||
temp_extract_dir = temp_file.with_name(temp_file.stem)
|
temp_extract_dir = temp_file.with_name(temp_file.stem)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -202,18 +202,25 @@ class SubtitleModule(_ModuleBase):
|
|||||||
fileURI = FileURI.from_uri(download_dir.as_posix())
|
fileURI = FileURI.from_uri(download_dir.as_posix())
|
||||||
storage = fileURI.storage
|
storage = fileURI.storage
|
||||||
download_dir = Path(fileURI.path)
|
download_dir = Path(fileURI.path)
|
||||||
target_dir = download_dir / folder_name if folder_name else download_dir
|
|
||||||
for _ in range(30):
|
for _ in range(30):
|
||||||
found = storageChain.get_file_item(storage, target_dir)
|
found = storageChain.get_file_item(storage, download_dir / folder_name)
|
||||||
if found:
|
if found:
|
||||||
working_dir_item = found
|
working_dir_item = found
|
||||||
break
|
break
|
||||||
time.sleep(1)
|
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:
|
if not working_dir_item:
|
||||||
working_dir_item = storageChain.get_folder(storage, target_dir)
|
logger.error(f"下载目录不存在,无法保存字幕:{download_dir / folder_name}")
|
||||||
if not working_dir_item:
|
|
||||||
logger.error(f"下载目录不存在,无法保存字幕:{target_dir}")
|
|
||||||
return
|
return
|
||||||
# 读取网站代码
|
# 读取网站代码
|
||||||
sublink_list = self._get_subtitle_links(torrent)
|
sublink_list = self._get_subtitle_links(torrent)
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ from unittest.mock import MagicMock
|
|||||||
|
|
||||||
import app.chain.download as download_module
|
import app.chain.download as download_module
|
||||||
from app.chain.download import DownloadChain
|
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.core.metainfo import MetaInfo
|
||||||
from app.schemas import NotExistMediaInfo
|
from app.schemas import FileItem, NotExistMediaInfo
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
|
||||||
@@ -42,6 +43,50 @@ class _FakeThreadHelper:
|
|||||||
self.submitted.append((func, args, kwargs))
|
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):
|
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:
|
class _FakeBatchTorrentHelper:
|
||||||
"""
|
"""
|
||||||
为批量下载测试提供稳定排序和种子文件集数解析。
|
为批量下载测试提供稳定排序和种子文件集数解析。
|
||||||
|
|||||||
@@ -1,66 +1,9 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from app.core.context import Context, TorrentInfo
|
|
||||||
from app.modules.subtitle import SubtitleModule
|
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():
|
def test_parse_subtitle_links_filters_detail_page_action_links():
|
||||||
"""
|
|
||||||
详情页解析字幕链接时应过滤上传和外部搜索动作链接。
|
|
||||||
"""
|
|
||||||
html_text = """
|
html_text = """
|
||||||
<tr>
|
<tr>
|
||||||
<td class="rowhead" valign="top">字幕</td>
|
<td class="rowhead" valign="top">字幕</td>
|
||||||
@@ -100,47 +43,3 @@ def test_parse_subtitle_links_filters_detail_page_action_links():
|
|||||||
assert links == [
|
assert links == [
|
||||||
"https://audiences.me/downloadsubs.php?torrentid=621182&subid=2148"
|
"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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user