Scrobbling not played tracks as 'now playing'

Issue description:

Hi,

I’m using Symfonium on my S24+ on the latest stock ROM, without rooting or modifying the system.
My server setup is Navidrome + Multi Scrobbler for scrobbling. I am experiencing random scrobbling events for tracks I haven’t started playing. It appears to be initially triggered by the system showing Symfoniums playback notification/widget, e.g. when I press the volume up button while watching a video.

I know there are many existing posts regarding scrobbling issues but I haven’t found a post with the exact same problem. I’m a developer myself and have read the Subsonic docs for the scrobbling API, and both, Navidrome and Multi Scrobblers source code to understand the issue and confirming my findings, before making this post.

What happens exactly?

  1. Volume buttons being pressed (while e.g. a Youtube video)
  2. Music widget/notification shows the media player (Symfonium) with playback controls in the notification area with the currently selected track in Symfonium. No playback from Symfonium occurs because I’m using another media source.
  3. Symfonium sends a scrobble request with submission=False to Navidrome with the song shown in the media player (logs below).
  4. Navidrome correctly handles this request as “now playing” scrobble update and treats the song as playback started (which is wrong because it was not played, only shown in the OS notification)
  5. Navidrome tracks the song internally as being played. No other playback or scrobble updates appear (because I’m not listening to music with Symfonium).
  6. The unplayed song is considered as 100% played after some time, because Navidrome things the song is actually being currenly played, causing wrong statistics.

Especially when not listening with music for a long time, this selected song gets wrongly scrobbled many times, completely disrupting the statistics to a point where it loses its purpose due to too many wrong statistics.

Why I think the issue lays in Symfonium:

  • OpenSubsonic’s scrobble docs describes submission = False as “now playing”, which implies playback.
  • These docs don’t state that it’s required to send submission = True after a song has been completely played back.
  • This issue only appears with Symfonium. No other player shows this behavior.

Here are the logs from the time Symfonium gets active after volume button press:

