refactor: split music streaming into Hope Music
Rebuild & Deploy / rebuild (push) Successful in 2m7s

Move the media server, downloader, library volume, and browser player to the
standalone hope-media-music product. Keep Hope's music tools as a thin HTTP
client of the LAN service on port 8094.

Use a stable voice-room player scope instead of ephemeral LiveKit room names.
The agent now owns the conversation handshake: fresh reported playback mutes
its input, joining pauses music, and the last participant leaving resumes only
music it paused. Peer failures remain non-fatal.

Also move the voice deploy workflow onto the active Unraid Gitea runner and
remove yt-dlp/ffmpeg/media health and volume dependencies from the image.
This commit is contained in:
snowspeeder authored and Shane Maynard committed 2026-09-12 06:35:15 -04:00
1 parent d4ea4fc984
commit 436b3fc8b1
23 files changed
+356 -2518

No files matched your search

-85
View File
@@ -42,11 +42,6 @@
<span>Tasks</span>
<span class="tasks-badge" id="tasksBadge" hidden>0</span>
</button>
<button id="mediaBtn" class="btn btn-secondary tasks-trigger" aria-label="Music player" title="Music player">
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>
<span>Music</span>
<span class="tasks-badge" id="mediaBadge" hidden>&#9835;</span>
</button>
</div>
</header>
@@ -59,7 +54,6 @@
<div class="orb-stage">
<div class="led-wall" id="ledWall" data-state="idle" aria-hidden="true">
<img id="ledArt" class="led-art" alt="">
</div>
<div class="orb-labels" aria-live="polite">
<span class="state-label" id="stateLabel">Ready when you are</span>
@@ -151,85 +145,6 @@
</div>
</div>
<div class="tasks-panel media-panel" id="mediaPanel" aria-label="Music player">
<div class="tasks-header">
<div>
<h2>Music</h2>
<p>YouTube Music downloads and live streams, streamed to you.</p>
</div>
<button id="closeMediaBtn" class="btn btn-icon" aria-label="Close music player">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"></path></svg>
</button>
</div>
<div class="media-now">
<img id="mediaArt" class="media-art" alt="" hidden>
<div class="media-art-placeholder" id="mediaArtPlaceholder" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>
</div>
<div class="media-now-info">
<strong id="mediaTitle">Nothing loaded</strong>
<span id="mediaChannel"></span>
<div class="media-progress" id="mediaProgressWrap" hidden>
<div class="media-progress-fill" id="mediaProgressFill"></div>
<span class="media-progress-label" id="mediaProgressLabel">Fetching&hellip;</span>
</div>
</div>
</div>
<div class="media-controls" role="group" aria-label="Playback controls">
<button id="rewBtn" class="btn btn-icon" aria-label="Back 10 seconds" title="Back 10s">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 17a5 5 0 1 0-5-5"></path><path d="M11 17H6v-5"></path><path d="m19 5-4 4h-3"></path><path d="m15 9 4-4"></path></svg>
</button>
<button id="playPauseBtn" class="btn btn-icon media-play" aria-label="Play or pause" title="Play / pause">
<svg id="playIcon" class="play-icon" viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7Z"></path></svg>
<svg id="pauseIcon" class="pause-icon" viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M7 5h4v14H7zM13 5h4v14h-4z"></path></svg>
</button>
<button id="fwdBtn" class="btn btn-icon" aria-label="Forward 10 seconds" title="Forward 10s">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 17a5 5 0 1 1 5-5"></path><path d="M13 17h5v-5"></path><path d="m5 5 4 4h3"></path><path d="m9 9 4-4"></path></svg>
</button>
<button id="stopMediaBtn" class="btn btn-icon" aria-label="Stop" title="Stop">
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2"></rect></svg>
</button>
</div>
<div class="media-seek">
<span class="media-time" id="mediaCur">0:00</span>
<input type="range" id="seekBar" min="0" max="1000" value="0" step="1" aria-label="Seek position">
<span class="media-time" id="mediaDur">0:00</span>
</div>
<div class="media-volume">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 5 6 9H2v6h4l5 4V5Z"></path><path d="M15.5 8.5a5 5 0 0 1 0 7"></path></svg>
<input type="range" id="volumeBar" min="0" max="100" value="90" aria-label="Volume">
</div>
<h3 class="sheet-section-title">Search YouTube Music</h3>
<form class="media-search" id="mediaSearchForm">
<input id="mediaSearchInput" type="search" placeholder="Song, artist, lyrics&hellip;" autocomplete="off">
<button class="btn btn-secondary" type="submit">Find</button>
</form>
<div class="media-list" id="mediaResults"></div>
<div id="mediaChoicesWrap" hidden>
<h3 class="sheet-section-title">Hope&rsquo;s suggestions</h3>
<div class="media-list" id="mediaChoices"></div>
</div>
<h3 class="sheet-section-title">Library</h3>
<div class="media-list" id="mediaLibrary">
<div class="media-empty">Downloaded songs and streams live here.</div>
</div>
<h3 class="sheet-section-title">Add live stream</h3>
<form class="media-search media-stream-form" id="streamForm">
<input id="streamName" type="text" placeholder="Station name" autocomplete="off">
<input id="streamUrl" type="url" placeholder="https://&hellip; audio stream URL" required>
<button class="btn btn-secondary" type="submit">Add</button>
</form>
</div>
<audio id="mediaAudio" preload="metadata"></audio>
<script src="livekit-client.umd.js"></script>
<script src="app.js"></script>
</body>