Spotifyd / spotifyd

A spotify daemon
https://spotifyd.rs
GNU General Public License v3.0
9.68k stars 444 forks source link

Information about currently played track / context in D-Bus, socket or some other way of communication with a client #1250

Open ciembor opened 7 months ago

ciembor commented 7 months ago

Is your feature request related to a problem? Please describe. I'm working on a client that has to be as responsible as possible (manual knobs, button and UI on the screen of a device). Number of requests sent to API is limited and I have to send requests about current track every second to get this info and update UI.

Describe the solution you'd like Daemon have info about current track already and uses D-Bus to notify about currently playing track / context:

            .emits_changed_false()
            .get(move |_, _| {
                if let Ok(Some(playback)) = sp_client.current_playback(None, None::<Vec<_>>) {
                    if playback.device.name == mv_device_name {
                        if playback.is_playing {
                            return Ok("Playing".to_string());
                        } else {
                            return Ok("Paused".to_string());
                        }
                    }
                }
                Ok("Stopped".to_string())
            });

But I need something more, spotify id of a track / context and type (I'm not sure here, but probably you have different types of contexts). It would be wise to not send these requests from both daemon and client.

Describe alternatives you've considered I don't know if D-Bus isn't to narrow, sockets might be more universal.

Drexel2k commented 7 months ago

Why do you have to request the API every second and not only when something relevant changes? You could listen to PropertiesChanged event/signal on the DBus and only request new information when the track changes e.g.

And the DBus interface is a general MediaPlayer standard:

Maybe in the metadata of the PropertiesChanged signal is also the informatio you need.

If not that would mean adding info beyond the standard somewhere.

ciembor commented 7 months ago

@Drexel2k Thank you for link to the DBus documentation, I didn't work with DBus so far and I thought it's more generic, like sockets and allows apps to use their own interfaces. Anyway, I don't want to rely on DBus, my client should work with any device and there are no websockets in Spotify API, so without spotifyd I have to do these requests all the time (that's sad). However I can use DBus if I'm sure spotifyd connection is available.

Oh, and one more thing, I use RaspberryPi and I've read there are problems with efficiency of spotifyd DBus. Is it still the case? Was it somehow fixed or it's something that can't be solved?

Drexel2k commented 7 months ago

I am new to Linux and Raspberry also, but from my understanding DBus is one standard way of Linux for inter process communication. It works via file links or sockets. I really think all the information you needs IS in the Metadata of the properties changed Event,e.g:

`

('org.mpris.MediaPlayer2.Player', {'Metadata': ('a{sv}', {'xesam:albumArtist': ('as', ['Daft Punk', 'Pharrell Williams', 'Nile Rodgers']), 'mpris:trackid': ('o', '/spotify/track/2Foc5Q5nqNiosCNqttzHof'), 'xesam:title': ('s', 'Get Lucky (Radio Edit) [feat. Pharrell Williams and Nile Rodgers]'), 'xesam:album': ('s', 'Get Lucky (Radio Edit) [feat. Pharrell Williams and Nile Rodgers]'), 'xesam:trackNumber': ('u', 1), 'xesam:discNumber': ('i', 1), 'mpris:artUrl': ('s', 'https://i.scdn.co/image/ab67616d0000b2731d5cf960a92bb8b03fc2be7f'), 'xesam:url': ('s', 'https://open.spotify.com/track/2Foc5Q5nqNiosCNqttzHof'), 'mpris:length': ('x', 248413000), 'xesam:autoRating': ('d', 0.82), 'xesam:artist': ('as', ['Daft Punk', 'Pharrell Williams', 'Nile Rodgers'])}), 'PlaybackStatus': ('s', 'Playing')}, []) `

JJ-Author commented 7 months ago

I also do not really understand what is being proposed / requested here? I see that the MPRIS interface of spotifyd does not provide all information available in the spotify context (e.g. current playlist id/name is missing, track position also does not work correctly in playlists). But what information do you actually need?

Anyway, I don't want to rely on DBus, my client should work with any device and there are no websockets in Spotify API, so without spotifyd I have to do these requests all the time (that's sad). However I can use DBus if I'm sure spotifyd connection is available.

I do not understand this. You opened this issue in the spotifyd project, so it makes sense to assume that you have connection to a running version of spotifyd, right? The only thing I could see here that your client is not running on the same machine as spotifyd but in the same network, and you are looking for a way to discover and control all spotifyd instances in a local and out-of-the-box way (so without using spotify API and special setups like dbus-proxying)

I use RaspberryPi and I've read there are problems with efficiency of spotifyd DBus. Is it still the case? Was it somehow fixed or it's something that can't be solved?

I use it on the super old Pi 1B that has only 220 MB RAM available and it works fast on that old machine (I experienced a stability issue though #1246) fast enough to e.g. poll the playback position 1-2 times a second

see e.g. this

root@DietPi:~# while true; do    time position=$(dbus-send --system --print-reply --dest=org.mpris.MediaPlayer2.spotifyd.instance1840 /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Position | grep variant | awk '{print $3}');     echo "Current Position: $position";     sleep 1  # Query every 1 second. Adjust this interval as needed.  
done

real    0m0.240s                                                                                                                                                                   
user    0m0.010s                                                                                                                                                                   
sys     0m0.100s                                                                                                                                                                   
Current Position: 10019000     
ciembor commented 7 months ago

@JJ-Author I wasn't aware what is and what isn't available in this protocol, but if you say there is no track / context id, name, information about the type of the context (playlist, artist, album - I use these, maybe there are more types), etc., it would be very nice to have it in metadata delivered from the spotifyd. In other case I have to use Spotify API to get these data for my client. And HTTP request is not as responsive as using local protocol. If such data is already available localy in spotifyd, why duplicate this funcionality?

In general I think all the metadata available in spotifyd should be shared and available from a client. In this or another way (like sockets).

I will write my app in a way where DBus is optional (because I already started without it), but if someone writes something that may work only with spotifyd device, than it would be good strategy.

JJ-Author commented 7 months ago

As @Drexel2k already wrote the trackid is available. But for other data, you could try to open a "fresh and clean" issue specifying that you would like to have more spotify context metadata on MPRIS or DBUS But I htink it is tricky to map the information from the context into MPRIS (and some information might not be able to be expressed) and if somebody would commit to implement that^^ I had a look at the spotify client for linux from spotify engineers/developers and it also does not support more metadata (so also no playlist name, no playlist tracklist, etc.) via MPRIS

I personally would do it like this (assuming your client is running on the same device/dbus).

I would consider adding some stability features

here is an example command that subscribes to every MPRIS capable player (also non-spotify ones) for changed properties (e.g. playback status, metadata change)

dbus-monitor "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/mpris/MediaPlayer2',arg0='org.mpris.MediaPlayer2.Player'" 
eladyn commented 6 months ago

@JJ-Author In general, I totally agree with your write-up. That sounds like the right thing to do.

i would poll the position with timeouts for the dbus commands every few seconds and if the polls are too slow or hang i would switch back to API for some time interval

However, this is something I would strongly recommend against using, since we (unfortunately) rely on the Spotify API as well, to get the playback position. :sweat_smile: So you'll most likely run into 429s when doing this too often. The recommended strategy is: