Improve coding style and formatting in dbquery code
This commit is contained in:
parent
a32db9e33d
commit
8e59c5b24f
|
@ -59,36 +59,37 @@ QUrl QueryResultsModel::webUrl(const QModelIndex &index)
|
||||||
|
|
||||||
TagValue QueryResultsModel::fieldValue(int row, KnownField knownField) const
|
TagValue QueryResultsModel::fieldValue(int row, KnownField knownField) const
|
||||||
{
|
{
|
||||||
if (row < m_results.size()) {
|
if (row >= m_results.size()) {
|
||||||
const SongDescription &res = m_results.at(row);
|
return TagValue();
|
||||||
switch (knownField) {
|
}
|
||||||
case KnownField::Title:
|
const SongDescription &res = m_results.at(row);
|
||||||
returnValue(title);
|
switch (knownField) {
|
||||||
case KnownField::Album:
|
case KnownField::Title:
|
||||||
returnValue(album);
|
returnValue(title);
|
||||||
case KnownField::Artist:
|
case KnownField::Album:
|
||||||
returnValue(artist);
|
returnValue(album);
|
||||||
case KnownField::Genre:
|
case KnownField::Artist:
|
||||||
returnValue(genre);
|
returnValue(artist);
|
||||||
case KnownField::Year:
|
case KnownField::Genre:
|
||||||
returnValue(year);
|
returnValue(genre);
|
||||||
case KnownField::TrackPosition:
|
case KnownField::Year:
|
||||||
return TagValue(PositionInSet(res.track, res.totalTracks));
|
returnValue(year);
|
||||||
case KnownField::PartNumber:
|
case KnownField::TrackPosition:
|
||||||
return TagValue(res.track);
|
return TagValue(PositionInSet(res.track, res.totalTracks));
|
||||||
case KnownField::TotalParts:
|
case KnownField::PartNumber:
|
||||||
return TagValue(res.totalTracks);
|
return TagValue(res.track);
|
||||||
case KnownField::Cover:
|
case KnownField::TotalParts:
|
||||||
if (!res.cover.isEmpty()) {
|
return TagValue(res.totalTracks);
|
||||||
TagValue tagValue(res.cover.data(), static_cast<size_t>(res.cover.size()), TagDataType::Picture);
|
case KnownField::Cover:
|
||||||
tagValue.setMimeType(containerMimeType(parseSignature(res.cover.data(), res.cover.size())));
|
if (!res.cover.isEmpty()) {
|
||||||
return tagValue;
|
TagValue tagValue(res.cover.data(), static_cast<size_t>(res.cover.size()), TagDataType::Picture);
|
||||||
}
|
tagValue.setMimeType(containerMimeType(parseSignature(res.cover.data(), res.cover.size())));
|
||||||
break;
|
return tagValue;
|
||||||
case KnownField::Lyrics:
|
|
||||||
returnValue(lyrics);
|
|
||||||
default:;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case KnownField::Lyrics:
|
||||||
|
returnValue(lyrics);
|
||||||
|
default:;
|
||||||
}
|
}
|
||||||
return TagValue();
|
return TagValue();
|
||||||
}
|
}
|
||||||
|
@ -97,38 +98,39 @@ TagValue QueryResultsModel::fieldValue(int row, KnownField knownField) const
|
||||||
|
|
||||||
QVariant QueryResultsModel::data(const QModelIndex &index, int role) const
|
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()) {
|
||||||
const SongDescription &res = m_results.at(index.row());
|
return QVariant();
|
||||||
switch (role) {
|
}
|
||||||
case Qt::DisplayRole:
|
const SongDescription &res = m_results.at(index.row());
|
||||||
switch (index.column()) {
|
switch (role) {
|
||||||
case TitleCol:
|
case Qt::DisplayRole:
|
||||||
return res.title;
|
switch (index.column()) {
|
||||||
case AlbumCol:
|
case TitleCol:
|
||||||
return res.album;
|
return res.title;
|
||||||
case ArtistCol:
|
case AlbumCol:
|
||||||
return res.artist;
|
return res.album;
|
||||||
case GenreCol:
|
case ArtistCol:
|
||||||
return res.genre;
|
return res.artist;
|
||||||
case YearCol:
|
case GenreCol:
|
||||||
return res.year;
|
return res.genre;
|
||||||
case TrackCol:
|
case YearCol:
|
||||||
if (res.track) {
|
return res.year;
|
||||||
return res.track;
|
case TrackCol:
|
||||||
} else {
|
if (res.track) {
|
||||||
return QString();
|
return res.track;
|
||||||
}
|
} else {
|
||||||
case TotalTracksCol:
|
return QString();
|
||||||
if (res.totalTracks) {
|
}
|
||||||
return res.totalTracks;
|
case TotalTracksCol:
|
||||||
} else {
|
if (res.totalTracks) {
|
||||||
return QString();
|
return res.totalTracks;
|
||||||
}
|
} else {
|
||||||
default:;
|
return QString();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -186,11 +188,12 @@ int QueryResultsModel::columnCount(const QModelIndex &parent) const
|
||||||
|
|
||||||
const QByteArray *QueryResultsModel::cover(const QModelIndex &index) const
|
const QByteArray *QueryResultsModel::cover(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if (!index.parent().isValid() && index.row() < m_results.size()) {
|
if (!index.isValid() || index.row() >= m_results.size()) {
|
||||||
const QByteArray &cover = m_results.at(index.row()).cover;
|
return nullptr;
|
||||||
if (!cover.isEmpty()) {
|
}
|
||||||
return &cover;
|
const auto &cover = m_results.at(index.row()).cover;
|
||||||
}
|
if (!cover.isEmpty()) {
|
||||||
|
return &cover;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -216,11 +219,12 @@ bool QueryResultsModel::fetchCover(const QModelIndex &index)
|
||||||
|
|
||||||
const QString *QueryResultsModel::lyrics(const QModelIndex &index) const
|
const QString *QueryResultsModel::lyrics(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if (!index.parent().isValid() && index.row() < m_results.size()) {
|
if (!index.isValid() || index.row() >= m_results.size()) {
|
||||||
const QString &lyrics = m_results.at(index.row()).lyrics;
|
return nullptr;
|
||||||
if (!lyrics.isEmpty()) {
|
}
|
||||||
return &lyrics;
|
const auto &lyrics = m_results.at(index.row()).lyrics;
|
||||||
}
|
if (!lyrics.isEmpty()) {
|
||||||
|
return &lyrics;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -268,16 +272,16 @@ HttpResultsModel::~HttpResultsModel()
|
||||||
*/
|
*/
|
||||||
void HttpResultsModel::handleInitialReplyFinished()
|
void HttpResultsModel::handleInitialReplyFinished()
|
||||||
{
|
{
|
||||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
if (auto *newReply = evaluateReplyResults(reply, data, false)) {
|
if (auto *const newReply = evaluateReplyResults(reply, data, false)) {
|
||||||
addReply(newReply, this, &HttpResultsModel::handleInitialReplyFinished);
|
addReply(newReply, this, &HttpResultsModel::handleInitialReplyFinished);
|
||||||
} else {
|
return;
|
||||||
if (!data.isEmpty()) {
|
|
||||||
parseInitialResults(data);
|
|
||||||
}
|
|
||||||
setResultsAvailable(true); // update status, emit resultsAvailable()
|
|
||||||
}
|
}
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
parseInitialResults(data);
|
||||||
|
}
|
||||||
|
setResultsAvailable(true); // update status, emit resultsAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByteArray &data, bool alwaysFollowRedirection)
|
QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByteArray &data, bool alwaysFollowRedirection)
|
||||||
|
@ -286,37 +290,35 @@ QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByt
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
m_replies.removeAll(reply);
|
m_replies.removeAll(reply);
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
|
||||||
if (!redirectionTarget.isNull()) {
|
|
||||||
// there's a redirection available
|
|
||||||
// -> resolve new URL
|
|
||||||
const QUrl newUrl = reply->url().resolved(redirectionTarget.toUrl());
|
|
||||||
// -> ask user whether to follow redirection unless alwaysFollowRedirection is true
|
|
||||||
if (!alwaysFollowRedirection) {
|
|
||||||
const QString message
|
|
||||||
= tr("<p>Do you want to redirect form <i>%1</i> to <i>%2</i>?</p>").arg(reply->url().toString(), newUrl.toString());
|
|
||||||
alwaysFollowRedirection
|
|
||||||
= QMessageBox::question(nullptr, tr("Search"), message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
|
|
||||||
}
|
|
||||||
if (alwaysFollowRedirection) {
|
|
||||||
return networkAccessManager().get(QNetworkRequest(newUrl));
|
|
||||||
} else {
|
|
||||||
m_errorList << tr("Redirection to: ") + newUrl.toString();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((data = reply->readAll()).isEmpty()) {
|
|
||||||
m_errorList << tr("Server replied no data.");
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_BUILD
|
|
||||||
cerr << "Results from HTTP query:" << endl;
|
|
||||||
cerr << data.data() << endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_errorList << reply->errorString();
|
m_errorList << reply->errorString();
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
const auto redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||||
|
if (redirectionTarget.isNull()) {
|
||||||
|
// read all data if it is not redirection
|
||||||
|
if ((data = reply->readAll()).isEmpty()) {
|
||||||
|
m_errorList << tr("Server replied no data.");
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
|
cerr << "Results from HTTP query:" << endl;
|
||||||
|
cerr << data.data() << endl;
|
||||||
|
#endif
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's a redirection available
|
||||||
|
// -> resolve new URL
|
||||||
|
const auto newUrl = reply->url().resolved(redirectionTarget.toUrl());
|
||||||
|
// -> ask user whether to follow redirection unless alwaysFollowRedirection is true
|
||||||
|
if (!alwaysFollowRedirection) {
|
||||||
|
const auto message = tr("<p>Do you want to redirect form <i>%1</i> to <i>%2</i>?</p>").arg(reply->url().toString(), newUrl.toString());
|
||||||
|
alwaysFollowRedirection = QMessageBox::question(nullptr, tr("Search"), message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
|
||||||
|
}
|
||||||
|
if (alwaysFollowRedirection) {
|
||||||
|
return networkAccessManager().get(QNetworkRequest(newUrl));
|
||||||
|
}
|
||||||
|
m_errorList << tr("Redirection to: ") + newUrl.toString();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,40 +327,41 @@ QNetworkReply *HttpResultsModel::evaluateReplyResults(QNetworkReply *reply, QByt
|
||||||
*/
|
*/
|
||||||
void HttpResultsModel::abort()
|
void HttpResultsModel::abort()
|
||||||
{
|
{
|
||||||
if (!m_replies.isEmpty()) {
|
if (m_replies.isEmpty()) {
|
||||||
qDeleteAll(m_replies);
|
return;
|
||||||
m_replies.clear();
|
|
||||||
// must update status manually because handleReplyFinished() won't be called anymore
|
|
||||||
m_errorList << tr("Aborted by user.");
|
|
||||||
setResultsAvailable(true);
|
|
||||||
}
|
}
|
||||||
|
qDeleteAll(m_replies);
|
||||||
|
m_replies.clear();
|
||||||
|
// must update status manually because handleReplyFinished() won't be called anymore
|
||||||
|
m_errorList << tr("Aborted by user.");
|
||||||
|
setResultsAvailable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpResultsModel::handleCoverReplyFinished(QNetworkReply *reply, const QString &albumId, int row)
|
void HttpResultsModel::handleCoverReplyFinished(QNetworkReply *reply, const QString &albumId, int row)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
if (auto *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));
|
||||||
} else {
|
return;
|
||||||
if (!data.isEmpty()) {
|
|
||||||
parseCoverResults(albumId, row, data);
|
|
||||||
}
|
|
||||||
setResultsAvailable(true);
|
|
||||||
}
|
}
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
parseCoverResults(albumId, row, data);
|
||||||
|
}
|
||||||
|
setResultsAvailable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpResultsModel::parseCoverResults(const QString &albumId, int row, const QByteArray &data)
|
void HttpResultsModel::parseCoverResults(const QString &albumId, int row, const QByteArray &data)
|
||||||
{
|
{
|
||||||
// add cover -> determine album ID and row
|
// add cover -> determine album ID and row
|
||||||
if (!albumId.isEmpty() && row < m_results.size()) {
|
if (albumId.isEmpty() || row >= m_results.size()) {
|
||||||
if (!data.isEmpty()) {
|
|
||||||
m_coverData[albumId] = data;
|
|
||||||
m_results[row].cover = data;
|
|
||||||
emit coverAvailable(index(row, 0));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_errorList << tr("Internal error: context for cover reply invalid");
|
m_errorList << tr("Internal error: context for cover reply invalid");
|
||||||
setResultsAvailable(true);
|
setResultsAvailable(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
m_coverData[albumId] = data;
|
||||||
|
m_results[row].cover = data;
|
||||||
|
emit coverAvailable(index(row, 0));
|
||||||
}
|
}
|
||||||
setFetchingCover(false);
|
setFetchingCover(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ static const QString defaultLyricsWikiaUrl(QStringLiteral("https://lyrics.wikia.
|
||||||
|
|
||||||
QUrl lyricsWikiaApiUrl()
|
QUrl lyricsWikiaApiUrl()
|
||||||
{
|
{
|
||||||
const QString &lyricsWikiaUrl = Settings::values().dbQuery.lyricsWikiaUrl;
|
const auto &lyricsWikiaUrl = Settings::values().dbQuery.lyricsWikiaUrl;
|
||||||
return QUrl((lyricsWikiaUrl.isEmpty() ? defaultLyricsWikiaUrl : lyricsWikiaUrl) + QStringLiteral("/api.php"));
|
return QUrl((lyricsWikiaUrl.isEmpty() ? defaultLyricsWikiaUrl : lyricsWikiaUrl) + QStringLiteral("/api.php"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,33 +33,35 @@ LyricsWikiaResultsModel::LyricsWikiaResultsModel(SongDescription &&initialSongDe
|
||||||
|
|
||||||
bool LyricsWikiaResultsModel::fetchCover(const QModelIndex &index)
|
bool LyricsWikiaResultsModel::fetchCover(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
if (!index.parent().isValid() && index.row() < m_results.size()) {
|
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
||||||
SongDescription &desc = m_results[index.row()];
|
return true;
|
||||||
if (!desc.cover.isEmpty()) {
|
}
|
||||||
// cover is already available -> nothing to do
|
SongDescription &desc = m_results[index.row()];
|
||||||
} else if (!desc.albumId.isEmpty()) {
|
if (!desc.cover.isEmpty()) {
|
||||||
try {
|
// cover is already available -> nothing to do
|
||||||
// the item belongs to an album which cover has already been fetched
|
return true;
|
||||||
desc.cover = m_coverData.at(desc.albumId);
|
}
|
||||||
} catch (const out_of_range &) {
|
if (desc.albumId.isEmpty()) {
|
||||||
if (desc.coverUrl.isEmpty()) {
|
m_errorList << tr("Unable to fetch cover: Album ID unknown");
|
||||||
// request the cover URL
|
emit resultsAvailable();
|
||||||
auto *reply = requestAlbumDetails(desc);
|
return true;
|
||||||
addReply(reply, bind(&LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished, this, reply, index.row()));
|
}
|
||||||
setFetchingCover(true);
|
try {
|
||||||
return false;
|
// the item belongs to an album which cover has already been fetched
|
||||||
} else {
|
desc.cover = m_coverData.at(desc.albumId);
|
||||||
// request the cover art
|
} catch (const out_of_range &) {
|
||||||
auto *reply = networkAccessManager().get(QNetworkRequest(QUrl(desc.coverUrl)));
|
if (desc.coverUrl.isEmpty()) {
|
||||||
addReply(reply, bind(&LyricsWikiaResultsModel::handleCoverReplyFinished, this, reply, desc.albumId, index.row()));
|
// request the cover URL
|
||||||
setFetchingCover(true);
|
auto *const reply = requestAlbumDetails(desc);
|
||||||
return false;
|
addReply(reply, bind(&LyricsWikiaResultsModel::handleAlbumDetailsReplyFinished, this, reply, index.row()));
|
||||||
}
|
setFetchingCover(true);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
m_errorList << tr("Unable to fetch cover: Album ID unknown");
|
// request the cover art
|
||||||
emit resultsAvailable();
|
auto *const reply = networkAccessManager().get(QNetworkRequest(QUrl(desc.coverUrl)));
|
||||||
|
addReply(reply, bind(&LyricsWikiaResultsModel::handleCoverReplyFinished, this, reply, desc.albumId, index.row()));
|
||||||
|
setFetchingCover(true);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -90,42 +92,27 @@ void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
QXmlStreamReader xmlReader(data);
|
QXmlStreamReader xmlReader(data);
|
||||||
|
|
||||||
// parse XML tree
|
// parse XML tree
|
||||||
|
// clang-format off
|
||||||
#include <qtutilities/misc/xmlparsermacros.h>
|
#include <qtutilities/misc/xmlparsermacros.h>
|
||||||
children
|
children {
|
||||||
{
|
iftag("getArtistResponse") {
|
||||||
iftag("getArtistResponse")
|
|
||||||
{
|
|
||||||
QString artist;
|
QString artist;
|
||||||
children
|
children {
|
||||||
{
|
iftag("artist") {
|
||||||
iftag("artist")
|
|
||||||
{
|
|
||||||
artist = text;
|
artist = text;
|
||||||
}
|
} eliftag("albums") {
|
||||||
eliftag("albums")
|
children {
|
||||||
{
|
iftag("albumResult") {
|
||||||
children
|
|
||||||
{
|
|
||||||
iftag("albumResult")
|
|
||||||
{
|
|
||||||
QString album, year;
|
QString album, year;
|
||||||
QList<SongDescription> songs;
|
QList<SongDescription> songs;
|
||||||
children
|
children {
|
||||||
{
|
iftag("album") {
|
||||||
iftag("album")
|
|
||||||
{
|
|
||||||
album = text;
|
album = text;
|
||||||
}
|
} eliftag("year") {
|
||||||
eliftag("year")
|
|
||||||
{
|
|
||||||
year = text;
|
year = text;
|
||||||
}
|
} eliftag("songs") {
|
||||||
eliftag("songs")
|
children {
|
||||||
{
|
iftag("item") {
|
||||||
children
|
|
||||||
{
|
|
||||||
iftag("item")
|
|
||||||
{
|
|
||||||
songs << SongDescription();
|
songs << SongDescription();
|
||||||
songs.back().title = text;
|
songs.back().title = text;
|
||||||
songs.back().track = songs.size();
|
songs.back().track = songs.size();
|
||||||
|
@ -139,7 +126,7 @@ void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
if ((m_initialDescription.album.isEmpty() || m_initialDescription.album == album)
|
if ((m_initialDescription.album.isEmpty() || m_initialDescription.album == album)
|
||||||
&& (m_initialDescription.year.isEmpty() || m_initialDescription.year == year)
|
&& (m_initialDescription.year.isEmpty() || m_initialDescription.year == year)
|
||||||
&& (!m_initialDescription.totalTracks || m_initialDescription.totalTracks == songs.size())) {
|
&& (!m_initialDescription.totalTracks || m_initialDescription.totalTracks == songs.size())) {
|
||||||
for (SongDescription &song : songs) {
|
for (auto &song : songs) {
|
||||||
if ((m_initialDescription.title.isEmpty() || m_initialDescription.title == song.title)
|
if ((m_initialDescription.title.isEmpty() || m_initialDescription.title == song.title)
|
||||||
&& (!m_initialDescription.track || m_initialDescription.track == song.track)) {
|
&& (!m_initialDescription.track || m_initialDescription.track == song.track)) {
|
||||||
song.album = album;
|
song.album = album;
|
||||||
|
@ -166,6 +153,7 @@ void LyricsWikiaResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
#include <qtutilities/misc/undefxmlparsermacros.h>
|
#include <qtutilities/misc/undefxmlparsermacros.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// check for parsing errors
|
// check for parsing errors
|
||||||
switch (xmlReader.error()) {
|
switch (xmlReader.error()) {
|
||||||
|
@ -230,25 +218,18 @@ void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
||||||
QUrl parsedUrl;
|
QUrl parsedUrl;
|
||||||
|
|
||||||
// parse XML tree
|
// parse XML tree
|
||||||
|
// clang-format off
|
||||||
QXmlStreamReader xmlReader(data);
|
QXmlStreamReader xmlReader(data);
|
||||||
#include <qtutilities/misc/xmlparsermacros.h>
|
#include <qtutilities/misc/xmlparsermacros.h>
|
||||||
children
|
children {
|
||||||
{
|
iftag("LyricsResult") {
|
||||||
iftag("LyricsResult")
|
|
||||||
{
|
|
||||||
SongDescription parsedDesc;
|
SongDescription parsedDesc;
|
||||||
children
|
children {
|
||||||
{
|
iftag("artist") {
|
||||||
iftag("artist")
|
|
||||||
{
|
|
||||||
parsedDesc.artist = text;
|
parsedDesc.artist = text;
|
||||||
}
|
} eliftag("song") {
|
||||||
eliftag("song")
|
|
||||||
{
|
|
||||||
parsedDesc.title = text;
|
parsedDesc.title = text;
|
||||||
}
|
} eliftag("url") {
|
||||||
eliftag("url")
|
|
||||||
{
|
|
||||||
parsedUrl = text;
|
parsedUrl = text;
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
|
@ -264,6 +245,7 @@ void LyricsWikiaResultsModel::parseSongDetails(int row, const QByteArray &data)
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
#include <qtutilities/misc/undefxmlparsermacros.h>
|
#include <qtutilities/misc/undefxmlparsermacros.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// check for parsing errors
|
// check for parsing errors
|
||||||
switch (xmlReader.error()) {
|
switch (xmlReader.error()) {
|
||||||
|
@ -296,12 +278,12 @@ void LyricsWikiaResultsModel::handleLyricsReplyFinished(QNetworkReply *reply, in
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
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));
|
||||||
} else {
|
return;
|
||||||
if (!data.isEmpty()) {
|
|
||||||
parseLyricsResults(row, data);
|
|
||||||
}
|
|
||||||
setResultsAvailable(true);
|
|
||||||
}
|
}
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
parseLyricsResults(row, data);
|
||||||
|
}
|
||||||
|
setResultsAvailable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data)
|
void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data)
|
||||||
|
@ -318,13 +300,13 @@ void LyricsWikiaResultsModel::parseLyricsResults(int row, const QByteArray &data
|
||||||
const QString html(data);
|
const QString html(data);
|
||||||
|
|
||||||
// parse lyrics from HTML
|
// parse lyrics from HTML
|
||||||
const int 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);
|
||||||
setResultsAvailable(true);
|
setResultsAvailable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const int lyricsEnd = html.indexOf(QLatin1String("<div class='lyricsbreak'></div>"), lyricsStart);
|
const auto lyricsEnd = html.indexOf(QLatin1String("<div class='lyricsbreak'></div>"), lyricsStart);
|
||||||
QTextDocument textDoc;
|
QTextDocument textDoc;
|
||||||
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();
|
||||||
|
@ -384,7 +366,7 @@ void LyricsWikiaResultsModel::parseAlbumDetailsAndFetchCover(int row, const QByt
|
||||||
}
|
}
|
||||||
|
|
||||||
// request the cover art
|
// request the cover art
|
||||||
auto *reply = networkAccessManager().get(QNetworkRequest(QUrl(assocDesc.coverUrl)));
|
auto *const reply = networkAccessManager().get(QNetworkRequest(QUrl(assocDesc.coverUrl)));
|
||||||
addReply(reply, bind(&LyricsWikiaResultsModel::handleCoverReplyFinished, this, reply, assocDesc.albumId, row));
|
addReply(reply, bind(&LyricsWikiaResultsModel::handleCoverReplyFinished, this, reply, assocDesc.albumId, row));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,25 +25,29 @@ MusicBrainzResultsModel::MusicBrainzResultsModel(SongDescription &&initialSongDe
|
||||||
|
|
||||||
bool MusicBrainzResultsModel::fetchCover(const QModelIndex &index)
|
bool MusicBrainzResultsModel::fetchCover(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
if (!index.parent().isValid() && index.row() < m_results.size()) {
|
if (index.parent().isValid() || index.row() >= m_results.size()) {
|
||||||
SongDescription &desc = m_results[index.row()];
|
return true;
|
||||||
if (!desc.cover.isEmpty()) {
|
}
|
||||||
// cover is already available -> nothing to do
|
SongDescription &desc = m_results[index.row()];
|
||||||
} else if (!desc.albumId.isEmpty()) {
|
if (!desc.cover.isEmpty()) {
|
||||||
try {
|
// cover is already available -> nothing to do
|
||||||
// the item belongs to an album which cover has already been fetched
|
return true;
|
||||||
desc.cover = m_coverData.at(desc.albumId);
|
}
|
||||||
} catch (const out_of_range &) {
|
|
||||||
// request the cover art
|
if (desc.albumId.isEmpty()) {
|
||||||
auto *reply = queryCoverArtArchive(desc.albumId);
|
m_errorList << tr("Unable to fetch cover: Album ID unknown");
|
||||||
addReply(reply, bind(&MusicBrainzResultsModel::handleCoverReplyFinished, this, reply, desc.albumId, index.row()));
|
emit resultsAvailable();
|
||||||
setFetchingCover(true);
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
try {
|
||||||
} else {
|
// the item belongs to an album which cover has already been fetched
|
||||||
m_errorList << tr("Unable to fetch cover: Album ID unknown");
|
desc.cover = m_coverData.at(desc.albumId);
|
||||||
emit resultsAvailable();
|
} catch (const out_of_range &) {
|
||||||
}
|
// request the cover art
|
||||||
|
auto *const reply = queryCoverArtArchive(desc.albumId);
|
||||||
|
addReply(reply, bind(&MusicBrainzResultsModel::handleCoverReplyFinished, this, reply, desc.albumId, index.row()));
|
||||||
|
setFetchingCover(true);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -64,90 +68,61 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
QXmlStreamReader xmlReader(data);
|
QXmlStreamReader xmlReader(data);
|
||||||
|
|
||||||
// parse XML tree
|
// parse XML tree
|
||||||
|
// clang-format off
|
||||||
#include <qtutilities/misc/xmlparsermacros.h>
|
#include <qtutilities/misc/xmlparsermacros.h>
|
||||||
children
|
children {
|
||||||
{
|
iftag("metadata") {
|
||||||
iftag("metadata")
|
children {
|
||||||
{
|
iftag("recording-list") {
|
||||||
children
|
children {
|
||||||
{
|
iftag("recording") {
|
||||||
iftag("recording-list")
|
|
||||||
{
|
|
||||||
children
|
|
||||||
{
|
|
||||||
iftag("recording")
|
|
||||||
{
|
|
||||||
SongDescription currentDescription(attribute("id").toString());
|
SongDescription currentDescription(attribute("id").toString());
|
||||||
children{ iftag("title"){ currentDescription.title = text;
|
children {
|
||||||
}
|
iftag("title") {
|
||||||
eliftag("artist-credit")
|
currentDescription.title = text;
|
||||||
{
|
} eliftag("artist-credit") {
|
||||||
children
|
children {
|
||||||
{
|
iftag("name-credit") {
|
||||||
iftag("name-credit")
|
children {
|
||||||
{
|
iftag("artist") {
|
||||||
children
|
children {
|
||||||
{
|
iftag("name") {
|
||||||
iftag("artist")
|
currentDescription.artist = text;
|
||||||
{
|
}
|
||||||
children
|
else_skip
|
||||||
{
|
}
|
||||||
iftag("name")
|
|
||||||
{
|
|
||||||
currentDescription.artist = text;
|
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
}
|
} eliftag("release-list") {
|
||||||
else_skip
|
children {
|
||||||
}
|
iftag("release") {
|
||||||
}
|
if (currentDescription.albumId.isEmpty()) {
|
||||||
eliftag("release-list")
|
currentDescription.albumId = attribute("id").toString();
|
||||||
{
|
}
|
||||||
children
|
children {
|
||||||
{
|
iftag("title") {
|
||||||
iftag("release")
|
currentDescription.album = text;
|
||||||
{
|
} eliftag("date") {
|
||||||
if (currentDescription.albumId.isEmpty()) {
|
currentDescription.year = text;
|
||||||
currentDescription.albumId = attribute("id").toString();
|
} eliftag("medium-list") {
|
||||||
}
|
children {
|
||||||
children
|
iftag("medium") {
|
||||||
{
|
children {
|
||||||
iftag("title")
|
iftag("position") {
|
||||||
{
|
currentDescription.disk = text.toInt();
|
||||||
currentDescription.album = text;
|
} eliftag("track-list") {
|
||||||
}
|
currentDescription.totalTracks = attribute("count").toInt();
|
||||||
eliftag("date")
|
children {
|
||||||
{
|
iftag("track") {
|
||||||
currentDescription.year = text;
|
children {
|
||||||
}
|
iftag("number") {
|
||||||
eliftag("medium-list")
|
currentDescription.track = text.toInt();
|
||||||
{
|
} else_skip
|
||||||
children
|
}
|
||||||
{
|
|
||||||
iftag("medium")
|
|
||||||
{
|
|
||||||
children
|
|
||||||
{
|
|
||||||
iftag("position")
|
|
||||||
{
|
|
||||||
currentDescription.disk = text.toUInt();
|
|
||||||
}
|
|
||||||
eliftag("track-list")
|
|
||||||
{
|
|
||||||
currentDescription.totalTracks = attribute("count").toUInt();
|
|
||||||
children
|
|
||||||
{
|
|
||||||
iftag("track")
|
|
||||||
{
|
|
||||||
children
|
|
||||||
{
|
|
||||||
iftag("number")
|
|
||||||
{
|
|
||||||
currentDescription.track = text.toUInt();
|
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
|
@ -163,44 +138,36 @@ void MusicBrainzResultsModel::parseInitialResults(const QByteArray &data)
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
}
|
} eliftag("tag-list") {
|
||||||
else_skip
|
children {
|
||||||
}
|
iftag("tag") {
|
||||||
}
|
children {
|
||||||
eliftag("tag-list")
|
iftag("name") {
|
||||||
{
|
if (!currentDescription.genre.isEmpty()) {
|
||||||
children
|
currentDescription.genre.append(QLatin1Char(' '));
|
||||||
{
|
}
|
||||||
iftag("tag")
|
currentDescription.genre.append(text);
|
||||||
{
|
}
|
||||||
children
|
else_skip
|
||||||
{
|
|
||||||
iftag("name")
|
|
||||||
{
|
|
||||||
if (!currentDescription.genre.isEmpty()) {
|
|
||||||
currentDescription.genre.append(QLatin1Char(' '));
|
|
||||||
}
|
}
|
||||||
currentDescription.genre.append(text);
|
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
|
m_results << currentDescription;
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
m_results << currentDescription;
|
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else_skip
|
else_skip
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else_skip
|
|
||||||
} // namespace QtGui
|
|
||||||
#include <qtutilities/misc/undefxmlparsermacros.h>
|
#include <qtutilities/misc/undefxmlparsermacros.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// check for parsing errors
|
// check for parsing errors
|
||||||
switch (xmlReader.error()) {
|
switch (xmlReader.error()) {
|
||||||
|
@ -217,7 +184,7 @@ endResetModel();
|
||||||
|
|
||||||
QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
||||||
{
|
{
|
||||||
static const QString defaultMusicBrainzUrl(QStringLiteral("https://musicbrainz.org/ws/2/recording/"));
|
static const auto defaultMusicBrainzUrl(QStringLiteral("https://musicbrainz.org/ws/2/recording/"));
|
||||||
|
|
||||||
// compose parts
|
// compose parts
|
||||||
QStringList parts;
|
QStringList parts;
|
||||||
|
@ -236,7 +203,7 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
// compose URL
|
// compose URL
|
||||||
const QString &musicBrainzUrl = Settings::values().dbQuery.musicBrainzUrl;
|
const auto &musicBrainzUrl = Settings::values().dbQuery.musicBrainzUrl;
|
||||||
QUrl url(musicBrainzUrl.isEmpty() ? defaultMusicBrainzUrl : (musicBrainzUrl + QStringLiteral("/recording/")));
|
QUrl url(musicBrainzUrl.isEmpty() ? defaultMusicBrainzUrl : (musicBrainzUrl + QStringLiteral("/recording/")));
|
||||||
QUrlQuery query;
|
QUrlQuery query;
|
||||||
query.addQueryItem(QStringLiteral("query"), parts.join(QStringLiteral(" AND ")));
|
query.addQueryItem(QStringLiteral("query"), parts.join(QStringLiteral(" AND ")));
|
||||||
|
@ -250,8 +217,8 @@ QueryResultsModel *queryMusicBrainz(SongDescription &&songDescription)
|
||||||
|
|
||||||
QNetworkReply *queryCoverArtArchive(const QString &albumId)
|
QNetworkReply *queryCoverArtArchive(const QString &albumId)
|
||||||
{
|
{
|
||||||
static const QString defaultArchiveUrl(QStringLiteral("https://coverartarchive.org"));
|
static const auto defaultArchiveUrl(QStringLiteral("https://coverartarchive.org"));
|
||||||
const QString &coverArtArchiveUrl = Settings::values().dbQuery.coverArtArchiveUrl;
|
const auto &coverArtArchiveUrl = Settings::values().dbQuery.coverArtArchiveUrl;
|
||||||
return networkAccessManager().get(QNetworkRequest(QUrl(
|
return networkAccessManager().get(QNetworkRequest(QUrl(
|
||||||
(coverArtArchiveUrl.isEmpty() ? defaultArchiveUrl : coverArtArchiveUrl) % QStringLiteral("/release/") % albumId % QStringLiteral("/front"))));
|
(coverArtArchiveUrl.isEmpty() ? defaultArchiveUrl : coverArtArchiveUrl) % QStringLiteral("/release/") % albumId % QStringLiteral("/front"))));
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,33 +101,35 @@ DbQueryWidget::~DbQueryWidget()
|
||||||
|
|
||||||
void DbQueryWidget::insertSearchTermsFromTagEdit(TagEdit *tagEdit)
|
void DbQueryWidget::insertSearchTermsFromTagEdit(TagEdit *tagEdit)
|
||||||
{
|
{
|
||||||
if (tagEdit) {
|
if (!tagEdit) {
|
||||||
// set title, album and artist
|
return;
|
||||||
m_ui->titleLineEdit->setText(tagValueToQString(tagEdit->value(KnownField::Title)));
|
}
|
||||||
m_ui->albumLineEdit->setText(tagValueToQString(tagEdit->value(KnownField::Album)));
|
|
||||||
m_ui->artistLineEdit->setText(tagValueToQString(tagEdit->value(KnownField::Artist)));
|
|
||||||
|
|
||||||
// set track number, or if not available part number
|
// set title, album and artist
|
||||||
bool trackValueOk = false;
|
m_ui->titleLineEdit->setText(tagValueToQString(tagEdit->value(KnownField::Title)));
|
||||||
try {
|
m_ui->albumLineEdit->setText(tagValueToQString(tagEdit->value(KnownField::Album)));
|
||||||
TagValue trackValue = tagEdit->value(KnownField::TrackPosition);
|
m_ui->artistLineEdit->setText(tagValueToQString(tagEdit->value(KnownField::Artist)));
|
||||||
if (!trackValue.isEmpty()) {
|
|
||||||
m_ui->trackSpinBox->setValue(trackValue.toPositionInSet().position());
|
// set track number, or if not available part number
|
||||||
trackValueOk = true;
|
bool trackValueOk = false;
|
||||||
}
|
try {
|
||||||
} catch (const ConversionException &) {
|
TagValue trackValue = tagEdit->value(KnownField::TrackPosition);
|
||||||
|
if (!trackValue.isEmpty()) {
|
||||||
|
m_ui->trackSpinBox->setValue(trackValue.toPositionInSet().position());
|
||||||
|
trackValueOk = true;
|
||||||
}
|
}
|
||||||
if (!trackValueOk) {
|
} catch (const ConversionException &) {
|
||||||
TagValue trackValue = tagEdit->value(KnownField::PartNumber);
|
}
|
||||||
if (!trackValue.isEmpty()) {
|
if (!trackValueOk) {
|
||||||
m_ui->trackSpinBox->setValue(trackValue.toInteger());
|
TagValue trackValue = tagEdit->value(KnownField::PartNumber);
|
||||||
trackValueOk = true;
|
if (!trackValue.isEmpty()) {
|
||||||
}
|
m_ui->trackSpinBox->setValue(trackValue.toInteger());
|
||||||
}
|
trackValueOk = true;
|
||||||
if (!trackValueOk) {
|
|
||||||
m_ui->trackSpinBox->clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!trackValueOk) {
|
||||||
|
m_ui->trackSpinBox->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SongDescription DbQueryWidget::currentSongDescription() const
|
SongDescription DbQueryWidget::currentSongDescription() const
|
||||||
|
@ -186,50 +188,52 @@ void DbQueryWidget::searchLyricsWikia()
|
||||||
|
|
||||||
void DbQueryWidget::abortSearch()
|
void DbQueryWidget::abortSearch()
|
||||||
{
|
{
|
||||||
if (m_model) {
|
if (!m_model) {
|
||||||
if (m_model->isFetchingCover()) {
|
return;
|
||||||
// call abort to abort fetching cover
|
}
|
||||||
m_model->abort();
|
if (m_model->isFetchingCover()) {
|
||||||
} else if (!m_model->areResultsAvailable()) {
|
// call abort to abort fetching cover
|
||||||
// delete model to abort search
|
m_model->abort();
|
||||||
m_ui->resultsTreeView->setModel(nullptr);
|
} else if (!m_model->areResultsAvailable()) {
|
||||||
delete m_model;
|
// delete model to abort search
|
||||||
m_model = nullptr;
|
m_ui->resultsTreeView->setModel(nullptr);
|
||||||
|
delete m_model;
|
||||||
|
m_model = nullptr;
|
||||||
|
|
||||||
// update status
|
// update status
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::Information);
|
m_ui->notificationLabel->setNotificationType(NotificationType::Information);
|
||||||
m_ui->notificationLabel->setText(tr("Aborted"));
|
m_ui->notificationLabel->setText(tr("Aborted"));
|
||||||
setStatus(true);
|
setStatus(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbQueryWidget::showResults()
|
void DbQueryWidget::showResults()
|
||||||
{
|
{
|
||||||
if (m_model) {
|
if (!m_model) {
|
||||||
if (m_model->errorList().isEmpty()) {
|
return;
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::TaskComplete);
|
|
||||||
if (m_model->results().isEmpty()) {
|
|
||||||
m_ui->notificationLabel->setText(tr("No results available"));
|
|
||||||
} else {
|
|
||||||
m_ui->notificationLabel->setText(tr("%1 result(s) available", 0, m_model->results().size()).arg(m_model->results().size()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::Critical);
|
|
||||||
m_ui->notificationLabel->clearText();
|
|
||||||
for (const QString &error : m_model->errorList()) {
|
|
||||||
m_ui->notificationLabel->appendLine(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_model->results().isEmpty()) {
|
|
||||||
m_ui->applyPushButton->setEnabled(false);
|
|
||||||
} else {
|
|
||||||
m_ui->resultsTreeView->selectionModel()->setCurrentIndex(
|
|
||||||
m_model->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
|
||||||
m_ui->applyPushButton->setEnabled(m_tagEditorWidget->activeTagEdit());
|
|
||||||
}
|
|
||||||
setStatus(true);
|
|
||||||
}
|
}
|
||||||
|
if (m_model->errorList().isEmpty()) {
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::TaskComplete);
|
||||||
|
if (m_model->results().isEmpty()) {
|
||||||
|
m_ui->notificationLabel->setText(tr("No results available"));
|
||||||
|
} else {
|
||||||
|
m_ui->notificationLabel->setText(tr("%1 result(s) available", nullptr, m_model->results().size()).arg(m_model->results().size()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Critical);
|
||||||
|
m_ui->notificationLabel->clearText();
|
||||||
|
for (const QString &error : m_model->errorList()) {
|
||||||
|
m_ui->notificationLabel->appendLine(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_model->results().isEmpty()) {
|
||||||
|
m_ui->applyPushButton->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
m_ui->resultsTreeView->selectionModel()->setCurrentIndex(
|
||||||
|
m_model->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||||||
|
m_ui->applyPushButton->setEnabled(m_tagEditorWidget->activeTagEdit());
|
||||||
|
}
|
||||||
|
setStatus(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbQueryWidget::setStatus(bool aborted)
|
void DbQueryWidget::setStatus(bool aborted)
|
||||||
|
@ -254,9 +258,9 @@ void DbQueryWidget::fileStatusChanged(bool, bool hasTags)
|
||||||
void DbQueryWidget::applySelectedResults()
|
void DbQueryWidget::applySelectedResults()
|
||||||
{
|
{
|
||||||
// check whether model, tag edit and current selection exist
|
// check whether model, tag edit and current selection exist
|
||||||
if (TagEdit *tagEdit = m_tagEditorWidget->activeTagEdit()) {
|
if (auto *const tagEdit = m_tagEditorWidget->activeTagEdit()) {
|
||||||
if (const QItemSelectionModel *selectionModel = m_ui->resultsTreeView->selectionModel()) {
|
if (const auto *const selectionModel = m_ui->resultsTreeView->selectionModel()) {
|
||||||
const QModelIndexList selection = selectionModel->selection().indexes();
|
const auto selection = selectionModel->selection().indexes();
|
||||||
if (!selection.isEmpty()) {
|
if (!selection.isEmpty()) {
|
||||||
applyResults(tagEdit, selection.front());
|
applyResults(tagEdit, selection.front());
|
||||||
}
|
}
|
||||||
|
@ -290,9 +294,9 @@ void DbQueryWidget::applyMatchingResults(TagEdit *tagEdit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine already present title, album and artist
|
// determine already present title, album and artist
|
||||||
const TagValue givenTitle = tagEdit->value(KnownField::Title);
|
const auto givenTitle = tagEdit->value(KnownField::Title);
|
||||||
const TagValue givenAlbum = tagEdit->value(KnownField::Album);
|
const auto givenAlbum = tagEdit->value(KnownField::Album);
|
||||||
const TagValue givenArtist = tagEdit->value(KnownField::Artist);
|
const auto givenArtist = tagEdit->value(KnownField::Artist);
|
||||||
|
|
||||||
// also determine already present track number (which is a little bit more complex -> TODO: improve backend API)
|
// also determine already present track number (which is a little bit more complex -> TODO: improve backend API)
|
||||||
int givenTrack;
|
int givenTrack;
|
||||||
|
@ -352,71 +356,79 @@ void DbQueryWidget::autoInsertMatchingResults()
|
||||||
*/
|
*/
|
||||||
void DbQueryWidget::applyResults(TagEdit *tagEdit, const QModelIndex &resultIndex)
|
void DbQueryWidget::applyResults(TagEdit *tagEdit, const QModelIndex &resultIndex)
|
||||||
{
|
{
|
||||||
if (m_model) {
|
if (!m_model) {
|
||||||
// determine previous value handling
|
return;
|
||||||
PreviousValueHandling previousValueHandling
|
}
|
||||||
= m_ui->overrideCheckBox->isChecked() ? PreviousValueHandling::Update : PreviousValueHandling::Keep;
|
|
||||||
|
|
||||||
// loop through all fields
|
// determine previous value handling
|
||||||
for (const ChecklistItem &item : values().dbQuery.fields.items()) {
|
const auto previousValueHandling = m_ui->overrideCheckBox->isChecked() ? PreviousValueHandling::Update : PreviousValueHandling::Keep;
|
||||||
if (item.isChecked()) {
|
|
||||||
// field should be used
|
|
||||||
const auto field = static_cast<KnownField>(item.id().toInt());
|
|
||||||
int row = resultIndex.row();
|
|
||||||
TagValue value = m_model->fieldValue(row, field);
|
|
||||||
|
|
||||||
if (value.isEmpty()) {
|
// loop through all fields
|
||||||
// cover and lyrics might be fetched belated
|
for (const ChecklistItem &item : values().dbQuery.fields.items()) {
|
||||||
switch (field) {
|
if (!item.isChecked()) {
|
||||||
case KnownField::Cover:
|
continue;
|
||||||
if (m_model->fetchCover(resultIndex)) {
|
}
|
||||||
// cover is available now
|
|
||||||
tagEdit->setValue(KnownField::Cover, m_model->fieldValue(row, KnownField::Cover), previousValueHandling);
|
|
||||||
} else {
|
|
||||||
// cover is fetched asynchronously
|
|
||||||
// -> show status
|
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
|
||||||
m_ui->notificationLabel->appendLine(tr("Retrieving cover art to be applied ..."));
|
|
||||||
setStatus(false);
|
|
||||||
// -> apply cover when available
|
|
||||||
connect(m_model, &QueryResultsModel::coverAvailable, [this, row, previousValueHandling](const QModelIndex &index) {
|
|
||||||
if (row == index.row()) {
|
|
||||||
if (TagEdit *tagEdit = m_tagEditorWidget->activeTagEdit()) {
|
|
||||||
tagEdit->setValue(KnownField::Cover, m_model->fieldValue(row, KnownField::Cover), previousValueHandling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KnownField::Lyrics:
|
// determine the field to be used and its value
|
||||||
if (m_model->fetchLyrics(resultIndex)) {
|
const auto field = static_cast<KnownField>(item.id().toInt());
|
||||||
// lyrics are available now
|
const auto row = resultIndex.row();
|
||||||
tagEdit->setValue(KnownField::Lyrics, m_model->fieldValue(row, KnownField::Lyrics), previousValueHandling);
|
const auto value = m_model->fieldValue(row, field);
|
||||||
} else {
|
|
||||||
// lyrics are fetched asynchronously
|
|
||||||
// -> show status
|
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
|
||||||
m_ui->notificationLabel->appendLine(tr("Retrieving lyrics to be applied ..."));
|
|
||||||
setStatus(false);
|
|
||||||
// -> apply cover when available
|
|
||||||
connect(m_model, &QueryResultsModel::lyricsAvailable, [this, row, previousValueHandling](const QModelIndex &index) {
|
|
||||||
if (row == index.row()) {
|
|
||||||
if (TagEdit *tagEdit = m_tagEditorWidget->activeTagEdit()) {
|
|
||||||
tagEdit->setValue(KnownField::Lyrics, m_model->fieldValue(row, KnownField::Lyrics), previousValueHandling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:;
|
// set the value if available
|
||||||
}
|
if (!value.isEmpty()) {
|
||||||
} else {
|
tagEdit->setValue(field, value, previousValueHandling);
|
||||||
// any other fields are just set
|
continue;
|
||||||
tagEdit->setValue(field, value, previousValueHandling);
|
}
|
||||||
}
|
|
||||||
|
// cover and lyrics might be fetched asynchronously
|
||||||
|
switch (field) {
|
||||||
|
case KnownField::Cover:
|
||||||
|
if (m_model->fetchCover(resultIndex)) {
|
||||||
|
// cover is available now
|
||||||
|
tagEdit->setValue(KnownField::Cover, m_model->fieldValue(row, KnownField::Cover), previousValueHandling);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cover is fetched asynchronously
|
||||||
|
// -> show status
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
||||||
|
m_ui->notificationLabel->appendLine(tr("Retrieving cover art to be applied ..."));
|
||||||
|
setStatus(false);
|
||||||
|
// -> apply cover when available
|
||||||
|
connect(m_model, &QueryResultsModel::coverAvailable, [this, row, previousValueHandling](const QModelIndex &index) {
|
||||||
|
if (row != index.row()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (auto *const tagEdit = m_tagEditorWidget->activeTagEdit()) {
|
||||||
|
tagEdit->setValue(KnownField::Cover, m_model->fieldValue(row, KnownField::Cover), previousValueHandling);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KnownField::Lyrics:
|
||||||
|
if (m_model->fetchLyrics(resultIndex)) {
|
||||||
|
// lyrics are available now
|
||||||
|
tagEdit->setValue(KnownField::Lyrics, m_model->fieldValue(row, KnownField::Lyrics), previousValueHandling);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lyrics are fetched asynchronously
|
||||||
|
// -> show status
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
||||||
|
m_ui->notificationLabel->appendLine(tr("Retrieving lyrics to be applied ..."));
|
||||||
|
setStatus(false);
|
||||||
|
// -> apply cover when available
|
||||||
|
connect(m_model, &QueryResultsModel::lyricsAvailable, [this, row, previousValueHandling](const QModelIndex &index) {
|
||||||
|
if (row != index.row()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (auto *const tagEdit = m_tagEditorWidget->activeTagEdit()) {
|
||||||
|
tagEdit->setValue(KnownField::Lyrics, m_model->fieldValue(row, KnownField::Lyrics), previousValueHandling);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,93 +440,98 @@ void DbQueryWidget::insertSearchTermsFromActiveTagEdit()
|
||||||
|
|
||||||
void DbQueryWidget::showResultsContextMenu()
|
void DbQueryWidget::showResultsContextMenu()
|
||||||
{
|
{
|
||||||
if (const QItemSelectionModel *selectionModel = m_ui->resultsTreeView->selectionModel()) {
|
const auto *const selectionModel = m_ui->resultsTreeView->selectionModel();
|
||||||
const QModelIndexList selection = selectionModel->selection().indexes();
|
if (!selectionModel) {
|
||||||
if (!selection.isEmpty()) {
|
return;
|
||||||
QMenu contextMenu;
|
|
||||||
if (m_ui->applyPushButton->isEnabled()) {
|
|
||||||
contextMenu.addAction(m_ui->applyPushButton->icon(), tr("Use selected row"), this,
|
|
||||||
static_cast<void (DbQueryWidget::*)(void)>(&DbQueryWidget::applySelectedResults));
|
|
||||||
}
|
|
||||||
if (m_model && m_model->areResultsAvailable()) {
|
|
||||||
if (!contextMenu.isEmpty()) {
|
|
||||||
contextMenu.addSeparator();
|
|
||||||
}
|
|
||||||
contextMenu.addAction(
|
|
||||||
QIcon::fromTheme(QStringLiteral("view-preview")), tr("Show cover"), this, &DbQueryWidget::fetchAndShowCoverForSelection);
|
|
||||||
contextMenu.addAction(
|
|
||||||
QIcon::fromTheme(QStringLiteral("view-media-lyrics")), tr("Show lyrics"), this, &DbQueryWidget::fetchAndShowLyricsForSelection);
|
|
||||||
contextMenu.addAction(
|
|
||||||
QIcon::fromTheme(QStringLiteral("internet-web-browser")), tr("Show in browser"), this, &DbQueryWidget::openSelectionInBrowser);
|
|
||||||
}
|
|
||||||
contextMenu.exec(QCursor::pos());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const auto selection = selectionModel->selection().indexes();
|
||||||
|
if (selection.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMenu contextMenu;
|
||||||
|
if (m_ui->applyPushButton->isEnabled()) {
|
||||||
|
contextMenu.addAction(m_ui->applyPushButton->icon(), tr("Use selected row"), this,
|
||||||
|
static_cast<void (DbQueryWidget::*)(void)>(&DbQueryWidget::applySelectedResults));
|
||||||
|
}
|
||||||
|
if (m_model && m_model->areResultsAvailable()) {
|
||||||
|
if (!contextMenu.isEmpty()) {
|
||||||
|
contextMenu.addSeparator();
|
||||||
|
}
|
||||||
|
contextMenu.addAction(
|
||||||
|
QIcon::fromTheme(QStringLiteral("view-preview")), tr("Show cover"), this, &DbQueryWidget::fetchAndShowCoverForSelection);
|
||||||
|
contextMenu.addAction(
|
||||||
|
QIcon::fromTheme(QStringLiteral("view-media-lyrics")), tr("Show lyrics"), this, &DbQueryWidget::fetchAndShowLyricsForSelection);
|
||||||
|
contextMenu.addAction(
|
||||||
|
QIcon::fromTheme(QStringLiteral("internet-web-browser")), tr("Show in browser"), this, &DbQueryWidget::openSelectionInBrowser);
|
||||||
|
}
|
||||||
|
contextMenu.exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbQueryWidget::fetchAndShowCoverForSelection()
|
void DbQueryWidget::fetchAndShowCoverForSelection()
|
||||||
{
|
{
|
||||||
const QModelIndex selectedIndex = this->selectedIndex();
|
const auto selectedIndex = this->selectedIndex();
|
||||||
if (!selectedIndex.isValid()) {
|
if (!selectedIndex.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const QByteArray *cover = m_model->cover(selectedIndex)) {
|
if (const QByteArray *const cover = m_model->cover(selectedIndex)) {
|
||||||
showCover(*cover);
|
showCover(*cover);
|
||||||
} else {
|
return;
|
||||||
if (m_model->fetchCover(selectedIndex)) {
|
}
|
||||||
if (const QByteArray *cover = m_model->cover(selectedIndex)) {
|
|
||||||
showCover(*cover);
|
if (m_model->fetchCover(selectedIndex)) {
|
||||||
} else {
|
if (const QByteArray *const cover = m_model->cover(selectedIndex)) {
|
||||||
// cover couldn't be fetched, error tracked via resultsAvailable() signal so nothing to do
|
showCover(*cover);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// cover is fetched asynchronously
|
// cover couldn't be fetched, error tracked via resultsAvailable() signal so nothing to do
|
||||||
// -> memorize index to be shown
|
|
||||||
m_coverIndex = selectedIndex.row();
|
|
||||||
// -> show status
|
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
|
||||||
m_ui->notificationLabel->setText(tr("Retrieving cover art ..."));
|
|
||||||
setStatus(false);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// cover is fetched asynchronously
|
||||||
|
// -> memorize index to be shown
|
||||||
|
m_coverIndex = selectedIndex.row();
|
||||||
|
// -> show status
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
||||||
|
m_ui->notificationLabel->setText(tr("Retrieving cover art ..."));
|
||||||
|
setStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbQueryWidget::fetchAndShowLyricsForSelection()
|
void DbQueryWidget::fetchAndShowLyricsForSelection()
|
||||||
{
|
{
|
||||||
const QModelIndex selectedIndex = this->selectedIndex();
|
const auto selectedIndex = this->selectedIndex();
|
||||||
if (!selectedIndex.isValid()) {
|
if (!selectedIndex.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const QString *lyrics = m_model->lyrics(selectedIndex)) {
|
if (const QString *const lyrics = m_model->lyrics(selectedIndex)) {
|
||||||
showLyrics(*lyrics);
|
showLyrics(*lyrics);
|
||||||
} else {
|
return;
|
||||||
if (m_model->fetchLyrics(selectedIndex)) {
|
}
|
||||||
if (const QByteArray *cover = m_model->cover(selectedIndex)) {
|
|
||||||
showLyrics(*cover);
|
if (m_model->fetchLyrics(selectedIndex)) {
|
||||||
} else {
|
if (const QByteArray *cover = m_model->cover(selectedIndex)) {
|
||||||
// lyrics couldn't be fetched, error tracked via resultsAvailable() signal so nothing to do
|
showLyrics(*cover);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// lyrics are fetched asynchronously
|
// lyrics couldn't be fetched, error tracked via resultsAvailable() signal so nothing to do
|
||||||
// -> memorize index to be shown
|
|
||||||
m_lyricsIndex = selectedIndex.row();
|
|
||||||
// -> show status
|
|
||||||
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
|
||||||
m_ui->notificationLabel->setText(tr("Retrieving lyrics ..."));
|
|
||||||
setStatus(false);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// lyrics are fetched asynchronously
|
||||||
|
// -> memorize index to be shown
|
||||||
|
m_lyricsIndex = selectedIndex.row();
|
||||||
|
// -> show status
|
||||||
|
m_ui->notificationLabel->setNotificationType(NotificationType::Progress);
|
||||||
|
m_ui->notificationLabel->setText(tr("Retrieving lyrics ..."));
|
||||||
|
setStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbQueryWidget::openSelectionInBrowser()
|
void DbQueryWidget::openSelectionInBrowser()
|
||||||
{
|
{
|
||||||
const QModelIndex selectedIndex = this->selectedIndex();
|
const auto selectedIndex = this->selectedIndex();
|
||||||
if (!selectedIndex.isValid()) {
|
if (!selectedIndex.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const QUrl url(m_model->webUrl(selectedIndex));
|
const auto url = m_model->webUrl(selectedIndex);
|
||||||
if (url.isEmpty()) {
|
if (url.isEmpty()) {
|
||||||
m_ui->notificationLabel->appendLine(tr("No web URL available."));
|
m_ui->notificationLabel->appendLine(tr("No web URL available."));
|
||||||
return;
|
return;
|
||||||
|
@ -611,11 +628,11 @@ QModelIndex DbQueryWidget::selectedIndex() const
|
||||||
if (!m_model) {
|
if (!m_model) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
const QItemSelectionModel *selectionModel = m_ui->resultsTreeView->selectionModel();
|
const auto *const selectionModel = m_ui->resultsTreeView->selectionModel();
|
||||||
if (!selectionModel) {
|
if (!selectionModel) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
const QModelIndexList selection = selectionModel->selectedRows();
|
const auto selection = selectionModel->selectedRows();
|
||||||
if (selection.size() != 1) {
|
if (selection.size() != 1) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue