—Hello again proggers—

5.4.0 is actually out, publicly, in people's hands. Diffed v5.3.4 through v5.4.0: 40 commits, Aug 16-18, four threads of work.

Cat or dog? spectral.resume()

—Yap—

Shared play became joinable, Streamer Queue and the Discord bot got hardened, story/visualizer bugs got chased down in one sitting, and some platform/perf issues got caught. None of these were small.

—The Great Suffering—

1 — Shared Play Can Be Joined Now

spectralis://shared-play/join links used to fall through to nothing. Now routed to a real handler. New SharedPlayJoinRuntime.cs (280 lines, ported from legacy) polls the host's room every second with drift-corrected seeking — 0.8s threshold while playing, 0.35s paused — so listeners stay locked without micro-seeking. SharedPlayJoinedPackageStore.cs ported the joined-session package cache, so capsules actually load for listeners, not just bare audio. Presence/reactions/queue round-trip through the CDN client now, panel got a listener count, reactions ticker, and request list.

Buried fix: HostAsync was reading settings once at startup and ignoring live changes to CDN URL/live-channel toggle. Now re-applies on every change.

2 — Streamer Queue: Host Controls + A Bot That Won't Die

Host can add tracks and mark played/skipped directly, no viewer round-trip. Desktop got an add-to-queue button and history panel. Both pollers (desktop + bot) back off dead rooms instead of hammering them. SQ API calls got a 10s timeout + one retry, GETs only — mutating calls don't retry, to avoid double-submitting on a timed-out-but-successful response.

Bot had zero global error handlers before this. One bad promise took the whole process down silently. Now it logs and survives, plus 3-attempt startup retry.

3 — Story Pages vs. Visualizers: Three Bugs, One Afternoon

A capsule with both used to have them fight. Story can now queue a surface behind it — TryAdvancePastStory() swaps in the queued surface on spectral.resume(). Audio was starting under the story before the reader finished — now gated on whether a surface is queued. Once queued, picker/prev-next/keyboard could still hijack it — new IsVisualizerLocked blocks all three (user-picked "Special:" visualizers stay switchable).

4 — Platform + Perf Fixes

MP3 decoding silently depended on Windows' ACM codec via Mp3FileReader. Swapped to NLayer, pure managed decoder — MP3s decode on Linux/macOS now too. WebView2Host inline-rendered small visualizers via NavigateToString, which serves from about:blank with no real origin and tanks frame rate — everything goes through the fast file-backed path now. Auto-cycle switching checked IsLoaded/IsPlaying, both false during Spotify playback since Spotify never loads into the local engine — switched to HasTrack/IsPlaying. And: a regex stripping onclick="..." had no <script> exception, matched JS identifiers like onSomething =, and being quote-unguarded ate everything to the next stray quote anywhere later in the file. Fixed with a <script>-block carve-out.

—FAQ (no one asked these but they are here)—

Q: What is a "progger"? A: A human programmer. You. The one who's been waiting on shared play join links forever.

Q: Was shared play really host-only until now? A: Yes. The link existed, it just went nowhere.

Q: How bad was the regex bug? A: Silent corruption of unrelated code anywhere later in the file, no error thrown. Very glad it's found.

Q: Is 5.4.0 stable? A: As far as testing shows. That's what everyone says right before the hotfix.

—This is the end*—

*end for now, public users permitting

CYA!!!


Migration note: Imported from the original project journal. Publication date is estimated from the supplied sequence and approximately 13 work hours per day. Hours are a retrospective estimate. Original publication timestamps and media files were not included in the archive.