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

Adding Metadata -- prefered method? #803

Closed
ashthespy opened this issue Feb 15, 2021 · 16 comments
Closed

Adding Metadata -- prefered method? #803

ashthespy opened this issue Feb 15, 2021 · 16 comments

Comments

@ashthespy
Copy link

I noticed that there is some rudimentary "metadata" implemented already for the librespot by reading off the stderr output.

// Librespot patch:
// info!("metadata:{{\"ARTIST\":\"{}\",\"TITLE\":\"{}\"}}", artist.name, track.name);
// non patched:
// info!("Track \"{}\" loaded", track.name);
// If we detect a patched libreelec we don't want to bother with this anymoer
// to avoid duplicate metadata pushes
if (!libreelec_patched)

I have wrapped librespot to create a pipe with proper metadata and would like to incorporate some metadata support into snapcast for Spotify.

From a quick look at the codebase, this can be done similar to how the shairport-sync metadata stream is read, but then would require patching the librespot_stream.cpp with yet another "custom" implementation.

As I am not very well versed with this codebase, I would like to ask if is there already a stream agnostic way to add metadata to a particular stream?
I could then plug my librespot based daemon to output to this preferred stream as well, and then configure the server to read the audio stream + metadata stream.

@badaix
Copy link
Owner

badaix commented Feb 15, 2021

There is no generic way to add meta tags. I've started with it in the metatags branch, but focused on reading meta data from MPD and cover art from musicbrainz, not yet on the framework part.
I'm somehow bombed by support and feature requests, eating up my rare spare time that I can invest into Snapcast, no time for the fun stuff :(

@ashthespy
Copy link
Author

Ah, maybe I could pick your brain about how you see metadata support evolving?
I am not sure you'd be happy to add yet another stream reader for Spotify again, so was wondering if there way another way.
For example, I currently expose metadata over a udp socket in JSON format.

let json = json!(
    { "metadata" : {
        "track_id": spotify_id.to_base62(),
        "track_name": track.name,
        "artist_id": artist_ids,
        "artist_name": artist_names,
        "album_id": album.id.to_base62(),
        "album_name": album.name,
        "duration_ms": track.duration,
        "albumartId": covers,
        "position_ms": position_ms.unwrap_or(0),
    }});            

I'm somehow bombed by support and feature requests, eating up my rare spare time that I can invest into Snapcast, no time for the fun stuff :(

Take care! :-)

@Sirnut
Copy link

Sirnut commented Mar 4, 2021

@ashthespy Could I get some more details on how you're sharing metadata between your server and clients? I'm trying to brainstorm a way to get the data out to multiple volumio clients. What I envision is probably a dirty way to go about it, but since I only use spotify connect to play music back I want to package the data on the server where the spotify connect plugin is located, send it to the clients (who's only installed plugin is a snapclient) to have it then broadcast to the UI

@badaix
Copy link
Owner

badaix commented Mar 10, 2021

I think the preferred method for adding metadata in case of Librespot is to have it integrated into the librespot stream source, but without any if (libreelec_patched). Instead the official metadata should be used, which currently not really available, only in the debug logs and when using the --onevent script, which lacks important information, such as artist and album and this script method is also not very handy to integrate.
If you could get your librespot changes upstream, than this could be officially integrated into Snapcast.

@ashthespy
Copy link
Author

ashthespy commented Mar 10, 2021

@ashthespy Could I get some more details on how you're sharing metadata between your server and clients?

I currently have metadata only within Volumio's ecosystem, but not on other clients, which is why this issue ;-)

Instead the official metadata should be used, which currently not really available, only in the debug logs and when using the --onevent script, which lacks important information, such as artist and album and this script method is also not very handy to integrate.

Ah, I was hoping that you had some plans for a stream agnostic way of incorporating metadata, instead of a custom metadata parser for each stream.

W.t.r to libresepot we keep having discussions about metadata, but ultimately we arrive at the concussion that librespot is a library, and the current librespot binary should get moved into librespotd that handles metadata in a plugable manner similar to how backends are currently implemented.

But this still leaves a few thoughts from my side on the preferred method to get metadata in

-- We could patch it to add more information in a parseable manner to stdout, (Additional metadata is currently lacking as this is done from the player thread that is nonblocking, but this is an implementation detail that isn't of much interest to you ;-))
-- Would it work to have a separate pipe for just metadata?
-- Is there a standard metadata from that is preferred? ;-)

@kingosticks
Copy link
Contributor

Can we use Snapsever's Stream.SetMeta JSON-RPC method for setting the metadata? In the case of Mopidy we could easily have an extension to do that. Or some external glue script/program doing it using MPD, which has the bonus of providing support for both Mopidy and mpd streams.The metatags branch seemed to be going in the other direction and implementing a "custom metadata parser for each stream" approach, is that preferable?

Relatedly, I noticed Stream.OnMetadata and Stream.SetMeta are missing from the RPC API doc. Would a PR to add those be helpful?

@badaix
Copy link
Owner

badaix commented May 11, 2021

@kingosticks the meta branch is kind of stalled. The idea was to have a toolbox of some generic meta-readers that can be assigned to a streaming source. This is the "If the mountain will not come to Mohammed, Mohammed will go to the mountain" approach.
What I'm currently thinking about is MPRIS integration. Mopidy has an extension, for MPD there is a wrapper mpDris2 available and a lot of media players have built-in support for MPRIS (e.g. the official Spotify client, don't know about librespot).
The Snapserver could act as an MPRIS proxy and forward the current stream's MPRIS metainformation to the Snapclient, while the Snapclient itself could support MPRIS to display cover art, to control the volume and could even tunnel back control commands (like play, pause, prev, next) to the player.

