Drop Whisper's invented subtitle credits from transcripts

Measured on 64 real clips with ggml-medium: 54 came back as genuine Polish, 9 as
bracketed non-speech markers that are accurate ([muzyka] on music), and one as
"Napisy stworzone przez społeczność Amara.org" — a credit line Whisper learned
from training data and emits over silence. That is not a record of what is on the
disc, so it does not belong in an archival catalogue. Bracketed markers stay:
they describe the recording truthfully.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Patryk Gensch
2026-08-21 16:30:14 +02:00
co-authored by Claude Opus 5
parent 930cfb3462
commit a773d791a7
@@ -52,6 +52,17 @@ public final class Transcriber {
*/ */
private static final String THREADS = System.getProperty("catalog.whisper.threads", "4"); private static final String THREADS = System.getProperty("catalog.whisper.threads", "4");
/**
* Zdania, które Whisper dopisuje z siebie na ciszy i muzyce — podpisy ekip
* tworzących napisy, wyuczone z materiału treningowego. To nie jest zapis
* tego, co słychać, tylko wymysł modelu, więc do katalogu archiwalnego
* trafić nie może. Znaczniki w rodzaju {@code [muzyka]} zostawiamy: one
* opisują nagranie zgodnie z prawdą.
*/
private static final List<String> WYMYSLY = List.of(
"napisy stworzone przez", "napisy: ", "subtitles by", "amara.org",
"zdjęcia i napisy", "dziękuję za uwagę", "thanks for watching");
/** Nazwy, pod którymi whisper.cpp bywa instalowany. */ /** Nazwy, pod którymi whisper.cpp bywa instalowany. */
private static final List<String> BINARIES = private static final List<String> BINARIES =
List.of("whisper-cli", "whisper-cpp", "whisper", "main"); List.of("whisper-cli", "whisper-cpp", "whisper", "main");
@@ -276,7 +287,7 @@ public final class Transcriber {
for (int i = 0; i < ready.size(); i++) { for (int i = 0; i < ready.size(); i++) {
try { try {
String text = readResult(inputs.get(i)); String text = withoutHallucinations(readResult(inputs.get(i)));
synchronized (lock) { synchronized (lock) {
// pusty wynik też zapisujemy: cisza albo sam efekt dźwiękowy, // pusty wynik też zapisujemy: cisza albo sam efekt dźwiękowy,
// inaczej ten plik wracałby przy każdym kolejnym przebiegu // inaczej ten plik wracałby przy każdym kolejnym przebiegu
@@ -328,6 +339,25 @@ public final class Transcriber {
} }
} }
/**
* Usuwa zdania, których model nie usłyszał, tylko je dopisał. Jeśli po
* odsianiu nie zostaje nic, zapisujemy pustkę — plik jest wtedy oznaczony
* jako przerobiony i nie wraca przy kolejnym przebiegu.
*/
static String withoutHallucinations(String text) {
if (text == null || text.isBlank()) {
return "";
}
StringBuilder out = new StringBuilder();
for (String line : text.split("\n")) {
String probe = line.toLowerCase(Locale.ROOT);
if (WYMYSLY.stream().noneMatch(probe::contains)) {
out.append(line).append('\n');
}
}
return out.toString().trim();
}
/** whisper.cpp dopisuje rozszerzenie do pełnej nazwy wejścia: klip3.wav → klip3.wav.txt. */ /** whisper.cpp dopisuje rozszerzenie do pełnej nazwy wejścia: klip3.wav → klip3.wav.txt. */
private static Path resultPath(Path input) { private static Path resultPath(Path input) {
return input.resolveSibling(input.getFileName() + ".txt"); return input.resolveSibling(input.getFileName() + ".txt");