Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kingosticks change: Obtain spclient access token using login5 instead of keymaster (Fixes librespot-org#1179) #1220

Closed
wants to merge 7 commits into from

Conversation

Gerrelt
Copy link

@Gerrelt Gerrelt commented Nov 13, 2023

See issue ccomment:
#1179 (comment)

@kingosticks asked if somebody else wanted to create the pull-request.
I've added the changes to the latest dev branch of librespot, compiled and tested it on raspberry pi.
It seems to still solve the problem as addressed in issuee 1179, it's working fine.
But I just copied the changes from kingostick, so all the questions, and the honor ( :-) ) are for kingosticks!

This is my attempt at creating a pull request. I've only done it once before, so I hope it's done correctly.

@Gerrelt Gerrelt mentioned this pull request Nov 13, 2023
@roderickvd
Copy link
Member

Thanks for making the effort.

Let's first get to ticking the first two checkboxes:

  • Getting the build to succeed
  • Confirming the compatibility questions stated in Error 403 #1179

Overall I really like that this moves away from the Mercury keymaster and towards a HTTP endpoint. That's where this should go.

For bonus points you could look into also fixing the Mercury keymaster request. The solution probably lies in inspecting the traffic from the official client, and get the combination of HTTP headers, protobuf request, and keymaster ID precisely right per platform. The official clients do it differently between Windows, Linux, macOS, iOS and Android. When I was an active developer I reverse engineered everything but only tested Linux and macOS.

@roderickvd roderickvd added SpotifyAPI Interop b/w librespot and Spotify new api labels Nov 15, 2023
@roderickvd roderickvd linked an issue Nov 19, 2023 that may be closed by this pull request
@roderickvd
Copy link
Member

Good to see work on it again! Let's resolve those clippy warnings 👍

@Gerrelt
Copy link
Author

Gerrelt commented Dec 11, 2023

Hello Roderick,

I've solved the fmt and clippy warnings, could you review te change again? This librespot version, with the latest changes, was tested on a raspberry pi, and it seems to work fine.

@kingosticks
Copy link
Contributor

kingosticks commented Dec 11, 2023

The remaining checkbox blocking this is to confirm/deny the the iOS problems that were reported at #1179 (comment)

I don't have any iOS devices, I can't help.

@Gerrelt
Copy link
Author

Gerrelt commented Dec 11, 2023

OK, I will test that scenario.

@Gerrelt
Copy link
Author

Gerrelt commented Dec 11, 2023

I've did a test with an iPhone 12, with librespot still on a Raspberry Pi.

  1. connected iPhone, started a song, it started playing on the Pi
  2. skipped to middle of song
  3. skipped to next song
  4. skipped to nearly the end of the song, and let it play into the next song.
    This all went without problems.

Then I switched the Raspberry Pi off, and we saw the spotify connect device disappear on the iphone.
Started the Pi again, and connected the iPhone again.
We then could succesfully repeat the steps 1 - 4 again as described above.
So, seems to work ok?

@herrernst
Copy link
Contributor

Then I switched the Raspberry Pi off, and we saw the spotify connect device disappear on the iphone. Started the Pi again, and connected the iPhone again. We then could succesfully repeat the steps 1 - 4 again as described above. So, seems to work ok?

You don't have username and password hardcoded as librespot params, or do you? If not (i.e. if iPhone initiates login and that works), it's good. (Unfortunately I can't test because I don't have an active subscription.)

@Gerrelt
Copy link
Author

Gerrelt commented Dec 12, 2023

You don't have username and password hardcoded as librespot params, or do you?
If not (i.e. if iPhone initiates login and that works), it's good. (Unfortunately I can't test because I don't have an active subscription.)

No, I don't have the username and password hardcoded as params.
iPhone (or Android device) is logged in into Spotify, and then it connects to the librespot spotify connect device on the Pi.

@roderickvd
Copy link
Member

Great work. I’m not entirely on top and cannot test myself. Will you give me the go ahead if I can merge?

@Gerrelt
Copy link
Author

Gerrelt commented Dec 15, 2023

Yes, you can merge it.

@kingosticks
Copy link
Contributor

I never implemented the hash cash stuff for this. In what situation is that used? Have we missed a test case or is it just not required here?

break message.ok().to_owned();
}

