From 9b06e85f9463c08b4c44c8e022696b2d224d361e Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 11 Feb 2023 01:33:31 +0100 Subject: [PATCH] Stands4LyricsProvider: Use API for search --- src/lyrics/stands4lyricsprovider.cpp | 126 +++++++++++++++++++++++---- src/lyrics/stands4lyricsprovider.h | 13 ++- 2 files changed, 119 insertions(+), 20 deletions(-) diff --git a/src/lyrics/stands4lyricsprovider.cpp b/src/lyrics/stands4lyricsprovider.cpp index dee1265e3..b1412d2ad 100644 --- a/src/lyrics/stands4lyricsprovider.cpp +++ b/src/lyrics/stands4lyricsprovider.cpp @@ -22,20 +22,26 @@ #include #include #include +#include #include #include #include +#include +#include +#include #include "core/logging.h" #include "core/networkaccessmanager.h" #include "utilities/strutils.h" #include "lyricsfetcher.h" -#include "lyricsprovider.h" #include "stands4lyricsprovider.h" -const char *Stands4LyricsProvider::kUrl = "https://www.lyrics.com/lyrics/"; +const char *Stands4LyricsProvider::kApiUrl = "https://www.abbreviations.com/services/v2/lyrics.php"; +const char *Stands4LyricsProvider::kLyricsUrl = "https://www.lyrics.com/lyrics/"; +const char *Stands4LyricsProvider::kUID = "11363"; +const char *Stands4LyricsProvider::kTokenB64 = "b3FOYmxhV1ZKRGxIMnV4OA=="; -Stands4LyricsProvider::Stands4LyricsProvider(NetworkAccessManager *network, QObject *parent) : LyricsProvider("Stands4Lyrics", true, false, network, parent) {} +Stands4LyricsProvider::Stands4LyricsProvider(NetworkAccessManager *network, QObject *parent) : JsonLyricsProvider("Stands4Lyrics", true, false, network, parent) {} Stands4LyricsProvider::~Stands4LyricsProvider() { @@ -50,16 +56,20 @@ Stands4LyricsProvider::~Stands4LyricsProvider() { bool Stands4LyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, const int id) { - Q_UNUSED(album); - - QUrl url(kUrl + StringFixup(artist) + "/" + StringFixup(title) + ".html"); + QUrlQuery url_query; + url_query.addQueryItem(QUrl::toPercentEncoding("uid"), QUrl::toPercentEncoding(kUID)); + url_query.addQueryItem(QUrl::toPercentEncoding("tokenid"), QUrl::toPercentEncoding(QByteArray::fromBase64(kTokenB64))); + url_query.addQueryItem(QUrl::toPercentEncoding("format"), "json"); + url_query.addQueryItem(QUrl::toPercentEncoding("artist"), artist); + url_query.addQueryItem(QUrl::toPercentEncoding("album"), album); + url_query.addQueryItem(QUrl::toPercentEncoding("term"), title); + QUrl url(kApiUrl); + url.setQuery(url_query); QNetworkRequest req(url); req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); QNetworkReply *reply = network_->get(req); replies_ << reply; - QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, id, artist, title]() { HandleSearchReply(reply, id, artist, title); }); - - qLog(Debug) << "Stands4Lyrics: Sending request for" << url; + QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, id, artist, album, title]() { HandleSearchReply(reply, id, artist, album, title); }); return true; @@ -67,7 +77,88 @@ bool Stands4LyricsProvider::StartSearch(const QString &artist, const QString &al void Stands4LyricsProvider::CancelSearch(const int id) { Q_UNUSED(id); } -void Stands4LyricsProvider::HandleSearchReply(QNetworkReply *reply, const int id, const QString &artist, const QString &title) { +void Stands4LyricsProvider::HandleSearchReply(QNetworkReply *reply, const int id, const QString &artist, const QString &album, const QString &title) { + + if (!replies_.contains(reply)) return; + replies_.removeAll(reply); + QObject::disconnect(reply, nullptr, this, nullptr); + reply->deleteLater(); + + QByteArray data = ExtractData(reply); + if (data.isEmpty()) { + emit SearchFinished(id); + return; + } + + QJsonObject json_obj = ExtractJsonObj(data); + if (json_obj.isEmpty()) { + qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << album << title; + emit SearchFinished(id); + return; + } + + if (!json_obj.contains("result") || !json_obj["result"].isArray()) { + Error("Json reply is missing result.", json_obj); + emit SearchFinished(id); + return; + } + + QJsonArray json_result = json_obj["result"].toArray(); + if (json_result.isEmpty()) { + qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << album << title; + emit SearchFinished(id); + return; + } + + LyricsSearchResults results; + for (const QJsonValueRef value : json_result) { + if (!value.isObject()) { + qLog(Error) << "Stands4Lyrics: Invalid Json reply, result is not an object."; + qLog(Debug) << value; + continue; + } + QJsonObject obj = value.toObject(); + if ( + !obj.contains("song") || + !obj.contains("artist") || + !obj.contains("album") + ) { + qLog(Error) << "Stands4Lyrics: Invalid Json reply, result is missing data."; + qLog(Debug) << value; + continue; + } + QString result_artist = obj["artist"].toString(); + QString result_album = obj["album"].toString(); + QString result_title = obj["song"].toString(); + if (result_artist.compare(artist, Qt::CaseInsensitive) != 0 && + result_album.compare(album, Qt::CaseInsensitive) != 0 && + result_title.compare(title, Qt::CaseInsensitive) != 0) { + continue; + } + + SendLyricsRequest(id, result_artist, result_album, result_title); + + return; + + } + + qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << album << title; + emit SearchFinished(id); + +} + +void Stands4LyricsProvider::SendLyricsRequest(const int id, const QString &artist, const QString &album, const QString &title) { + + QUrl url(kLyricsUrl + StringFixup(artist) + "/" + StringFixup(title) + ".html"); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); + QNetworkReply *reply = network_->get(req); + replies_ << reply; + QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, id, artist, album, title]() { HandleLyricsReply(reply, id, artist, album, title); }); + +} + +void Stands4LyricsProvider::HandleLyricsReply(QNetworkReply *reply, const int id, const QString &artist, const QString &album, const QString &title) { if (!replies_.contains(reply)) return; replies_.removeAll(reply); @@ -76,32 +167,35 @@ void Stands4LyricsProvider::HandleSearchReply(QNetworkReply *reply, const int id if (reply->error() != QNetworkReply::NoError) { Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error())); - emit SearchFinished(id, LyricsSearchResults()); + emit SearchFinished(id); return; } else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { Error(QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())); - emit SearchFinished(id, LyricsSearchResults()); + emit SearchFinished(id); return; } const QByteArray data = reply->readAll(); if (data.isEmpty()) { Error("Empty reply received from server."); - emit SearchFinished(id, LyricsSearchResults()); + emit SearchFinished(id); return; } const QString lyrics = ParseLyricsFromHTML(QString::fromUtf8(data), QRegularExpression("]*>"), QRegularExpression("<\\/div>"), QRegularExpression("
]+>"), false); if (lyrics.isEmpty() || lyrics.contains("Click to search for the Lyrics on Lyrics.com", Qt::CaseInsensitive)) { - qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << title; - emit SearchFinished(id, LyricsSearchResults()); + qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << album << title; + emit SearchFinished(id); return; } - qLog(Debug) << "Stands4Lyrics: Got lyrics for" << artist << title; + qLog(Debug) << "Stands4Lyrics: Got lyrics for" << artist << album << title; LyricsSearchResult result; + result.artist = artist; + result.album = album; + result.title = title; result.lyrics = lyrics; emit SearchFinished(id, LyricsSearchResults() << result); diff --git a/src/lyrics/stands4lyricsprovider.h b/src/lyrics/stands4lyricsprovider.h index 46bc567d6..9f75b2bc8 100644 --- a/src/lyrics/stands4lyricsprovider.h +++ b/src/lyrics/stands4lyricsprovider.h @@ -26,13 +26,13 @@ #include #include -#include "lyricsprovider.h" +#include "jsonlyricsprovider.h" #include "lyricsfetcher.h" class QNetworkReply; class NetworkAccessManager; -class Stands4LyricsProvider : public LyricsProvider { +class Stands4LyricsProvider : public JsonLyricsProvider { Q_OBJECT public: @@ -43,14 +43,19 @@ class Stands4LyricsProvider : public LyricsProvider { void CancelSearch(const int id) override; private: + void SendLyricsRequest(const int id, const QString &artist, const QString &album, const QString &title); void Error(const QString &error, const QVariant &debug = QVariant()) override; static QString StringFixup(QString string); private slots: - void HandleSearchReply(QNetworkReply *reply, const int id, const QString &artist, const QString &title); + void HandleSearchReply(QNetworkReply *reply, const int id, const QString &artist, const QString &album, const QString &title); + void HandleLyricsReply(QNetworkReply *reply, const int id, const QString &artist, const QString &album, const QString &title); private: - static const char *kUrl; + static const char *kApiUrl; + static const char *kLyricsUrl; + static const char *kUID; + static const char *kTokenB64; QList replies_; };