librespot-org / librespot

Open Source Spotify client library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Metadata

frafall opened this issue Β· comments

Damn nice initiative πŸ‘

One area we would need to look at is metadata, a lot of forks is due to the need to integrate this and we should put down some standard way to do it.

This includes two parts, what tags to use (ex Vorbis) and how to deliver it to the rest of the world as in encoding (json, xml, ...) and transport (stderr/stdout, pipe, tcp, ucp...)

This could/would also imply changes to the audio adapter API, ex to have metadata available for pulseaudio properties.

commented

Hi @frafall I agree, I'm doing some research on it now.

This is a good read as you know.

More for reference this is the web API track object. I feel this may be easier to use if there is an easy way to authenticate with the API (So far I've found it a pain)

As in the above issue I would also say that writing to file may be the best way as it is somewhat easy to get information from. Of course other ways could be added but I would say that write to file should be the first focus.

Some issues with file writes, u never know if the data is current or some leftover from earlier runs, it's localized inside a docker image or VM (unless exported explicitly) and pushing new data is a hassle as u either have to add new data at end or re-write file and leave it to the client to poll this. I like push options.

My ideal solution would be opening a socket to libreelec where it pushed data, kinda similar to listening to stderr or a pipe if you have control of the process. I kinda dislike pipes due to the locality (single machine) and single reader things.

But, for now I've been happy reading the stdout output, rather that than a file.

commented

I see where you're coming from with writing to file, however its sometimes the only way to feed from other programs the information. Usually by polling said file, containing only now playing metadata. (I don't like it either).

I think like you some sort of push service were other devices listen would be best, but I feel that should be as a secondary objective.

Also not everyone will want metadata so I think it should be an option, views?

I think stout should be a priority as it would be one of the easiest get a proof as concept.

stdout support would provide file writing support "for free" because of the lovely concept of pipes (which work on both UNIX-likes and Windows cmd, AFAIK). I'd advocate implementing this for stdout first, making sure to move any current stdout prints during normal operation into stderr so the stream is free of non-metadata noise.

This feature would enable me to use librespot where I can't use it currently.

I did implement a (dodgy) websockets server a little while back in an earlier version of librespot that pumped out metadata to clients on various changes, though it wasn't merged as Paul wanted to keep librespot relatively minimal. With regards to metadata format, do we think JSON would be suitable? I only suggest that so that those who choose to interact with the Web API as well don't have to think about multiple data formats.

Also, I agree that the metadata output should be optional, some people just dont need it if they integrate with librespot directly. Perhaps a compile time flag if people want metadata output?

I would prefer a meta-data pipe. That would be compatible with forked-daapd.

Well, maybe it's just because I'm curmudgeonly, but I'd prefer not to have to write any code in Rust. What I'd like is for the example Spotify Connect daemon that comes with librespot to offer a metadata to stdout flag. You could implement similar backends for other protocols too, even HTTP. With the new librespot-org we don't have to worry about if Paul wants to keep the thing minimal if the current maintainers don't feel that way ;)

Since librespot has PulseAudio output, you can kinda see where I'm going with my design if you look at what Tribblify can do... Keeping in mind that all sinks have a source named sinkname.monitor in PulseAudio... ;)

As far as data format, any of the following would work for my purposes:

  • JSON
  • XML
  • CSV
  • URL encoded param string (like artist=foo&song=Benny & the Jets)

What probably wouldn't work is having the format be Artist - Song Title, because then how would you parse a string like: Andrea Bocelli - Live - Phantom of the Opera - Venice? Where is the delimiter between artist and song title? I have this current limitation with my parsing of the Spotify window title, so switching to librespot would be a marked improvement there. Plus it'd be headless.

I get where you're coming from, I'm not a huge fan of rust either. However, in the interests of maintainability, I do think he was right not to allow things like a websockets server to be baked in to librespot with no way to turn it off. However, compile-time feature flags are our friend here, to a certain extent, in that any pipe/websocket/etc. solution can be selectively enabled at compile time. I.e. those who don't want a bigger executable can just build without the features they don't need. I do agree with you though, in that whilst librespot is technically a library, I get the feeling that most people are using it as a daemon/standalone program, thus not having metadata easily available in daemon mode is a pain.

