Was macht Persian Sub Tizone — ساخت زیرنویس SRT فارسی?
Pipeline: ffmpeg (Audio extrahieren) → 9Router /v1/audio/transcriptions (Transkribieren) → Zeitliche Ausrichtung mit überlappenden Blöcken → Ausgabe .srt.
Schnellmethode — führe dies einfach aus
Ein Befehl erledigt alles (Audio extrahieren → einmaliger vollständiger Text → überlappende Blöcke mit Cache → Ausrichtung → .srt schreiben):
python3 pipeline/stt.py "$VIDEO" # → neben dem Video: video.srt
python3 pipeline/stt.py "$VIDEO" -m groq/whisper-large-v3-turbo # Modell ersetzen
Ausgabe: Datei video.srt neben der Originaldatei — direkt in Premiere importierbar (File → Import oder in die Timeline ziehen).
Verhalten, das du kennen solltest:
- Die Arbeit bleibt im Ordner
<video>.pst/neben der Datei; eine erneute Ausführung setzt dort fort (bereits abgeschlossene Blöcke werden nicht erneut aufgerufen, nur der unvollständige Block wird wiederholt). - 429 (Kontingent): Liest die Nachricht und schläft genau so lange (3 Mal), falls danach immer noch 429, wird auf
gemini/gemini-2.5-flash-lite(separates Kontingent) zurückgegriffen. 3–5 Sekunden Abstand zwischen Aufrufen und ein Timeout von jeweils 60 Sekunden sind ebenfalls im Skript enthalten. - Standardmodell ist
gemini/gemini-2.5-flash(bester persischer Text). - Ohne Schlüssel wird nicht gestartet: Es sagt
NINEROUTER_KEY fehltund stoppt — hole den Schlüssel vom Benutzer, rate nicht.
Tests (ohne Schlüssel und ohne Netzwerk):
python3 pipeline/stt.py --selftest # Chunking + Multipart + Parsing von 429
python3 pipeline/test_e2e.py # gesamte Pipeline auf lokalem Fake-STT-Server
Voraussetzung (einmalig)
export NINEROUTER_URL="https://9router.peditx.ir" # oder der 9Router-Host des Benutzers
export NINEROUTER_KEY="sk-..."
curl -s "$NINEROUTER_URL/api/health" # sollte {"ok":true} zurückgeben
Wenn kein Schlüssel vorhanden, hole ihn vom Benutzer; rate nicht.
Pipeline-Details (nur zur Fehlerbehebung/Entwicklung)
1) Audio extrahieren
DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$VIDEO")
ffmpeg -y -i "$VIDEO" -vn -ac 1 -ar 16000 -b:a 64k /tmp/audio.mp3
2) Transkribieren — überlappende Blöcke von 6 Sekunden (hop=4s)
# jedes Wort ist in ≥ einem Block
ffmpeg -i audio.mp3 -ss <t> -t 6 -c copy ch/NN_t.mp3
# jeder Block: POST /v1/audio/transcriptions model=gemini/gemini-2.5-flash response_format=json
- Nimm 429 ernst: Lies die Nachricht, schlafe
reset after Xmlang, versuche es erneut. - Beschädigte/unvollständige Antworten mit
try/exceptabfangen und maximal 3 Mal wiederholen. - Ergebnisse in
chunks.jsonspeichern, damit Resume funktioniert.
3) Zeitliche Ausrichtung (Qualitätsschlüssel)
Hole den vollständigen Text einmal aus dem gesamten Video, gleiche dann die Wörter mit difflib.SequenceMatcher auf die Blöcke ab und interpoliere jedem Wort eine Zeit:
import difflib, re
W = lambda s: re.findall(r"[^\s]+", s)
norm = lambda w: re.sub(r"[^\w-ۿ]", "", w.lower())
gw = W(full_text)
# für jeden Block: beste Startposition in gw mit SequenceMatcher finden
# word j ← time = start + 6 * i/(n-1) ; Lücken interpolieren
Wenn ratio < 0.45, block verwerfen und Zeiten aus den segments des gleichen Blocks als Fallback verwenden.
4) SRT erstellen
MAX_CHARS, MAX_DUR = 84, 7.0
SOFT_CHARS, SOFT_DUR = 72, 6.0
# Aufteilen: zuerst Satzende (. ؟ !) mit Länge ≥30 → Priorität; sonst Komma/Länge/Dauer
# Wenn ein Cue unter 22 Zeichen hat, mit vorherigem Cue zusammenführen
def ts(x): return f"{int(x//3600):02d}:{int(x%3600//60):02d}:{int(x%60):02d},{int(round((x-int(x))*1000)):03d}"
# Zeilenumbruch: Ausgeglichenheit zweier Zeilen in 46 Zeichen (maximal 2 Zeilen)
Implementierung: pipeline/mk_srt.py
python3 pipeline/mk_srt.py -t g.json -c chunks.json --media "$VIDEO" -o "${VIDEO%.*}.srt"
python3 pipeline/mk_srt.py --selftest
5) Persische Rechtschreibkorrektur (optional)
{"model":"claude-sonnet-4-6","temperature":0.1,
"messages":[{"role":"system","content":"Persischer Redakteur. Ausgabe nur gültiges JSON."},
{"role":"user","content":"Korrigiere nur die persische Rechtschreibung; ändere nicht Bedeutung/Wortanzahl/Reihenfolge. Ausgabe: [{\"i\":..,\"t\":\"..\"}]"}]}
Die Ausgabe muss exakt die gleiche Anzahl an Cues zurückgeben; sonst assert und zum rohen Text zurückkehren.
Sichere Normalisierung: ي → ی und ك → ک.
6) Endgültige Überprüfung
- Lese an mehreren Stellen einen Frame des Videos (
fps=1/6) und überprüfe den Untertiteltext auf dem Bild mit den Cues. - Führe an einigen separaten kurzen Abschnitten (
ffmpeg -ss) STT durch und gleiche mit den Cue-Zeiten ab; falls Abweichung > 2 Sekunden, korrigiere die Zeiten.
Praktische Hinweise
ffmpeg/ffprobesind immer vorhanden;whisperist nicht lokal installiert.- Führe das Skript mit
python3 - <<'PY'aus; für große Payloads-d @file.jsonverwenden, nicht argv. - Begrenze die Zeit jedes Aufrufs (
-m 60); das Umgebungs-Timeout beträgt etwa 175 Sekunden → Aufrufe aufteilen. - Endgültiger Dateiname: neben dem Video mit der Erweiterung
.srt.
Ausgabedateien, die in Premiere verwendet werden können:
| Datei | Verwendung |
|---|---|
video.srt | Untertitel — direkt File → Import oder in die Timeline ziehen |
video.mp4.pst/chunks.json | STT-Cache (für spätere manuelle Korrektur) |
video.mp4.pst/full.json | Referenzvolltext |