tagreader: Relocate artist/album/title guessing code

Move the code that attempts to fill missing song metadata out of the
tagreader worker. In the main process, it will be controllable using
settings and calling context.

The methods were moved into a new SongPathParser class that checks new
settings to determine if action should be taken.
This commit is contained in:
Jim Broadus 2021-05-13 23:18:30 -07:00 committed by John Maguire
parent 75de59703c
commit 1309c76bec
6 changed files with 163 additions and 64 deletions

View File

@ -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;

View File

@ -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

105
src/core/songpathparser.cpp Normal file
View File

@ -0,0 +1,105 @@
/* This file is part of Clementine.
Copyright 2021, Jim Broadus <jbroadus@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "songpathparser.h"
#include <QDir>
#include <QFileInfo>
#include <QSettings>
#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);
}
}

43
src/core/songpathparser.h Normal file
View File

@ -0,0 +1,43 @@
/* This file is part of Clementine.
Copyright 2021, Jim Broadus <jbroadus@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_SONGPATHPARSER_H_
#define CORE_SONGPATHPARSER_H_
#include <QString>
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_

View File

@ -21,19 +21,24 @@
#include "tagreaderclient.h"
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <QTcpServer>
#include <QThread>
#include <QUrl>
#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<HandlerType>(this)) {
: QObject(parent),
worker_pool_(new WorkerPool<HandlerType>(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();
}

View File

@ -21,6 +21,8 @@
#ifndef CORE_TAGREADERCLIENT_H_
#define CORE_TAGREADERCLIENT_H_
#include <memory.h>
#include <QStringList>
#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<cpb::tagreader::Message> HandlerType;
typedef HandlerType::ReplyType ReplyType;
@ -79,6 +83,7 @@ class TagReaderClient : public QObject {
WorkerPool<HandlerType>* worker_pool_;
QList<cpb::tagreader::Message> message_queue_;
std::unique_ptr<SongPathParser> path_parser_;
};
typedef TagReaderClient::ReplyType TagReaderReply;