sp-playcount-librespot icon indicating copy to clipboard operation
sp-playcount-librespot copied to clipboard

Unable to get playcounts Error 502

Open phuketbinaryt opened this issue 1 year ago • 51 comments

Since today I am unable to get the playcounts for any track. Only getting the folling error:

xyz.gianlu.librespot.mercury.MercuryClient$MercuryException: status: 502 at xyz.gianlu.librespot.mercury.MercuryClient.sendSync(MercuryClient.java:83) at xyz.gianlu.librespot.handler.PlayCountHandler.handle(PlayCountHandler.java:57) at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77) at jdk.httpserver/sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82) at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80) at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:848) at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77) at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:817) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:829)

Did something change on Spotify's side?

phuketbinaryt avatar Jun 19 '23 16:06 phuketbinaryt

I recently have been unable to access playcounts aswell, but I am having the same issue as #11 although this has not happened before. I am assuming this might be something on Spotify's side too

Rchrdlss3 avatar Jun 19 '23 16:06 Rchrdlss3

Same issue for me 😔

Seeing they have some outages on downdetector. Hopefully that’s all it is.

jpgrovy avatar Jun 19 '23 19:06 jpgrovy

I don’t see playcounts being returned even in Spotify’s own web anymore either. The counts always showed in the logs of the api call. Now they aren’t there.

jpgrovy avatar Jun 19 '23 19:06 jpgrovy

I am getting this error

{"success": false, "data": "An unknown error has occurred; logged to console"}

http://MYIPADDRESS:8080/albumPlayCount?albumid=XXXXXX

and this error in the console

2023-06-19 21:26:33,101 INFO ApResolver:66 - Loaded aps into pool: {accesspoint=[ap-gew4.spotify.com:4070, ap-gew4.spotify.com:443, ap-gew4.spotify.com:80, ap-guc3.spotify.com:4070, ap-gue1.spotify.com:443, ap-gew1.spotify.com:80], dealer=[gew4-dealer.spotify.com:443, guc3-dealer.spotify.com:443, gue1-dealer.spotify.com:443, gew1-dealer.spotify.com:443], spclient=[gew4-spclient.spotify.com:443, guc3-spclient.spotify.com:443, gue1-spclient.spotify.com:443, gew1-spclient.spotify.com:443]} 2023-06-19 21:26:33,366 INFO Session:94 - Created new session! {deviceId: c70f928698cc099e39bddfd469f7de38e3b805db, ap: ap-guc3.spotify.com:4070} 2023-06-19 21:26:34,351 INFO Session:223 - Connected successfully! 2023-06-19 21:26:34,647 INFO Session:242 - Authenticated as woklxlz1f9wkuu6j9fk3x6q54! 2023-06-19 21:26:34,647 INFO Session:716 - Skipping SecretBlock 2023-06-19 21:26:34,647 INFO Session:694 - Received LicenseVersion: 0 2023-06-19 21:26:34,647 INFO Session:684 - Received CountryCode: NL [main] INFO org.cache2k.core.Cache2kCoreProviderImpl - cache2k starting. version=1.2.4.Final 2023-06-19 21:26:34,694 INFO Session:716 - Skipping UnknownData_AllZeros 2023-06-19 21:26:34,694 INFO Session:716 - Skipping LegacyWelcome Listening on port 8080 2023-06-19 21:26:35,085 INFO Session:651 - Skipping unknown command {cmd: 0x75, payload: 000000} xyz.gianlu.librespot.mercury.MercuryClient$MercuryException: status: 502 at xyz.gianlu.librespot.mercury.MercuryClient.sendSync(MercuryClient.java:83) at xyz.gianlu.librespot.handler.PlayCountHandler.handle(PlayCountHandler.java:57) at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source) at sun.net.httpserver.AuthFilter.doFilter(Unknown Source) at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source) at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(Unknown Source) at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source) at sun.net.httpserver.ServerImpl$Exchange.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)

surfmore avatar Jun 19 '23 19:06 surfmore

I recently have been unable to access playcounts aswell, but I am having the same issue as #11 although this has not happened before. I am assuming this might be something on Spotify's side too

This is related to the country of the account. Spotify seems to have changed the accounts to the country they were created. I changed back to the country where librespot is running, but didnt fix the problem. Now, it is happening with the error I just posted above.

surfmore avatar Jun 19 '23 19:06 surfmore

This issue is also only happening for the artistInfo and albumPlayCount end points . artistInsights still works.

