refactor(meta): stabilize recognition contract

This commit is contained in:
jxxghp
2026-08-25 08:12:04 +08:00
parent fcf6f0c7df
commit 8e99abd678
6 changed files with 340 additions and 85 deletions
+45
View File
@@ -23,6 +23,19 @@ def test_build_inputs_separates_video_and_music_domains():
for kind, value, _subtitle in music_once if kind == "music_query")
def test_configure_benchmark_runtime_injects_extensions_and_accelerator(monkeypatch):
"""独立基准必须显式注入文件类型和 Rust 适配器,不能依赖应用启动副作用。"""
configure_runtime = Mock()
monkeypatch.setattr(benchmark, "configure_recognition_runtime", configure_runtime)
benchmark.configure_benchmark_runtime()
kwargs = configure_runtime.call_args.kwargs
assert kwargs["accelerator"] is benchmark.rust_accel
assert ".mkv" in kwargs["media_extensions_provider"]()
assert ".flac" in kwargs["audio_extensions_provider"]()
def test_parse_input_uses_public_production_entries(monkeypatch):
"""输入分发应调用 MetaInfo、MetaInfoPath 和 MetaMusic.parse_query 公开入口。"""
title_result = object()
@@ -140,6 +153,24 @@ def test_video_projection_ignores_python_parser_internal_state():
)
def test_video_projection_covers_stable_path_merge_fields():
"""差异投影必须覆盖路径合并容易遗漏的平台、效果、位深和范围字段。"""
result = benchmark.MetaInfo("Show S01E01 2026 2160p AMZN WEB-DL HDR H265 10bit")
result.web_source = "Amazon"
result.resource_team = "GROUP"
result.customization = "CUSTOM"
projected = benchmark.project_video_result(result)
assert projected["web_source"] == "Amazon"
assert projected["resource_effect"] == "HDR"
assert projected["video_bit"] == "10bit"
assert projected["begin_season"] == 1
assert projected["begin_episode"] == 1
assert projected["resource_team"] == "GROUP"
assert projected["customization"] == "CUSTOM"
def test_validate_rust_runtime_rejects_disabled_and_old_extensions(monkeypatch):
"""运行前检查应拒绝关闭的 Rust 和缺少音乐入口的旧扩展。"""
rust_accel = benchmark.rust_accel
@@ -149,6 +180,12 @@ def test_validate_rust_runtime_rejects_disabled_and_old_extensions(monkeypatch):
benchmark.validate_rust_runtime()
monkeypatch.setattr(rust_accel, "is_enabled", Mock(return_value=True))
monkeypatch.setattr(benchmark, "get_metainfo_accelerator", Mock(return_value=rust_accel))
monkeypatch.setattr(
rust_accel,
"parse_metainfo",
Mock(return_value={"en_name": "Benchmark Movie"}),
)
monkeypatch.setattr(rust_accel, "parse_metamusic", Mock(return_value={}), raising=False)
monkeypatch.setattr(rust_accel, "_moviepilot_rust", SimpleNamespace())
@@ -161,6 +198,12 @@ def test_validate_rust_runtime_requires_successful_music_probe(monkeypatch):
rust_accel = benchmark.rust_accel
extension = SimpleNamespace(parse_metamusic_fast=Mock())
monkeypatch.setattr(rust_accel, "is_enabled", Mock(return_value=True))
monkeypatch.setattr(benchmark, "get_metainfo_accelerator", Mock(return_value=rust_accel))
monkeypatch.setattr(
rust_accel,
"parse_metainfo",
Mock(return_value={"en_name": "Benchmark Movie"}),
)
monkeypatch.setattr(rust_accel, "_moviepilot_rust", extension)
monkeypatch.setattr(rust_accel, "parse_metamusic", Mock(return_value=None), raising=False)
@@ -170,6 +213,7 @@ def test_validate_rust_runtime_requires_successful_music_probe(monkeypatch):
def test_main_outputs_independent_video_and_music_metrics(monkeypatch, capsys):
"""主程序应分别输出影视和音乐等价状态、耗时及性能提升。"""
monkeypatch.setattr(benchmark, "configure_benchmark_runtime", Mock())
monkeypatch.setattr(benchmark, "validate_rust_runtime", Mock())
monkeypatch.setattr(benchmark, "build_video_inputs", Mock(return_value=[("title", "V", None)]))
monkeypatch.setattr(
@@ -212,6 +256,7 @@ def test_main_outputs_independent_video_and_music_metrics(monkeypatch, capsys):
def test_main_reports_runtime_failure_with_nonzero_exit(monkeypatch, capsys):
"""Rust 未就绪时主程序应明确报错并返回非零状态。"""
monkeypatch.setattr(benchmark, "configure_benchmark_runtime", Mock())
monkeypatch.setattr(
benchmark,
"validate_rust_runtime",
+79 -1
View File
@@ -7,9 +7,15 @@ import pytest
from app.domain.context import MediaInfo
from app.domain.metainfo import MetaInfo, MetaInfoPath, find_metainfo
from app.domain.meta.metabase import MetaBase
from app.domain.meta.metabase import MetaBase, MetaInfoSnapshot
from app.domain.meta.metamusic import MetaMusic
from app.domain.meta.metaanime import MetaAnime
from app.domain.meta.runtime import (
configure_recognition_runtime,
get_audio_extensions,
get_media_extensions,
get_metainfo_accelerator,
)
from app.application.torrent import TorrentHelper
from app.schemas.types import MediaSource, MediaType
from tests.cases.meta import meta_cases
@@ -100,6 +106,78 @@ def test_metainfopath_with_empty_custom_words():
assert meta is not None
def test_metainfo_snapshot_exposes_complete_immutable_contract():
"""稳定快照应包含路径合并字段,且不受原 MetaBase 后续修改影响。"""
meta = MetaInfo("Show.S01E01.2026.2160p.WEB-DL.HDR.H265.10bit-GROUP.mkv")
meta.web_source = "Amazon"
snapshot = MetaInfoSnapshot.from_meta(meta)
meta.web_source = "Netflix"
assert snapshot.kind == "video"
assert snapshot.begin_season == 1
assert snapshot.begin_episode == 1
assert snapshot.resource_effect == "HDR"
assert snapshot.video_bit == "10bit"
assert snapshot.web_source == "Amazon"
assert snapshot.apply_words == ()
def test_metainfopath_merges_parent_streaming_platform():
"""文件名缺少平台时应从父目录补充,避免路径识别丢失稳定资源字段。"""
media_extensions = get_media_extensions()
audio_extensions = get_audio_extensions()
accelerator = get_metainfo_accelerator()
configure_recognition_runtime(
media_extensions_provider=lambda: (".mkv",),
audio_extensions_provider=lambda: (),
accelerator=None,
)
try:
meta = MetaInfoPath(Path("/Show 2024 AMZN WEB-DL/Show.S01E01.mkv"))
finally:
configure_recognition_runtime(
media_extensions_provider=lambda: media_extensions,
audio_extensions_provider=lambda: audio_extensions,
accelerator=accelerator,
)
assert meta.web_source == "Amazon"
assert meta.year == "2024"
assert meta.episode == "E01"
def test_numeric_video_filename_sets_single_episode_total():
"""纯数字视频文件名表示单集时,范围字段必须保持自洽。"""
media_extensions = get_media_extensions()
audio_extensions = get_audio_extensions()
accelerator = get_metainfo_accelerator()
configure_recognition_runtime(
media_extensions_provider=lambda: (".mkv",),
audio_extensions_provider=lambda: (),
accelerator=None,
)
try:
meta = MetaInfo("5.mkv")
finally:
configure_recognition_runtime(
media_extensions_provider=lambda: media_extensions,
audio_extensions_provider=lambda: audio_extensions,
accelerator=accelerator,
)
assert meta.begin_episode == 5
assert meta.end_episode is None
assert meta.total_episode == 1
def test_empty_video_title_keeps_optional_original_name_none():
"""无法提取标题时 original_name 保持空值,不使用含义不同的空字符串。"""
meta = MetaInfo("S02E1000.mkv")
assert meta.name == ""
assert meta.original_name is None
def test_custom_words_apply_words_recording():
"""测试 apply_words 记录功能。"""
custom_words = ["替换词 => 新词"]