Overlay: fuenf Browser-Sources fuer OBS
Einblendungen, Chat, Statusleiste, Effekte, Pausenbild. Kein Build, kein Framework - OBS laedt die Dateien direkt, und jede Zwischenstufe waere ein Ding, das mitten im Stream kaputtgehen kann. Server-Sent Events statt WebSocket, entgegen der Skizze in chat-control.md: Node bringt einen WebSocket-*Client* mit, aber keinen Server, `EventSource` verbindet sich von allein neu, und die Richtung stimmt - das Overlay hoert nur zu. Gegen die tote Browser-Source nach einem Neustart stehen drei Dinge: der eingebaute Reconnect, ein Herzschlag als echtes Ereignis (ein SSE-Kommentar haelt die Verbindung offen, ist im Browser aber unsichtbar), und ein Zustands-Schnappschuss beim Verbinden. Abos, Raids, Bits und Loeschungen kommen ueber IRC-USERNOTICE und CLEARCHAT herein; das wurde bisher weggeworfen. Follows fehlen weiter, die gibt es nur ueber EventSub. GIFs im Chat: Twitch schickt dafuer einen Tag `gifs`, den wir nicht kannten. gifs=0-22|Pnd7qLFuGCOntZcNKB|https://media0.giphy.com/media/.../giphy.gif PRIVMSG #abdulive_ :[Idk GIF by Respective] Der Nachrichtentext ist nur ein Platzhalter, der Bereich markiert ihn. Ausgewertet wird er jetzt; ohne das stand woertlich "[Idk GIF by Respective]" im Bild. Gefunden wurde der Tag durch einen Rohmitschnitt - in 3094 Nachrichten aus acht grossen Kanaelen war kein einziges GIF, weil die Adresse eben nicht im Text steht. Zwei Fehler, die beim Durchklicken im Browser aufgefallen sind: - Die Aufraeumfunktion im Chat zaehlte Zeilen, die erst nach der Ausblend-Animation aus dem DOM verschwinden, und lief in eine Endlosschleife. In OBS waere die Quelle beim vierzehnten Chatbeitrag eingefroren. - Der Bereichsparser der GIFs war zu nachsichtig: `-1-4` zerfaellt beim Trennen in ['', '1', '4'], und `Number('')` ist 0 - der Bereich saehe gueltig aus und wuerde den falschen Textteil ersetzen. Auf der Uebersichtsseite steht fuer jede Einblendung, jede Chat-Variante und jeden Effekt ein Knopf. Ohne den liesse sich eine Raid-Einblendung nur pruefen, indem man auf einen echten Raid wartet. Was einen Zustand setzt, ist waehrend eines Streams gesperrt; "Stand zuruecksetzen" ausdruecklich nicht, sonst kaeme man aus einem erfundenen Zustand nicht mehr heraus. build-package.ts nimmt jetzt auch html/css/js mit - sonst liefert das Paket 404 statt Overlay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3b606e36fb
commit
bc2c8f6cbe
24 files changed
+3221
-8
No files matched your search
@@ -0,0 +1,56 @@
|
||||
// Dauerbetrieb mit erfundenen Ereignissen: erfundener Chat und alle paar
|
||||
// Sekunden eine Einblendung.
|
||||
//
|
||||
// Fuer `abdulive overlay --demo` und fuer den Knopf "Vorfuehrung" auf der
|
||||
// Uebersichtsseite. Die einzelnen Ereignisse kommen aus test-events.ts - die
|
||||
// Knoepfe und der Dauerbetrieb sollen dasselbe zeigen.
|
||||
|
||||
import { logger } from '#core/logger.ts';
|
||||
import type { Bus } from './bus.ts';
|
||||
import { testEvent, zufall, type TestArt } from './test-events.ts';
|
||||
|
||||
const log = logger('overlay:demo');
|
||||
|
||||
const ALARME: TestArt[] = ['sub', 'resub', 'subgift', 'massgift', 'raid', 'cheer', 'first'];
|
||||
const NACHRICHTEN: TestArt[] = ['chat', 'chat', 'chat', 'chat-emote', 'chat-emote', 'chat-erstmals'];
|
||||
|
||||
/**
|
||||
* Speist den Bus, bis `signal` abbricht.
|
||||
*
|
||||
* Der Stream-Zustand wird mitgesetzt, damit Statusleiste und Pausenbild etwas
|
||||
* zu zeigen haben. Genau deshalb darf das nicht laufen, waehrend ein echter
|
||||
* Stream laeuft - der Aufrufer prueft das.
|
||||
*/
|
||||
export function startDemo(bus: Bus, signal: AbortSignal): void {
|
||||
log.info('Vorfuehrmodus: erfundener Chat und alle paar Sekunden eine Einblendung');
|
||||
|
||||
let viewers = 17;
|
||||
const seit = new Date(Date.now() - 74 * 60_000).toISOString();
|
||||
|
||||
const stand = (): void => {
|
||||
viewers = Math.max(3, viewers + Math.round((Math.random() - 0.45) * 4));
|
||||
bus.publish({
|
||||
kind: 'stream', live: true,
|
||||
title: 'Elden Ring, aber nur mit Fäusten - Tag 3',
|
||||
category: 'Elden Ring',
|
||||
viewers, startedAt: seit,
|
||||
});
|
||||
};
|
||||
stand();
|
||||
|
||||
const sende = (art: TestArt): void => {
|
||||
const ev = testEvent(art);
|
||||
if (ev) bus.publish(ev);
|
||||
};
|
||||
|
||||
const chat = setInterval(() => sende(zufall(NACHRICHTEN)), 1400);
|
||||
const alarm = setInterval(() => sende(zufall(ALARME)), 9000);
|
||||
const puls = setInterval(stand, 20_000);
|
||||
// Ab und zu eine Chat-Spitze, damit der Emote-Regen auch mal laeuft.
|
||||
const spitze = setInterval(() => sende('hype'), 31_000);
|
||||
|
||||
signal.addEventListener('abort', () => {
|
||||
clearInterval(chat); clearInterval(alarm); clearInterval(puls); clearInterval(spitze);
|
||||
log.info('Vorfuehrmodus beendet');
|
||||
}, { once: true });
|
||||
}
|
||||
Reference in new issue
Block a user