File indexing completed on 2025-02-16 04:25:53
0001 #include "spotifyService.h" 0002 #include <QEventLoop> 0003 #include <QJsonObject> 0004 #include <QNetworkAccessManager> 0005 #include <QNetworkReply> 0006 0007 using namespace PULPO; 0008 0009 spotify::spotify() 0010 { 0011 this->scope.insert(ONTOLOGY::ALBUM, {INFO::ARTWORK}); 0012 this->scope.insert(ONTOLOGY::ARTIST, {INFO::ARTWORK, INFO::TAGS}); 0013 this->scope.insert(ONTOLOGY::TRACK, {INFO::TAGS, INFO::ARTWORK, INFO::METADATA}); 0014 connect(this, &spotify::arrayReady, this, &spotify::parse); 0015 } 0016 0017 void spotify::set(const PULPO::REQUEST &request) 0018 { 0019 qDebug() << "Setting the spotify request" << request.track; 0020 this->request = request; 0021 0022 if (!scopePass()) { 0023 ERROR(this->request) 0024 } 0025 0026 auto url = this->API; 0027 0028 QUrl encodedArtist(this->request.track[FMH::MODEL_KEY::ARTIST]); 0029 encodedArtist.toEncoded(QUrl::FullyEncoded); 0030 0031 switch (this->request.ontology) { 0032 case ONTOLOGY::ARTIST: { 0033 url.append("artist:"); 0034 url.append(encodedArtist.toString()); 0035 url.append("&type=artist&limit=5"); 0036 break; 0037 } 0038 0039 case ONTOLOGY::ALBUM: { 0040 QUrl encodedAlbum(this->request.track[FMH::MODEL_KEY::ALBUM]); 0041 encodedAlbum.toEncoded(QUrl::FullyEncoded); 0042 0043 url.append("album:"); 0044 url.append(encodedAlbum.toString()); 0045 url.append("%20artist:"); 0046 url.append(encodedArtist.toString()); 0047 url.append("&type=album"); 0048 break; 0049 } 0050 0051 case ONTOLOGY::TRACK: { 0052 QUrl encodedTrack(this->request.track[FMH::MODEL_KEY::TITLE]); 0053 encodedTrack.toEncoded(QUrl::FullyEncoded); 0054 0055 url.append("track:"); 0056 url.append(encodedTrack.toString()); 0057 0058 url.append("&type=track&limit=5"); 0059 break; 0060 } 0061 } 0062 0063 auto credentials = this->CLIENT_ID + ":" + this->CLIENT_SECRET; 0064 auto auth = credentials.toLocal8Bit().toBase64(); 0065 QString header = "Basic " + auth; 0066 0067 auto sp_request = QNetworkRequest(QUrl("https://accounts.spotify.com/api/token")); 0068 sp_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 0069 sp_request.setRawHeader("Authorization", header.toLocal8Bit()); 0070 0071 static QNetworkAccessManager *manager = new QNetworkAccessManager; 0072 QNetworkReply *reply = manager->post(sp_request, "grant_type=client_credentials"); 0073 0074 connect(reply, &QNetworkReply::finished, [this, reply, url]() { 0075 if (reply->error()) { 0076 qDebug() << reply->error(); 0077 ERROR(this->request) 0078 } 0079 0080 auto response = reply->readAll(); 0081 auto data = QJsonDocument::fromJson(response).object().toVariantMap(); 0082 auto token = data["access_token"].toString(); 0083 0084 qDebug() << "[spotify service]: " << url << token; 0085 0086 this->retrieve(url, {{"Authorization", "Bearer " + token}}); 0087 0088 reply->deleteLater(); 0089 }); 0090 } 0091 0092 void spotify::parseArtist(const QByteArray &array) 0093 { 0094 qDebug() << "trying to parse artists form spotify array"; 0095 QJsonParseError jsonParseError; 0096 QJsonDocument jsonResponse = QJsonDocument::fromJson(static_cast<QString>(array).toUtf8(), &jsonParseError); 0097 0098 if (jsonParseError.error != QJsonParseError::NoError) { 0099 ERROR(this->request) 0100 } 0101 0102 if (!jsonResponse.isObject()) { 0103 ERROR(this->request) 0104 } 0105 0106 auto data = jsonResponse.object().toVariantMap(); 0107 auto itemMap = data.value("artists").toMap().value("items"); 0108 0109 if (itemMap.isNull()) { 0110 ERROR(this->request) 0111 } 0112 0113 QList<QVariant> items = itemMap.toList(); 0114 0115 if (items.isEmpty()) { 0116 ERROR(this->request) 0117 } 0118 0119 auto root = items.first().toMap(); 0120 0121 if (this->request.info.contains(INFO::TAGS)) { 0122 QStringList stats; 0123 stats << root.value("popularity").toString(); 0124 stats << root.value("followers").toMap().value("total").toString(); 0125 0126 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::ARTIST_STAT, stats}; 0127 0128 auto genres = root.value("genres").toStringList(); 0129 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::GENRE, genres}; 0130 } 0131 0132 if (this->request.info.contains(INFO::ARTWORK)) { 0133 const auto images = root.value("images").toList(); 0134 auto albumArt_url = images.isEmpty() ? "" : images.first().toMap().value("url").toString(); 0135 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::IMAGE, albumArt_url}; 0136 } 0137 0138 emit this->responseReady(this->request, this->responses); 0139 } 0140 0141 void spotify::parseAlbum(const QByteArray &array) 0142 { 0143 QJsonParseError jsonParseError; 0144 QJsonDocument jsonResponse = QJsonDocument::fromJson(static_cast<QString>(array).toUtf8(), &jsonParseError); 0145 0146 if (jsonParseError.error != QJsonParseError::NoError) { 0147 ERROR(this->request) 0148 } 0149 0150 if (!jsonResponse.isObject()) { 0151 ERROR(this->request) 0152 } 0153 0154 QJsonObject mainJsonObject(jsonResponse.object()); 0155 auto data = mainJsonObject.toVariantMap(); 0156 auto itemMap = data.value("albums").toMap().value("items"); 0157 0158 if (itemMap.isNull()) { 0159 ERROR(this->request) 0160 } 0161 QList<QVariant> items = itemMap.toList(); 0162 0163 if (items.isEmpty()) { 0164 ERROR(this->request) 0165 } 0166 0167 if (this->request.info.contains(INFO::ARTWORK)) { 0168 auto albumArt_url = items.first().toMap().value("images").toList().first().toMap().value("url").toString(); 0169 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::IMAGE, albumArt_url}; 0170 } 0171 0172 emit this->responseReady(this->request, this->responses); 0173 } 0174 0175 void spotify::parseTrack(const QByteArray &array) 0176 { 0177 QJsonParseError jsonParseError; 0178 QJsonDocument jsonResponse = QJsonDocument::fromJson(static_cast<QString>(array).toUtf8(), &jsonParseError); 0179 0180 if (jsonParseError.error != QJsonParseError::NoError) { 0181 ERROR(this->request) 0182 } 0183 0184 if (!jsonResponse.isObject()) { 0185 ERROR(this->request) 0186 } 0187 0188 QJsonObject mainJsonObject(jsonResponse.object()); 0189 auto data = mainJsonObject.toVariantMap(); 0190 auto itemMap = data.value("tracks").toMap().value("items"); 0191 0192 if (itemMap.isNull()) { 0193 ERROR(this->request) 0194 } 0195 0196 QList<QVariant> items = itemMap.toList(); 0197 0198 if (items.isEmpty()) { 0199 ERROR(this->request) 0200 } 0201 0202 // get album title 0203 for (const auto &item : items) { 0204 auto album = item.toMap().value("album").toMap(); 0205 auto trackArtist = album.value("artists").toList().first().toMap().value("name").toString(); 0206 0207 if (trackArtist.contains(this->request.track[FMH::MODEL_KEY::ARTIST])) { 0208 if (this->request.info.contains(INFO::TAGS)) { 0209 auto popularity = item.toMap().value("popularity").toString(); 0210 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::TRACK_STAT, popularity}; 0211 } 0212 0213 if (this->request.info.contains(INFO::METADATA)) { 0214 auto trackAlbum = album.value("name").toString(); 0215 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::ALBUM_TITLE, trackAlbum}; 0216 0217 auto trackPosition = item.toMap().value("track_number").toString(); 0218 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::TRACK_NUMBER, trackPosition}; 0219 } 0220 0221 if (this->request.info.contains(INFO::ARTWORK)) { 0222 auto albumArt_url = album.value("images").toList().first().toMap().value("url").toString(); 0223 this->responses << PULPO::RESPONSE{PULPO_CONTEXT::IMAGE, albumArt_url}; 0224 } 0225 0226 } else 0227 continue; 0228 } 0229 0230 emit this->responseReady(this->request, this->responses); 0231 }