abschrift_korrigieren.py.
- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| tests | ||
| .gitignore | ||
| abschliessen.py | ||
| abschrift_korrigieren.py | ||
| aenderungen.py | ||
| BEDIENUNGSANLEITUNG.md | ||
| extract_text.py | ||
| glossar_vorschlag.py | ||
| pytest.ini | ||
| README.md | ||
| vtt2txt.py | ||
| yt_untertitel.py | ||
youtube_abschrift_korrigieren
Korrigiert automatische Abschriften gesprochener Beiträge – Reden, Vorträge,
Interviews, Videos, auf Deutsch oder Englisch (z. B. YouTube-Untertitel als Text) –
mit einem lokalen Sprachmodell über Ollama: Rechtschreibung, Satzzeichen,
Hörfehler, Absätze – möglichst wortgetreu, ohne Zusammenfassung oder Glättung.
Das Hauptprogramm ist abschrift_korrigieren.py.
Eine ausführliche Anleitung für die Benutzung Schritt für Schritt, mit Arbeitsablauf, Glossar-Pflege, Prüfung der Ergebnisse und Fehlerbehebung, steht in der BEDIENUNGSANLEITUNG.md. Diese README ist die knappe technische Übersicht.
Die Werkzeuge sind einzeln oder hintereinander nutzbar:
| Skript | Aufgabe |
|---|---|
extract_text.py |
YouTube-URL → Textdatei in einem Schritt (die beiden folgenden zusammen) |
yt_untertitel.py |
Untertitel eines YouTube-Videos als VTT herunterladen |
vtt2txt.py |
VTT in eine Rohabschrift (Text) umwandeln |
abschrift_korrigieren.py |
Rohabschrift (oder VTT direkt) mit dem Sprachmodell korrigieren |
aenderungen.py |
Änderungsbericht: was hat das Modell gegenüber dem Rohtext geändert? |
glossar_vorschlag.py |
Glossar-Entwurf: vermutete Hörfehler bei Namen und Begriffen |
abschliessen.py |
fertige Abschrift mit Quellenangabe nach fertig/, Arbeitsordner löschen |
Voraussetzungen
- Python 3.9+ (nur Standardbibliothek)
- für den Download: yt-dlp (
pip install yt-dlp) - Ollama auf
127.0.0.1:11434mit dem Modellqwen3:32b(ollama pull qwen3:32b)abschrift_korrigieren.pyundglossar_vorschlag.pyprüfen beides vor dem Start und brechen sonst mit einem Hinweis ab (sudo systemctl start ollamabzw.ollama pull …) - für den Änderungsbericht eine Wortliste:
/usr/share/dict/ngerman(Paketwngerman), für englische Beiträge/usr/share/dict/american-english(Paketwamerican). Fehlt sie, gibt es einen Hinweis und die Prüfung „gültiges Wort ersetzt“ entfällt
Für gute Geschwindigkeit muss das Modell komplett in den Grafikspeicher passen.
Mit OLLAMA_NUM_PARALLEL=1 braucht qwen3:32b bei 16k Kontext rund 24 GB
(eine RTX 3090). Bei OLLAMA_NUM_PARALLEL=4 landet ein Drittel auf der CPU und
der Lauf wird etwa 12-mal langsamer.
Aufruf
Von der YouTube-URL zum korrigierten Text in einem Befehl, dann abschließen:
./abschrift_korrigieren.py -u 'https://www.youtube.com/watch?v=rm6lADgf6GQ' -g glossare/krone-schmalz.glossar.txt
# → abschriften/<Name>/: <Name>.txt (Rohtext), <Name>.quelle.json,
# <Name>.korrigiert.txt, <Name>.aenderungen.html, <Name>.korrigiert.parts/
./abschliessen.py abschriften/<Name> # → fertig/<Name>.txt, Arbeitsordner nach Rückfrage gelöscht
<Name> ist <Kanal>_<Titel>_<Datum>, siehe Dateiablage.
Oder in zwei Schritten, um den Rohtext vorher anzusehen:
./extract_text.py -u 'https://www.youtube.com/watch?v=rm6lADgf6GQ' # → abschriften/<Name>/<Name>.txt
./abschrift_korrigieren.py -i abschriften/<Name>/<Name>.txt # Ergebnis im selben Ordner
Dasselbe in Einzelschritten, wenn die VTT-Datei erhalten bleiben soll:
./yt_untertitel.py -u 'https://www.youtube.com/watch?v=rm6lADgf6GQ' -n Lahnstein
# → abschriften/Lahnstein/Lahnstein.de-orig.vtt
./vtt2txt.py -i abschriften/Lahnstein/Lahnstein.de-orig.vtt
./abschrift_korrigieren.py -i abschriften/Lahnstein/Lahnstein.de-orig.txt
Der Schritt über vtt2txt.py ist optional: abschrift_korrigieren.py nimmt auch die
.vtt direkt. Die .txt lohnt sich, um den Rohtext anzusehen oder ein Glossar
daraus zu erstellen.
URL → Text in einem Schritt: extract_text.py
Lädt die Untertitel und speichert nur den Text; die VTT-Datei bleibt ein
Zwischenschritt im temporären Verzeichnis. Die Spurwahl ist dieselbe wie bei
yt_untertitel.py, nur ist die Standardsprache die des Original-Audios.
./extract_text.py -u 'https://www.youtube.com/watch?v=0LmCveTM788'
# → abschriften/John_Mearsheimer_…_2026-07-30/John_Mearsheimer_…_2026-07-30.txt (+ .quelle.json)
./extract_text.py -u URL -l de -o texte/rede.txt # Sprache und Ziel
./extract_text.py -u URL -f -o rede.txt # lesbarer Fließtext
./extract_text.py -u URL -z 0:03:17-0:05:25 # nur ein Ausschnitt
./extract_text.py -u URL -z 00:05-00:06 00:10-00:12 00:25 # mehrere Bereiche
./abschrift_korrigieren.py -i rede.txt # → rede.korrigiert.txt
| Option | Bedeutung |
|---|---|
-u, --url |
YouTube-URL, Pflicht |
-l, --lang |
Sprache; Standard: Sprache des Original-Audios (<sprache>-orig) |
-o, --output |
Ausgabedatei (Verzeichnisse werden angelegt, vorhandene Datei überschrieben); Standard: abschriften/<Name>/<Name>.txt, siehe unten |
-z, --zeitraeume |
nur Untertitel in diesen Bereichen, siehe Zeitauswahl |
--exakt |
genau an den Zeiten schneiden statt an Satzgrenzen |
-f, --fliesstext |
Fließtext, auf 80 Zeichen umbrochen (Wörter werden nie getrennt), statt der kurzen Untertitelzeilen; beides versteht abschrift_korrigieren.py |
Standard-Dateinamen für YouTube-Videos
Ohne -o bzw. -n bilden extract_text.py, yt_untertitel.py und
abschrift_korrigieren.py -u den Namen aus Kanal, Titel und Hochladedatum und
schreiben in den Arbeitsordner abschriften/<Name>/:
| Video | Name |
|---|---|
| John Mearsheimer, „Episode 1: Origins of the War In Ukraine“ | John_Mearsheimer_Offensive_Realism_Episode_1_Origins_of_the_War_In_Ukraine_2026-07-30 |
| GGBLahnstein, „Prof. Gabriele Krone-Schmalz – Respekt geht anders – …“ | GGBLahnstein_Prof_Gabriele_Krone-Schmalz_Respekt_geht_anders_2026-07-30 |
Wörter werden mit _ verbunden. Bindestriche und Punkte im Wort bleiben
(„Krone-Schmalz“), andere Zeichen fallen weg. Der Kanal wird auf 40, der Titel auf
60 Zeichen an einer Wortgrenze gekürzt. Ohne verwertbaren Kanal und Titel gilt die
Video-ID. Mit -z kommen die Bereiche dazu, z. B.
…_2026-07-30_00-03-17_bis_00-05-25 (-z A: _ab_…, -z 0-B bzw. -z -B: _bis_…;
mehrere Bereiche mit + verbunden: …_00-05-00_bis_00-06-00+ab_00-25-00).
Gleiches Video ergibt immer denselben Namen und denselben Ordner, Ausschnitte
eines Videos landen also beisammen. Eine vorhandene Datei wird überschrieben (ein mit
-u gespeicherter, abweichender Rohtext und eine von Hand geänderte Korrektur aber
nie). Neben dem Rohtext entsteht <Rohtext>.quelle.json mit Titel, Kanal, Datum und
URL; daraus macht abschliessen.py den Quellenkopf.
Untertitel herunterladen: yt_untertitel.py
Lädt die Untertitel eines YouTube-Videos über yt-dlp als VTT-Datei, ohne das
Video selbst. Gewählt wird:
- manuelle Untertitel in der gewünschten Sprache, sonst
- automatische Untertitel in der Originalsprache des Videos (
de-orig).
Automatische Untertitel ohne -orig sind Maschinenübersetzungen aus einer anderen
Sprache und werden nie genommen. Gibt es keine passende Spur, nennt das Skript die
vorhandenen Sprachen.
Videos mit KI-synchronisierten Tonspuren haben für jede Synchronsprache eine
-orig-Spur. Die Originalsprache kommt deshalb aus den Video-Infos (language, sonst
die Tonspur mit dem Vermerk „original“); automatische Untertitel anderer Sprachen
gehören zur Synchronisation und werden nicht genommen. Sprachen werden auch mit
Region gefunden: -l en findet manuelle Untertitel en-US.
./yt_untertitel.py -u URL # → abschriften/<Name>/<Name>.de-orig.vtt
./yt_untertitel.py -u URL -n Lahnstein # → abschriften/Lahnstein/Lahnstein.de-orig.vtt
./yt_untertitel.py -u URL -n Lahnstein -d untertitel # → untertitel/Lahnstein.de-orig.vtt
| Option | Bedeutung |
|---|---|
-u, --url |
YouTube-URL, Pflicht |
-n, --name |
Dateiname ohne Endung; Standard: <Kanal>_<Titel>_<Datum> |
-l, --lang |
Sprache; Standard: de |
-d, --dir |
Zielverzeichnis (wird angelegt); Standard: Arbeitsordner abschriften/<Name>/ |
Eine vorhandene Datei gleichen Namens wird überschrieben.
Untertitel umwandeln: vtt2txt.py
Macht aus VTT-Untertiteln (z. B. von yt-dlp --write-auto-subs) die Rohabschrift
für abschrift_korrigieren.py: Zeitstempel und Tags werden entfernt. YouTube-Auto-Untertitel
zeigen ein rollendes Fenster, jeder Block wiederholt das Ende des vorigen. Übernommen
wird nur, was nach dieser Überlappung neu ist; sagt der Redner eine Zeile wirklich
zweimal, bleibt sie zweimal erhalten. Ob eine Datei rollt, wird pro Datei bestimmt:
Wiederholt mindestens die Hälfte der Blöcke das Ende des vorigen (Auto-Untertitel:
über 95 %), gilt sie als rollend. Sonst, etwa bei manuell erstellten Untertiteln,
wird jeder Block vollständig übernommen.
./vtt2txt.py -i rede.de.vtt # → rede.de.txt
./vtt2txt.py -i ramstein.de*.vtt # mehrere Dateien, je → *.txt
./vtt2txt.py -i "ramstein.de*.vtt" # Muster löst das Programm auf (Windows)
./vtt2txt.py -i rede.de.vtt -o out/rede.txt # eigene Ausgabedatei
./vtt2txt.py -i *.vtt -o out/ # alle in ein Verzeichnis
./vtt2txt.py -i rede.de.vtt -z 0:03:17-0:05:25 # Ausschnitt → rede.de_00-03-17_bis_00-05-25.txt
./vtt2txt.py -i rede.de.vtt -z 00:05-00:06 00:25 # mehrere Bereiche in einer Datei
| Option | Bedeutung |
|---|---|
-i, --input |
eine oder mehrere VTT-Dateien oder Muster, Pflicht |
-o, --output |
Ausgabedatei (nur bei einer Eingabedatei) oder Verzeichnis (vorhanden oder mit / am Ende); Standard: <Eingabe ohne Endung>.txt neben der Eingabe, mit -z zusätzlich die Bereiche im Namen |
-z, --zeitraeume |
nur Untertitel in diesen Bereichen, siehe unten |
--exakt |
genau an den Zeiten schneiden, auch mitten im Satz |
Zeitauswahl (-z)
Gilt gleich für vtt2txt.py, extract_text.py und abschrift_korrigieren.py.
| Zeit | Bedeutung |
|---|---|
00:05:10 |
Stunden:Minuten:Sekunden (auch 00:5:10, Sekunden mit Nachkommastellen) |
00:20 |
Stunden:Minuten, also Minute 20 |
25 |
Minute 25 |
Achtung: Zwei Zahlen sind Stunden:Minuten, 05:00 ist also Stunde 5, nicht
Minute 5 (dafür 00:05 oder 5). Reicht ein Bereich über die letzte
Untertitelzeile hinaus, nennt ein Hinweis das Ende der Untertitel, bei Bereichen ab
einer Stunde mit diesem Tipp.
| Aufruf | Text |
|---|---|
-z 00:05:10 |
ab 5:10 bis zum Ende des Videos |
-z 0-00:20 oder -z -00:20 |
vom Anfang bis Minute 20 |
-z 00:05-00:06 |
von Minute 5 bis Minute 6 |
-z 00:5:00-00:6:00 00:10-00:12:00 00:25 |
Minute 5–6, Minute 10–12 und alles ab Minute 25 |
Ein Bereich ist A-B, A (ab A bis zum Ende) oder 0-B bzw. kurz -B (vom
Anfang bis B). Mehrere Bereiche werden durch Leerzeichen getrennt, mit oder ohne
Anführungszeichen (-z "00:05-00:06 00:25" ist dasselbe). Sie dürfen sich
nicht überschneiden und werden nach der Zeit sortiert.
Alle Bereiche landen in einer Datei. Vor jedem Bereich steht eine Überschrift mit seinen tatsächlichen Zeiten:
[00:04:57 - 00:06:04]
Ihr seht, es gibt keinen einzigen Dialog …
[00:09:58 - 00:12:03]
Also, wenn ihr Programme baut mit Claude Code, …
Angeschnittene Sätze werden an beiden Enden vollständig aufgenommen. Der
Ausschnitt beginnt nach dem letzten Satzende vor dem Start und endet mit dem
ersten Satzende nach dem Ende, höchstens 30 Sekunden darüber hinaus
(MAX_SNAP_SECONDS). Liegt kein Satzende in Reichweite, wird an dieser Seite
genau geschnitten (--exakt schneidet immer genau).
Die Zeiten der Überschrift sind die Start- und Endzeit der ersten bzw. letzten Untertitelzeile, also auf wenige Sekunden genau. Überschneiden sich zwei Bereiche erst durch die Satz-Erweiterung, werden sie zu einem Block zusammengefasst. Bereiche ohne Untertitel werden mit einem Hinweis übersprungen. Der Dateiname enthält immer die angegebenen Zeiten.
Eine fehlerhafte Datei (keine Untertitel, nicht UTF-8) wird gemeldet, die übrigen werden trotzdem umgewandelt; der Exit-Code ist dann 1.
Abschrift korrigieren: abschrift_korrigieren.py
./abschrift_korrigieren.py -i rede.txt # → rede.korrigiert.txt
./abschrift_korrigieren.py -i rede.txt -o out/rede.txt # eigene Ausgabedatei
./abschrift_korrigieren.py -i rede.txt -g glossare/rede.glossar.txt # mit Glossar
./abschrift_korrigieren.py -i rede.txt -g glossare/allgemein.glossar.txt -g glossare/rede.glossar.txt # kombiniert
./abschrift_korrigieren.py -i rede.de.vtt # VTT direkt → rede.de.korrigiert.txt
./abschrift_korrigieren.py -u URL # YouTube → abschriften/<Name>/<Name>.korrigiert.txt
./abschrift_korrigieren.py -i talk.txt -l en # englischer Beitrag
./abschrift_korrigieren.py -u URL -z 0:03:17-0:05:25 # nur ein Ausschnitt des Videos
./abschrift_korrigieren.py -u URL -z 00:05-00:06 00:25 # mehrere Bereiche, eine Datei
| Option | Bedeutung |
|---|---|
-i, --input |
Eingabedatei: Rohabschrift (.txt) oder Untertitel (.vtt, wird wie mit vtt2txt.py umgewandelt) |
-u, --url |
statt -i: YouTube-URL; Untertitel wie bei extract_text.py. Der Rohtext wird neben der Ausgabe gespeichert (rede.korrigiert.txt → rede.txt, sonst <Name>.roh.txt), eine vorhandene, abweichende Datei aber nie überschrieben |
-l, --lang |
Sprache des Beitrags: de oder en (auch mit Region, z. B. en-US); wählt Prompts und Prüfmarke. Mit -u zugleich die Sprache der Untertitel. Standard: de, mit -u die Sprache des Original-Audios |
-o, --output |
Ausgabedatei; Standard: <Eingabe ohne Endung>.korrigiert.txt neben der Eingabe, mit -u abschriften/<Name>/<Name>.korrigiert.txt; mit -z zusätzlich die Bereiche im Namen |
-z, --zeitraeume |
nur diese Bereiche korrigieren, siehe Zeitauswahl; nur mit .vtt-Eingabe oder -u, da reiner Text keine Zeitstempel hat |
--exakt |
genau an den Zeiten schneiden statt an Satzgrenzen |
-g, --glossar |
Glossar mit festen Ersetzungen und Schreibweisen; mehrfach angeben zum Kombinieren (Ersetzungen in Reihenfolge der Angabe). Das Video-Glossar des Ausgabeordners kommt automatisch dazu, siehe Glossar |
-m, --model |
Ollama-Modell; Standard: qwen3:32b (Konstante MODEL) |
--kein-bericht |
keinen Änderungsbericht schreiben |
--ueberschreiben |
Ausgabedatei auch ersetzen, wenn sie seit dem letzten Lauf von Hand geändert wurde |
Eine von Hand nachbearbeitete Ausgabedatei überschreibt ein erneuter Lauf nicht: Das
Ergebnis kommt dann nach <Name>.neu.korrigiert.txt, mit Warnung. Erkannt wird das
an der Prüfsumme der zuletzt geschriebenen Ausgabe in <Ausgabe>.parts/ausgabe.sha256;
ohne Prüfsumme gilt jede abweichende vorhandene Datei als von Hand geändert.
Mit Zeitauswahl wird jeder Bereich als eigener Block korrigiert und gegliedert;
die Überschriften [00:04:57 - 00:06:04] bekommt das Modell nicht zu sehen, sie
stehen unverändert als eigene Zeilen im Ergebnis. Zusätzlich weist der Prompt das
Modell an, an den Rändern der Ausschnitte nichts zu ergänzen. Eine Rohabschrift
(.txt) mit solchen Überschriften, etwa von vtt2txt.py -z, wird ebenso in
Blöcken korrigiert.
Dateiablage
glossare/ gemeinsame Glossare je Sprecher oder Kanal
abschriften/<Name>/ Arbeitsordner, einer je Video (auch für Ausschnitte)
<Name>.txt Rohtext
<Name>.quelle.json Titel, Kanal, Datum, URL
<Name>.glossar.txt Glossar nur dieses Videos (optional, automatisch genutzt)
<Name>[_bereiche].korrigiert.txt Ergebnis, darf von Hand nachbearbeitet werden
<Name>[_bereiche].aenderungen.html Änderungsbericht
<Name>[_bereiche].korrigiert.parts/ Zwischenstände
fertig/<Name>[_bereiche].txt abgeschlossene Abschriften mit Quellenkopf
testdaten/<rede>/ echte Reden als Regressionstest-Eingaben (.vtt, .txt, Glossar)
tests/daten/ erfundene Beispielrede für die Tests (im Repository)
glossare/, abschriften/, fertig/ und testdaten/ stehen in .gitignore: Sie
enthalten Inhalte realer Beiträge und Personen und bleiben nur lokal. Wertvoll sind
davon auf Dauer nur glossare/ und fertig/. Diese beiden gehören in eine eigene
Sicherung, z. B. ein zweites, privates Git-Repository.
Ein Video durchläuft: Rohtext holen → (Video-Glossar anlegen) → korrigieren →
Bericht prüfen und nacharbeiten → mit abschliessen.py abschließen. Danach bleibt
nur fertig/<Name>.txt.
Ablauf
- Vorprüfung: Läuft Ollama, ist das Modell installiert? Sonst Abbruch mit Hinweis, noch vor jedem Download.
- Fließtext: Untertitel-Zeilenumbrüche werden entfernt. Zeit-Überschriften
(
-z) teilen den Text in Blöcke, die einzeln bearbeitet werden. - Feste Ersetzungen aus dem Glossar (
falsch => richtig) werden im Rohtext angewendet, bevor das Modell ihn sieht. - Korrektur: Der Text wird in Abschnitte von ca. 1200 Wörtern geteilt (immer am Satzende; Abkürzungen wie „z. B.“, „Dr.“, „bzw.“ und Ordnungszahlen wie „2. Weltkrieg“ zählen nicht als Satzende) und abschnittsweise vom Modell korrigiert. Fortschritt wird live angezeigt. Der Prompt verbietet Ergänzungen, ausdrücklich auch Vornamen, Titel oder Namensteile aus dem Glossar, die nicht gesagt wurden (sonst wurde aus „Dobrindt“ gern „Alexander Dobrindt“).
- Wortzahl-Prüfung: Weicht die Wortzahl eines Abschnitts um mehr als 5 % ab, gibt es eine Warnung (mögliche Auslassungen oder Ergänzungen).
- Absätze: Das Modell nennt nur Satznummern, an denen ein Absatz beginnt; der Text selbst wird dabei nicht verändert. Absätze über 10 Sätze werden nochmals geteilt, Absätze unter 3 Sätzen mit einem Nachbarn verbunden. Passt der kurze Absatz an beide Nachbarn, entscheidet das Modell nach dem Inhalt, ob er den vorherigen Gedanken abschließt oder den folgenden einleitet.
- Änderungsbericht: Rohtext (vor dem Glossar) und Ergebnis werden Wort für Wort verglichen; eigenmächtige Änderungen des Modells kommen in die Liste „Zu prüfen“, siehe unten.
Zweifelhafte Stellen markiert das Modell als Lesart [prüfen: Originalwortlaut],
in englischen Reden als reading [check: original wording].
Englische Reden bekommen eigene, englische Prompts für Korrektur und Absätze
(SYSTEM_EN usw. in abschrift_korrigieren.py); dort entfernt das Modell auch die
Sprecherwechsel-Marken >> der YouTube-Auto-Untertitel. Weitere Sprachen lassen sich
über das Wörterbuch PROMPTS ergänzen.
Änderungsbericht
Jeder Lauf von abschrift_korrigieren.py schreibt neben die Ausgabe
<Name>.aenderungen.html (aus rede.korrigiert.txt wird rede.aenderungen.html)
und zeigt die häufigsten Änderungen in der Konsole. Der Bericht enthält:
- Zu prüfen: eigenmächtige Änderungen des Modells, die den Sinn ändern können, mit Grund und Zusammenhang (siehe unten),
- alle Änderungen nach Häufigkeit (
gedeiliche → gedeihliche,∅ → istfür Einfügungen), - den korrigierten Text mit seinen Absätzen: ersetzte Wörter gelb mit dem Original
dahinter, Einfügungen grün, Streichungen rot durchgestrichen,
[prüfen: …]orange, Stellen „zu prüfen“ rot umrahmt (gestrichene als ⚠, Grund beim Darüberfahren).
Nicht markiert werden Groß-/Kleinschreibung, Satzzeichen (auch eingefügte Gedankenstriche) und Schreibweisen wie „z.B.“ → „z. B.“. So bleiben die Wortänderungen übrig, die man prüfen sollte, gerade eigenmächtige wie „gedeiliche“ → „geregelte“.
Zu prüfen
Verglichen wird hier der Text, den das Modell bekommen hat (nach den festen Ersetzungen des Glossars), mit seinem Ergebnis. Gemeldet werden:
| Grund | Beispiel |
|---|---|
| gültiges Wort ersetzt | „Professor“ → „Professorin“, „unterschreiben“ → „unterscheiden“ (das alte Wort steht in der Wortliste, war also kein Hörfehler) |
| Wortform geändert | „Punkten“ → „Punkte“, „das“ → „dass“, „sehe“ → „sähe“: ein gültiges Wort, nur Endung oder Umlaut anders (meist Grammatik); eigene Gruppe am Ende der Liste |
| stark verändert | „CT1“ → „ZDF“ (Ähnlichkeit unter 60 %) |
| eingefügt | „∅“ → „ist“ |
| gestrichen | „Sicherungskonzepte geht“ → „∅“ (Füllwörter wie „äh“ nicht) |
Nicht gemeldet werden Hörfehler-Korrekturen, die ähnlich bleiben („Gradwanderung“ → „Gratwanderung“), reine Zusammen-, Getrennt- und Bindestrich-Schreibung („Clan Kriminalität“ → „Clankriminalität“) und Schreibweisen aus dem Glossar („Bar“ → „Bahr“ bei „Egon Bahr“ im Glossar): Die neuen Wörter müssen zusammenhängend in einem Glossareintrag stehen und dem alten Text ähneln. Einzelne Allerweltswörter aus längeren Einträgen („Sie“ aus „finden Sie“) und eingefügte Wörter (ein ergänzter Vorname „Alexander“) erklärt das Glossar nicht. Die Liste erscheint auch in der Konsole, sinnverändernde Fälle zuerst, Wortformen danach.
Als Wortform gilt ein einzelnes Wort, das bis auf die Endung gleich bleibt (Stamm
mindestens 3 Buchstaben, beide Endungen aus INFLECTIONS, z. B. -e, -n, -s, -en,
-er, -es, -st, -t) oder sich nur im Umlaut unterscheidet. „-in“ gehört bewusst nicht
dazu: „Professor“ → „Professorin“ bleibt in der Hauptliste.
Für vorhandene Dateien gibt es das eigene Skript. Mit denselben Glossaren wie bei der Korrektur gelten deren Ersetzungen und Begriffe nicht als verdächtig:
./aenderungen.py -a rede.txt -b rede.korrigiert.txt # → rede.aenderungen.html
./aenderungen.py -a rede.de.vtt -b rede.korrigiert.txt -o bericht.html
./aenderungen.py -a rede.txt -b rede.korrigiert.txt -g glossare/krone-schmalz.glossar.txt -g glossare/rede.glossar.txt
./aenderungen.py -a talk.txt -b talk.korrigiert.txt -l en # englische Wortliste
Glossar
Eine Zeile pro Eintrag, # leitet Kommentare ein:
# feste Ersetzung (ganze Wörter, Groß-/Kleinschreibung zählt, Reihenfolge = Datei)
Willly Brand => Willy Brandt
Kremmel => Kreml
# Hinweis fürs Modell (korrekte Schreibweise, optional mit Erläuterung)
Multi-Domain Task Force
Michael Gorbatschow (Vorname so belassen)
Ersetzungen ohne Treffer werden gesammelt gemeldet. Bei einem Glossar für einen einzelnen Beitrag deutet das auf einen Tippfehler hin, bei einem gemeinsamen Glossar ist es normal.
Es gibt zwei Arten von Glossaren:
- Gemeinsame Glossare für wiederkehrende Namen und Hörfehler eines Redners oder
Kanals liegen in
glossare/, z. B.glossare/krone-schmalz.glossar.txt. Sie werden mit-gangegeben. - Video-Glossare gelten nur für ein Video und liegen in dessen Arbeitsordner,
benannt wie der Ordner:
abschriften/<Name>/<Name>.glossar.txt.abschrift_korrigieren.py,glossar_vorschlag.pyundaenderungen.pynutzen es automatisch (Meldung „Video-Glossar, automatisch“), und zwar nach den mit-gangegebenen, damit die gemeinsamen Ersetzungen zuerst greifen. Beim Abschluss nenntabschliessen.pyseine Einträge, die noch in keinem gemeinsamen Glossar stehen.
Glossare gehören zu realen Beiträgen und Personen und werden deshalb nicht
versioniert (glossare/, abschriften/ und *.glossar.txt stehen in .gitignore);
glossare/ sollte gesondert gesichert werden. Im Repository ist als Beispiel nur
tests/daten/beispiel.glossar.txt zur erfundenen Beispielrede.
Glossar-Entwurf: glossar_vorschlag.py
Ein Glossar von Hand zu erstellen heißt, den ganzen Rohtext zu lesen. Den ersten Entwurf liefert das Skript:
- Das Modell sucht abschnittsweise vermutete Hörfehler: Namen, Orte, Fremdwörter, Fachbegriffe und falsch zusammen- oder getrennt geschriebene Wörter. Behalten werden nur Funde, die wörtlich im Rohtext stehen; was das Modell für sicher hält, steht zuerst.
- Mit
-kkommen die Wörter dazu, die ein Korrekturlauf geändert hat, ohne das, was die Glossare ohnehin erledigen. Vieles davon hängt vom Zusammenhang ab (das → dass), anderes zeigt Fehlgriffe des Modells (Kremmel → Kremml).
Alle Einträge sind als # ? falsch => richtig auskommentiert: Auch „sichere“ Funde
des Modells sind oft falsch (Lahnstein: Deckmäntelchen → Deckmäntel), und eine
falsche feste Ersetzung verdirbt den Text unbemerkt. Zum Übernehmen # ? entfernen.
Einträge aus den mit -g angegebenen Glossaren werden vorher angewendet und nicht
erneut vorgeschlagen. Zeit-Überschriften von Ausschnitten
([00:04:57 - 00:06:04], siehe Zeitauswahl) werden aus
Roh- und korrigiertem Text entfernt; das Modell bekommt sie nicht zu sehen.
./glossar_vorschlag.py -i rede.txt -k rede.korrigiert.txt -g glossare/krone-schmalz.glossar.txt
# → rede.glossar.entwurf.txt
./glossar_vorschlag.py -i tests/daten/beispiel.de.txt # an der Beispielrede ausprobieren
| Option | Bedeutung |
|---|---|
-i, --input |
Rohabschrift (.txt oder .vtt), Pflicht |
-k, --korrigiert |
korrigierter Text eines Laufs (optional) |
-g, --glossar |
vorhandene Glossare, mehrfach möglich |
-o, --output |
Entwurf; Standard: <Eingabe ohne Endung>.glossar.entwurf.txt |
-m, --model |
Ollama-Modell; Standard: qwen3:32b |
Der Entwurf ist ein Ausgangspunkt, kein fertiges Glossar: jeden Eintrag prüfen,
Brauchbares ins Glossar des Beitrags (oder ins gemeinsame) übernehmen. Die Modellantworten
werden in <Entwurf>.parts/ zwischengespeichert.
Abschluss: abschliessen.py
./abschliessen.py abschriften/<Name> # mit Rückfrage vor dem Löschen
./abschliessen.py abschriften/<Name> --ja # ohne Rückfrage
-
Ergebnisse sind alle
*.korrigiert.txtim Ordner (bei Ausschnitten mehrere). -
Abbruch, solange ein
*.neu.korrigiert.txt(Konflikt mit Handarbeit) da ist oder noch[prüfen: …]-Marken im Text stehen (--trotzdemschließt dennoch ab). -
Jedes Ergebnis kommt als
fertig/<Name>[_bereiche].txtmit Quellenkopf:Titel: Prof. Gabriele Krone-Schmalz – Respekt geht anders Kanal: GGBLahnstein Datum: 2026-07-30 Quelle: https://www.youtube.com/watch?v=rm6lADgf6GQ Abgeschlossen: 2026-10-11 <korrigierter Text>Ohne
*.quelle.jsonnennt der Kopf nur die Datei. Eine vorhandene Datei infertig/mit anderem Text wird nie überschrieben; mit demselben Text gilt das Video als schon übernommen (z. B. nach „nicht löschen“). -
Einträge des Video-Glossars, die in keinem Glossar unter
glossare/stehen, werden aufgelistet, damit Brauchbares vorher ins gemeinsame Glossar wandert. -
Der Arbeitsordner wird angezeigt und nach Rückfrage gelöscht. Nie gelöscht werden Ordner mit fremden Dateien (andere als
.txt,.vtt,.html,.json,*.parts/), das Aufrufverzeichnis und alles intestdaten/,glossare/,tests/oderfertig/.
| Option | Bedeutung |
|---|---|
ordner |
Arbeitsordner, z. B. abschriften/<Name> |
--ziel |
Verzeichnis für fertige Abschriften; Standard: fertig |
--glossare |
Verzeichnis der gemeinsamen Glossare; Standard: glossare |
--ja |
ohne Rückfrage löschen |
--trotzdem |
auch mit offenen Prüfmarken abschließen |
Testdaten
Im Repository: tests/daten/ enthält eine frei erfundene Beispielrede
(Festrede zum Jubiläum der Gemeinde „Auental“; Ort und Personen gibt es nicht), als
Untertitel im Format der YouTube-Auto-Untertitel (beispiel.de.vtt, rollendes
Fenster, Wort-Zeitstempel), daraus gewonnener Rohtext (beispiel.de.txt) und ein
passendes Glossar (beispiel.glossar.txt) für die eingebauten Hörfehler wie
„Auen Tal“ oder „Haber Kamp“. Die Tests nutzen nur diese Daten.
./abschrift_korrigieren.py -i tests/daten/beispiel.de.txt -g tests/daten/beispiel.glossar.txt -o /tmp/beispiel.korrigiert.txt
Nur lokal: Echte Reden gehören wegen des Urheberrechts nicht ins Repository;
testdaten/ ist deshalb in .gitignore, ebenso die Glossare. Es enthält je Rede
einen Ordner mit unveränderlichen Eingaben, nie Ausgaben:
testdaten/ramstein/:ramstein.de.vtt,ramstein.de.txt,ramstein.glossar.txt(Rede über die US Air Base Ramstein)testdaten/magdeburg/:magdeburg.de-orig.vtt,.txt,magdeburg.glossar.txt(Rede in Magdeburg)testdaten/lahnstein/:Lahnstein.de-orig.vtt,.txt,lahnstein.glossar.txt(Rede „Respekt geht anders“, Glossar mit Hilfe vonglossar_vorschlag.py), zu laden mit./yt_untertitel.py -u 'https://www.youtube.com/watch?v=rm6lADgf6GQ' -n Lahnstein -d testdaten/lahnstein
Liegen sie dort und glossare/krone-schmalz.glossar.txt daneben, laufen zusätzlich
die Tests, die diese Glossare prüfen; sonst werden sie übersprungen. Eine echte Rede
korrigiert man mit Ausgabe in einen Arbeitsordner, damit testdaten/ sauber bleibt:
./abschrift_korrigieren.py -i testdaten/ramstein/ramstein.de.txt \
-g glossare/krone-schmalz.glossar.txt -g testdaten/ramstein/ramstein.glossar.txt \
-o abschriften/ramstein.de/ramstein.de.korrigiert.txt
Ein Lauf über eine Rede dauert mit qwen3:32b vollständig auf einer RTX 3090 etwa
6–8 Minuten.
Tests
python3 -m pytest
Die Tests brauchen weder Ollama noch Netz: Modellaufrufe und yt-dlp werden durch
Fakes ersetzt. Als Text dient die erfundene Beispielrede in tests/daten/ (siehe
Testdaten). In einem frischen Klon laufen 339 Tests, 7 werden
übersprungen; sie prüfen die Glossare der echten Reden und laufen nur, wenn diese
lokal in testdaten/ liegen (dann 346).
tests/test_abschrift_korrigieren.py prüft Abschnittsbildung, Satzgrenzen
(Abkürzungen und Ordnungszahlen sind kein Satzende), Glossar (jede Ersetzung des
Beispielglossars trifft die Beispielrede; lokal zusätzlich: jede Ersetzung im
Glossar einer Rede trifft diese Rede, jede im gemeinsamen Glossar mindestens eine),
Prompts (keine Namensteile ergänzen), Streaming-Auswertung, Absatzgliederung (Text
bleibt unverändert, 3–10 Sätze), Verbinden kurzer Absätze (Modell wird nur gefragt,
wenn beide Nachbarn passen), Zwischenspeicher/Fortsetzen, Arbeitsordner,
Quellenangabe und automatisches Video-Glossar, Schutz von Handarbeit
(.neu.korrigiert.txt, --ueberschreiben), Zeitauswahl, Sprachen und die
Kommandozeile.
tests/test_glossar_vorschlag.py prüft Filter (nur Funde im Text, keine bekannten),
Ausgabeformat (gültiges Glossar, Unsicheres auskommentiert) und Zwischenspeicher.
tests/test_aenderungen.py prüft den Wortvergleich (Satzzeichen, Groß-/Kleinschreibung
und „z. B.“ zählen nicht), die Liste „Zu prüfen“ (Gründe, Wortformen, Füllwörter je
Sprache, Glossar-Schreibweisen, fehlende Wortliste) und den HTML-Bericht
(vollständiger Text, Absätze, Escaping, Reihenfolge).
Die Tests nutzen eine kleine eigene Wortliste statt der des Systems.
tests/test_extract_text.py prüft den Gesamtablauf URL → Text, Sprachwahl
(Original-Audio, manuell vor automatisch) und Fehlerfälle.
tests/test_yt_untertitel.py prüft Spurauswahl, Aufruf von yt-dlp und
Fehlerfälle (ohne Netz, yt-dlp wird durch Fakes ersetzt).
tests/test_abschliessen.py prüft den Abschluss: Quellenkopf, mehrere Ausschnitte,
Abbruch bei Prüfmarken und Konflikten, nie überschreiben, Rückfrage, Schutz vor dem
Löschen fremder und geschützter Ordner und den Hinweis auf Video-Glossar-Einträge.
tests/test_vtt2txt.py prüft die VTT-Umwandlung: die Beispiel-.vtt (und lokal
jede Rede in testdaten/) ergibt genau die zugehörige .txt, echte Wiederholungen
bleiben erhalten, auch bei Untertiteln ohne rollendes Fenster, dazu Zeitauswahl
(mit Hinweis bei Bereichen über das Ende hinaus), Muster, Ausgabeverzeichnis und
Fehlerfälle.
Zwischenstände
Jeder fertige Abschnitt, jede Absatzgliederung und jede Entscheidung beim
Verbinden kurzer Absätze wird in <Ausgabe>.parts/ gespeichert. Ein erneuter
Lauf überspringt alles, was schon berechnet ist.
Ändern sich Modell (auch per -m), Prompt, Glossar oder Text eines Abschnitts, wird
genau dieser Teil neu berechnet. Glossar-Begriffe stehen im Prompt; ein neuer Begriff
lässt deshalb alle Abschnitte neu korrigieren. Den Ordner kann man jederzeit löschen;
abschliessen.py löscht ihn mit dem Arbeitsordner. Er wächst sonst mit jeder
Prompt- oder Modelländerung, weil alte Fassungen liegen bleiben.
glossar_vorschlag.py speichert seine Modellantworten ebenso in
<Entwurf>.parts/.
Einstellungen
Oben in abschrift_korrigieren.py:
| Konstante | Standard | Bedeutung |
|---|---|---|
MODEL |
qwen3:32b |
Ollama-Modell (Standard für -m) |
CHUNK_SIZE |
1200 | Wörter pro Korrekturabschnitt |
MAX_EXTRA_WORDS |
300 | so weit darf ein Abschnitt bis zum nächsten Satzende wachsen |
MAX_WORD_DEVIATION |
0.05 | Warnschwelle Wortzahl |
PARAGRAPH_WINDOW |
150 | Sätze pro Anfrage bei der Absatzgliederung |
MAX_PARAGRAPH_SENTENCES |
10 | längster erlaubter Absatz |
MIN_PARAGRAPH_SENTENCES |
3 | kürzester gewünschter Absatz |
NO_SENTENCE_END |
Muster | Abkürzungen, die kein Satzende sind |
In aenderungen.py:
| Konstante | Standard | Bedeutung |
|---|---|---|
WORDLISTS |
/usr/share/dict/ngerman, …/american-english |
Wortlisten je Sprache |
MIN_SIMILARITY |
0.6 | darunter gilt eine Ersetzung als „stark verändert“ |
FILLERS |
je Sprache: äh, ähm, …; uh, um, er, … | Füllwörter, deren Streichung nicht gemeldet wird |
INFLECTIONS |
je Sprache: -e, -n, -s, -en, …; -s, -ed, -ing, … | Endungen, die nur die Wortform ändern |
MIN_STEM |
3 | kürzester gemeinsamer Wortstamm einer Wortform-Änderung |
In vtt2txt.py:
| Konstante | Standard | Bedeutung |
|---|---|---|
MAX_SNAP_SECONDS |
30 | so weit darf ein Zeitbereich bis zur nächsten Satzgrenze wachsen |
ROLLING_SHARE |
0.5 | ab diesem Anteil sich überlappender Blöcke gilt eine Datei als rollend |
In yt_untertitel.py und abschliessen.py (Pfade relativ zum Aufrufverzeichnis, also
im Programmordner aufrufen):
| Konstante | Standard | Bedeutung |
|---|---|---|
WORK_DIR |
abschriften |
Arbeitsordner je Video: abschriften/<Name>/ |
FINISHED_DIR |
fertig |
abgeschlossene Abschriften (Option --ziel) |
GLOSSARY_DIR |
glossare |
gemeinsame Glossare (Option --glossare) |
Bekannte Grenzen
- Das Modell ändert gelegentlich Wörter eigenmächtig, auch sinnverändernd (Magdeburg-Rede ohne Glossar: „Professor“ → „Professorin“, „gedeiliche“ → „geregelte“ statt „gedeihliche“). Die Liste „Zu prüfen“ im Bericht findet die meisten solcher Fälle, aber nicht alle: Ein falsch erkanntes Wort, das das Modell durch ein ähnliches, aber falsches ersetzt, fällt nicht auf. Gefundene Fälle gehören ins Glossar.
qwen3:32bohne Denkmodus erkennt Hörfehler ohne Glossar nur teilweise und setzt kaum[prüfen: …]-Marken. Bekannte Fehler gehören deshalb ins Glossar.- Einzelbuchstaben und ein- bis zweistellige Zahlen mit Punkt gelten nie als Satzende („Plan B.“, „Es waren 12.“); das beeinflusst nur mögliche Absatzgrenzen, nicht den Text.