feat: Hinweis, wenn ein Zeitbereich über das Ende der Untertitel reicht

„-z 0-05:00“ bedeutet fünf Stunden und bearbeitete bisher stillschweigend
das ganze Video. vtt2txt.py (und damit extract_text.py und
abschrift_korrigieren.py) nennt jetzt das Ende der Untertitel, bei
Bereichen ab einer Stunde mit dem Tipp „Minute 5 ist 00:05“.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-10-11 21:58:12 +02:00
commit d9512bcedc
4 changed files with 57 additions and 5 deletions

View file

@ -559,6 +559,8 @@ Option kennen `extract_text.py` und `vtt2txt.py`.
> **Vorsicht:** `05:00` bedeutet 5 Stunden, nicht 5 Minuten. Für „5 Minuten“
> `00:05` oder `5` schreiben, für 5 Minuten 30 Sekunden `00:05:30`.
> Reicht ein Bereich über das Ende des Videos hinaus, weist das Programm darauf hin
> und nennt die Länge der Untertitel.
### 10.2 Bereiche
@ -712,7 +714,8 @@ Weitere Einstellungen (Abschnittsgröße, Absatzlängen, Schwellen) sind in der
| `Abschnitt 3 wurde abgeschnitten. CHUNK_SIZE verkleinern und erneut starten.` | Die Antwort des Modells war zu lang. `CHUNK_SIZE` oben in `abschrift_korrigieren.py` verkleinern (z. B. 900). |
| `Absatzgliederung: unbrauchbare Antwort: …` / `Absätze verbinden: unbrauchbare Antwort: …` | Das Modell hat kein gültiges JSON geliefert; selten. Erneut starten, die Korrektur selbst ist zwischengespeichert. Tritt es wiederholt auf, ein anderes Modell versuchen. |
| `Hinweis: keine Wortliste für „de“ (Paket wngerman); …` | `sudo apt install wngerman` (für Englisch `wamerican`). Ohne Wortliste fehlt im Bericht nur der Grund „gültiges Wort ersetzt“. |
| `Hinweis: keine Untertitel im Bereich …` | Einer von mehreren Bereichen ist leer und wird übersprungen. |
| `Hinweis: keine Untertitel im Bereich …` | Einer von mehreren Bereichen ist leer und wird übersprungen. In Klammern steht, wo die Untertitel enden. |
| `Hinweis: Bereich Anfang – 05:00:00 reicht über das Ende der Untertitel hinaus (…)` | Der Bereich endet nach dem Video. Meist war Minute statt Stunde gemeint: `05:00` ist Stunde 5, Minute 5 ist `00:05`. Bearbeitet wird trotzdem, bis zum Ende des Videos. |
| `Keine Berechtigung` beim Start mit `./…` | `chmod +x *.py` oder `python3 programm.py …` aufrufen. |
| Lauf ist extrem langsam | Das Modell passt nicht in den Grafikspeicher. `OLLAMA_NUM_PARALLEL=1` setzen (siehe [Abschnitt 2.2](#22-grafikkarte)), andere Programme mit Grafikspeicherbedarf schließen oder ein kleineres Modell wählen. Prüfen mit `ollama ps` (Spalte PROCESSOR sollte „100% GPU“ zeigen). |

View file

@ -182,7 +182,9 @@ Gilt gleich für `vtt2txt.py`, `extract_text.py` und `abschrift_korrigieren.py`.
| `25` | Minute 25 |
Achtung: Zwei Zahlen sind Stunden:Minuten, `05:00` ist also Stunde 5, nicht
Minute 5 (dafür `00:05` oder `5`).
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 |
|---|---|

View file

@ -366,10 +366,33 @@ def test_vtt_to_text_skips_empty_range(capsys):
assert text == "[00:01:40 - 00:01:41]\nErster Satz ist hier."
out = capsys.readouterr().out
assert "keine Untertitel im Bereich 00:00:00 – 00:00:50" in out
assert "keine Untertitel im Bereich 00:03:20 – Ende" in out
assert "keine Untertitel im Bereich 00:03:20 – Ende (die Untertitel enden bei" in out
assert vtt2txt.vtt_to_text(SPEECH, [(200, None)]) == ""
def test_vtt_to_text_notes_range_past_last_caption(capsys):
# "-z 0-05:00" means five hours, not five minutes
text = vtt2txt.vtt_to_text(SPEECH, [(0, 5 * 3600)])
assert text.startswith("[00:01:40 - ")
out = capsys.readouterr().out
assert "Bereich 00:00:00 – 05:00:00 reicht über das Ende der Untertitel hinaus" in out
assert "Stunden:Minuten, Minute 5 ist 00:05" in out
@pytest.mark.parametrize("start, end, expected", [
(100, 110, ""), # inside
(None, None, ""),
(100, 200, " (die Untertitel enden bei 00:02:00)"),
(150, None, " (die Untertitel enden bei 00:02:00)"),
(0, 3600, " (die Untertitel enden bei 00:02:00; zwei Zahlen wie 05:00 sind "
"Stunden:Minuten, Minute 5 ist 00:05)"),
(0, 200, ""), # no captions at all: no end known
])
def test_end_hint(start, end, expected):
captions_end = None if (start, end) == (0, 200) else 120
assert vtt2txt.end_hint(start, end, captions_end) == expected
def test_vtt_to_text_without_ranges_has_no_header():
assert vtt2txt.vtt_to_text(SPEECH).splitlines()[0] == "Erster Satz ist hier."

View file

@ -307,18 +307,42 @@ def range_label(start, end):
return f"{left} – {right}"
def end_hint(start, end, captions_end):
"""Note for a range reaching past the last caption, else ""; two numbers
are hours:minutes, so "05:00" for minute 5 is a likely slip."""
if captions_end is None:
return ""
past_end = (end is not None and end > captions_end) or (
start is not None and start >= captions_end
)
if not past_end:
return ""
hint = f" (die Untertitel enden bei {format_time(captions_end)}"
if max(start or 0, end or 0) >= 3600 > captions_end:
hint += "; zwei Zahlen wie 05:00 sind Stunden:Minuten, Minute 5 ist 00:05"
return hint + ")"
def excerpts(timed, ranges, exact=False):
"""Return (start, end in seconds, lines) of the text block of each time range.
Blocks that overlap after growing to whole sentences are merged; ranges
without captions are skipped with a note.
without captions are skipped with a note, ranges reaching past the last
caption get one.
"""
captions_end = max((end for _, end, _ in timed if end is not None), default=None)
bounds = []
for start, end in ranges:
found = excerpt_bounds(timed, start, end, exact)
hint = end_hint(start, end, captions_end)
if found is None:
print(f"Hinweis: keine Untertitel im Bereich {range_label(start, end)}")
print(f"Hinweis: keine Untertitel im Bereich {range_label(start, end)}{hint}")
continue
if hint:
print(
f"Hinweis: Bereich {range_label(start, end)} reicht über das Ende "
f"der Untertitel hinaus{hint}"
)
if bounds and found[0] < bounds[-1][1]:
bounds[-1] = (bounds[-1][0], max(bounds[-1][1], found[1]))
else: