37 headless tests covering the logic that is cheap to break: - split planner: plan_disc_split, chapter helpers (whole-fit, multi-disc partition, single-movie chapter/even-cut split, contiguous coverage) - High-profile fill-disc bitrate math (disc count + bounds + capacity) - ffmpeg command generation: profiles, text/image hardsub, 2-pass, nice, progress-to-file, even pad/crop + positive offsets, -ss seek, and the AC3 448k audio-clamp regression - ffprobe subtitle detection: text/image classification, tags/disposition tests/conftest.py makes the in-tree package importable and installs the gettext _ builtin so modules run headless. Forgejo Actions workflow runs the suite on push/PR. Update CLAUDE.md's testing section accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
82 lines
2.7 KiB
Python
82 lines
2.7 KiB
Python
"""Tests for embedded subtitle detection in ffprobe.process_json."""
|
|
|
|
import json
|
|
import devedeng.ffprobe
|
|
|
|
|
|
def _probe(streams):
|
|
p = devedeng.ffprobe.ffprobe()
|
|
data = json.dumps({"streams": streams})
|
|
err = p.process_json("/movies/input.mkv", data)
|
|
return p, err
|
|
|
|
|
|
def _video(index=0, duration="120.0"):
|
|
return {"index": index, "codec_type": "video", "codec_name": "h264",
|
|
"width": 1920, "height": 1080, "avg_frame_rate": "24/1",
|
|
"duration": duration}
|
|
|
|
|
|
def _audio(index=1):
|
|
return {"index": index, "codec_type": "audio", "codec_name": "aac",
|
|
"sample_rate": "48000"}
|
|
|
|
|
|
def _sub(index, codec, language="eng", title="", default=0, forced=0):
|
|
return {"index": index, "codec_type": "subtitle", "codec_name": codec,
|
|
"tags": {"language": language, "title": title},
|
|
"disposition": {"default": default, "forced": forced}}
|
|
|
|
|
|
def test_no_subtitles_empty_list():
|
|
p, err = _probe([_video(), _audio()])
|
|
assert err is False
|
|
assert p.subtitle_tracks == []
|
|
|
|
|
|
def test_text_subtitle_classified_text():
|
|
p, _ = _probe([_video(), _audio(), _sub(2, "subrip", "eng", default=1)])
|
|
assert len(p.subtitle_tracks) == 1
|
|
t = p.subtitle_tracks[0]
|
|
assert t["index"] == 2
|
|
assert t["codec"] == "subrip"
|
|
assert t["language"] == "eng"
|
|
assert t["kind"] == "text"
|
|
assert t["default"] is True
|
|
assert t["forced"] is False
|
|
|
|
|
|
def test_image_subtitles_classified_image():
|
|
for codec in ("hdmv_pgs_subtitle", "dvd_subtitle", "dvdsub", "pgssub", "xsub"):
|
|
p, _ = _probe([_video(), _audio(), _sub(2, codec)])
|
|
assert p.subtitle_tracks[0]["kind"] == "image", codec
|
|
|
|
|
|
def test_various_text_codecs_classified_text():
|
|
for codec in ("subrip", "ass", "ssa", "mov_text", "webvtt"):
|
|
p, _ = _probe([_video(), _audio(), _sub(2, codec)])
|
|
assert p.subtitle_tracks[0]["kind"] == "text", codec
|
|
|
|
|
|
def test_multiple_subtitle_tracks_in_order():
|
|
p, _ = _probe([
|
|
_video(), _audio(),
|
|
_sub(2, "subrip", "eng", default=1),
|
|
_sub(3, "hdmv_pgs_subtitle", "fre", forced=1),
|
|
_sub(4, "ass", "jpn", title="Signs"),
|
|
])
|
|
assert [t["index"] for t in p.subtitle_tracks] == [2, 3, 4]
|
|
assert [t["kind"] for t in p.subtitle_tracks] == ["text", "image", "text"]
|
|
assert p.subtitle_tracks[1]["forced"] is True
|
|
assert p.subtitle_tracks[2]["title"] == "Signs"
|
|
|
|
|
|
def test_missing_tags_and_disposition_defaults():
|
|
# a subtitle stream with no tags/disposition must not crash
|
|
p, _ = _probe([_video(), _audio(),
|
|
{"index": 2, "codec_type": "subtitle",
|
|
"codec_name": "subrip"}])
|
|
t = p.subtitle_tracks[0]
|
|
assert t["language"] == ""
|
|
assert t["default"] is False
|
|
assert t["forced"] is False
|