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
@@ -838,6 +838,59 @@ async function discordBot(): Promise<void> {
|
||||
await bot.run(abort.signal);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- rewards:anlegen
|
||||
|
||||
/**
|
||||
* Eine Kanalpunkt-Belohnung ueber die API anlegen statt im Dashboard.
|
||||
*
|
||||
* Der Grund ist nicht Bequemlichkeit: Twitch laesst nur die App, die eine
|
||||
* Belohnung angelegt hat, deren Einloesungen aendern. Eine im Dashboard
|
||||
* angelegte Belohnung kann das Toolkit weder erstatten noch als erledigt
|
||||
* markieren. Wiederholbar: steht die Belohnung schon, wird nur ihre ID
|
||||
* gezeigt.
|
||||
*/
|
||||
async function rewardsAnlegen(args: string[]): Promise<void> {
|
||||
const wert = (name: string): string | undefined => {
|
||||
const i = args.indexOf(name);
|
||||
return i === -1 ? undefined : args[i + 1];
|
||||
};
|
||||
const titel = wert('--titel');
|
||||
const kosten = Number(wert('--kosten'));
|
||||
if (!titel || !Number.isInteger(kosten) || kosten < 1) {
|
||||
throw new Error('Aufruf: rewards:anlegen --titel "Nachricht vorlesen" --kosten 1000 ' +
|
||||
'[--text] [--hinweis "..."] [--abklingzeit SEKUNDEN]');
|
||||
}
|
||||
const cfg = loadConfig();
|
||||
const { helix } = await import('#twitch/helix.ts');
|
||||
const user = await helix.userByLogin(cfg.channel.login);
|
||||
if (!user) throw new Error(`Kanal ${cfg.channel.login} nicht gefunden`);
|
||||
|
||||
const alle = await helix.customRewards(user.id);
|
||||
const eigene = new Set((await helix.customRewards(user.id, true)).map((r) => r.id));
|
||||
const da = alle.find((r) => r.title.toLowerCase() === titel.toLowerCase());
|
||||
if (da) {
|
||||
console.log(`Steht schon: "${da.title}" (${da.cost} Punkte), ID ${da.id}`);
|
||||
if (!eigene.has(da.id)) {
|
||||
console.log('Achtung: im Dashboard angelegt, nicht vom Toolkit. Erstatten und ' +
|
||||
'Abschliessen gehen damit nicht. Im Dashboard loeschen und hier neu anlegen.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const abklingzeit = Number(wert('--abklingzeit') ?? 0);
|
||||
const neu = await helix.createCustomReward(user.id, {
|
||||
title: titel,
|
||||
cost: kosten,
|
||||
...(args.includes('--text') ? { is_user_input_required: true } : {}),
|
||||
...(wert('--hinweis') ? { prompt: wert('--hinweis')! } : {}),
|
||||
...(abklingzeit > 0
|
||||
? { is_global_cooldown_enabled: true, global_cooldown_seconds: abklingzeit } : {}),
|
||||
should_redemptions_skip_request_queue: false,
|
||||
});
|
||||
console.log(`Angelegt: "${neu.title}" (${neu.cost} Punkte), ID ${neu.id}`);
|
||||
console.log('In config.yaml unter rewards.belohnungen mit `id:` eintragen - die ID gewinnt gegen den Titel.');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- help
|
||||
|
||||
function help(): void {
|
||||
@@ -860,6 +913,9 @@ abdulive - Werkzeugkasten fuer Abdus Stream
|
||||
discord:bot Rollen-Knoepfe bedienen (laeuft sonst im Daemon mit)
|
||||
golive:test [--echt] Go-Live-Post proben: ohne Argument in den internen
|
||||
Kanal ohne Ping, mit --echt in die Ankuendigungen
|
||||
rewards:anlegen --titel T --kosten N [--text] [--hinweis H] [--abklingzeit S]
|
||||
Kanalpunkt-Belohnung ueber die API anlegen (noetig
|
||||
zum Erstatten, siehe apps/hub/src/rewards.ts)
|
||||
help Diese Uebersicht
|
||||
|
||||
Einrichtung nach dem Start: http://localhost:4792/setup
|
||||
@@ -892,6 +948,7 @@ try {
|
||||
case 'render': await renderOne(args[0], args[1]); break;
|
||||
case 'discord:setup': await discordSetup(args); break;
|
||||
case 'golive:test': await goLiveTest(args); break;
|
||||
case 'rewards:anlegen': await rewardsAnlegen(args); break;
|
||||
case 'hub': {
|
||||
const { runHub } = await import('../hub/src/index.ts');
|
||||
await runHub();
|
||||
|
||||
Reference in new issue
Block a user