librespot-org / librespot

Open Source Spotify client library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for Spotify Radio/Dynamic Playlists (Context Support)

sashahilton00 opened this issue · comments

Issue by arigit
Friday May 12, 2017 at 16:48 GMT
Originally opened as plietar/librespot#184


Since forever I've noticed that when playing spotify Radio (launching it from an official spotify android client or from a linux client), librespot will play about ten songs and then it will restart from the first song, instead of continuing to pull new songs to play from the "dynamic playlist".

This indeed seems to be a librespot problem with dynamic playlists (that keep adding content forever). If one plays the media locally, then the client continues adding new content to the dynamic playlist and never repeats a song.

There is a long issue thread in spotify's support site, people complaining that their SpotifyConnect speakers will behave as described above. However, recently spotify people advised that this is not an issue with the Spotify Client, but an issue with very old firmware in the SpotifyConnect / speakers i.e. librespot.

Here's the thread.

https://community.spotify.com/t5/Ongoing-Issues/Radio-not-loading-more-songs-when-using-Spotify-Connect/idc-p/1673495#M42870

Hereby the request to have librespot support spotify's dynamic playlists including Radio

Comment by sashahilton00
Friday May 12, 2017 at 17:00 GMT


I'm not sure radio support has been implemented yet. I assume this is happening because librespot just receives the seed track list and doesn't listen/poll for updates to the song radio?

Comment by arigit
Friday May 12, 2017 at 17:08 GMT


Radio works, the problem is that it only plays 10 songs and then restarts. Same as older spotifyConnect clients apparently, lots of people complained in that thread. Looks like librespot is expected to "refresh the list" when it's reaching the end, radio is a special kind of playlist - dynamic playlist that never ends. The current behavior is a bit annoying.
It's been like this from the beginning and I thought it was time to raise it. Coudn't find previous references so here we are.

Comment by plietar
Friday May 12, 2017 at 17:12 GMT


The way Connect works is the remote sends a "Context URI" and a short list of tracks from that context.
If the receiver supports that context type, it can just use it to get the track list, otherwise it falls back to that list sent by the client.
Librespot doesn't support contexts at all, so it will always use that list. This is why radios and eg long playlists only play a few tracks in a loop

Comment by arigit
Friday May 12, 2017 at 17:19 GMT


I see, thanks.
Is there any chance we could get that in librespot? It's a fairly common use case, very useful, and librespot has been uber stable for some time (at least on my raspberry). It would be great to have this feature

Comment by jsopenrb
Saturday May 13, 2017 at 11:08 GMT


I suppose the same thing goes for playing similar tracks when an album ends and daily mixes. This is very desirable to have.

Comment by airdrummingfool
Thursday May 18, 2017 at 07:44 GMT


This is why radios and eg long playlists only play a few tracks in a loop

I'm not sure what a "long" playlist is, but I've tested playlists as long as 100 tracks and Librespot plays them fine.

However, I notice the 10-track-loop constantly on Radio and Daily Mixes, and would love to have context working correctly to support those features. As a bonus, I bet it would also add support for showing (in Spotify clients) which playlist/track is currently playing (such as the speaker icon next to the playlist name, the track highlighted green in the list of songs for the playlist, etc).

Playing on another device (top) vs playing on Librespot (bottom):
image

I believe issue #21 is related to this feature request.

Comment by infinity85
Wednesday May 24, 2017 at 11:13 GMT


Aaah... this issue explains why my Onkyo Audio Receiver (2012 lineup) has this behavior since Spotify introduced Radio and Mix Tapes features some months ago. So it is actually because these older devices and librespot rely on some kind of fallback.

I'd love to see this "dynamic" feature introduced in librespot :)

Comment by sashahilton00
Sunday Jun 04, 2017 at 19:48 GMT


might be worth adding the feature request tag to this

Comment by arigit
Thursday Sep 07, 2017 at 21:53 GMT


Yes very old spotify firmware in speakers has the same problem as librespot, it's unable to play more than a dozen songs from a dynamic list (dynamic lists include Spotify Radios and Daily Mixes), and then it starts repeating the same few songs instead of loading new ones.
Still hoping that librespot will get support for this.
@sashahilton00 I couldn't find the way to add a Feature Request tag. Looks like only the project owner can.

This should be a priority, as quite a bit of stuff seemingly depends on it (dynamic playlists, radio, daily mix, green selected bar in spotify, etc.)

Just to note, the websockets API from Spotify uses context URI's quite a lot, and provides a good example of how they are structured/used if we're looking to implement support for them. /cc @kingosticks fyi.

