simon-weber / gmusicapi

An unofficial client library for Google Music.
https://unofficial-google-music-api.readthedocs.io
BSD 3-Clause "New" or "Revised" License
2.48k stars 257 forks source link

Update on the "thumbs up" playlist #633

Closed justin-gerhardt closed 5 years ago

justin-gerhardt commented 5 years ago

As has been noted in previous issues I have noticed that MobileClient::get_all_playlists does not return the auto-generated playlists. The current workaround seems to be to look for thumbed up tracks in library but this omits songs that are rated but not added.

I did a bit of digging in the play music web application and found a request to https://play.google.com/music/services/getephemthumbsup. I played with the parameters a bit and found you can get a json response describing all thumbs up'ed tracks (added to the library or not) by sending a post request with no query parameters or body and an Authorization: Bearer header set to the oauth access token from the MobileClient.

simon-weber commented 5 years ago

That's interesting. I'm pretty sure I tested cross-api auth in the past and only got 403s.

I wonder if Google's mobile clients use that endpoint, or whether it's a more general thing that auth works for other endpoints now.

thebigmunch commented 5 years ago

I wonder if Google's mobile clients use that endpoint, or whether it's a more general thing that auth works for other endpoints now.

It doesn't. #560 and my experience say that the ephemeral/top mobile endpoint returns thumbs up songs for mobile clients. I believe it's returns the same as https://play.google.com/music/services/getephemthumbsup, but I can't remember off the top of my head if it was exactly the same. Would have to retest; I should really keep better notes ^_^

I'm definitely going to test out using web calls with mobile auth, though, as I've been chasing down reliability in moving or adding multiple songs (to anywhere but the end) in playlists and the plentriesbatch endpoint is flaky on Google's end. For example, a simple test Google's client fails is if you move 2 songs from the end of a playlist to the beginning, one song will end up at the end again once the client syncs. I believe this happens for any mutations that rely on each other in the same batch call. I have yet to try with the plentries endpoint which exists, but, last time I was playing with playlists in Google's web client, it seemed reliable.

If you don't test it before I do, I'll report back on cross-API auth.

thebigmunch commented 5 years ago

