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:
parent
37e4a2bca0
commit
d9512bcedc
4 changed files with 57 additions and 5 deletions
|
|
@ -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“
|
> **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`.
|
> `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
|
### 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). |
|
| `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. |
|
| `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 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. |
|
| `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). |
|
| 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). |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,9 @@ Gilt gleich für `vtt2txt.py`, `extract_text.py` und `abschrift_korrigieren.py`.
|
||||||
| `25` | Minute 25 |
|
| `25` | Minute 25 |
|
||||||
|
|
||||||
Achtung: Zwei Zahlen sind Stunden:Minuten, `05:00` ist also Stunde 5, nicht
|
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 |
|
| Aufruf | Text |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
|
||||||
|
|
@ -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."
|
assert text == "[00:01:40 - 00:01:41]\nErster Satz ist hier."
|
||||||
out = capsys.readouterr().out
|
out = capsys.readouterr().out
|
||||||
assert "keine Untertitel im Bereich 00:00:00 – 00:00:50" in 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)]) == ""
|
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():
|
def test_vtt_to_text_without_ranges_has_no_header():
|
||||||
assert vtt2txt.vtt_to_text(SPEECH).splitlines()[0] == "Erster Satz ist hier."
|
assert vtt2txt.vtt_to_text(SPEECH).splitlines()[0] == "Erster Satz ist hier."
|
||||||
|
|
||||||
|
|
|
||||||
28
vtt2txt.py
28
vtt2txt.py
|
|
@ -307,18 +307,42 @@ def range_label(start, end):
|
||||||
return f"{left} – {right}"
|
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):
|
def excerpts(timed, ranges, exact=False):
|
||||||
"""Return (start, end in seconds, lines) of the text block of each time range.
|
"""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
|
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 = []
|
bounds = []
|
||||||
for start, end in ranges:
|
for start, end in ranges:
|
||||||
found = excerpt_bounds(timed, start, end, exact)
|
found = excerpt_bounds(timed, start, end, exact)
|
||||||
|
hint = end_hint(start, end, captions_end)
|
||||||
if found is None:
|
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
|
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]:
|
if bounds and found[0] < bounds[-1][1]:
|
||||||
bounds[-1] = (bounds[-1][0], max(bounds[-1][1], found[1]))
|
bounds[-1] = (bounds[-1][0], max(bounds[-1][1], found[1]))
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue