Open pezzy-o opened 3 years ago
So you'd like an API endpoint to list devides available on your network and one to connect to them?
Basically yes - that was just a long-winded way of asking, so I could explain the use case.
First part should be relatively easy: just need to make a mDNS probe and get the results. For the second one we need to craft the zeroconf blob which ultimately is a lot of wrapping around stored credentials data.
I'll start by modifying zeroconf-java to allow probing.
Awesome - thanks for getting started so quickly! This might be me, but I'm getting an authorisation failure (401) when I try to compile (mvn clean package): Downloading from github: https://maven.pkg.github.com/devgianlu/*/xyz/gianlu/zeroconf/zeroconf/1.2.1/zeroconf-1.2.1.pom [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary for librespot-java 1.5.6-SNAPSHOT: [INFO] [INFO] librespot-java ..................................... SUCCESS [ 0.150 s] [INFO] librespot-java sink API ............................ SUCCESS [ 2.256 s] [INFO] librespot-java default sink ........................ SUCCESS [ 0.584 s] [INFO] librespot-java decoder API ......................... SUCCESS [ 0.385 s] [INFO] librespot-java lib ................................. FAILURE [ 1.125 s] [INFO] librespot-java DACP interface ...................... SKIPPED [INFO] librespot-java player .............................. SKIPPED [INFO] librespot-java api ................................. SKIPPED [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5.275 s [INFO] Finished at: 2021-05-08T20:10:59+10:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal on project librespot-lib: Could not resolve dependencies for project xyz.gianlu.librespot:librespot-lib:jar:1.5.6-SNAPSHOT: Failed to collect dependencies at xyz.gianlu.zeroconf:zeroconf:jar:1.2.1: Failed to read artifact descriptor for xyz.gianlu.zeroconf:zeroconf:jar:1.2.1: Could not transfer artifact xyz.gianlu.zeroconf:zeroconf:pom:1.2.1 from/to github (https://maven.pkg.github.com/devgianlu/): Authentication failed for https://maven.pkg.github.com/devgianlu//xyz/gianlu/zeroconf/zeroconf/1.2.1/zeroconf-1.2.1.pom 401 Unauthorized -> [Help 1]
Yep, I know. The CI is also failing, there's something wrong with the CI on zeroconf-java because I had to switch away from Travis and it broke. Hadn't time to fix it yet.
As a workaround you can either clone zeroconf-java and do mvn clean install
or download the JAR and install it manually.
mvn clean install
is giving me the same result. I'm not sure how I would modify the compiled JAR file: I took a look inside the archive and couldn't immediately find the same filenames as were modified in the commit, but I'm not overly familiar with java, maven etc. so might be best for me to just wait until the CI is fixed and I can compile it again. Thanks @devgianlu
@benturnberg You should be good now.
Great - I can compile again 🤘
Using the librespot-api-1.6.1-SNAPSHOT.jar, I get the following result when running a POST to http://localhost:24879/discovery/list:
2021-05-10 16:26:25,521 TRACE selector:564 - Selected on sun.nio.ch.WindowsSelectorImpl@4166658f
2021-05-10 16:26:25,521 TRACE selector:583 - Selected key channel=java.nio.channels.SocketChannel[connected local=/[0:0:0:0:0:0:0:1]:24879 remote=/[0:0:0:0:0:0:0:1]:59437], selector=sun.nio.ch.WindowsSelectorImpl@4166658f, interestOps=1, readyOps=1 for java.nio.channels.SocketChannel[connected local=/[0:0:0:0:0:0:0:1]:24879 remote=/[0:0:0:0:0:0:0:1]:59437]
2021-05-10 16:26:25,521 TRACE selector:169 - Calling handleReady key 1 for java.nio.channels.SocketChannel[connected local=/[0:0:0:0:0:0:0:1]:24879 remote=/[0:0:0:0:0:0:0:1]:59437]
2021-05-10 16:26:25,522 TRACE listener:91 - Invoking listener io.undertow.server.protocol.http.HttpReadListener@22e2f427 on channel org.xnio.conduits.ConduitStreamSourceChannel@f641445
2021-05-10 16:26:25,522 TRACE transfer-encoding:119 - No content, starting next request
2021-05-10 16:26:25,522 TRACE selector:539 - Beginning select on sun.nio.ch.WindowsSelectorImpl@4166658f (with timeout)
2021-05-10 16:26:25,522 TRACE nio:550 - Select, queue is empty
2021-05-10 16:26:26,274 TRACE selector:564 - Selected on sun.nio.ch.WindowsSelectorImpl@4166658f
2021-05-10 16:26:26,275 TRACE selector:539 - Beginning select on sun.nio.ch.WindowsSelectorImpl@4166658f (with timeout)
2021-05-10 16:26:26,275 TRACE nio:550 - Select, queue is empty
2021-05-10 16:26:26,274 TRACE HttpServerExchange:1866 - Starting to write response for HttpServerExchange{ POST /discovery/list}
2021-05-10 16:26:26,277 TRACE safe-close:151 - Closing resource io.undertow.server.HttpServerExchange$DefaultBlockingHttpExchange@6da75879
2021-05-10 16:26:26,277 TRACE safe-close:151 - Closing resource io.undertow.server.HttpServerExchange$DefaultBlockingHttpExchange@6da75879
2021-05-10 16:26:27,002 TRACE selector:564 - Selected on sun.nio.ch.WindowsSelectorImpl@4166658f
2021-05-10 16:26:27,002 TRACE nio:611 - Running task io.undertow.util.DateUtils$2@4c001e57
2021-05-10 16:26:27,002 TRACE selector:539 - Beginning select on sun.nio.ch.WindowsSelectorImpl@4166658f (with timeout)
2021-05-10 16:26:27,002 TRACE nio:550 - Select, queue is empty
2021-05-10 16:26:30,906 TRACE selector:564 - Selected on sun.nio.ch.WindowsSelectorImpl@4166658f
2021-05-10 16:26:30,906 TRACE nio:611 - Running task io.undertow.server.protocol.ParseTimeoutUpdater@6b5bf646
2021-05-10 16:26:30,907 TRACE selector:539 - Beginning select on sun.nio.ch.WindowsSelectorImpl@4166658f (with timeout)
2021-05-10 16:26:30,907 TRACE nio:550 - Select, queue is empty
You should get the list of devices in the response body.
Sorry, should have included that - the response body is:
{
"value": [
],
"Count": 0
}
Just to add to this, the API does work for me when invoking /player/next for example... On the other hand, it also returns a blank response when (while connected) I invoke /web-api/v1/me/player/devices.
I think the problem goes back to zeroconf-java not being able to discover your librespot-java client.
Anything I can check on my side? I tried disabling AV and firewall just as a sense-check, still the same though.
Turns out there was a bug in zeroconf-java
. Should be fixed now.
Ok that's good. Now it can see itself, but it's not seeing any other players advertised on the local network:
{
"value": [
{
"name": "librespot-java",
"target": "librespot-java",
"port": 57351
}
],
"Count": 1
}
... or at least, it's not returning them in the body of the response
After more bug fixing it should be finally good.
It's still only recognising itself.
What do you see if you run dns-sd -B _spotify-connect local
?
The devices are there:
Timestamp A/R Flags if Domain Service Type Instance Name
16:03:30.999 Add 2 20 local. _spotify-connect._tcp. librespot-java
16:03:31.162 Add 2 20 local. _spotify-connect._tcp. sonos48A6B8XXXXX1
16:03:31.208 Add 2 20 local. _spotify-connect._tcp. sonos48A6B8XXXXX2
Hey @devgianlu let me know if there's anything else I can do to troubleshoot. Cheers
@benturnberg I just need to find the time to debug zeroconf-java with a Spotify Connect enabled device (likely my TV).
Thanks @devgianlu , I appreciate it. Finding the time can certainly be difficult!
Hi,
I just stumbled upon librespot-java
(which looks amazing!) and then this thread when trying to use it. I'm building an NFC remote control to enable the kids to play Spotify albums/playlists on a Sonos speaker by scanning a card with an NFC tag.
Like @benturnberg I can see the speakers with dns-sd
:
dns-sd -B _spotify-connect local
Browsing for _spotify-connect._tcp.local
DATE: ---Sun 20 Jun 2021---
13:39:23.219 ...STARTING...
Timestamp A/R Flags if Domain Service Type Instance Name
13:39:23.220 Add 3 6 local. _spotify-connect._tcp. sonosB8E9374XXXXX
13:39:23.220 Add 3 6 local. _spotify-connect._tcp. sonos7828CAXXXXX2
13:39:23.220 Add 3 6 local. _spotify-connect._tcp. sonos7828CAXXXXXA
13:39:23.220 Add 3 6 local. _spotify-connect._tcp. sonos7828CAXXXXX0
13:39:23.220 Add 3 6 local. _spotify-connect._tcp. librespot-java
13:39:23.220 Add 2 6 local. _spotify-connect._tcp. Kodi (mediacenter)
But when running the API server (via Coursier)
cs launch -r https://oss.sonatype.org/content/repositories/snapshots xyz.gianlu.librespot:librespot-api:1.6.1-SNAPSHOT -M xyz.gianlu.librespot.api.Main
and then calling the discovery endpoint, I can see only the other Spotify connect devices but not the Sonos speakers:
curl -s -X POST http://localhost:24879/discovery/list | jq
[
{
"name": "librespot-java",
"target": "Sorens-MacBook-Pro",
"port": 34515
},
{
"name": "Kodi (mediacenter)",
"target": "mediacenter.local",
"port": 46233
}
]
I also ran the discoverer manually (basically the code below from the DiscoveryHandler
just to check and got the same result (Sonos speakers not showing up).
https://github.com/librespot-org/librespot-java/blob/2745b502e69a2dc69bd461516cb62f7d6dfad4e4/api/src/main/java/xyz/gianlu/librespot/api/handlers/DiscoveryHandler.java#L43-L53
If there's anything I can do to help debugging the issue, I'd be happy to.
Wow that was fast @devgianlu. While looking into zeroconf-java, I just saw that you had released 1.3.1
. So I tried it and now the speakers show up on the discovery enpoint!
Turns out there was a bug in the code I hadn't written myself. It was working with zeroconf-java only because the packet creation is unoptimized while official clients are.
I was writing the code for the connection, but it can be done in two ways:
Let me know if you intended to use it differently.
I think connecting with the current user of librespot-java would be the best fit for my use-case.
Current user would work with my use case too. Thanks @devgianlu
@devgianlu now that discovery works is there anything I can do to help with the connect part?
I don't really have time to develop this feature. What needs to be done is writing the encryption code for the blob having the one for decryption.
Has anyone made any progress on this?
I have started the patchset to encrypt the blob and make the addUser
request to the HTTP server advertised by zeroconf. I thought I'd check in before I do too much more work on the feature. My use case is basically the same as @sbrunk's.
@dsheets I haven't looked into this further yet.
Any progress on this? I am trying to make the same addUser request in Python 3. Thanks.
Just wanted to add myself in here. I have been trying to get my Spocon setup to show up on the network for things like HomeAssistant and have been struggling. I think this is a similar issue?
I am trying to do the mDNS port forwarding and Google led me here.
Is there any way to wake up Spocon/librespot and show it on Spotify Connect?
Not sure on that, as I am not familiar with “Spocon”. Or is that short for Spotify Connect?
I have had success with getting the integration to work with the spotifyd and Spotify Connect Addon, both of which use librespot under the covers. You just have to configure librespot in discovery mode, and create a credentials.json file.
Checkout the SpotifyPlus Device Configuation Options for librespot wiki doc for more details.
@floodwayprintco
I did some more research on SpoCon, and found the SpoCon web-site and associated configuration docs. I think you can adjust your /opt/spocon/config.toml
configuration settings to use the following options for librespot:
# Spotify Connect device name and info
deviceName = "My-Spocon"
deviceType = "COMPUTER"
preferredLocale = "en"
[auth] # use zeroconf authentication (no username, password, blob)
strategy = "ZEROCONF"
[zeroconf] # listen on all interfaces, and bind to specific port 8600
listenPort = 8600
listenAll = true
[cache] # enable cache path for librespot auth credentials
enabled = true
dir = "./cache/"
doCleanUp = true
That should force SpoCon to advertise itself via zeroconf / mdns on port 8600. I just picked 8600 out of the blue, you can choose whatever port you want. I like a dedicated (vs random) port, in case you need to open firewalls. If you do open firewalls, open activity for 8600 TCP and 5353 UDP (mdns advertise).
I would also avoid spaces in the deviceName
parameter, as some Spotify Clients have issues with handling spaces in the name.
You should then be able to start the SpoCon service; after it is started, do the following:
1) Open the Spotify Desktop client from a machine on the same network as you ran this, ensuring no proxy is in use that may interfere with zeroconf. Note that the Spotify Mobile client does not find devices immediately like the Spotify Desktop client does. It is suggested that you use the Spotify Desktop client for this step.
2) Use the Spotify client "Connect to a Device" menu to select the device name that you specified for the deviceName
config value (My-Spocon
in this example).
At this point, librespot should have generated a credentials.json
file in the cache path that you specified for the dir
config value (./cache/credentials.json
in this example).
Please refer to the SpotifyPlus librespot Credentials File topic for further instructions on how to utilize the librespot credentials with the SpotifyPlus integration.
Hope it helps - let me know either way, as you are the first person I have heard of that utilizes SpoCon. I have never used it myself, but based on the docs that I read it should work fine. If it does work, then I will add this to my SpotifyPlus wiki documentation.
@floodwayprintco Apologies - I just realized that this thread is for the librespot-java process, and not SpotifyPlus integration.
Problem: Spotify Web API only allows me to connect to devices which are specifically registered to my account, rather than those devices which advertise themselves over mdns on my local network.
Possible solution: I'd like to be able to use an API in order to programmatically connect to any Spotify devices on my local network, using Zeroconf. I can see that you've done the hard yards to be able to connect the librespot-java player via Zeroconf: would it be possible to onboard other players using a similar method, and incorporate this into the API?
Background / use case: I have created an application in Spotify, and I can use it for Spotify Web API calls, including connecting to those devices which are registered to my Spotify account. I use Sonos speakers, which seem to rely more on Zeroconf connections for devices on the local network, so when I use the Spotify Web API to list available devices using the Spotify Web API, unless I've recently connected to the Sonos device specifically through the Spotify app, the Sonos devices do not appear in the list of available devices. The Sonos devices still show up on the Spotify Connect app (they're still advertised and over mdns), just not in the Web API, presumably because they aren't registered specifically to my account, and need to be available to other users on my local network.