Closed ashthespy closed 2 years ago
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 :(
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! :-)
@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
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 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? ;-)
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?
@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.
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).
damn, i see. I was already wondering why mpDris2 emphasizes that it's running in the user session.
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:
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
What method are you trying through which telnet interface?
I tried using Plugin.Stream.Player.GetProperties
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.
Oh, I understand, then where do I need to execute the stream plugin methods in order for it to recognize them?
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.
I noticed that there is some rudimentary "metadata" implemented already for the
librespot
by reading off the stderr output. https://github.com/badaix/snapcast/blob/1f98c790734f7b5de01497fa5243149dddd6dc0b/server/streamreader/librespot_stream.cpp#L138-L145I 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 thelibrespot_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 mylibrespot
based daemon to output to this preferred stream as well, and then configure the server to read the audio stream + metadata stream.