if count < MAX_TRIES {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this would be improved to check for the error cases where a retry is not sensible:

enum LoginError {
    UNKNOWN_ERROR = 0;
    INVALID_CREDENTIALS = 1;
    BAD_REQUEST = 2;
    UNSUPPORTED_LOGIN_PROTOCOL = 3;
    TIMEOUT = 4;
    UNKNOWN_IDENTIFIER = 5;
    TOO_MANY_ATTEMPTS = 6;
    INVALID_PHONENUMBER = 7;
    TRY_AGAIN_LATER = 8;
}

Copy link
Contributor

@kingosticks kingosticks Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least, a retry in the face of BAD_REQUEST or a TOO_MANY_ATTEMPTS error response is a bit rude.

Copy link
Contributor

@kingosticks kingosticks Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple exponential backup would be nice too, ideally implemented in a reusable way. But could leave that to another day, I'm just trying to keep a record of what was left unfinished.

EDIT: Actually, there is a solid handling of the HTTP side of this in our HttpClient . Given this is protobuf over HTTP, it depends what Spotify do. i.e. do they reflect protobuf errors in the HTTP response? I don't think I ever looked into that. @roderickvd do you know?

response = self.auth_token_request(&login_request).await?;
} else {
return Err(Error::failed_precondition(format!(
"Unable to solve any of {MAX_TRIES} hash cash challenges"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As is, there is still no hash cash handling so this error message makes no sense.

warn!("Unable to get client token. Trying to continue without...");
}
let client_token = self.client_token().await?;
headers_mut.insert(CLIENT_TOKEN, HeaderValue::from_str(&client_token)?);
Copy link
Contributor

@kingosticks kingosticks Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes how we handle the failing case. The old code used to continue anyway, maybe that's still what we want. I don't remember why I thought I had to change it...

@kingosticks
Copy link
Contributor

I am sorry for my late review. I should have highlighted more clearly (and earlier) the bits I didn't finish. It obviously all works as is but it could be improved on.

.token_provider()
.get_token("playlist-read")
.await?;
let auth_token = self.auth_token().await?;
Copy link
Contributor

@kingosticks kingosticks Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also probably check the result of this and break the loop if it failed.

EDIT: Cancel that, I forgot what ? does. But maybe that's actually the wrong thing to do given "currently these endpoints seem to work fine without it"...

@roderickvd
Copy link
Member

bump if you could take a look at resolving @kingosticks's review comments? ❤️

@roderickvd
Copy link
Member

Am I right that this one can be closed in favour of #1246?

@hamishfagg
Copy link

hamishfagg commented May 16, 2024

FWIW I get the following errors when trying to use this branch now (when trying to play a track):

2024-05-16 09-55-11.596 [Error] (librespot_playback::player) Unable to load audio item: Error { kind: InvalidArgument, error: StatusCode(400) }
2024-05-16 09-55-11.597 [Error] (librespot_playback::player) Skipping to next track, unable to load track <SpotifyId("spotify:track:7mDKRYiqejoHzP7dQGxLys")>: ()
2024-05-16 09-55-11.796 [Error] (librespot_playback::player) Unable to load audio item: Error { kind: InvalidArgument, error: StatusCode(400) }

DaXcess added a commit to SpoticordMusic/librespot that referenced this pull request Jul 1, 2024
@roderickvd
Copy link
Member

Can we deprecate in favour of #1309?

@roderickvd
Copy link
Member

Could you comment on the status of this? Do we want it in yes/no? @kingosticks

@photovoltex
Copy link
Contributor

Hey just to buzz in. I'm currently trying to replace mercury with the dealer/websocket for the spirc. I got stuck on putting the connect state for some days.

The solution in the end was it to merge @kingosticks login5 impl, and use that token for both the dealer and internal api request. So it would be much appreciated if we can get the login5 auth merged at some point :D

@roderickvd
Copy link
Member

You working on the dealer? Hero! Open a draft PR if you want some community input.

So you're saying this PR is good to merge?

@photovoltex
Copy link
Contributor

the general implementation of login5 is required to continue integrating the dealer, yeah

in regards to the dealer integration, i still have some stuff to do before i can make a pr, for now i need to have a consistent way to setup the dealer, currently it's still inconsistent putting the connect state and retrieving the cluster update. go-librespot seems to achieve such thing, but for my current impl it still seems to have an inconsistent behavior.

like for example a cold start works, restarting librespot right after, will not put the connect state successfully (the request is successful, but the dealer will not receive cluster updates)... until i have figured that out i will probably just experiment locally

@roderickvd
Copy link
Member

Thanks for the update.

More specifically, my question was about the readiness of this PR. Can it be merged yes/no?

@photovoltex
Copy link
Contributor

Oh sry misunderstood that.

Well the points @kingosticks pointed out are still valid. It would probably not be the end of the world to merge these, but fixing them beforehand is probably preferable. Besides with conflicts we can't even merge it, even if we would want to.

Because this PR seems pretty abandon maybe closing it and reopening might be the better way to go. I could take the changes again and clean them up like @kingosticks requested? Unless he wants to do that himself.

@photovoltex
Copy link
Contributor

sooo... as it seems login5 isn't required for the dealer. The old impl (mercury) and new impl (dealer) are conflicting with each other and sometime the new impl wins but most of the time the old wins.. retrieving a token via login5 probably delays the new method enough so that it has a higher change of winning... pretty silly reasoning, but looking back on it, it make a lot of sense

just wanted to clear up my misunderstanding

even tho i don't necessary need these changes anymore, my offer in regards to bringing them into dev still applies

@kingosticks
Copy link
Contributor

I sorted the conflicts in https://github.com/kingosticks/librespot/tree/login5-again

@photovoltex if you're intersted in taking it from half-baked proof-of-concept into something more mergable (e.g. sort the error handling) I would love that. Otherwise, given we don't seem to actually NEED it for a 0.5 release, I'll get to it at some point...

@roderickvd
Copy link
Member

OK, so I'll wait until someone gives me the sign and won't target it for 0.5.

@photovoltex
Copy link
Contributor

Already did some adjustments here https://github.com/photovoltex/librespot/tree/login5-again, if anyone wants to take a look. I moved the impl into an own file and manger. And additionally handled the errors and solved the response challenges for the retries.

I move the login5 code in an own file because it is only used by the spclient and doesn't use any endpoint of it.

I would like to do the same for the client-token retrieval and other methods that are unrelated to the spclient, but thats probably a point for another day.

I may open a PR in the next days. Unless someone takes the branch and opens a PR before me

@roderickvd
Copy link
Member

Continuing in #1344.

@roderickvd roderickvd closed this Sep 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new api SpotifyAPI Interop b/w librespot and Spotify
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error 403
6 participants