v0.3.16 — OpenSubsonic scrobble fix
Velvet v0.3.16 — OpenSubsonic scrobble fix
What changed
reportPlayback scrobbling now works correctly
Scrobbles sent by OpenSubsonic-compatible clients (such as Naviamp) were
silently dropped. Here is why, and what was fixed.
Background — two scrobble flows
The original Subsonic API has a scrobble endpoint: the client sends it
once when it decides the user has "finished" a track. Every client picks
its own threshold (often 50% played, or a hard-coded timer).
OpenSubsonic introduced a different, server-owned flow via the
reportPlayback extension (added to the spec March 2026). Instead of a
single "scrobble now" call, the client sends periodic state updates:
state=starting → user pressed play
state=playing → every ~15–30 s while playing
state=paused → user paused
state=stopped → user moved to the next track / stopped playback
The key design intent, as described in the OpenSubsonic spec:
"Servers should not assume a media is played after the calculated end
of content, but needs to wait for a stopped state."
The server decides when a stopped event should count as a scrobble,
keeping threshold logic in one place rather than every client re-implementing it.
What was wrong
Velvet's reportPlayback handler recognised started, playing,
paused, and completed — but completed is not a valid state in the
spec. The real terminal state is stopped. So every time Naviamp (or
any other compliant client) sent state=stopped when skipping or
finishing a track, the server did nothing and the scrobble was lost.
What is fixed
state=stoppednow triggers the scrobble path.- A listen-threshold check is applied before scrobbling on
stopped:
the user must have heard ≥ 50 % of the track or ≥ 4 minutes,
whichever is the shorter threshold (mirrors the Last.fm / ListenBrainz
minimum). Position is read from thepositionMsparameter; if that is
zero, elapsed time is estimated from whenstarting/playingwas last
received. - Tracks where
positionMsindicates less than 30 seconds of listening
are never scrobbled (handles quick skips). state=startingis accepted for now-playing tracking (the spec-correct
name; the oldstartedalias still works for older clients).state=completedis still accepted and scrobbles unconditionally, so
any client already using that value keeps working.ignoreScrobble=truesuppresses side effects onstoppedthe same way
it does on every other state.
Admin panel setting
The per-user toggles "Scrobble to Last.fm via Subsonic" and
"Scrobble to ListenBrainz via Subsonic" control whether stopped
events are forwarded. Users who have not enabled these remain unaffected.