chore: snapshot current project updates
This commit is contained in:
@@ -117,6 +117,102 @@ def test_generate_plan_for_series_with_season_and_episode(config):
|
||||
assert not operation.has_conflict
|
||||
|
||||
|
||||
def test_generate_plan_blocks_series_with_high_season(config):
|
||||
"""Series season above threshold should be no-op for manual review."""
|
||||
video_file = VideoFile(
|
||||
path=Path("/mnt/nas/videos/series/Show.Name.S20E01.mkv"),
|
||||
filename="Show.Name.S20E01.mkv",
|
||||
size_bytes=500000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="series"
|
||||
)
|
||||
identity = SeriesIdentity(
|
||||
title="Show Name",
|
||||
season=20,
|
||||
episodes=[1],
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="Show.Name.S20E01.mkv"
|
||||
)
|
||||
|
||||
plan = generate_plan([(video_file, identity)], config)
|
||||
operation = plan.operations[0]
|
||||
assert operation.operation_type == "no-op"
|
||||
assert "season exceeds configured threshold" in operation.reason
|
||||
|
||||
|
||||
def test_generate_plan_blocks_series_with_high_episode(config):
|
||||
"""Series episode above threshold should be no-op for manual review."""
|
||||
video_file = VideoFile(
|
||||
path=Path("/mnt/nas/videos/series/Show.Name.S01E120.mkv"),
|
||||
filename="Show.Name.S01E120.mkv",
|
||||
size_bytes=500000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="series"
|
||||
)
|
||||
identity = SeriesIdentity(
|
||||
title="Show Name",
|
||||
season=1,
|
||||
episodes=[120],
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="Show.Name.S01E120.mkv"
|
||||
)
|
||||
|
||||
plan = generate_plan([(video_file, identity)], config)
|
||||
operation = plan.operations[0]
|
||||
assert operation.operation_type == "no-op"
|
||||
assert "episode exceeds configured threshold" in operation.reason
|
||||
|
||||
|
||||
def test_generate_plan_sample_is_noop_by_default(config):
|
||||
"""Sample files should be excluded from organize actions by default."""
|
||||
video_file = VideoFile(
|
||||
path=Path("/mnt/nas/videos/series/Show.Name.Sample.S01E01.mkv"),
|
||||
filename="Show.Name.Sample.S01E01.mkv",
|
||||
size_bytes=500000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="series"
|
||||
)
|
||||
identity = SeriesIdentity(
|
||||
title="Show Name",
|
||||
season=1,
|
||||
episodes=[1],
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="Show.Name.Sample.S01E01.mkv"
|
||||
)
|
||||
|
||||
plan = generate_plan([(video_file, identity)], config)
|
||||
operation = plan.operations[0]
|
||||
assert operation.operation_type == "no-op"
|
||||
assert "Sample file excluded" in operation.reason
|
||||
|
||||
|
||||
def test_generate_plan_sample_can_be_included_via_config(config):
|
||||
"""When include_sample_files is enabled, sample files can be organized."""
|
||||
config.plan_include_sample_files = True
|
||||
video_file = VideoFile(
|
||||
path=Path("/mnt/nas/videos/series/Show.Name.Sample.S01E01.mkv"),
|
||||
filename="Show.Name.Sample.S01E01.mkv",
|
||||
size_bytes=500000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="series"
|
||||
)
|
||||
identity = SeriesIdentity(
|
||||
title="Show Name",
|
||||
season=1,
|
||||
episodes=[1],
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="Show.Name.Sample.S01E01.mkv"
|
||||
)
|
||||
|
||||
plan = generate_plan([(video_file, identity)], config)
|
||||
operation = plan.operations[0]
|
||||
assert operation.operation_type == "move"
|
||||
|
||||
|
||||
def test_generate_plan_for_series_without_season(config):
|
||||
"""Test plan generation for a series without season (needs review)."""
|
||||
video_file = VideoFile(
|
||||
@@ -423,6 +519,147 @@ def test_generate_plan_with_analysis_by_quality(config):
|
||||
assert "画质" in plan.operations[0].reason
|
||||
|
||||
|
||||
def test_generate_plan_with_analysis_by_reputation_quality_time_reason(config):
|
||||
"""New strategy should quarantine duplicates with explicit reason text."""
|
||||
config.duplicate_keep = "by_reputation_quality_time"
|
||||
p_old = Path("/mnt/nas/videos/movie/Test.2020.720p.WEB-DL.mkv")
|
||||
p_new = Path("/mnt/nas/videos/movie/Test.2020.1080p.BluRay.mkv")
|
||||
vf_old = VideoFile(
|
||||
path=p_old,
|
||||
filename="Test.2020.720p.WEB-DL.mkv",
|
||||
size_bytes=1000000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="movie",
|
||||
)
|
||||
vf_new = VideoFile(
|
||||
path=p_new,
|
||||
filename="Test.2020.1080p.BluRay.mkv",
|
||||
size_bytes=2000000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="movie",
|
||||
)
|
||||
identity = MovieIdentity(
|
||||
title="Test",
|
||||
year=2020,
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="Test.2020.mkv",
|
||||
)
|
||||
identities = [(vf_old, identity), (vf_new, identity)]
|
||||
analysis_data = {
|
||||
"metadata": {"source_identities": "identities.json"},
|
||||
"completeness": [],
|
||||
"duplicates": [
|
||||
{
|
||||
"identity": {"type": "movie", "title": "Test", "year": 2020},
|
||||
"files": [str(p_old), str(p_new)],
|
||||
"quality_comparison": [
|
||||
{"path": str(p_old), "resolution": "1280x720", "size_bytes": 1000000},
|
||||
{"path": str(p_new), "resolution": "1920x1080", "size_bytes": 2000000},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
plan = generate_plan(identities, config, analysis_data=analysis_data)
|
||||
assert plan.summary["quarantine"] == 1
|
||||
assert "外部评分>画质>时间" in plan.operations[0].reason
|
||||
|
||||
|
||||
def test_generate_plan_with_analysis_by_reputation_missing_scores_uses_fallback_reason(config):
|
||||
"""by_reputation should clearly state quality fallback when reputation is missing."""
|
||||
config.duplicate_keep = "by_reputation"
|
||||
p_web = Path("/mnt/nas/videos/movie/Test.2020.2160p.WEB-DL.mkv")
|
||||
p_bluray = Path("/mnt/nas/videos/movie/Test.2020.1080p.BluRay.mkv")
|
||||
vf_web = VideoFile(
|
||||
path=p_web,
|
||||
filename="Test.2020.2160p.WEB-DL.mkv",
|
||||
size_bytes=3000000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="movie",
|
||||
)
|
||||
vf_bluray = VideoFile(
|
||||
path=p_bluray,
|
||||
filename="Test.2020.1080p.BluRay.mkv",
|
||||
size_bytes=2000000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="movie",
|
||||
)
|
||||
identity = MovieIdentity(
|
||||
title="Test",
|
||||
year=2020,
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="Test.2020.mkv",
|
||||
)
|
||||
identities = [(vf_web, identity), (vf_bluray, identity)]
|
||||
analysis_data = {
|
||||
"metadata": {"source_identities": "identities.json"},
|
||||
"completeness": [],
|
||||
"duplicates": [
|
||||
{
|
||||
"identity": {"type": "movie", "title": "Test", "year": 2020},
|
||||
"files": [str(p_web), str(p_bluray)],
|
||||
"quality_comparison": [
|
||||
{"path": str(p_web), "resolution": "3840x2160", "size_bytes": 3000000},
|
||||
{"path": str(p_bluray), "resolution": "1920x1080", "size_bytes": 2000000},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
plan = generate_plan(identities, config, analysis_data=analysis_data)
|
||||
assert plan.summary["quarantine"] == 1
|
||||
assert "评分缺失/并列" in plan.operations[0].reason
|
||||
assert "删除建议(仅隔离建议" in plan.human_summary
|
||||
assert "评分依据不足" in plan.human_summary
|
||||
|
||||
|
||||
def test_generate_plan_human_summary_marks_disc_files_as_high_risk(config):
|
||||
"""Disc/part files should be listed with a conservative risk note in summary."""
|
||||
config.duplicate_keep = "by_reputation"
|
||||
p_disc1 = Path("/mnt/nas/videos/movie/The.Best.of.Youth.DISC1.mkv")
|
||||
p_disc2 = Path("/mnt/nas/videos/movie/The.Best.of.Youth.DISC2.mkv")
|
||||
vf1 = VideoFile(
|
||||
path=p_disc1,
|
||||
filename="The.Best.of.Youth.DISC1.mkv",
|
||||
size_bytes=2000000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="movie",
|
||||
)
|
||||
vf2 = VideoFile(
|
||||
path=p_disc2,
|
||||
filename="The.Best.of.Youth.DISC2.mkv",
|
||||
size_bytes=1800000,
|
||||
modified_timestamp=datetime.now(),
|
||||
category="movie",
|
||||
)
|
||||
identity = MovieIdentity(
|
||||
title="The Best of Youth",
|
||||
year=2003,
|
||||
confidence=0.9,
|
||||
needs_review=False,
|
||||
original_filename="The.Best.of.Youth.DISC1.mkv",
|
||||
)
|
||||
plan = generate_plan(
|
||||
[(vf1, identity), (vf2, identity)],
|
||||
config,
|
||||
analysis_data={
|
||||
"metadata": {"source_identities": "identities.json"},
|
||||
"completeness": [],
|
||||
"duplicates": [
|
||||
{
|
||||
"identity": {"type": "movie", "title": "The Best of Youth", "year": 2003},
|
||||
"files": [str(p_disc1), str(p_disc2)],
|
||||
"quality_comparison": [
|
||||
{"path": str(p_disc1), "resolution": "1920x1080", "size_bytes": 2000000},
|
||||
{"path": str(p_disc2), "resolution": "1920x1080", "size_bytes": 1800000},
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
assert "疑似多碟/分段文件" in plan.human_summary
|
||||
|
||||
|
||||
def test_generate_plan_with_different_extensions(config):
|
||||
"""Test plan generation preserves file extensions."""
|
||||
extensions = [".mp4", ".mkv", ".avi"]
|
||||
|
||||
Reference in New Issue
Block a user