mopidy / mopidy-spotify

Mopidy extension for playing music from Spotify

Home Page:https://mopidy.com/ext/spotify/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Replace libspotify / Streaming does not work - "Spotify login error: <ErrorType.USER_NEEDS_PREMIUM: 15>"

handle2001 opened this issue · comments

According to the Spotify Developer website, libspotify is no longer available for download, so the mopidy-spotify plugin cannot be installed anymore. Is there an alternate source for this library?

This is new to me (from https://developer.spotify.com/technologies/libspotify/#libspotify-and-cocoalibspotify-downloads):

Please note that we have removed the LibSpotify binaries from our website in an effort to phase out the usage of this deprecated library. LibSpotify has been considered deprecated since 2015 and will be shut down in 2017, so we want to ensure that all developers’ efforts and attention are focused on newer and better APIs that we actively support and maintain. These APIs include the Web API, iOS and Android SDKs, and upcoming embedded libraries for Windows, Mac OS X and Linux. More information regarding upcoming libraries will be provided in the coming months.

That sort of necessitates that we either distribute libspotify or rework mopidy to use the Web API's.

Oh dear. How did they forget to email us about this through their developer list AGAIN?!

There is no way to play music with the Web APIs (unless this has also changed without announcement). So distributing libspotify is probably the only option.

I think it will be necessary to rewrite mopidy-spotify using one of their new APIs or that "embedded" thing they mentioned. Libspotify will be forcibly prevented from accessing Spotify by some time in 2017, per Spotify themselves, so we can't count on its continued functioning (unless some enterprising hacker hacks the binaries somehow to trick Spotify's servers into letting us continue to use it, but don't count on it).

I am hopeful that it doesn't require some kind of protected audio path. It can't really, if it's on Linux, right? So hopefully there will be a way to get at the PCM data from the tracks. That's all we really need to be able to move forward.

...

If the web API doesn't work and the embedded clients aren't accessible to us, can we run an Android emulator on the user's system and run the Android Spotify API through that? :P just a thought...

"LibSpotify and CocoaLibSpotify are no longer under active development on any platform and are considered deprecated. If you are building applications for iOS or Android, please use the iOS SDK or Android SDK, and we will be providing a new library for other platforms later this year" from the original spotify developer Website.

So I think we have to wait now. But its clear that mopidy-spotify has to be overwritten.

@yannik-b, that particular note has been on their website for literally years, including the promise of a new library.

mopidy-spotify uses libspotify through pyspotify which might protect us from some of the changes. Lord knows what Spotify will actually end up doing and when they will actually do it. They have a history of this and unfortunately it's as unclear as it's always been.

@kingosticks Oh okay, I never looked for it.

Actually mopidy-spotify is working.

Is it possible to distribute libspotify in tar.gz format alongside mopidy without violating any licenses?

i think the problem is that the library will not work anymore if spotify stops the development.
are there any alternatives announces by spotify? i am really interested in this Extension and Keeping it up.

commented

I'm also keeping this alive. I guess we're in the hands of spotify at the moment, waiting for them to release a replacement library? Or is there work on the "embedded" track going on?

I imagine you are already aware of the it, but just in case, librespot is an effort to fix the libspotify deprecation issue, from what I understand:
https://github.com/plietar/librespot

Yes we know about it, but it won't help much if Spotify decides to turn down those endpoints. I looked at it bit when they started out trying to get Spotify Connect working, but I've not payed much attention since it didn't look like we could easily integrate it at the time.

Hi, just a quick update from the librespot repo, I've had a look at libspotify in a disassembler, and there are a couple of flags in there such as is_depreciated that are set to false. My guess is that Spotify will at some point set these to true and libspotify will die. Librespot is written in rust, and hence isn't the easiest to integrate with mopidy. To this end, spotifyd has been rewritten to use librespot, and spotify-http is a WIP looking to provide a web server to interact with librespot. Librespot has also had alsa, portaudio and stdout audio backends added. Thus it should be viable in the near future to use it with mopidy without too much effort. Alternatively, spotify-connect-web uses the embedded library that spotify references, which includes connect functionality, though there is no documentation for it, hence you are on your own with that.

Are you saying that librespot supports playback through an endpoint other than the one libspotify uses? i.e similar to however the Android/iOS SDKs work? Playback is the only part of the API that is not available through Spotify's Web API and the only thing we actually need going forward.

librespot simply connects to one of the spotify endpoints from https://apresolve.spotify.com/ and interacts with spotify using the Spotify protocol, the same way libspotify does. And they are not going to kill the endpoints that libspotify uses, I can almost guarantee it, as all of the OEM Spotify Connect speakers use a variant of libspotify called libspotify_embedded that contains the connect functionality. The amount of work they have put into the Spotify Protocol in terms of design/implementation means they are not going to scrap it, especially given this is how the Desktop clients interact with Spotify servers. I can almost guarantee that the shiny new embedded library that Spotify is talking about will be a repackage of the library that they have shipped to speaker manufacturers for the past few years.

If we assume they do not turn off the endpoints, then what advantage does something based on librespot have over just using the current version of libspotify that we already have?

Considering Spotify's complete failure to deliver a new SDK I don't think we can assume anything about what it will be (or if it will ever turn up).

Do you know if the Android/iOS SDKs use the same endpoints as libspotify?

At the moment there is no advantage, though as I mentioned previously, libspotify has an is_depreciated flag which could be flipped by spotify whenever. Also, librespot doesn't require an appkey. Though I am doubtful as to whether Spotify will kill libspotify this year, their devs seem to be the laziest bunch I've ever come across.

I get it Spotify, you don't want pirating being made too easy. But come on... There's so much potential for this. You doomed/broke hundreds of apps at the same time. :(

I suspect it is actually more of a licensing issue. libspotify was a free library that didn't require any contract with Spotify, whilst libspotify_embedded (with the Connect functionality) requires you to become a Spotify Partner and sign a contract/NDA before use. I would imagine there are licensing fees involved as well...

Also @kingosticks this is what I was talking about in February :/

There's a short discussion and nice example of how librespot can be used as a library here. Something like that would allow us to also drop the playback side of libspotify but keep our existing architecture.

But for the time being, libspotify should still be available through Mopidy's APT archive.

Does librespot access Spotify using a mechanism that won't break when they turn off libspotify? What mechanism is it exactly? That could be the savior of all FOSS Spotify clients needing direct audio access, if it works... and provided Spotify doesn't start suing people for using it :/

I think @sashahilton00 already covered that in #110 (comment)

@allquixotic librespot accesses Spotify using the same method that their desktop/mobile clients do. There is little/no risk that librespot will fail due to endpoints being closed. Occasionally librespot breaks when Spotify introduce new protobuf specifications that break previous implementations, but these updates are few and far between, and are both easy and quick to fix. librespot is pretty stable at the moment, though I'd suggest testing before using it in production obviously...

With regards to suing, pretty sure what librespot is doing falls within the DMCA Exceptions. We're pretty explicit on our policy of not supporting circumvention of Spotify's various protections, we're only after interoperability...

That would be more than great. I simply don't want to use libspotify because if it takes three months to develop something which will be scrapped this year it's simply not worth the effort.
How did they find this? Librespot doesn't seem to be the most stable solution either - but a great start...!

@dbischof90 librespot is pretty stable, and has been around for a couple of years now. I suspect the unstable impression is mainly because the Gitter chat room is used as a means for reporting exceptions. Occasionally there will be a song that won't load, in which case it will be skipped (this is rare) or librespot may crash if one is using some of the experimental features such as multi-account streaming. For the most part, it should be stable enough, and is probably the long term solution for Spotify streaming functionality as it stands.

The biggest barrier to adoption in my view is that it is written in rust. Whilst rust is a pretty good language, there are very few (relatively speaking) who are proficient in it. What we really need is for the protocol to be documented so that more accessible implementations can be developed. Paul is busy until July, hence the slow pace at the moment, though development should pick up pace again over summer.

Writing a wrapper for librespot that exposes audio and metadata through an HTTP API or similar is on my to-do list, it's just not very high on it right now.

Depending on the complexity of librespot, might it also be possible to maintain ports in other languages, such as Python (:)) or C++ or Java? One benefit of going C++ is that it's literally portable everywhere - there are precious few programming languages that can't invoke a native library, and we can also write the public interface in a "pure C" dialect so it can be called from C, JNA, etc. without using C++ mangling semantics.

We could use upstream librespot as our authoritative source of the specifications of the wire protocol that we need to support, and backport fixes there to other language ports, unless/until a port of librespot to another language has a more active maintainer or manages to write a library with substantially fewer bugs/limitations.

If that sounds awfully complex, well, it wouldn't be the first time. Look at all the bindings the Selenium project has. And all the ones listed on that page have extremely close feature parity with the "main" implementation of Selenium written in Java.

... I'm also equally interested in a REST or SOAP based API for librespot, but that basically means dragging along the Rust runtime to the system where this needs to live, and spawning a separate process for librespot, then doing cross-process IPC. While this may not seem like a big deal, managing an external process makes it harder to do logging and debugging, state management, and creates bloat that can be an issue if you're on an embedded system with limited persistent storage and RAM.

Since people are using mopidy on Raspberry Pis and stuff, I'm not sure if all that extra overhead would make a difference. Probably not on the storage side, since MicroSD cards can have many gigs of storage, but on the RAM/CPU side, ... maybe.

From my point of view having just a bare bones library for streaming would be enough. The metadata should be doable via the web APIs.

Does librespot support the same authentication method as the web API? Or would these be just as inconvenient to use together as libspotify and the web API is?

If we can use the same authentication method, I agree with @adamcik.

@adamcik soon Spotify is going to require all calls to the web API's to be authenticated. If one pulls the metadata from the Spotify API, authentication is not required.

@trygveaa no, it doesn't. I don't know the specifics, but it uses a different auth method to the web APIS.

I'm also equally interested in a REST or SOAP based API for librespot, but that basically means dragging along the Rust runtime to the system where this needs to live, and spawning a separate process for librespot, then doing cross-process IPC. While this may not seem like a big deal, managing an external process makes it harder to do logging and debugging, state management, and creates bloat that can be an issue if you're on an embedded system with limited persistent storage and RAM.

@allquixotic You're preaching to the choir here. Whilst I think rust is the theoretical language of choice for librespot, the entry level to getting involved is pretty high. Librespot will always need a separate process as Spotify connect is something that is initiated by Spotify servers so to speak, hence librespot is always listening for events. The memory usage of librespot is pretty damn efficient as well; writing it in another language will almost certainly use more memory. Though as stated before, I think it's a worthwhile tradeoff in exchange for being a more accessible FOSS project. Still, at this point in time, beggars can't be choosers...

So for example for a Python library it would be the sanest thing for now to "simply write a wrapper"?

This one seems to be maintained: https://github.com/plamere/spotipy

No, there is no streaming music support through the Web API.

It looks like there's now some web streaming API, but it requires EME https://beta.developer.spotify.com/documentation/web-playback-sdk/

Sadly it also seems to be in browser only, so not exactly something you could embed in mopidy and combine with GStreamer :(

Yeah, I suppose you can't touch the audio packets, they go straight through EME to browser audio output

I've tried twice to ask if this is supposed to the libspotify replacement but so far no response. In sort of related news, they broke libspotify search again.

They say this is not libspotify replacement, sorry for drama https://twitter.com/SpotifyPlatform/status/949225448336248832

I got a response:

We still know there's demand for playback in other situations including native/embedded devices, we promise we haven't forgotten! No announcements to make right now though.

EDIT: I see you got the same response. I can't see them doing it personally.

Holy crap, this will teach me for not using mailing lists and all that, I've only just seen this. I guess my latest batch of work on Blindspot will be put on indefinite hold.

This has literally just ruined my day

@craigbrett17 you may be able to use https://github.com/librespot-org/librespot in it's place. It's not perfect, but it's fairly stable.

@sashahilton00: Thanks for the suggestion. Rust interop with C# doesn't sound like anything too promising. It MAY work, I don't know. That and building Rust through Windows isn't even a scenario they cover. I could use a Unix subsystem to do it but then using that built DLL on windows... I'm just not confident it will work here. I'll try that before properly giving up. That being said, their disclaimer also puts me off. I'm not trying to do anything inflamatory, not now at least, I'm just trying to get access to the content I've paid for in a way that's accessible. I'd rather not get sued for quadrillions of dollars in some shadey court in California if I can help it :(

@craigbrett17 rust on windows isn't a problem, the project can be compiled on windows. With regards to the disclaimer, it's more there as a 'don't be stupid' policy; we've been in contact with Spotify about it before, and whilst they aren't endorsing it, they've let it exist as an OSS solution for a few years now.

Hi y'all, Sorry if I am off topic... was testing out Tizonia that uses libspotify and just found out it doesn't work anymore. I am building a new wearable music gadget and we're in the midst of deciding on the operating system. The gadget is supposed to play Spotify Premium offline... I am under the impression that I must run Android (or perhaps Linux) to get it working vis the current SDKs. Am I right here? Thanks

Android is your only option and there's plenty of info on the Spotify developer website. If you have further questions please ask Spotify, not here. Good luck.

So i'm guessing mopidy-spotify no longer works because of libspotify deprecation.

Sucks, guess we just wait to see and hope that spotify will release something in the future which will let it work as it once did?

Mopidy-spotify should work still if you have a copy of the library for your platform, unless something has very recently changed.

If they killed it from the server side, you might want to check out my project “tribblify” and use a “real” Spotify client. Unfortunately this requires an x86 processor to work and won’t work with ARM because I don’t think they have a full Spotify client that works on ARM.

I still need to document/fix Tribblify a bit but it should basically work. Let me know if you have problems.

Is there some fast way to use librespot instead of libspotify when building this lib?

No. There is not.

So, what is the current reason for not switching over to Librespot? It being FOSS should be good enough in itself to switch over from libspotify.

I was working on an Alpine Linux container for Mopidy, and wanted to include mopidy-spotify in it but found out that this isn't possible due to libspotify (Alpine Linux uses musl rather than glibc), so I praise any effort to get rid of it.

"lib"respot is more like "respotd". It spawns an external process that plays the music on the local system, and they (intentionally) don't make it very easy for you to get at the PCM data from the stream. The PCM data is what we'd need to be able to pipe into gstreamer to use the rest of the mopidy code. Without it, we'd basically be completely bypassing all the customizability and configurability of mopidy, and things like shoutcast/icecast streaming, inserting custom elements into the filter stage of the pipeline, etc. would be impossible.

Things may have changed, but last I checked, the librespot devs were afraid of exposing raw PCM extraction via API from their code because it would be more likely to raise Spotify's ire and possibly lead to it being taken down. Code is no longer free speech in the eyes of governments and websites such as Spotify and Microsoft (nee Github).

Aside from that, librespot is written in Rust, so we'd need some kind of a FFI or wrapper to call from Python to Rust even if it had the capability we need.

Thanks for this answer, it explains the situation much better than what info I had so far.
Trying to stay optimistic: is there any possibility of working with the librespot folks to get the needed functionality / code into mopidy?

You can get PCM from librespot, we provide it via a FIFO pipe. What we don't expose is the means to just request files and corresponding decryption keys to get raw PCM in a way that would be much faster than listening to each song. The aim of this is to prevent it being turned into a spotify ripper.

Having worked a bit on both projects I see the state of affairs as follows:

  • Forget about getting anything from Spotify. Their total lack of action demonstrates they have zero interest.
  • Librespot does break but it does so infrequently and so far has been relatively minor - the positive result from Spotify's lack of interest.
  • Integrating librespot with Mopidy:
    • Create a libspotify compatible wrapper for librespot that exposes the parts we need for Mopidy-spotify (just the audio?).
      • Could maybe extend the libspotify API with librespot's extra functionality e.g. retrieve Web API token, Connect…
      • Using this directly in pyspotify would require work to the ref counting stuff?
      • Still uses appsrc - nasty.
    • Simplify mopidy.audio by ditching appsrc and getting Spotify audio data from:
      • librespot(d) via fifo (Gst filesrc).
        • This is pretty clunky: fifos are awkward.
        • Needs a way for Mopidy to control librespot (command fifo, socket, Spotify Player API etc)
        • The least amount of work. Could be a good stop-gap.
      • librespot(d) via a Gst ipcpipeline.
        • Requires adding a Gstreamer backend to librespot(d). Is a benefit in replacing the multiple audio backends currently in librespot with one?
        • Potentially less clunky but how well does that inter-process element actually work?
        • Requires Gst v1.14.
        • Can also control librespot via this??
      • A new Spotify Gst source element plugin built on librespot.
        • The Gstreamer project is pro Rust so should be help available.
        • A non-trivial amount of work.

Just weighing in on some of the above:

However, support for streaming from Spotify’s CDN is a must (librespot-org/librespot#280). This is probably the blocker right now.

This and reconnection (librespot-org/librespot#134), which are the main sources of problems with regards to playback on slow/patchy connections

Needs a way for Mopidy to control librespot

This can be done via the connect web api. you could also hook librespot, but any kind of JSON API or similar for easier integration for control/querying belongs in librespotd, and will thus have to wait until the daemon is written.

Requires adding a Gstreamer backend to librespot(d). Is a benefit in replacing the multiple audio backends currently in librespot with one?

Audio backends are a pain to maintain at the moment. There is a basic modular system for adding sinks atm, but for choice we'd avoid adding more and more backends into librespot - what I (personally) would like to see happen would be for (once the daemon is written) all the audio backends to be moved to the daemon, and librespot core to just provide PCM samples, with potentially a cpal/rodio backend for people that don't want to run the daemon, but rather librespot as a minimal standalone.

Requires adding a Gstreamer backend to librespot(d). Is a benefit in replacing the multiple audio backends currently in librespot with one?

See above point, also there is already a GStreamer PR in the librespot repo, which might be a useful reference.

This can be done via the connect web api.

That's a good point. It's a basic API but basic is all we need.

there is already a GStreamer PR in the librespot repo

Oh I see at https://github.com/plietar/librespot/tree/gst. This just made the 2nd option a lot more interesting, I have amended my post above.

Is it possible to switch to spotifyd perhaps?
https://github.com/Spotifyd/spotifyd

No, that's a less useful (from a library point of view) version of librespot.

That said, a viable solution would be to stream spotifyd to pulseaudio, then capture the pulseaudio and stream and do what you want with it from there using e.g. Gstreamer. Would have to figure out how to get tags out of spotifyd though.

https://github.com/allquixotic/tribblify

The docs are out of date but it works; I've used it recently, in fact.

I see. I guess spotifyd with https://github.com/mopidy/mopidy-mpris should work though 🤔

On a side note, since I was thinking about this due to a question in an other bug:

In an ideal future state whatever we land on instead of libspotify should work without GStreamer's appsrc. Relying on that way of delivering audio seems to cause trouble and having something that is either a native GStreamer src element, or something that and existing src element can plug into as a URI would be much nicer. I.e. a lot of the trouble with getting gapless working well and to some degree streaming often boils down to appsrc to some degree.

The other half of this would be that I think I would prefer we use the web APIs as much as possible, relying on the libraries as little as possible

In regards to the first point, an updated version of https://github.com/plietar/librespot/tree/gst is at librespot-org/librespot@dev...allquixotic:gst1.0-2020. I never got around to testing it out but it sounded promising.

My gst forward-port just makes it work with the latest versions of the Rust gstreamer bindings and the librespot backend rearchitecture, and fixes a few bugs that were there in the original port. It's still far from perfect, actually, because what you actually hear often gets out of sync with what's currently being streamed into the appsrc by librespot. I think there might be pipeline hacks we can do to improve that, but I'm not sure. I usually test with a pipeline that puts a queue in front of the processing elements and ends in autoaudiosink. The queue is awesome for protecting against dropouts (I never get any) but might be filling up on silence or something during pauses; not sure.

Anyway, it's far from perfect, but the idea from adamcik about appsrc "causing trouble" is probably spot-on. I'm not sure how we could write a standalone gstreamer source element that can be linked into any process that would consume audio from librespot, though. Putting in playlist or song URIs via parameter is an option, but goes against the current UI model of librespot, where you use a real Spotify client using the Spotify Connect protocol to play audio through librespot. The librespot audio is handled 100% in-process, from the time it's received from Spotify's servers until it's passed to the gstreamer pipeline, and the only way it can leave the process is if the pipeline has it sink out somewhere that's accessible by another process or sound device.

We might be able to fix the bugs in the appsrc style implementation for a first pass and then do another iteration later that creates a proper gstreamer src element for spotify via librespot.

Anyway, these are thoughts that are sort of internal to the design of librespot, so I'm not sure what impact they have (if any) on mopidy-spotify.

If we assume they do not turn off the endpoints, then what advantage does something based on librespot have over just using the current version of libspotify that we already have?

Just to be clear, I think the primary reason people want a switch to librespot (other than future-proofing due to libspotify being unmaintained) is for the Spotify Connect support. Having that seemless integration with the rest of the Spotify ecosystem would be fantastic.

old libspotify is not available for arm64. librespot would fill that gap.

If we assume they do not turn off the endpoints, then what advantage does something based on librespot have over just using the current version of libspotify that we already have?

Just to be clear, I think the primary reason people want a switch to librespot (other than future-proofing due to libspotify being unmaintained) is for the Spotify Connect support. Having that seemless integration with the rest of the Spotify ecosystem would be fantastic.

Yes, definitely, Spotify Connect support is integral.

So I just tried playing around with librespot/librespot-java and gstreamer outside of mopidy to see what it was like. I tried librespot-java with PIPE and STDOUT, and the latest librespot with the gstreamer backend. Controlling it with the spotify connect apis. Couple of thoughts:

  • I couldn't get playbin to work with either, seemingly because decodebin couldn't figure out that the source was raw audio. When I used rawaudioparse or passed the correct sink-caps to decodebin it worked, but there doesn't seem to be a way to get playbin to pass that down to uridecodebin -> decodebin.

  • I also noticed was that latency for pause / next track was 2-4 seconds, maybe even a bit more for the rust gstreamer output, which sort of makes sense with gstreamer queueing and what not, but its quite a bit worst than mopidy-spotify which also uses gstreamer (and obviously librespot with just an os mixer output, so it wasn't spotify connect that was slow). Just speculation, because idk much about how this works, but I guess that this is because in the mopidy-spotify case the appsrc is 'closer' to the input controls and can throw away what's already been buffered on pause/next/etc?

Personally I was mostly interested in this for the internal spotify 'mercury' apis, and thought it would be nice to have just one less thing to run, but since I don't need arm and kind of think spotify connect is a downside, I probably won't do anymore on this. But maybe this will be useful if someone else wants to try it.

Here's the deets on gst commands that work

librespot-java with PIPE to a FIFO mkfifo player.output

gst-launch-1.0 filesrc location=player.output ! rawaudioparse ! input-selector ! playsink flags=audio

librespot with gstreamer backend

Note: need to build with gstreamer support

librespot --name 'rust' --backend gstreamer --device '! rawaudioparse ! input-selector ! playsink flags=audio'

decodebin issues

Internally playbin uses uridecodebin, which in turn use decodebin (note i was using input-selector -> playsink because that basically mirrors the pipeline mopidy's playbin ends up with). With the output of either librespot decodebin fails for typefind:

! decodebin ! input-selector ! playsink flags=audio

ERROR: from element /GstPipeline:pipeline0/GstDecodeBin:decodebin0/GstTypeFindElement:typefind: Could not determine type of stream.
Additional debug info:
../plugins/elements/gsttypefindelement.c(1000): gst_type_find_element_chain_do_typefinding (): /GstPipeline:pipeline0/GstDecodeBin:decodebin0/GstTypeFindElement:typefind
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
ERROR: from element /GstPipeline:pipeline0/GstFileSrc:filesrc0: Internal data stream error.
Additional debug info:
../libs/gst/base/gstbasesrc.c(3127): gst_base_src_loop (): /GstPipeline:pipeline0/GstFileSrc:filesrc0:
streaming stopped, reason error (-5)
ERROR: pipeline doesn't want to preroll.
Freeing pipeline ..

Whereas giving it a hint based on what I see in the logs of rawaudioparse everything works. I imagine this isn't a good idea, and anyways playbin/uridecodebin don't let you pass these hints along.

! decodebin sink-caps='audio/x-raw, format=(string)S16LE, layout=(string)interleaved, rate=(int)44100, channels=(int)2, channel-mask=(bitmask)0x0000000000000003' ! input-selector ! playsink flags=audio

I found the latency is improved with this version but I was using it without specifying a --device argument (so it was playing straight to autoaudiosink rather than trying to emulate Mopidy's pipeline).

Hey folks,

I've spent the last couple of weeks working on integrating Librespot + the Spotify Web Player API in Platypush, just to have a backup plan if libspotify stops working for good. I have managed to put together something quite usable (although a bit hacky) that could also be recycled here. For those interested:

  • Here is the code of the backend
  • Here is the code of the plugin

The backend acts as a wrapper around Librespot (I have only tested Librespot with the ALSA backend though, not gstreamer). Librespot is definitely not designed for asynchronous events management in mind though, but at least it can call an external script when something happens (e.g. new track, state change, volume change etc.), so a small script that synchronizes somehow with the main app upon events does the trick. The code of the backend is sufficient to get the device to appear in the Spotify Connect list on the Spotify app.

The plugin then uses the web player API to control the playback and manage the queue - which is possible now because the device appears as a Spotify Connect compatible player.

I have even managed to tweak the existing UI interface I made for mpd/mopidy to work with the new Spotify integration with just a couple of changes.

PROS

  • Unless Librespot gets broken in the future, this approach should save us from assisting to the slow agony of libspotify
  • Spotify Connect integration comes for free
  • The multi-device approach that comes from using directly the Player API allows also to control playback on other devices that run the native app or other Librespot instances - could be an alternative to the Snapcast interface on e.g. mopidy-iris

CONS

  • Although well-supported, Librespot is more a hacky executable PoC rather than a well-designed library. The workaround of the external script to communicate events back to the app adds a bit of latency. I haven't tested it with gstreamer and I've heard that gstreamer support is actually quite poor.
  • The /v1/me/player Spotify endpoint is actually quite flaky (not sure if it's deliberate). Even if a player is active/not asleep (like it's the case of the background Librespot process), it won't appear in the list unless it has recently been paired to a Spotify app/client (but it appears if you go to the "Devices" section of the Spotify app). And a PUT request to the endpoint (to start the playback) fails unless the device is on the players list.
  • Not sure if it's a "happy incident" or not, but with libspotify I can use the same account to simultaneously stream different tracks from different devices. This is no longer possible with Librespot+Player API, because there's only one stream associated to an account and only one "active" player can own it at a certain time.

Let me know what you folks think. The alternative I see are:

  1. We may decide that a migration to Librespot may be worth investigating for the long-term (if so, understand how it behaves with gstreamer)
  2. We may decide that it may be worth a separate mopidy-librespot integration for this
  3. We decide that the Librespot way it's still too hacky (and/or the project too immature) to be worth investing the effort on a migration or another integration (in that case I'll keep using my project's integration as a backup in case libspotify goes down)

On the "pros" side don't forget arm64 support. I have just migrated my Raspberry Pi to the 64 bit Raspberry Pi OS because it doubles and triples the performance of some other apps and found that now mopid-spotify no longer works. For now I can just rip some playlists as a workaround but it would be nice to be able to properly use Spotify again.

First, let me say thanks for this really great project!

My 2 cents on this, from the point of view of an external user: continuing to use a library that has been deprecated for many years now is simply not sustainable. The risk that it might stop working at any moment is only part of the story. Even if it doesn't, this choice will still lead to a slow death by stagnation, since nobody has the motivation to implement new features or fix bugs. I'm not sure what's the best migration plan is, but there should be such a plan.

FYI I had the same issue with OwnTone (previously forked-daapd), which I maintain. Its Spotify integration also was based on libspotify + web API, and libspotify was of course troublesome. I made several attempts at integrating with librespot instead, but it was too difficult (also because my Rust skills aren't good). So in the end I ended up partially porting librespot to C (as a library), and OwnTone is now using that. Here is the port. I didn't port the Connect stuff, since I only needed a libspotify replacement. Might do that later.

An added benefit is that it has made the Spotify login much more smooth. The token from the OAuth web API login can be reused for the librespot login, so that is nice (and a security improvement, since OwnTone thus never has the cleartext password).

Of course this is all very unofficial and can be broken by Spotify at any time, but I suppose the more Premium users there are on this alternative channel the less likely that is.

Btw I think Spotify Connect reuses a lot from libspotify, so I think that is why it is still working despite being deprecated for many years. That also means the risk of it breaking might be smaller than you might expect.

My 2 cents on this discussion, considering @blacklight 's comments:

  1. Keep using libspotify for, at the very least, playback, until it no longer works, at which point we would have to switch to librespot.
  2. Use the Web API for any other feature when possible (i.e merge mopidy-spotify-web into this repo)
  1. Keep using libspotify for, at the very least, playback, until it no longer works, at which point we would have to switch to librespot.

I guess this happend faster then expected. Is switching to librespot feasible by now?

Sady, yes, it's finally dead. They quietly posted a 1-month notice this was happening. I guess they forgot to post it on the Spotify Developer mailing list (which must also be dead!).

And actually yes, there's a better option for us now in the shape of https://lib.rs/crates/gst-plugin-spotify.

As I posted earlier on our forum, there is a possible hack to libspotify we could do but I think it's beyond me.

And actually yes, there's a better option for us now in the shape of https://lib.rs/crates/gst-plugin-spotify.

gst-launch-1.0 spotifyaudiosrc username=$USERNAME password=$PASSWORD track=spotify:track:3i3P1mGpV9eRlfKccjDjwi ! oggdemux ! vorbisdec ! audioconvert ! autoaudiosink

This is beautiful in so many ways. Wish we've had such a solution available earlier instead of sitting on libspotify's corpse while waiting for it to sink.

@kingosticks since this is already a Gst-native plugin, it probably shouldn't be too hard to integrate into the existing codebase, is that correct? It actually should simplify things - a lot of code in the playback module that manages audio buffers can go away. Is there any plan to change the implementation to gst-plugin-spotify? I can take a look when I have a bit of time, but I can't make big promises.

Yes, we want to use this, or something just like this. We've always wanted to get rid of using appsrc. We might have some performance problems to overcome but we can worry about them if/when we hit them.

It's a GST Rust plugin which is a little more complicated when it comes to packaging. It's also currently lacking URI handler support which I worked on a while back but never quite around to finishing/testing/upstreaming it.

I've just noticed that it has Librespot as a dependency. I've actually built the Spotify integration in Platypush using Librespot instead of libspotify (call it "future-proofing"). I mostly like it, but I've also got concerns on size, build time and packaging.

I couldn't find a binary Librespot executable installable on RPi - there's a librespot-dev Snap image, but then I guess that Snap would become a dependency for mopidy-spotify? The alternative is to build Librespot directly on the RPi as part of the extension's build process. I've done so on three RPis, and I can assure that it's a slow and painful process. First, because it requires the whole Rust environment (which, again, isn't available on RPi OS via apt) with a whole set of dependencies. Second, because the build process itself take ~1-3hr depending on the RPi. And you eventually end up with a few hundreds of MBs of used storage space.

Performance itself IMHO isn't a big blocker. I've been running Librespot on my RPis for a while and I didn't notice any performance issues when compared to mopidy-spotify. I'm mostly concerned on how to get Librespot installed in the first place without workarounds or painful manual processes.

https://github.com/dtcooper/raspotify

Installs librespot and has rpi binaries

For armhf buster



if [ $LMARCH == 'armhf' ]; then
  #apt-get -y -q install raspotify=0.31.8~librespot.v0.3.1-54-gf4be9bb
  wget https://github.com/dtcooper/raspotify/blob/2d6ceca0921a632b73e45cb8dbed6b8a57b3b608/pool/main/r/raspotify/raspotify_0.31.8~librespot.v0.3.1-54-gf4be9bb_armhf.deb?raw=true -O rasp.deb
  dpkg -i rasp.deb && rm -f rasp.deb
  if ! grep -q raspotify /etc/group; then
  	groupadd raspotify
  fi
  usermod -a -G raspotify user
  systemctl disable raspotify
  install -d -m 755 -o 1000 -g 1000 "/home/user/.config/systemd/"
  install -d -m 755 -o 1000 -g 1000 "/home/user/.config/systemd/user/"
  install -v -m 644 -o 1000 -g 1000 $FILE_FOLDER/raspotify.service "/home/user/.config/systemd/user/raspotify.service"
fi

apt-get clean

pip3 install --upgrade spotify-cli

Hmm I'm not sure if I like this.

We would be basically installing a whole service (Raspotify), and disable it after installation, just because it provides a ready-to-use Librespot binary.

Also, mopidy-spotify currently comes with apt builds, and I'm not sure if this is an elegant solution to use into an apt package.

Not that my proposed solutions were any better (installing the Rust environment to compile a heavy project like Librespot is probably worse than this). But if we have to go down this way, I'd rather just use the librespot-dev Snap image - at least Snap is available in the official repos and it's easier to handle and package.

We can cross-compile and distribute our own binaries. Don't get bogged down in any of that.

The performance I am talking about is in regards to loading track data - with spotifyaudiosrc the Spotify session lives only as long as the spotifyaudiosrc element and will be created and destroyed for each track when it starts/ends. This is different to how things work now.

@kingosticks do you know if that source supports the right URI interface for uriplaybin etc to pick the element. If that doesn't work we would need to get that fixed or come up with some workaround.

It would be real nice to kill the appsrc integration without adding new hacks.

As it is, it doesn't. But what I implemented in my branch should add that support and I know the gst maintainers want it done and will take it. So I'll try get a PR off to them tonight.

@kingosticks

We can cross-compile and distribute our own binaries

If we can cross-compile and bundle the binaries in the package then of course things are much simpler - no Snap/Rust builds required.

Just a heads up on a couple of issues that I've had with the Platypush/Librespot integration:

  • Tracks only play if I first open the Spotify client/app and select the Librespot player from the Spotify Connect panel. Unless I do that, the Librespot player doesn't even show under /me/player/devices, and calls to /me/player/play with the Librespot device_id just fail with a 404. Not sure if this is tackled somehow in the Gst plugin.

  • Librespot's way of dispatching events is quite hackish to say the least. It basically allows an external script to be passed via --onevent - no callbacks, DBus support etc. This means that, in order to synchronize the status of my app with playback changes performed by another client over Spotify Connect, I've had to resort to a workaround that involves an --onevent script that pushes events on a Redis queue, and the main app consumes them from the queue. Definitely not a clean nor efficient solution.

  • When playback is started by another client over Spotify Connect, it can take a while (sometimes 10-15 seconds) before the audio streams are synchronized to Librespot.

However, it also seems that we don't have many alternatives.

Let me know if you need any help with the migration. My mopidy-and-snapcast automation is currently broken (I've tried as long as possible to avoid restarting mopidy on some devices so I could leverage cached sessions that were still valid) so I'm happy to offer any support to make sure that an alternative is shipped ASAP.

I just changed the title in the hopes that people stop creating new duplicate issues for the problem...

Will there be a patch for mopidy spotify?
and are there any alternatives to mopidy that work with spotify and snapcast

and are there any alternatives to mopidy that work with spotify and snapcast

Depending on what you want to achieve, raspotify might do that.

I had some problems with spotifyaudiosrc which I have now resolved after getting some clues from the gst devs. Turns out "source-setup" only fires before the element starts, on Gstreamer v1.20 onwards. I'll push that stuff.

Next, I'll try and hack Mopidy-Spotify to use it. I only have some evenings to work on this, can't say when it'll be working.

Mopidy-Tidal might be an alternative. No idea what state that is in. Feel free to discuss that further in our forum.

Thanks so much for your time and effort on this @kingosticks. My children found out today that they can't play their music with the rfid cards... (we have been using a rpi with cards for almost 4 years) and before getting to this post i was totally lost :)

FYI I have migrated from Spotify to Tidal and cancelled my Spotify subscription after more than 10 years.

I started investigating gst-plugin-spotify, and in the past couple of days I have used my Librespot+Platypush integration to play music on my Raspberry Pis as a workaround, but:

  1. The latency on new track play/skip is almost unbearable (~10 seconds from the time one gives the command to the time some music actually happens).

  2. Over the past decade I've built a lot integrations and automation routines that relied on mopidy, and I don't think that it's fair to use more of my spare time to play this cat-and-mouse game with Spotify just to keep mopidy-spotify working, nor I am willing to rewrite all of my automation routines using a backend that is not MPD-compatible. If Spotify keeps breaking things without providing alternatives, and they don't care of how users actually want to consume content, then they just don't deserve my money.

I have used Soundiiz (https://soundiiz.com/) to migrate my collection from Spotify to Tidal. It requires a premium account for a full collection migration, but it's just $4.50 a month, and probably you can cancel it after moving your stuff.

mopidy-tidal doesn't look as mature as mopidy-spotify yet (among the first things I've spotted is that it doesn't support year/date on track metadata, and mopidy-iris doesn't support adding tracks to Tidal playlists), but IMHO it's worth rewarding the services that at least give us the opportunity to stream music outside of a browser.

@blacklight Thanks for telling me about these two amazing services. I will sure make use of them.

I have checked Tidal and for me it's sadly not an alternative yet. So if you can get mopidy-spotify working again every Phoniebox/RPi-Jukebox-RFID user would be very greatful. I'm sure we could even chime in with some well deserved donations.

Feel free to continue discussing alternatives etc at https://discourse.mopidy.com/ or our zulip instance, it's better if this bug is just focused on the issue at hand. Good luck finding something that works well for you :-)

I've hacked something up as a proof of concept. Plenty more to do but it's working well and I don't see any of the performance issues I was worried about. But packaging might be the tricky part of this.

@kingosticks Could you push somewhere so we can test and maybe start packaging work for other distributions?

I've not gotten much further so there's not a lot to test yet. But you are welcome to try what's there if you can get it going, gst-plugins-rs has some instructions how to compile.

Only track lookup is done but the others are pretty much the same. I'll see if I can get further tomorrow night. Help with the Debian packaging is probably the most useful thing to do. If someone can work out what we need for cargo-deb to work, assuming that's the best option.

Did I understand correctly that the source setup variant for setting user/pass doesn't work for the librespot gst integration, i.e. we can't do something like https://github.com/mopidy/mopidy/blob/develop/mopidy/audio/actor.py#L564 and just need to pass the auth info in the URI?

There's some bug/feature in GStreamer < v1.20 where source-setup fires AFTER the source has already been started, which is too late. In my tests the pipeline actually tries to start the source 2 more times after that, and for these the user and password will have been set by source-setup so they succeed. I'm trying to find out why that is and if we can rely on that. If we can't rely on that, and if we want to keep supporting GST v1.18 (in Debian stable so I think we must), seems we need to pass the auth info in the URI.

In GStreamer v1.20 it works nicely with a source-setup handler as you would expect. And we could have extensions implement that handler and remove everything Spotify specific from Audio.