jpgrovy avatar Jun 19 '23 20:06 jpgrovy

I think Spotify finally killed support for this mercury endpoint. You can actually replicate all these calls over REST to the sp client. For example https://gae2-spclient.spotify.com/artist/v1/{artistId}/desktop currently returns 502 with response:

{
  "error": {
    "status": 502,
    "message": "Request was not transferred"
  }
}

christosk92 avatar Jun 20 '23 10:06 christosk92

I hope not 😔 - I wonder if there is another way to retrieve playcounts then.

jpgrovy avatar Jun 20 '23 12:06 jpgrovy

I hope not 😔 - I wonder if there is another way to retrieve playcounts then.

well you can always find the appropriate album of a track, and then fetch the album (using the graphql endpoint)

https://api-partner.spotify.com/pathfinder/v1/query?operationName=getAlbum&variables={"uri":"spotify:album:{id}","locale":"","offset":0,"limit":50}&extensions={"persistedQuery":{"version":1,"sha256Hash":"46ae954ef2d2fe7732b4b2b4022157b2e18b7ea84f70591ceb164e4de1b5d5d3

returns

{
   "data":{
      "albumUnion":{
         "__typename":"Album",
         "uri":"spotify:album:...",
         "name":"...",
         "artists":{
            ....
         },
         "coverArt":{
         ...
         },
         ...
         "label":"...",
         "copyright":{...
         },
         "courtesyLine":"",
         "saved":false,
         "sharingInfo":{
            "shareUrl":"",
            "shareId":""
         },
         "tracks":{
            "totalCount":1,
            "items":[
               {
                  "uid":"edda05c86f012f7409cc",
                  "track":{
                     "saved":false,
                     "uri":"spotify:track:...",
                     "name":"...",
                     "playcount":"50288750",
                     "discNumber":1,
                     "trackNumber":1,
                     "contentRating":{
                        "label":"NONE"
                     },
                     "relinkingInformation":null,
                     "duration":{
                        "totalMilliseconds":192497
                     },
                     "playability":{
                        "playable":true
                     },
                     "artists":{
                       ....
                     }
                  }
               }
            ]
         },
         "moreAlbumsByArtist":{
        ....
      }
   },
   "extensions":{
      
   }
}

christosk92 avatar Jun 20 '23 12:06 christosk92

I hope not 😔 - I wonder if there is another way to retrieve playcounts then.

well you can always find the appropriate album of a track, and then fetch the album (using the graphql endpoint)

https://api-partner.spotify.com/pathfinder/v1/query?operationName=getAlbum&variables={"uri":"spotify:album:{id}","locale":"","offset":0,"limit":50}&extensions={"persistedQuery":{"version":1,"sha256Hash":"46ae954ef2d2fe7732b4b2b4022157b2e18b7ea84f70591ceb164e4de1b5d5d3

returns

{
   "data":{
      "albumUnion":{
         "__typename":"Album",
         "uri":"spotify:album:...",
         "name":"...",
         "artists":{
            ....
         },
         "coverArt":{
         ...
         },
         ...
         "label":"...",
         "copyright":{...
         },
         "courtesyLine":"",
         "saved":false,
         "sharingInfo":{
            "shareUrl":"",
            "shareId":""
         },
         "tracks":{
            "totalCount":1,
            "items":[
               {
                  "uid":"edda05c86f012f7409cc",
                  "track":{
                     "saved":false,
                     "uri":"spotify:track:...",
                     "name":"...",
                     "playcount":"50288750",
                     "discNumber":1,
                     "trackNumber":1,
                     "contentRating":{
                        "label":"NONE"
                     },
                     "relinkingInformation":null,
                     "duration":{
                        "totalMilliseconds":192497
                     },
                     "playability":{
                        "playable":true
                     },
                     "artists":{
                       ....
                     }
                  }
               }
            ]
         },
         "moreAlbumsByArtist":{
        ....
      }
   },
   "extensions":{
      
   }
}

But is there a way using graph to get the complete list of Albums and Singles for an artistID?

jpgrovy avatar Jun 20 '23 12:06 jpgrovy

@jpgrovy Yes but it's paged, which is really annoying. It's

https://api-partner.spotify.com/pathfinder/v1/query?operationName=queryArtistDiscographyAll&variables={"uri":"spotify:artist:{id}","offset":0,"limit":100}&extensions={"persistedQuery":{"version":1,"sha256Hash":"35a699e12a728c1a02f5bf67121a50f87341e65054e13126c03b7697fbd26692"}}

christosk92 avatar Jun 20 '23 13:06 christosk92

How / where do you use this link? How does it replace the mercury call? If i simply check the link you share it tells "No token provided"

SatyF avatar Jun 20 '23 13:06 SatyF

That would be for you to figure out :)

Hint: use the mercury client to get a bearer and then put it in the headers

christosk92 avatar Jun 20 '23 14:06 christosk92

That won't be for me then I suppose, I always struggle to cope with clients/protocols, etc, I have no idea what your hint means tbh

SatyF avatar Jun 20 '23 14:06 SatyF

Oh I thought you were a maintainer of this project.. Well first you'd need a bearer token to authenticate with this client. Since this is an internal API, you can't just fetch one using the official method (Spotify developer console), as that will result in an 403 (forbid).

This project I think has the ability to fetch a good token over mercury (hm://keymaster/token/authenticated?scope...), which can then be used to authenticate properly and fetch the resource I provided.

It would require quite some changes to the project, unfortunately I am not actively involved in this project so I wont do it. But the developer may of course.

christosk92 avatar Jun 20 '23 15:06 christosk92

@jpgrovy Yes but it's paged, which is really annoying. It's

https://api-partner.spotify.com/pathfinder/v1/query?operationName=queryArtistDiscographyAll&variables={"uri":"spotify:artist:{id}","offset":0,"limit":100}&extensions={"persistedQuery":{"version":1,"sha256Hash":"35a699e12a728c1a02f5bf67121a50f87341e65054e13126c03b7697fbd26692"}}

I get “RBAC: Access Denied” when trying to make the api-partner calls.

Side note… anyone want to be awesome and fix the librespot playcount code? 🙏🙏🙏

jpgrovy avatar Jun 20 '23 16:06 jpgrovy

Yep, they most likely killed the endpoint: librespot-org/librespot-java#678

Since it seems like Spotify doesn't use Hermes in their clients anymore, I think the only way forward with this project is to use Spotify's private API, so this repo may be archived once I figure things out.

entriphy avatar Jun 20 '23 17:06 entriphy

Well... hermes is just their internal router. As I mentioned earlier, all hermes endpoints can be translated to their HTTPs counterpart by just replacing hm:// with the spclient url.

I think hermes will still be supported, since a lot of their internal services may still use them. You can fetch an access token using : hm://keymaster/token/authenticated?scope=user-read-private,user-read-email,playlist-modify-public,ugc-image-upload,playlist-read-private,playlist-read-collaborative,playlist-read&client_id=65b708073fc0480ea92a077233ca87bd&device_id= Which you can then use to access the private api (partner + spclient) . The public (web api) endpoints also work

christosk92 avatar Jun 20 '23 18:06 christosk92

Yep, they most likely killed the endpoint: librespot-org#678

Since it seems like Spotify doesn't use Hermes in their clients anymore, I think the only way forward with this project is to use Spotify's private API, so this repo may be archived once I figure things out.

To re-do this repo maybe the easiest is to restart from the last version of librespot-java and import the few playcount / artist info handlers into it as the former now manages the Web API, has a token provider with the keymaster and all. In a first step it wouldn't be needed to stripe the unneeded content or anything.

SatyF avatar Jun 20 '23 19:06 SatyF

Well... hermes is just their internal router. As I mentioned earlier, all hermes endpoints can be translated to their HTTPs counterpart by just replacing hm:// with the spclient url.

Ah, I haven't worked on this project in a while and forgot the terminology. I meant to say that their clients switched to using the GraphQL API for retrieving stuff like track play counts in an album. Since it seems that all endpoints that this project uses have become useless (albumPlayCount and artistInfo return status code 502, artistAbout and artistInsights just fills in 0 for all relevant values), the GraphQL API seems to be the only replacement for those endpoints. The access token for the GraphQL endpoints needed don't require Mercury or authentication either (the token can be retrieved through https://open.spotify.com/get_access_token?reason=transport&productType=web_player).

...which at that point I'd essentially be making a CORS proxy for the GraphQL API. Seems like a cheap solution, though the main issue is that the GraphQL API is quite terrible to work with, so I'd have to look into that further :p

entriphy avatar Jun 20 '23 22:06 entriphy

Yes the graphql api is terrible to work with, and I do not understand why Spotify did not just build on the web-api, which has a good schema.

Anyways, I was also able to implement the oauth protocol using PKCE, and reverse engineer their persistent login mechanism. Which still uses the actual TCP connection some parts.

Mainly, after the oauth callback, you get an access token, which you can then use to authenticate using the old method (over TCP). Then in the APWelcome message, you have some re-usable credentials which are then posted in the body of https://login5.spotify.com/v3/login

In pseudo code this will look like

var webResponse = openBrowserAndCatchRedirectToken();
var accessToken = webResponse.accessToken;
var username = webResponse.username;

var credentials = new LoginCredentials
{
    Typ = AUTHENTICATION_SPOTIFY_TOKEN,
    Username = username,
    AuthData = ByteString.CopyFromUtf8(accessToken)
}
var apwelcome = performAuthOverTcp(credentials);

var reusablecredentials = apwelcome.reusable_auth_credentials;


Next in another login session: POST https://login5.spotify.com/v3/login the body is a protobuf with:

message LoginRequest {
    .spotify.login5.v3.ClientInfo client_info = 1;
    .spotify.login5.v3.credentials.StoredCredential stored_credential = 100;
}


christosk92 avatar Jun 21 '23 03:06 christosk92

Yes the graphql api is terrible to work with, and I do not understand why Spotify did not just build on the web-api, which has a good schema.

Anyways, I was also able to implement the oauth protocol using PKCE, and reverse engineer their persistent login mechanism. Which still uses the actual TCP connection some parts.

Mainly, after the oauth callback, you get an access token, which you can then use to authenticate using the old method (over TCP). Then in the APWelcome message, you have some re-usable credentials which are then posted in the body of https://login5.spotify.com/v3/login

In pseudo code this will look like

var webResponse = openBrowserAndCatchRedirectToken();
var accessToken = webResponse.accessToken;
var username = webResponse.username;

var credentials = new LoginCredentials
{
    Typ = AUTHENTICATION_SPOTIFY_TOKEN,
    Username = username,
    AuthData = ByteString.CopyFromUtf8(accessToken)
}
var apwelcome = performAuthOverTcp(credentials);

var reusablecredentials = apwelcome.reusable_auth_credentials;

Next in another login session: POST https://login5.spotify.com/v3/login the body is a protobuf with:

message LoginRequest {
    .spotify.login5.v3.ClientInfo client_info = 1;
    .spotify.login5.v3.credentials.StoredCredential stored_credential = 100;
}

You guys are too smart for me 😉 … does this mean it’s fixable to get working again?

jpgrovy avatar Jun 21 '23 04:06 jpgrovy

Hey @christosk92 ,

Do you know a replacement of artistInsights with their graphql API especially for top 50 cities ?

I don't find one.

pdenis avatar Jun 21 '23 07:06 pdenis

queryArtistOverview seems to be the only endpoint for retrieving artist data, which does include the top 5 cities.

I'm currently testing sp-playcount on api.t4ils.dev: https://api.t4ils.dev/getArtist?id=1g8HCTiMwBtFtpRR9JXAZR (uses queryArtistOverview) https://api.t4ils.dev/getAlbum?id=5K4YFkTizFoMOyN5Khfp7G (uses getAlbum) https://api.t4ils.dev/getAlbumTracks?id=5K4YFkTizFoMOyN5Khfp7G (uses queryAlbumTracks)

entriphy avatar Jun 21 '23 08:06 entriphy

Hi @entriphy, How did you find the definition of the GQL query you document here ?

louison avatar Jun 21 '23 08:06 louison

@entriphy : yes it seems old top 50 is no longer available via graphq :/

pdenis avatar Jun 21 '23 09:06 pdenis

@louison You can find those when looking in the source code of the webplayer. Those queries are then hashed to decrease request size.. Specifically, the web-player.{}.js file will be interesting. There's a line in the file:

 const t = await a.k.hash(e.body);

Which if you put a breakpoint there, you can expect the body

christosk92 avatar Jun 21 '23 09:06 christosk92

Hey @christosk92 ,

Do you know a replacement of artistInsights with their graphql API especially for top 50 cities ?

I don't find one.

Artist Insights is showing Top 50 cities and still works

jpgrovy avatar Jun 21 '23 12:06 jpgrovy

Hey @christosk92 , Do you know a replacement of artistInsights with their graphql API especially for top 50 cities ? I don't find one.

Artist Insights is showing Top 50 cities and still works

Yes I know, but I anticipate

pdenis avatar Jun 21 '23 12:06 pdenis

Is anyone working on an update / fix ?

jpgrovy avatar Jun 23 '23 01:06 jpgrovy