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 }