Merge pull request #4372 from cddjr/fix_4371

This commit is contained in:
jxxghp
2025-05-31 13:34:48 +08:00
committed by GitHub
5 changed files with 373 additions and 28 deletions

View File

@@ -866,19 +866,22 @@ class TransferChain(ChainBase, metaclass=Singleton):
logger.info("所有下载器中下载完成的文件已整理完成")
return True
def __get_trans_fileitems(self, fileitem: FileItem) -> List[Tuple[FileItem, bool]]:
def __get_trans_fileitems(
self, fileitem: FileItem, depth: int = 1
) -> List[Tuple[FileItem, bool]]:
"""
获取整理目录或文件列表
:param fileitem: 文件项
:param depth: 递归深度默认为1
"""
def __is_bluray_dir(_fileitem: FileItem) -> bool:
def __contains_bluray_sub(_fileitems: List[FileItem]) -> bool:
"""
判断是不是蓝光目录
判断是否包含蓝光目录
"""
subs = self.storagechain.list_files(_fileitem)
if subs:
for sub in subs:
if _fileitems:
for sub in _fileitems:
if sub.type == "dir" and sub.name in ["BDMV", "CERTIFICATE"]:
return True
return False
@@ -913,25 +916,22 @@ class TransferChain(ChainBase, metaclass=Singleton):
return [(fileitem, False)]
# 蓝光原盘根目录
if __is_bluray_dir(fileitem):
sub_items = self.storagechain.list_files(fileitem) or []
if __contains_bluray_sub(sub_items):
return [(fileitem, True)]
# 需要整理的文件项列表
trans_items = []
# 先检查当前目录的下级目录,以支持合集的情况
for sub_dir in self.storagechain.list_files(fileitem):
for sub_dir in sub_items if depth >= 1 else []:
if sub_dir.type == "dir":
if __is_bluray_dir(sub_dir):
trans_items.append((sub_dir, True))
else:
trans_items.append((sub_dir, False))
trans_items.extend(self.__get_trans_fileitems(sub_dir, depth=depth - 1))
if not trans_items:
# 没有有效子目录,直接整理当前目录
trans_items.append((fileitem, False))
else:
# 有子目录时,把当前目录的文件添加到整理任务中
sub_items = self.storagechain.list_files(fileitem)
if sub_items:
trans_items.extend([(f, False) for f in sub_items if f.type == "file"])
@@ -997,7 +997,9 @@ class TransferChain(ChainBase, metaclass=Singleton):
# 汇总错误信息
err_msgs: List[str] = []
# 待整理目录或文件项
trans_items = self.__get_trans_fileitems(fileitem)
trans_items = self.__get_trans_fileitems(
fileitem, depth=2 # 为解决 issue#4371 深度至少需要>=2
)
# 待整理的文件列表
file_items: List[Tuple[FileItem, bool]] = []

161
tests/cases/files.py Normal file
View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
bluray_files = [
{
"name": "FOLDER",
"children": [
{
"name": "Digimon",
"children": [
{
"name": "Digimon (2055)",
"children": [
{
"name": "BDMV",
"children": [
{
"name": "STREAM",
"children": [
{
"name": "00000.m2ts",
"size": 104857600,
},
{
"name": "00001.m2ts",
"size": 104857600,
},
],
},
],
},
{
"name": "CERTIFICATE",
"children": [],
},
],
},
{
"name": "Digimon (2099)",
"children": [
{
"name": "BDMV",
"children": [
{
"name": "STREAM",
"children": [
{
"name": "00000.m2ts",
"size": 104857600,
},
{
"name": "00001.m2ts",
"size": 104857600,
},
{
"name": "00002.m2ts.!qB",
"size": 104857600,
},
],
},
],
},
{
"name": "CERTIFICATE",
"children": [],
},
],
},
{
"name": "Digimon (2199)",
"children": [
{
"name": "Digimon.2199.mp4",
"size": 104857600,
},
],
},
],
},
{
"name": "Pokemon (2016)",
"children": [
{
"name": "BDMV",
"children": [
{
"name": "STREAM",
"children": [
{
"name": "00000.m2ts",
"size": 104857600,
},
{
"name": "00001.m2ts",
"size": 104857600,
},
],
},
],
},
{
"name": "CERTIFICATE",
"children": [],
},
],
},
{
"name": "Pokemon (2021)",
"children": [
{
"name": "BDMV",
"children": [
{
"name": "STREAM",
"children": [
{
"name": "00000.m2ts",
"size": 104857600,
},
{
"name": "00001.m2ts",
"size": 104857600,
},
],
},
],
},
{
"name": "CERTIFICATE",
"children": [],
},
],
},
{
"name": "Pokemon (2028)",
"children": [
{
"name": "Pokemon.2028.mkv",
"size": 104857600,
},
{
"name": "Pokemon.2028.hdr.mkv.!qB",
"size": 104857600,
},
],
},
{
"name": "Pokemon.2029.mp4",
"size": 104857600,
},
{
"name": "Pokemon (2030)",
"children": [
{
"name": "S",
"size": 104857600,
},
],
},
],
},
]

View File

@@ -139,7 +139,7 @@ meta_cases = [{
"episode": "E06",
"restype": "WEB-DL",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "AAC"
}
}, {
@@ -155,7 +155,7 @@ meta_cases = [{
"episode": "E02",
"restype": "WEB-DL",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "AAC"
}
}, {
@@ -235,7 +235,7 @@ meta_cases = [{
"episode": "",
"restype": "BluRay",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "Atmos TrueHD 7.1"
}
}, {
@@ -363,7 +363,7 @@ meta_cases = [{
"episode": "E01",
"restype": "UHD BluRay",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": ""
}
}, {
@@ -523,7 +523,7 @@ meta_cases = [{
"episode": "E02",
"restype": "BDRIP",
"pix": "1080p",
"video_codec": "X265",
"video_codec": "x265",
"audio_codec": "FLAC"
}
}, {
@@ -571,7 +571,7 @@ meta_cases = [{
"episode": "E05",
"restype": "WEB-DL",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "AAC"
}
}, {
@@ -667,7 +667,7 @@ meta_cases = [{
"episode": "",
"restype": "UHD BluRay DoVi",
"pix": "1080p",
"video_codec": "X265",
"video_codec": "x265",
"audio_codec": "DD+ 7.1"
}
}, {
@@ -683,7 +683,7 @@ meta_cases = [{
"episode": "E16",
"restype": "WEB-DL",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "AAC"
}
}, {
@@ -763,7 +763,7 @@ meta_cases = [{
"episode": "",
"restype": "BluRay",
"pix": "1080p",
"video_codec": "X265 10bit",
"video_codec": "x265 10bit",
"audio_codec": "DTS-HD MA 5.1"
}
}, {
@@ -779,7 +779,7 @@ meta_cases = [{
"episode": "",
"restype": "WEB-DL",
"pix": "1080p",
"video_codec": "X265",
"video_codec": "x265",
"audio_codec": ""
}
}, {
@@ -827,7 +827,7 @@ meta_cases = [{
"episode": "",
"restype": "BluRay",
"pix": "4k",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "DTS"
}
}, {
@@ -859,7 +859,7 @@ meta_cases = [{
"episode": "E01",
"restype": "WEB-DL",
"pix": "1080p",
"video_codec": "X265",
"video_codec": "x265",
"audio_codec": ""
}
}, {
@@ -923,7 +923,7 @@ meta_cases = [{
"episode": "E06",
"restype": "WEBRip",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "DD 5.1"
}
}, {
@@ -939,7 +939,7 @@ meta_cases = [{
"episode": "E05",
"restype": "WEBRip",
"pix": "1080p",
"video_codec": "X264",
"video_codec": "x264",
"audio_codec": "DD 5.1"
}
}, {

View File

@@ -1,5 +1,6 @@
import unittest
from tests.test_bluray import BluRayTest
from tests.test_metainfo import MetaInfoTest
if __name__ == '__main__':
@@ -9,6 +10,9 @@ if __name__ == '__main__':
suite.addTest(MetaInfoTest('test_metainfo'))
suite.addTest(MetaInfoTest('test_emby_format_ids'))
# 测试蓝光目录识别
suite.addTest(BluRayTest())
# 运行测试
runner = unittest.TextTestRunner()
runner.run(suite)

178
tests/test_bluray.py Normal file
View File

@@ -0,0 +1,178 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from pathlib import Path
from typing import List, Optional
from unittest import TestCase
from app import schemas
from app.chain.storage import StorageChain
from app.chain.transfer import TransferChain
from app.db.models.transferhistory import TransferHistory
from app.db.systemconfig_oper import SystemConfigOper
from app.db.transferhistory_oper import TransferHistoryOper
from tests.cases.files import bluray_files
class MockTransferHistoryOper(TransferHistoryOper):
def __init__(self):
# pylint: disable=super-init-not-called
self.history = []
def get_by_src(self, src, storage=None):
self.history.append(src)
return TransferHistory()
class MockStorage(StorageChain):
def __init__(self, files: list):
# pylint: disable=super-init-not-called
self.__root = schemas.FileItem(
path="/", name="", type="dir", extension="", size=0
)
self.__all = {self.__root.path: self.__root}
def __build_child(parent: schemas.FileItem, files: list[dict]):
parent.children = []
for item in files:
children = item.get("children")
sep = "" if parent.path.endswith("/") else "/"
name: str = item["name"]
file_item = schemas.FileItem(
path=f"{parent.path}{sep}{name}",
name=name,
extension=Path(name).suffix[1:],
basename=Path(name).stem,
type="file" if children is None else "dir",
size=item.get("size", 0),
)
parent.children.append(file_item)
self.__all[file_item.path] = file_item
if children is not None:
__build_child(file_item, children)
__build_child(self.__root, files)
def list_files(
self, fileitem: schemas.FileItem, recursion: bool = False
) -> Optional[List[schemas.FileItem]]:
if fileitem.type != "dir":
return None
if recursion:
result = []
file_path = f"{fileitem.path}/"
for path, item in self.__all.items():
if path.startswith(file_path):
result.append(item)
return result
else:
return fileitem.children
def get_file_item(self, storage: str, path: Path) -> Optional[schemas.FileItem]:
"""
根据路径获取文件项
"""
path_posix = path.as_posix()
return self.__all.get(path_posix)
class MockTransferChain(TransferChain):
def __init__(self, storage: MockStorage):
# pylint: disable=super-init-not-called
self.transferhis = MockTransferHistoryOper()
self.systemconfig = SystemConfigOper()
self.storagechain = storage
def test(self, path: str):
self.transferhis.history.clear()
self.do_transfer(
force=False,
background=False,
fileitem=self.storagechain.get_file_item(None, Path(path)),
)
return self.transferhis.history
class BluRayTest(TestCase):
def __init__(self, methodName="test"):
super().__init__(methodName)
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
def test(self):
transfer = MockTransferChain(MockStorage(bluray_files))
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2055)",
"/FOLDER/Digimon/Digimon (2099)",
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4",
],
transfer.test("/FOLDER/Digimon"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2055)",
],
transfer.test("/FOLDER/Digimon/Digimon (2055)"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2055)",
],
transfer.test("/FOLDER/Digimon/Digimon (2055)/BDMV"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2055)",
],
transfer.test("/FOLDER/Digimon/Digimon (2055)/BDMV/STREAM"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2055)",
],
transfer.test("/FOLDER/Digimon/Digimon (2055)/BDMV/STREAM/00001.m2ts"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4",
],
transfer.test("/FOLDER/Digimon/Digimon (2199)"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4",
],
transfer.test("/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4"),
)
self.assertEqual(
[
"/FOLDER/Pokemon.2029.mp4",
],
transfer.test("/FOLDER/Pokemon.2029.mp4"),
)
self.assertEqual(
[
"/FOLDER/Digimon/Digimon (2055)",
"/FOLDER/Digimon/Digimon (2099)",
"/FOLDER/Digimon/Digimon (2199)/Digimon.2199.mp4",
"/FOLDER/Pokemon (2016)",
"/FOLDER/Pokemon (2021)",
"/FOLDER/Pokemon (2028)/Pokemon.2028.mkv",
"/FOLDER/Pokemon.2029.mp4",
],
transfer.test("/FOLDER"),
)