Implement getNowPlaying + live Streams on Activity page (#237) - #263
Merged
Conversation
…age (closes #237) - New in-memory NowPlayingService (5-min TTL, one entry per user+client, stable playerId) fed by scrobble?submission=false pings; nothing durable. - getNowPlaying implemented per-user (owner decision on #237): callers see only their own players. Entries carry the full Song + username, minutesAgo, playerId, playerName; starred/playCount annotated. - scrobble submission param now parsed case-insensitively (py-sonic sends 'False'; matches Java Boolean.parseBoolean) — caught by the federation compat suite; pitfall documented. - Admin GET /api/admin/hub/activity/active gains nowPlaying[] (cross-user); Activity page renders live now-playing rows atop the Streams list. - SPA PlayerBar pings now-playing on play/resume/track-change and every 2 min while playing, for both local and Sonos-cast sinks. - Docs: opensubsonic.md (getNowPlaying Stub→Implemented, scrobble note), pitfalls.md boolean-param row. Compat suite stub test upgraded to exercise the real flow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SwwPcrohddTUN7kmQVedQC
6 tasks
benders
marked this pull request as ready for review
July 16, 2026 05:39
Owner
Author
|
FROM @claude:
|
Owner
Author
|
FROM @claude:
|
…ks (PR #263 review, refs #237) - stream.ts: pipe with stream/promises pipeline() instead of bare .pipe(). A client abort now cancels the upstream fetch and finish()es the op with the real byte count; before, aborted requests neither ended nor errored — entries leaked in the Active list forever and recorded full-file sizes (the "identical duplicate rows" symptom). Pitfall documented. - StreamTrackingService: close out rows left unfinished by a previous process at boot (error='interrupted'); finish() is idempotent. - Now-playing entries carry a preferred-source snapshot (sourceKind/ sourcePeerId/format/bitrate) + albumId, resolved at ping time. - stream_operations gains album_id (additive migration); Subsonic and federation stream starts record it. - Activity page: Active split into "Now Playing" and "Transfers" subsections; History coalesces consecutive same-listen transfers into one ×N row with summed bytes; track titles link to their album. - Vibrdrome absence from Now Playing confirmed as a client limitation: it never calls scrobble (no submission=false pings). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #237.
What
getNowPlayingimplemented (was a stub). Fed byscrobble?submission=falsenow-playing pings, held in a new in-memoryNowPlayingService— one entry per (user, client) player, stableplayerId, expiring 5 minutes after the last ping. Entries are full Song objects plususername,minutesAgo,playerId,playerName, with starred/playCount annotations.GET /api/admin/hub/activity/activenow returnsnowPlaying[]; the Streams list shows live playback rows (green pulsing "Playing" badge, user, client, minutes-ago) above the in-flight transfer rows — so a track playing from the SPA, a 3rd-party client, or a Sonos cast is visible for the duration of the listen, not just while bytes move.scrobble(id, false)on play/resume/track-change and every 2 minutes while playing, on both local and Sonos-cast sinks.submissionwas parsed with=== "false", but py-sonic sendssubmission=False(capital F) — the ping was being recorded as a durable play. Now parsed case-insensitively like Java'sBoolean.parseBoolean. Added todocs/pitfalls.md.Not in scope
The broader Activity-page overhaul from the issue comment (restructuring history into local "plays" vs peer "fetches") — this PR adds the live now-playing surface; the history-view rework is a follow-up if wanted.
Testing
pnpm verify: hub 815, frontend 150 — greenpnpm lint: zero outputpnpm test:federation: 84/84 py-sonic compat (incl. upgradedtest_get_now_playingexercising the real ping→read flow) + eviction/re-admission acts — green🤖 Generated with Claude Code
https://claude.ai/code/session_01SwwPcrohddTUN7kmQVedQC