Is anyone working on this right now perhaps in a fork or another thread? I'd like to help... I'm trying to figure out where to familiarize myself with what's going wrong and what's necessary (re ContextURI) before I make a commitment that I don't have the skills to back up.

I presume that official clients are following the rules 😃so when the platform sends play signals to the connect clients (like librespot) something is going wrong there (as written above).

Seems like @plietar had some detailed understanding about a year ago; is it a state thing, like the playback software needs to be told its in "radio mode" and has to ask for more tracks via some other api? Or is something more magical supposed to happen?

(I've got Chromecasts and other officially supported devices running, but I prefer librespot for a number of reasons, so I'd like to help any effort to bring this to parity.)

Basically you need to add support for fetching a list of tracks given a Context URI. You should be able to see the context uri by adding some println in spirc.rs.

You can look at how the official client fetches the track list using spotify-analyze (works on macOS and soon on linux: librespot-org/spotify-analyze#1). Look at the requests it makes, especially the mercury related ones.

You would then need to reimplement the fetching for each type of Context URI (eg. playlist, radio, album, artist, search, ...). Given a "hermes"/"mercury" URI (starts with hm://) you can fetch the resource using :

let future = session.mercury().get("hm://foo/bar/baz").and_then(|response| {
    protobuf::parse_from_bytes(response.payload[0]).unwrap()
});

This gives you a future which will eventually resolve to the response.
To start off easily, you can reuse the play.rs example and do core.run(future), which will wait for the result.

Let me know if there's anything else you would need help with

I'll also add, you can use Chrome to inspect the websockets API for play.spotify.com if you just want to see the context URIs and the retrieved tracks easily, though it won't help in terms of understanding how to implement it in librespot.

Thanks for the fast response! But what does librespot need to do with these responses?

Do we need a whole suite of logic to be changed according to these instead of the old API/way?

Is it that radical of a change?

I've been tweaking librespot a little for personal purposes and ran into similar issues with shuffling as others have reported. I'm interested in adding support for proper queue and context management. Has any progress been made on fetching the tracklist from a context uri (before I dive too deep down the rabbit hole)?

commented

FWIW: ashthespy@8a0b004 I have working dynamic playlists - except for the dailymix stuff.

Very cool! I'll base my work off of that. Any reason the features on that branch never got merged?

commented

Hmm because I couldn't find the proper mercury endpoint to give me that data in protobuf format. Currently it's in JSON and rather inelegant if you ask me.

FWIW: ashthespy@8a0b004 I have working dynamic playlists - except for the dailymix stuff.

Will this eventually be merged with the master? I use Raspotify since a few days and notice the same issues. Would like to turn on a track-based radio and listen to it forever, but I am not comfortable with tweaking the code as described by @ashthespy . Thanks though for figuring this out!

@ashthespy If you use hm://context-resolve/v1/ it will give you the next page URL and there you can retrieve the next tracks in JSON format. The problem I am facing is that when I add them to the tracks list and send it to server it returns 500.

The playback works, but the client isn't being updated.

commented

@devgianlu I retrieve the track references and next page url via the hm://radio-apollo endpoint.
Then update frame with the resolved tracks to get it to update on the client side. This seems to work as expected so far :)

radio

Will this eventually be merged with the master? I use Raspotify since a few days and notice the same issues.

@mendeldesign My implementation is very rudimentary and could use a some refinement before being merged into master. I for one don't like the JSON endpoint, and would prefer to sniff out the protobuf one. I do believe there is a protobuf endpoint - there is a ContextPage protobuf message that I saw when pulling proto files from the official client.

Would like to turn on a track-based radio and listen to it forever, but I am not comfortable with tweaking the code as described by @ashthespy . Thanks though for figuring this out!

I have a few pre-compiled binaries with a few customisations (and a socket for Volumio) for the usual fruity boards over at https://github.com/ashthespy/Vollibrespot/releases

@mendeldesign I have the radio working on librespot-java, version 0.1.3. As sad by Ash there is some junkiness going on, but it works.

commented

@devgianlu Does the context-resolve endpoint support the daily-mix stuff?

Thanks @ashthespy and @devgianlu, I fear though that I am not skilled enough to implement these solutions. I used the step-by-step installation from Raspotify and am happy that I got it working, but I am missing the radio feature and controlling pauze/play via a python script would be nice to have. I will try to get the latter working by adding Spotipy to my pi to control playback. However, I am (still!) uncomfortable with installing pre-compiled binaries or replacing the implementation I currently have in Raspbian. If an future update to librespot (and thus to Raspbian) fixes the dynamic playlist issue, I will be happy.

@ashthespy It doesn't, returns 404. I'm hoping the webgate one does.