jellyfin-audio-player
jellyfin-audio-player copied to clipboard
Support scrobbling?
Hey, first off -- thanks for making this excellent app. The UI is especially neat.
It would be great if you could add support for "scrobbling" tracks back to the Jellyfin server. I'm not exactly sure how it works, but I'm using https://github.com/lyarenei/jellyfin-plugin-listenbrainz. I suppose there's some API you can hit on the Jellyfin server to facilitate this.
Hi there! Thanks for your input. This should be very possible to implement if we can find the right API call to make. If you could help me out locating the right call, I can implement it quite quickly.
Cheers!
Hi @leinelissen, these are the endpoints: For now playing: https://api.jellyfin.org/#operation/OnPlaybackStart For scrobbling: https://api.jellyfin.org/#operation/OnPlaybackStopped
Please don't forget to include the optional positionTicks
field, as this is necessary to determine how much of the track has been played (i.e.: listenbrainz requires 4 minutes or a half of a track to be considered as played). Thanks! :)
Ah perfect, that's super helpful! I'll try and see if I can make some time for a quick implementation in the coming weeks. Can I ask you for an e-mail, so that I can send you a preview build and double-check everything is sorted out? You can find my e-mail on my profile.
Cheers!
I've updated the GH profile and the e-mail should be now visible. Hit me up once you have it ready. :)
Since Offline Support is a thing in the app now, would plays be cached and pushed to the server if away from home when the Jellyfin server is not publicly accessible?
At least not initially, because implementing it takes some extra time. We can create a separate issue, as soon as scribbling is available.
At least not initially, because implementing it takes some extra time. We can create a separate issue, as soon as scribbling is available.
Sounds good!
Hey everyone! I've made a naive implementation, but I need someone to verify that it works as intended. Can anyone help out with this? Also pinging @attie @sseneca and @the-r0cket06...
@leinelissen As long as you are calling these two endpoints I mentioned earlier, it should work without any issues. Also, I recently updated the listenbrainz plugin to optionally handle events for MarkPlayedItem
endpoint, effectively supporting offline scenarios @GlassedSilver was talking about.
With that said, I can test it. I don't see any branches, so I assume you have an IPA available somewhere?
@leinelissen As long as you are calling these two endpoints I mentioned earlier, it should work without any issues. Also, I recently updated the listenbrainz plugin to optionally handle events for
MarkPlayedItem
endpoint, effectively supporting offline scenarios @GlassedSilver was talking about.With that said, I can test it. I don't see any branches, so I assume you have an IPA available somewhere?
Is my understanding correct that marking something played isn't meaning the same thing as play count and last played date?
Meaning: Merging of multiple plays from different sources is possible or not?
i.e. sync at home with phone, last played date (and time) is A. Then you play a song, now the last played date is B on the server and the let's say desktop client or something. (or just the server in terms of a web UI play). No further sync happens with the phone as you leave the house shortly after. You play that same song a few more times.... your phone's last played date is C.
Now when the phone syncs with the server when you arrive at home there are two new last played dates. Each device can tell the server the difference between the last played date A and their respective current value and the server should calculate the new total amount of plays like so: A + (B - A) + (C - A) = new total, last played date is set to the highest value and clients are updated accordingly as well to reflect that.
I've tried to keep the implementation as simple as possible for now. This means I've implemented the session events I see regular Jellyfin web throwing towards the server. I know that Finamp has also implemented scrobbling in this manner. I'm doing this live, so if a phone is offline, the events are lost.
For details see this file: https://github.com/leinelissen/jellyfin-audio-player/blob/0bf2775c93b4a8fad91d810834411dc01779f8f7/src/utility/PlaybackService.ts
@lyarenei This would be great! Let me put together a TestFlight build later this week and share it with you. Thanks in advance!
Is my understanding correct that marking something played isn't meaning the same thing as play count and last played date?
Meaning: Merging of multiple plays from different sources is possible or not?
@GlassedSilver Jellyfin does not care about the last played dates in a way that it would require to be in order or something. The mentioned API endpoint just accepts whatever date was sent (if any) in the call and then simply increments the play count.
The plugin I talked about only subcribes to selected events emitted by the server and then acts accordingly. When I mentioned offline support, I meant that if the client stores playback history when offline and then, when comes online and reports the history using that API endpoint, the plugin is able (if enabled) to react on the events emitted by that endpoint and pass on the playback data to ListenBrainz.
~@leinelissen Oh you have it in master already? If that's all, then I can just compile the branch and sideload the app, no need to bother with testflight. :)~
Okay, I guess fastlane does not like personal teams, so I guess IPA or testflight from you would be much easier after all. :)
Hey @lyarenei I managed to get it onto TestFlight. Can you PM me your Apple ID email? Then I'll add you to the release. You can find my contact details on my profile.
Thanks for the invitation.
I tried to play some stuff, but unfortunately it doesn't look like it's working.
Seeing that the call is made only when the setting is enabled https://github.com/leinelissen/jellyfin-audio-player/blob/0e298c6b996bfd71ac6c288d24c8d7d48d3ed4cd/src/utility/PlaybackService.ts#L44C1-L46
I tried to toggle it a few times to make sure it's enabled, but it didn't make any difference.
Next, I looked at server logs to see if perhaps there is something wrong with the request. Unfortunately, it seems like the server does not receive any playback status requests at all.
From that I can only guess what's happening. But either:
- The reporting setting toggle is not working correctly and the stored value is always false
- Something goes wrong before the request is made
I'm not sure if you are able to pull some logs from TestFlight (I honestly have no idea how does it work) to get any clues to what might be the issue. Looking at the code for sendPlaybackEvent
, I don't see anything what could cause any issues. Maybe if one of the things pulled from the player was null
for some reason?
Thanks for the effort! I might have made a wrong assumption on the data that is available in the background worker. I can't pull any logs from your device, but I should be able to debug this locally. Let me investigate and get back to you in t he coming week.
Hey @lyarenei, sorry for not getting back to you earlier, but I might have figured the thing out. I was submitting the payload via GET which obviously doesn't work as it needs to be a POST request 🤦♂️. I'm putting it through the TestFlight mill again, would dearly appreciate you trying it out once it's available to you.
@leinelissen Nice catch! :) The requests are now indeed processed by the server, however, the ItemId
is not set:
Also, subsequent progress requests have negative position ticks:
Okay, thanks again for checking it out. I've managed to fix the missing ItemId, it would have been missing for the first track in the queue. However, I haven't managed to reproduce the negative position ticks. Do you seen them consistently for all queue items?
However, I haven't managed to reproduce the negative position ticks. Do you seen them consistently for all queue items?
Unfortunately, I can't reproduce it now, so I guess it was just something weird at play.
I did some quick testing and it works when starting playback of an item. After that, the reqeust fails, because the request has a float/double value for playback position instead of int.
There are still 3 decimals remaining after converting to ticks here: https://github.com/leinelissen/jellyfin-audio-player/blob/9aff784580f72e0856f58213a6c6bc3e070b9ef4/src/utility/JellyfinApi.ts#L287
Ah right, that's a great catch. I've solved it now and I'm getting it to TestFlight tomorrow. It's showing up for me in the admin dashboard now, but it's constantly toggling between the playing track and showing an empty card. Any suggestions there by any chance?
The update has just reached TestFlight.
It's showing up for me in the admin dashboard now, but it's constantly toggling between the playing track and showing an empty card. Any suggestions there by any chance?
Hmm, for me it disappears after some time of inactivity. Which I think is a correct behavior. It shows currently playing item just fine during active playback. However, the progress keeps resetting to 1s. Two things on that:
- If the position taken from the player is in seconds, then you are off by an order. .NET defines 1 second as 10 000 000 ticks.
- You actually don't need to report progress periodically, the server can estimate the position on its own, from the provided playback rate field. So I think you'd be fine with just sending the progress when it actually matters (on pause and seek/resume).
Oof yeah, I completely missed that. I've done the calculation multiple times in the app, but I dropped the ball on this one 😅. I've fixed that and will make the release available as open beta. Solving the ticks also solves the Jellyfin interface issue btw. I'll stick with periodically reporting since the Jellyfin web app is doing so as well. Thanks so much for coaching me throughout this thing, and I hope all the data looks good on your end as well.
Sure, no problem. I did not much testing, but I played few songs and they were tracked correctly (minus the progress resetting). So I believe you are good to go.