TrackProvider.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include "TrackProvider.h"
  2. #include <memory>
  3. #include "AccessKeyFetcher.h"
  4. #include "CDNTrackStream.h"
  5. #include "Logger.h"
  6. #include "MercurySession.h"
  7. #include "TrackReference.h"
  8. #include "Utils.h"
  9. #include "protobuf/metadata.pb.h"
  10. using namespace cspot;
  11. TrackProvider::TrackProvider(std::shared_ptr<cspot::Context> ctx) {
  12. this->accessKeyFetcher = std::make_shared<cspot::AccessKeyFetcher>(ctx);
  13. this->ctx = ctx;
  14. this->cdnStream =
  15. std::make_unique<cspot::CDNTrackStream>(this->accessKeyFetcher);
  16. this->trackInfo = {};
  17. }
  18. TrackProvider::~TrackProvider() {
  19. pb_release(Track_fields, &trackInfo);
  20. }
  21. std::shared_ptr<cspot::CDNTrackStream> TrackProvider::loadFromTrackRef(TrackReference& trackRef) {
  22. auto track = std::make_shared<cspot::CDNTrackStream>(this->accessKeyFetcher);
  23. this->currentTrackReference = track;
  24. this->trackIdInfo = trackRef;
  25. queryMetadata();
  26. return track;
  27. }
  28. void TrackProvider::queryMetadata() {
  29. std::string requestUrl = string_format(
  30. "hm://metadata/3/%s/%s", trackIdInfo.type == TrackReference::Type::TRACK ? "track" : "episode",
  31. bytesToHexString(trackIdInfo.gid).c_str());
  32. CSPOT_LOG(debug, "Requesting track metadata from %s", requestUrl.c_str());
  33. auto responseHandler = [this](MercurySession::Response& res) {
  34. this->onMetadataResponse(res);
  35. };
  36. // Execute the request
  37. ctx->session->execute(MercurySession::RequestType::GET, requestUrl,
  38. responseHandler);
  39. }
  40. void TrackProvider::onMetadataResponse(MercurySession::Response& res) {
  41. CSPOT_LOG(debug, "Got track metadata response");
  42. pb_release(Track_fields, &trackInfo);
  43. pbDecode(trackInfo, Track_fields, res.parts[0]);
  44. CSPOT_LOG(info, "Track name: %s", trackInfo.name);
  45. CSPOT_LOG(info, "Track duration: %d", trackInfo.duration);
  46. CSPOT_LOG(debug, "trackInfo.restriction.size() = %d",
  47. trackInfo.restriction_count);
  48. int altIndex = -1;
  49. while (!canPlayTrack(altIndex)) {
  50. altIndex++;
  51. CSPOT_LOG(info, "Trying alternative %d", altIndex);
  52. if (altIndex >= trackInfo.alternative_count) {
  53. // no alternatives for song
  54. if (!this->currentTrackReference.expired()) {
  55. auto trackRef = this->currentTrackReference.lock();
  56. trackRef->status = CDNTrackStream::Status::FAILED;
  57. trackRef->trackReady->give();
  58. }
  59. return;
  60. }
  61. }
  62. std::vector<uint8_t> trackId;
  63. std::vector<uint8_t> fileId;
  64. if (altIndex < 0) {
  65. trackId = pbArrayToVector(trackInfo.gid);
  66. for (int x = 0; x < trackInfo.file_count; x++) {
  67. if (trackInfo.file[x].format == ctx->config.audioFormat) {
  68. fileId = pbArrayToVector(trackInfo.file[x].file_id);
  69. break; // If file found stop searching
  70. }
  71. }
  72. } else {
  73. trackId = pbArrayToVector(trackInfo.alternative[altIndex].gid);
  74. for (int x = 0; x < trackInfo.alternative[altIndex].file_count; x++) {
  75. if (trackInfo.alternative[altIndex].file[x].format == ctx->config.audioFormat) {
  76. fileId =
  77. pbArrayToVector(trackInfo.alternative[altIndex].file[x].file_id);
  78. break; // If file found stop searching
  79. }
  80. }
  81. }
  82. if (!this->currentTrackReference.expired()) {
  83. auto trackRef = this->currentTrackReference.lock();
  84. auto imageId =
  85. pbArrayToVector(trackInfo.album.cover_group.image[0].file_id);
  86. trackRef->trackInfo.trackId = bytesToHexString(trackIdInfo.gid);
  87. trackRef->trackInfo.name = std::string(trackInfo.name);
  88. trackRef->trackInfo.album = std::string(trackInfo.album.name);
  89. trackRef->trackInfo.artist = std::string(trackInfo.artist[0].name);
  90. trackRef->trackInfo.imageUrl =
  91. "https://i.scdn.co/image/" + bytesToHexString(imageId);
  92. trackRef->trackInfo.duration = trackInfo.duration;
  93. }
  94. this->fetchFile(fileId, trackId);
  95. }
  96. void TrackProvider::fetchFile(const std::vector<uint8_t>& fileId,
  97. const std::vector<uint8_t>& trackId) {
  98. ctx->session->requestAudioKey(
  99. trackId, fileId,
  100. [this, fileId](bool success, const std::vector<uint8_t>& audioKey) {
  101. if (success) {
  102. CSPOT_LOG(info, "Got audio key");
  103. if (!this->currentTrackReference.expired()) {
  104. auto ref = this->currentTrackReference.lock();
  105. ref->fetchFile(fileId, audioKey);
  106. }
  107. } else {
  108. CSPOT_LOG(error, "Failed to get audio key");
  109. if (!this->currentTrackReference.expired()) {
  110. auto ref = this->currentTrackReference.lock();
  111. ref->fail();
  112. }
  113. }
  114. });
  115. }
  116. bool countryListContains(char* countryList, char* country) {
  117. uint16_t countryList_length = strlen(countryList);
  118. for (int x = 0; x < countryList_length; x += 2) {
  119. if (countryList[x] == country[0] && countryList[x + 1] == country[1]) {
  120. return true;
  121. }
  122. }
  123. return false;
  124. }
  125. bool TrackProvider::canPlayTrack(int altIndex) {
  126. if (altIndex < 0) {
  127. for (int x = 0; x < trackInfo.restriction_count; x++) {
  128. if (trackInfo.restriction[x].countries_allowed != nullptr) {
  129. return countryListContains(trackInfo.restriction[x].countries_allowed,
  130. (char*)ctx->config.countryCode.c_str());
  131. }
  132. if (trackInfo.restriction[x].countries_forbidden != nullptr) {
  133. return !countryListContains(
  134. trackInfo.restriction[x].countries_forbidden,
  135. (char*)ctx->config.countryCode.c_str());
  136. }
  137. }
  138. } else {
  139. for (int x = 0; x < trackInfo.alternative[altIndex].restriction_count;
  140. x++) {
  141. if (trackInfo.alternative[altIndex].restriction[x].countries_allowed !=
  142. nullptr) {
  143. return countryListContains(
  144. trackInfo.alternative[altIndex].restriction[x].countries_allowed,
  145. (char*)ctx->config.countryCode.c_str());
  146. }
  147. if (trackInfo.alternative[altIndex].restriction[x].countries_forbidden !=
  148. nullptr) {
  149. return !countryListContains(
  150. trackInfo.alternative[altIndex].restriction[x].countries_forbidden,
  151. (char*)ctx->config.countryCode.c_str());
  152. }
  153. }
  154. }
  155. return true;
  156. }