youtube_abschrift_korrigieren/yt_untertitel.py
dschlueter ba61434798 feat: extract_text.py – YouTube-URL → Textdatei in einem Schritt
- Kombiniert yt_untertitel.py und vtt2txt.py; die VTT bleibt temporär
- Standardsprache ist die des Original-Audios (original_language())
- -o Ausgabedatei, Standard output.txt
- Tests ohne Netz, README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:51:32 +02:00

129 lines
4.4 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Lädt die Untertitel eines YouTube-Videos als VTT-Datei herunter (über yt-dlp),
als Eingabe für vtt2txt.py bzw. rede_korrigieren.py."""
from pathlib import Path
import argparse
import json
import shutil
import subprocess
YT_DLP = "yt-dlp"
def fetch_info(url):
"""Return the video metadata from yt-dlp, without downloading anything."""
result = subprocess.run(
[YT_DLP, "--dump-single-json", "--skip-download", "--no-playlist", url],
capture_output=True, text=True,
)
if result.returncode != 0:
raise SystemExit(f"yt-dlp konnte das Video nicht abrufen:\n{result.stderr.strip()}")
return json.loads(result.stdout)
def choose_track(info, lang):
"""Pick the subtitle track for `lang`: (track name, is_auto) or None.
Manual subtitles come first. Of the automatic captions only the original
language ("<lang>-orig") is used: plain "<lang>" there is a machine
translation whenever the video is in another language.
"""
if lang in (info.get("subtitles") or {}):
return lang, False
if f"{lang}-orig" in (info.get("automatic_captions") or {}):
return f"{lang}-orig", True
return None
def original_language(info):
"""Language of the video's audio, or None if yt-dlp does not know it.
YouTube marks the automatic captions in the spoken language with "-orig".
"""
for name in info.get("automatic_captions") or {}:
if name.endswith("-orig"):
return name.removesuffix("-orig")
return info.get("language")
def available_tracks(info):
manual = sorted((info.get("subtitles") or {}).keys())
original = sorted(
name.removesuffix("-orig")
for name in (info.get("automatic_captions") or {})
if name.endswith("-orig")
)
return manual, original
def download(url, track, is_auto, target_base):
"""Download one subtitle track; yt-dlp writes <target_base>.<track>.vtt."""
command = [
YT_DLP, "--skip-download", "--no-playlist", "--force-overwrites",
"--write-auto-subs" if is_auto else "--write-subs",
"--sub-langs", track, "--sub-format", "vtt",
"-o", f"{target_base}.%(ext)s",
url,
]
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode != 0:
raise SystemExit(f"Download fehlgeschlagen:\n{result.stderr.strip()}")
def parse_args():
parser = argparse.ArgumentParser(
description="Lädt die Untertitel eines YouTube-Videos als VTT-Datei herunter."
)
parser.add_argument("-u", "--url", required=True, help="YouTube-URL des Videos")
parser.add_argument(
"-n", "--name",
help="Dateiname ohne Endung, z. B. Lahnstein (Standard: Video-ID)",
)
parser.add_argument(
"-l", "--lang", default="de", help="Sprache der Untertitel (Standard: de)"
)
parser.add_argument(
"-d", "--dir", type=Path, default=Path("."),
help="Zielverzeichnis (Standard: aktuelles Verzeichnis)",
)
args = parser.parse_args()
if args.name is not None and (not args.name or Path(args.name).name != args.name):
parser.error("-n/--name ist ein Dateiname ohne Verzeichnis; dafür gibt es -d/--dir.")
if shutil.which(YT_DLP) is None:
parser.error(f"{YT_DLP} nicht gefunden (Installation: pip install yt-dlp).")
return args
def main():
args = parse_args()
info = fetch_info(args.url)
print(f"Video: {info.get('title', '?')}")
chosen = choose_track(info, args.lang)
if chosen is None:
manual, original = available_tracks(info)
raise SystemExit(
f"Keine Untertitel in „{args.lang}“ (weder manuell noch automatisch in "
f"Originalsprache).\n"
f"Manuell: {', '.join(manual) or '–'}\n"
f"Automatisch (Originalsprache): {', '.join(original) or '–'}"
)
track, is_auto = chosen
kind = "automatische" if is_auto else "manuelle"
print(f"Lade {kind} Untertitel „{track}“ …", flush=True)
args.dir.mkdir(parents=True, exist_ok=True)
target_base = args.dir / (args.name or info["id"])
download(args.url, track, is_auto, target_base)
target = Path(f"{target_base}.{track}.vtt")
if not target.is_file():
raise SystemExit(f"yt-dlp hat keine Datei {target} geschrieben.")
print(f"Gespeichert: {target}")
print(f"Weiter mit: ./vtt2txt.py -i {target}")
if __name__ == "__main__":
main()