Perhaps a separate GH project entirely for the daemon that's actually featureful? That would be amazing! :) It could go under librespot-org, too...

I think that was Paul's intention, that all the websocket server, piped metadata, etc. go in a separate project, and librespot is simply for core functionality.

The only reason I get a little nervous about compile-time flags is that, if actually compiling the thing is a nightmare (which could happen if we keep super up to date with the latest Rust stuff and you're running, say, Ubuntu 16.04 with 2 year old Rust), and binaries happen to be conveniently available from distro packages or a PPA, then we'd want to make sure that distros ship with all possible options enabled at compile-time, and just have them be runtime flags.

I agree with you that having a web server enabled by default is way overkill, but I think all the possible features of the daemon should be there in a default compile, and encourage anyone packaging binaries, whether it be Ubuntu or a PPA maintainer or some other distro, to enable all options. Everything that's a compile-time flag should also be disabled by default in the daemon if you just run it with only the minimum required arguments to establish a Spotify connection; to get extra features like metadata, it's fine to expect the user to have to pass a command-line argument.

I don't think code size is an issue even on a Raspberry Pi, because the storage capacity of MicroSD cards is ridiculous these days. Compiling in Rust code for a web server may add a megabyte to the code; it may add five megabytes... I can't bring myself to care at all on any platforms that would have the cycles to run librespot in the first place.

Fair enough. To be honest, compiling librespot is really not very difficult. One simply installs rustup, installs the latest rust version, makes sure portaudio is installed and runs cargo build. I would rather keep librespot fairly vanilla, and have the various features such as dns-sd over mdns, etc. that frankly most users are not going to care about as compile time flags, then have a separate project as you suggested which is the bells and whistles daemon. That way, the daemon repo can handle which features it wants when compiling librespot, and the daemon/standalone binary can have all of the metadata/webserver/etc stuff built in and toggleable by runtime flags. The daemon/standalone project could then be binary that gets distributed via PPAs and such, whilst librespot is kept thin for people using it as a library.

That way, the daemon repo can handle which features it wants when compiling librespot, and the daemon/standalone binary can have all of the metadata/webserver/etc stuff built in and toggleable by runtime flags. The daemon/standalone project could then be binary that gets distributed via PPAs and such, whilst librespot is kept thin for people using it as a library.

Yeah, that sounds like a great plan.

commented

I like this a lot, so just to check?

librespot librespotd?
Releases No releases, the master is just the latest Have a steady release cycle (monthly?)
Compiling Barebones (if we release binaries at all) All options on by default at compile time
Locations Only on github Create a ppa in addition?

Yeah, that sounds about right.

I would ask to consider dropping the "lib" on "librespotd" to avoid confusion. "lib" always implies some kind of non-runnable component that software has to consume via code; the intent of "librespotd" is the opposite.

So, maybe respotd?

Or maybe not; now that I think about it, there's precedent for leaving the "lib" in there...

As far as package names, on Debian/Ubuntu at least, there's a reasonable precedent for having packages associated with the same project but with executables in a -bin package. So:

  • librespot-dev would provide the Rust libraries for building your own programs
  • librespot-bin would provide the daemon.

We could follow the model of libtool (libtool-bin) and libvirt (libvirt-bin) there. libtool keeps the lib on its main binary program; libvirt has libvirtd and virsh.

...unless its name is not lib-respot, but libre-spot ;-)

As for piping out metadata to stdout: this would become difficult if librespot was set up to pipe the audio data to stdout. Which is part of the options now, and which I do indeed use in my case.

I really like the organization with separate 'base' library and a bells-and-whistles daemon, keeps the separation of duties clean, this also enables writing the daemon in other languages against a stable API.

As for the piping, separating stdout for audio data and stderr for metadata is an option, easy to handle in both controlling daemons and scripts.

My main issue with a lot of media players is that they tend to add on metadata as an afterthought instead of offering it in the audio channel implementation, ex. Pulseaudio output implementations often do not set the metadata properties even though is is somewhat defined, even album art can be pushed there as a binary blob or url (although not well defined).

So, for pulseaudio set the properties, for process control use stdout/stderr, for alsa we probably need to put it somewhere but there the picture gets messy. (at least in my head)

As for presenting album art an image web service is probably a nice way to go, a controlling daemon could subscribe to the service and push it wherever we (like a Kodi addon). But, for the Linux desktop, playing librespot on a pulseaudio interface could offer metadata like the Mopidy player does which would display but, again, the album art part is not well defined.

My input for goals on metadata are:

  • Integrate into Linux desktop environment, ie something like Mopidy/Pulseaudio (dbus can of worms)
  • Enable integration to media centers or multi room solutions, ie stream metadata to controlling daemon
  • Offer 'the rest' some consistent way to get hold of the metadata, probably audio output adapter dependent, ie push metadata to the audio output adapter in daemon.

I.E. Command line flags select audio output devices with metadata. Then an option to start the web service.

So, for me this leaves the question, should we modify the audio output adapter interface in the base library? Open it for the bells-and-whistles daemon to extend it with metadata or move the functionality there?

Nice work setting up the org πŸ‘

Almost forgot, json, encoding (album art), tag standards (Vorbis?)... probably more to think about here.
But, standardize on something open and language/protocol independent...

Ops, almost forgot more, as we can not possibly push everything Spotify provides or any1 could possibly want we should add a tag for song Spotify reference, enables lookup for further info. Also, if librespot http service is enabled a reference to this.

commented

Just so as to not clutter this issue continued discussion of librespotd (or whatever we call it) should continue here: #20

For reference, #25 is semi-related.

Search functionality: #23

For anyone who is interested, I added the ability to bypass the internal soft volume control and export the volume via a metadata pipe which is compatible with forked-daapd.

billsq@74e3209

Use arguments --linear-volume --mixer pipe --metadata-pipe </srv/music/librespot.metadata> to properly send the volume.

I am new to Rust... It would be appreciated if someone could help to review the code.

Full forked-daapd metadata pipe support added:

billsq@77c644d

Any chance this gets merged?

If he opens a PR and then once it's reviewed, sure.

#214 PR created.

This is brilliant. I'll give your repo a clone while we wait for the daemon. Thanks @billsq !

So I cloned the rep to get this fix. Compiling was successful and the client runs.
The pipe.metadata file has the same name as the pipe and is in the same dir. It is readable and writable.
However forked-daapd is not picking up any metadata. It plays the source fine, but volume changes do not work.
No other metadata about the sourve is displayed.

@chimpy Does your pipe.metadata have the same permission as your pipe file? Did you run librespot with the arguments I mentioned above? Did you restart your forked-daapd?

Hi @billsq ! Thanks for the reply. Details below:

chimpy@hassbox:/opt/music$ ls -lh
total 4.0K
prw-r--r-- 1 root root 0 Aug 1 21:24 spotify
-rw-r--r-- 1 root root 135 Aug 1 21:24 spotify.metadata

librespot -n "House Speakers" -b 320 -c /var/cache/spotify --initial-volume 30 --device-type avr -u chimpy -p "redacted" --backend pipe --device /opt/music/spotify --linear-volume --mixer pipe --metadata-pipe /opt/music/spotify.metadata

I have restarted forked-daapd before using your fork, no change. If I do a tail -f on the metadata file when adjusting volume, playing and changing a song I see it updating, but no change on forked-daapd's side.

Ah, this gets mentioned in forked-daapd's log:
forked-daapd[19611]: [ LOG] player: Source type is pipe, but path is not a fifo: /opt/music/spotify.metadata

OK, so I realised that maybe spotify.metadata should be a pipe as well. I did a mkfifo /opt/music/spotify.metadata and restarted forked-daapd but now librespot authenticates and then hangs. It does not appear in the client list.

Try restarting librespot. spotify.metadata
should be a pipe.

Hey bill, not sure if you saw my edited comment, but I did that. I did mkfifo spotify.metadata. When I restart librespot it hangs after authenticating my user but before showing the country code.

It happens to me with stock librespot sometimes. Just try to restart it. I don’t think it has anything to do with the pipe code.

@billsq Everything still working for you? The problem above has back for me with a vengeance. After a about 40 start/stops, it's still hanging before getting the country code.

It is working as usual.

Has this been merged? Using --mixer pipe --metadata-pipe /srv/music/spotify.metadata gives me error: Unrecognized option: 'metadata-pipe' on what I built from source yesterday

@chimpy Finally found the root cause or your issue. It is due to forked-daapd doesn't open metadata pipe by default. Now should be working fine.