diff --git a/ext/libclementine-tagreader/tagreader.cpp b/ext/libclementine-tagreader/tagreader.cpp index 49e648fc4..e6fc5592d 100644 --- a/ext/libclementine-tagreader/tagreader.cpp +++ b/ext/libclementine-tagreader/tagreader.cpp @@ -126,67 +126,8 @@ namespace { const char* kMP4_OriginalYear_ID = "----:com.apple.iTunes:ORIGINAL YEAR"; const char* kASF_OriginalDate_ID = "WM/OriginalReleaseTime"; const char* kASF_OriginalYear_ID = "WM/OriginalReleaseYear"; - -// Helpers for GuessArtistAndTitle() -QString WithoutExtension(const QString& s) { - if (s.isEmpty()) return s; - const int i = s.lastIndexOf('.'); - if (i < 0) return s; - return s.left(i); -} - -QString ReplaceUnderscoresWithSpaces(const QString& s) { - QString ret(s); - ret.replace('_', ' '); - return ret; -} - } // namespace -void TagReader::GuessArtistAndTitle(cpb::tagreader::SongMetadata* song) const { - QString artist = QString::fromStdString(song->artist()); - QString title = QString::fromStdString(song->title()); - const QString bn = QString::fromStdString(song->basefilename()); - if (!artist.isEmpty() || !title.isEmpty()) return; - if (bn.isEmpty()) return; - - QRegExp rx("^(.*)[\\s_]\\-[\\s_](.*)\\.\\w*$"); - if (rx.indexIn(bn) >= 0) { - artist = rx.cap(1); - title = rx.cap(2); - } else { - title = WithoutExtension(bn); - } - - artist = ReplaceUnderscoresWithSpaces(artist); - title = ReplaceUnderscoresWithSpaces(title); - artist = artist.trimmed(); - title = title.trimmed(); - if (!artist.isEmpty()) { - song->set_artist(artist.toUtf8().data()); - } - if (!title.isEmpty()) { - song->set_title(title.toUtf8().data()); - } -} - -void TagReader::GuessAlbum(const QFileInfo& info, - cpb::tagreader::SongMetadata* song) const { - QString album = QString::fromStdString(song->album()); - if (!album.isEmpty()) return; - const QString str_dir = info.absoluteDir().absolutePath(); - if (str_dir.isEmpty()) return; - const QFileInfo dir(str_dir); - const QString dir_bn = dir.baseName(); - if (dir_bn.isEmpty()) return; - album = ReplaceUnderscoresWithSpaces(dir_bn); - album = album.trimmed(); - if (album.isEmpty()) return; - const QString al = album.toLower(); - if (al == "various" || al == "downloads" || al == "music") return; - song->set_album(album.toUtf8().data()); -} - TagReader::TagReader() : factory_(new TagLibFileRefFactory), kEmbeddedCover("(embedded)") {} @@ -233,8 +174,6 @@ void TagReader::ReadFile(const QString& filename, // Try fallback -- GME filetypes GME::ReadFile(info, song); - GuessArtistAndTitle(song); - GuessAlbum(info, song); return; } @@ -248,8 +187,6 @@ void TagReader::ReadFile(const QString& filename, song->set_track(tag->track()); song->set_valid(true); } - GuessArtistAndTitle(song); - GuessAlbum(info, song); QString disc; QString compilation; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4268ed28c..9fee565cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,7 @@ set(SOURCES core/signalchecker.cpp core/song.cpp core/songloader.cpp + core/songpathparser.cpp core/stylesheetloader.cpp core/tagreaderclient.cpp core/taskmanager.cpp diff --git a/src/core/songpathparser.cpp b/src/core/songpathparser.cpp new file mode 100644 index 000000000..cd2cc775a --- /dev/null +++ b/src/core/songpathparser.cpp @@ -0,0 +1,105 @@ +/* This file is part of Clementine. + Copyright 2021, Jim Broadus + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "songpathparser.h" + +#include +#include +#include + +#include "core/logging.h" +#include "song.h" + +const char* SongPathParser::kSongMetadataSettingsGroup = "SongMetadata"; +const char* SongPathParser::kGuessMetadataSetting = "guess_metadata"; +const bool SongPathParser::kGuessMetadataSettingDefault = true; + +SongPathParser::SongPathParser() : guess_metadata_(true) { ReloadSettings(); } + +void SongPathParser::ReloadSettings() { + QSettings s; + s.beginGroup(kSongMetadataSettingsGroup); + guess_metadata_ = + s.value(kGuessMetadataSetting, kGuessMetadataSettingDefault).toBool(); +} + +// Helpers for GuessArtistAndTitle() +static QString WithoutExtension(const QString& s) { + if (s.isEmpty()) return s; + const int i = s.lastIndexOf('.'); + if (i < 0) return s; + return s.left(i); +} + +static QString ReplaceUnderscoresWithSpaces(const QString& s) { + QString ret(s); + ret.replace('_', ' '); + return ret; +} + +void SongPathParser::GuessArtistAndTitle(Song* song) { + qLog(Debug) << "Guess artist and title"; + QString artist = song->artist(); + QString title = song->title(); + const QString bn = song->basefilename(); + if (!artist.isEmpty() || !title.isEmpty()) return; + if (bn.isEmpty()) return; + + QRegExp rx("^(.*)[\\s_]\\-[\\s_](.*)\\.\\w*$"); + if (rx.indexIn(bn) >= 0) { + artist = rx.cap(1); + title = rx.cap(2); + } else { + title = WithoutExtension(bn); + } + + artist = ReplaceUnderscoresWithSpaces(artist); + title = ReplaceUnderscoresWithSpaces(title); + artist = artist.trimmed(); + title = title.trimmed(); + if (!artist.isEmpty()) { + song->set_artist(artist); + } + if (!title.isEmpty()) { + song->set_title(title); + } +} + +void SongPathParser::GuessAlbum(const QString& path, Song* song) { + qLog(Debug) << "Guess album"; + QFileInfo info(path); + QString album = song->album(); + if (!album.isEmpty()) return; + const QString str_dir = info.absoluteDir().absolutePath(); + if (str_dir.isEmpty()) return; + const QFileInfo dir(str_dir); + const QString dir_bn = dir.baseName(); + if (dir_bn.isEmpty()) return; + album = ReplaceUnderscoresWithSpaces(dir_bn); + album = album.trimmed(); + if (album.isEmpty()) return; + const QString al = album.toLower(); + if (al == "various" || al == "downloads" || al == "music") return; + song->set_album(album); +} + +void SongPathParser::GuessMissingFields(Song* song, QString path) { + if (guess_metadata_) { + GuessArtistAndTitle(song); + GuessAlbum(path, song); + } +} diff --git a/src/core/songpathparser.h b/src/core/songpathparser.h new file mode 100644 index 000000000..3cc99bd6d --- /dev/null +++ b/src/core/songpathparser.h @@ -0,0 +1,43 @@ +/* This file is part of Clementine. + Copyright 2021, Jim Broadus + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef CORE_SONGPATHPARSER_H_ +#define CORE_SONGPATHPARSER_H_ + +#include + +class Song; + +class SongPathParser { + public: + SongPathParser(); + + static const char* kSongMetadataSettingsGroup; + static const char* kGuessMetadataSetting; + static const bool kGuessMetadataSettingDefault; + + void GuessMissingFields(Song* song, QString path); + void ReloadSettings(); + + private: + void GuessArtistAndTitle(Song* song); + void GuessAlbum(const QString& path, Song* song); + + bool guess_metadata_; +}; + +#endif // CORE_SONGPATHPARSER_H_ diff --git a/src/core/tagreaderclient.cpp b/src/core/tagreaderclient.cpp index db2a21929..47a262a71 100644 --- a/src/core/tagreaderclient.cpp +++ b/src/core/tagreaderclient.cpp @@ -21,19 +21,24 @@ #include "tagreaderclient.h" #include +#include #include +#include #include #include #include #include #include "player.h" +#include "songpathparser.h" const char* TagReaderClient::kWorkerExecutableName = "clementine-tagreader"; TagReaderClient* TagReaderClient::sInstance = nullptr; TagReaderClient::TagReaderClient(QObject* parent) - : QObject(parent), worker_pool_(new WorkerPool(this)) { + : QObject(parent), + worker_pool_(new WorkerPool(this)), + path_parser_(new SongPathParser()) { sInstance = this; setObjectName("Tag reader client"); @@ -49,6 +54,8 @@ TagReaderClient::TagReaderClient(QObject* parent) SLOT(WorkerFailedToStart())); } +TagReaderClient::~TagReaderClient() {} + void TagReaderClient::Start() { worker_pool_->Start(); } void TagReaderClient::WorkerFailedToStart() { @@ -156,6 +163,7 @@ void TagReaderClient::ReadFileBlocking(const QString& filename, Song* song) { TagReaderReply* reply = ReadFile(filename); if (reply->WaitForFinished()) { song->InitFromProtobuf(reply->message().read_file_response().metadata()); + path_parser_->GuessMissingFields(song, filename); } reply->deleteLater(); } diff --git a/src/core/tagreaderclient.h b/src/core/tagreaderclient.h index 018b06774..77728c27e 100644 --- a/src/core/tagreaderclient.h +++ b/src/core/tagreaderclient.h @@ -21,6 +21,8 @@ #ifndef CORE_TAGREADERCLIENT_H_ #define CORE_TAGREADERCLIENT_H_ +#include + #include #include "core/messagehandler.h" @@ -30,12 +32,14 @@ class QLocalServer; class QProcess; +class SongPathParser; class TagReaderClient : public QObject { Q_OBJECT public: explicit TagReaderClient(QObject* parent = nullptr); + virtual ~TagReaderClient(); typedef AbstractMessageHandler HandlerType; typedef HandlerType::ReplyType ReplyType; @@ -79,6 +83,7 @@ class TagReaderClient : public QObject { WorkerPool* worker_pool_; QList message_queue_; + std::unique_ptr path_parser_; }; typedef TagReaderClient::ReplyType TagReaderReply;