"""Tests for extract_text.py; yt-dlp is always replaced by fakes (no network).""" import sys import pytest import extract_text import yt_untertitel as yt URL = "https://www.youtube.com/watch?v=abc123" VTT = ( "WEBVTT\n\n" "00:00:01.000 --> 00:00:02.000\n \nGuten<00:00:01.500> Abend.\n\n" "00:00:02.000 --> 00:00:03.000\nGuten Abend.\nSchön, dass Sie da sind.\n" ) GERMAN_VIDEO = { "id": "abc123", "title": "Eine Rede", "channel": "Kanal", "upload_date": "20260328", "subtitles": {"en": []}, "automatic_captions": {"de-orig": [], "de": [], "en": []}, } pytestmark = pytest.mark.usefixtures("yt_dlp_installed") def run_main(monkeypatch, *args): monkeypatch.setattr(sys, "argv", ["extract_text.py", *map(str, args)]) extract_text.main() def test_default_language_is_original_audio(fake_yt_dlp, tmp_path, monkeypatch, capsys): run = fake_yt_dlp(GERMAN_VIDEO, vtt=VTT) monkeypatch.setattr(yt.subprocess, "run", run) monkeypatch.chdir(tmp_path) run_main(monkeypatch, "-u", URL) text = (tmp_path / "Kanal_Eine_Rede_2026-03-28.txt").read_text(encoding="utf-8") assert text == "Guten Abend.\nSchön, dass Sie da sind.\n" assert "de-orig" in run.calls[1] assert "--write-auto-subs" in run.calls[1] out = capsys.readouterr().out assert "Kanal_Eine_Rede_2026-03-28.txt (2 Zeilen, 7 Wörter)" in out # Only the text file remains, no VTT assert [p.name for p in tmp_path.iterdir()] == ["Kanal_Eine_Rede_2026-03-28.txt"] def test_default_language_ignores_dubbed_tracks(fake_yt_dlp, tmp_path, monkeypatch): info = { "id": "dub", "language": "en-US", "subtitles": {"en-US": []}, "automatic_captions": {"ar-orig": [], "en-orig": []}, } run = fake_yt_dlp(info, vtt=VTT) monkeypatch.setattr(yt.subprocess, "run", run) run_main(monkeypatch, "-u", URL, "-o", tmp_path / "out.txt") download = run.calls[1] assert download[download.index("--sub-langs") + 1] == "en-US" assert "--write-subs" in download def test_fliesstext(fake_yt_dlp, tmp_path, monkeypatch, capsys): long_vtt = "WEBVTT\n\n" + "".join( f"00:00:{i:02d}.000 --> 00:00:{i + 1:02d}.000\nSatz Nummer {i} zum EU-Gipfel.\n\n" for i in range(20) ) monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(GERMAN_VIDEO, vtt=long_vtt)) target = tmp_path / "rede.txt" run_main(monkeypatch, "-u", URL, "-f", "-o", target) lines = target.read_text(encoding="utf-8").splitlines() assert 1 < len(lines) < 20 assert all(len(line) <= extract_text.FLOW_WIDTH for line in lines) assert " ".join(lines) == " ".join(f"Satz Nummer {i} zum EU-Gipfel." for i in range(20)) assert "100 Wörter" in capsys.readouterr().out def test_explicit_language_prefers_manual(fake_yt_dlp, tmp_path, monkeypatch): run = fake_yt_dlp(GERMAN_VIDEO, vtt=VTT) monkeypatch.setattr(yt.subprocess, "run", run) target = tmp_path / "neu" / "rede.en.txt" run_main(monkeypatch, "-u", URL, "-l", "en", "-o", target) assert target.is_file() assert "--write-subs" in run.calls[1] def test_original_language_from_metadata_without_auto_captions( fake_yt_dlp, tmp_path, monkeypatch ): info = {"id": "x", "language": "fr", "subtitles": {"fr": []}, "automatic_captions": {}} run = fake_yt_dlp(info, vtt=VTT) monkeypatch.setattr(yt.subprocess, "run", run) run_main(monkeypatch, "-u", URL, "-o", tmp_path / "out.txt") assert run.calls[1][run.calls[1].index("--sub-langs") + 1] == "fr" @pytest.mark.parametrize("info, args, message", [ # Translation only: no "en-orig", no manual "en" (GERMAN_VIDEO, ["-l", "fr"], "Keine Untertitel in „fr“"), ({"id": "x", "subtitles": {}, "automatic_captions": {}}, [], "Originalsprache"), ]) def test_no_matching_track(fake_yt_dlp, tmp_path, monkeypatch, info, args, message): monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(info, vtt=VTT)) with pytest.raises(SystemExit, match=message): run_main(monkeypatch, "-u", URL, "-o", tmp_path / "out.txt", *args) assert not (tmp_path / "out.txt").exists() def test_empty_subtitles(fake_yt_dlp, tmp_path, monkeypatch): monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(GERMAN_VIDEO, vtt="WEBVTT\n")) with pytest.raises(SystemExit, match="keinen Text"): run_main(monkeypatch, "-u", URL, "-o", tmp_path / "out.txt") @pytest.mark.parametrize("args, message", [ ([], "required"), (["-u", URL, "-o", "{dir}"], "ist ein Verzeichnis"), ]) def test_cli_errors(tmp_path, monkeypatch, capsys, args, message): args = [a.format(dir=tmp_path) for a in args] with pytest.raises(SystemExit) as exc: run_main(monkeypatch, *args) assert exc.value.code == 2 assert message in capsys.readouterr().err def test_time_range_default_name(fake_yt_dlp, tmp_path, monkeypatch): monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(GERMAN_VIDEO, vtt=VTT)) monkeypatch.chdir(tmp_path) run_main(monkeypatch, "-u", URL, "-s", "2", "-e", "0:03") name = "Kanal_Eine_Rede_2026-03-28_00-00-02_bis_00-00-03.txt" assert (tmp_path / name).read_text(encoding="utf-8") == "Schön, dass Sie da sind.\n" def test_time_range_without_text(fake_yt_dlp, tmp_path, monkeypatch): monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(GERMAN_VIDEO, vtt=VTT)) with pytest.raises(SystemExit, match="im gewählten Zeitraum keinen Text"): run_main(monkeypatch, "-u", URL, "-s", "1:00", "-o", tmp_path / "out.txt") def test_time_range_order_error(monkeypatch, capsys): with pytest.raises(SystemExit) as exc: run_main(monkeypatch, "-u", URL, "-s", "2:00", "-e", "1:00") assert exc.value.code == 2 assert "muss vor -e/--ende liegen" in capsys.readouterr().err