jishi / node-sonos-http-api

An HTTP API bridge for Sonos easing automation. Hostable on any node.js capable device, like a raspberry pi or similar.
http://jishi.github.io/node-sonos-http-api/
MIT License
1.85k stars 460 forks source link

Google Play Music Support #372

Open BenC14 opened 7 years ago

BenC14 commented 7 years ago

I notice that you do not currently have support for Google Play Music. Is that because you tried to implement it and ran into some sort of technical limitation or have you just not tried yet. I just got an Amazon Echo and plan to use this repo in conjunction with the echo-sonos skill. I am aware Google Play Music doesn't have an official API so I am hoping to use the playmusic node module to get my music. Any information you could give me about your music search functionality would be extremely helpful.

jplourde5 commented 7 years ago

I need some sort of search api for musicSearch. Sonos does not implement the search and instead leaves it to the client solution to perform a search. The API does not have to be official and it may even require a logon. OAuth is problematic, if required, because it normally requires an approved partner ID that is difficult, if not impossible to get. I have not yet tried to implement Google Play but will be happy to add support if you can point me to API info that will work.

BenC14 commented 7 years ago

I haven't spent more than a day looking at this api(I got my Echo yesterday) but it looks promising, https://github.com/jamon/playmusic. What kind of information do you need to pass to the Sonos to get it to play a song is it just looking for a track id or some sort or formatted uri? It seems to be slightly different for all the services @jishi has already implemented which is why I ask.

jplourde5 commented 7 years ago

There is a good bit to it, but it mainly boils down to building uri and metadata strings that have to get passed. The magic is in building the strings correctly. I built the service to be extensible, so it is not too difficult for me to extend it if there is an accessible search API. I'll take a look at play music to see if I can find the search api that is used.

BenC14 commented 7 years ago

Awesome thanks! I will check back regularly too and can offer assistance if you want to chat. I am a college student studying computer science and this is my first public repo that I have commented on so any help I can give would be great experience for me. Good Luck!

jplourde5 commented 7 years ago

Did you buy the streaming service?

BenC14 commented 7 years ago

Do you mean do I have Google Play All Access, yes.

jishi commented 7 years ago

I think they have a 3 month free trial.

jplourde5 commented 7 years ago

Bad news. I got play music to work fine but the search is not returning a track id that matches what Sonos is using for the track id. It almost looks like Sonos is encrypting the track id in the uri and metadata.

BenC14 commented 7 years ago

Could you post your branch so I could take a look?

jplourde5 commented 7 years ago

At this point all I really did was what I normally do to begin work on another service. 1) I add a track, album, station to Sonos Favorites. 2) In replaceWithFavorite.js in Sonos Discovery, I send the favorite uri and metadata to the console. 3) I execute searches using the API and compare what gets returned with what I'm seeing in the metadata. In this case I used the PlayMusic solution's test app to check the search results. This is where I'm seeing that the track ID in the search results is not matching the ID in the metadata.

The search result actually looks like what I normally see for an ID. The metadata is looking like the ID is being encrypted now. I saw something similar when I recently played with adding Plex support. Sonos may be purposely making it more difficult to hack the solution. I fear that this pattern may become the norm for any new services adopting their new common approach for service adoption.

BenC14 commented 7 years ago

Very Interesting, my initial thought was perhaps it had something to do with the fact that google play supports casting to the sonos and that might give a different id but if you were also seeing it with plex perhaps there is a new more advanced encryption system. Nonetheless I'll follow the steps you took and see if I can get any further. I really don't want to switch music streaming services because of this.

jishi commented 7 years ago

I know that the Sonos uri/metadata is different when casting from the Google Music app onto sonos, compared with starting music playback from Google Music within the Sonos app.

jplourde5 commented 7 years ago

If there are two different Google Search API's, one for Sonos and another for the other clients, and they are not returning the same ID, then we will need the API that the Sonos client is using instead of what playMusic is using. What is concerning is that in the case of both Plex and Google Play, the "ID" that I'm seeing in the Sonos metadata looks very cryptic compared to the ID's I've seen with what is already supported. In the case of Plex, the length of the ID was like 20 or 30 characters. Google Play was not that long but it was much longer than what I have seen with other services and longer than the ID that playMusic is returning.

jetlag55 commented 7 years ago

Very interested in seeing support for Google Music as well. Did last month's research seem to indicate a dead end? Thanks!

digitalkaoz commented 7 years ago

any news on that?

jishi commented 7 years ago

No, unfortunately not.

Guimcha commented 6 years ago

any news ? thank you for this great lib !

alaycock commented 6 years ago

I spent a weekend trying to figure this out, and also ran into the same issue of Sonos using different IDs than what the Play Music API returns. I was trying to intercept the calls to the Play Music API, and I could see that it was making calls to https://mclients.googleapis.com (the same API that is outlined here). I couldn't intercept the requests themselves though, I'm not 100% sure why, but it's likely due to certificate pinning. Charles Proxy was able to see the call to the server, but the connection failed, which is probably because the Sonos client was pinning the certificate used by mclients.googleapis.com.

I took a look at the Sonos client using the Hopper disassembler, but couldn't find any references to the Google service it's connecting to, and I'm not proficient enough at disassembling to dig any deeper than that. My thought is that the list of music services is loaded from somewhere else and not built into the main executable.

For reference, here's the same IDs provided by Sonos and the Play Music APIs.

Sonos: A0DvPDnowsJ2TPMgSwXGtyPUHeiFf5XLuXj3hSPlc9Lze1KCYJefUQ Play Music: 4ugjf42vs6ygihuxrf3rzo6xou (or possibly T4ugjf42vs6ygihuxrf3rzo6xou).

Sonos track information:
https://gist.github.com/alaycock/0fa4391cde36c5cb0a9f23fbaf09fe0f
Play Music track information:
https://gist.github.com/alaycock/3de5337a95b501ea7bbde5209a23e53d

All Play Music tracks have the prefix A0DvPDnows, which indicates to me that the ID is encoded, not encrypted, and there's probably a way to reverse it, but I don't know enough about that sort of thing to actually do it. I tried the basic stuff like base64 but didn't have any luck. Maybe not though, I don't know a ton about hashing/encryption.

The last thing I found was a reference to a specific Sonos endpoint for Play Music API in the Sonos log (/Users/username/Library/Logs/Sonos on OSX). https://mclients.googleapis.com/music/sonos/wsf/smapi#getLastUpdate. It appears that the Google Music API has a specific API for Play Music, which would mean that reversing the IDs might be pretty tricky because they could be using a secret key on the Play Music servers to encode the ID.

I also saw the line 2017/11/25 16:20:46:210 certval(1): (pass 1) local cert validation failed (27) for mclients.googleapis.com in the logs from the day I was attempting to intercept the connection, which seems to line up with my theory of local certificate pinning.

If anyone else wants to try moving forward in the direction I've been going, I'd say the next step would be trying to find the local certificate that the Sonos client is using for Google, and swap it out with the cert provided by Charles Proxy, which would allow you to MITM the connection to the Play Music server and figure out how that API works.

moschnetwork commented 4 years ago

I spent a weekend trying to figure this out, and also ran into the same issue of Sonos using different IDs than what the Play Music API returns. I was trying to intercept the calls to the Play Music API, and I could see that it was making calls to https://mclients.googleapis.com (the same API that is outlined here). I couldn't intercept the requests themselves though, I'm not 100% sure why, but it's likely due to certificate pinning. Charles Proxy was able to see the call to the server, but the connection failed, which is probably because the Sonos client was pinning the certificate used by mclients.googleapis.com.

I took a look at the Sonos client using the Hopper disassembler, but couldn't find any references to the Google service it's connecting to, and I'm not proficient enough at disassembling to dig any deeper than that. My thought is that the list of music services is loaded from somewhere else and not built into the main executable.

For reference, here's the same IDs provided by Sonos and the Play Music APIs.

Sonos: A0DvPDnowsJ2TPMgSwXGtyPUHeiFf5XLuXj3hSPlc9Lze1KCYJefUQ Play Music: 4ugjf42vs6ygihuxrf3rzo6xou (or possibly T4ugjf42vs6ygihuxrf3rzo6xou).

Sonos track information: https://gist.github.com/alaycock/0fa4391cde36c5cb0a9f23fbaf09fe0f Play Music track information: https://gist.github.com/alaycock/3de5337a95b501ea7bbde5209a23e53d

All Play Music tracks have the prefix A0DvPDnows, which indicates to me that the ID is encoded, not encrypted, and there's probably a way to reverse it, but I don't know enough about that sort of thing to actually do it. I tried the basic stuff like base64 but didn't have any luck. Maybe not though, I don't know a ton about hashing/encryption.

The last thing I found was a reference to a specific Sonos endpoint for Play Music API in the Sonos log (/Users/username/Library/Logs/Sonos on OSX). https://mclients.googleapis.com/music/sonos/wsf/smapi#getLastUpdate. It appears that the Google Music API has a specific API for Play Music, which would mean that reversing the IDs might be pretty tricky because they could be using a secret key on the Play Music servers to encode the ID.

I also saw the line 2017/11/25 16:20:46:210 certval(1): (pass 1) local cert validation failed (27) for mclients.googleapis.com in the logs from the day I was attempting to intercept the connection, which seems to line up with my theory of local certificate pinning.

If anyone else wants to try moving forward in the direction I've been going, I'd say the next step would be trying to find the local certificate that the Sonos client is using for Google, and swap it out with the cert provided by Charles Proxy, which would allow you to MITM the connection to the Play Music server and figure out how that API works.

dear alaycock,

thank you for this informations and for this post. I try to find a solution for "thumb downs" for google music and for sonos. I cant find nothing about sonos http commands for thumb downs and google provide no infos for "last listening" songs. I saw that with "tasker" on android smartphones are working: https://forum.joaoapps.com/index.php?resources/press-a-button-on-an-existing-notification-thumbs-up-on-google-play-music.140/

But I need a solution for Sonos & thumb downs for google music. I hope you know a solution.

thank you

jutta