Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(auto)play errors on startup or reconnect of spotifyd #1155

Open
JJ-Author opened this issue Jan 3, 2023 · 6 comments
Open

(auto)play errors on startup or reconnect of spotifyd #1155

JJ-Author opened this issue Jan 3, 2023 · 6 comments
Labels
bug A functionality or parts of a program that do not work as intended

Comments

@JJ-Author
Copy link

JJ-Author commented Jan 3, 2023

Description
after spotifyd is started up freshly (or reconnecting to AP) it seems not possible to start the playback via spotify API

( I can not test MPRIS since armv6 binary has no dbus support but probably that is affected also in similar way)
EDIT yes it is: see #1157

To Reproduce

  1. start spotifyd (but no other spotify device like android app or web player!)
  2. go to spotify console and click get token button
  3. select scope user-read-playback-state and user-write-playback-state
  4. get the device id of spotifyd https://developer.spotify.com/console/get-users-available-devices/
  5. browse to https://developer.spotify.com/console/put-user-player/ and set the body (replace the id from 4!) { "device_ids": [ "1ecc089171983b406deeb321b10c98f0dd62d7da" ], "play":true }
  6. see the spotifyd log "restart" --> AutoplayError: MercuryError will appear
  7. See error -->

Expected behavior
I think if spotifyd loses its "playback context" on (re)connect to spotify it would make sense to restore it somehow. I think a convenient strategy would be to continue with the last played song and its context (e.g. the playlist or autoplay station) when the play command is sent. Using this API call https://developer.spotify.com/console/get-recently-played/ this should be possible. For the transfer playback call I would expect that no error is shown and the same strategy could be used.

Logs

Click to show logs

reconnect

Jan 03 09:00:56 DietPi spotifyd[5497]: Loading <Cannons> with Spotify URI <spotify:track:1tgLMyNVmgtLJcOfE0wbyJ>                                                                                            
Jan 03 09:00:56 DietPi spotifyd[5497]: <Cannons> (224773 ms) loaded                                                                                                                                         
Jan 03 09:14:11 DietPi spotifyd[5497]: Fetching autoplay context uri                                                                                                                                        
Jan 03 09:14:11 DietPi spotifyd[5497]: Autoplay uri resolved to <"spotify:station:album:6EB14IXV5oyOiItGBv7mtG">                                                                                            
Jan 03 09:14:11 DietPi spotifyd[5497]: Loading <Cannons> with Spotify URI <spotify:track:1tgLMyNVmgtLJcOfE0wbyJ>                                                                                            
Jan 03 09:14:11 DietPi spotifyd[5497]: Resolved 50 tracks from <"spotify:album:6EB14IXV5oyOiItGBv7mtG">                                                                                                     
Jan 03 09:14:11 DietPi spotifyd[5497]: <Cannons> (224773 ms) loaded                                                                                                                                         
Jan 03 18:28:39 DietPi spotifyd[5497]: subscription terminated                                                                                                                                              
Jan 03 18:28:39 DietPi spotifyd[5497]: Connecting to AP "ap.spotify.com:443"                                                                                                                                
Jan 03 18:28:40 DietPi spotifyd[5497]: Connection reset by peer (os error 104)                                                                                                                              
Jan 03 18:28:40 DietPi spotifyd[5497]: Authenticated as "xxx" !                                                                                                                    
Jan 03 18:28:40 DietPi spotifyd[5497]: Country: "DE"                                                                                                                                                        
Jan 03 18:28:40 DietPi spotifyd[5497]: Using Alsa sink with format: S16                                                                                                                                     
Jan 03 19:39:35 DietPi spotifyd[5497]: Fetching autoplay context uri                                                                                                                                        
Jan 03 19:39:35 DietPi spotifyd[5497]: No more tracks left in queue                                                                                                                                         
Jan 03 19:39:35 DietPi spotifyd[5497]: error 400 for uri hm://autoplay-enabled/query?uri=                                                                                                                   
Jan 03 19:39:35 DietPi spotifyd[5497]: AutoplayError: MercuryError                                                                                                                                           

restart