Verbose/PlayerService: MediaSession(true): Push state: PlaybackState {state=2, position=0, buffered position=0, speed=1.0, updated=166616963, actions=3964, error code=0, error message=null, custom actions=[Action:mName='Favorite, mIcon=2131230838, mExtras=null, Action:mName='Repeat, mIcon=2131230851, mExtras=null, Action:mName='Shuffle, mIcon=2131230861, mExtras=null], active item id=106}
Verbose/PlayerService: MediaSession(true): Skip queue update
Verbose/PlayerService: MediaSession(true: Push metadata (current_playlist/106): <SONG TITLE> [null/android.graphics.Bitmap@f8b3f7d]
Verbose/SubsonicLogger: --> [849] GET https://<MyNavidromeServerUrl>/rest/scrobble.view?id=song_id_redacted&submission=false&u=REDACTED&t=REDACTED&s=REDACTED&v=1.13.0&c=Symfonium&f=json
Verbose/SubsonicLogger: --> [849] END GET
Verbose/SubsonicLogger: --> [125] GET https://<MyNavidromeServerUrl>/rest/scrobble.view?id=song_id_redacted&submission=false&u=REDACTED&t=REDACTED&s=REDACTED&v=1.13.0&c=Symfonium&f=json
Verbose/SubsonicLogger: --> [125] END GET
Verbose/VolumeController: From Renderer: 26 [20]
Verbose/SubsonicLogger: <-- [849] 200 https://<MyNavidromeServerUrl>/rest/scrobble.view?id=song_id_redacted&submission=false&u=REDACTED&t=REDACTED&s=REDACTED&v=1.13.0&c=Symfonium&f=json (80ms, unknown-length body)
Verbose/SubsonicLogger: <-- [849] {"subsonic-response":{"status":"ok","version":"1.16.1","type":"navidrome","serverVersion":"0.59.0 (cc3cca60)","openSubsonic":true}}
Verbose/SubsonicLogger: <-- [849] END HTTP (81ms, 131-byte body)
Verbose/PlayerService: MediaSession(true): Skip state update
Verbose/SubsonicLogger: <-- [125] 200 https://<MyNavidromeServerUrl>/rest/scrobble.view?id=song_id_redacted&submission=false&u=REDACTED&t=REDACTED&s=REDACTED&v=1.13.0&c=Symfonium&f=json (92ms, unknown-length body)
Verbose/SubsonicLogger: <-- [125] {"subsonic-response":{"status":"ok","version":"1.16.1","type":"navidrome","serverVersion":"0.59.0 (cc3cca60)","openSubsonic":true}}
Verbose/SubsonicLogger: <-- [125] END HTTP (93ms, 131-byte body)
Verbose/StartActivity: Intent: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 xflg=0x4 cmp=app.symfonik.music.player/app.symfonik.ui.MainActivity.Note bnds=[304,1414][514,1624] } null
Verbose/StateManager: New state (true): true-false-false 
Verbose/MediaProviderManager: Scheduling providers: true / false / true
Verbose/SubsonicLogger: --> [942] GET https://<MyNavidromeServerUrl>/rest/ping.view?u=REDACTED&t=REDACTED&s=REDACTED&v=1.13.0&c=Symfonium&f=json
Verbose/SubsonicLogger: --> [942] END GET
Verbose/QueryBuilder: Query: <some irrelevant sql select query, selecting infos of the song>.
Verbose/SubsonicLogger: <-- [942] 200 https://<MyNavidromeServerUrl>/rest/ping.view?u=REDACTED&t=REDACTED&s=REDACTED&v=1.13.0&c=Symfonium&f=json (45ms, unknown-length body)
Verbose/SubsonicLogger: <-- [942] {"subsonic-response":{"status":"ok","version":"1.16.1","type":"navidrome","serverVersion":"0.59.0 (cc3cca60)","openSubsonic":true}}

The logs from Navidrome:

level=info msg="Now Playing" artist=<> player="Symfonium [Symfonium/Android]" position=0 requestId=<SongId> title="<The same title from Symfonium logs>" user=user
level=info msg="Now Playing" artist=<> player="Symfonium [Symfonium/Android]" position=0 requestId=<SongId> title="<The same title from Symfonium logs>" user=user

The logs from Multi Scrobbler:

[Sources] [Subsonic - unnamed] [Player 1-user] New Play: (<Song ID>) <Song Name>
[Sources] [Subsonic - unnamed] [Player 1-user] Started new Player listen range.
11x [Sources] [Subsonic - unnamed] Last activity was at 11:44:00+01:00 | Next check in 10.00s | No new tracks discovered
[Heartbeat] [Clients] Starting check...
[Heartbeat] [Clients] Checked Dead letter queue for 1 clients.
[Heartbeat] [Sources] Starting check...
[Heartbeat] [Sources] Checked 1 sources for start signals.
20x [Sources] [Subsonic - unnamed] Last activity was at 11:48:40+01:00 | Next check in 10.00s | No new tracks discovered
[Sources] [Subsonic - unnamed] Stale after no Play updates for 01:40 (staleAfter 90s)
[Sources] [Subsonic - unnamed] Ended current Player listen range.
[Sources] [Subsonic - unnamed] Generating play object with playDateCompleted
[Sources] [Subsonic - unnamed] [Stale Player Cleanup] (<Song ID>) <Song Name> added after met thresholds with tracked time of 280.00s (wanted 240s) and tracked percent of 99.00% (wanted 50%) and not matchin
[Sources] [Subsonic - unnamed] Discovered => <Song Name> @ 2025-12-07T11:44:00+01:00 (S)
[Sources] [Subsonic - unnamed] Last activity was at 11:48:40+01:00 (01:40 ago) | Next check in 10.00s
[Scrobblers] [Maloja - unnamed-mlj] [Upstream Refresh] Should refresh => newest queued scrobble playDate is newer than last refresh
[Scrobblers] [Maloja - unnamed-mlj] Refreshing recent scrobbles
[Scrobblers] [Maloja - unnamed-mlj] Found 40 recent scrobbles
[Scrobblers] [Maloja - unnamed-mlj] Scrobbled (Backlog)     => (Subsonic) <Song Name> @ 2025-12-07T11:44:00+01:00 (S)

I removed some unnecessary parts from the logs and redacted some parts, such as the Navidrome URL, for privacy.
If there are more information required regarding these or in any other way, please let me know.
I’m also happy to do further tests or give further details if needed.
Thank you a lot for this incredible app, I’m loving it and enjoying listening to music even more thanks to it!

Logs:

Upload description: The same logs also provided in the post. No further data added. Some parts are redacted for privacy. If more details are required for this bug, I’m happy to provide them.

Additional information:

 

 

Reproduction steps:

 

 

Media provider:

Subsonic

Screenshots:

     

  1. Samsung restart the app in the background for no reason, the only workaround is to disable media session restoration in the settings as per the many other issues.
  2. Subsonic API is very limited and different servers have different implementations. I created OpenSubsonic to improve many many things, scrobbling was not touched yet. submission=false means that it’s playing to show on a now playing dashboard. Without a submission=true playcounts and lastplayed are not updated and so external scrobbling should not occur. If some plugins decide to do that, they makes assumptions about the API that are simply not true and not generic enough. Symfonium gives controls about when and how a playcount and a resume point is updated and use the API accordingly for that.

For the rest a small extract of the logs and modified to not be parsed by my tools is useless as lack 99% of the need things.

So there’s not much to change on Symfonium side, if you want better global control then it needs an OS extension to properly describe all the needed API things.

Clearly state the difference between submission false being for now playing page in the web app and true being for actual submissions.

And stream | OpenSubsonic that clarify for OS that stream must not be taken in account for playcounts.