123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- #include "SpotifyTrack.h"
- #include "unistd.h"
- #include "MercuryManager.h"
- #include <cassert>
- #include "CspotAssert.h"
- #include "Logger.h"
- #include "ConfigJSON.h"
- SpotifyTrack::SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_ptr<TrackReference> trackReference, uint32_t position_ms, bool isPaused)
- {
- 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);
- };
- mercuryCallback episodeResponseLambda = [=](std::unique_ptr<MercuryResponse> res) {
- this->episodeInformationCallback(std::move(res), position_ms, isPaused);
- };
- if (trackReference->isEpisode)
- {
- this->reqSeqNum = this->manager->execute(MercuryType::GET, "hm://metadata/3/episode/" + bytesToHexString(trackReference->gid), episodeResponseLambda);
- }
- else
- {
- this->reqSeqNum = this->manager->execute(MercuryType::GET, "hm://metadata/3/track/" + bytesToHexString(trackReference->gid), trackResponseLambda);
- }
- }
- SpotifyTrack::~SpotifyTrack()
- {
- this->manager->unregisterMercuryCallback(this->reqSeqNum);
- this->manager->freeAudioKeyCallback();
- pb_release(Track_fields, this->trackInfo);
- pb_release(Episode_fields, this->episodeInfo);
- }
- bool SpotifyTrack::countryListContains(std::string countryList, std::string country)
- {
- for (int x = 0; x < countryList.size(); x += 2)
- {
- if (countryList.substr(x, 2) == country)
- {
- return true;
- }
- }
- return false;
- }
- bool SpotifyTrack::canPlayTrack()
- {
- for (int x = 0; x < trackInfo.restriction_count; x++)
- {
- if (trackInfo.restriction[x].countries_allowed != nullptr)
- {
- return countryListContains(std::string(trackInfo.restriction[x].countries_allowed), manager->countryCode);
- }
- if (trackInfo.restriction[x].countries_forbidden != nullptr)
- {
- return !countryListContains(std::string(trackInfo.restriction[x].countries_forbidden), manager->countryCode);
- }
- }
- return true;
- }
- void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused)
- {
- if (this->fileId.size() != 0)
- return;
- CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
- pb_release(Track_fields, trackInfo);
- pbDecode(trackInfo, Track_fields, response->parts[0]);
- 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 = 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 = pbArrayToVector(trackInfo.gid);
- this->fileId = std::vector<uint8_t>();
- for (int x = 0; x < trackInfo.file_count; x++)
- {
- if (trackInfo.file[x].format == configMan->format)
- {
- 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 = 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, position_ms, isPaused);
- }
- void SpotifyTrack::episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused)
- {
- if (this->fileId.size() != 0)
- return;
- CSPOT_LOG(debug, "Got to episode");
- CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
- pb_release(Episode_fields, episodeInfo);
- pbDecode(episodeInfo, Episode_fields, response->parts[0]);
- 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_count; x++)
- {
- if (episodeInfo.audio[x].format == AudioFormat_OGG_VORBIS_96)
- {
- this->fileId = pbArrayToVector(episodeInfo.audio[x].file_id);
- break; // If file found stop searching
- }
- }
- if (trackInfoReceived != nullptr)
- {
- auto imageId = pbArrayToVector(episodeInfo.covers->image[0].file_id);
- TrackInfo simpleTrackInfo = {
- .name = std::string(episodeInfo.name),
- .album = "",
- .artist = "",
- .imageUrl = "https://i.scdn.co/image/" + bytesToHexString(imageId),
- .duration = trackInfo.duration,
- };
- trackInfoReceived(simpleTrackInfo);
- }
- 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)
- {
- audioKeyCallback audioKeyLambda = [=](bool success, std::vector<uint8_t> res) {
- if (success)
- {
- CSPOT_LOG(info, "Successfully got audio key!");
- auto audioKey = std::vector<uint8_t>(res.begin() + 4, res.end());
- if (this->fileId.size() > 0)
- {
- this->audioStream = std::make_unique<ChunkedAudioStream>(this->fileId, audioKey, trackDuration, this->manager, position_ms, isPaused);
- loadedTrackCallback();
- }
- else
- {
- CSPOT_LOG(error, "Error while fetching audiokey...");
- }
- }
- else
- {
- auto code = ntohs(extract<uint16_t>(res, 4));
- CSPOT_LOG(error, "Error while fetching audiokey, error code: %d", code);
- }
- };
- this->manager->requestAudioKey(trackId, fileId, audioKeyLambda);
- }
|