Jan 03 19:57:59 DietPi systemd[1]: Stopping Spotifyd (DietPi)...
Jan 03 19:57:59 DietPi systemd[1]: spotifyd.service: Succeeded.
Jan 03 19:57:59 DietPi systemd[1]: Stopped Spotifyd (DietPi).
Jan 03 19:57:59 DietPi systemd[1]: spotifyd.service: Consumed 26min 34.637s CPU time.
Jan 03 19:57:59 DietPi systemd[1]: Started Spotifyd (DietPi).
Jan 03 19:57:59 DietPi spotifyd[6762]: Loading config from "/mnt/dietpi_userdata/spotifyd/spotifyd.conf"
Jan 03 19:57:59 DietPi spotifyd[6762]: No password specified. Checking password_cmd
Jan 03 19:57:59 DietPi spotifyd[6762]: No password_cmd specified
Jan 03 19:57:59 DietPi spotifyd[6762]: No proxy specified
Jan 03 19:57:59 DietPi spotifyd[6762]: Using software volume controller.
Jan 03 19:57:59 DietPi spotifyd[6762]: Connecting to AP "ap.spotify.com:443"
Jan 03 19:57:59 DietPi spotifyd[6762]: Authenticated as "xxx" !
Jan 03 19:57:59 DietPi spotifyd[6762]: Country: "DE"
Jan 03 19:57:59 DietPi spotifyd[6762]: Using Alsa sink with format: S16
Jan 03 19:58:33 DietPi spotifyd[6762]: Fetching autoplay context uri
Jan 03 19:58:33 DietPi spotifyd[6762]: No more tracks left in queue
Jan 03 19:58:33 DietPi spotifyd[6762]: error 400 for uri hm://autoplay-enabled/query?uri=
Jan 03 19:58:33 DietPi spotifyd[6762]: AutoplayError: MercuryError

Versions (please complete the following information):

  • OS: DietPi 8.0.2
  • Spotifyd: 0.3.4-armv6-slim
@JJ-Author JJ-Author added the bug A functionality or parts of a program that do not work as intended label Jan 3, 2023
@JJ-Author JJ-Author changed the title (auto)play errors on startup of spotifyd (auto)play errors on startup or reconnect of spotifyd Jan 19, 2023
@eladyn
Copy link
Member

eladyn commented Mar 2, 2023

Thank you for the detailed report, and sorry for the long response time.

it seems not possible to start the playback via spotify API

  • using transfer-playback

There's currently ongoing work on adding a TransferPlayback method to D-Bus (#1162), although that API call seems to be not working for you as well? Does it just not start playing music, but transfer the playback to the device, or does it fail in general?

For me personally, it works when I close all clients, start spotifyd and call the new io.github.spotifyd.TransferPlayback method. Maybe I'd need to wait some time before connecting spotifyd?

Apart from that, since we're completely relying on librespot for the backend handling, I imagine it would be quite difficult, to implement what you describe in spotifyd itself. So let's hope that the new D-Bus method helps you get further.

@JJ-Author
Copy link
Author

There's currently ongoing work on adding a TransferPlayback method to D-Bus (#1162), although that API call seems to be not working for you as well? Does it just not start playing music, but transfer the playback to the device, or does it fail in general?
if another device is/was playing very recently then that worked. But is doest not transfer the playback (I guess since there is nothing to transfer since there is no other device)

For me personally, it works when I close all clients, start spotifyd and call the new io.github.spotifyd.TransferPlayback method. Maybe I'd need to wait some time before connecting spotifyd?
This is the new DBUS method that will be released in the future? Or how do I invoke that method?

Yes you need to wait a bit (4 hours should be save) if another client was playing before and also restart spotifyd after that.

Apart from that, since we're completely relying on librespot for the backend handling, I imagine it would be quite difficult, to implement what you describe in spotifyd itself. So let's hope that the new D-Bus method helps you get further.

Yeah of course we can also try to forward relevant parts of issue at the librespot issue tracker. I am not sure which errors are caused by what tool. the "mercury error" e.g. is it caused in librespot or is it just a wrong usage of its autoplay feature in spotifyd?

EDIT: Just saw that there is the 0.3.5 release. will try that when I have time to compile that with DBus on ARMv6.

@JJ-Author
Copy link
Author

Took me quite some time to figure it out but I managed to compile spotifyd-0.3.5+5565f24 with DBUS for armv6l using qemu.

good news: transfer-playback both from dbus as well as from the API work now from an autoplay context smoothly and without error.

bad news: the play function still does not work from either MPRIS or the API (without any error in the log though) in the following states

  1. fresh start of spotifyd
  2. reconnect of spotifyd after network interruption
  3. when playback was transferred from spotifyd to another device that was then paused and put offline after playing 1-2 tracks

Note: it works however if i just play some tracks and pause, then I can resume with play button hours later... assuming stable internet and that I did not use any other spotify device to play on my account.

My questions now are:

  • why is that? with the official spotify devices this does not happen.
  • why not use the code that drives the transferplayback DBUS function also for MPRIS play function as a fallback if play routine does not succeed? Otherwise I think spotifyd MPRIS interface seems implemented in an incorrect way since the CanPlay property to be true constantly obviously breaks the standard because it does not follow the definition since playback can not be started using Play or PlayPause in above states 1-3. for this specific standard violation I can open another issue, but setting this correctly to false in states 1-3 would not help much with the problem that spotifyd can still practically not be used (or very much limited only) by standard MPRIS clients/controllers.

