idf overriding method to bring back SPDIF and fix SPI + new CSPOT (which crashes)

This commit is contained in:
Philippe G
2022-01-04 00:15:33 -08:00
parent cf1315e6a4
commit 06b637c55b
43 changed files with 2955 additions and 402 deletions

View File

@@ -33,16 +33,16 @@ bool ConfigJSON::load()
auto bitrateObject = cJSON_GetObjectItemCaseSensitive(root, "bitrate");
switch((uint16_t)cJSON_GetNumberValue(bitrateObject)){
case 320:
this->format = AudioFormat::OGG_VORBIS_320;
this->format = AudioFormat_OGG_VORBIS_320;
break;
case 160:
this->format = AudioFormat::OGG_VORBIS_160;
this->format = AudioFormat_OGG_VORBIS_160;
break;
case 96:
this->format = AudioFormat::OGG_VORBIS_96;
this->format = AudioFormat_OGG_VORBIS_96;
break;
default:
this->format = AudioFormat::OGG_VORBIS_320;
this->format = AudioFormat_OGG_VORBIS_320;
break;
}
}
@@ -59,7 +59,7 @@ bool ConfigJSON::load()
// Set default values
this->volume = 32767;
this->deviceName = defaultDeviceName;
this->format = AudioFormat::OGG_VORBIS_160;
this->format = AudioFormat_OGG_VORBIS_160;
}
return true;
}
@@ -76,13 +76,13 @@ bool ConfigJSON::save()
obj["volume"] = this->volume;
obj["deviceName"] = this->deviceName;
switch(this->format){
case AudioFormat::OGG_VORBIS_320:
case AudioFormat_OGG_VORBIS_320:
obj["bitrate"] = 320;
break;
case AudioFormat::OGG_VORBIS_160:
case AudioFormat_OGG_VORBIS_160:
obj["bitrate"] = 160;
break;
case AudioFormat::OGG_VORBIS_96:
case AudioFormat_OGG_VORBIS_96:
obj["bitrate"] = 96;
break;
default:

View File

@@ -103,7 +103,7 @@ void LoginBlob::loadUserPass(const std::string &username, const std::string &pas
{
this->username = username;
this->authData = std::vector<uint8_t>(password.begin(), password.end());
this->authType = static_cast<uint32_t>(AuthenticationType::AUTHENTICATION_USER_PASS);
this->authType = static_cast<uint32_t>(AuthenticationType_AUTHENTICATION_USER_PASS);
}
void LoginBlob::loadJson(const std::string &json)

View File

@@ -11,6 +11,7 @@ std::map<MercuryType, std::string> MercuryTypeMap({
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, +1, 1)
{
tempMercuryHeader = Header_init_default;
this->timeProvider = std::make_shared<TimeProvider>();
this->callbacks = std::map<uint64_t, mercuryCallback>();
this->subscriptions = std::map<std::string, mercuryCallback>();
@@ -27,6 +28,11 @@ MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("me
};
}
MercuryManager::~MercuryManager()
{
pbFree(Header_fields, &tempMercuryHeader);
}
bool MercuryManager::timeoutHandler()
{
if (!isRunning) return true;
@@ -257,9 +263,10 @@ void MercuryManager::updateQueue() {
{
auto response = std::make_unique<MercuryResponse>(packet->data);
if (this->subscriptions.count(response->mercuryHeader.uri.value()) > 0)
auto uri = std::string(response->mercuryHeader.uri);
if (this->subscriptions.count(uri) > 0)
{
this->subscriptions[response->mercuryHeader.uri.value()](std::move(response));
this->subscriptions[uri](std::move(response));
//this->subscriptions.erase(std::string(response->mercuryHeader.uri));
}
break;
@@ -288,9 +295,8 @@ uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCal
// Construct mercury header
CSPOT_LOG(debug, "executing MercuryType %s", MercuryTypeMap[method].c_str());
Header mercuryHeader;
mercuryHeader.uri = uri;
mercuryHeader.method = MercuryTypeMap[method];
tempMercuryHeader.uri = (char *)(uri.c_str());
tempMercuryHeader.method = (char *)(MercuryTypeMap[method].c_str());
// GET and SEND are actually the same. Therefore the override
// The difference between them is only in header's method
@@ -299,7 +305,7 @@ uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCal
method = MercuryType::SEND;
}
auto headerBytes = encodePb(mercuryHeader);
auto headerBytes = pbEncode(Header_fields, &tempMercuryHeader);
// Register a subscription when given method is called
if (method == MercuryType::SUB)

View File

@@ -3,10 +3,15 @@
MercuryResponse::MercuryResponse(std::vector<uint8_t> &data)
{
// this->mercuryHeader = std::make_unique<Header>();
this->mercuryHeader = Header_init_default;
this->parts = mercuryParts(0);
this->parseResponse(data);
}
MercuryResponse::~MercuryResponse() {
pbFree(Header_fields, &mercuryHeader);
}
void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
{
auto sequenceLength = ntohs(extract<uint16_t>(data, 0));
@@ -29,5 +34,5 @@ void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
pos += 2 + partSize;
}
this->mercuryHeader = decodePb<Header>(headerBytes);
pbDecode(this->mercuryHeader, Header_fields, headerBytes);
}

View File

@@ -5,35 +5,56 @@
PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
{
this->timeProvider = timeProvider;
innerFrame = {};
remoteFrame = {};
// Prepare default state
innerFrame.state.emplace();
innerFrame.state->position_ms = 0;
innerFrame.state->status = PlayStatus::kPlayStatusStop;
innerFrame.state->position_measured_at = 0;
innerFrame.state->shuffle = false;
innerFrame.state->repeat = false;
innerFrame.state.has_position_ms = true;
innerFrame.state.position_ms = 0;
innerFrame.device_state.emplace();
innerFrame.device_state->sw_version = swVersion;
innerFrame.device_state->is_active = false;
innerFrame.device_state->can_play = true;
innerFrame.device_state->volume = configMan->volume;
innerFrame.device_state->name = configMan->deviceName;
innerFrame.state.status = PlayStatus_kPlayStatusStop;
innerFrame.state.has_status = true;
innerFrame.state.position_measured_at = 0;
innerFrame.state.has_position_measured_at = true;
innerFrame.state.shuffle = false;
innerFrame.state.has_shuffle = true;
innerFrame.state.repeat = false;
innerFrame.state.has_repeat = true;
innerFrame.device_state.sw_version = (char*) swVersion;
innerFrame.device_state.is_active = false;
innerFrame.device_state.has_is_active = true;
innerFrame.device_state.can_play = true;
innerFrame.device_state.has_can_play = true;
innerFrame.device_state.volume = configMan->volume;
innerFrame.device_state.has_volume = true;
innerFrame.device_state.name = (char*) configMan->deviceName.c_str();
// Prepare player's capabilities
innerFrame.device_state->capabilities = std::vector<Capability>();
addCapability(CapabilityType::kCanBePlayer, 1);
addCapability(CapabilityType::kDeviceType, 4);
addCapability(CapabilityType::kGaiaEqConnectId, 1);
addCapability(CapabilityType::kSupportsLogout, 0);
addCapability(CapabilityType::kIsObservable, 1);
addCapability(CapabilityType::kVolumeSteps, 64);
addCapability(CapabilityType::kSupportedContexts, -1,
addCapability(CapabilityType_kCanBePlayer, 1);
addCapability(CapabilityType_kDeviceType, 4);
addCapability(CapabilityType_kGaiaEqConnectId, 1);
addCapability(CapabilityType_kSupportsLogout, 0);
addCapability(CapabilityType_kIsObservable, 1);
addCapability(CapabilityType_kVolumeSteps, 64);
addCapability(CapabilityType_kSupportedContexts, -1,
std::vector<std::string>({"album", "playlist", "search", "inbox",
"toplist", "starred", "publishedstarred", "track"}));
addCapability(CapabilityType::kSupportedTypes, -1,
addCapability(CapabilityType_kSupportedTypes, -1,
std::vector<std::string>({"audio/local", "audio/track", "audio/episode", "local", "track"}));
innerFrame.device_state.capabilities_count = 8;
}
PlayerState::~PlayerState() {
pbFree(Frame_fields, &innerFrame);
pbFree(Frame_fields, &remoteFrame);
}
void PlayerState::setPlaybackState(const PlaybackState state)
@@ -42,38 +63,40 @@ void PlayerState::setPlaybackState(const PlaybackState state)
{
case PlaybackState::Loading:
// Prepare the playback at position 0
innerFrame.state->status = PlayStatus::kPlayStatusPause;
innerFrame.state->position_ms = 0;
innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp();
innerFrame.state.status = PlayStatus_kPlayStatusPause;
innerFrame.state.position_ms = 0;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
break;
case PlaybackState::Playing:
innerFrame.state->status = PlayStatus::kPlayStatusPlay;
innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp();
innerFrame.state.status = PlayStatus_kPlayStatusPlay;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
break;
case PlaybackState::Stopped:
break;
case PlaybackState::Paused:
// Update state and recalculate current song position
innerFrame.state->status = PlayStatus::kPlayStatusPause;
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state->position_measured_at.value();
this->updatePositionMs(innerFrame.state->position_ms.value() + diff);
innerFrame.state.status = PlayStatus_kPlayStatusPause;
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state.position_measured_at;
this->updatePositionMs(innerFrame.state.position_ms + diff);
break;
}
}
bool PlayerState::isActive()
{
return innerFrame.device_state->is_active.value();
return innerFrame.device_state.is_active;
}
bool PlayerState::nextTrack()
{
innerFrame.state->playing_track_index.value()++;
if (innerFrame.state.repeat) return true;
if (innerFrame.state->playing_track_index >= innerFrame.state->track.size())
innerFrame.state.playing_track_index++;
if (innerFrame.state.playing_track_index >= innerFrame.state.track_count)
{
innerFrame.state->playing_track_index = 0;
if (!innerFrame.state->repeat)
innerFrame.state.playing_track_index = 0;
if (!innerFrame.state.repeat)
{
setPlaybackState(PlaybackState::Paused);
return false;
@@ -85,44 +108,46 @@ bool PlayerState::nextTrack()
void PlayerState::prevTrack()
{
if (innerFrame.state->playing_track_index > 0)
if (innerFrame.state.playing_track_index > 0)
{
innerFrame.state->playing_track_index.value()--;
innerFrame.state.playing_track_index--;
}
else if (innerFrame.state->repeat)
else if (innerFrame.state.repeat)
{
innerFrame.state->playing_track_index = innerFrame.state->track.size() - 1;
innerFrame.state.playing_track_index = innerFrame.state.track_count - 1;
}
}
void PlayerState::setActive(bool isActive)
{
innerFrame.device_state->is_active = isActive;
innerFrame.device_state.is_active = isActive;
if (isActive)
{
innerFrame.device_state->became_active_at = timeProvider->getSyncedTimestamp();
innerFrame.device_state.became_active_at = timeProvider->getSyncedTimestamp();
innerFrame.device_state.has_became_active_at = true;
}
}
void PlayerState::updatePositionMs(uint32_t position)
{
innerFrame.state->position_ms = position;
innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp();
innerFrame.state.position_ms = position;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
}
void PlayerState::updateTracks()
{
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state->track.size());
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
// innerFrame.state->context_uri = remoteFrame.state->context_uri == nullptr ? nullptr : strdup(otherFrame->state->context_uri);
std::copy(std::begin(remoteFrame.state.track), std::end(remoteFrame.state.track), std::begin(innerFrame.state.track));
innerFrame.state.track_count = remoteFrame.state.track_count;
innerFrame.state.has_playing_track_index = true;
innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
innerFrame.state->track = remoteFrame.state->track;
innerFrame.state->playing_track_index = remoteFrame.state->playing_track_index;
if (remoteFrame.state->repeat.value())
if (remoteFrame.state.repeat)
{
setRepeat(true);
}
if (remoteFrame.state->shuffle.value())
if (remoteFrame.state.shuffle)
{
setShuffle(true);
}
@@ -130,70 +155,87 @@ void PlayerState::updateTracks()
void PlayerState::setVolume(uint32_t volume)
{
innerFrame.device_state->volume = volume;
innerFrame.device_state.volume = volume;
configMan->volume = volume;
configMan->save();
}
void PlayerState::setShuffle(bool shuffle)
{
innerFrame.state->shuffle = shuffle;
innerFrame.state.shuffle = shuffle;
if (shuffle)
{
// Put current song at the begining
auto tmp = innerFrame.state->track.at(0);
innerFrame.state->track.at(0) = innerFrame.state->track.at(innerFrame.state->playing_track_index.value());
innerFrame.state->track.at(innerFrame.state->playing_track_index.value()) = tmp;
auto tmp = innerFrame.state.track[0];
innerFrame.state.track[0] = innerFrame.state.track[innerFrame.state.playing_track_index];
innerFrame.state.track[innerFrame.state.playing_track_index] = tmp;
// Shuffle current tracks
for (int x = 1; x < innerFrame.state->track.size() - 1; x++)
for (int x = 1; x < innerFrame.state.track_count - 1; x++)
{
auto j = x + (std::rand() % (innerFrame.state->track.size() - x));
tmp = innerFrame.state->track.at(j);
innerFrame.state->track.at(j) = innerFrame.state->track.at(x);
innerFrame.state->track.at(x) = tmp;
auto j = x + (std::rand() % (innerFrame.state.track_count - x));
tmp = innerFrame.state.track[j];
innerFrame.state.track[j] = innerFrame.state.track[x];
innerFrame.state.track[x] = tmp;
}
innerFrame.state->playing_track_index = 0;
innerFrame.state.playing_track_index = 0;
}
}
void PlayerState::setRepeat(bool repeat)
{
innerFrame.state->repeat = repeat;
innerFrame.state.repeat = repeat;
}
std::shared_ptr<TrackReference> PlayerState::getCurrentTrack()
{
// Wrap current track in a class
return std::make_shared<TrackReference>(&innerFrame.state->track.at(innerFrame.state->playing_track_index.value()));
return std::make_shared<TrackReference>(&innerFrame.state.track[innerFrame.state.playing_track_index]);
}
std::vector<uint8_t> PlayerState::encodeCurrentFrame(MessageType typ)
{
// Prepare current frame info
innerFrame.version = 1;
innerFrame.ident = deviceId;
innerFrame.ident = (char *) deviceId;
innerFrame.seq_nr = this->seqNum;
innerFrame.protocol_version = protocolVersion;
innerFrame.protocol_version = (char*) protocolVersion;
innerFrame.typ = typ;
innerFrame.state_update_id = timeProvider->getSyncedTimestamp();
innerFrame.has_version = true;
innerFrame.has_seq_nr = true;
innerFrame.recipient_count = 0;
innerFrame.has_state = true;
innerFrame.has_device_state = true;
innerFrame.has_typ = true;
innerFrame.has_state_update_id = true;
this->seqNum += 1;
auto fram = encodePb(innerFrame);
return fram;
return pbEncode(Frame_fields, &innerFrame);
}
// Wraps messy nanopb setters. @TODO: find a better way to handle this
void PlayerState::addCapability(CapabilityType typ, int intValue, std::vector<std::string> stringValue)
{
auto capability = Capability();
capability.typ = typ;
innerFrame.device_state.capabilities[capabilityIndex].has_typ = true;
this->innerFrame.device_state.capabilities[capabilityIndex].typ = typ;
if (intValue != -1)
{
capability.intValue = std::vector<int64_t>({intValue});
this->innerFrame.device_state.capabilities[capabilityIndex].intValue[0] = intValue;
this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count = 1;
}
else
{
this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count = 0;
}
capability.stringValue = stringValue;
innerFrame.device_state->capabilities.push_back(capability);
for (int x = 0; x < stringValue.size(); x++)
{
stringValue[x].copy(this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x], stringValue[x].size());
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x][stringValue[x].size()] = '\0';
}
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue_count = stringValue.size();
this->capabilityIndex += 1;
}

View File

@@ -6,11 +6,24 @@ using random_bytes_engine = std::independent_bits_engine<std::default_random_eng
Session::Session()
{
this->clientHello = ClientHello_init_default;
this->apResponse = APResponseMessage_init_default;
this->authRequest = ClientResponseEncrypted_init_default;
this->clientResPlaintext = ClientResponsePlaintext_init_default;
// Generates the public and priv key
this->crypto = std::make_unique<Crypto>();
this->shanConn = std::make_shared<ShannonConnection>();
}
Session::~Session()
{
pbFree(ClientHello_fields, &clientHello);
pbFree(APResponseMessage_fields, &apResponse);
pbFree(ClientResponseEncrypted_fields, &authRequest);
pbFree(ClientResponsePlaintext_fields, &clientResPlaintext);
}
void Session::connect(std::unique_ptr<PlainConnection> connection)
{
this->conn = std::move(connection);
@@ -37,16 +50,16 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
authBlob = blob;
// prepare authentication request proto
authRequest.login_credentials.username = blob->username;
authRequest.login_credentials.auth_data = blob->authData;
authRequest.login_credentials.typ = static_cast<AuthenticationType>(blob->authType);
authRequest.system_info.cpu_family = CpuFamily::CPU_UNKNOWN;
authRequest.system_info.os = Os::OS_UNKNOWN;
authRequest.system_info.system_information_string = std::string(informationString);
authRequest.system_info.device_id = std::string(deviceId);
authRequest.version_string = std::string(versionString);
authRequest.login_credentials.username = (char *)(blob->username.c_str());
authRequest.login_credentials.auth_data = vectorToPbArray(blob->authData);
authRequest.login_credentials.typ = (AuthenticationType) blob->authType;
authRequest.system_info.cpu_family = CpuFamily_CPU_UNKNOWN;
authRequest.system_info.os = Os_OS_UNKNOWN;
authRequest.system_info.system_information_string = (char *)informationString;
authRequest.system_info.device_id = (char *)deviceId;
authRequest.version_string = (char *)versionString;
auto data = encodePb(authRequest);
auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest);
// Send login request
this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data);
@@ -82,11 +95,11 @@ void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
CSPOT_LOG(debug, "Received AP hello response");
// Decode the response
auto skipSize = std::vector<uint8_t>(data.begin() + 4, data.end());
apResponse = decodePb<APResponseMessage>(skipSize);
pbDecode(apResponse, APResponseMessage_fields, skipSize);
auto kkEy = apResponse.challenge->login_crypto_challenge.diffie_hellman->gs;
auto diffieKey = std::vector<uint8_t>(apResponse.challenge.login_crypto_challenge.diffie_hellman.gs, apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96);
// Compute the diffie hellman shared key based on the response
auto sharedKey = this->crypto->dhCalculateShared(kkEy);
auto sharedKey = this->crypto->dhCalculateShared(diffieKey);
// Init client packet + Init server packets are required for the hmac challenge
data.insert(data.begin(), helloPacket.begin(), helloPacket.end());
@@ -106,11 +119,14 @@ void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
auto lastVec = std::vector<uint8_t>(resultData.begin(), resultData.begin() + 0x14);
// Digest generated!
clientResPlaintext.login_crypto_response = {};
clientResPlaintext.login_crypto_response.diffie_hellman.emplace();
clientResPlaintext.login_crypto_response.diffie_hellman->hmac = crypto->sha1HMAC(lastVec, data);
auto digest = crypto->sha1HMAC(lastVec, data);
clientResPlaintext.login_crypto_response.has_diffie_hellman = true;
auto resultPacket = encodePb(clientResPlaintext);
std::copy(digest.begin(),
digest.end(),
clientResPlaintext.login_crypto_response.diffie_hellman.hmac);
auto resultPacket = pbEncode(ClientResponsePlaintext_fields, &clientResPlaintext);
auto emptyPrefix = std::vector<uint8_t>(0);
@@ -136,20 +152,27 @@ std::vector<uint8_t> Session::sendClientHelloRequest()
this->crypto->dhInit();
// Copy the public key into diffiehellman hello packet
clientHello.login_crypto_hello.diffie_hellman.emplace();
clientHello.feature_set.emplace();
clientHello.login_crypto_hello.diffie_hellman->gc = this->crypto->publicKey;
clientHello.login_crypto_hello.diffie_hellman->server_keys_known = 1;
clientHello.build_info.product = Product::PRODUCT_PARTNER;
clientHello.build_info.platform = Platform::PLATFORM_LINUX_X86;
clientHello.build_info.version = 112800721;
clientHello.feature_set->autoupdate2 = true;
clientHello.cryptosuites_supported = std::vector<Cryptosuite>({Cryptosuite::CRYPTO_SUITE_SHANNON});
clientHello.padding = std::vector<uint8_t>({0x1E});
std::copy(this->crypto->publicKey.begin(),
this->crypto->publicKey.end(),
clientHello.login_crypto_hello.diffie_hellman.gc);
clientHello.login_crypto_hello.diffie_hellman.server_keys_known = 1;
clientHello.build_info.product = Product_PRODUCT_PARTNER;
clientHello.build_info.platform = Platform2_PLATFORM_LINUX_X86;
clientHello.build_info.version = SPOTIFY_VERSION;
clientHello.feature_set.autoupdate2 = true;
clientHello.cryptosuites_supported[0] = Cryptosuite_CRYPTO_SUITE_SHANNON;
clientHello.padding[0] = 0x1E;
clientHello.has_feature_set = true;
clientHello.login_crypto_hello.has_diffie_hellman = true;
clientHello.has_padding = true;
clientHello.has_feature_set = true;
// Generate the random nonce
clientHello.client_nonce = crypto->generateVectorWithRandomData(16);
auto vecData = encodePb(clientHello);
auto nonce = crypto->generateVectorWithRandomData(16);
std::copy(nonce.begin(), nonce.end(), clientHello.client_nonce);
auto vecData = pbEncode(ClientHello_fields, &clientHello);
auto prefix = std::vector<uint8_t>({0x00, 0x04});
return this->conn->sendPrefixPacket(prefix, vecData);
}

View File

@@ -5,6 +5,8 @@
// #define NDEBUG
#include <assert.h>
using std::size_t;
static inline uint32_t rotl(uint32_t n, unsigned int c)
{
const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2.
@@ -440,4 +442,4 @@ void Shannon::finish(std::vector<uint8_t> &bufVec)
break;
}
}
}
}

View File

@@ -25,7 +25,7 @@ SpircController::SpircController(std::shared_ptr<MercuryManager> manager,
void SpircController::subscribe() {
mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
// this->trackInformationCallback(std::move(res));
sendCmd(MessageType::kMessageTypeHello);
sendCmd(MessageType_kMessageTypeHello);
CSPOT_LOG(debug, "Sent kMessageTypeHello!");
};
mercuryCallback subLambda = [=](std::unique_ptr<MercuryResponse> res) {
@@ -60,7 +60,7 @@ void SpircController::disconnect(void) {
}
void SpircController::playToggle() {
if (state->innerFrame.state->status.value() == PlayStatus::kPlayStatusPause) {
if (state->innerFrame.state.status == PlayStatus_kPlayStatusPause) {
setPause(false);
} else {
setPause(true);
@@ -68,8 +68,8 @@ void SpircController::playToggle() {
}
void SpircController::adjustVolume(int by) {
if (state->innerFrame.device_state->volume.has_value()) {
int volume = state->innerFrame.device_state->volume.value() + by;
if (state->innerFrame.device_state.has_volume) {
int volume = state->innerFrame.device_state.volume + by;
if (volume < 0) volume = 0;
else if (volume > MAX_VOLUME) volume = MAX_VOLUME;
setVolume(volume);
@@ -103,45 +103,45 @@ void SpircController::prevSong() {
}
void SpircController::handleFrame(std::vector<uint8_t> &data) {
state->remoteFrame = decodePb<Frame>(data);
pbDecode(state->remoteFrame, Frame_fields, data);
switch (state->remoteFrame.typ.value()) {
case MessageType::kMessageTypeNotify: {
switch (state->remoteFrame.typ) {
case MessageType_kMessageTypeNotify: {
CSPOT_LOG(debug, "Notify frame");
// Pause the playback if another player took control
if (state->isActive() &&
state->remoteFrame.device_state->is_active.value()) {
state->remoteFrame.device_state.is_active) {
disconnect();
}
break;
}
case MessageType::kMessageTypeSeek: {
case MessageType_kMessageTypeSeek: {
CSPOT_LOG(debug, "Seek command");
sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position.value());
state->updatePositionMs(state->remoteFrame.position.value());
this->player->seekMs(state->remoteFrame.position.value());
sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position);
state->updatePositionMs(state->remoteFrame.position);
this->player->seekMs(state->remoteFrame.position);
notify();
break;
}
case MessageType::kMessageTypeVolume:
sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume.value());
setVolume(state->remoteFrame.volume.value());
case MessageType_kMessageTypeVolume:
sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume);
setVolume(state->remoteFrame.volume);
break;
case MessageType::kMessageTypePause:
case MessageType_kMessageTypePause:
setPause(true);
break;
case MessageType::kMessageTypePlay:
case MessageType_kMessageTypePlay:
setPause(false);
break;
case MessageType::kMessageTypeNext:
case MessageType_kMessageTypeNext:
sendEvent(CSpotEventType::NEXT);
nextSong();
break;
case MessageType::kMessageTypePrev:
case MessageType_kMessageTypePrev:
sendEvent(CSpotEventType::PREV);
prevSong();
break;
case MessageType::kMessageTypeLoad: {
case MessageType_kMessageTypeLoad: {
CSPOT_LOG(debug, "Load frame!");
state->setActive(true);
@@ -154,25 +154,25 @@ void SpircController::handleFrame(std::vector<uint8_t> &data) {
// bool isPaused = (state->remoteFrame.state->status.value() ==
// PlayStatus::kPlayStatusPlay) ? false : true;
loadTrack(state->remoteFrame.state->position_ms.value(), false);
state->updatePositionMs(state->remoteFrame.state->position_ms.value());
loadTrack(state->remoteFrame.state.position_ms, false);
state->updatePositionMs(state->remoteFrame.state.position_ms);
this->notify();
break;
}
case MessageType::kMessageTypeReplace: {
case MessageType_kMessageTypeReplace: {
CSPOT_LOG(debug, "Got replace frame!");
break;
}
case MessageType::kMessageTypeShuffle: {
case MessageType_kMessageTypeShuffle: {
CSPOT_LOG(debug, "Got shuffle frame");
state->setShuffle(state->remoteFrame.state->shuffle.value());
state->setShuffle(state->remoteFrame.state.shuffle);
this->notify();
break;
}
case MessageType::kMessageTypeRepeat: {
case MessageType_kMessageTypeRepeat: {
CSPOT_LOG(debug, "Got repeat frame");
state->setRepeat(state->remoteFrame.state->repeat.value());
state->setRepeat(state->remoteFrame.state.repeat);
this->notify();
break;
}
@@ -194,7 +194,7 @@ void SpircController::loadTrack(uint32_t position_ms, bool isPaused) {
}
void SpircController::notify() {
this->sendCmd(MessageType::kMessageTypeNotify);
this->sendCmd(MessageType_kMessageTypeNotify);
}
void SpircController::sendEvent(CSpotEventType eventType, std::variant<TrackInfo, int, bool> data) {
@@ -218,7 +218,6 @@ void SpircController::setEventHandler(cspotEventHandler callback) {
info.imageUrl = track.imageUrl;
info.name = track.name;
info.duration = track.duration;
this->sendEvent(CSpotEventType::TRACK_INFO, info);
});
}

View File

@@ -10,6 +10,8 @@ SpotifyTrack::SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_
{
this->manager = manager;
this->fileId = std::vector<uint8_t>();
episodeInfo = Episode_init_default;
trackInfo = Track_init_default;
mercuryCallback trackResponseLambda = [=](std::unique_ptr<MercuryResponse> res) {
this->trackInformationCallback(std::move(res), position_ms, isPaused);
@@ -33,6 +35,8 @@ SpotifyTrack::~SpotifyTrack()
{
this->manager->unregisterMercuryCallback(this->reqSeqNum);
this->manager->freeAudioKeyCallback();
pbFree(Track_fields, &this->trackInfo);
pbFree(Episode_fields, &this->episodeInfo);
}
bool SpotifyTrack::countryListContains(std::string countryList, std::string country)
@@ -47,18 +51,18 @@ bool SpotifyTrack::countryListContains(std::string countryList, std::string coun
return false;
}
bool SpotifyTrack::canPlayTrack(std::vector<Restriction>& restrictions)
bool SpotifyTrack::canPlayTrack()
{
for (int x = 0; x < restrictions.size(); x++)
for (int x = 0; x < trackInfo.restriction_count; x++)
{
if (restrictions[x].countries_allowed.has_value())
if (trackInfo.restriction[x].countries_allowed != nullptr)
{
return countryListContains(restrictions[x].countries_allowed.value(), manager->countryCode);
return countryListContains(std::string(trackInfo.restriction[x].countries_allowed), manager->countryCode);
}
if (restrictions[x].countries_forbidden.has_value())
if (trackInfo.restriction[x].countries_forbidden != nullptr)
{
return !countryListContains(restrictions[x].countries_forbidden.value(), manager->countryCode);
return !countryListContains(std::string(trackInfo.restriction[x].countries_forbidden), manager->countryCode);
}
}
@@ -71,48 +75,49 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
return;
CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
trackInfo = decodePb<Track>(response->parts[0]);
pbDecode(trackInfo, Track_fields, response->parts[0]);
CSPOT_LOG(info, "Track name: %s", trackInfo.name.value().c_str());
CSPOT_LOG(info, "Track duration: %d", trackInfo.duration.value());
CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction.size());
CSPOT_LOG(info, "Track name: %s", trackInfo.name);
CSPOT_LOG(info, "Track duration: %d", trackInfo.duration);
CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction_count);
int altIndex = 0;
while (!canPlayTrack(trackInfo.restriction))
while (!canPlayTrack())
{
trackInfo.restriction = trackInfo.alternative[altIndex].restriction;
trackInfo.restriction_count = trackInfo.alternative[altIndex].restriction_count;
trackInfo.gid = trackInfo.alternative[altIndex].gid;
trackInfo.file = trackInfo.alternative[altIndex].file;
altIndex++;
CSPOT_LOG(info, "Trying alternative %d", altIndex);
}
auto trackId = trackInfo.gid.value();
auto trackId = pbArrayToVector(trackInfo.gid);
this->fileId = std::vector<uint8_t>();
for (int x = 0; x < trackInfo.file.size(); x++)
for (int x = 0; x < trackInfo.file_count; x++)
{
if (trackInfo.file[x].format == configMan->format)
{
this->fileId = trackInfo.file[x].file_id.value();
this->fileId = pbArrayToVector(trackInfo.file[x].file_id);
break; // If file found stop searching
}
}
if (trackInfoReceived != nullptr)
{
auto imageId = pbArrayToVector(trackInfo.album.cover_group.image[0].file_id);
TrackInfo simpleTrackInfo = {
.name = trackInfo.name.value(),
.album = trackInfo.album.value().name.value(),
.artist = trackInfo.artist[0].name.value(),
.imageUrl = "https://i.scdn.co/image/" + bytesToHexString(trackInfo.album.value().cover_group.value().image[0].file_id.value()),
.duration = trackInfo.duration.value(),
.name = std::string(trackInfo.name),
.album = std::string(trackInfo.album.name),
.artist = std::string(trackInfo.artist[0].name),
.imageUrl = "https://i.scdn.co/image/" + bytesToHexString(imageId),
.duration = trackInfo.duration,
};
trackInfoReceived(simpleTrackInfo);
}
this->requestAudioKey(this->fileId, trackId, trackInfo.duration.value(), position_ms, isPaused);
this->requestAudioKey(this->fileId, trackId, trackInfo.duration, position_ms, isPaused);
}
void SpotifyTrack::episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused)
@@ -121,23 +126,23 @@ void SpotifyTrack::episodeInformationCallback(std::unique_ptr<MercuryResponse> r
return;
CSPOT_LOG(debug, "Got to episode");
CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
episodeInfo = decodePb<Episode>(response->parts[0]);
pbDecode(episodeInfo, Episode_fields, response->parts[0]);
CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name.value().c_str());
CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name);
this->fileId = std::vector<uint8_t>();
// TODO: option to set file quality
for (int x = 0; x < episodeInfo.audio.size(); x++)
for (int x = 0; x < episodeInfo.audio_count; x++)
{
if (episodeInfo.audio[x].format == AudioFormat::OGG_VORBIS_96)
if (episodeInfo.audio[x].format == AudioFormat_OGG_VORBIS_96)
{
this->fileId = episodeInfo.audio[x].file_id.value();
this->fileId = pbArrayToVector(episodeInfo.audio[x].file_id);
break; // If file found stop searching
}
}
this->requestAudioKey(episodeInfo.gid.value(), this->fileId, episodeInfo.duration.value(), position_ms, isPaused);
this->requestAudioKey(pbArrayToVector(episodeInfo.gid), this->fileId, episodeInfo.duration, position_ms, isPaused);
}
void SpotifyTrack::requestAudioKey(std::vector<uint8_t> fileId, std::vector<uint8_t> trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused)

View File

@@ -3,13 +3,13 @@
TrackReference::TrackReference(TrackRef *ref)
{
if (ref->gid.has_value())
if (ref->gid != nullptr)
{
gid = ref->gid.value();
gid = pbArrayToVector(ref->gid);
}
else if (ref->uri.has_value())
else if (ref->uri != nullptr)
{
auto uri = ref->uri.value();
auto uri = std::string(ref->uri);
auto idString = uri.substr(uri.find_last_of(":") + 1, uri.size());
CSPOT_LOG(debug, "idString = %s", idString.c_str());
gid = base62Decode(idString);
@@ -19,6 +19,7 @@ TrackReference::TrackReference(TrackRef *ref)
TrackReference::~TrackReference()
{
//pbFree(TrackRef_fields, &ref);
}
std::vector<uint8_t> TrackReference::base62Decode(std::string uri)