Pult: Stream-PC per Tipp freigeben, Sound-Quelle legt er selbst in OBS an
Paket bauen und veroeffentlichen / paket (push) Successful in 42s
Paket bauen und veroeffentlichen / paket (push) Successful in 42s
- ingest wuerfelt beim ersten Start einen Schluessel und meldet sich beim Hub; der antwortet 202, bis im Pult jemand auf Freigeben tippt - nach dem Verbinden schickt der Hub die Adresse von /sounds, ingest legt Szene "abdulive Ton" mit der Browser-Source an und haengt sie in jede Szene - fern:verbinden bleibt als Weg von Hand Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
32416d1916
commit
54dbe1265b
15 files changed
+675
-50
No files matched your search
@@ -3,7 +3,9 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import type { ObsClient } from '#obs/client.ts';
|
||||
import { befehlAus, sseZerleger, standLesen } from './fern.ts';
|
||||
import {
|
||||
befehlAus, einrichtenAus, SOUND_QUELLE, soundQuelleEinrichten, sseZerleger, standLesen, TON_SZENE,
|
||||
} from './fern.ts';
|
||||
|
||||
test('SSE: Nachrichten, die zerteilt oder zu zweit ankommen', () => {
|
||||
const z = sseZerleger();
|
||||
@@ -67,3 +69,83 @@ test('Stand ohne Wiedergabepuffer: kein Clip-Knopf, aber Szenen', async () => {
|
||||
assert.equal(s.puffer, false);
|
||||
assert.deepEqual(s.szenen, ['A']);
|
||||
});
|
||||
|
||||
test('nur eine Adresse auf die Sound-Seite wird als Browser-Source angelegt', () => {
|
||||
assert.deepEqual(einrichtenAus({ art: 'einrichten', soundsUrl: 'https://abdu.dimension47.de/sounds?key=4OB0-x_y' }),
|
||||
{ art: 'einrichten', soundsUrl: 'https://abdu.dimension47.de/sounds?key=4OB0-x_y' });
|
||||
assert.equal(einrichtenAus({ art: 'einrichten', soundsUrl: 'https://boese.example/irgendwas' }), null);
|
||||
assert.equal(einrichtenAus({ art: 'einrichten', soundsUrl: 'javascript:alert(1)//sounds?key=a' }), null);
|
||||
assert.equal(einrichtenAus({ art: 'szene', soundsUrl: 'https://a/sounds?key=a' }), null);
|
||||
});
|
||||
|
||||
/** OBS zum Mitschreiben: Szenen, Quellen und was darin steckt. */
|
||||
function obsMitSzenen(szenen: string[]) {
|
||||
const inhalt = new Map<string, string[]>(szenen.map((s) => [s, []]));
|
||||
const quellen = new Map<string, Record<string, unknown>>();
|
||||
const aufrufe: string[] = [];
|
||||
const obs: ObsClient = {
|
||||
run: async () => {},
|
||||
isConnected: () => true,
|
||||
request: async <T>(type: string, data?: unknown): Promise<T> => {
|
||||
const d = (data ?? {}) as Record<string, unknown>;
|
||||
aufrufe.push(type);
|
||||
switch (type) {
|
||||
case 'GetSceneList': return { scenes: [...inhalt.keys()].map((sceneName) => ({ sceneName })) } as T;
|
||||
case 'CreateScene': inhalt.set(String(d['sceneName']), []); return {} as T;
|
||||
case 'GetInputSettings': {
|
||||
const q = quellen.get(String(d['inputName']));
|
||||
if (!q) throw new Error('OBS lehnt ab (600): keine Quelle');
|
||||
return { inputSettings: q } as T;
|
||||
}
|
||||
case 'CreateInput':
|
||||
quellen.set(String(d['inputName']), d['inputSettings'] as Record<string, unknown>);
|
||||
inhalt.get(String(d['sceneName']))!.push(String(d['inputName']));
|
||||
return {} as T;
|
||||
case 'SetInputSettings':
|
||||
Object.assign(quellen.get(String(d['inputName']))!, d['inputSettings']);
|
||||
return {} as T;
|
||||
case 'GetSceneItemList':
|
||||
return { sceneItems: inhalt.get(String(d['sceneName']))!.map((sourceName) => ({ sourceName })) } as T;
|
||||
case 'CreateSceneItem':
|
||||
inhalt.get(String(d['sceneName']))!.push(String(d['sourceName']));
|
||||
return {} as T;
|
||||
}
|
||||
throw new Error(`unerwartet: ${type}`);
|
||||
},
|
||||
};
|
||||
return { obs, inhalt, quellen, aufrufe };
|
||||
}
|
||||
|
||||
test('Sound-Quelle: einmal angelegt, in jeder Szene, ein zweiter Lauf aendert nichts', async () => {
|
||||
const o = obsMitSzenen(['Gameplay', 'Pause']);
|
||||
const url = 'https://hub/sounds?key=a';
|
||||
const getan = await soundQuelleEinrichten(o.obs, url);
|
||||
assert.equal(getan.length, 4);
|
||||
assert.deepEqual(o.inhalt.get(TON_SZENE), [SOUND_QUELLE]);
|
||||
assert.deepEqual(o.inhalt.get('Gameplay'), [TON_SZENE]);
|
||||
assert.deepEqual(o.inhalt.get('Pause'), [TON_SZENE]);
|
||||
const q = o.quellen.get(SOUND_QUELLE)!;
|
||||
assert.equal(q['reroute_audio'], true, 'sonst geht der Ton am Stream vorbei');
|
||||
assert.equal(q['shutdown'], false);
|
||||
|
||||
assert.deepEqual(await soundQuelleEinrichten(o.obs, url), []);
|
||||
assert.equal(o.aufrufe.filter((a) => a.startsWith('Create')).length, 4);
|
||||
});
|
||||
|
||||
test('Sound-Quelle: neue Szene und neuer Schluessel werden nachgezogen', async () => {
|
||||
const o = obsMitSzenen(['Gameplay']);
|
||||
await soundQuelleEinrichten(o.obs, 'https://hub/sounds?key=alt');
|
||||
o.inhalt.set('Just Chatting', []);
|
||||
const getan = await soundQuelleEinrichten(o.obs, 'https://hub/sounds?key=neu');
|
||||
assert.deepEqual(getan, [`Adresse von "${SOUND_QUELLE}" erneuert`, 'in "Just Chatting" eingehaengt']);
|
||||
assert.equal(o.quellen.get(SOUND_QUELLE)!['url'], 'https://hub/sounds?key=neu');
|
||||
});
|
||||
|
||||
test('die eigene Ton-Szene steht nicht in der Szenenauswahl', async () => {
|
||||
const obs = falschesObs({
|
||||
GetSceneList: () => ({ currentProgramSceneName: 'A', scenes: [
|
||||
{ sceneName: 'A', sceneIndex: 1 }, { sceneName: TON_SZENE, sceneIndex: 0 }] }),
|
||||
GetInputList: () => ({ inputs: [] }),
|
||||
});
|
||||
assert.deepEqual((await standLesen(obs)).szenen, ['A']);
|
||||
});
|
||||
Reference in new issue
Block a user