@eladyn
Copy link
Member

eladyn commented Jan 4, 2024

Calling play currently just uses the play method offered by librespot, which forwards it to whatever device is the currently active playing device.

Transferring the playback to itself is (to my knowledge) not implemented in librespot, so we have to rely on the Spotify API for that, which is not very elegant and also slow. So I would personally rather spend the time implementing the "better" version that librespot can claim the currently active device itself without the help of the Spotify API instead of abusing the API once more for fixing MPRIS behaviour. But if you really need that feature, patches are very welcome!

@JJ-Author
Copy link
Author

JJ-Author commented Jan 5, 2024

Calling play currently just uses the play method offered by librespot, which forwards it to whatever device is the currently active playing device.
Hm I can not confirm that in practice. MPRIS play of spotifyd never started my other spotify devices (even if I paused them a second ago)

I am fine and happy at the moment, with a modified version of this https://github.com/FreekBes/spotify_web_controller and the dbus transfer-playback call. just wanted to report back.
but I wonder how spotifyd users start playing music, given that mpris is not of help here - is everybody using spotify web app, or spotify app on a smartphone? And I had the vision of letting buttons of an VCR IR remote control trigger MPRIS calls to control spotifyd, bluetooth sources or whatever mpris player is running on my Pi, but with some extra logic and the transfer-playback DBUS call this should be also possible.

so maybe it makes sense to close this issue and create a new and more specific one for improving MPRIS implementation?

Nevertheless I had a look but I think I can not be of help here, would just break things. many mpris implementation decisions are too cryptic to me (adding to the cryptic rust syntax)
It seems to be a combination of

  • librespot_connect spirc (mostly for control)
  • rspotify (mostly for metadata)

but I don't understand why not directly
https://docs.rs/librespot-playback/0.4.2/librespot_playback/player/struct.Player.html#method.play has been used instead of librespot_connect
and
https://docs.rs/librespot-metadata/0.4.2/librespot_metadata/struct.Track.html
instead of rspotify

also properties that emit changes are defined as emits_changed_false
the seeked signal is not defined

@eladyn
Copy link
Member

eladyn commented Jan 6, 2024

I am fine and happy at the moment, with a modified version of this https://github.com/FreekBes/spotify_web_controller and the dbus transfer-playback call. just wanted to report back. but I wonder how spotifyd users start playing music, given that mpris is not of help here - is everybody using spotify web app, or spotify app on a smartphone? And I had the vision of letting buttons of an VCR IR remote control trigger MPRIS calls to control spotifyd, bluetooth sources or whatever mpris player is running on my Pi, but with some extra logic and the transfer-playback DBUS call this should be also possible.

In comparison to the official clients or things like ncspot or spot, this project is not meant to provide the full client experience from searching for music to playing it, but rather a stripped down version, mainly the playback part. This makes it similar to many third-party speakers or audio systems which also only support playback and need a controlling Spotify client. In addition to that, spotifyd can control itself - to some extent, and that can also be used to get something like a basic player interface. But since that part is an optional feature, the primary way to control spotifyd is still some third-party application.

so maybe it makes sense to close this issue and create a new and more specific one for improving MPRIS implementation?

I think, there's a lot of room for improvement with new sessions like continuing playback or, as you proposed, automatically transferring playback to the device, when play is called.

Nevertheless I had a look but I think I can not be of help here, would just break things. many mpris implementation decisions are too cryptic to me (adding to the cryptic rust syntax) It seems to be a combination of

* librespot_connect spirc (mostly for control)

* rspotify (mostly for metadata)

Yes, the rspotify part is unfortunately needed, since librespot doesn't expose most of the metadata it has available. This will change in the next release of librespot, but until then we need to stick with those API requests unfortunately.

but I don't understand why not directly https://docs.rs/librespot-playback/0.4.2/librespot_playback/player/struct.Player.html#method.play has been used instead of librespot_connect and https://docs.rs/librespot-metadata/0.4.2/librespot_metadata/struct.Track.html instead of rspotify

As for the Player part: Since we are a Spotify connect device, we need to communicate all player changes through "Spirc". In the librespot architecture, Spirc wraps the player and makes sure that playback changes are synchronized with the Spotify servers / other clients. So using Player directly would probably work, but cause issues with other clients.

also properties that emit changes are defined as emits_changed_false

To be honest, I'm not really sure what that method does, probably some introspection things? If so, it might make sense to change that to true.

the seeked signal is not defined

Oh, good point. If you want, feel free to add that definition!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A functionality or parts of a program that do not work as intended
Projects
None yet
Development

No branches or pull requests

2 participants