Stands4LyricsProvider: Use API for search
This commit is contained in:
@@ -22,20 +22,26 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/networkaccessmanager.h"
|
#include "core/networkaccessmanager.h"
|
||||||
#include "utilities/strutils.h"
|
#include "utilities/strutils.h"
|
||||||
#include "lyricsfetcher.h"
|
#include "lyricsfetcher.h"
|
||||||
#include "lyricsprovider.h"
|
|
||||||
#include "stands4lyricsprovider.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() {
|
Stands4LyricsProvider::~Stands4LyricsProvider() {
|
||||||
|
|
||||||
@@ -50,16 +56,20 @@ Stands4LyricsProvider::~Stands4LyricsProvider() {
|
|||||||
|
|
||||||
bool Stands4LyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, const int id) {
|
bool Stands4LyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, const int id) {
|
||||||
|
|
||||||
Q_UNUSED(album);
|
QUrlQuery url_query;
|
||||||
|
url_query.addQueryItem(QUrl::toPercentEncoding("uid"), QUrl::toPercentEncoding(kUID));
|
||||||
QUrl url(kUrl + StringFixup(artist) + "/" + StringFixup(title) + ".html");
|
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);
|
QNetworkRequest req(url);
|
||||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
QNetworkReply *reply = network_->get(req);
|
QNetworkReply *reply = network_->get(req);
|
||||||
replies_ << reply;
|
replies_ << reply;
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, id, artist, title]() { HandleSearchReply(reply, id, artist, title); });
|
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, id, artist, album, title]() { HandleSearchReply(reply, id, artist, album, title); });
|
||||||
|
|
||||||
qLog(Debug) << "Stands4Lyrics: Sending request for" << url;
|
|
||||||
|
|
||||||
return true;
|
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::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;
|
if (!replies_.contains(reply)) return;
|
||||||
replies_.removeAll(reply);
|
replies_.removeAll(reply);
|
||||||
@@ -76,32 +167,35 @@ void Stands4LyricsProvider::HandleSearchReply(QNetworkReply *reply, const int id
|
|||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
||||||
emit SearchFinished(id, LyricsSearchResults());
|
emit SearchFinished(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
||||||
Error(QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()));
|
Error(QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()));
|
||||||
emit SearchFinished(id, LyricsSearchResults());
|
emit SearchFinished(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray data = reply->readAll();
|
const QByteArray data = reply->readAll();
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
Error("Empty reply received from server.");
|
Error("Empty reply received from server.");
|
||||||
emit SearchFinished(id, LyricsSearchResults());
|
emit SearchFinished(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString lyrics = ParseLyricsFromHTML(QString::fromUtf8(data), QRegularExpression("<div[^>]*>"), QRegularExpression("<\\/div>"), QRegularExpression("<div id=\"lyrics\"[^>]+>"), false);
|
const QString lyrics = ParseLyricsFromHTML(QString::fromUtf8(data), QRegularExpression("<div[^>]*>"), QRegularExpression("<\\/div>"), QRegularExpression("<div id=\"lyrics\"[^>]+>"), false);
|
||||||
if (lyrics.isEmpty() || lyrics.contains("Click to search for the Lyrics on Lyrics.com", Qt::CaseInsensitive)) {
|
if (lyrics.isEmpty() || lyrics.contains("Click to search for the Lyrics on Lyrics.com", Qt::CaseInsensitive)) {
|
||||||
qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << title;
|
qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << album << title;
|
||||||
emit SearchFinished(id, LyricsSearchResults());
|
emit SearchFinished(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qLog(Debug) << "Stands4Lyrics: Got lyrics for" << artist << title;
|
qLog(Debug) << "Stands4Lyrics: Got lyrics for" << artist << album << title;
|
||||||
|
|
||||||
LyricsSearchResult result;
|
LyricsSearchResult result;
|
||||||
|
result.artist = artist;
|
||||||
|
result.album = album;
|
||||||
|
result.title = title;
|
||||||
result.lyrics = lyrics;
|
result.lyrics = lyrics;
|
||||||
emit SearchFinished(id, LyricsSearchResults() << result);
|
emit SearchFinished(id, LyricsSearchResults() << result);
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,13 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "lyricsprovider.h"
|
#include "jsonlyricsprovider.h"
|
||||||
#include "lyricsfetcher.h"
|
#include "lyricsfetcher.h"
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class NetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
|
|
||||||
class Stands4LyricsProvider : public LyricsProvider {
|
class Stands4LyricsProvider : public JsonLyricsProvider {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -43,14 +43,19 @@ class Stands4LyricsProvider : public LyricsProvider {
|
|||||||
void CancelSearch(const int id) override;
|
void CancelSearch(const int id) override;
|
||||||
|
|
||||||
private:
|
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;
|
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||||
static QString StringFixup(QString string);
|
static QString StringFixup(QString string);
|
||||||
|
|
||||||
private slots:
|
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:
|
private:
|
||||||
static const char *kUrl;
|
static const char *kApiUrl;
|
||||||
|
static const char *kLyricsUrl;
|
||||||
|
static const char *kUID;
|
||||||
|
static const char *kTokenB64;
|
||||||
QList<QNetworkReply*> replies_;
|
QList<QNetworkReply*> replies_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user