Vorlesen: Piper im Hub statt Sprachausgabe im Browser
In OBS' Browser-Quelle kam `speechSynthesis` amerikanisch heraus: der eingebaute Browser sah keine deutsche Stimme, und die Seite nahm die erste beliebige. Welche Stimmen es gibt, haengt am Rechner, der streamt. Jetzt spricht der Hub mit Piper (lokal im LXC, 200 Zeichen in 0,7 s). Die WAV liegt kurz im Speicher, `/tts` holt sie unter /tts/audio/<id> mit dem Overlay-Schluessel und spielt sie ueber <audio> ab - das laeuft sicher ueber OBS' Tonspur. Der Browser springt nur noch mit einer deutschen Stimme ein, sonst wird nur angezeigt. Stimme und Sprecher stehen in rewards.tts. Dazu: - `abdulive rewards:anlegen`: Belohnung ueber die API anlegen. Nur die App, die sie angelegt hat, darf Einloesungen abschliessen oder erstatten. - Einloesungen werden nach der Aktion als FULFILLED markiert, statt in Twitchs Warteschlange liegen zu bleiben. - Wortfilter normalisiert vorher: Leetspeak, Punkte dazwischen, gezogene Buchstaben, Akzente. Kurze Eintraege nur am Wortanfang. - Der Testknopf "Vorlesen" geht durch Piper und prueft so die echte Stimme. - scripts/install-piper.sh richtet Piper und Stimme im LXC ein. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b1cf0f8fab
commit
63ea40d5a8
16 files changed
+640
-68
No files matched your search
+45
-3
@@ -105,9 +105,7 @@ export function entscheiden(
|
||||
return { art: 'abgelehnt', grund: `zu lang (${text.length} von ${max} Zeichen)` };
|
||||
}
|
||||
|
||||
const klein = text.toLowerCase();
|
||||
const treffer = cfg.tts.blockliste.find((w) => w && klein.includes(w.toLowerCase()));
|
||||
if (treffer) return { art: 'abgelehnt', grund: 'Wortfilter' };
|
||||
if (wortfilterTreffer(text, cfg.tts.blockliste)) return { art: 'abgelehnt', grund: 'Wortfilter' };
|
||||
|
||||
if (bisherigeDieseStunde >= cfg.tts.proPersonProStunde) {
|
||||
return { art: 'abgelehnt', grund: 'zu viele Einloesungen in der letzten Stunde' };
|
||||
@@ -117,6 +115,50 @@ export function entscheiden(
|
||||
return freigabe ? { art: 'warteschlange', text } : { art: 'vorlesen', text };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Wortfilter
|
||||
|
||||
const LEET: Record<string, string> = {
|
||||
'0': 'o', '1': 'i', '3': 'e', '4': 'a', '5': 's', '7': 't', '8': 'b',
|
||||
'@': 'a', '$': 's', '!': 'i', '|': 'i',
|
||||
};
|
||||
|
||||
/**
|
||||
* Text auf das bringen, was man hoert: klein, ohne Akzente, Leetspeak
|
||||
* zurueckuebersetzt, Buchstabenketten auf einen gekuerzt. Wer "N 3 G G 3 R"
|
||||
* oder "h.i.t.l.e.r" schreibt, will am Filter vorbei - vorgelesen klingt es
|
||||
* trotzdem wie das Original.
|
||||
*
|
||||
* `mitLuecken` laesst Wortgrenzen als Leerzeichen stehen, sonst wird alles
|
||||
* zusammengezogen.
|
||||
*/
|
||||
export function fuerFilter(text: string, mitLuecken: boolean): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/ß/g, 'ss')
|
||||
.normalize('NFKD').replace(/\p{M}/gu, '')
|
||||
.replace(/[0134578@$!|]/g, (c) => LEET[c] ?? c)
|
||||
.replace(/[^a-z]+/g, mitLuecken ? ' ' : '')
|
||||
.replace(/([a-z])\1+/g, '$1')
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trifft der Text ein Wort der Blockliste?
|
||||
*
|
||||
* Lange Eintraege (ab 5 Buchstaben) werden im zusammengezogenen Text
|
||||
* gesucht, damit Punkte und Leerzeichen dazwischen nicht helfen. Kurze nur
|
||||
* am Wortanfang: zusammengezogen stuende "nazi" sonst auch in "kann azubi".
|
||||
*/
|
||||
export function wortfilterTreffer(text: string, blockliste: string[]): string | undefined {
|
||||
const dicht = fuerFilter(text, false);
|
||||
const woerter = fuerFilter(text, true).split(' ');
|
||||
return blockliste.find((eintrag) => {
|
||||
const e = fuerFilter(eintrag, false);
|
||||
if (!e) return false;
|
||||
return e.length >= 5 ? dicht.includes(e) : woerter.some((w) => w.startsWith(e));
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Warteschlange
|
||||
|
||||
export type Wartend = {
|
||||
|
||||
Reference in new issue
Block a user