Improve album and title disc, remastered, etc matching
Don't partial remove things like "(Mono / Remastered)". Fixes #1387
This commit is contained in:
parent
b365131363
commit
7f4c61b15a
|
@ -1681,7 +1681,7 @@ QString CollectionModel::PrettyYearAlbum(const int year, const QString &album) {
|
||||||
|
|
||||||
QString CollectionModel::PrettyAlbumDisc(const QString &album, const int disc) {
|
QString CollectionModel::PrettyAlbumDisc(const QString &album, const int disc) {
|
||||||
|
|
||||||
if (disc <= 0 || album.contains(Song::kAlbumRemoveDisc)) return TextOrUnknown(album);
|
if (disc <= 0 || Song::AlbumContainsDisc(album)) return TextOrUnknown(album);
|
||||||
else return TextOrUnknown(album) + " - (Disc " + QString::number(disc) + ")";
|
else return TextOrUnknown(album) + " - (Disc " + QString::number(disc) + ")";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1693,7 +1693,7 @@ QString CollectionModel::PrettyYearAlbumDisc(const int year, const QString &albu
|
||||||
if (year <= 0) str = TextOrUnknown(album);
|
if (year <= 0) str = TextOrUnknown(album);
|
||||||
else str = QString::number(year) + " - " + TextOrUnknown(album);
|
else str = QString::number(year) + " - " + TextOrUnknown(album);
|
||||||
|
|
||||||
if (!album.contains(Song::kAlbumRemoveDisc) && disc > 0) str += " - (Disc " + QString::number(disc) + ")";
|
if (!Song::AlbumContainsDisc(album) && disc > 0) str += " - (Disc " + QString::number(disc) + ")";
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
|
|
||||||
|
|
|
@ -167,9 +167,20 @@ const QString Song::kFtsColumnSpec = Song::kFtsColumns.join(", ");
|
||||||
const QString Song::kFtsBindSpec = Utilities::Prepend(":", Song::kFtsColumns).join(", ");
|
const QString Song::kFtsBindSpec = Utilities::Prepend(":", Song::kFtsColumns).join(", ");
|
||||||
const QString Song::kFtsUpdateSpec = Utilities::Updateify(Song::kFtsColumns).join(", ");
|
const QString Song::kFtsUpdateSpec = Utilities::Updateify(Song::kFtsColumns).join(", ");
|
||||||
|
|
||||||
const QRegularExpression Song::kAlbumRemoveDisc(" ?-? ((\\(|\\[)?)(Disc|CD) ?([0-9]{1,2})((\\)|\\])?)$", QRegularExpression::CaseInsensitiveOption);
|
const Song::RegularExpressionList Song::kAlbumDisc = Song::RegularExpressionList()
|
||||||
const QRegularExpression Song::kAlbumRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|([0-9]{1,4}) *Remaster|Explicit) ?((\\)|\\])?)$", QRegularExpression::CaseInsensitiveOption);
|
<< QRegularExpression("\\s+-*\\s*(Disc|CD)\\s*([0-9]{1,2})$", QRegularExpression::CaseInsensitiveOption)
|
||||||
const QRegularExpression Song::kTitleRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|Remastered Version|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$", QRegularExpression::CaseInsensitiveOption);
|
<< QRegularExpression("\\s+-*\\s*\\(\\s*(Disc|CD)\\s*([0-9]{1,2})\\)$", QRegularExpression::CaseInsensitiveOption)
|
||||||
|
<< QRegularExpression("\\s+-*\\s*\\[\\s*(Disc|CD)\\s*([0-9]{1,2})\\]$", QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
|
const Song::RegularExpressionList Song::kAlbumMisc = Song::RegularExpressionList()
|
||||||
|
<< QRegularExpression("\\s+-*\\s*(Remastered|([0-9]{1,4})\\s*Remaster|Explicit)\\s*$", QRegularExpression::CaseInsensitiveOption)
|
||||||
|
<< QRegularExpression("\\s+-*\\s*\\(\\s*(Remastered|([0-9]{1,4})\\s*Remaster|Explicit)\\s*\\)\\s*$", QRegularExpression::CaseInsensitiveOption)
|
||||||
|
<< QRegularExpression("\\s+-*\\s*\\[\\s*(Remastered|([0-9]{1,4})\\s*Remaster|Explicit)\\s*\\]\\s*$", QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
|
const Song::RegularExpressionList Song::kTitleMisc = Song::RegularExpressionList()
|
||||||
|
<< QRegularExpression("\\s+-*\\s*(Remastered|Remastered Version|([0-9]{1,4})\\s*Remaster)\\s*$", QRegularExpression::CaseInsensitiveOption)
|
||||||
|
<< QRegularExpression("\\s+-*\\s*\\(\\s*(Remastered|Remastered Version|([0-9]{1,4})\\s*Remaster)\\s*\\)\\s*$", QRegularExpression::CaseInsensitiveOption)
|
||||||
|
<< QRegularExpression("\\s+-*\\s*\\[\\s*(Remastered|Remastered Version|([0-9]{1,4})\\s*Remaster)\\s*\\]\\s*$", QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
const QStringList Song::kArticles = QStringList() << "the " << "a " << "an ";
|
const QStringList Song::kArticles = QStringList() << "the " << "a " << "an ";
|
||||||
|
|
||||||
|
@ -1843,3 +1854,53 @@ size_t HashSimilar(const Song &song) {
|
||||||
// Should compare the same fields as function IsSimilar
|
// Should compare the same fields as function IsSimilar
|
||||||
return qHash(song.title().toLower()) ^ qHash(song.artist().toLower()) ^ qHash(song.album().toLower());
|
return qHash(song.title().toLower()) ^ qHash(song.artist().toLower()) ^ qHash(song.album().toLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Song::ContainsRegexList(const QString &str, const RegularExpressionList ®ex_list) {
|
||||||
|
|
||||||
|
for (const QRegularExpression ®ex : regex_list) {
|
||||||
|
if (str.contains(regex)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Song::StripRegexList(QString str, const RegularExpressionList ®ex_list) {
|
||||||
|
|
||||||
|
for (const QRegularExpression ®ex : regex_list) {
|
||||||
|
str = str.remove(regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Song::AlbumContainsDisc(const QString &album) {
|
||||||
|
|
||||||
|
return ContainsRegexList(album, kAlbumDisc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Song::AlbumRemoveDisc(const QString &album) {
|
||||||
|
|
||||||
|
return StripRegexList(album, kAlbumDisc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Song::AlbumRemoveMisc(const QString &album) {
|
||||||
|
|
||||||
|
return StripRegexList(album, kAlbumMisc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Song::AlbumRemoveDiscMisc(const QString &album) {
|
||||||
|
|
||||||
|
return StripRegexList(album, RegularExpressionList() << kAlbumDisc << kAlbumMisc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Song::TitleRemoveMisc(const QString &title) {
|
||||||
|
|
||||||
|
return StripRegexList(title, kTitleMisc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -123,9 +123,10 @@ class Song {
|
||||||
static const QString kFtsBindSpec;
|
static const QString kFtsBindSpec;
|
||||||
static const QString kFtsUpdateSpec;
|
static const QString kFtsUpdateSpec;
|
||||||
|
|
||||||
static const QRegularExpression kAlbumRemoveDisc;
|
using RegularExpressionList = QList<QRegularExpression>;
|
||||||
static const QRegularExpression kAlbumRemoveMisc;
|
static const RegularExpressionList kAlbumDisc;
|
||||||
static const QRegularExpression kTitleRemoveMisc;
|
static const RegularExpressionList kAlbumMisc;
|
||||||
|
static const RegularExpressionList kTitleMisc;
|
||||||
|
|
||||||
static const QStringList kArticles;
|
static const QStringList kArticles;
|
||||||
|
|
||||||
|
@ -443,6 +444,14 @@ class Song {
|
||||||
// It is more efficient to use IsOnSameAlbum, but this function can be used when you need to hash the key to do fast lookups.
|
// It is more efficient to use IsOnSameAlbum, but this function can be used when you need to hash the key to do fast lookups.
|
||||||
QString AlbumKey() const;
|
QString AlbumKey() const;
|
||||||
|
|
||||||
|
static bool ContainsRegexList(const QString &str, const RegularExpressionList ®ex_list);
|
||||||
|
static QString StripRegexList(QString str, const RegularExpressionList ®ex_list);
|
||||||
|
static bool AlbumContainsDisc(const QString &album);
|
||||||
|
static QString AlbumRemoveDisc(const QString &album);
|
||||||
|
static QString AlbumRemoveMisc(const QString &album);
|
||||||
|
static QString AlbumRemoveDiscMisc(const QString &album);
|
||||||
|
static QString TitleRemoveMisc(const QString &title);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Private;
|
struct Private;
|
||||||
|
|
||||||
|
|
|
@ -333,7 +333,7 @@ AlbumCoverImageResult AlbumCoverChoiceController::SearchForImage(Song *song) {
|
||||||
if (!song->url().isValid() || !song->url().isLocalFile() || song->effective_albumartist().isEmpty() || song->album().isEmpty()) return AlbumCoverImageResult();
|
if (!song->url().isValid() || !song->url().isLocalFile() || song->effective_albumartist().isEmpty() || song->album().isEmpty()) return AlbumCoverImageResult();
|
||||||
|
|
||||||
QString album = song->effective_album();
|
QString album = song->effective_album();
|
||||||
album = album.remove(Song::kAlbumRemoveDisc).remove(Song::kAlbumRemoveMisc);
|
album = Song::AlbumRemoveDiscMisc(album);
|
||||||
|
|
||||||
// Get something sensible to stick in the search box
|
// Get something sensible to stick in the search box
|
||||||
return cover_searcher_->Exec(song->effective_albumartist(), album);
|
return cover_searcher_->Exec(song->effective_albumartist(), album);
|
||||||
|
|
|
@ -66,9 +66,7 @@ quint64 AlbumCoverFetcher::FetchAlbumCover(const QString &artist, const QString
|
||||||
CoverSearchRequest request;
|
CoverSearchRequest request;
|
||||||
request.id = ++next_id_;
|
request.id = ++next_id_;
|
||||||
request.artist = artist;
|
request.artist = artist;
|
||||||
request.album = album;
|
request.album = Song::AlbumRemoveDiscMisc(album);
|
||||||
request.album = request.album.remove(Song::kAlbumRemoveDisc);
|
|
||||||
request.album = request.album.remove(Song::kAlbumRemoveMisc);
|
|
||||||
request.title = title;
|
request.title = title;
|
||||||
request.search = false;
|
request.search = false;
|
||||||
request.batch = batch;
|
request.batch = batch;
|
||||||
|
@ -83,9 +81,7 @@ quint64 AlbumCoverFetcher::SearchForCovers(const QString &artist, const QString
|
||||||
CoverSearchRequest request;
|
CoverSearchRequest request;
|
||||||
request.id = ++next_id_;
|
request.id = ++next_id_;
|
||||||
request.artist = artist;
|
request.artist = artist;
|
||||||
request.album = album;
|
request.album = Song::AlbumRemoveDiscMisc(album);
|
||||||
request.album = request.album.remove(Song::kAlbumRemoveDisc);
|
|
||||||
request.album = request.album.remove(Song::kAlbumRemoveMisc);
|
|
||||||
request.title = title;
|
request.title = title;
|
||||||
request.search = true;
|
request.search = true;
|
||||||
request.batch = false;
|
request.batch = false;
|
||||||
|
|
|
@ -265,12 +265,9 @@ void DeezerCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
|
||||||
}
|
}
|
||||||
QString album = obj_album["title"].toString();
|
QString album = obj_album["title"].toString();
|
||||||
|
|
||||||
album = album.remove(Song::kAlbumRemoveDisc);
|
|
||||||
album = album.remove(Song::kAlbumRemoveMisc);
|
|
||||||
|
|
||||||
CoverProviderSearchResult cover_result;
|
CoverProviderSearchResult cover_result;
|
||||||
cover_result.artist = artist;
|
cover_result.artist = artist;
|
||||||
cover_result.album = album;
|
cover_result.album = Song::AlbumRemoveDiscMisc(album);
|
||||||
|
|
||||||
bool have_cover = false;
|
bool have_cover = false;
|
||||||
QList<QPair<QString, QSize>> cover_sizes = QList<QPair<QString, QSize>>() << qMakePair(QString("cover_xl"), QSize(1000, 1000))
|
QList<QPair<QString, QSize>> cover_sizes = QList<QPair<QString, QSize>>() << qMakePair(QString("cover_xl"), QSize(1000, 1000))
|
||||||
|
|
|
@ -262,12 +262,9 @@ void QobuzCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id) {
|
||||||
}
|
}
|
||||||
QUrl cover_url(obj_image["large"].toString());
|
QUrl cover_url(obj_image["large"].toString());
|
||||||
|
|
||||||
album = album.remove(Song::kAlbumRemoveDisc);
|
|
||||||
album = album.remove(Song::kAlbumRemoveMisc);
|
|
||||||
|
|
||||||
CoverProviderSearchResult cover_result;
|
CoverProviderSearchResult cover_result;
|
||||||
cover_result.artist = artist;
|
cover_result.artist = artist;
|
||||||
cover_result.album = album;
|
cover_result.album = Song::AlbumRemoveDiscMisc(album);
|
||||||
cover_result.image_url = cover_url;
|
cover_result.image_url = cover_url;
|
||||||
cover_result.image_size = QSize(600, 600);
|
cover_result.image_size = QSize(600, 600);
|
||||||
results << cover_result;
|
results << cover_result;
|
||||||
|
|
|
@ -241,15 +241,11 @@ void TidalCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QString album = obj_album["title"].toString();
|
QString album = obj_album["title"].toString();
|
||||||
QString cover = obj_album["cover"].toString();
|
QString cover = obj_album["cover"].toString().replace("-", "/");
|
||||||
|
|
||||||
album = album.remove(Song::kAlbumRemoveDisc);
|
|
||||||
album = album.remove(Song::kAlbumRemoveMisc);
|
|
||||||
cover = cover.replace("-", "/");
|
|
||||||
|
|
||||||
CoverProviderSearchResult cover_result;
|
CoverProviderSearchResult cover_result;
|
||||||
cover_result.artist = artist;
|
cover_result.artist = artist;
|
||||||
cover_result.album = album;
|
cover_result.album = Song::AlbumRemoveDiscMisc(album);
|
||||||
cover_result.number = ++i;
|
cover_result.number = ++i;
|
||||||
|
|
||||||
QList<QPair<QString, QSize>> cover_sizes = QList<QPair<QString, QSize>>() << qMakePair(QString("1280x1280"), QSize(1280, 1280))
|
QList<QPair<QString, QSize>> cover_sizes = QList<QPair<QString, QSize>>() << qMakePair(QString("1280x1280"), QSize(1280, 1280))
|
||||||
|
|
|
@ -53,10 +53,8 @@ quint64 LyricsFetcher::Search(const QString &effective_albumartist, const QStrin
|
||||||
LyricsSearchRequest search_request;
|
LyricsSearchRequest search_request;
|
||||||
search_request.albumartist = effective_albumartist;
|
search_request.albumartist = effective_albumartist;
|
||||||
search_request.artist = artist;
|
search_request.artist = artist;
|
||||||
search_request.album = album;
|
search_request.album = Song::AlbumRemoveDiscMisc(album);
|
||||||
search_request.album.remove(Song::kAlbumRemoveMisc);
|
search_request.title = Song::TitleRemoveMisc(title);
|
||||||
search_request.title = title;
|
|
||||||
search_request.title.remove(Song::kTitleRemoveMisc);
|
|
||||||
|
|
||||||
Request request;
|
Request request;
|
||||||
request.id = ++next_id_;
|
request.id = ++next_id_;
|
||||||
|
|
|
@ -1189,7 +1189,7 @@ void QobuzRequest::ParseSong(Song &song, const QJsonObject &json_obj, const Arti
|
||||||
url.setScheme(url_handler_->scheme());
|
url.setScheme(url_handler_->scheme());
|
||||||
url.setPath(song_id);
|
url.setPath(song_id);
|
||||||
|
|
||||||
title.remove(Song::kTitleRemoveMisc);
|
title = Song::TitleRemoveMisc(title);
|
||||||
|
|
||||||
//qLog(Debug) << "id" << song_id << "track" << track << "title" << title << "album" << album << "album artist" << album_artist << cover_url << streamable << url;
|
//qLog(Debug) << "id" << song_id << "track" << track << "title" << title << "album" << album << "album artist" << album_artist << cover_url << streamable << url;
|
||||||
|
|
||||||
|
|
|
@ -50,14 +50,14 @@ bool ScrobblerService::ExtractJsonObj(const QByteArray &data, QJsonObject &json_
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ScrobblerService::StripAlbum(QString album) const {
|
QString ScrobblerService::StripAlbum(const QString &album) const {
|
||||||
|
|
||||||
return album.remove(Song::kAlbumRemoveDisc).remove(Song::kAlbumRemoveMisc);
|
return Song::AlbumRemoveDisc(album);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ScrobblerService::StripTitle(QString title) const {
|
QString ScrobblerService::StripTitle(const QString &title) const {
|
||||||
|
|
||||||
return title.remove(Song::kTitleRemoveMisc);
|
return Song::TitleRemoveMisc(title);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,8 @@ class ScrobblerService : public QObject {
|
||||||
|
|
||||||
bool ExtractJsonObj(const QByteArray &data, QJsonObject &json_obj, QString &error_description);
|
bool ExtractJsonObj(const QByteArray &data, QJsonObject &json_obj, QString &error_description);
|
||||||
|
|
||||||
QString StripAlbum(QString album) const;
|
QString StripAlbum(const QString &album) const;
|
||||||
QString StripTitle(QString title) const;
|
QString StripTitle(const QString &title) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void Submit() = 0;
|
virtual void Submit() = 0;
|
||||||
|
|
|
@ -1129,7 +1129,7 @@ void TidalRequest::ParseSong(Song &song, const QJsonObject &json_obj, const Arti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
title.remove(Song::kTitleRemoveMisc);
|
title = Song::TitleRemoveMisc(title);
|
||||||
|
|
||||||
//qLog(Debug) << "id" << song_id << "track" << track << "disc" << disc << "title" << title << "album" << album << "album artist" << album_artist << "artist" << artist << cover << allow_streaming << url;
|
//qLog(Debug) << "id" << song_id << "track" << track << "disc" << disc << "title" << title << "album" << album << "album artist" << album_artist << "artist" << artist << cover << allow_streaming << url;
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ QString CoverUtils::CoverFilenameFromSource(const Song::Source source, const QUr
|
||||||
|
|
||||||
QString CoverUtils::CoverFilenameFromVariable(const CoverOptions &options, const QString &artist, QString album, const QString &extension) {
|
QString CoverUtils::CoverFilenameFromVariable(const CoverOptions &options, const QString &artist, QString album, const QString &extension) {
|
||||||
|
|
||||||
album = album.remove(Song::kAlbumRemoveDisc);
|
album = Song::AlbumRemoveDisc(album);
|
||||||
|
|
||||||
QString filename(options.cover_pattern);
|
QString filename(options.cover_pattern);
|
||||||
filename.replace("%albumartist", artist);
|
filename.replace("%albumartist", artist);
|
||||||
|
|
Loading…
Reference in New Issue