Pushing playback state/info to Volumio

Hi development team,

Maybe it’s a weird question, but in the light of integrating services I was wondering if a service could ‘push’ a state as opposed to retrieving it from Volumio?

For example, we have the librespot (and spotify-connect-web) integrations; at this moment it’s impossible to have the UI show that the plugin is actually playing. I mean in the plugin you can have Volumio (or just MPD) stop playback, but the metadata of the playing items can never be propagated to the UI.
Same goes for LMS for that matter, I can image someone wanting to run LMS from their server/PC, because indexing on a stronger machine is way more convenient, but using software like that means you are unable to see what is playing in the UI. Also, next order of business would then be to map the buttons to functionality within the plugin.

Being able to push to Volumio makes Volumio the main point of entry, this makes development for e.g. LastFM or LCD plugins easier, since you only need to subscribe to pushState. Just thinking out loud here, if there’s anything I haven’t thought of that makes this impossible, then please let me know. :slight_smile:
Or of course, if this does not fit the vision of the Volumio team. As said, I’m just thinking out loud here, not complaining or forcing anything. :wink:

We do need to establish ground rules regarding the pushing of information to Volumio, for example only when Volumio itself is not playing, and maybe more.

The metadata of Spotify-Web-Connect is:

{ "album_name": "Brothers In Arms (Remastered)", "album_uri": "spotify:album:1NF8WUbdC632SIwixiWrLh", "artist_name": "Dire Straits", "artist_uri": "spotify:artist:0WwSkZ7LtFUFjGjMZBMt6T", "context_uri": "spotify:internal:search:tracklist:bt-4", "cover_uri": "spotify:image:78ace3900b14d836c3d45603872dd63300cb89f5", "data0": "", "duration": 424000, "track_name": "Brothers In Arms", "track_uri": "spotify:track:7EmSPZ9f1AiLR9eVHNSI62", "volume": 59636 }

Logitech Media Server (LMS) supports a little more information (for tracks from the library a little less is available).

WEBRADIO:

{ "method": "slim.request", "id": 1, "params": [ "b8:27:eb:20:27:4b", [ "status", "-", "1000", "tags:adKl" ] ], "result": { "playlist shuffle": 0, "rate": 1, "playlist_tracks": 1, "remoteMeta": { "id": "-124451144", "title": "Under The Bridge S.S.", "artist": "Red Hot Chili Peppers", "duration": "0", "artwork_url": "http://cdn-albums.tunein.com/gn/Z3NGZG36GRg.jpg" }, "sync_slaves": "b8:27:eb:72:2d:69", "sync_master": "b8:27:eb:20:27:4b", "playlist mode": "off", "playlist_timestamp": 1506338088.35735, "power": 1, "time": 126.52452082634, "digital_volume_control": 1, "mixer volume": 35, "playlist_cur_index": "0", "playlist repeat": 0, "playlist_loop": [ { "title": "Under The Bridge S.S.", "artist": "Red Hot Chili Peppers", "duration": "0", "playlist index": 0, "artwork_url": "http://cdn-albums.tunein.com/gn/Z3NGZG36GRg.jpg", "id": "-124451144" } ], "signalstrength": 0, "current_title": "Veronica 90s Hits", "player_name": "max2play", "seq_no": 0, "player_ip": "192.168.178.182:57972", "remote": 1, "mode": "play", "player_connected": 1 } }

LIBRARY:

{ "result": { "can_seek": 1, "mode": "play", "player_connected": 1, "player_name": "max2play", "duration": 420.92, "player_ip": "192.168.178.182:58524", "seq_no": 0, "power": 1, "time": 16.129, "digital_volume_control": 1, "mixer volume": 35, "playlist_cur_index": "8", "playlist repeat": 0, "playlist_loop": [ { "id": 19, "album": "Brothers In Arms", "playlist index": 8, "duration": 420.92, "artist": "Dire Straits", "title": "Brothers In Arms" } ], "signalstrength": 0, "playlist shuffle": 0, "rate": 1, "playlist_tracks": 9, "sync_slaves": "b8:27:eb:72:2d:69", "sync_master": "b8:27:eb:20:27:4b", "waitingToPlay": 1, "playlist mode": "off", "playlist_timestamp": 1506338864.21653 }, "params": [ "b8:27:eb:20:27:4b", [ "status", "-", "1000", "tags:adKl" ] ], "id": 1, "method": "slim.request" }

Yes there is, and we’re doing it in the airplay plugin. What you need to do is to use the volatile state, look at the airplay plugin to see how its implemented :wink:

@ Saiyato
How do you get the current status of Spotify-Connect-Web without polling? I.e how do you get the connect daemon to notify Volumio that it is active?

Cheers! :slight_smile:

At this point that is impossible, I’ve created an issue for this. Also the current position is not availble in the metadata, so that’s the second challenge.

I’ve been reading into the Airplay plugin and I see the buttons are disabled, this is a nice workaround, but even nicer would be if we could map the buttons to the service currently playing (Airplay/Spotify/Mpd/etc). But that’s phase two (or three) anyways. Thanks for the pointer!

Then, I see I can request info when emitting getState, however that state differs from stateService. For example when listening to webradio the stateService pushes this info:

{ "status": "play", "position": 0, "seek": 481, "duration": 0, "samplerate": "44.1 KHz", "bitdepth": "24 bit", "channels": 2, "random": false, "updatedb": false, "repeat": false, "isStreaming": false, "title": "Baldassarre Galuppi (1706-1785) - Sonata per pianoforte in si bemolle Maggiore (08:19) {+info: veniceclassicradio.eu}", "artist": "Venice Classic Radio Italia", "album": null, "uri": "http://144.217.49.251:80/stream1", "trackType": "251:80/stream1" }

Whereas pushState gives you the following:

{ "status": "play", "title": "Federigo Fiorillo (1755-1823) - Sinfonia concertante per due oboi e orchestra in fa Maggiore (18:11) {+info: veniceclassicradio.eu}", "artist": "Venice Classic Radio Italia", "album": null, "albumart": "/albumart?cacheid=0&web=Venice%20Classic%20Radio%20Italia//extralarge", "uri": "http://144.217.49.251:80/stream1", "trackType": "webradio", "seek": 65619, "duration": 0, "samplerate": "", "bitdepth": "", "channels": 2, "random": false, "repeat": false, "repeatSingle": false, "consume": false, "volume": 96, "mute": false, "stream": true, "updatedb": false, "volatile": true, "service": "webradio" }

As you can see pushState has more properties, but not all properties are populated (e.g. bit depth and sample rate). When using for example Airplay I only get the pushState (no stateService is pushed):

{ "status": "play", "title": "Uprising", "artist": "Muse", "album": "The Resistance", "albumart": "/albumart?cacheid=0&web=Muse/The%20Resistance/extralarge&path=&metadata=false", "uri": "", "trackType": "airplay", "seek": 2, "duration": 303, "samplerate": "44.1 KHz", "bitdepth": "16 bit", "channels": 2, "random": false, "repeat": false, "repeatSingle": false, "consume": false, "volume": 96, "mute": false, "stream": false, "updatedb": false, "volatile": true, "service": "airplay" }

Is it possible to propagate the properties from the stateService to the pushState object? Did I stumble upon a limit or maybe bug?

Another thing I just found out, iTunes was showing a sample rate of 48k, but in the Volumio UI 44.1k was shown. Maybe someone already knows the answer, else I will investigate myself. :unamused:

As Spotify have been threatening to shutdown libspotify, librespotify and the SpotifyWebAPI looks the solution of choice for the future.

That being said, for now, I have managed to get a working version of metadata (and device state) for Spotify connect using a modified version of Spotify-Connect-Web.

SpotConnect adds a simple socket, and the plugin subscribes to events. Just like the airplay emulation plugin.
The current implementation is one direction - but I will add some simple playback control in the future. Then we can get rid of the additional webserver. Adding the track position should also be trivial - I have not so elegant solution, but will check if libspotify has something we can use.
I first wanted to get something up and running.

Have also submitted a PR with the relevant additions on the plugin side
It works quite well, except for truncation errors in the time - will fix it later!

I 'm on it! expect a new version of plugin soon!!!
Thanks a lot for your great work! :smiley: :smiley:

Oh wow, this looks very promising! Awesome work indeed! Keep it up :slight_smile:

Truncation seems to be fixed, when playing the song you used I get 4:44 (same as in your printscreen). The meta data is as follows:

{ "volume": 97, "context_uri": "spotify:internal:search:tracklist:let%20-4", "cover_uri": "spotify:image:6fc20dccd0612b85e55ae21b845ed187f3d028c6", "artist_name": "Thievery Corporation", "track_uri": "spotify:track:4pDeigJyLKpkOA14ZllSQP", "artist_uri": "spotify:artist:25KNo5GDS6ZpLkjasaecA3", "track_name": "Let The Chalice Blaze", "albumart": "http://o.scdn.co/640/6fc20dccd0612b85e55ae21b845ed187f3d028c6", "album_uri": "spotify:album:3naxO4ZMrfJqEpEKjkVvlH", "duration": 284026, "album_name": "The Temple of I & I", "data0": "" }

284026 / 1000 % 60 = 44

Excellent job! Loving it!

I’ve been investigating the Airplay ‘seek’ update problem, it seems as if the seek property is not correctly set when syncing the state pushed by the Airplay plugin. It’s something in the statemachine, will keep investigating, but it might take a little while :wink: lots of ifs and elses.

Yep, duration was just the propagation of my bad Javascript skills, one Google, and it’s now fine :wink:

The issue with the seek is that I don’t think there is a direct method to probe the current track position via the libspotify_embedded library.
I have a WIP where I modify the callback, but that only gives the position when manually changed, i.e when you change the position from an external client. This with a timestamp might be the way to go forward.

I haven’t dug into the volumio code, but it would be great if we could update partial state - i.e just the volume. This is throwing me off as, each time I update the volume state, of course the seek returns back to 0. The UI volume dial works fine to actually change the value, just doesn’t update when a spotify client changes the volume.

BUT, to be frank - the point of Spotify Connect is to control the device from elsewhere, if we really want so much control from the Volumio interface, we should be using the Spotify plugin :smiley:

Did you figure out how to push only certain aspects of the state to Saiyato?
I have the issue of pushing a volume change to UI, which results in resetting the seek timer. One workaround is to start a timer as well to track the track position, and then use its value when pushing a new state?