@kingosticks
Copy link
Contributor

I've considered using mpris for PiMuiscbox but I concluded it wasn't worth the dbus faff. The main issue we have with Mopidy-Mpris is that, because it's dbus, for everything to see each other they all need to be on the same bus. Mopidy is flexible and can join either system or user bus but other programs (such a gnome tray applet etc) are not and will only join the user bus which means being then forced to run snapcast as a user service also (sort of related to the pulseaudio quirks). Not the end of the world but its not quite as good as it may first appear. Having said that, the Just Boom player software uses mpris for integrating everything and they've written simple mpris (system bus) wrappers around things that don't speak mpris themselves (like librespot).

@badaix
Copy link
Owner

badaix commented May 11, 2021

damn, i see. I was already wondering why mpDris2 emphasizes that it's running in the user session.

@badaix
Copy link
Owner

badaix commented Jun 17, 2021

Update on this: I've started to add generic meta data and stream control support. There is an early version of the documentation in the develop branch

In a nutshell:
A stream can be configure to start a control script, using the new parameter controlscript:

source = pipe:///tmp/snapfifo?name=Pipe&codec=flac&controlscript=/home/johannes/Develop/snapcast/control/meta_mpd.py -d

The server will send JSON RPC commands to the script's stdin and receive responses and notification from it's stdout.
In future a plugin might also be a shared library, configured with controlplugin or something.

Commands are for instance play, pause, stop, getMetadata, setProperty [shuffle, loop, ..] and notifications are used to push metadata and property changes to the Snapserver.
The interface is heavily inspired by MPRIS, which is serving exactly the same purpose.

Metadata, properties and control commands are exposed to Snapserver's RPC interface, so that clients can get the metadata and control specific streams via the Snapserver's websocket interface.

This is still work in progress and changes almost every day, but the interface is starting to get "stable". As proof of concept I'm developing a control script for MPD, based on mpDris2 and a Snapcast to MPRIS script for the desktop as well as on an integration into Snapweb, maybe also in Snapdroid.

As a teaser: this is how it looks like at the moment:

image

@MattiaBralla
Copy link

Hello, I have tried to download the develop version of snapserver in order to setup the stream plugin, I installed the dependencies (python-mpd2 and musicbrainzngs) and specified the controlscript in my source.

The problem is that when i try to test a method using telnet it returns "Method not found", code -32601. The JSON RPC API works fine tho.

Do you have any ideas on what could be my issue here?
Thanks in advance

@badaix
Copy link
Owner

badaix commented Oct 18, 2021

What method are you trying through which telnet interface?

@MattiaBralla
Copy link

I tried using Plugin.Stream.Player.GetProperties

@badaix
Copy link
Owner

badaix commented Oct 18, 2021

This API is exposed by the Metadata plugin, not by the Snapserver, i.e. meta_mpd.py.
Snapserver polls the metadata using this API from the plugin.

@MattiaBralla
Copy link

Oh, I understand, then where do I need to execute the stream plugin methods in order for it to recognize them?

@badaix
Copy link
Owner

badaix commented Oct 21, 2021

I've updated the documentation (which if far from being finished yet). To summarize:
The Control (Telnet) interface is not allowed change metadata for a stream, but is allowed to set certain Properties, i.e. "loopstatus, shuffle, volume, rate". Those properties are forwarded to the stream through the Stream plugin's Plugin.Stream.Player.SetProperty, and the stream plugin is responsible to apply these properties to the actual source.
Also the control API can issue control commands (Play, pause, stop, next, ...), which are again forwarded to the stream plugin Plugin.Stream.Player.Control.
So the Snapserver is acting here as kind of a proxy with syntax checking and fires some notifications to other control clients about changes.
The server will send Stream.OnProperties notification to the connected control clients, whenever properties or meta data of a stream changes. There is no interface yet to explicitly query stream properties (metadata are part of the properties). You can get the full picture with Server.GetStatus.
For an example control client that makes use of the properties, you can check the metadata branch of snapweb.

@badaix badaix closed this as completed Dec 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants