MPD and Scene fixes + twitch-bot cleanup
This commit is contained in:
@@ -376,6 +376,7 @@ body {
|
||||
const cdMin = data.countdownMin ?? 5;
|
||||
|
||||
const PROMPT = 'OPHI-118://> ';
|
||||
const MODE_KEYS = ['repeat', 'random', 'single'];
|
||||
const rigName = (window.__TEL?.rig || 'unknown').toUpperCase();
|
||||
const lines = [
|
||||
{ kind: 'cmd', text: 'loadproject --manifest' },
|
||||
@@ -434,11 +435,36 @@ body {
|
||||
await sleep(280);
|
||||
}
|
||||
|
||||
function normalizePlayback(playback) {
|
||||
const src = playback && typeof playback === 'object' ? playback : {};
|
||||
const normalized = {};
|
||||
for (const key of MODE_KEYS) normalized[key] = !!src[key];
|
||||
normalized.consume = !!src.consume;
|
||||
normalized.volume = Number.isFinite(src.volume) ? src.volume : null;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function formatMusicCommand(playback) {
|
||||
const p = normalizePlayback(playback);
|
||||
const parts = ['music', '--boot'];
|
||||
if (p.volume !== null) parts.push(`--volume=${p.volume}%`);
|
||||
MODE_KEYS.forEach(key => {
|
||||
if (p[key]) parts.push(`--${key}`);
|
||||
});
|
||||
if (p.consume) parts.push('--consume');
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
// ── Music: passive listener; daemon (music/index.html) plays the audio ──
|
||||
// Track count comes from playlist.js; current track + progress arrive over
|
||||
// OBS WebSocket as 'mpd:state' broadcasts from the daemon.
|
||||
const playlistData = window.__PLAYLIST || { tracks: [] };
|
||||
const allTracks = playlistData.tracks || [];
|
||||
let bootStarted = false;
|
||||
let bootFinished = false;
|
||||
let lastPlaybackModes = null;
|
||||
let pendingState = null;
|
||||
let modeLogChain = Promise.resolve();
|
||||
|
||||
let npLine = null;
|
||||
function ensureNowPlayingLine() {
|
||||
@@ -467,6 +493,59 @@ body {
|
||||
return `${pad(Math.floor(s / 60))}:${pad(Math.floor(s % 60))}`;
|
||||
}
|
||||
|
||||
async function logModeChange(mode, enabled) {
|
||||
await typeCommand(`music --${mode}=${enabled ? 'true' : 'false'}`);
|
||||
logBeforeNp(`[audio] ${mode} mode ${enabled ? 'enabled' : 'disabled'}`, 'term-out');
|
||||
}
|
||||
|
||||
function trackModeChanges(playback) {
|
||||
const current = normalizePlayback(playback);
|
||||
if (!lastPlaybackModes) {
|
||||
lastPlaybackModes = current;
|
||||
return;
|
||||
}
|
||||
MODE_KEYS.forEach(key => {
|
||||
if (current[key] !== lastPlaybackModes[key]) {
|
||||
modeLogChain = modeLogChain.then(() => logModeChange(key, current[key]));
|
||||
}
|
||||
});
|
||||
lastPlaybackModes = current;
|
||||
}
|
||||
|
||||
function renderMusicState(s) {
|
||||
if (!npLine) ensureNowPlayingLine();
|
||||
if (lastTrackIndex !== null && s.index !== lastTrackIndex) {
|
||||
logBeforeNp(`[audio] next: ${s.title || '?'}`);
|
||||
}
|
||||
lastTrackIndex = s.index;
|
||||
npLine.querySelector('.np-title').textContent = s.title || '—';
|
||||
npLine.querySelector('.np-time').textContent =
|
||||
`[${fmtClock(s.currentTime)} / ${fmtClock(s.duration)}]`;
|
||||
}
|
||||
|
||||
async function bootFromState(s) {
|
||||
if (bootStarted) return;
|
||||
bootStarted = true;
|
||||
lastPlaybackModes = normalizePlayback(s.playback);
|
||||
|
||||
await typeCommand(formatMusicCommand(s.playback));
|
||||
if (allTracks.length === 0) {
|
||||
append('> ', '[audio] no tracks queued — run scripts/playlist.sh', 'term-dim');
|
||||
bootFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
append('> ', `[audio] indexed ${allTracks.length} tracks`, 'term-out');
|
||||
await sleep(180);
|
||||
append('> ', '[audio] connecting to daemon on ws://localhost:4455', 'term-out');
|
||||
await sleep(180);
|
||||
append('> ', '[audio] subscribing to mpd:state events', 'term-out');
|
||||
await sleep(180);
|
||||
append('> ', '[audio] standby for transmission ✓', 'term-out');
|
||||
bootFinished = true;
|
||||
if (pendingState) renderMusicState(pendingState);
|
||||
}
|
||||
|
||||
// Connect to OBS WebSocket and listen for daemon broadcasts.
|
||||
let lastTrackIndex = null;
|
||||
async function connectMusic() {
|
||||
@@ -482,15 +561,14 @@ body {
|
||||
setTimeout(connectMusic, 2000);
|
||||
});
|
||||
obs.onCustom('mpd:state', (s) => {
|
||||
if (!npLine) ensureNowPlayingLine();
|
||||
// Detect track change (don't log on first state, just initialize).
|
||||
if (lastTrackIndex !== null && s.index !== lastTrackIndex) {
|
||||
logBeforeNp(`[audio] next: ${s.title || '?'}`);
|
||||
pendingState = s;
|
||||
if (!bootStarted) {
|
||||
bootFromState(s);
|
||||
return;
|
||||
}
|
||||
lastTrackIndex = s.index;
|
||||
npLine.querySelector('.np-title').textContent = s.title || '—';
|
||||
npLine.querySelector('.np-time').textContent =
|
||||
`[${fmtClock(s.currentTime)} / ${fmtClock(s.duration)}]`;
|
||||
if (!bootFinished) return;
|
||||
renderMusicState(s);
|
||||
trackModeChanges(s.playback);
|
||||
});
|
||||
} catch (e) {
|
||||
logBeforeNp(`[audio] daemon unreachable (${e.message}) — retrying`, 'term-dim');
|
||||
@@ -500,21 +578,7 @@ body {
|
||||
|
||||
async function startMusic() {
|
||||
await sleep(500);
|
||||
await typeCommand('mpd --queue ~/playlists/stream-mix');
|
||||
|
||||
if (allTracks.length === 0) {
|
||||
append('> ', '[audio] no tracks queued — run scripts/playlist.sh', 'term-dim');
|
||||
return;
|
||||
}
|
||||
|
||||
append('> ', `[audio] indexed ${allTracks.length} tracks`, 'term-out');
|
||||
await sleep(180);
|
||||
append('> ', '[audio] connecting to daemon on ws://localhost:4455', 'term-out');
|
||||
await sleep(180);
|
||||
append('> ', '[audio] subscribing to mpd:state events', 'term-out');
|
||||
await sleep(220);
|
||||
|
||||
ensureNowPlayingLine();
|
||||
append('', '[audio] waiting for mpd state', 'term-dim');
|
||||
connectMusic();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user