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/makeitpersonal.h
|
||||
dbquery/lyricswikia.h
|
||||
dbquery/tekstowo.h
|
||||
gui/dbquerywidget.h
|
||||
misc/networkaccessmanager.h
|
||||
renamingutility/filesystemitem.h
|
||||
|
@ -82,6 +83,7 @@ set(WIDGETS_SRC_FILES
|
|||
dbquery/musicbrainz.cpp
|
||||
dbquery/makeitpersonal.cpp
|
||||
dbquery/lyricswikia.cpp
|
||||
dbquery/tekstowo.cpp
|
||||
gui/dbquerywidget.cpp
|
||||
misc/networkaccessmanager.cpp
|
||||
renamingutility/filesystemitem.cpp
|
||||
|
|
|
@ -206,6 +206,7 @@ void restore()
|
|||
v.dbQuery.musicBrainzUrl = settings.value(QStringLiteral("musicbrainzurl")).toString();
|
||||
v.dbQuery.lyricsWikiaUrl = settings.value(QStringLiteral("lyricwikiurl")).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();
|
||||
settings.endGroup();
|
||||
|
||||
|
@ -302,6 +303,7 @@ void save()
|
|||
settings.setValue(QStringLiteral("musicbrainzurl"), v.dbQuery.musicBrainzUrl);
|
||||
settings.setValue(QStringLiteral("lyricwikiurl"), v.dbQuery.lyricsWikiaUrl);
|
||||
settings.setValue(QStringLiteral("makeitpersonalurl"), v.dbQuery.makeItPersonalUrl);
|
||||
settings.setValue(QStringLiteral("tekstowourl"), v.dbQuery.tekstowoUrl);
|
||||
settings.setValue(QStringLiteral("coverartarchiveurl"), v.dbQuery.coverArtArchiveUrl);
|
||||
settings.endGroup();
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ struct DbQuery {
|
|||
QString coverArtArchiveUrl;
|
||||
QString lyricsWikiaUrl;
|
||||
QString makeItPersonalUrl;
|
||||
QString tekstowoUrl;
|
||||
};
|
||||
|
||||
struct RenamingUtility {
|
||||
|
|
|
@ -142,6 +142,11 @@ QJSValue UtilityObject::queryMakeItPersonal(const QJSValue &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)
|
||||
{
|
||||
auto desc = QtGui::SongDescription(obj.property(QStringLiteral("songId")).toString());
|
||||
|
|
|
@ -61,6 +61,7 @@ public Q_SLOTS:
|
|||
QJSValue queryMusicBrainz(const QJSValue &songDescription);
|
||||
QJSValue queryLyricsWikia(const QJSValue &songDescription);
|
||||
QJSValue queryMakeItPersonal(const QJSValue &songDescription);
|
||||
QJSValue queryTekstowo(const QJSValue &songDescription);
|
||||
|
||||
private:
|
||||
static QtGui::SongDescription makeSongDescription(const QJSValue &obj);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "../misc/networkaccessmanager.h"
|
||||
#include "../misc/utility.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <tagparser/signature.h>
|
||||
#include <tagparser/tag.h>
|
||||
#include <tagparser/tagvalue.h>
|
||||
|
@ -25,7 +27,7 @@ SongDescription::SongDescription(const QString &songId)
|
|||
}
|
||||
|
||||
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)
|
||||
: QAbstractTableModel(parent)
|
||||
|
@ -104,7 +106,7 @@ QVariant QueryResultsModel::data(const QModelIndex &index, int role) const
|
|||
if (!index.isValid() || index.row() >= m_results.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
const SongDescription &res = m_results.at(index.row());
|
||||
const auto &res = m_results.at(index.row());
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
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 flags = Qt::ItemNeverHasChildren | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
auto flags = Qt::ItemFlags(Qt::ItemNeverHasChildren | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
if (index.isValid()) {
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
@ -311,6 +313,16 @@ void HttpResultsModel::handleInitialReplyFinished()
|
|||
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)
|
||||
{
|
||||
// delete reply (later)
|
||||
|
@ -328,8 +340,10 @@ QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByt
|
|||
m_errorList << tr("Server replied no data.");
|
||||
}
|
||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||
cerr << "Results from HTTP query:" << endl;
|
||||
cerr << data.data() << endl;
|
||||
static const auto enableQueryLogging = qEnvironmentVariableIntValue(PROJECT_VARNAME_UPPER "_ENABLE_QUERY_LOGGING");
|
||||
if (enableQueryLogging) {
|
||||
std::cerr << "Results from HTTP query:\n" << data.data() << '\n';
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -366,7 +380,7 @@ void HttpResultsModel::abort()
|
|||
|
||||
void HttpResultsModel::handleCoverReplyFinished(QNetworkReply *reply, const QString &albumId, int row)
|
||||
{
|
||||
QByteArray data;
|
||||
auto data = QByteArray();
|
||||
if (auto *const newReply = evaluateReplyResults(reply, data, true)) {
|
||||
addReply(newReply, bind(&HttpResultsModel::handleCoverReplyFinished, this, newReply, albumId, row));
|
||||
return;
|
||||
|
|
|
@ -22,7 +22,7 @@ TAGEDITOR_ENUM_CLASS KnownField : unsigned int;
|
|||
namespace QtGui {
|
||||
|
||||
struct SongDescription {
|
||||
SongDescription(const QString &songId = QString());
|
||||
explicit SongDescription(const QString &songId = QString());
|
||||
|
||||
QString songId;
|
||||
QString title;
|
||||
|
@ -85,7 +85,7 @@ Q_SIGNALS:
|
|||
void lyricsAvailable(const QModelIndex &index);
|
||||
|
||||
protected:
|
||||
QueryResultsModel(QObject *parent = nullptr);
|
||||
explicit QueryResultsModel(QObject *parent = nullptr);
|
||||
void setResultsAvailable(bool resultsAvailable);
|
||||
void setFetchingCover(bool fetchingCover);
|
||||
|
||||
|
@ -124,7 +124,7 @@ public:
|
|||
void abort() override;
|
||||
|
||||
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 Function> void addReply(QNetworkReply *reply, Function handler);
|
||||
virtual void parseInitialResults(const QByteArray &data) = 0;
|
||||
|
@ -135,17 +135,20 @@ protected:
|
|||
|
||||
private Q_SLOTS:
|
||||
void handleInitialReplyFinished();
|
||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||
void logReply(QNetworkReply *reply);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
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)
|
||||
{
|
||||
(m_replies << reply), connect(reply, &QNetworkReply::finished, object, handler);
|
||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||
std::cerr << "HTTP query: " << reply->url().toString().toUtf8().data() << std::endl;
|
||||
logReply(reply);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -157,7 +160,7 @@ template <class Function> inline void HttpResultsModel::addReply(QNetworkReply *
|
|||
{
|
||||
(m_replies << reply), connect(reply, &QNetworkReply::finished, handler);
|
||||
#ifdef CPP_UTILITIES_DEBUG_BUILD
|
||||
std::cerr << "HTTP query: " << reply->url().toString().toUtf8().data() << std::endl;
|
||||
logReply(reply);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -165,6 +168,7 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription);
|
|||
QueryResultsModel *queryLyricsWikia(SongDescription &&songDescription);
|
||||
QNetworkReply *queryCoverArtArchive(const QString &albumId);
|
||||
QueryResultsModel *queryMakeItPersonal(SongDescription &&songDescription);
|
||||
QueryResultsModel *queryTekstowo(SongDescription &&songDescription);
|
||||
|
||||
} // namespace QtGui
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ using namespace Utility;
|
|||
|
||||
namespace QtGui {
|
||||
|
||||
static const QString defaultLyricsWikiaUrl(QStringLiteral("https://lyrics.fandom.com"));
|
||||
static const auto defaultLyricsWikiaUrl = QStringLiteral("https://lyrics.fandom.com");
|
||||
|
||||
static QUrl lyricsWikiaApiUrl()
|
||||
{
|
||||
|
@ -43,15 +43,12 @@ LyricsWikiaResultsModel::LyricsWikiaResultsModel(SongDescription &&initialSongDe
|
|||
|
||||
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()) {
|
||||
return true;
|
||||
}
|
||||
SongDescription &desc = m_results[index.row()];
|
||||
|
||||
// skip if cover is already available
|
||||
auto &desc = m_results[index.row()];
|
||||
if (!desc.cover.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -87,13 +84,12 @@ bool LyricsWikiaResultsModel::fetchCover(const QModelIndex &index)
|
|||
|
||||
bool LyricsWikiaResultsModel::fetchLyrics(const QModelIndex &index)
|
||||
{
|
||||
// find song description
|
||||
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
||||
return true;
|
||||
}
|
||||
SongDescription &desc = m_results[index.row()];
|
||||
|
||||
// skip if lyrics already present
|
||||
auto &desc = m_results[index.row()];
|
||||
if (!desc.lyrics.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -113,25 +109,24 @@ bool LyricsWikiaResultsModel::fetchLyrics(const QModelIndex &index)
|
|||
|
||||
void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||
{
|
||||
// prepare parsing LyricsWikia meta data
|
||||
beginResetModel();
|
||||
m_results.clear();
|
||||
QXmlStreamReader xmlReader(data);
|
||||
|
||||
// parse XML tree
|
||||
auto xmlReader = QXmlStreamReader(data);
|
||||
// clang-format off
|
||||
#include <qtutilities/misc/xmlparsermacros.h>
|
||||
children {
|
||||
iftag("getArtistResponse") {
|
||||
QString artist;
|
||||
auto artist = QString();
|
||||
children {
|
||||
iftag("artist") {
|
||||
artist = text;
|
||||
} eliftag("albums") {
|
||||
children {
|
||||
iftag("albumResult") {
|
||||
QString album, year;
|
||||
QList<SongDescription> songs;
|
||||
auto album = QString(), year = QString();
|
||||
auto songs = QList<SongDescription>();
|
||||
children {
|
||||
iftag("album") {
|
||||
album = text;
|
||||
|
@ -169,7 +164,7 @@ void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
|||
}
|
||||
else_skip
|
||||
}
|
||||
for (SongDescription &song : m_results) {
|
||||
for (auto &song : m_results) {
|
||||
// set the arist which is the same for all results
|
||||
song.artist = artist;
|
||||
// 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();
|
||||
}
|
||||
|
||||
// promote changes
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QNetworkReply *LyricsWikiaResultsModel::requestSongDetails(const SongDescription &songDescription)
|
||||
{
|
||||
// compose URL
|
||||
QUrlQuery query;
|
||||
auto query = QUrlQuery();
|
||||
query.addQueryItem(QStringLiteral("func"), QStringLiteral("getSong"));
|
||||
query.addQueryItem(QStringLiteral("action"), QStringLiteral("lyrics"));
|
||||
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
|
||||
query.addQueryItem(QStringLiteral("album"), songDescription.album);
|
||||
}
|
||||
QUrl url(lyricsWikiaApiUrl());
|
||||
auto url = lyricsWikiaApiUrl();
|
||||
url.setQuery(query);
|
||||
|
||||
return Utility::networkAccessManager().get(QNetworkRequest(url));
|
||||
}
|
||||
|
||||
QNetworkReply *LyricsWikiaResultsModel::requestAlbumDetails(const SongDescription &songDescription)
|
||||
{
|
||||
QUrl url(lyricsWikiaApiUrl());
|
||||
auto url = lyricsWikiaApiUrl();
|
||||
url.setPath(QStringLiteral("/wiki/") + songDescription.albumId);
|
||||
return Utility::networkAccessManager().get(QNetworkRequest(url));
|
||||
}
|
||||
|
||||
void LyricsWikiaResultsModel::handleSongDetailsFinished(QNetworkReply *reply, int row)
|
||||
{
|
||||
QByteArray data;
|
||||
auto data = QByteArray();
|
||||
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||
addReply(newReply, bind(&LyricsWikiaResultsModel::handleSongDetailsFinished, this, newReply, row));
|
||||
} else if (!data.isEmpty()) {
|
||||
|
@ -234,23 +227,21 @@ void LyricsWikiaResultsModel::handleSongDetailsFinished(QNetworkReply *reply, in
|
|||
|
||||
void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
||||
{
|
||||
// find associated result/desc
|
||||
if (row >= m_results.size()) {
|
||||
m_errorList << tr("Internal error: context for song details reply invalid");
|
||||
setResultsAvailable(true);
|
||||
return;
|
||||
}
|
||||
SongDescription &assocDesc = m_results[row];
|
||||
|
||||
QUrl parsedUrl;
|
||||
|
||||
// parse XML tree
|
||||
auto &assocDesc = m_results[row];
|
||||
auto parsedUrl = QUrl();
|
||||
auto xmlReader = QXmlStreamReader(data);
|
||||
// clang-format off
|
||||
QXmlStreamReader xmlReader(data);
|
||||
#include <qtutilities/misc/xmlparsermacros.h>
|
||||
children {
|
||||
iftag("LyricsResult") {
|
||||
SongDescription parsedDesc;
|
||||
auto parsedDesc = SongDescription();
|
||||
children {
|
||||
iftag("artist") {
|
||||
parsedDesc.artist = text;
|
||||
|
@ -297,7 +288,7 @@ void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
|||
.arg(assocDesc.artist, assocDesc.title);
|
||||
}
|
||||
// -> do not use parsed URL "as-is" in any case to avoid unintended requests
|
||||
QUrl requestUrl(lyricsWikiaApiUrl());
|
||||
auto requestUrl = lyricsWikiaApiUrl();
|
||||
requestUrl.setPath(parsedUrl.path());
|
||||
// -> initialize the actual request
|
||||
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)
|
||||
{
|
||||
QByteArray data;
|
||||
auto data = QByteArray();
|
||||
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||
addReply(newReply, bind(&LyricsWikiaResultsModel::handleLyricsReplyFinished, this, newReply, row));
|
||||
return;
|
||||
|
@ -321,18 +312,15 @@ void LyricsWikiaResultsModel::handleLyricsReplyFinished(QNetworkReply *reply, in
|
|||
|
||||
void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data)
|
||||
{
|
||||
// find associated result/desc
|
||||
if (row >= m_results.size()) {
|
||||
m_errorList << tr("Internal error: context for LyricsWikia page reply invalid");
|
||||
setResultsAvailable(true);
|
||||
return;
|
||||
}
|
||||
SongDescription &assocDesc = m_results[row];
|
||||
|
||||
// convert data to QString
|
||||
const QString html(data);
|
||||
|
||||
// parse lyrics from HTML
|
||||
auto &assocDesc = m_results[row];
|
||||
const auto html = QString(data);
|
||||
const auto lyricsStart = html.indexOf(QLatin1String("<div class='lyricbox'>"));
|
||||
if (lyricsStart < 0) {
|
||||
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;
|
||||
}
|
||||
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));
|
||||
assocDesc.lyrics = textDoc.toPlainText();
|
||||
|
||||
|
@ -350,7 +338,7 @@ void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data
|
|||
|
||||
void LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished(QNetworkReply *reply, int row)
|
||||
{
|
||||
QByteArray data;
|
||||
auto data = QByteArray();
|
||||
if (auto *newReply = evaluateReplyResults(reply, data, true)) {
|
||||
addReply(newReply, bind(&LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished, this, newReply, row));
|
||||
} else {
|
||||
|
@ -367,19 +355,16 @@ void LyricsWikiaResultsModel::parseAlbumDetailsAndFetchCover(int row, const QByt
|
|||
return;
|
||||
}
|
||||
|
||||
// find associated result/desc
|
||||
if (row >= m_results.size()) {
|
||||
m_errorList << tr("Internal error: context for LyricsWikia page reply invalid");
|
||||
setFetchingCover(false);
|
||||
setResultsAvailable(true);
|
||||
return;
|
||||
}
|
||||
SongDescription &assocDesc = m_results[row];
|
||||
|
||||
// convert data to QString
|
||||
const auto html = QString(data);
|
||||
|
||||
// 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;
|
||||
if (coverDivStart > 56) {
|
||||
const auto coverHrefStart = html.indexOf(QLatin1String("href=\""), coverDivStart) + 6;
|
||||
|
@ -410,31 +395,28 @@ QUrl LyricsWikiaResultsModel::webUrl(const QModelIndex &index)
|
|||
return QUrl();
|
||||
}
|
||||
|
||||
SongDescription &desc = m_results[index.row()];
|
||||
auto &desc = m_results[index.row()];
|
||||
lazyInitializeLyricsWikiaSongId(desc);
|
||||
|
||||
// return URL
|
||||
QUrl url(lyricsWikiaApiUrl());
|
||||
auto url = lyricsWikiaApiUrl();
|
||||
url.setPath(QStringLiteral("/wiki/") + desc.songId);
|
||||
return url;
|
||||
}
|
||||
|
||||
QueryResultsModel *queryLyricsWikia(SongDescription &&songDescription)
|
||||
{
|
||||
// compose URL
|
||||
QUrlQuery query;
|
||||
auto query = QUrlQuery();
|
||||
query.addQueryItem(QStringLiteral("func"), QStringLiteral("getArtist"));
|
||||
query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("xml"));
|
||||
query.addQueryItem(QStringLiteral("fixXML"), QString());
|
||||
query.addQueryItem(QStringLiteral("artist"), songDescription.artist);
|
||||
QUrl url(lyricsWikiaApiUrl());
|
||||
auto url = lyricsWikiaApiUrl();
|
||||
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
|
||||
// be done manually when parsing results.
|
||||
|
||||
// make request
|
||||
return new LyricsWikiaResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
||||
}
|
||||
|
||||
} // namespace QtGui
|
||||
|
|
|
@ -11,7 +11,7 @@ class LyricsWikiaResultsModel : public HttpResultsModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LyricsWikiaResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||
explicit LyricsWikiaResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||
bool fetchCover(const QModelIndex &index) override;
|
||||
bool fetchLyrics(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)
|
||||
{
|
||||
// prepare parsing meta data
|
||||
beginResetModel();
|
||||
m_results.clear();
|
||||
|
||||
SongDescription desc = m_initialDescription;
|
||||
auto desc = m_initialDescription;
|
||||
desc.songId = m_initialDescription.artist + m_initialDescription.title;
|
||||
desc.artistId = m_initialDescription.artist;
|
||||
desc.lyrics = QString::fromUtf8(data).trimmed();
|
||||
if (desc.lyrics != QLatin1String("Sorry, We don't have lyrics for this song yet.")) {
|
||||
m_results << std::move(desc);
|
||||
}
|
||||
|
||||
// promote changes
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QueryResultsModel *queryMakeItPersonal(SongDescription &&songDescription)
|
||||
{
|
||||
// compose URL
|
||||
QUrlQuery query;
|
||||
auto query = QUrlQuery();
|
||||
query.addQueryItem(QStringLiteral("artist"), songDescription.artist);
|
||||
query.addQueryItem(QStringLiteral("title"), songDescription.title);
|
||||
QUrl url(makeItPersonalApiUrl());
|
||||
auto url = makeItPersonalApiUrl();
|
||||
url.setQuery(query);
|
||||
|
||||
// make request
|
||||
return new MakeItPersonalResultsModel(std::move(songDescription), Utility::networkAccessManager().get(QNetworkRequest(url)));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class MakeItPersonalResultsModel : public HttpResultsModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MakeItPersonalResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||
explicit MakeItPersonalResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||
bool fetchLyrics(const QModelIndex &index) override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -40,15 +40,12 @@ MusicBrainzResultsModel::MusicBrainzResultsModel(SongDescription &&initialSongDe
|
|||
|
||||
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()) {
|
||||
return true;
|
||||
}
|
||||
SongDescription &desc = m_results[index.row()];
|
||||
|
||||
// skip if cover is already available
|
||||
auto &desc = m_results[index.row()];
|
||||
if (!desc.cover.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -84,17 +81,16 @@ QUrl MusicBrainzResultsModel::webUrl(const QModelIndex &index)
|
|||
|
||||
void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||
{
|
||||
// prepare parsing MusicBrainz meta data
|
||||
beginResetModel();
|
||||
m_results.clear();
|
||||
|
||||
// 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
|
||||
unordered_map<QString, vector<SongDescription>> releasesByRecording;
|
||||
auto releasesByRecording = std::unordered_map<QString, std::vector<SongDescription>>();
|
||||
|
||||
// parse XML tree
|
||||
QXmlStreamReader xmlReader(data);
|
||||
auto xmlReader = QXmlStreamReader(data);
|
||||
// clang-format off
|
||||
#include <qtutilities/misc/xmlparsermacros.h>
|
||||
children {
|
||||
|
@ -103,7 +99,7 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
|||
iftag("recording-list") {
|
||||
children {
|
||||
iftag("recording") {
|
||||
SongDescription currentDescription(attribute("id").toString());
|
||||
auto currentDescription = SongDescription(attribute("id").toString());
|
||||
children {
|
||||
iftag("title") {
|
||||
currentDescription.title = text;
|
||||
|
@ -130,7 +126,7 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
|||
} eliftag("release-list") {
|
||||
children {
|
||||
iftag("release") {
|
||||
SongDescription releaseInfo;
|
||||
auto releaseInfo = SongDescription();
|
||||
releaseInfo.albumId = attribute("id").toString();
|
||||
children {
|
||||
iftag("title") {
|
||||
|
@ -220,7 +216,7 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
|||
|
||||
// populate results
|
||||
// -> 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) {
|
||||
const auto &releases = releasesByRecording[recording.songId];
|
||||
for (const auto &release : releases) {
|
||||
|
@ -272,7 +268,6 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
|||
m_errorList << xmlReader.errorString();
|
||||
}
|
||||
|
||||
// promote changes
|
||||
endResetModel();
|
||||
}
|
||||
// clang-format on
|
||||
|
@ -281,8 +276,7 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
|||
{
|
||||
static const auto defaultMusicBrainzUrl(QStringLiteral("https://musicbrainz.org/ws/2/recording/"));
|
||||
|
||||
// compose parts
|
||||
QStringList parts;
|
||||
auto parts = QStringList();
|
||||
parts.reserve(4);
|
||||
if (!songDescription.title.isEmpty()) {
|
||||
parts << QChar('\"') % songDescription.title % QChar('\"');
|
||||
|
@ -297,15 +291,12 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
|||
parts << QStringLiteral("number:") + QString::number(songDescription.track);
|
||||
}
|
||||
|
||||
// compose URL
|
||||
const auto &musicBrainzUrl = Settings::values().dbQuery.musicBrainzUrl;
|
||||
QUrl url(musicBrainzUrl.isEmpty() ? defaultMusicBrainzUrl : (musicBrainzUrl + QStringLiteral("/recording/")));
|
||||
QUrlQuery query;
|
||||
auto url = QUrl(musicBrainzUrl.isEmpty() ? defaultMusicBrainzUrl : (musicBrainzUrl + QStringLiteral("/recording/")));
|
||||
auto query = QUrlQuery();
|
||||
query.addQueryItem(QStringLiteral("query"), parts.join(QStringLiteral(" AND ")));
|
||||
url.setQuery(query);
|
||||
|
||||
// make request
|
||||
QNetworkRequest request(url);
|
||||
auto request = QNetworkRequest(url);
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ private:
|
|||
enum What { MusicBrainzMetaData, CoverArt };
|
||||
|
||||
public:
|
||||
MusicBrainzResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||
explicit MusicBrainzResultsModel(SongDescription &&initialSongDescription, QNetworkReply *reply);
|
||||
bool fetchCover(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 "../misc/utility.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
#include "ui_dbquerywidget.h"
|
||||
|
||||
#include <tagparser/tag.h>
|
||||
|
@ -47,6 +48,13 @@ DbQueryWidget::DbQueryWidget(TagEditorWidget *tagEditorWidget, QWidget *parent)
|
|||
, m_coverIndex(-1)
|
||||
, m_lyricsIndex(-1)
|
||||
, 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);
|
||||
updateStyleSheet();
|
||||
|
@ -72,20 +80,27 @@ DbQueryWidget::DbQueryWidget(TagEditorWidget *tagEditorWidget, QWidget *parent)
|
|||
|
||||
// setup menu
|
||||
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->setIcon(searchIcon);
|
||||
m_searchMusicBrainzAction = m_lastSearchAction = m_menu->addAction(tr("Query MusicBrainz"));
|
||||
m_searchMusicBrainzAction->setIcon(searchIcon);
|
||||
m_searchMusicBrainzAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_M));
|
||||
connect(m_searchMusicBrainzAction, &QAction::triggered, this, &DbQueryWidget::searchMusicBrainz);
|
||||
m_searchLyricsWikiaAction = m_menu->addAction(tr("Query LyricsWikia"));
|
||||
m_searchLyricsWikiaAction->setIcon(searchIcon);
|
||||
m_searchLyricsWikiaAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_L));
|
||||
connect(m_searchLyricsWikiaAction, &QAction::triggered, this, &DbQueryWidget::searchLyricsWikia);
|
||||
m_searchMakeItPersonalAction = m_menu->addAction(tr("Query makeitpersonal"));
|
||||
m_searchMakeItPersonalAction->setIcon(searchIcon);
|
||||
m_searchMakeItPersonalAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_K));
|
||||
connect(m_searchMakeItPersonalAction, &QAction::triggered, this, &DbQueryWidget::searchMakeItPersonal);
|
||||
if (enableLegacyProvider) {
|
||||
m_searchLyricsWikiaAction = m_menu->addAction(tr("Query LyricsWikia"));
|
||||
m_searchLyricsWikiaAction->setIcon(searchIcon);
|
||||
m_searchLyricsWikiaAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_L));
|
||||
connect(m_searchLyricsWikiaAction, &QAction::triggered, this, &DbQueryWidget::searchLyricsWikia);
|
||||
m_searchMakeItPersonalAction = m_menu->addAction(tr("Query makeitpersonal"));
|
||||
m_searchMakeItPersonalAction->setIcon(searchIcon);
|
||||
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_insertPresentDataAction = m_menu->addAction(tr("Use present data as search criteria"));
|
||||
m_insertPresentDataAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
||||
|
@ -150,7 +165,7 @@ void DbQueryWidget::insertSearchTermsFromTagEdit(TagEdit *tagEdit, bool songSpec
|
|||
m_ui->titleLineEdit->setText(newTitle);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (m_lastSearchAction != m_searchMakeItPersonalAction) {
|
||||
if (m_lastSearchAction != m_searchTekstowoAction && m_lastSearchAction != m_searchMakeItPersonalAction) {
|
||||
const auto newTrackNumber = tagEdit->trackNumber();
|
||||
if (m_ui->trackSpinBox->value() != newTrackNumber) {
|
||||
m_ui->trackSpinBox->setValue(newTrackNumber);
|
||||
|
@ -246,6 +261,30 @@ void DbQueryWidget::searchMakeItPersonal()
|
|||
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()
|
||||
{
|
||||
if (!m_model) {
|
||||
|
@ -331,7 +370,9 @@ void DbQueryWidget::setStatus(bool aborted)
|
|||
{
|
||||
m_ui->abortPushButton->setVisible(!aborted);
|
||||
m_searchMusicBrainzAction->setEnabled(aborted);
|
||||
m_searchLyricsWikiaAction->setEnabled(aborted);
|
||||
if (m_searchLyricsWikiaAction) {
|
||||
m_searchLyricsWikiaAction->setEnabled(aborted);
|
||||
}
|
||||
m_ui->applyPushButton->setVisible(aborted);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ public Q_SLOTS:
|
|||
void searchMusicBrainz();
|
||||
void searchLyricsWikia();
|
||||
void searchMakeItPersonal();
|
||||
void searchTekstowo();
|
||||
void abortSearch();
|
||||
void applySelectedResults();
|
||||
void applySpecifiedResults(const QModelIndex &modelIndex);
|
||||
|
@ -82,6 +83,7 @@ private:
|
|||
QAction *m_searchMusicBrainzAction;
|
||||
QAction *m_searchLyricsWikiaAction;
|
||||
QAction *m_searchMakeItPersonalAction;
|
||||
QAction *m_searchTekstowoAction;
|
||||
QAction *m_lastSearchAction;
|
||||
QAction *m_refreshAutomaticallyAction;
|
||||
QPoint m_contextMenuPos;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import * as http from "http.js"
|
||||
const cache = {};
|
||||
|
||||
function waitFor(signal) {
|
||||
signal.connect(() => { utility.exit(); });
|
||||
utility.exec();
|
||||
}
|
||||
|
||||
function queryMakeItPersonal(searchCriteria) {
|
||||
const lyricsModel = utility.queryMakeItPersonal(searchCriteria);
|
||||
function queryProvider(provider, searchCriteria) {
|
||||
const lyricsModel = utility["query" + provider](searchCriteria);
|
||||
if (!lyricsModel.areResultsAvailable) {
|
||||
waitFor(lyricsModel.resultsAvailable);
|
||||
}
|
||||
|
@ -20,8 +20,21 @@ function queryMakeItPersonal(searchCriteria) {
|
|||
return lyrics;
|
||||
}
|
||||
|
||||
function queryProviders(providers, searchCriteria) {
|
||||
for (const provider of providers) {
|
||||
const res = queryProvider(provider, searchCriteria);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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