mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2026-05-16 02:05:30 +01:00
applied platformio structure
This commit is contained in:
40
lib/spotify/cspot/include/AccessKeyFetcher.h
Normal file
40
lib/spotify/cspot/include/AccessKeyFetcher.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic> // or std::atomic
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
|
||||
namespace cspot {
|
||||
struct Context;
|
||||
|
||||
class AccessKeyFetcher {
|
||||
public:
|
||||
AccessKeyFetcher(std::shared_ptr<cspot::Context> ctx);
|
||||
|
||||
/**
|
||||
* @brief Checks if key is expired
|
||||
* @returns true when currently held access key is not valid
|
||||
*/
|
||||
bool isExpired();
|
||||
|
||||
/**
|
||||
* @brief Fetches a new access key
|
||||
* @remark In case the key is expired, this function blocks until a refresh is done.
|
||||
* @returns access key
|
||||
*/
|
||||
std::string getAccessKey();
|
||||
|
||||
/**
|
||||
* @brief Forces a refresh of the access key
|
||||
*/
|
||||
void updateAccessKey();
|
||||
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
|
||||
std::atomic<bool> keyPending = false;
|
||||
std::string accessKey;
|
||||
long long int expiresAt;
|
||||
};
|
||||
} // namespace cspot
|
||||
23
lib/spotify/cspot/include/ApResolve.h
Normal file
23
lib/spotify/cspot/include/ApResolve.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <string> // for string
|
||||
#ifdef BELL_ONLY_CJSON
|
||||
#include "cJSON.h"
|
||||
#else
|
||||
#endif
|
||||
|
||||
namespace cspot {
|
||||
class ApResolve {
|
||||
public:
|
||||
ApResolve(std::string apOverride);
|
||||
|
||||
/**
|
||||
* @brief Connects to spotify's servers and returns first valid ap address
|
||||
* @returns std::string Address in form of url:port
|
||||
*/
|
||||
std::string fetchFirstApAddress();
|
||||
|
||||
private:
|
||||
std::string apOverride;
|
||||
};
|
||||
} // namespace cspot
|
||||
64
lib/spotify/cspot/include/AuthChallenges.h
Normal file
64
lib/spotify/cspot/include/AuthChallenges.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Crypto.h" // for Crypto
|
||||
#include "protobuf/authentication.pb.h" // for ClientResponseEncrypted
|
||||
#include "protobuf/keyexchange.pb.h" // for APResponseMessage, ClientHello
|
||||
|
||||
namespace cspot {
|
||||
class AuthChallenges {
|
||||
public:
|
||||
AuthChallenges();
|
||||
~AuthChallenges();
|
||||
|
||||
/**
|
||||
* @brief Prepares a spotify authentication packet
|
||||
* @param authBlob authentication blob bytes
|
||||
* @param authType value representing spotify's authentication type
|
||||
* @param deviceId device id to use during auth.
|
||||
* @param username spotify's username
|
||||
*
|
||||
* @returns vector containing bytes of the authentication packet
|
||||
*/
|
||||
std::vector<uint8_t> prepareAuthPacket(std::vector<uint8_t>& authBlob,
|
||||
int authType,
|
||||
const std::string& deviceId,
|
||||
const std::string& username);
|
||||
|
||||
/**
|
||||
* @brief Solves the ApHello packet, and returns a packet with response
|
||||
*
|
||||
* @param helloPacket hello packet bytes received from the server
|
||||
* @param data authentication data received from the server
|
||||
*
|
||||
* @returns vector containing response packet
|
||||
*/
|
||||
std::vector<uint8_t> solveApHello(std::vector<uint8_t>& helloPacket,
|
||||
std::vector<uint8_t>& data);
|
||||
|
||||
/**
|
||||
* @brief Prepares an client hello packet, used for initial auth with spotify
|
||||
*
|
||||
* @returns vector containing the packet's data
|
||||
*/
|
||||
std::vector<uint8_t> prepareClientHello();
|
||||
|
||||
std::vector<uint8_t> shanSendKey = {};
|
||||
std::vector<uint8_t> shanRecvKey = {};
|
||||
|
||||
private:
|
||||
const long long SPOTIFY_VERSION = 0x10800000000;
|
||||
|
||||
// Protobuf structures
|
||||
ClientResponseEncrypted authRequest;
|
||||
ClientResponsePlaintext clientResPlaintext;
|
||||
ClientHello clientHello;
|
||||
APResponseMessage apResponse;
|
||||
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
};
|
||||
} // namespace cspot
|
||||
90
lib/spotify/cspot/include/CDNAudioFile.h
Normal file
90
lib/spotify/cspot/include/CDNAudioFile.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Crypto.h" // for Crypto
|
||||
#include "HTTPClient.h" // for HTTPClient
|
||||
|
||||
namespace bell {
|
||||
class WrappedSemaphore;
|
||||
} // namespace bell
|
||||
|
||||
namespace cspot {
|
||||
class AccessKeyFetcher;
|
||||
|
||||
class CDNAudioFile {
|
||||
|
||||
public:
|
||||
CDNAudioFile(const std::string& cdnUrl, const std::vector<uint8_t>& audioKey);
|
||||
|
||||
/**
|
||||
* @brief Opens connection to the provided cdn url, and fetches track metadata.
|
||||
*/
|
||||
void openStream();
|
||||
|
||||
/**
|
||||
* @brief Read and decrypt part of the cdn stream
|
||||
*
|
||||
* @param dst buffer where to read received data to
|
||||
* @param amount of bytes to read
|
||||
*
|
||||
* @returns amount of bytes read
|
||||
*/
|
||||
size_t readBytes(uint8_t* dst, size_t bytes);
|
||||
|
||||
/**
|
||||
* @brief Returns current position in CDN stream
|
||||
*/
|
||||
size_t getPosition();
|
||||
|
||||
/**
|
||||
* @brief returns total size of the audio file in bytes
|
||||
*/
|
||||
size_t getSize();
|
||||
|
||||
/**
|
||||
* @brief Seeks the track to provided position
|
||||
* @param position position where to seek the track
|
||||
*/
|
||||
void seek(size_t position);
|
||||
|
||||
private:
|
||||
const int OPUS_HEADER_SIZE = 8 * 1024;
|
||||
const int OPUS_FOOTER_PREFFERED = 1024 * 12; // 12K should be safe
|
||||
const int SEEK_MARGIN_SIZE = 1024 * 4;
|
||||
|
||||
const int HTTP_BUFFER_SIZE = 1024 * 14;
|
||||
const int SPOTIFY_OPUS_HEADER = 167;
|
||||
|
||||
// Used to store opus metadata, speeds up read
|
||||
std::vector<uint8_t> header = std::vector<uint8_t>(OPUS_HEADER_SIZE);
|
||||
std::vector<uint8_t> footer;
|
||||
|
||||
// General purpose buffer to read data
|
||||
std::vector<uint8_t> httpBuffer = std::vector<uint8_t>(HTTP_BUFFER_SIZE);
|
||||
|
||||
// AES IV for decrypting the audio stream
|
||||
const std::vector<uint8_t> audioAESIV = {0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb,
|
||||
0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64,
|
||||
0x3f, 0x63, 0x0d, 0x93};
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
|
||||
std::unique_ptr<bell::HTTPClient::Response> httpConnection;
|
||||
|
||||
size_t position = 0;
|
||||
size_t totalFileSize = 0;
|
||||
size_t lastRequestPosition = 0;
|
||||
size_t lastRequestCapacity = 0;
|
||||
|
||||
bool enableRequestMargin = false;
|
||||
|
||||
std::string cdnUrl;
|
||||
std::vector<uint8_t> audioKey;
|
||||
|
||||
void decrypt(uint8_t* dst, size_t nbytes, size_t pos);
|
||||
};
|
||||
} // namespace cspot
|
||||
82
lib/spotify/cspot/include/CSpotContext.h
Normal file
82
lib/spotify/cspot/include/CSpotContext.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "LoginBlob.h"
|
||||
#include "MercurySession.h"
|
||||
#include "TimeProvider.h"
|
||||
#include "protobuf/authentication.pb.h" // for AuthenticationType_AUTHE...
|
||||
#include "protobuf/metadata.pb.h"
|
||||
#ifdef BELL_ONLY_CJSON
|
||||
#include "cJSON.h"
|
||||
#else
|
||||
#include "nlohmann/detail/json_pointer.hpp" // for json_pointer<>::string_t
|
||||
#include "nlohmann/json.hpp" // for basic_json<>::object_t, basic_json
|
||||
#include "nlohmann/json_fwd.hpp" // for json
|
||||
#endif
|
||||
|
||||
namespace cspot {
|
||||
struct Context {
|
||||
struct ConfigState {
|
||||
// Setup default bitrate to 160
|
||||
AudioFormat audioFormat = AudioFormat::AudioFormat_OGG_VORBIS_160;
|
||||
std::string deviceId;
|
||||
std::string deviceName;
|
||||
std::string clientId;
|
||||
std::string clientSecret;
|
||||
std::vector<uint8_t> authData;
|
||||
int volume;
|
||||
|
||||
std::string username;
|
||||
std::string countryCode;
|
||||
};
|
||||
|
||||
ConfigState config;
|
||||
|
||||
std::shared_ptr<TimeProvider> timeProvider;
|
||||
std::shared_ptr<cspot::MercurySession> session;
|
||||
std::string getCredentialsJson() {
|
||||
#ifdef BELL_ONLY_CJSON
|
||||
cJSON* json_obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(json_obj, "authData",
|
||||
Crypto::base64Encode(config.authData).c_str());
|
||||
cJSON_AddNumberToObject(
|
||||
json_obj, "authType",
|
||||
AuthenticationType_AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS);
|
||||
cJSON_AddStringToObject(json_obj, "username", config.username.c_str());
|
||||
|
||||
char* str = cJSON_PrintUnformatted(json_obj);
|
||||
cJSON_Delete(json_obj);
|
||||
std::string json_objStr(str);
|
||||
free(str);
|
||||
|
||||
return json_objStr;
|
||||
#else
|
||||
nlohmann::json obj;
|
||||
obj["authData"] = Crypto::base64Encode(config.authData);
|
||||
obj["authType"] =
|
||||
AuthenticationType_AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS;
|
||||
obj["username"] = config.username;
|
||||
|
||||
return obj.dump();
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::shared_ptr<Context> createFromBlob(
|
||||
std::shared_ptr<LoginBlob> blob) {
|
||||
auto ctx = std::make_shared<Context>();
|
||||
ctx->timeProvider = std::make_shared<TimeProvider>();
|
||||
|
||||
ctx->session = std::make_shared<MercurySession>(ctx->timeProvider);
|
||||
ctx->config.deviceId = blob->getDeviceId();
|
||||
ctx->config.deviceName = blob->getDeviceName();
|
||||
ctx->config.authData = blob->authData;
|
||||
ctx->config.volume = 0;
|
||||
ctx->config.username = blob->getUserName();
|
||||
|
||||
return ctx;
|
||||
}
|
||||
};
|
||||
} // namespace cspot
|
||||
17
lib/spotify/cspot/include/ConstantParameters.h
Normal file
17
lib/spotify/cspot/include/ConstantParameters.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#define MAX_VOLUME 65536
|
||||
|
||||
// variable weakly set in ZeroconfAuthentificator.cpp
|
||||
extern char deviceId[];
|
||||
|
||||
namespace cspot {
|
||||
// Hardcoded information sent to spotify servers
|
||||
const char* const informationString = "cspot-player";
|
||||
const char* const brandName = "cspot";
|
||||
const char* const versionString = "cspot-1.1";
|
||||
const char* const protocolVersion = "2.7.1";
|
||||
const char* const defaultDeviceName = "CSpot";
|
||||
const char* const swVersion = "1.0.0";
|
||||
|
||||
} // namespace cspot
|
||||
15
lib/spotify/cspot/include/CspotAssert.h
Normal file
15
lib/spotify/cspot/include/CspotAssert.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef CSPOT_ASSERT_H
|
||||
#define CSPOT_ASSERT_H
|
||||
#include <stdio.h>
|
||||
#include <cassert>
|
||||
|
||||
#define CSPOT_ASSERT(CONDITION, MESSAGE) \
|
||||
do { \
|
||||
if (!(CONDITION)) { \
|
||||
printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, \
|
||||
__LINE__, #CONDITION, MESSAGE); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
8
lib/spotify/cspot/include/Logger.h
Normal file
8
lib/spotify/cspot/include/Logger.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <BellLogger.h>
|
||||
|
||||
#define CSPOT_LOG(type, ...) \
|
||||
do { \
|
||||
bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \
|
||||
} while (0)
|
||||
46
lib/spotify/cspot/include/LoginBlob.h
Normal file
46
lib/spotify/cspot/include/LoginBlob.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // for uint8_t, uint32_t
|
||||
#include <map> // for map
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Crypto.h" // for CryptoMbedTLS, Crypto
|
||||
|
||||
namespace cspot {
|
||||
class LoginBlob {
|
||||
private:
|
||||
int blobSkipPosition = 0;
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
std::string name, deviceId;
|
||||
|
||||
uint32_t readBlobInt(const std::vector<uint8_t>& loginData);
|
||||
std::vector<uint8_t> decodeBlob(const std::vector<uint8_t>& blob,
|
||||
const std::vector<uint8_t>& sharedKey);
|
||||
std::vector<uint8_t> decodeBlobSecondary(const std::vector<uint8_t>& blob,
|
||||
const std::string& username,
|
||||
const std::string& deviceId);
|
||||
|
||||
public:
|
||||
LoginBlob(std::string name);
|
||||
std::vector<uint8_t> authData;
|
||||
std::string username = "";
|
||||
int authType;
|
||||
|
||||
// Loading
|
||||
void loadZeroconfQuery(std::map<std::string, std::string>& queryParams);
|
||||
void loadZeroconf(const std::vector<uint8_t>& blob,
|
||||
const std::vector<uint8_t>& sharedKey,
|
||||
const std::string& deviceId, const std::string& username);
|
||||
void loadUserPass(const std::string& username, const std::string& password);
|
||||
void loadJson(const std::string& json);
|
||||
|
||||
std::string buildZeroconfInfo();
|
||||
std::string getDeviceId();
|
||||
std::string getDeviceName();
|
||||
std::string getUserName();
|
||||
|
||||
std::string toJson();
|
||||
};
|
||||
} // namespace cspot
|
||||
134
lib/spotify/cspot/include/MercurySession.h
Normal file
134
lib/spotify/cspot/include/MercurySession.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic> // for atomic
|
||||
#include <cstdint> // for uint8_t, uint64_t, uint32_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <string> // for string
|
||||
#include <unordered_map> // for unordered_map
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "BellTask.h" // for Task
|
||||
#include "Packet.h" // for Packet
|
||||
#include "Queue.h" // for Queue
|
||||
#include "Session.h" // for Session
|
||||
#include "protobuf/mercury.pb.h" // for Header
|
||||
|
||||
namespace cspot {
|
||||
class TimeProvider;
|
||||
|
||||
class MercurySession : public bell::Task, public cspot::Session {
|
||||
public:
|
||||
MercurySession(std::shared_ptr<cspot::TimeProvider> timeProvider);
|
||||
~MercurySession();
|
||||
typedef std::vector<std::vector<uint8_t>> DataParts;
|
||||
|
||||
struct Response {
|
||||
Header mercuryHeader;
|
||||
uint8_t flags;
|
||||
DataParts parts;
|
||||
uint64_t sequenceId;
|
||||
bool fail;
|
||||
};
|
||||
|
||||
typedef std::function<void(Response&)> ResponseCallback;
|
||||
typedef std::function<void(bool, const std::vector<uint8_t>&)>
|
||||
AudioKeyCallback;
|
||||
typedef std::function<void()> ConnectionEstabilishedCallback;
|
||||
|
||||
enum class RequestType : uint8_t {
|
||||
SUB = 0xb3,
|
||||
UNSUB = 0xb4,
|
||||
SUBRES = 0xb5,
|
||||
SEND = 0xb2,
|
||||
GET = 0xFF, // Shitty workaround, it's value is actually same as SEND
|
||||
PING = 0x04,
|
||||
PONG_ACK = 0x4a,
|
||||
AUDIO_CHUNK_REQUEST_COMMAND = 0x08,
|
||||
AUDIO_CHUNK_SUCCESS_RESPONSE = 0x09,
|
||||
AUDIO_CHUNK_FAILURE_RESPONSE = 0x0A,
|
||||
AUDIO_KEY_REQUEST_COMMAND = 0x0C,
|
||||
AUDIO_KEY_SUCCESS_RESPONSE = 0x0D,
|
||||
AUDIO_KEY_FAILURE_RESPONSE = 0x0E,
|
||||
COUNTRY_CODE_RESPONSE = 0x1B,
|
||||
};
|
||||
|
||||
std::unordered_map<RequestType, std::string> RequestTypeMap = {
|
||||
{RequestType::GET, "GET"},
|
||||
{RequestType::SEND, "SEND"},
|
||||
{RequestType::SUB, "SUB"},
|
||||
{RequestType::UNSUB, "UNSUB"},
|
||||
};
|
||||
|
||||
void handlePacket();
|
||||
|
||||
uint64_t executeSubscription(RequestType type, const std::string& uri,
|
||||
ResponseCallback callback,
|
||||
ResponseCallback subscription, DataParts& parts);
|
||||
uint64_t executeSubscription(RequestType type, const std::string& uri,
|
||||
ResponseCallback callback,
|
||||
ResponseCallback subscription) {
|
||||
DataParts parts = {};
|
||||
return this->executeSubscription(type, uri, callback, subscription, parts);
|
||||
}
|
||||
|
||||
uint64_t execute(RequestType type, const std::string& uri,
|
||||
ResponseCallback callback) {
|
||||
return this->executeSubscription(type, uri, callback, nullptr);
|
||||
}
|
||||
|
||||
uint64_t execute(RequestType type, const std::string& uri,
|
||||
ResponseCallback callback, DataParts& parts) {
|
||||
return this->executeSubscription(type, uri, callback, nullptr, parts);
|
||||
}
|
||||
|
||||
void unregister(uint64_t sequenceId);
|
||||
|
||||
void unregisterAudioKey(uint32_t sequenceId);
|
||||
|
||||
uint32_t requestAudioKey(const std::vector<uint8_t>& trackId,
|
||||
const std::vector<uint8_t>& fileId,
|
||||
AudioKeyCallback audioCallback);
|
||||
|
||||
std::string getCountryCode();
|
||||
|
||||
void disconnect();
|
||||
|
||||
void setConnectedHandler(ConnectionEstabilishedCallback callback);
|
||||
|
||||
bool triggerTimeout() override;
|
||||
|
||||
private:
|
||||
const int PING_TIMEOUT_MS = 2 * 60 * 1000 + 5000;
|
||||
|
||||
std::shared_ptr<cspot::TimeProvider> timeProvider;
|
||||
Header tempMercuryHeader = {};
|
||||
ConnectionEstabilishedCallback connectionReadyCallback = nullptr;
|
||||
|
||||
bell::Queue<cspot::Packet> packetQueue;
|
||||
|
||||
void runTask() override;
|
||||
void reconnect();
|
||||
|
||||
std::unordered_map<uint64_t, ResponseCallback> callbacks;
|
||||
std::unordered_map<std::string, ResponseCallback> subscriptions;
|
||||
std::unordered_map<uint32_t, AudioKeyCallback> audioKeyCallbacks;
|
||||
|
||||
uint64_t sequenceId = 1;
|
||||
uint32_t audioKeySequence = 1;
|
||||
|
||||
unsigned long long timestampDiff;
|
||||
unsigned long long lastPingTimestamp = -1;
|
||||
std::string countryCode = "";
|
||||
|
||||
std::mutex isRunningMutex;
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::atomic<bool> isReconnecting = false;
|
||||
std::atomic<bool> executeEstabilishedCallback = false;
|
||||
|
||||
void failAllPending();
|
||||
|
||||
Response decodeResponse(const std::vector<uint8_t>& data);
|
||||
};
|
||||
} // namespace cspot
|
||||
11
lib/spotify/cspot/include/Packet.h
Normal file
11
lib/spotify/cspot/include/Packet.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace cspot {
|
||||
struct Packet {
|
||||
uint8_t command;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
} // namespace cspot
|
||||
45
lib/spotify/cspot/include/PlainConnection.h
Normal file
45
lib/spotify/cspot/include/PlainConnection.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef PLAINCONNECTION_H
|
||||
#define PLAINCONNECTION_H
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "win32shim.h"
|
||||
#else
|
||||
#include <unistd.h> // for size_t
|
||||
#endif
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <functional> // for function
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
typedef std::function<bool()> timeoutCallback;
|
||||
|
||||
namespace cspot {
|
||||
class PlainConnection {
|
||||
public:
|
||||
PlainConnection();
|
||||
~PlainConnection();
|
||||
|
||||
/**
|
||||
* @brief Connect to the given AP address
|
||||
*
|
||||
* @param apAddress The AP url to connect to
|
||||
*/
|
||||
void connect(const std::string& apAddress);
|
||||
void close();
|
||||
|
||||
timeoutCallback timeoutHandler;
|
||||
std::vector<uint8_t> sendPrefixPacket(const std::vector<uint8_t>& prefix,
|
||||
const std::vector<uint8_t>& data);
|
||||
std::vector<uint8_t> recvPacket();
|
||||
|
||||
void readBlock(const uint8_t* dst, size_t size);
|
||||
size_t writeBlock(const std::vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
int apSock;
|
||||
};
|
||||
} // namespace cspot
|
||||
|
||||
#endif
|
||||
97
lib/spotify/cspot/include/PlaybackState.h
Normal file
97
lib/spotify/cspot/include/PlaybackState.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // for uint8_t, uint32_t
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "TrackReference.h"
|
||||
#include "protobuf/spirc.pb.h" // for Frame, TrackRef, CapabilityType, Mess...
|
||||
|
||||
namespace cspot {
|
||||
struct Context;
|
||||
|
||||
class PlaybackState {
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
|
||||
uint32_t seqNum = 0;
|
||||
uint8_t capabilityIndex = 0;
|
||||
|
||||
std::vector<uint8_t> frameData;
|
||||
|
||||
void addCapability(
|
||||
CapabilityType typ, int intValue = -1,
|
||||
std::vector<std::string> stringsValue = std::vector<std::string>());
|
||||
|
||||
public:
|
||||
Frame innerFrame;
|
||||
Frame remoteFrame;
|
||||
|
||||
std::vector<TrackReference> remoteTracks;
|
||||
|
||||
enum class State { Playing, Stopped, Loading, Paused };
|
||||
|
||||
/**
|
||||
* @brief Player state represents the current state of player.
|
||||
*
|
||||
* Responsible for keeping track of player's state. Doesn't control the playback itself.
|
||||
*
|
||||
* @param timeProvider synced time provider
|
||||
*/
|
||||
PlaybackState(std::shared_ptr<cspot::Context> ctx);
|
||||
|
||||
~PlaybackState();
|
||||
|
||||
/**
|
||||
* @brief Updates state according to current playback state.
|
||||
*
|
||||
* @param state playback state
|
||||
*/
|
||||
void setPlaybackState(const PlaybackState::State state);
|
||||
|
||||
/**
|
||||
* @brief Sets player activity
|
||||
*
|
||||
* @param isActive activity status
|
||||
*/
|
||||
void setActive(bool isActive);
|
||||
|
||||
/**
|
||||
* @brief Simple getter
|
||||
*
|
||||
* @return true player is active
|
||||
* @return false player is inactive
|
||||
*/
|
||||
bool isActive();
|
||||
|
||||
/**
|
||||
* @brief Updates local track position.
|
||||
*
|
||||
* @param position position in milliseconds
|
||||
*/
|
||||
void updatePositionMs(uint32_t position);
|
||||
|
||||
/**
|
||||
* @brief Sets local volume on internal state.
|
||||
*
|
||||
* @param volume volume between 0 and UINT16 max
|
||||
*/
|
||||
void setVolume(uint32_t volume);
|
||||
|
||||
/**
|
||||
* @brief Updates local track queue from remote data.
|
||||
*/
|
||||
void syncWithRemote();
|
||||
|
||||
/**
|
||||
* @brief Encodes current frame into binary data via protobuf.
|
||||
*
|
||||
* @param typ message type to include in frame type
|
||||
* @return std::vector<uint8_t> binary frame data
|
||||
*/
|
||||
std::vector<uint8_t> encodeCurrentFrame(MessageType typ);
|
||||
|
||||
bool decodeRemoteFrame(std::vector<uint8_t>& data);
|
||||
};
|
||||
} // namespace cspot
|
||||
40
lib/spotify/cspot/include/Session.h
Normal file
40
lib/spotify/cspot/include/Session.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // for uint8_t
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
namespace cspot {
|
||||
class AuthChallenges;
|
||||
class LoginBlob;
|
||||
class PlainConnection;
|
||||
class ShannonConnection;
|
||||
} // namespace cspot
|
||||
|
||||
#define LOGIN_REQUEST_COMMAND 0xAB
|
||||
#define AUTH_SUCCESSFUL_COMMAND 0xAC
|
||||
#define AUTH_DECLINED_COMMAND 0xAD
|
||||
|
||||
namespace cspot {
|
||||
class Session {
|
||||
protected:
|
||||
std::unique_ptr<cspot::AuthChallenges> challenges;
|
||||
std::shared_ptr<cspot::PlainConnection> conn;
|
||||
std::shared_ptr<LoginBlob> authBlob;
|
||||
|
||||
std::string deviceId = "142137fd329622137a14901634264e6f332e2411";
|
||||
|
||||
public:
|
||||
Session();
|
||||
~Session();
|
||||
|
||||
std::shared_ptr<cspot::ShannonConnection> shanConn;
|
||||
|
||||
void connect(std::unique_ptr<cspot::PlainConnection> connection);
|
||||
void connectWithRandomAp();
|
||||
void close();
|
||||
virtual bool triggerTimeout() = 0;
|
||||
std::vector<uint8_t> authenticate(std::shared_ptr<LoginBlob> blob);
|
||||
};
|
||||
} // namespace cspot
|
||||
43
lib/spotify/cspot/include/Shannon.h
Normal file
43
lib/spotify/cspot/include/Shannon.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef SHANNON_H
|
||||
#define SHANNON_H
|
||||
|
||||
#include <cstdint> // for uint32_t, uint8_t
|
||||
#include <vector> // for vector
|
||||
|
||||
class Shannon {
|
||||
public:
|
||||
static constexpr unsigned int N = 16;
|
||||
|
||||
void key(const std::vector<uint8_t>& key); /* set key */
|
||||
void nonce(const std::vector<uint8_t>& nonce); /* set Init Vector */
|
||||
void stream(std::vector<uint8_t>& buf); /* stream cipher */
|
||||
void maconly(std::vector<uint8_t>& buf); /* accumulate MAC */
|
||||
void encrypt(std::vector<uint8_t>& buf); /* encrypt + MAC */
|
||||
void decrypt(std::vector<uint8_t>& buf); /* finalize + MAC */
|
||||
void finish(std::vector<uint8_t>& buf); /* finalise MAC */
|
||||
|
||||
private:
|
||||
static constexpr unsigned int FOLD = Shannon::N;
|
||||
static constexpr unsigned int INITKONST = 0x6996c53a;
|
||||
static constexpr unsigned int KEYP = 13;
|
||||
uint32_t R[Shannon::N];
|
||||
uint32_t CRC[Shannon::N];
|
||||
uint32_t initR[Shannon::N];
|
||||
uint32_t konst;
|
||||
uint32_t sbuf;
|
||||
uint32_t mbuf;
|
||||
int nbuf;
|
||||
static uint32_t sbox1(uint32_t w);
|
||||
static uint32_t sbox2(uint32_t w);
|
||||
void cycle();
|
||||
void crcfunc(uint32_t i);
|
||||
void macfunc(uint32_t i);
|
||||
void initState();
|
||||
void saveState();
|
||||
void reloadState();
|
||||
void genkonst();
|
||||
void diffuse();
|
||||
void loadKey(const std::vector<uint8_t>& key);
|
||||
};
|
||||
|
||||
#endif
|
||||
41
lib/spotify/cspot/include/ShannonConnection.h
Normal file
41
lib/spotify/cspot/include/ShannonConnection.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef SHANNONCONNECTION_H
|
||||
#define SHANNONCONNECTION_H
|
||||
|
||||
#include <cstdint> // for uint8_t, uint32_t
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Packet.h" // for Packet
|
||||
|
||||
class Shannon;
|
||||
namespace cspot {
|
||||
|
||||
class PlainConnection;
|
||||
} // namespace cspot
|
||||
|
||||
#define MAC_SIZE 4
|
||||
namespace cspot {
|
||||
class ShannonConnection {
|
||||
private:
|
||||
std::unique_ptr<Shannon> sendCipher;
|
||||
std::unique_ptr<Shannon> recvCipher;
|
||||
uint32_t sendNonce = 0;
|
||||
uint32_t recvNonce = 0;
|
||||
std::vector<uint8_t> cipherPacket(uint8_t cmd, std::vector<uint8_t>& data);
|
||||
std::mutex writeMutex;
|
||||
std::mutex readMutex;
|
||||
|
||||
public:
|
||||
ShannonConnection();
|
||||
~ShannonConnection();
|
||||
void wrapConnection(std::shared_ptr<PlainConnection> conn,
|
||||
std::vector<uint8_t>& sendKey,
|
||||
std::vector<uint8_t>& recvKey);
|
||||
void sendPacket(uint8_t cmd, std::vector<uint8_t>& data);
|
||||
std::shared_ptr<PlainConnection> conn;
|
||||
Packet recvPacket();
|
||||
};
|
||||
} // namespace cspot
|
||||
|
||||
#endif
|
||||
82
lib/spotify/cspot/include/SpircHandler.h
Normal file
82
lib/spotify/cspot/include/SpircHandler.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // for uint32_t, uint8_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <string> // for string
|
||||
#include <variant> // for variant
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "CDNAudioFile.h" // for CDNTrackStream, CDNTrackStream::Track...
|
||||
#include "TrackQueue.h"
|
||||
#include "protobuf/spirc.pb.h" // for MessageType
|
||||
|
||||
namespace cspot {
|
||||
class TrackPlayer;
|
||||
struct Context;
|
||||
|
||||
class SpircHandler {
|
||||
public:
|
||||
SpircHandler(std::shared_ptr<cspot::Context> ctx);
|
||||
|
||||
enum class EventType {
|
||||
PLAY_PAUSE,
|
||||
VOLUME,
|
||||
TRACK_INFO,
|
||||
DISC,
|
||||
NEXT,
|
||||
PREV,
|
||||
SEEK,
|
||||
DEPLETED,
|
||||
FLUSH,
|
||||
PLAYBACK_START
|
||||
};
|
||||
|
||||
typedef std::variant<TrackInfo, int, bool> EventData;
|
||||
|
||||
struct Event {
|
||||
EventType eventType;
|
||||
EventData data;
|
||||
};
|
||||
|
||||
typedef std::function<void(std::unique_ptr<Event>)> EventHandler;
|
||||
|
||||
void subscribeToMercury();
|
||||
std::shared_ptr<TrackPlayer> getTrackPlayer();
|
||||
|
||||
void setEventHandler(EventHandler handler);
|
||||
|
||||
void setPause(bool pause);
|
||||
|
||||
bool previousSong();
|
||||
|
||||
bool nextSong();
|
||||
|
||||
void notifyAudioReachedPlayback();
|
||||
void notifyAudioEnded();
|
||||
void updatePositionMs(uint32_t position);
|
||||
void setRemoteVolume(int volume);
|
||||
void loadTrackFromURI(const std::string& uri);
|
||||
std::shared_ptr<cspot::TrackQueue> getTrackQueue() { return trackQueue; }
|
||||
|
||||
void disconnect();
|
||||
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
std::shared_ptr<cspot::TrackPlayer> trackPlayer;
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue;
|
||||
|
||||
EventHandler eventHandler = nullptr;
|
||||
|
||||
std::shared_ptr<cspot::PlaybackState> playbackState;
|
||||
|
||||
void sendCmd(MessageType typ);
|
||||
|
||||
void sendEvent(EventType type);
|
||||
void sendEvent(EventType type, EventData data);
|
||||
|
||||
bool skipSong(TrackQueue::SkipDirection dir);
|
||||
void handleFrame(std::vector<uint8_t>& data);
|
||||
void notify();
|
||||
};
|
||||
} // namespace cspot
|
||||
32
lib/spotify/cspot/include/TimeProvider.h
Normal file
32
lib/spotify/cspot/include/TimeProvider.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // for uint8_t
|
||||
#include <vector> // for vector
|
||||
|
||||
namespace cspot {
|
||||
class TimeProvider {
|
||||
private:
|
||||
unsigned long long timestampDiff;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Bypasses the need for NTP server sync by syncing with spotify's servers
|
||||
*
|
||||
*/
|
||||
TimeProvider();
|
||||
|
||||
/**
|
||||
* @brief Syncs the TimeProvider with spotify server's timestamp
|
||||
*
|
||||
* @param pongPacket pong packet containing timestamp
|
||||
*/
|
||||
void syncWithPingPacket(const std::vector<uint8_t>& pongPacket);
|
||||
|
||||
/**
|
||||
* @brief Get current timestamp synced with spotify servers
|
||||
*
|
||||
* @return unsigned long long timestamp
|
||||
*/
|
||||
unsigned long long getSyncedTimestamp();
|
||||
};
|
||||
} // namespace cspot
|
||||
98
lib/spotify/cspot/include/TrackPlayer.h
Normal file
98
lib/spotify/cspot/include/TrackPlayer.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic> // for atomic
|
||||
#include <cstdint> // for uint8_t, int64_t
|
||||
#include <ctime> // for size_t, time
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <string_view> // for string_view
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "BellTask.h" // for Task
|
||||
#include "CDNAudioFile.h"
|
||||
#include "TrackQueue.h"
|
||||
|
||||
namespace bell {
|
||||
class WrappedSemaphore;
|
||||
} // namespace bell
|
||||
|
||||
#ifdef BELL_VORBIS_FLOAT
|
||||
#include "vorbis/vorbisfile.h"
|
||||
#else
|
||||
#include "ivorbisfile.h" // for OggVorbis_File, ov_callbacks
|
||||
#endif
|
||||
|
||||
namespace cspot {
|
||||
class TrackProvider;
|
||||
class TrackQueue;
|
||||
struct Context;
|
||||
struct TrackReference;
|
||||
|
||||
class TrackPlayer : bell::Task {
|
||||
public:
|
||||
// Callback types
|
||||
typedef std::function<void(std::shared_ptr<QueuedTrack>, bool)>
|
||||
TrackLoadedCallback;
|
||||
typedef std::function<size_t(uint8_t*, size_t, std::string_view)>
|
||||
DataCallback;
|
||||
typedef std::function<void()> EOFCallback;
|
||||
|
||||
TrackPlayer(std::shared_ptr<cspot::Context> ctx,
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue,
|
||||
EOFCallback eofCallback, TrackLoadedCallback loadedCallback);
|
||||
~TrackPlayer();
|
||||
|
||||
void loadTrackFromRef(TrackReference& ref, size_t playbackMs,
|
||||
bool startAutomatically);
|
||||
void setDataCallback(DataCallback callback);
|
||||
|
||||
// CDNTrackStream::TrackInfo getCurrentTrackInfo();
|
||||
void seekMs(size_t ms);
|
||||
void resetState(bool paused = false);
|
||||
|
||||
// Vorbis codec callbacks
|
||||
size_t _vorbisRead(void* ptr, size_t size, size_t nmemb);
|
||||
size_t _vorbisClose();
|
||||
int _vorbisSeek(int64_t offset, int whence);
|
||||
long _vorbisTell();
|
||||
|
||||
void stop();
|
||||
void start();
|
||||
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue;
|
||||
std::shared_ptr<cspot::CDNAudioFile> currentTrackStream;
|
||||
|
||||
std::unique_ptr<bell::WrappedSemaphore> playbackSemaphore;
|
||||
|
||||
TrackLoadedCallback trackLoaded;
|
||||
DataCallback dataCallback = nullptr;
|
||||
EOFCallback eofCallback;
|
||||
|
||||
// Playback control
|
||||
std::atomic<bool> currentSongPlaying;
|
||||
std::mutex playbackMutex;
|
||||
std::mutex dataOutMutex;
|
||||
|
||||
// Vorbis related
|
||||
OggVorbis_File vorbisFile;
|
||||
ov_callbacks vorbisCallbacks;
|
||||
int currentSection;
|
||||
|
||||
std::vector<uint8_t> pcmBuffer = std::vector<uint8_t>(1024);
|
||||
|
||||
bool autoStart = false;
|
||||
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::atomic<bool> pendingReset = false;
|
||||
std::atomic<bool> inFuture = false;
|
||||
std::atomic<size_t> pendingSeekPositionMs = 0;
|
||||
std::atomic<bool> startPaused = false;
|
||||
|
||||
std::mutex runningMutex;
|
||||
|
||||
void runTask() override;
|
||||
};
|
||||
} // namespace cspot
|
||||
134
lib/spotify/cspot/include/TrackQueue.h
Normal file
134
lib/spotify/cspot/include/TrackQueue.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "BellTask.h"
|
||||
#include "PlaybackState.h"
|
||||
#include "TrackReference.h"
|
||||
|
||||
#include "protobuf/metadata.pb.h" // for Track, _Track, AudioFile, Episode
|
||||
|
||||
namespace bell {
|
||||
class WrappedSemaphore;
|
||||
};
|
||||
|
||||
namespace cspot {
|
||||
struct Context;
|
||||
class AccessKeyFetcher;
|
||||
class CDNAudioFile;
|
||||
|
||||
// Used in got track info event
|
||||
struct TrackInfo {
|
||||
std::string name, album, artist, imageUrl, trackId;
|
||||
uint32_t duration, number, discNumber;
|
||||
|
||||
void loadPbTrack(Track* pbTrack, const std::vector<uint8_t>& gid);
|
||||
void loadPbEpisode(Episode* pbEpisode, const std::vector<uint8_t>& gid);
|
||||
};
|
||||
|
||||
class QueuedTrack {
|
||||
public:
|
||||
QueuedTrack(TrackReference& ref, std::shared_ptr<cspot::Context> ctx,
|
||||
uint32_t requestedPosition = 0);
|
||||
~QueuedTrack();
|
||||
|
||||
enum class State {
|
||||
QUEUED,
|
||||
PENDING_META,
|
||||
KEY_REQUIRED,
|
||||
PENDING_KEY,
|
||||
CDN_REQUIRED,
|
||||
READY,
|
||||
FAILED
|
||||
};
|
||||
|
||||
std::shared_ptr<bell::WrappedSemaphore> loadedSemaphore;
|
||||
|
||||
State state = State::QUEUED; // Current state of the track
|
||||
TrackReference ref; // Holds GID, URI and Context
|
||||
TrackInfo trackInfo; // Full track information fetched from spotify, name etc
|
||||
|
||||
uint32_t requestedPosition;
|
||||
std::string identifier;
|
||||
bool loading = false;
|
||||
|
||||
// Will return nullptr if the track is not ready
|
||||
std::shared_ptr<cspot::CDNAudioFile> getAudioFile();
|
||||
|
||||
// --- Steps ---
|
||||
void stepLoadMetadata(
|
||||
Track* pbTrack, Episode* pbEpisode, std::mutex& trackListMutex,
|
||||
std::shared_ptr<bell::WrappedSemaphore> updateSemaphore);
|
||||
|
||||
void stepParseMetadata(Track* pbTrack, Episode* pbEpisode);
|
||||
|
||||
void stepLoadAudioFile(
|
||||
std::mutex& trackListMutex,
|
||||
std::shared_ptr<bell::WrappedSemaphore> updateSemaphore);
|
||||
|
||||
void stepLoadCDNUrl(const std::string& accessKey);
|
||||
|
||||
void expire();
|
||||
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
|
||||
uint64_t pendingMercuryRequest = 0;
|
||||
uint32_t pendingAudioKeyRequest = 0;
|
||||
|
||||
std::vector<uint8_t> trackId, fileId, audioKey;
|
||||
std::string cdnUrl;
|
||||
};
|
||||
|
||||
class TrackQueue : public bell::Task {
|
||||
public:
|
||||
TrackQueue(std::shared_ptr<cspot::Context> ctx,
|
||||
std::shared_ptr<cspot::PlaybackState> playbackState);
|
||||
~TrackQueue();
|
||||
|
||||
enum class SkipDirection { NEXT, PREV };
|
||||
|
||||
std::shared_ptr<bell::WrappedSemaphore> playableSemaphore;
|
||||
std::atomic<bool> notifyPending = false;
|
||||
|
||||
void runTask() override;
|
||||
void stopTask();
|
||||
|
||||
bool hasTracks();
|
||||
bool isFinished();
|
||||
bool skipTrack(SkipDirection dir, bool expectNotify = true);
|
||||
bool updateTracks(uint32_t requestedPosition = 0, bool initial = false);
|
||||
TrackInfo getTrackInfo(std::string_view identifier);
|
||||
std::shared_ptr<QueuedTrack> consumeTrack(
|
||||
std::shared_ptr<QueuedTrack> prevSong, int& offset);
|
||||
|
||||
private:
|
||||
static const int MAX_TRACKS_PRELOAD = 3;
|
||||
|
||||
std::shared_ptr<cspot::AccessKeyFetcher> accessKeyFetcher;
|
||||
std::shared_ptr<PlaybackState> playbackState;
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
std::shared_ptr<bell::WrappedSemaphore> processSemaphore;
|
||||
|
||||
std::deque<std::shared_ptr<QueuedTrack>> preloadedTracks;
|
||||
std::vector<TrackReference> currentTracks;
|
||||
std::mutex tracksMutex, runningMutex;
|
||||
|
||||
// PB data
|
||||
Track pbTrack;
|
||||
Episode pbEpisode;
|
||||
|
||||
std::string accessKey;
|
||||
|
||||
int16_t currentTracksIndex = -1;
|
||||
|
||||
bool isRunning = false;
|
||||
|
||||
void processTrack(std::shared_ptr<QueuedTrack> track);
|
||||
bool queueNextTrack(int offset = 0, uint32_t positionMs = 0);
|
||||
};
|
||||
} // namespace cspot
|
||||
36
lib/spotify/cspot/include/TrackReference.h
Normal file
36
lib/spotify/cspot/include/TrackReference.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <pb_encode.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "NanoPBHelper.h"
|
||||
#include "pb_decode.h"
|
||||
#include "protobuf/spirc.pb.h"
|
||||
|
||||
namespace cspot {
|
||||
struct TrackReference {
|
||||
TrackReference();
|
||||
|
||||
// Resolved track GID
|
||||
std::vector<uint8_t> gid;
|
||||
std::string uri, context;
|
||||
std::optional<bool> queued;
|
||||
|
||||
// Type identifier
|
||||
enum class Type { TRACK, EPISODE };
|
||||
|
||||
Type type;
|
||||
|
||||
void decodeURI();
|
||||
|
||||
bool operator==(const TrackReference& other) const;
|
||||
|
||||
// Encodes list of track references into a pb structure, used by nanopb
|
||||
static bool pbEncodeTrackList(pb_ostream_t* stream, const pb_field_t* field,
|
||||
void* const* arg);
|
||||
|
||||
static bool pbDecodeTrackList(pb_istream_t* stream, const pb_field_t* field,
|
||||
void** arg);
|
||||
};
|
||||
} // namespace cspot
|
||||
120
lib/spotify/cspot/include/Utils.h
Normal file
120
lib/spotify/cspot/include/Utils.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include <cstdio> // for snprintf, size_t
|
||||
#include <vector> // for vector
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "win32shim.h"
|
||||
#else
|
||||
|
||||
#endif
|
||||
#include <cstdint> // for uint8_t, uint64_t
|
||||
#include <cstring> // for memcpy
|
||||
#include <memory> // for unique_ptr
|
||||
#include <stdexcept> // for runtime_error
|
||||
#include <string> // for string
|
||||
|
||||
#define HMAC_SHA1_BLOCKSIZE 64
|
||||
|
||||
/**
|
||||
* @brief Returns current timestamp
|
||||
*
|
||||
* @return unsigned long long resulting timestamp in milliseconds from unix time zero
|
||||
*/
|
||||
unsigned long long getCurrentTimestamp();
|
||||
|
||||
/**
|
||||
* @brief portable 64bit equivalent of htons / htonl. aka endianess swap
|
||||
*
|
||||
* @param value input value to swap
|
||||
* @return uint64_t swapped result
|
||||
*/
|
||||
uint64_t hton64(uint64_t value);
|
||||
|
||||
std::vector<uint8_t> bigNumDivide(std::vector<uint8_t> num, int n);
|
||||
|
||||
/**
|
||||
* @brief Performs big number multiplication on two numbers
|
||||
*
|
||||
* @param num big num in vector format
|
||||
* @param n secondary number
|
||||
* @return std::vector<uint8_t> resulting number
|
||||
*/
|
||||
std::vector<uint8_t> bigNumMultiply(std::vector<uint8_t> num, int n);
|
||||
|
||||
/**
|
||||
* @brief Performs big number addition on two numbers
|
||||
*
|
||||
* @param num big num in vector format
|
||||
* @param n secondary number
|
||||
* @return std::vector<uint8_t> resulting number
|
||||
*/
|
||||
std::vector<uint8_t> bigNumAdd(std::vector<uint8_t> num, int n);
|
||||
|
||||
unsigned char h2int(char c);
|
||||
|
||||
std::string urlDecode(std::string str);
|
||||
|
||||
/**
|
||||
* @brief Converts provided hex string into binary data
|
||||
*
|
||||
* @param s string containing hex data
|
||||
* @return std::vector<uint8_t> vector containing binary data
|
||||
*/
|
||||
std::vector<uint8_t> stringHexToBytes(const std::string& s);
|
||||
|
||||
/**
|
||||
* @brief Converts provided bytes into a human readable hex string
|
||||
*
|
||||
* @param bytes vector containing binary data
|
||||
* @return std::string string containing hex representation of inputted data
|
||||
*/
|
||||
std::string bytesToHexString(const std::vector<uint8_t>& bytes);
|
||||
|
||||
/**
|
||||
* @brief Extracts given type from binary data
|
||||
*
|
||||
* @tparam T type to extract
|
||||
* @param v vector containing binary data to extract from
|
||||
* @param pos position offset
|
||||
* @return T extracted type
|
||||
*/
|
||||
template <typename T>
|
||||
T extract(const std::vector<unsigned char>& v, int pos) {
|
||||
T value;
|
||||
memcpy(&value, &v[pos], sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Packs given type into binary data
|
||||
*
|
||||
* @tparam T type of data to pack
|
||||
* @param data data to pack
|
||||
* @return std::vector<uint8_t> resulting vector containing binary data
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<uint8_t> pack(T data) {
|
||||
std::vector<std::uint8_t> rawData((std::uint8_t*)&data,
|
||||
(std::uint8_t*)&(data) + sizeof(T));
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string string_format(const std::string& format, Args... args) {
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) +
|
||||
1; // Extra space for '\0'
|
||||
if (size_s <= 0) {
|
||||
throw std::runtime_error("Error during formatting.");
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(),
|
||||
buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user