-
Notifications
You must be signed in to change notification settings - Fork 639
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
fix: streaming on Android devices #1403
Conversation
Hey @SilverMira, Even tho mocking linux on android wouldn't be so far off from the reality :D, thanks in advance |
Hello @photovoltex, getting the token from login5 is not an issue, I can confirm The problem happens when we try to connect to a Session using the obtained credentials, all 6 APs will error with TryAnotherAP. Here's how I've been using the login5 credentials, maybe I'm not using the credentials correctly (not sure either since there isn't much example of how to use the obtained credentials). let session_config = SessionConfig::default();
let session = Session::new(session_config, None);
log::trace!("Authing with login5");
let (token, auth_data) = match session.login5().login(id.clone(), password).await {
Ok(result) => result,
Err(err) => {
log::error!("Error while login5 auth: {err:?}");
Err(err)?
}
};
log::trace!("Login5 success: token={token:?} auth_data={auth_data:?}");
let credentials = Credentials {
username: Some(id),
auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS,
auth_data,
};
// This session.connect always fails with TryAnotherAP
if let Err(err) = session.connect(credentials, false).await {
log::error!("Error connecting to librespot: {err:?}");
Err(err)?;
}; What I'd found is that during The trace when running the above snippet with no other modifications than [2024-11-26T01:52:35Z DEBUG librespot_core::login5] Received 1 challenges, solving...
[2024-11-26T01:52:35Z DEBUG librespot_core::login5] Solving hashcash took 0s 1759000ns
[2024-11-26T01:52:35Z DEBUG librespot_core::http_client] Requesting https://login5.spotify.com/v3/login
[2024-11-26T01:52:35Z TRACE rust_lib_frb_base::api::simple] Login5 success: token=Token { access_token: "---", expires_in: 3600s, token_type: "Bearer", scopes: [], timestamp: Instant { t: 1622.7673751s } } auth_data=[---]
[2024-11-26T01:52:35Z DEBUG librespot::component] new ApResolver
[2024-11-26T01:52:35Z DEBUG librespot_core::http_client] Requesting https://apresolve.spotify.com/?type=accesspoint&type=dealer&type=spclient
[2024-11-26T01:52:35Z DEBUG rustls::client::hs] No cached session for DnsName("apresolve.spotify.com")
[2024-11-26T01:52:35Z DEBUG rustls::client::hs] Not resuming any session
[2024-11-26T01:52:35Z TRACE rustls::client::hs] Sending ClientHello Message {...}
[2024-11-26T01:52:36Z TRACE rustls::client::hs] We got ServerHello ServerHelloPayload {...}
[2024-11-26T01:52:36Z DEBUG rustls::client::hs] Using ciphersuite TLS13_AES_256_GCM_SHA384
[2024-11-26T01:52:36Z DEBUG rustls::client::tls13] Not resuming
[2024-11-26T01:52:36Z TRACE rustls::client::client_conn] EarlyData rejected
[2024-11-26T01:52:36Z TRACE rustls::conn] Dropping CCS
[2024-11-26T01:52:36Z DEBUG rustls::client::tls13] TLS1.3 encrypted extensions: [Protocols([ProtocolName(6832)])]
[2024-11-26T01:52:36Z DEBUG rustls::client::hs] ALPN protocol is Some(b"h2")
[2024-11-26T01:52:36Z TRACE rustls::client::tls13] Server cert is CertificateChain(...)
[2024-11-26T01:52:36Z INFO librespot_core::session] Connecting to AP "ap-gae2.spotify.com:4070"
[2024-11-26T01:52:36Z DEBUG librespot_core::connection] Authenticating with AP using AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS
[2024-11-26T01:52:36Z WARN librespot_core::session] Instructed to try another access point...
[2024-11-26T01:52:36Z INFO librespot_core::session] Connecting to AP "ap-gae2.spotify.com:443"
[2024-11-26T01:52:36Z DEBUG librespot_core::connection] Authenticating with AP using AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS
[2024-11-26T01:52:36Z WARN librespot_core::session] Instructed to try another access point...
[2024-11-26T01:52:36Z INFO librespot_core::session] Connecting to AP "ap-gae2.spotify.com:80"
[2024-11-26T01:52:36Z DEBUG librespot_core::connection] Authenticating with AP using AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS
[2024-11-26T01:52:37Z WARN librespot_core::session] Instructed to try another access point...
[2024-11-26T01:52:37Z INFO librespot_core::session] Connecting to AP "ap-guc3.spotify.com:4070"
[2024-11-26T01:52:37Z DEBUG librespot_core::connection] Authenticating with AP using AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS
[2024-11-26T01:52:38Z WARN librespot_core::session] Instructed to try another access point...
[2024-11-26T01:52:38Z INFO librespot_core::session] Connecting to AP "ap-gue1.spotify.com:443"
[2024-11-26T01:52:38Z DEBUG librespot_core::connection] Authenticating with AP using AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS
[2024-11-26T01:52:38Z WARN librespot_core::session] Instructed to try another access point...
[2024-11-26T01:52:38Z INFO librespot_core::session] Connecting to AP "ap-gew4.spotify.com:80"
[2024-11-26T01:52:39Z DEBUG librespot_core::connection] Authenticating with AP using AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS
[2024-11-26T01:52:39Z ERROR librespot_core::session] Tried too many access points
[2024-11-26T01:52:39Z ERROR rust_lib_frb_base::api::simple] Error connecting to librespot: Error { kind: PermissionDenied, error: LoginFailed(TryAnotherAP) } Also, I'm not sure why the |
Oh thanks for the clarification. Looking back i never tried to use the stored credentials from the login5 login to connect. What a bummer that it doesn't work. Could you maybe document in code why we match both os android and linux for the linux platform, so it doesn't feel like a random decision? Later down the line with the dealer and a different key retrieval for the tracks, we might even be able to completely avoid the handshake in the future on mobile. But for now we might just have to use a workaround. |
Can you connect to the access point if you do it the normal way, with an access token as per the docs, for example. I recall login5 works very differently on Android and I'm not sure how much of that got implemented, I didn't follow it |
@photovoltex sure, will add a few lines there. @kingosticks Yes, as per findings in this PR's description, I've tried with access token obtained via oauth with Keymaster's client ID (ie: the desktop oauth way), that too will error with TryAnotherAP during Session::connect. And access token obtained via oauth with Android's client ID, we'd get the same TryAnotherAP as well. After getting past TryAnotherAP with 080aa3f, we'd still need b488b8f to get Keymaster's access token to work for playback on Android |
It appears that a combination of `Platform::PLATFORM_ANDROID_ARM` and connecting with `Credentials::with_access_token` causes all AP to error TryAnotherAP
If we are trying to get an access token from login5 using stored credentials, ie: from oauth flow, we should use the oauth's client ID, this matches with the semantics as described in `Login5Manager::auth_token`
52848f5
to
62c1eba
Compare
What about with a token from https://open.spotify.com/get_access_token or https://developer.spotify.com/documentation/web-playback-sdk/tutorials/getting-starte Does mobile actually still use Mercury (an access point connection)? |
I have just checked for access_tokens from both of them, TryAnotherAP as well.
The go example is essentially |
From what i saw, I think it doesn't. But without the dealer and a different way to get the encryption keys for the tracks we still rely on it. At least thats my current understanding. |
That certainly would explain things. In which case we either pretend not to be Android, or leave it broken. And then re-evaluate once all the non-Mercury pieces are in. |
It would probably be in favor of people using librespot on android to have a working version to stream audio. So I would say bringing these changes in is a good thing, as long as we document the decisions for it^^ |
I agree. Would you write a CHANGELOG message before we merge this? |
Has a merge conflict now that I've merged your other change before 😅 |
No issues, I've just resolved it, kinda expected the changelog to have a conflict when I was writing for both the PRs |
Fixes: #1399
I have mostly figured out the nuances and rules to get streaming on Android to work again.
client_hello
that hasplatform == Platform::PLATFORM_ANDROID_ARM
, it does not matter what credentials we send to the AP, all APs will error withTryAnotherAP
, tested true for credentials obtained viastreaming
scope (Credentials::with_access_token)streaming
scope (Credentials::with_access_token)client_hello
, additional spoofing isn't required,authenticate
can still present ourselves asAndroid
OS after that. With just this, playback from credentials obtained fromLogin5Manager::login
is already working.Login5Manager::login5_request
must use the same Keymaster client ID to get an api access token forTrack::get
. Prior to this PR, on platforms such as Android/IOS where we have specific client IDs, this scenario always fails withInvalidCredentials
.arm64
when matching againststd::env::consts::ARCH
If this is merged, to get playback on Android working, there's 2 ways.
Session
, manually overrideSessionConfig::client_id
to be Keymaster's client ID, this is the same flow as using librespot on desktop platforms. (example)stored_credentials
returned fromLogin5Manager::login
as credentials forSession::connect
(example)Additionally, from my testing, it appears that although we can initiate an oauth flow using Android client ID with scope streaming, I have never gotten it to work with Session::connect,
PremiumAccountRequired
error always appears duringSession::connect
, though in the first place, I couldn't find any references that mentioned this is possible anyways.