Overhaul meta-data search
* Improve coding style * Remove useless code comments * Hide legacy providers by default * Add Tekstowo to have at least one functioning provider for lyrics again * Enable query logging only if an environment variable is set * Use Tekstowo in example JavaScript
This commit is contained in:
parent
dace19b2bf
commit
e69278634f
|
@ -52,6 +52,7 @@ set(WIDGETS_HEADER_FILES
|
||||||
dbquery/musicbrainz.h
|
dbquery/musicbrainz.h
|
||||||
dbquery/makeitpersonal.h
|
dbquery/makeitpersonal.h
|
||||||
dbquery/lyricswikia.h
|
dbquery/lyricswikia.h
|
||||||
|
dbquery/tekstowo.h
|
||||||
gui/dbquerywidget.h
|
gui/dbquerywidget.h
|
||||||
misc/networkaccessmanager.h
|
misc/networkaccessmanager.h
|
||||||
renamingutility/filesystemitem.h
|
renamingutility/filesystemitem.h
|
||||||
|
@ -82,6 +83,7 @@ set(WIDGETS_SRC_FILES
|
||||||
dbquery/musicbrainz.cpp
|
dbquery/musicbrainz.cpp
|
||||||
dbquery/makeitpersonal.cpp
|
dbquery/makeitpersonal.cpp
|
||||||
dbquery/lyricswikia.cpp
|
dbquery/lyricswikia.cpp
|
||||||
|
dbquery/tekstowo.cpp
|
||||||
gui/dbquerywidget.cpp
|
gui/dbquerywidget.cpp
|
||||||
misc/networkaccessmanager.cpp
|
misc/networkaccessmanager.cpp
|
||||||
renamingutility/filesystemitem.cpp
|
renamingutility/filesystemitem.cpp
|
||||||
|
|
|
@ -206,6 +206,7 @@ void restore()
|
||||||
v.dbQuery.musicBrainzUrl = settings.value(QStringLiteral("musicbrainzurl")).toString();
|
v.dbQuery.musicBrainzUrl = settings.value(QStringLiteral("musicbrainzurl")).toString();
|
||||||
v.dbQuery.lyricsWikiaUrl = settings.value(QStringLiteral("lyricwikiurl")).toString();
|
v.dbQuery.lyricsWikiaUrl = settings.value(QStringLiteral("lyricwikiurl")).toString();
|
||||||
v.dbQuery.makeItPersonalUrl = settings.value(QStringLiteral("makeitpersonalurl")).toString();
|
v.dbQuery.makeItPersonalUrl = settings.value(QStringLiteral("makeitpersonalurl")).toString();
|
||||||
|
v.dbQuery.tekstowoUrl = settings.value(QStringLiteral("tekstowourl")).toString();
|
||||||
v.dbQuery.coverArtArchiveUrl = settings.value(QStringLiteral("coverartarchiveurl")).toString();
|
v.dbQuery.coverArtArchiveUrl = settings.value(QStringLiteral("coverartarchiveurl")).toString();
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
|
@ -302,6 +303,7 @@ void save()
|
||||||
settings.setValue(QStringLiteral("musicbrainzurl"), v.dbQuery.musicBrainzUrl);
|
settings.setValue(QStringLiteral("musicbrainzurl"), v.dbQuery.musicBrainzUrl);
|
||||||
settings.setValue(QStringLiteral("lyricwikiurl"), v.dbQuery.lyricsWikiaUrl);
|
settings.setValue(QStringLiteral("lyricwikiurl"), v.dbQuery.lyricsWikiaUrl);
|
||||||
settings.setValue(QStringLiteral("makeitpersonalurl"), v.dbQuery.makeItPersonalUrl);
|
settings.setValue(QStringLiteral("makeitpersonalurl"), v.dbQuery.makeItPersonalUrl);
|
||||||
|
settings.setValue(QStringLiteral("tekstowourl"), v.dbQuery.tekstowoUrl);
|
||||||
settings.setValue(QStringLiteral("coverartarchiveurl"), v.dbQuery.coverArtArchiveUrl);
|
settings.setValue(QStringLiteral("coverartarchiveurl"), v.dbQuery.coverArtArchiveUrl);
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ struct DbQuery {
|
||||||
QString coverArtArchiveUrl;
|
QString coverArtArchiveUrl;
|
||||||
QString lyricsWikiaUrl;
|
QString lyricsWikiaUrl;
|
||||||
QString makeItPersonalUrl;
|
QString makeItPersonalUrl;
|
||||||
|
QString tekstowoUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RenamingUtility {
|
struct RenamingUtility {
|
||||||
|
|
|
@ -142,6 +142,11 @@ QJSValue UtilityObject::queryMakeItPersonal(const QJSValue &songDescription)
|
||||||
return m_engine->newQObject(QtGui::queryMakeItPersonal(makeSongDescription(songDescription)));
|
return m_engine->newQObject(QtGui::queryMakeItPersonal(makeSongDescription(songDescription)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJSValue UtilityObject::queryTekstowo(const QJSValue &songDescription)
|
||||||
|
{
|
||||||
|
return m_engine->newQObject(QtGui::queryTekstowo(makeSongDescription(songDescription)));
|
||||||
|
}
|
||||||
|
|
||||||
QtGui::SongDescription UtilityObject::makeSongDescription(const QJSValue &obj)
|
QtGui::SongDescription UtilityObject::makeSongDescription(const QJSValue &obj)
|
||||||
{
|
{
|
||||||
auto desc = QtGui::SongDescription(obj.property(QStringLiteral("songId")).toString());
|
auto desc = QtGui::SongDescription(obj.property(QStringLiteral("songId")).toString());
|
||||||
|
|
|
@ -61,6 +61,7 @@ public Q_SLOTS:
|
||||||
QJSValue queryMusicBrainz(const QJSValue &songDescription);
|
QJSValue queryMusicBrainz(const QJSValue &songDescription);
|
||||||
QJSValue queryLyricsWikia(const QJSValue &songDescription);
|
QJSValue queryLyricsWikia(const QJSValue &songDescription);
|
||||||
QJSValue queryMakeItPersonal(const QJSValue &songDescription);
|
QJSValue queryMakeItPersonal(const QJSValue &songDescription);
|
||||||
|
QJSValue queryTekstowo(const QJSValue &songDescription);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QtGui::SongDescription makeSongDescription(const QJSValue &obj);
|
static QtGui::SongDescription makeSongDescription(const QJSValue &obj);
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "../misc/networkaccessmanager.h"
|
#include "../misc/networkaccessmanager.h"
|
||||||
#include "../misc/utility.h"
|
#include "../misc/utility.h"
|
||||||
|
|
||||||
|
#include "resources/config.h"
|
||||||
|
|
||||||
#include <tagparser/signature.h>
|
#include <tagparser/signature.h>
|
||||||
#include <tagparser/tag.h>
|
#include <tagparser/tag.h>
|
||||||
#include <tagparser/tagvalue.h>
|
#include <tagparser/tagvalue.h>
|
||||||
|
@ -25,7 +27,7 @@ SongDescription::SongDescription(const QString &songId)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<QString> QueryResultsModel::s_coverNames = std::list<QString>();
|
std::list<QString> QueryResultsModel::s_coverNames = std::list<QString>();
|
||||||
map<QString, QByteArray> QueryResultsModel::s_coverData = map<QString, QByteArray>();
|
std::map<QString, QByteArray> QueryResultsModel::s_coverData = std::map<QString, QByteArray>();
|
||||||
|
|
||||||
QueryResultsModel::QueryResultsModel(QObject *parent)
|
QueryResultsModel::QueryResultsModel(QObject *parent)
|
||||||
: QAbstractTableModel(parent)
|
: QAbstractTableModel(parent)
|
||||||
|
@ -104,7 +106,7 @@ QVariant QueryResultsModel::data(const QModelIndex &index, int role) const
|
||||||
if (!index.isValid() || index.row() >= m_results.size()) {
|
if (!index.isValid() || index.row() >= m_results.size()) {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
const SongDescription &res = m_results.at(index.row());
|
const auto &res = m_results.at(index.row());
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
|
@ -146,7 +148,7 @@ QVariant QueryResultsModel::data(const QModelIndex &index, int role) const
|
||||||
|
|
||||||
Qt::ItemFlags QueryResultsModel::flags(const QModelIndex &index) const
|
Qt::ItemFlags QueryResultsModel::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags flags = Qt::ItemNeverHasChildren | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
auto flags = Qt::ItemFlags(Qt::ItemNeverHasChildren | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
flags |= Qt::ItemIsUserCheckable;
|
flags |= Qt::ItemIsUserCheckable;
|
||||||
}
|
}
|
||||||
|
@ -311,6 +313,16 @@ void HttpResultsModel::handleInitialReplyFinished()
|
||||||
setResultsAvailable(true); // update status, emit resultsAvailable()
|
setResultsAvailable(true); // update status, emit resultsAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||||
|
void HttpResultsModel::logReply(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
static const auto enableQueryLogging = qEnvironmentVariableIntValue(PROJECT_VARNAME_UPPER "_ENABLE_QUERY_LOGGING");
|
||||||
|
if (enableQueryLogging) {
|
||||||
|
std::cerr << "HTTP query: " << reply->url().toString().toUtf8().data() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByteArray &data, bool alwaysFollowRedirection)
|
QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByteArray &data, bool alwaysFollowRedirection)
|
||||||
{
|
{
|
||||||
// delete reply (later)
|
// delete reply (later)
|
||||||
|
@ -328,8 +340,10 @@ QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByt
|
||||||
m_errorList << tr("Server replied no data.");
|
m_errorList << tr("Server replied no data.");
|
||||||
}
|
}
|
||||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||||
cerr << "Results from HTTP query:" << endl;
|
static const auto enableQueryLogging = qEnvironmentVariableIntValue(PROJECT_VARNAME_UPPER "_ENABLE_QUERY_LOGGING");
|
||||||
cerr << data.data() << endl;
|
if (enableQueryLogging) {
|
||||||
|
std::cerr << "Results from HTTP query:\n" << data.data() << '\n';
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -366,7 +380,7 @@ void HttpResultsModel::abort()
|
||||||
|
|
||||||
void HttpResultsModel::handleCoverReplyFinished(QNetworkReply *reply, const QString &albumId, int row)
|
void HttpResultsModel::handleCoverReplyFinished(QNetworkReply *reply, const QString &albumId, int row)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
auto data = QByteArray();
|
||||||
if (auto *const newReply = evaluateReplyResults(reply, data, true)) {
|
if (auto *const newReply = evaluateReplyResults(reply, data, true)) {
|
||||||
addReply(newReply, bind(&HttpResultsModel::handleCoverReplyFinished, this, newReply, albumId, row));
|
addReply(newReply, bind(&HttpResultsModel::handleCoverReplyFinished, this, newReply, albumId, row));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,7 +22,7 @@ TAGEDITOR_ENUM_CLASS KnownField : unsigned int;
|
||||||
namespace QtGui {
|
namespace QtGui {
|
||||||
|
|
||||||
struct SongDescription {
|
struct SongDescription {
|
||||||
SongDescription(const QString &songId = QString());
|
explicit SongDescription(const QString &songId = QString());
|
||||||
|
|
||||||
QString songId;
|
QString songId;
|
||||||
QString title;
|
QString title;
|
||||||
|
@ -85,7 +85,7 @@ Q_SIGNALS:
|
||||||
void lyricsAvailable(const QModelIndex &index);
|
void lyricsAvailable(const QModelIndex &index);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QueryResultsModel(QObject *parent = nullptr);
|
explicit QueryResultsModel(QObject *parent = nullptr);
|
||||||
void setResultsAvailable(bool resultsAvailable);
|
void setResultsAvailable(bool resultsAvailable);
|
||||||
void setFetchingCover(bool fetchingCover);
|
void setFetchingCover(bool fetchingCover);
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ public:
|
||||||
void abort() override;
|
void abort() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
HttpResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
explicit HttpResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||||
template <class Object, class Function> void addReply(QNetworkReply *reply, Object object, Function handler);
|
template <class Object, class Function> void addReply(QNetworkReply *reply, Object object, Function handler);
|
||||||
template <class Function> void addReply(QNetworkReply *reply, Function handler);
|
template <class Function> void addReply(QNetworkReply *reply, Function handler);
|
||||||
virtual void parseInitialResults(const QByteArray &data) = 0;
|
virtual void parseInitialResults(const QByteArray &data) = 0;
|
||||||
|
@ -135,17 +135,20 @@ protected:
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleInitialReplyFinished();
|
void handleInitialReplyFinished();
|
||||||
|
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||||
|
void logReply(QNetworkReply *reply);
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QList<QNetworkReply *> m_replies;
|
QList<QNetworkReply *> m_replies;
|
||||||
const SongDescription m_initialDescription;
|
SongDescription m_initialDescription;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class Object, class Function> inline void HttpResultsModel::addReply(QNetworkReply *reply, Object object, Function handler)
|
template <class Object, class Function> inline void HttpResultsModel::addReply(QNetworkReply *reply, Object object, Function handler)
|
||||||
{
|
{
|
||||||
(m_replies << reply), connect(reply, &QNetworkReply::finished, object, handler);
|
(m_replies << reply), connect(reply, &QNetworkReply::finished, object, handler);
|
||||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||||
std::cerr << "HTTP query: " << reply->url().toString().toUtf8().data() << std::endl;
|
logReply(reply);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ template <class Function> inline void HttpResultsModel::addReply(QNetworkReply *
|
||||||
{
|
{
|
||||||
(m_replies << reply), connect(reply, &QNetworkReply::finished, handler);
|
(m_replies << reply), connect(reply, &QNetworkReply::finished, handler);
|
||||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||||
std::cerr << "HTTP query: " << reply->url().toString().toUtf8().data() << std::endl;
|
logReply(reply);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +168,7 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription);
|
||||||
QueryResultsModel *queryLyricsWikia(SongDescription &&songDescription);
|
QueryResultsModel *queryLyricsWikia(SongDescription &&songDescription);
|
||||||
QNetworkReply *queryCoverArtArchive(const QString &albumId);
|
QNetworkReply *queryCoverArtArchive(const QString &albumId);
|
||||||
QueryResultsModel *queryMakeItPersonal(SongDescription &&songDescription);
|
QueryResultsModel *queryMakeItPersonal(SongDescription &&songDescription);
|
||||||
|
QueryResultsModel *queryTekstowo(SongDescription &&songDescription);
|
||||||
|
|
||||||
} // namespace QtGui
|
} // namespace QtGui
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ using namespace Utility;
|
||||||
|
|
||||||
namespace QtGui {
|
namespace QtGui {
|
||||||
|
|
||||||
static const QString defaultLyricsWikiaUrl(QStringLiteral("https://lyrics.fandom.com"));
|
static const auto defaultLyricsWikiaUrl = QStringLiteral("https://lyrics.fandom.com");
|
||||||
|
|
||||||
static QUrl lyricsWikiaApiUrl()
|
static QUrl lyricsWikiaApiUrl()
|
||||||
{
|
{
|
||||||
|
@ -43,15 +43,12 @@ LyricsWikiaResultsModel::LyricsWikiaResultsModel(SongDescription &&initialSongDe
|
||||||
|
|
||||||
bool LyricsWikiaResultsModel::fetchCover(const QModelIndex &index)
|
bool LyricsWikiaResultsModel::fetchCover(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
// FIXME: avoid code duplication with musicbrainz.cpp
|
|
||||||
|
|
||||||
// find song description
|
|
||||||
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SongDescription &desc = m_results[index.row()];
|
|
||||||
|
|
||||||
// skip if cover is already available
|
// skip if cover is already available
|
||||||
|
auto &desc = m_results[index.row()];
|
||||||
if (!desc.cover.isEmpty()) {
|
if (!desc.cover.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -87,13 +84,12 @@ bool LyricsWikiaResultsModel::fetchCover(const QModelIndex &index)
|
||||||
|
|
||||||
bool LyricsWikiaResultsModel::fetchLyrics(const QModelIndex &index)
|
bool LyricsWikiaResultsModel::fetchLyrics(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
// find song description
|
|
||||||
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SongDescription &desc = m_results[index.row()];
|
|
||||||
|
|
||||||
// skip if lyrics already present
|
// skip if lyrics already present
|
||||||
|
auto &desc = m_results[index.row()];
|
||||||
if (!desc.lyrics.isEmpty()) {
|
if (!desc.lyrics.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -113,25 +109,24 @@ bool LyricsWikiaResultsModel::fetchLyrics(const QModelIndex &index)
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// prepare parsing LyricsWikia meta data
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_results.clear();
|
m_results.clear();
|
||||||
QXmlStreamReader xmlReader(data);
|
|
||||||
|
|
||||||
// parse XML tree
|
// parse XML tree
|
||||||
|
auto xmlReader = QXmlStreamReader(data);
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include <qtutilities/misc/xmlparsermacros.h>
|
#include <qtutilities/misc/xmlparsermacros.h>
|
||||||
children {
|
children {
|
||||||
iftag("getArtistResponse") {
|
iftag("getArtistResponse") {
|
||||||
QString artist;
|
auto artist = QString();
|
||||||
children {
|
children {
|
||||||
iftag("artist") {
|
iftag("artist") {
|
||||||
artist = text;
|
artist = text;
|
||||||
} eliftag("albums") {
|
} eliftag("albums") {
|
||||||
children {
|
children {
|
||||||
iftag("albumResult") {
|
iftag("albumResult") {
|
||||||
QString album, year;
|
auto album = QString(), year = QString();
|
||||||
QList<SongDescription> songs;
|
auto songs = QList<SongDescription>();
|
||||||
children {
|
children {
|
||||||
iftag("album") {
|
iftag("album") {
|
||||||
album = text;
|
album = text;
|
||||||
|
@ -169,7 +164,7 @@ void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
for (SongDescription &song : m_results) {
|
for (auto &song : m_results) {
|
||||||
// set the arist which is the same for all results
|
// set the arist which is the same for all results
|
||||||
song.artist = artist;
|
song.artist = artist;
|
||||||
// set the album ID (album is identified by its artist, year and name)
|
// set the album ID (album is identified by its artist, year and name)
|
||||||
|
@ -191,14 +186,13 @@ void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
m_errorList << xmlReader.errorString();
|
m_errorList << xmlReader.errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// promote changes
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *LyricsWikiaResultsModel::requestSongDetails(const SongDescription &songDescription)
|
QNetworkReply *LyricsWikiaResultsModel::requestSongDetails(const SongDescription &songDescription)
|
||||||
{
|
{
|
||||||
// compose URL
|
// compose URL
|
||||||
QUrlQuery query;
|
auto query = QUrlQuery();
|
||||||
query.addQueryItem(QStringLiteral("func"), QStringLiteral("getSong"));
|
query.addQueryItem(QStringLiteral("func"), QStringLiteral("getSong"));
|
||||||
query.addQueryItem(QStringLiteral("action"), QStringLiteral("lyrics"));
|
query.addQueryItem(QStringLiteral("action"), QStringLiteral("lyrics"));
|
||||||
query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("xml"));
|
query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("xml"));
|
||||||
|
@ -209,22 +203,21 @@ QNetworkReply *LyricsWikiaResultsModel::requestSongDetails(const SongDescription
|
||||||
// specifying album seems to have no effect but also doesn't hurt
|
// specifying album seems to have no effect but also doesn't hurt
|
||||||
query.addQueryItem(QStringLiteral("album"), songDescription.album);
|
query.addQueryItem(QStringLiteral("album"), songDescription.album);
|
||||||
}
|
}
|
||||||
QUrl url(lyricsWikiaApiUrl());
|
auto url = lyricsWikiaApiUrl();
|
||||||
url.setQuery(query);
|
url.setQuery(query);
|
||||||
|
|
||||||
return Utility::networkAccessManager().get(QNetworkRequest(url));
|
return Utility::networkAccessManager().get(QNetworkRequest(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *LyricsWikiaResultsModel::requestAlbumDetails(const SongDescription &songDescription)
|
QNetworkReply *LyricsWikiaResultsModel::requestAlbumDetails(const SongDescription &songDescription)
|
||||||
{
|
{
|
||||||
QUrl url(lyricsWikiaApiUrl());
|
auto url = lyricsWikiaApiUrl();
|
||||||
url.setPath(QStringLiteral("/wiki/") + songDescription.albumId);
|
url.setPath(QStringLiteral("/wiki/") + songDescription.albumId);
|
||||||
return Utility::networkAccessManager().get(QNetworkRequest(url));
|
return Utility::networkAccessManager().get(QNetworkRequest(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::handleSongDetailsFinished(QNetworkReply *reply, int row)
|
void LyricsWikiaResultsModel::handleSongDetailsFinished(QNetworkReply *reply, int row)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
auto data = QByteArray();
|
||||||
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||||
addReply(newReply, bind(&LyricsWikiaResultsModel::handleSongDetailsFinished, this, newReply, row));
|
addReply(newReply, bind(&LyricsWikiaResultsModel::handleSongDetailsFinished, this, newReply, row));
|
||||||
} else if (!data.isEmpty()) {
|
} else if (!data.isEmpty()) {
|
||||||
|
@ -234,23 +227,21 @@ void LyricsWikiaResultsModel::handleSongDetailsFinished(QNetworkReply *reply, in
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
||||||
{
|
{
|
||||||
// find associated result/desc
|
|
||||||
if (row >= m_results.size()) {
|
if (row >= m_results.size()) {
|
||||||
m_errorList << tr("Internal error: context for song details reply invalid");
|
m_errorList << tr("Internal error: context for song details reply invalid");
|
||||||
setResultsAvailable(true);
|
setResultsAvailable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SongDescription &assocDesc = m_results[row];
|
|
||||||
|
|
||||||
QUrl parsedUrl;
|
|
||||||
|
|
||||||
// parse XML tree
|
// parse XML tree
|
||||||
|
auto &assocDesc = m_results[row];
|
||||||
|
auto parsedUrl = QUrl();
|
||||||
|
auto xmlReader = QXmlStreamReader(data);
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QXmlStreamReader xmlReader(data);
|
|
||||||
#include <qtutilities/misc/xmlparsermacros.h>
|
#include <qtutilities/misc/xmlparsermacros.h>
|
||||||
children {
|
children {
|
||||||
iftag("LyricsResult") {
|
iftag("LyricsResult") {
|
||||||
SongDescription parsedDesc;
|
auto parsedDesc = SongDescription();
|
||||||
children {
|
children {
|
||||||
iftag("artist") {
|
iftag("artist") {
|
||||||
parsedDesc.artist = text;
|
parsedDesc.artist = text;
|
||||||
|
@ -297,7 +288,7 @@ void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
||||||
.arg(assocDesc.artist, assocDesc.title);
|
.arg(assocDesc.artist, assocDesc.title);
|
||||||
}
|
}
|
||||||
// -> do not use parsed URL "as-is" in any case to avoid unintended requests
|
// -> do not use parsed URL "as-is" in any case to avoid unintended requests
|
||||||
QUrl requestUrl(lyricsWikiaApiUrl());
|
auto requestUrl = lyricsWikiaApiUrl();
|
||||||
requestUrl.setPath(parsedUrl.path());
|
requestUrl.setPath(parsedUrl.path());
|
||||||
// -> initialize the actual request
|
// -> initialize the actual request
|
||||||
auto *const reply = Utility::networkAccessManager().get(QNetworkRequest(requestUrl));
|
auto *const reply = Utility::networkAccessManager().get(QNetworkRequest(requestUrl));
|
||||||
|
@ -306,7 +297,7 @@ void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::handleLyricsReplyFinished(QNetworkReply *reply, int row)
|
void LyricsWikiaResultsModel::handleLyricsReplyFinished(QNetworkReply *reply, int row)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
auto data = QByteArray();
|
||||||
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||||
addReply(newReply, bind(&LyricsWikiaResultsModel::handleLyricsReplyFinished, this, newReply, row));
|
addReply(newReply, bind(&LyricsWikiaResultsModel::handleLyricsReplyFinished, this, newReply, row));
|
||||||
return;
|
return;
|
||||||
|
@ -321,18 +312,15 @@ void LyricsWikiaResultsModel::handleLyricsReplyFinished(QNetworkReply *reply, in
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data)
|
void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data)
|
||||||
{
|
{
|
||||||
// find associated result/desc
|
|
||||||
if (row >= m_results.size()) {
|
if (row >= m_results.size()) {
|
||||||
m_errorList << tr("Internal error: context for LyricsWikia page reply invalid");
|
m_errorList << tr("Internal error: context for LyricsWikia page reply invalid");
|
||||||
setResultsAvailable(true);
|
setResultsAvailable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SongDescription &assocDesc = m_results[row];
|
|
||||||
|
|
||||||
// convert data to QString
|
|
||||||
const QString html(data);
|
|
||||||
|
|
||||||
// parse lyrics from HTML
|
// parse lyrics from HTML
|
||||||
|
auto &assocDesc = m_results[row];
|
||||||
|
const auto html = QString(data);
|
||||||
const auto lyricsStart = html.indexOf(QLatin1String("<div class='lyricbox'>"));
|
const auto lyricsStart = html.indexOf(QLatin1String("<div class='lyricbox'>"));
|
||||||
if (lyricsStart < 0) {
|
if (lyricsStart < 0) {
|
||||||
m_errorList << tr("Song details requested for %1/%2 do not contain lyrics").arg(assocDesc.artist, assocDesc.title);
|
m_errorList << tr("Song details requested for %1/%2 do not contain lyrics").arg(assocDesc.artist, assocDesc.title);
|
||||||
|
@ -340,7 +328,7 @@ void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto lyricsEnd = html.indexOf(QLatin1String("<div class='lyricsbreak'></div>"), lyricsStart);
|
const auto lyricsEnd = html.indexOf(QLatin1String("<div class='lyricsbreak'></div>"), lyricsStart);
|
||||||
QTextDocument textDoc;
|
auto textDoc = QTextDocument();
|
||||||
textDoc.setHtml(html.mid(lyricsStart, (lyricsEnd > lyricsStart) ? (lyricsEnd - lyricsStart) : -1));
|
textDoc.setHtml(html.mid(lyricsStart, (lyricsEnd > lyricsStart) ? (lyricsEnd - lyricsStart) : -1));
|
||||||
assocDesc.lyrics = textDoc.toPlainText();
|
assocDesc.lyrics = textDoc.toPlainText();
|
||||||
|
|
||||||
|
@ -350,7 +338,7 @@ void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished(QNetworkReply *reply, int row)
|
void LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished(QNetworkReply *reply, int row)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
auto data = QByteArray();
|
||||||
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||||
addReply(newReply, bind(&LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished, this, newReply, row));
|
addReply(newReply, bind(&LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished, this, newReply, row));
|
||||||
} else {
|
} else {
|
||||||
|
@ -367,19 +355,16 @@ void LyricsWikiaResultsModel::parseAlbumDetailsAndFetchCover(int row, const QByt
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find associated result/desc
|
|
||||||
if (row >= m_results.size()) {
|
if (row >= m_results.size()) {
|
||||||
m_errorList << tr("Internal error: context for LyricsWikia page reply invalid");
|
m_errorList << tr("Internal error: context for LyricsWikia page reply invalid");
|
||||||
setFetchingCover(false);
|
setFetchingCover(false);
|
||||||
setResultsAvailable(true);
|
setResultsAvailable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SongDescription &assocDesc = m_results[row];
|
|
||||||
|
|
||||||
// convert data to QString
|
|
||||||
const auto html = QString(data);
|
|
||||||
|
|
||||||
// parse cover URL from HTML
|
// parse cover URL from HTML
|
||||||
|
auto &assocDesc = m_results[row];
|
||||||
|
const auto html = QString(data);
|
||||||
const auto coverDivStart = html.indexOf(QLatin1String("<div class=\"plainlinks\" style=\"clear:right; float:right;")) + 56;
|
const auto coverDivStart = html.indexOf(QLatin1String("<div class=\"plainlinks\" style=\"clear:right; float:right;")) + 56;
|
||||||
if (coverDivStart > 56) {
|
if (coverDivStart > 56) {
|
||||||
const auto coverHrefStart = html.indexOf(QLatin1String("href=\""), coverDivStart) + 6;
|
const auto coverHrefStart = html.indexOf(QLatin1String("href=\""), coverDivStart) + 6;
|
||||||
|
@ -410,31 +395,28 @@ QUrl LyricsWikiaResultsModel::webUrl(const QModelIndex &index)
|
||||||
return QUrl();
|
return QUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
SongDescription &desc = m_results[index.row()];
|
auto &desc = m_results[index.row()];
|
||||||
lazyInitializeLyricsWikiaSongId(desc);
|
lazyInitializeLyricsWikiaSongId(desc);
|
||||||
|
|
||||||
// return URL
|
// return URL
|
||||||
QUrl url(lyricsWikiaApiUrl());
|
auto url = lyricsWikiaApiUrl();
|
||||||
url.setPath(QStringLiteral("/wiki/") + desc.songId);
|
url.setPath(QStringLiteral("/wiki/") + desc.songId);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryResultsModel *queryLyricsWikia(SongDescription &&songDescription)
|
QueryResultsModel *queryLyricsWikia(SongDescription &&songDescription)
|
||||||
{
|
{
|
||||||
// compose URL
|
auto query = QUrlQuery();
|
||||||
QUrlQuery query;
|
|
||||||
query.addQueryItem(QStringLiteral("func"), QStringLiteral("getArtist"));
|
query.addQueryItem(QStringLiteral("func"), QStringLiteral("getArtist"));
|
||||||
query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("xml"));
|
query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("xml"));
|
||||||
query.addQueryItem(QStringLiteral("fixXML"), QString());
|
query.addQueryItem(QStringLiteral("fixXML"), QString());
|
||||||
query.addQueryItem(QStringLiteral("artist"), songDescription.artist);
|
query.addQueryItem(QStringLiteral("artist"), songDescription.artist);
|
||||||
QUrl url(lyricsWikiaApiUrl());
|
auto url = lyricsWikiaApiUrl();
|
||||||
url.setQuery(query);
|
url.setQuery(query);
|
||||||
|
return new LyricsWikiaResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
||||||
|
|
||||||
// NOTE: Only getArtist seems to work, so artist must be specified and filtering must
|
// NOTE: Only getArtist seems to work, so artist must be specified and filtering must
|
||||||
// be done manually when parsing results.
|
// be done manually when parsing results.
|
||||||
|
|
||||||
// make request
|
|
||||||
return new LyricsWikiaResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QtGui
|
} // namespace QtGui
|
||||||
|
|
|
@ -11,7 +11,7 @@ class LyricsWikiaResultsModel : public HttpResultsModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LyricsWikiaResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
explicit LyricsWikiaResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||||
bool fetchCover(const QModelIndex &index) override;
|
bool fetchCover(const QModelIndex &index) override;
|
||||||
bool fetchLyrics(const QModelIndex &index) override;
|
bool fetchLyrics(const QModelIndex &index) override;
|
||||||
QUrl webUrl(const QModelIndex &index) override;
|
QUrl webUrl(const QModelIndex &index) override;
|
||||||
|
|
|
@ -37,32 +37,25 @@ bool MakeItPersonalResultsModel::fetchLyrics(const QModelIndex &index)
|
||||||
|
|
||||||
void MakeItPersonalResultsModel::parseInitialResults(const QByteArray &data)
|
void MakeItPersonalResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// prepare parsing meta data
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_results.clear();
|
m_results.clear();
|
||||||
|
auto desc = m_initialDescription;
|
||||||
SongDescription desc = m_initialDescription;
|
|
||||||
desc.songId = m_initialDescription.artist + m_initialDescription.title;
|
desc.songId = m_initialDescription.artist + m_initialDescription.title;
|
||||||
desc.artistId = m_initialDescription.artist;
|
desc.artistId = m_initialDescription.artist;
|
||||||
desc.lyrics = QString::fromUtf8(data).trimmed();
|
desc.lyrics = QString::fromUtf8(data).trimmed();
|
||||||
if (desc.lyrics != QLatin1String("Sorry, We don't have lyrics for this song yet.")) {
|
if (desc.lyrics != QLatin1String("Sorry, We don't have lyrics for this song yet.")) {
|
||||||
m_results << std::move(desc);
|
m_results << std::move(desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// promote changes
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryResultsModel *queryMakeItPersonal(SongDescription &&songDescription)
|
QueryResultsModel *queryMakeItPersonal(SongDescription &&songDescription)
|
||||||
{
|
{
|
||||||
// compose URL
|
auto query = QUrlQuery();
|
||||||
QUrlQuery query;
|
|
||||||
query.addQueryItem(QStringLiteral("artist"), songDescription.artist);
|
query.addQueryItem(QStringLiteral("artist"), songDescription.artist);
|
||||||
query.addQueryItem(QStringLiteral("title"), songDescription.title);
|
query.addQueryItem(QStringLiteral("title"), songDescription.title);
|
||||||
QUrl url(makeItPersonalApiUrl());
|
auto url = makeItPersonalApiUrl();
|
||||||
url.setQuery(query);
|
url.setQuery(query);
|
||||||
|
|
||||||
// make request
|
|
||||||
return new MakeItPersonalResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
return new MakeItPersonalResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ class MakeItPersonalResultsModel : public HttpResultsModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MakeItPersonalResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
explicit MakeItPersonalResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||||
bool fetchLyrics(const QModelIndex &index) override;
|
bool fetchLyrics(const QModelIndex &index) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -40,15 +40,12 @@ MusicBrainzResultsModel::MusicBrainzResultsModel(SongDescription &&initialSongDe
|
||||||
|
|
||||||
bool MusicBrainzResultsModel::fetchCover(const QModelIndex &index)
|
bool MusicBrainzResultsModel::fetchCover(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
// FIXME: avoid code duplication with lyricswikia.cpp
|
|
||||||
|
|
||||||
// find song description
|
|
||||||
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SongDescription &desc = m_results[index.row()];
|
|
||||||
|
|
||||||
// skip if cover is already available
|
// skip if cover is already available
|
||||||
|
auto &desc = m_results[index.row()];
|
||||||
if (!desc.cover.isEmpty()) {
|
if (!desc.cover.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -84,17 +81,16 @@ QUrl MusicBrainzResultsModel::webUrl(const QModelIndex &index)
|
||||||
|
|
||||||
void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// prepare parsing MusicBrainz meta data
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_results.clear();
|
m_results.clear();
|
||||||
|
|
||||||
// store all song information (called recordings by MusicBrainz)
|
// store all song information (called recordings by MusicBrainz)
|
||||||
vector<SongDescription> recordings;
|
auto recordings = std::vector<SongDescription>();
|
||||||
// store all albums/collections (called releases by MusicBrainz) for a song
|
// store all albums/collections (called releases by MusicBrainz) for a song
|
||||||
unordered_map<QString, vector<SongDescription>> releasesByRecording;
|
auto releasesByRecording = std::unordered_map<QString, std::vector<SongDescription>>();
|
||||||
|
|
||||||
// parse XML tree
|
// parse XML tree
|
||||||
QXmlStreamReader xmlReader(data);
|
auto xmlReader = QXmlStreamReader(data);
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include <qtutilities/misc/xmlparsermacros.h>
|
#include <qtutilities/misc/xmlparsermacros.h>
|
||||||
children {
|
children {
|
||||||
|
@ -103,7 +99,7 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
iftag("recording-list") {
|
iftag("recording-list") {
|
||||||
children {
|
children {
|
||||||
iftag("recording") {
|
iftag("recording") {
|
||||||
SongDescription currentDescription(attribute("id").toString());
|
auto currentDescription = SongDescription(attribute("id").toString());
|
||||||
children {
|
children {
|
||||||
iftag("title") {
|
iftag("title") {
|
||||||
currentDescription.title = text;
|
currentDescription.title = text;
|
||||||
|
@ -130,7 +126,7 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
} eliftag("release-list") {
|
} eliftag("release-list") {
|
||||||
children {
|
children {
|
||||||
iftag("release") {
|
iftag("release") {
|
||||||
SongDescription releaseInfo;
|
auto releaseInfo = SongDescription();
|
||||||
releaseInfo.albumId = attribute("id").toString();
|
releaseInfo.albumId = attribute("id").toString();
|
||||||
children {
|
children {
|
||||||
iftag("title") {
|
iftag("title") {
|
||||||
|
@ -220,7 +216,7 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
|
|
||||||
// populate results
|
// populate results
|
||||||
// -> create a song for each recording/release combination and group those songs by their releases sorted ascendingly from oldest to latest
|
// -> create a song for each recording/release combination and group those songs by their releases sorted ascendingly from oldest to latest
|
||||||
map<QString, vector<SongDescription>> recordingsByRelease;
|
auto recordingsByRelease = std::map<QString, std::vector<SongDescription>>();
|
||||||
for (const auto &recording : recordings) {
|
for (const auto &recording : recordings) {
|
||||||
const auto &releases = releasesByRecording[recording.songId];
|
const auto &releases = releasesByRecording[recording.songId];
|
||||||
for (const auto &release : releases) {
|
for (const auto &release : releases) {
|
||||||
|
@ -272,7 +268,6 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
m_errorList << xmlReader.errorString();
|
m_errorList << xmlReader.errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// promote changes
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@ -281,8 +276,7 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
||||||
{
|
{
|
||||||
static const auto defaultMusicBrainzUrl(QStringLiteral("https://musicbrainz.org/ws/2/recording/"));
|
static const auto defaultMusicBrainzUrl(QStringLiteral("https://musicbrainz.org/ws/2/recording/"));
|
||||||
|
|
||||||
// compose parts
|
auto parts = QStringList();
|
||||||
QStringList parts;
|
|
||||||
parts.reserve(4);
|
parts.reserve(4);
|
||||||
if (!songDescription.title.isEmpty()) {
|
if (!songDescription.title.isEmpty()) {
|
||||||
parts << QChar('\"') % songDescription.title % QChar('\"');
|
parts << QChar('\"') % songDescription.title % QChar('\"');
|
||||||
|
@ -297,15 +291,12 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
||||||
parts << QStringLiteral("number:") + QString::number(songDescription.track);
|
parts << QStringLiteral("number:") + QString::number(songDescription.track);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compose URL
|
|
||||||
const auto &musicBrainzUrl = Settings::values().dbQuery.musicBrainzUrl;
|
const auto &musicBrainzUrl = Settings::values().dbQuery.musicBrainzUrl;
|
||||||
QUrl url(musicBrainzUrl.isEmpty() ? defaultMusicBrainzUrl : (musicBrainzUrl + QStringLiteral("/recording/")));
|
auto url = QUrl(musicBrainzUrl.isEmpty() ? defaultMusicBrainzUrl : (musicBrainzUrl + QStringLiteral("/recording/")));
|
||||||
QUrlQuery query;
|
auto query = QUrlQuery();
|
||||||
query.addQueryItem(QStringLiteral("query"), parts.join(QStringLiteral(" AND ")));
|
query.addQueryItem(QStringLiteral("query"), parts.join(QStringLiteral(" AND ")));
|
||||||
url.setQuery(query);
|
url.setQuery(query);
|
||||||
|
auto request = QNetworkRequest(url);
|
||||||
// make request
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, QStringLiteral("Mozilla/5.0 (X11; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"));
|
request.setHeader(QNetworkRequest::UserAgentHeader, QStringLiteral("Mozilla/5.0 (X11; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"));
|
||||||
return new MusicBrainzResultsModel(std::move(songDescription), Utility::networkAccessManager().get(request));
|
return new MusicBrainzResultsModel(std::move(songDescription), Utility::networkAccessManager().get(request));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ private:
|
||||||
enum What { MusicBrainzMetaData, CoverArt };
|
enum What { MusicBrainzMetaData, CoverArt };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MusicBrainzResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
explicit MusicBrainzResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||||
bool fetchCover(const QModelIndex &index) override;
|
bool fetchCover(const QModelIndex &index) override;
|
||||||
QUrl webUrl(const QModelIndex &index) override;
|
QUrl webUrl(const QModelIndex &index) override;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
#include "./tekstowo.h"
|
||||||
|
|
||||||
|
#include "../application/settings.h"
|
||||||
|
#include "../misc/networkaccessmanager.h"
|
||||||
|
#include "../misc/utility.h"
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QTextDocumentFragment>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::placeholders;
|
||||||
|
using namespace Utility;
|
||||||
|
|
||||||
|
namespace QtGui {
|
||||||
|
|
||||||
|
static const auto defaultTekstowoUrl = QStringLiteral("https://www.tekstowo.pl");
|
||||||
|
|
||||||
|
static QUrl tekstowoUrl()
|
||||||
|
{
|
||||||
|
const auto &url = Settings::values().dbQuery.tekstowoUrl;
|
||||||
|
return QUrl(url.isEmpty() ? defaultTekstowoUrl : url);
|
||||||
|
}
|
||||||
|
|
||||||
|
TekstowoResultsModel::TekstowoResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply)
|
||||||
|
: HttpResultsModel(std::move(initialSongDescription), reply)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TekstowoResultsModel::fetchLyrics(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
if ((index.parent().isValid() || index.row() >= m_results.size()) && !m_results[index.row()].lyrics.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto url = webUrl(index);
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
m_errorList << tr("Unable to fetch lyrics: web URL is unknown.");
|
||||||
|
emit resultsAvailable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *reply = Utility::networkAccessManager().get(QNetworkRequest(url));
|
||||||
|
addReply(reply, bind(&TekstowoResultsModel::handleLyricsReplyFinished, this, reply, index.row()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TekstowoResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_results.clear();
|
||||||
|
auto dropLast = false;
|
||||||
|
auto hasExactMatch = false;
|
||||||
|
auto exactMatch = QList<SongDescription>::size_type();
|
||||||
|
for (auto index = exactMatch; index >= 0;) {
|
||||||
|
const auto linkStart = data.indexOf("<a href=\"/piosenka,", index);
|
||||||
|
if (linkStart < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto hrefStart = linkStart + 9;
|
||||||
|
const auto hrefEnd = data.indexOf("\"", hrefStart + 1);
|
||||||
|
if (hrefEnd <= hrefStart) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto linkEnd = data.indexOf("</a>", hrefEnd);
|
||||||
|
if (linkEnd < linkStart) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index = linkEnd + 4;
|
||||||
|
auto linkText = QTextDocumentFragment::fromHtml(QString::fromUtf8(data.begin() + linkStart, linkEnd + 3 - linkStart)).toPlainText().trimmed();
|
||||||
|
auto titleStart = linkText.indexOf(QLatin1String(" - "));
|
||||||
|
auto &songDetails = dropLast ? m_results.back() : m_results.emplace_back();
|
||||||
|
songDetails.songId = QTextDocumentFragment::fromHtml(QString::fromUtf8(data.begin() + hrefStart, hrefEnd - hrefStart)).toPlainText();
|
||||||
|
if (titleStart > -1) {
|
||||||
|
songDetails.artist = linkText.mid(0, titleStart);
|
||||||
|
if (songDetails.artist != m_initialDescription.artist) {
|
||||||
|
dropLast = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
songDetails.title = linkText.mid(titleStart + 3);
|
||||||
|
} else {
|
||||||
|
songDetails.title = std::move(linkText);
|
||||||
|
}
|
||||||
|
if (!hasExactMatch && songDetails.title == m_initialDescription.title) {
|
||||||
|
hasExactMatch = true;
|
||||||
|
exactMatch = m_results.size() - 1;
|
||||||
|
}
|
||||||
|
dropLast = false;
|
||||||
|
}
|
||||||
|
if (dropLast) {
|
||||||
|
m_results.pop_back();
|
||||||
|
}
|
||||||
|
// ensure the first exact match for the song title is placed first
|
||||||
|
if (hasExactMatch && exactMatch != 0) {
|
||||||
|
std::swap(m_results[exactMatch], m_results[0]);
|
||||||
|
}
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TekstowoResultsModel::handleLyricsReplyFinished(QNetworkReply *reply, int row)
|
||||||
|
{
|
||||||
|
auto data = QByteArray();
|
||||||
|
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||||
|
addReply(newReply, bind(&TekstowoResultsModel::handleLyricsReplyFinished, this, newReply, row));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
parseLyricsResults(row, data);
|
||||||
|
}
|
||||||
|
if (!m_resultsAvailable) {
|
||||||
|
setResultsAvailable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TekstowoResultsModel::parseLyricsResults(int row, const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (row >= m_results.size()) {
|
||||||
|
m_errorList << tr("Internal error: context for Tekstowo page reply invalid");
|
||||||
|
setResultsAvailable(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto lyricsStart = data.indexOf("<div class=\"inner-text\">");
|
||||||
|
if (lyricsStart < 0) {
|
||||||
|
const auto &assocDesc = m_results[row];
|
||||||
|
m_errorList << tr("Song details requested for %1/%2 do not contain lyrics").arg(assocDesc.artist, assocDesc.title);
|
||||||
|
setResultsAvailable(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto lyricsEnd = data.indexOf("</div>", lyricsStart += 24); // hopefully lyrics don't contain nested </div>
|
||||||
|
m_results[row].lyrics = QTextDocumentFragment::fromHtml(
|
||||||
|
QString::fromUtf8(data.data() + lyricsStart, lyricsEnd > -1 ? lyricsEnd - lyricsStart : data.size() - lyricsStart))
|
||||||
|
.toPlainText()
|
||||||
|
.trimmed();
|
||||||
|
setResultsAvailable(true);
|
||||||
|
emit lyricsAvailable(index(row, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl TekstowoResultsModel::webUrl(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
if (index.parent().isValid() || index.row() >= results().size()) {
|
||||||
|
return QUrl();
|
||||||
|
}
|
||||||
|
auto url = tekstowoUrl();
|
||||||
|
url.setPath(m_results[index.row()].songId);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryResultsModel *queryTekstowo(SongDescription &&songDescription)
|
||||||
|
{
|
||||||
|
auto url = tekstowoUrl();
|
||||||
|
url.setPath(QStringLiteral("/szukaj,wykonawca,%1,tytul,%2.html").arg(songDescription.artist, songDescription.title));
|
||||||
|
return new TekstowoResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtGui
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef QTGUI_TEKSTOWO_H
|
||||||
|
#define QTGUI_TEKSTOWO_H
|
||||||
|
|
||||||
|
#include "./dbquery.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace QtGui {
|
||||||
|
|
||||||
|
class TekstowoResultsModel : public HttpResultsModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TekstowoResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||||
|
bool fetchLyrics(const QModelIndex &index) override;
|
||||||
|
QUrl webUrl(const QModelIndex &index) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void parseInitialResults(const QByteArray &data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleLyricsReplyFinished(QNetworkReply *reply, int row);
|
||||||
|
void parseLyricsResults(int row, const QByteArray &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QtGui
|
||||||
|
|
||||||
|
#endif // QTGUI_TEKSTOWO_H
|
|
@ -7,6 +7,7 @@
|
||||||
#include "../dbquery/dbquery.h"
|
#include "../dbquery/dbquery.h"
|
||||||
#include "../misc/utility.h"
|
#include "../misc/utility.h"
|
||||||
|
|
||||||
|
#include "resources/config.h"
|
||||||
#include "ui_dbquerywidget.h"
|
#include "ui_dbquerywidget.h"
|
||||||
|
|
||||||
#include <tagparser/tag.h>
|
#include <tagparser/tag.h>
|
||||||
|
@ -47,6 +48,13 @@ DbQueryWidget::DbQueryWidget(TagEditorWidget *tagEditorWidget, QWidget *parent)
|
||||||
, m_coverIndex(-1)
|
, m_coverIndex(-1)
|
||||||
, m_lyricsIndex(-1)
|
, m_lyricsIndex(-1)
|
||||||
, m_menu(new QMenu(parent))
|
, m_menu(new QMenu(parent))
|
||||||
|
, m_insertPresentDataAction(nullptr)
|
||||||
|
, m_searchMusicBrainzAction(nullptr)
|
||||||
|
, m_searchLyricsWikiaAction(nullptr)
|
||||||
|
, m_searchMakeItPersonalAction(nullptr)
|
||||||
|
, m_searchTekstowoAction(nullptr)
|
||||||
|
, m_lastSearchAction(nullptr)
|
||||||
|
, m_refreshAutomaticallyAction(nullptr)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
updateStyleSheet();
|
updateStyleSheet();
|
||||||
|
@ -72,20 +80,27 @@ DbQueryWidget::DbQueryWidget(TagEditorWidget *tagEditorWidget, QWidget *parent)
|
||||||
|
|
||||||
// setup menu
|
// setup menu
|
||||||
const auto searchIcon = QIcon::fromTheme(QStringLiteral("search"));
|
const auto searchIcon = QIcon::fromTheme(QStringLiteral("search"));
|
||||||
|
const auto enableLegacyProvider = qEnvironmentVariableIntValue(PROJECT_VARNAME_UPPER "_ENABLE_LEGACY_METADATA_PROVIDERS");
|
||||||
m_menu->setTitle(tr("New search"));
|
m_menu->setTitle(tr("New search"));
|
||||||
m_menu->setIcon(searchIcon);
|
m_menu->setIcon(searchIcon);
|
||||||
m_searchMusicBrainzAction = m_lastSearchAction = m_menu->addAction(tr("Query MusicBrainz"));
|
m_searchMusicBrainzAction = m_lastSearchAction = m_menu->addAction(tr("Query MusicBrainz"));
|
||||||
m_searchMusicBrainzAction->setIcon(searchIcon);
|
m_searchMusicBrainzAction->setIcon(searchIcon);
|
||||||
m_searchMusicBrainzAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_M));
|
m_searchMusicBrainzAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_M));
|
||||||
connect(m_searchMusicBrainzAction, &QAction::triggered, this, &DbQueryWidget::searchMusicBrainz);
|
connect(m_searchMusicBrainzAction, &QAction::triggered, this, &DbQueryWidget::searchMusicBrainz);
|
||||||
m_searchLyricsWikiaAction = m_menu->addAction(tr("Query LyricsWikia"));
|
if (enableLegacyProvider) {
|
||||||
m_searchLyricsWikiaAction->setIcon(searchIcon);
|
m_searchLyricsWikiaAction = m_menu->addAction(tr("Query LyricsWikia"));
|
||||||
m_searchLyricsWikiaAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_L));
|
m_searchLyricsWikiaAction->setIcon(searchIcon);
|
||||||
connect(m_searchLyricsWikiaAction, &QAction::triggered, this, &DbQueryWidget::searchLyricsWikia);
|
m_searchLyricsWikiaAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_L));
|
||||||
m_searchMakeItPersonalAction = m_menu->addAction(tr("Query makeitpersonal"));
|
connect(m_searchLyricsWikiaAction, &QAction::triggered, this, &DbQueryWidget::searchLyricsWikia);
|
||||||
m_searchMakeItPersonalAction->setIcon(searchIcon);
|
m_searchMakeItPersonalAction = m_menu->addAction(tr("Query makeitpersonal"));
|
||||||
m_searchMakeItPersonalAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_K));
|
m_searchMakeItPersonalAction->setIcon(searchIcon);
|
||||||
connect(m_searchMakeItPersonalAction, &QAction::triggered, this, &DbQueryWidget::searchMakeItPersonal);
|
m_searchMakeItPersonalAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_K));
|
||||||
|
connect(m_searchMakeItPersonalAction, &QAction::triggered, this, &DbQueryWidget::searchMakeItPersonal);
|
||||||
|
}
|
||||||
|
m_searchTekstowoAction = m_menu->addAction(tr("Query Tekstowo"));
|
||||||
|
m_searchTekstowoAction->setIcon(searchIcon);
|
||||||
|
m_searchTekstowoAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_T));
|
||||||
|
connect(m_searchTekstowoAction, &QAction::triggered, this, &DbQueryWidget::searchTekstowo);
|
||||||
m_menu->addSeparator();
|
m_menu->addSeparator();
|
||||||
m_insertPresentDataAction = m_menu->addAction(tr("Use present data as search criteria"));
|
m_insertPresentDataAction = m_menu->addAction(tr("Use present data as search criteria"));
|
||||||
m_insertPresentDataAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
m_insertPresentDataAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
||||||
|
@ -150,7 +165,7 @@ void DbQueryWidget::insertSearchTermsFromTagEdit(TagEdit *tagEdit, bool songSpec
|
||||||
m_ui->titleLineEdit->setText(newTitle);
|
m_ui->titleLineEdit->setText(newTitle);
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
if (m_lastSearchAction != m_searchMakeItPersonalAction) {
|
if (m_lastSearchAction != m_searchTekstowoAction && m_lastSearchAction != m_searchMakeItPersonalAction) {
|
||||||
const auto newTrackNumber = tagEdit->trackNumber();
|
const auto newTrackNumber = tagEdit->trackNumber();
|
||||||
if (m_ui->trackSpinBox->value() != newTrackNumber) {
|
if (m_ui->trackSpinBox->value() != newTrackNumber) {
|
||||||
m_ui->trackSpinBox->setValue(newTrackNumber);
|
m_ui->trackSpinBox->setValue(newTrackNumber);
|
||||||
|
@ -246,6 +261,30 @@ void DbQueryWidget::searchMakeItPersonal()
|
||||||
useQueryResults(queryMakeItPersonal(currentSongDescription()));
|
useQueryResults(queryMakeItPersonal(currentSongDescription()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DbQueryWidget::searchTekstowo()
|
||||||
|
{
|
||||||
|
m_lastSearchAction = m_searchMakeItPersonalAction;
|
||||||
|
|
||||||
|
// check whether enough search terms are supplied
|
||||||
|
if (m_ui->artistLineEdit->text().isEmpty() || m_ui->titleLineEdit->text().isEmpty()) {
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Critical);
|
||||||
|
m_ui->notificationLabel->setText(tr("Insufficient search criteria supplied - artist and title are mandatory"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete current model
|
||||||
|
m_ui->resultsTreeView->setModel(nullptr);
|
||||||
|
delete m_model;
|
||||||
|
|
||||||
|
// show status
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
||||||
|
m_ui->notificationLabel->setText(tr("Retrieving lyrics from Tekstowo ..."));
|
||||||
|
setStatus(false);
|
||||||
|
|
||||||
|
// do actual query
|
||||||
|
useQueryResults(queryTekstowo(currentSongDescription()));
|
||||||
|
}
|
||||||
|
|
||||||
void DbQueryWidget::abortSearch()
|
void DbQueryWidget::abortSearch()
|
||||||
{
|
{
|
||||||
if (!m_model) {
|
if (!m_model) {
|
||||||
|
@ -331,7 +370,9 @@ void DbQueryWidget::setStatus(bool aborted)
|
||||||
{
|
{
|
||||||
m_ui->abortPushButton->setVisible(!aborted);
|
m_ui->abortPushButton->setVisible(!aborted);
|
||||||
m_searchMusicBrainzAction->setEnabled(aborted);
|
m_searchMusicBrainzAction->setEnabled(aborted);
|
||||||
m_searchLyricsWikiaAction->setEnabled(aborted);
|
if (m_searchLyricsWikiaAction) {
|
||||||
|
m_searchLyricsWikiaAction->setEnabled(aborted);
|
||||||
|
}
|
||||||
m_ui->applyPushButton->setVisible(aborted);
|
m_ui->applyPushButton->setVisible(aborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ public Q_SLOTS:
|
||||||
void searchMusicBrainz();
|
void searchMusicBrainz();
|
||||||
void searchLyricsWikia();
|
void searchLyricsWikia();
|
||||||
void searchMakeItPersonal();
|
void searchMakeItPersonal();
|
||||||
|
void searchTekstowo();
|
||||||
void abortSearch();
|
void abortSearch();
|
||||||
void applySelectedResults();
|
void applySelectedResults();
|
||||||
void applySpecifiedResults(const QModelIndex &modelIndex);
|
void applySpecifiedResults(const QModelIndex &modelIndex);
|
||||||
|
@ -82,6 +83,7 @@ private:
|
||||||
QAction *m_searchMusicBrainzAction;
|
QAction *m_searchMusicBrainzAction;
|
||||||
QAction *m_searchLyricsWikiaAction;
|
QAction *m_searchLyricsWikiaAction;
|
||||||
QAction *m_searchMakeItPersonalAction;
|
QAction *m_searchMakeItPersonalAction;
|
||||||
|
QAction *m_searchTekstowoAction;
|
||||||
QAction *m_lastSearchAction;
|
QAction *m_lastSearchAction;
|
||||||
QAction *m_refreshAutomaticallyAction;
|
QAction *m_refreshAutomaticallyAction;
|
||||||
QPoint m_contextMenuPos;
|
QPoint m_contextMenuPos;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import * as http from "http.js"
|
const cache = {};
|
||||||
|
|
||||||
function waitFor(signal) {
|
function waitFor(signal) {
|
||||||
signal.connect(() => { utility.exit(); });
|
signal.connect(() => { utility.exit(); });
|
||||||
utility.exec();
|
utility.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryMakeItPersonal(searchCriteria) {
|
function queryProvider(provider, searchCriteria) {
|
||||||
const lyricsModel = utility.queryMakeItPersonal(searchCriteria);
|
const lyricsModel = utility["query" + provider](searchCriteria);
|
||||||
if (!lyricsModel.areResultsAvailable) {
|
if (!lyricsModel.areResultsAvailable) {
|
||||||
waitFor(lyricsModel.resultsAvailable);
|
waitFor(lyricsModel.resultsAvailable);
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,21 @@ function queryMakeItPersonal(searchCriteria) {
|
||||||
return lyrics;
|
return lyrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function queryProviders(providers, searchCriteria) {
|
||||||
|
for (const provider of providers) {
|
||||||
|
const res = queryProvider(provider, searchCriteria);
|
||||||
|
if (res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function queryLyrics(searchCriteria) {
|
export function queryLyrics(searchCriteria) {
|
||||||
return queryMakeItPersonal(searchCriteria);
|
const cacheKey = searchCriteria.title + "_" + searchCriteria.artist;
|
||||||
|
const cachedValue = cache[cacheKey];
|
||||||
|
return cachedValue
|
||||||
|
? cachedValue
|
||||||
|
: cache[cacheKey] = queryProviders(["Tekstowo", "MakeItPersonal"], searchCriteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue