mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-02 05:11:31 +08:00
修复 RAR 字幕包下载识别
This commit is contained in:
@@ -36,6 +36,11 @@ class DownloadChain(ChainBase):
|
||||
下载处理链
|
||||
"""
|
||||
|
||||
_SUBTITLE_ARCHIVE_FORMATS = {
|
||||
".zip": "zip",
|
||||
".rar": "rar",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _safe_subtitle_file_name(file_name: str, fallback_name: str) -> str:
|
||||
"""
|
||||
@@ -51,7 +56,14 @@ class DownloadChain(ChainBase):
|
||||
"""
|
||||
判断是否为字幕压缩包。
|
||||
"""
|
||||
return Path(file_name).suffix.lower() == ".zip"
|
||||
return Path(file_name).suffix.lower() in DownloadChain._SUBTITLE_ARCHIVE_FORMATS
|
||||
|
||||
@classmethod
|
||||
def _subtitle_archive_format(cls, file_name: str) -> Optional[str]:
|
||||
"""
|
||||
获取字幕压缩包格式。
|
||||
"""
|
||||
return cls._SUBTITLE_ARCHIVE_FORMATS.get(Path(file_name).suffix.lower())
|
||||
|
||||
@staticmethod
|
||||
def _is_subtitle_file(file_name: str) -> bool:
|
||||
@@ -154,7 +166,15 @@ class DownloadChain(ChainBase):
|
||||
try:
|
||||
temp_file.write_bytes(response.content)
|
||||
if self._is_subtitle_archive(file_name):
|
||||
shutil.unpack_archive(temp_file, temp_extract_dir, format='zip')
|
||||
try:
|
||||
SystemUtils.unpack_archive(
|
||||
temp_file,
|
||||
temp_extract_dir,
|
||||
archive_format=self._subtitle_archive_format(file_name),
|
||||
)
|
||||
except Exception as err:
|
||||
logger.error(f"字幕压缩包解压失败:{temp_file} - {str(err)}")
|
||||
return []
|
||||
for sub_file in SystemUtils.list_files(temp_extract_dir, settings.RMT_SUBEXT):
|
||||
uploaded_path = self._upload_subtitle_file(
|
||||
storage_chain=storage_chain,
|
||||
|
||||
@@ -28,6 +28,11 @@ class SubtitleModule(_ModuleBase):
|
||||
字幕下载模块
|
||||
"""
|
||||
|
||||
_SUBTITLE_ARCHIVE_FORMATS = {
|
||||
".zip": "zip",
|
||||
".rar": "rar",
|
||||
}
|
||||
|
||||
# 站点详情页字幕下载元素识别XPATH
|
||||
_SITE_SUBTITLE_XPATH = [
|
||||
'//td[@class="rowhead"][text()="字幕"]/following-sibling::td//a[not(@class)]',
|
||||
@@ -233,40 +238,52 @@ class SubtitleModule(_ModuleBase):
|
||||
ua=torrent.site_ua,
|
||||
proxies=settings.PROXY if torrent.site_proxy else None,
|
||||
)
|
||||
settings.TEMP_PATH.mkdir(parents=True, exist_ok=True)
|
||||
for sublink in sublink_list:
|
||||
logger.info(f"找到字幕下载链接:{sublink},开始下载...")
|
||||
# 下载
|
||||
ret = request.get_res(sublink)
|
||||
if ret and ret.status_code == 200:
|
||||
# 保存ZIP
|
||||
file_name = TorrentHelper.get_url_filename(ret, sublink)
|
||||
if not file_name:
|
||||
logger.warn(f"链接不是字幕文件:{sublink}")
|
||||
continue
|
||||
if file_name.lower().endswith(".zip"):
|
||||
# ZIP包
|
||||
zip_file = settings.TEMP_PATH / file_name
|
||||
archive_format = self._SUBTITLE_ARCHIVE_FORMATS.get(Path(file_name).suffix.lower())
|
||||
if archive_format:
|
||||
archive_file = settings.TEMP_PATH / file_name
|
||||
# 保存
|
||||
zip_file.write_bytes(ret.content)
|
||||
archive_file.write_bytes(ret.content)
|
||||
# 解压路径
|
||||
zip_path = zip_file.with_name(zip_file.stem)
|
||||
# 解压文件
|
||||
shutil.unpack_archive(zip_file, zip_path, format='zip')
|
||||
# 遍历转移文件
|
||||
for sub_file in SystemUtils.list_files(zip_path, settings.RMT_SUBEXT):
|
||||
target_sub_file = Path(working_dir_item.path) / Path(sub_file.name)
|
||||
if storageChain.get_file_item(storage, 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)
|
||||
archive_path = archive_file.with_name(archive_file.stem)
|
||||
try:
|
||||
# 解压文件
|
||||
SystemUtils.unpack_archive(
|
||||
archive_file,
|
||||
archive_path,
|
||||
archive_format=archive_format,
|
||||
)
|
||||
# 遍历转移文件
|
||||
for sub_file in SystemUtils.list_files(archive_path, settings.RMT_SUBEXT):
|
||||
target_sub_file = Path(working_dir_item.path) / Path(sub_file.name)
|
||||
if storageChain.get_file_item(storage, 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)
|
||||
except Exception as err:
|
||||
logger.error(f"字幕压缩包解压失败:{archive_file} - {str(err)}")
|
||||
# 删除临时文件
|
||||
try:
|
||||
shutil.rmtree(zip_path)
|
||||
zip_file.unlink()
|
||||
if archive_path.exists():
|
||||
shutil.rmtree(archive_path)
|
||||
if archive_file.exists():
|
||||
archive_file.unlink()
|
||||
except Exception as err:
|
||||
logger.error(f"删除临时文件失败:{str(err)}")
|
||||
else:
|
||||
if Path(file_name).suffix.lower() not in settings.RMT_SUBEXT:
|
||||
logger.warn(f"链接不是支持的字幕文件:{sublink} - {file_name}")
|
||||
continue
|
||||
sub_file = settings.TEMP_PATH / file_name
|
||||
# 保存
|
||||
sub_file.write_bytes(ret.content)
|
||||
|
||||
@@ -262,6 +262,84 @@ class SystemUtils:
|
||||
_scan_directory(directory, recursive)
|
||||
return files
|
||||
|
||||
@staticmethod
|
||||
def unpack_archive(archive_file: Path, extract_dir: Path, archive_format: Optional[str] = None) -> None:
|
||||
"""
|
||||
解压压缩包,并补充标准库未覆盖的 RAR 格式支持。
|
||||
|
||||
:param archive_file: 待解压的压缩包文件
|
||||
:param extract_dir: 解压目标目录
|
||||
:param archive_format: 压缩包格式,未指定时按文件后缀推断
|
||||
"""
|
||||
if archive_format == "rar" or (not archive_format and archive_file.suffix.lower() == ".rar"):
|
||||
SystemUtils.__unpack_rar_archive(archive_file, extract_dir)
|
||||
return
|
||||
shutil.unpack_archive(archive_file, extract_dir, format=archive_format)
|
||||
|
||||
@staticmethod
|
||||
def __unpack_rar_archive(archive_file: Path, extract_dir: Path) -> None:
|
||||
"""
|
||||
调用系统解压工具处理 RAR 压缩包。
|
||||
"""
|
||||
extract_dir.mkdir(parents=True, exist_ok=True)
|
||||
commands = []
|
||||
if shutil.which("unar"):
|
||||
commands.append([
|
||||
"unar",
|
||||
"-quiet",
|
||||
"-force-overwrite",
|
||||
"-output-directory",
|
||||
extract_dir.as_posix(),
|
||||
archive_file.as_posix(),
|
||||
])
|
||||
if shutil.which("unrar"):
|
||||
commands.append([
|
||||
"unrar",
|
||||
"x",
|
||||
"-o+",
|
||||
"-idq",
|
||||
archive_file.as_posix(),
|
||||
f"{extract_dir.as_posix()}/",
|
||||
])
|
||||
if shutil.which("7z"):
|
||||
commands.append([
|
||||
"7z",
|
||||
"x",
|
||||
"-y",
|
||||
f"-o{extract_dir.as_posix()}",
|
||||
archive_file.as_posix(),
|
||||
])
|
||||
if shutil.which("bsdtar"):
|
||||
commands.append([
|
||||
"bsdtar",
|
||||
"-xf",
|
||||
archive_file.as_posix(),
|
||||
"-C",
|
||||
extract_dir.as_posix(),
|
||||
])
|
||||
if not commands:
|
||||
raise RuntimeError("未找到可用的 RAR 解压工具,请安装 unar、unrar、7z 或 bsdtar")
|
||||
|
||||
errors = []
|
||||
for command in commands:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=120,
|
||||
)
|
||||
except Exception as err:
|
||||
errors.append(f"{command[0]}:{str(err)}")
|
||||
continue
|
||||
if result.returncode == 0:
|
||||
return
|
||||
output = (result.stderr or result.stdout or "").strip()
|
||||
errors.append(f"{command[0]}:{output or f'返回码 {result.returncode}'}")
|
||||
raise RuntimeError(f"RAR 压缩包解压失败:{';'.join(errors)}")
|
||||
|
||||
@staticmethod
|
||||
def exits_files(directory: Path, extensions: list, min_filesize: int = 0, recursive: bool = True) -> bool:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user