fix: preserve all recognized resource types

This commit is contained in:
jxxghp
2026-08-09 18:41:54 +08:00
parent 36666313b7
commit 53a169d955
5 changed files with 95 additions and 63 deletions

View File

@@ -28,22 +28,27 @@ DIY_TITLE_RE = re.compile(r'-DIY@', re.IGNORECASE)
DESCRIPTION_SPLIT_RE = re.compile(r'[\s/|]+')
SPACE_RE = re.compile(r'\s+')
SEASON_SUFFIX_RE = re.compile(r"SEASON$", re.IGNORECASE)
REMUX_RESOURCE_RE = re.compile(r'(?<![A-Z0-9])REMUX(?![A-Z0-9])', re.IGNORECASE)
def normalize_resource_type(resource_type: Optional[str], title: Optional[str]) -> Optional[str]:
"""
根据原始标题补全连续出现的复合资源类型。
:param resource_type: 解析器已识别的资源类型
:param title: 原始标题
:return: 补全后的资源类型
"""
if resource_type == "UHD BluRay" \
and title \
and REMUX_RESOURCE_RE.search(title):
return f"{resource_type} REMUX"
return resource_type
SOURCE_RE = (
r"^BLURAY$|^HDTV$|^UHDTV$|^HDDVD$|^WEBRIP$|^DVDRIP$|^BDRIP$|"
r"^BLU$|^WEB$|^BD$|^HDRip$|^REMUX$|^UHD$"
)
SOURCE_PATTERN = re.compile(r"(%s)" % SOURCE_RE, re.IGNORECASE)
SOURCE_NAMES = {
"BLURAY": "BluRay",
"HDTV": "HDTV",
"UHDTV": "UHDTV",
"HDDVD": "HDDVD",
"WEBRIP": "WEBRip",
"DVDRIP": "DVDRip",
"BDRIP": "BDRIP",
"BLU": "BLU",
"WEB": "WEB",
"BD": "BD",
"HDRIP": "HDRip",
"REMUX": "REMUX",
"UHD": "UHD",
}
class MetaVideo(MetaBase):
@@ -57,14 +62,14 @@ class MetaVideo(MetaBase):
_last_token_type = ""
_continue_flag = True
_unknown_name_str = ""
_source = ""
_sources = []
_effect = []
# 正则式区
_season_re = r"S(\d{3})|^S(\d{1,3})$|S(\d{1,3})E"
_episode_re = r"EP?(\d{2,4})$|^EP?(\d{1,4})$|^S\d{1,2}EP?(\d{1,4})$|S\d{2}EP?(\d{2,4})"
_part_re = r"(^PART[0-9ABI]{0,2}$|^CD[0-9]{0,2}$|^DVD[0-9]{0,2}$|^DISK[0-9]{0,2}$|^DISC[0-9]{0,2}$)"
_roman_numerals = r"^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"
_source_re = r"^BLURAY$|^HDTV$|^UHDTV$|^HDDVD$|^WEBRIP$|^DVDRIP$|^BDRIP$|^BLU$|^WEB$|^BD$|^HDRip$|^REMUX$|^UHD$"
_source_re = SOURCE_RE
_effect_re = r"^SDR$|^HDR\d*$|^HDRVIVID$|^DOLBY$|^DOVI$|^DV$|^3D$|^REPACK$|^HLG$|^HDR10(\+|Plus)$|^HDR10P$|^VIVID$|^EDR$|^HQ$"
_resources_type_re = r"%s|%s" % (_source_re, _effect_re)
_name_no_begin_re = r"^[\[【].+?[\]】]"
@@ -91,7 +96,7 @@ class MetaVideo(MetaBase):
_episode_pattern = re.compile(_episode_re, re.IGNORECASE)
_part_pattern = re.compile(_part_re, re.IGNORECASE)
_roman_numerals_pattern = re.compile(_roman_numerals)
_source_pattern = re.compile(r"(%s)" % _source_re, re.IGNORECASE)
_source_pattern = SOURCE_PATTERN
_effect_pattern = re.compile(r"(%s)" % _effect_re, re.IGNORECASE)
_resources_type_pattern = re.compile(r"(%s)" % _resources_type_re, re.IGNORECASE)
_name_no_chinese_pattern = re.compile(_name_no_chinese_re, re.IGNORECASE)
@@ -114,7 +119,7 @@ class MetaVideo(MetaBase):
if not title:
return
original_title = title
self._source = ""
self._sources = []
self._effect = []
self._index = 0
# 判断是否纯数字命名
@@ -200,8 +205,8 @@ class MetaVideo(MetaBase):
if self._effect:
self._effect.reverse()
self.resource_effect = " ".join(self._effect)
if self._source:
self.resource_type = normalize_resource_type(self._source.strip(), original_title)
if self._sources:
self.resource_type = " ".join(self._sources)
# 提取原盘DIY
if self.resource_type and "BluRay" in self.resource_type:
if (self.subtitle and DIY_RE.search(self.subtitle)) \
@@ -590,6 +595,29 @@ class MetaVideo(MetaBase):
elif token.upper() == "EPISODE":
self._last_token_type = "EPISODE"
def __append_resource_source(self, source: str) -> None:
"""
按出现顺序追加资源类型并忽略重复项。
:param source: 原始资源类型标记
"""
source_name = SOURCE_NAMES.get(source.upper(), source)
if source_name.casefold() not in {
item.casefold() for item in self._sources
}:
self._sources.append(source_name)
def __replace_last_resource_source(self, source: str, replacement: str) -> None:
"""
将拆分的资源类型前缀替换为完整规范名称。
:param source: 待替换的末尾资源类型
:param replacement: 完整资源类型
"""
if self._sources and self._sources[-1].casefold() == source.casefold():
self._sources.pop()
self.__append_resource_source(replacement)
def __init_resource_type(self, token):
"""
识别资源类型
@@ -599,32 +627,17 @@ class MetaVideo(MetaBase):
if token.upper() == "DL" \
and self._last_token_type == "source" \
and self._last_token == "WEB":
self._source = "WEB-DL"
self.__replace_last_resource_source("WEB", "WEB-DL")
self._continue_flag = False
return
elif token.upper() == "RAY" \
and self._last_token_type == "source" \
and self._last_token == "BLU":
# UHD BluRay组合
if self._source == "UHD":
self._source = "UHD BluRay"
else:
self._source = "BluRay"
self.__replace_last_resource_source("BLU", "BluRay")
self._continue_flag = False
return
elif token.upper() == "WEBDL":
self._source = "WEB-DL"
self._continue_flag = False
return
# UHD REMUX组合
if token.upper() == "REMUX" \
and self._source == "BluRay":
self._source = "BluRay REMUX"
self._continue_flag = False
return
elif token.upper() == "BLURAY" \
and self._source == "UHD":
self._source = "UHD BluRay"
self.__append_resource_source("WEB-DL")
self._continue_flag = False
return
source_res = self._source_pattern.search(token)
@@ -632,9 +645,9 @@ class MetaVideo(MetaBase):
self._last_token_type = "source"
self._continue_flag = False
self._stop_name_flag = True
if not self._source:
self._source = source_res.group(1)
self._last_token = self._source.upper()
source = source_res.group(1)
self.__append_resource_source(source)
self._last_token = source.upper()
return
effect_res = self._effect_pattern.search(token)
if effect_res:

View File

@@ -11,7 +11,6 @@ from app.core.meta.infopath import (
clear_parsed_title_for_parent_merge,
should_use_parent_title_for_file_stem,
)
from app.core.meta.metavideo import normalize_resource_type
from app.core.meta.words import WordsMatcher
from app.log import logger
from app.schemas.types import MediaType
@@ -380,10 +379,7 @@ def _meta_from_rust(parsed: dict) -> Optional[MetaBase]:
"begin_episode": parsed.get("begin_episode"),
"end_episode": parsed.get("end_episode"),
"part": parsed.get("part"),
"resource_type": normalize_resource_type(
parsed.get("resource_type"),
parsed.get("org_string") or parsed.get("title"),
),
"resource_type": parsed.get("resource_type"),
"resource_effect": parsed.get("resource_effect"),
"resource_pix": parsed.get("resource_pix"),
"resource_team": parsed.get("resource_team"),

View File

@@ -1,4 +1,4 @@
moviepilot-rust~=0.2.5
moviepilot-rust~=0.2.6
pydantic>=2.13.4,<3.0.0
pydantic-settings>=2.14.2,<3.0.0
SQLAlchemy~=2.0.50

View File

@@ -1246,7 +1246,7 @@ meta_cases = [{
"part": "",
"season": "",
"episode": "",
"restype": "UHD",
"restype": "UHD BluRay",
"pix": "2160p",
"video_codec": "HEVC",
"audio_codec": "DTS-HD MA 5.1"

View File

@@ -3,6 +3,8 @@ from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
import pytest
from app.core.metainfo import MetaInfo, MetaInfoPath, find_metainfo
from app.core.meta import MetaBase, MetaMusic
from app.core.meta.metaanime import MetaAnime
@@ -173,25 +175,46 @@ def test_python_metainfo_fallback_preserves_xxx_movie_title():
assert meta.audio_encode == "DDP 5.1"
def test_metainfo_preserves_uhd_bluray_remux_resource_type():
"""默认解析入口应保留 UHD、BluRay 与 REMUX 三段资源类型。"""
meta = MetaInfo(
RESOURCE_TYPE_CASES = [
(
"They.Will.Kill.You.2026.2160p.UHD.BluRay.Remux."
"HEVC.DV.TrueHD.7.1.Atmos.mkv"
)
assert meta.resource_type == "UHD BluRay REMUX"
"HEVC.DV.TrueHD.7.1.Atmos.mkv",
"UHD BluRay REMUX",
),
(
"Movie.2026.2160p.UHD.Blu-ray.Remux.BDRip.HEVC.mkv",
"UHD BluRay REMUX BDRIP",
),
(
"Movie.2026.2160p.UHD.BluRay.UHD.Remux.Remux.HEVC.mkv",
"UHD BluRay REMUX",
),
(
"Movie.2026.1080p.WEB-DL.WEBRip.Remux.H264.mkv",
"WEB-DL WEBRip REMUX",
),
]
def test_python_metainfo_preserves_uhd_bluray_remux_resource_type():
"""Python 兜底解析应保留 UHD、BluRay 与 REMUX 三段资源类型。"""
@pytest.mark.parametrize(("title", "expected"), RESOURCE_TYPE_CASES)
def test_metainfo_preserves_all_resource_types(title, expected):
"""默认解析入口应按顺序保留并去重所有受支持的资源类型。"""
meta = MetaInfo(title)
assert meta.resource_type == expected
assert meta.edition.startswith(expected)
assert expected in meta.resource_term
@pytest.mark.parametrize(("title", "expected"), RESOURCE_TYPE_CASES)
def test_python_metainfo_preserves_all_resource_types(title, expected):
"""Python 兜底解析应按顺序保留并去重所有受支持的资源类型。"""
with patch("app.core.metainfo.rust_accel.parse_metainfo", return_value=None):
meta = MetaInfo(
"They.Will.Kill.You.2026.2160p.UHD.BluRay.Remux."
"HEVC.DV.TrueHD.7.1.Atmos.mkv"
)
meta = MetaInfo(title)
assert meta.resource_type == "UHD BluRay REMUX"
assert meta.resource_type == expected
assert meta.edition.startswith(expected)
assert expected in meta.resource_term
def test_metainfo_routes_audio_filename_to_music():