123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- #include "AccessKeyFetcher.h"
- #include <cstring> // for strrchr
- #include <initializer_list> // for initializer_list
- #include <map> // for operator!=, operator==
- #include <type_traits> // for remove_extent_t
- #include <vector> // for vector
- #include "BellLogger.h" // for AbstractLogger
- #include "CSpotContext.h" // for Context
- #include "Logger.h" // for CSPOT_LOG
- #include "MercurySession.h" // for MercurySession, MercurySession::Res...
- #include "Packet.h" // for cspot
- #include "TimeProvider.h" // for TimeProvider
- #include "Utils.h" // for string_format
- #include "WrappedSemaphore.h"
- #ifdef BELL_ONLY_CJSON
- #include "cJSON.h"
- #else
- #include "nlohmann/json.hpp" // for basic_json<>::object_t, basic_json
- #include "nlohmann/json_fwd.hpp" // for json
- #endif
- 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
- AccessKeyFetcher::AccessKeyFetcher(std::shared_ptr<cspot::Context> ctx)
- : ctx(ctx) {
- this->updateSemaphore = std::make_shared<bell::WrappedSemaphore>();
- }
- bool AccessKeyFetcher::isExpired() {
- if (accessKey.empty()) {
- return true;
- }
- if (ctx->timeProvider->getSyncedTimestamp() > expiresAt) {
- return true;
- }
- return false;
- }
- std::string AccessKeyFetcher::getAccessKey() {
- if (!isExpired()) {
- return accessKey;
- }
- updateAccessKey();
- return accessKey;
- }
- void AccessKeyFetcher::updateAccessKey() {
- if (keyPending) {
- // Already pending refresh request
- return;
- }
- keyPending = true;
- CSPOT_LOG(info, "Access token expired, fetching new one...");
- std::string url =
- string_format("hm://keymaster/token/authenticated?client_id=%s&scope=%s",
- CLIENT_ID.c_str(), SCOPES.c_str());
- auto timeProvider = this->ctx->timeProvider;
- ctx->session->execute(
- MercurySession::RequestType::GET, url,
- [this, timeProvider](MercurySession::Response& res) {
- if (res.fail)
- return;
- auto accessJSON =
- std::string((char*)res.parts[0].data(), res.parts[0].size());
- #ifdef BELL_ONLY_CJSON
- cJSON* jsonBody = cJSON_Parse(accessJSON.c_str());
- this->accessKey =
- cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring;
- int expiresIn = cJSON_GetObjectItem(jsonBody, "expiresIn")->valueint;
- cJSON_Delete(jsonBody);
- #else
- auto jsonBody = nlohmann::json::parse(accessJSON);
- this->accessKey = jsonBody["accessToken"];
- int expiresIn = jsonBody["expiresIn"];
- #endif
- expiresIn = expiresIn / 2; // Refresh token before it expires
- this->expiresAt =
- timeProvider->getSyncedTimestamp() + (expiresIn * 1000);
- updateSemaphore->give();
- });
- updateSemaphore->twait(5000);
- // Mark as not pending for refresh
- keyPending = false;
- }
|