From c0d2add55b3a8f77ff8bdc0d1074adfe048328a6 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 25 Dec 2025 00:02:02 +0100 Subject: [PATCH] Use client id & secret for Spotify --- components/spotify/Shim.cpp | 12 +++ .../bell/main/platform/win32/MDNSService.cpp | 1 + .../spotify/cspot/include/CSpotContext.h | 2 + .../spotify/cspot/src/AccessKeyFetcher.cpp | 82 ++++++------------- 4 files changed, 41 insertions(+), 56 deletions(-) diff --git a/components/spotify/Shim.cpp b/components/spotify/Shim.cpp index 89d10a0f..261509ac 100644 --- a/components/spotify/Shim.cpp +++ b/components/spotify/Shim.cpp @@ -33,6 +33,16 @@ #include "nvs_utilities.h" #include "tools.h" +#if __has_include("client_info.h") +#include "client_info.h" +#endif + +#if !defined(CLIENT_ID) || !defined(CLIENT_SECRET) +#warning "missing Spotify's CLIENT_ID and/or CLIENT_SECRET (set env varibles or in client_info.h" +#define CLIENT_ID "" +#define CLIENT_SECRET "" +#endif + static class cspotPlayer *player; static const struct { @@ -365,6 +375,8 @@ void cspotPlayer::runTask() { ctx->session->connectWithRandomAp(); ctx->config.authData = ctx->session->authenticate(blob); + ctx->config.clientId = CLIENT_ID; + ctx->config.clientSecret = CLIENT_SECRET; // Auth successful if (ctx->config.authData.size() > 0) { diff --git a/components/spotify/cspot/bell/main/platform/win32/MDNSService.cpp b/components/spotify/cspot/bell/main/platform/win32/MDNSService.cpp index 62fa75f7..c57f353e 100644 --- a/components/spotify/cspot/bell/main/platform/win32/MDNSService.cpp +++ b/components/spotify/cspot/bell/main/platform/win32/MDNSService.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "BellLogger.h" #include "MDNSService.h" diff --git a/components/spotify/cspot/include/CSpotContext.h b/components/spotify/cspot/include/CSpotContext.h index d4fdde84..e7d4c70f 100644 --- a/components/spotify/cspot/include/CSpotContext.h +++ b/components/spotify/cspot/include/CSpotContext.h @@ -24,6 +24,8 @@ struct Context { AudioFormat audioFormat = AudioFormat::AudioFormat_OGG_VORBIS_160; std::string deviceId; std::string deviceName; + std::string clientId; + std::string clientSecret; std::vector authData; int volume; diff --git a/components/spotify/cspot/src/AccessKeyFetcher.cpp b/components/spotify/cspot/src/AccessKeyFetcher.cpp index 2d9783d4..28bea607 100644 --- a/components/spotify/cspot/src/AccessKeyFetcher.cpp +++ b/components/spotify/cspot/src/AccessKeyFetcher.cpp @@ -7,6 +7,7 @@ #include // for vector #include "BellLogger.h" // for AbstractLogger +#include "BellUtils.h" // for BELL_SLEEP_MS #include "CSpotContext.h" // for Context #include "HTTPClient.h" #include "Logger.h" // for CSPOT_LOG @@ -24,13 +25,8 @@ #include "nlohmann/json_fwd.hpp" // for json #endif -#include "protobuf/login5.pb.h" // for LoginRequest - using namespace cspot; -static std::string CLIENT_ID = - "65b708073fc0480ea92a077233ca87bd"; // Spotify web client's client id - static std::string SCOPES = "streaming,user-library-read,user-library-modify,user-top-read,user-read-" "recently-played"; // Required access scopes @@ -68,69 +64,43 @@ void AccessKeyFetcher::updateAccessKey() { keyPending = true; - // Prepare a protobuf login request - static LoginRequest loginRequest = LoginRequest_init_zero; - static LoginResponse loginResponse = LoginResponse_init_zero; - - // Assign necessary request fields - loginRequest.client_info.client_id.funcs.encode = &bell::nanopb::encodeString; - loginRequest.client_info.client_id.arg = &CLIENT_ID; - - loginRequest.client_info.device_id.funcs.encode = &bell::nanopb::encodeString; - loginRequest.client_info.device_id.arg = &ctx->config.deviceId; - - loginRequest.login_method.stored_credential.username.funcs.encode = - &bell::nanopb::encodeString; - loginRequest.login_method.stored_credential.username.arg = - &ctx->config.username; - - // Set login method to stored credential - loginRequest.which_login_method = LoginRequest_stored_credential_tag; - loginRequest.login_method.stored_credential.data.funcs.encode = - &bell::nanopb::encodeVector; - loginRequest.login_method.stored_credential.data.arg = &ctx->config.authData; - // Max retry of 3, can receive different hash cat types int retryCount = 3; bool success = false; do { - auto encodedRequest = pbEncode(LoginRequest_fields, &loginRequest); - CSPOT_LOG(info, "Access token expired, fetching new one... %d", - encodedRequest.size()); + CSPOT_LOG(info, "Access token expired, fetching new one..."); - // Perform a login5 request, containing the encoded protobuf data + auto credentials = "grant_type=client_credentials&client_id=" + ctx->config.clientId + "&client_secret=" + ctx->config.clientSecret; + std::vector body(credentials.begin(), credentials.end()); + auto response = bell::HTTPClient::post( - "https://login5.spotify.com/v3/login", - {{"Content-Type", "application/x-protobuf"}}, encodedRequest); - - auto responseBytes = response->bytes(); - - // Deserialize the response - pbDecode(loginResponse, LoginResponse_fields, responseBytes); - - if (loginResponse.which_response == LoginResponse_ok_tag) { - // Successfully received an auth token + "https://accounts.spotify.com/api/token", + { {"Content-Type", "application/x-www-form-urlencoded"} }, body); + +#ifdef BELL_ONLY_CJSON + cJSON* root = cJSON_Parse(response->body().data()); + if (!cJSON_GetObjectItem(root, "error")) { + accessKey = std::string(cJSON_GetObjectItem(root, "access_token")->valuestring); + int expiresIn = cJSON_GetObjectItem(root, "expires_in")->valueint; + cJSON_Delete(root); +#else + auto root = nlohmann::json::parse(response->bytes()); + if (!root.contains("error")) { + accessKey = std::string(root["access_token"]); + int expiresIn = root["expires_in"]; +#endif + // Successfully received an auth token CSPOT_LOG(info, "Access token sucessfully fetched"); success = true; - accessKey = std::string(loginResponse.response.ok.access_token); - - // Expire in ~30 minutes - int expiresIn = 3600 / 2; - - if (loginResponse.response.ok.has_access_token_expires_in) { - int expiresIn = loginResponse.response.ok.access_token_expires_in / 2; - } - this->expiresAt = - ctx->timeProvider->getSyncedTimestamp() + (expiresIn * 1000); - } else { - CSPOT_LOG(error, "Failed to fetch access token"); + ctx->timeProvider->getSyncedTimestamp() + (expiresIn * 1000); + } + else { + CSPOT_LOG(error, "Failed to fetch access token"); + BELL_SLEEP_MS(3000); } - - // Free up allocated memory for response - pb_release(LoginResponse_fields, &loginResponse); retryCount--; } while (retryCount >= 0 && !success);