mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-05 03:38:36 +08:00
Merge pull request #4372 from cddjr/fix_4371
This commit is contained in:
@@ -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
161
tests/cases/files.py
Normal 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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -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"
|
||||
}
|
||||
}, {
|
||||
|
||||
@@ -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
178
tests/test_bluray.py
Normal 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"),
|
||||
)
|
||||
Reference in New Issue
Block a user