mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-31 11:56:32 +03:00
Use client id & secret for Spotify
This commit is contained in:
@@ -33,6 +33,16 @@
|
|||||||
#include "nvs_utilities.h"
|
#include "nvs_utilities.h"
|
||||||
#include "tools.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 "<your client id>"
|
||||||
|
#define CLIENT_SECRET "<your client secret>"
|
||||||
|
#endif
|
||||||
|
|
||||||
static class cspotPlayer *player;
|
static class cspotPlayer *player;
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
@@ -365,6 +375,8 @@ void cspotPlayer::runTask() {
|
|||||||
|
|
||||||
ctx->session->connectWithRandomAp();
|
ctx->session->connectWithRandomAp();
|
||||||
ctx->config.authData = ctx->session->authenticate(blob);
|
ctx->config.authData = ctx->session->authenticate(blob);
|
||||||
|
ctx->config.clientId = CLIENT_ID;
|
||||||
|
ctx->config.clientSecret = CLIENT_SECRET;
|
||||||
|
|
||||||
// Auth successful
|
// Auth successful
|
||||||
if (ctx->config.authData.size() > 0) {
|
if (ctx->config.authData.size() > 0) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "BellLogger.h"
|
#include "BellLogger.h"
|
||||||
#include "MDNSService.h"
|
#include "MDNSService.h"
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ struct Context {
|
|||||||
AudioFormat audioFormat = AudioFormat::AudioFormat_OGG_VORBIS_160;
|
AudioFormat audioFormat = AudioFormat::AudioFormat_OGG_VORBIS_160;
|
||||||
std::string deviceId;
|
std::string deviceId;
|
||||||
std::string deviceName;
|
std::string deviceName;
|
||||||
|
std::string clientId;
|
||||||
|
std::string clientSecret;
|
||||||
std::vector<uint8_t> authData;
|
std::vector<uint8_t> authData;
|
||||||
int volume;
|
int volume;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "BellLogger.h" // for AbstractLogger
|
#include "BellLogger.h" // for AbstractLogger
|
||||||
|
#include "BellUtils.h" // for BELL_SLEEP_MS
|
||||||
#include "CSpotContext.h" // for Context
|
#include "CSpotContext.h" // for Context
|
||||||
#include "HTTPClient.h"
|
#include "HTTPClient.h"
|
||||||
#include "Logger.h" // for CSPOT_LOG
|
#include "Logger.h" // for CSPOT_LOG
|
||||||
@@ -24,13 +25,8 @@
|
|||||||
#include "nlohmann/json_fwd.hpp" // for json
|
#include "nlohmann/json_fwd.hpp" // for json
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "protobuf/login5.pb.h" // for LoginRequest
|
|
||||||
|
|
||||||
using namespace cspot;
|
using namespace cspot;
|
||||||
|
|
||||||
static std::string CLIENT_ID =
|
|
||||||
"65b708073fc0480ea92a077233ca87bd"; // Spotify web client's client id
|
|
||||||
|
|
||||||
static std::string SCOPES =
|
static std::string SCOPES =
|
||||||
"streaming,user-library-read,user-library-modify,user-top-read,user-read-"
|
"streaming,user-library-read,user-library-modify,user-top-read,user-read-"
|
||||||
"recently-played"; // Required access scopes
|
"recently-played"; // Required access scopes
|
||||||
@@ -68,69 +64,43 @@ void AccessKeyFetcher::updateAccessKey() {
|
|||||||
|
|
||||||
keyPending = true;
|
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
|
// Max retry of 3, can receive different hash cat types
|
||||||
int retryCount = 3;
|
int retryCount = 3;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
auto encodedRequest = pbEncode(LoginRequest_fields, &loginRequest);
|
CSPOT_LOG(info, "Access token expired, fetching new one...");
|
||||||
CSPOT_LOG(info, "Access token expired, fetching new one... %d",
|
|
||||||
encodedRequest.size());
|
auto credentials = "grant_type=client_credentials&client_id=" + ctx->config.clientId + "&client_secret=" + ctx->config.clientSecret;
|
||||||
|
std::vector<uint8_t> body(credentials.begin(), credentials.end());
|
||||||
|
|
||||||
// Perform a login5 request, containing the encoded protobuf data
|
|
||||||
auto response = bell::HTTPClient::post(
|
auto response = bell::HTTPClient::post(
|
||||||
"https://login5.spotify.com/v3/login",
|
"https://accounts.spotify.com/api/token",
|
||||||
{{"Content-Type", "application/x-protobuf"}}, encodedRequest);
|
{ {"Content-Type", "application/x-www-form-urlencoded"} }, body);
|
||||||
|
|
||||||
auto responseBytes = response->bytes();
|
#ifdef BELL_ONLY_CJSON
|
||||||
|
cJSON* root = cJSON_Parse(response->body().data());
|
||||||
// Deserialize the response
|
if (!cJSON_GetObjectItem(root, "error")) {
|
||||||
pbDecode(loginResponse, LoginResponse_fields, responseBytes);
|
accessKey = std::string(cJSON_GetObjectItem(root, "access_token")->valuestring);
|
||||||
|
int expiresIn = cJSON_GetObjectItem(root, "expires_in")->valueint;
|
||||||
if (loginResponse.which_response == LoginResponse_ok_tag) {
|
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
|
// Successfully received an auth token
|
||||||
CSPOT_LOG(info, "Access token sucessfully fetched");
|
CSPOT_LOG(info, "Access token sucessfully fetched");
|
||||||
success = true;
|
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 =
|
this->expiresAt =
|
||||||
ctx->timeProvider->getSyncedTimestamp() + (expiresIn * 1000);
|
ctx->timeProvider->getSyncedTimestamp() + (expiresIn * 1000);
|
||||||
} else {
|
|
||||||
CSPOT_LOG(error, "Failed to fetch access token");
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// Free up allocated memory for response
|
CSPOT_LOG(error, "Failed to fetch access token");
|
||||||
pb_release(LoginResponse_fields, &loginResponse);
|
BELL_SLEEP_MS(3000);
|
||||||
|
}
|
||||||
|
|
||||||
retryCount--;
|
retryCount--;
|
||||||
} while (retryCount >= 0 && !success);
|
} while (retryCount >= 0 && !success);
|
||||||
|
|||||||
Reference in New Issue
Block a user