Ah, right. I remember better after my morning warmup. ephemeral/top returns store thumbs up songs (may have done something different at one point, but I haven't seen anything but thumbs up songs in years). https://play.google.com/music/services/getephemthumbsup returns the actual playlist. And I think I remember seeing calls for the last added and free & purchased playlists at some point, but I could be wrong.

justin-gerhardt commented 5 years ago

"Recently Added" and "Free & Purchased" appears to be generated from https://play.google.com/music/services/streamingloadalltracks?format=jsarray. Get or post requests work and mobile oauth tokens are accepted. Unfortunately, the output format is an undocumented js array embedded in html. Requesting json just 400's

thebigmunch commented 5 years ago

gmusicapi already has support for Google's jsarray in utils because of its previous Webclient functionality.

thebigmunch commented 5 years ago

So, you can definitely get a 200 response. But all I got when poking at it quick is an empty track list.

@justin-gerhardt You can set the format param to json to get back regular JSON.

thebigmunch commented 5 years ago

So, I was getting an empty track list because https://play.google.com/music/services/getephemthumbsup only returns non-library thumbs up songs (just like ephemeral/top), not the playlist itself. Seems it works the same way as mobile for library thumbs up songs.

@justin-gerhardt Can you confirm this with using format=json? I only say to use json because it is easier to read the response.

TL/DR This specific call doesn't add anything for the mobile client. But the fact that you can seem to call the web API with a Mobile token (only tested with Bearer) seems useful.

justin-gerhardt commented 5 years ago

I can confirm that https://play.google.com/music/services/getephemthumbsup is producing a json response (with no format parameter) that lists all of the "store" songs that I've thumbed up. It omits songs that have been uploaded. This is the same behavior as you have described for the mobile ephemeral/top url. I was mainly interested in it as it permits getting thumbed up store songs that have not been added to the library (the primary way I use play music). I didn't realize this seems to be already implemented under the name "promoted songs". In particular the description for MobileClient::get_promoted_songs makes it sound more like the algorithmic "I'm feeling lucky" rather than the thumbed up playlist.

The https://play.google.com/music/services/streamingloadalltracks url with a format parameter set to json produces a 400 error. It appears to only function with format set to jsarray. That said it's only notable for completeness, the information it produces seems to be no greater than the existing MobileClient::get_all_songs method.

Overall, your conclusion seems to be correct, these url's add nothing to the current implementation but they are interesting for auth reasons.

thebigmunch commented 5 years ago

The https://play.google.com/music/services/streamingloadalltracks url with a format parameter set to json produces a 400 error. It appears to only function with format set to jsarray. That said it's only notable for completeness, the information it produces seems to be no greater than the existing

I'm not sure how you were doing it, but it certainly returned properly for me with the format param set to json. Using requests, I simply set params={'format': 'json'} in requests.post. Using the Composer in Fiddler, it was just a matter of changing it in the URL.

I didn't realize this seems to be already implemented under the name "promoted songs".

Nobody is quite sure. Like Simon, I seem to recall it doing something other than just returning store thumbs up songs at some point in the past. Given its name vs the naming in the Web API, that's probably correct. I haven't seen it do anything but return thumbs up songs with my own accounts for years now. That doesn't necessarily mean it doesn't still. But, knowing Google, they probably changed what they were doing with it and didn't bother to use a better endpoint like ephemeral/thumbsup (which doesn't seem to exist and definitely isn't used).

Overall, your conclusion seems to be correct, these url's add nothing to the current implementation but they are interesting for auth reasons.

Probably the most useful web API call for the Mobileclient would be for editing song metadata; Google removed that capability from the Mobile API years ago.

justin-gerhardt commented 5 years ago

For the streamingloadalltracks I executed curl -i -X POST -H "Authorization:Bearer <MobileClient Token>" "https://play.google.com/music/services/streamingloadalltracks?format=json" which produced

<HTML>
<HEAD>
<TITLE>Bad Request</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Bad Request</H1>
<H2>Error 400</H2>
</BODY>
</HTML>

changing format to jsarray worked fine.

As for metadata modification a cursory look shows that modification is done via a post to https://play.google.com/music/services/modifytracks. Using a MobileClient token works. A bit of manipulation shows no query parameters are needed. The request body for this particular example was

[["ssmpbh3cx1rx",1],[[["2127ff23-94c2-3bc3-b6a8-8e1a0dae63a1","Stronger123",null,null,null,null,null,null,null,null,null,null,null,null,null,0,null,0,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[],null,null,true,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[]]]]]

"Stronger123" was the new title, all other parameters were unaltered. The response was {"timestampMillis":1545949483626} I'll look at making a more complete example in a bit.

Edit: A somewhat more complete example [["fdkt4zz89zhl",1],[[["d12c5beb-0873-315c-b06f-87f970406db7","Name",null,"Artist","Album","Album Artist",null,null,null,null,"Composer","Genre",null,null,"8","9","6","7","2888",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1,[],null,null,true,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[]]]]]

where the parameters are as named. 8 is the track number. 9 is the total number of tracks. 6 is the disc number. 7 is the total number of disks. 2888 is the year. I set the track to explicit which is the probably the 1 (unsetting yields 2). When resetting to suggestions the parameter to the right of name became a url for song art. The first segment [["fdkt4zz89zhl",1] seems to change every request and editing a track then repeating an old request returns a 200 but doesn't seem to have any effect. My guess is that it some kind of random nonce.

thebigmunch commented 5 years ago

We were talking about the streamingloadalltracks call...

I haven't looked that call to see if there is a format param for it or not. Not at my computer to test with curl and the thumbs up call, but that works just fine as I said with requests and Fiddler.

Also, the metadata changing is already figured out and implemented for the Webclient in gmusicapi. All that would have to be done is making it calls let with the Mobileclient.

justin-gerhardt commented 5 years ago

Excellent, already implemented features are the best features.

thebigmunch commented 5 years ago

Double fun fact, you can also use a bearer token from the Music Manager side of things to access the web calls.

justin-gerhardt commented 5 years ago

Can a mobile token access the Music Manager api?

thebigmunch commented 5 years ago

Can a mobile token access the Music Manager api?

Nope. No holy grail there it seems. Each scope appears to be limited to specific Google client IDs, so no cross-calling there.

justin-gerhardt commented 5 years ago

Do you mean the api errors or the scope can't be requested? I just tried using the mobile client creds to request both https://www.googleapis.com/auth/musicmanager and https://www.googleapis.com/auth/skyjam scopes and that worked. I haven't tested calling the api yet though.

thebigmunch commented 5 years ago

You can get a token. But cross-API calls will fail with a 403.