Save embedded cover in the same process as tags

Possible fix for #1158
This commit is contained in:
Jonas Kvinge
2023-03-18 20:03:07 +01:00
parent 394955a03f
commit e20cbe4170
42 changed files with 1205 additions and 723 deletions

View File

@@ -1169,7 +1169,6 @@ void MainWindow::ReloadAllSettings() {
collection_view_->ReloadSettings();
ui_->playlist->view()->ReloadSettings();
app_->playlist_manager()->playlist_container()->ReloadSettings();
app_->album_cover_loader()->ReloadSettings();
album_cover_choice_controller_->ReloadSettings();
context_view_->ReloadSettings();
file_view_->ReloadSettings();
@@ -2152,7 +2151,7 @@ void MainWindow::SongSaveComplete(TagReaderReply *reply, const QPersistentModelI
if (reply->is_successful() && idx.isValid()) {
app_->playlist_manager()->current()->ReloadItems(QList<int>() << idx.row());
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
}

View File

@@ -52,7 +52,7 @@
#include "engine/enginebase.h"
#include "utilities/strutils.h"
#include "utilities/timeutils.h"
#include "utilities/cryptutils.h"
#include "utilities/coverutils.h"
#include "utilities/timeconstants.h"
#include "song.h"
#include "application.h"
@@ -61,9 +61,6 @@
#include "sqlrow.h"
#include "tagreadermessages.pb.h"
#define QStringFromStdString(x) QString::fromUtf8((x).data(), (x).size())
#define DataCommaSizeFromQString(x) (x).toUtf8().constData(), (x).toUtf8().length()
const QStringList Song::kColumns = QStringList() << "title"
<< "album"
<< "artist"
@@ -903,27 +900,27 @@ void Song::InitFromProtobuf(const spb::tagreader::SongMetadata &pb) {
d->init_from_file_ = true;
d->valid_ = pb.valid();
set_title(QStringFromStdString(pb.title()));
set_album(QStringFromStdString(pb.album()));
set_artist(QStringFromStdString(pb.artist()));
set_albumartist(QStringFromStdString(pb.albumartist()));
set_title(QString::fromUtf8(pb.title().data(), pb.title().size()));
set_album(QString::fromUtf8(pb.album().data(), pb.album().size()));
set_artist(QString::fromUtf8(pb.artist().data(), pb.artist().size()));
set_albumartist(QString::fromUtf8(pb.albumartist().data(), pb.albumartist().size()));
d->track_ = pb.track();
d->disc_ = pb.disc();
d->year_ = pb.year();
d->originalyear_ = pb.originalyear();
d->genre_ = QStringFromStdString(pb.genre());
d->genre_ = QString::fromUtf8(pb.genre().data(), pb.genre().size());
d->compilation_ = pb.compilation();
d->composer_ = QStringFromStdString(pb.composer());
d->performer_ = QStringFromStdString(pb.performer());
d->grouping_ = QStringFromStdString(pb.grouping());
d->comment_ = QStringFromStdString(pb.comment());
d->lyrics_ = QStringFromStdString(pb.lyrics());
d->composer_ = QString::fromUtf8(pb.composer().data(), pb.composer().size());
d->performer_ = QString::fromUtf8(pb.performer().data(), pb.performer().size());
d->grouping_ = QString::fromUtf8(pb.grouping().data(), pb.grouping().size());
d->comment_ = QString::fromUtf8(pb.comment().data(), pb.comment().size());
d->lyrics_ = QString::fromUtf8(pb.lyrics().data(), pb.lyrics().size());
set_length_nanosec(static_cast<qint64>(pb.length_nanosec()));
d->bitrate_ = pb.bitrate();
d->samplerate_ = pb.samplerate();
d->bitdepth_ = pb.bitdepth();
set_url(QUrl::fromEncoded(QByteArray(pb.url().data(), static_cast<qint64>(pb.url().size()))));
d->basefilename_ = QStringFromStdString(pb.basefilename());
d->basefilename_ = QString::fromUtf8(pb.basefilename().data(), pb.basefilename().size());
d->filetype_ = static_cast<FileType>(pb.filetype());
d->filesize_ = pb.filesize();
d->mtime_ = pb.mtime();
@@ -956,27 +953,27 @@ void Song::ToProtobuf(spb::tagreader::SongMetadata *pb) const {
const QByteArray art_automatic(d->art_automatic_.toEncoded());
pb->set_valid(d->valid_);
pb->set_title(DataCommaSizeFromQString(d->title_));
pb->set_album(DataCommaSizeFromQString(d->album_));
pb->set_artist(DataCommaSizeFromQString(d->artist_));
pb->set_albumartist(DataCommaSizeFromQString(d->albumartist_));
pb->set_title(d->title_.toStdString());
pb->set_album(d->album_.toStdString());
pb->set_artist(d->artist_.toStdString());
pb->set_albumartist(d->albumartist_.toStdString());
pb->set_track(d->track_);
pb->set_disc(d->disc_);
pb->set_year(d->year_);
pb->set_originalyear(d->originalyear_);
pb->set_genre(DataCommaSizeFromQString(d->genre_));
pb->set_genre(d->genre_.toStdString());
pb->set_compilation(d->compilation_);
pb->set_composer(DataCommaSizeFromQString(d->composer_));
pb->set_performer(DataCommaSizeFromQString(d->performer_));
pb->set_grouping(DataCommaSizeFromQString(d->grouping_));
pb->set_comment(DataCommaSizeFromQString(d->comment_));
pb->set_lyrics(DataCommaSizeFromQString(d->lyrics_));
pb->set_composer(d->composer_.toStdString());
pb->set_performer(d->performer_.toStdString());
pb->set_grouping(d->grouping_.toStdString());
pb->set_comment(d->comment_.toStdString());
pb->set_lyrics(d->lyrics_.toStdString());
pb->set_length_nanosec(length_nanosec());
pb->set_bitrate(d->bitrate_);
pb->set_samplerate(d->samplerate_);
pb->set_bitdepth(d->bitdepth_);
pb->set_url(url.constData(), url.size());
pb->set_basefilename(DataCommaSizeFromQString(d->basefilename_));
pb->set_basefilename(d->basefilename_.toStdString());
pb->set_filetype(static_cast<spb::tagreader::SongMetadata_FileType>(d->filetype_));
pb->set_filesize(d->filesize_);
pb->set_mtime(d->mtime_);
@@ -1080,7 +1077,7 @@ void Song::InitArtManual() {
// If we don't have an art, check if we have one in the cache
if (d->art_manual_.isEmpty() && d->art_automatic_.isEmpty() && !effective_albumartist().isEmpty() && !effective_album().isEmpty()) {
QString filename(Utilities::Sha1CoverHash(effective_albumartist(), effective_album()).toHex() + ".jpg");
QString filename(CoverUtils::Sha1CoverHash(effective_albumartist(), effective_album()).toHex() + ".jpg");
QString path(ImageCacheDir(d->source_) + "/" + filename);
if (QFile::exists(path)) {
d->art_manual_ = QUrl::fromLocalFile(path);
@@ -1153,7 +1150,7 @@ void Song::InitFromItdb(Itdb_Track *track, const QString &prefix) {
QString cover_path = ImageCacheDir(Source::Device);
QDir dir(cover_path);
if (!dir.exists()) dir.mkpath(cover_path);
QString cover_file = cover_path + "/" + Utilities::Sha1CoverHash(effective_albumartist(), effective_album()).toHex() + ".jpg";
QString cover_file = cover_path + "/" + CoverUtils::Sha1CoverHash(effective_albumartist(), effective_album()).toHex() + ".jpg";
GError *error = nullptr;
if (dir.exists() && gdk_pixbuf_save(pixbuf, cover_file.toUtf8().constData(), "jpeg", &error, nullptr)) {
d->art_manual_ = QUrl::fromLocalFile(cover_file);
@@ -1526,7 +1523,7 @@ bool Song::IsMetadataEqual(const Song &other) const {
d->cue_path_ == other.d->cue_path_;
}
bool Song::IsStatisticsEqual(const Song &other) const {
bool Song::IsPlayStatisticsEqual(const Song &other) const {
return d->playcount_ == other.d->playcount_ &&
d->skipcount_ == other.d->skipcount_ &&
@@ -1556,7 +1553,7 @@ bool Song::IsArtEqual(const Song &other) const {
bool Song::IsAllMetadataEqual(const Song &other) const {
return IsMetadataEqual(other) &&
IsStatisticsEqual(other) &&
IsPlayStatisticsEqual(other) &&
IsRatingEqual(other) &&
IsFingerprintEqual(other) &&
IsArtEqual(other);

View File

@@ -390,7 +390,7 @@ class Song {
// Comparison functions
bool IsMetadataEqual(const Song &other) const;
bool IsStatisticsEqual(const Song &other) const;
bool IsPlayStatisticsEqual(const Song &other) const;
bool IsRatingEqual(const Song &other) const;
bool IsFingerprintEqual(const Song &other) const;
bool IsArtEqual(const Song &other) const;

View File

@@ -38,8 +38,6 @@
#include "tagreaderclient.h"
#include "settings/collectionsettingspage.h"
#define DataCommaSizeFromQString(x) (x).toUtf8().constData(), (x).toUtf8().length()
const char *TagReaderClient::kWorkerExecutableName = "strawberry-tagreader";
TagReaderClient *TagReaderClient::sInstance = nullptr;
@@ -82,9 +80,10 @@ void TagReaderClient::WorkerFailedToStart() {
TagReaderReply *TagReaderClient::IsMediaFile(const QString &filename) {
spb::tagreader::Message message;
spb::tagreader::IsMediaFileRequest *req = message.mutable_is_media_file_request();
spb::tagreader::IsMediaFileRequest *request = message.mutable_is_media_file_request();
req->set_filename(DataCommaSizeFromQString(filename));
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
return worker_pool_->SendMessageWithReply(&message);
@@ -93,21 +92,35 @@ TagReaderReply *TagReaderClient::IsMediaFile(const QString &filename) {
TagReaderReply *TagReaderClient::ReadFile(const QString &filename) {
spb::tagreader::Message message;
spb::tagreader::ReadFileRequest *req = message.mutable_read_file_request();
spb::tagreader::ReadFileRequest *request = message.mutable_read_file_request();
req->set_filename(DataCommaSizeFromQString(filename));
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
return worker_pool_->SendMessageWithReply(&message);
}
TagReaderReply *TagReaderClient::SaveFile(const QString &filename, const Song &metadata) {
TagReaderReply *TagReaderClient::SaveFile(const QString &filename, const Song &metadata, const SaveTags save_tags, const SavePlaycount save_playcount, const SaveRating save_rating, const SaveCoverOptions &save_cover_options) {
spb::tagreader::Message message;
spb::tagreader::SaveFileRequest *req = message.mutable_save_file_request();
spb::tagreader::SaveFileRequest *request = message.mutable_save_file_request();
req->set_filename(DataCommaSizeFromQString(filename));
metadata.ToProtobuf(req->mutable_metadata());
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
request->set_save_tags(save_tags == SaveTags::On);
request->set_save_playcount(save_playcount == SavePlaycount::On);
request->set_save_rating(save_rating == SaveRating::On);
request->set_save_cover(save_cover_options.enabled);
request->set_cover_is_jpeg(save_cover_options.is_jpeg);
if (save_cover_options.cover_filename.length() > 0) {
const QByteArray cover_filename = filename.toUtf8();
request->set_cover_filename(cover_filename.constData(), cover_filename.length());
}
if (save_cover_options.cover_data.length() > 0) {
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
}
metadata.ToProtobuf(request->mutable_metadata());
ReplyType *reply = worker_pool_->SendMessageWithReply(&message);
@@ -118,21 +131,31 @@ TagReaderReply *TagReaderClient::SaveFile(const QString &filename, const Song &m
TagReaderReply *TagReaderClient::LoadEmbeddedArt(const QString &filename) {
spb::tagreader::Message message;
spb::tagreader::LoadEmbeddedArtRequest *req = message.mutable_load_embedded_art_request();
spb::tagreader::LoadEmbeddedArtRequest *request = message.mutable_load_embedded_art_request();
req->set_filename(DataCommaSizeFromQString(filename));
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
return worker_pool_->SendMessageWithReply(&message);
}
TagReaderReply *TagReaderClient::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
TagReaderReply *TagReaderClient::SaveEmbeddedArt(const QString &filename, const SaveCoverOptions &save_cover_options) {
spb::tagreader::Message message;
spb::tagreader::SaveEmbeddedArtRequest *req = message.mutable_save_embedded_art_request();
spb::tagreader::SaveEmbeddedArtRequest *request = message.mutable_save_embedded_art_request();
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
request->set_cover_is_jpeg(save_cover_options.is_jpeg);
if (save_cover_options.cover_filename.length() > 0) {
const QByteArray cover_filename = filename.toUtf8();
request->set_cover_filename(cover_filename.constData(), cover_filename.length());
}
if (save_cover_options.cover_data.length() > 0) {
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
}
req->set_filename(DataCommaSizeFromQString(filename));
req->set_data(data.constData(), data.size());
return worker_pool_->SendMessageWithReply(&message);
@@ -141,10 +164,11 @@ TagReaderReply *TagReaderClient::SaveEmbeddedArt(const QString &filename, const
TagReaderReply *TagReaderClient::UpdateSongPlaycount(const Song &metadata) {
spb::tagreader::Message message;
spb::tagreader::SaveSongPlaycountToFileRequest *req = message.mutable_save_song_playcount_to_file_request();
spb::tagreader::SaveSongPlaycountToFileRequest *request = message.mutable_save_song_playcount_to_file_request();
req->set_filename(DataCommaSizeFromQString(metadata.url().toLocalFile()));
metadata.ToProtobuf(req->mutable_metadata());
const QByteArray filename_data = metadata.url().toLocalFile().toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
metadata.ToProtobuf(request->mutable_metadata());
return worker_pool_->SendMessageWithReply(&message);
@@ -162,10 +186,11 @@ void TagReaderClient::UpdateSongsPlaycount(const SongList &songs) {
TagReaderReply *TagReaderClient::UpdateSongRating(const Song &metadata) {
spb::tagreader::Message message;
spb::tagreader::SaveSongRatingToFileRequest *req = message.mutable_save_song_rating_to_file_request();
spb::tagreader::SaveSongRatingToFileRequest *request = message.mutable_save_song_rating_to_file_request();
req->set_filename(DataCommaSizeFromQString(metadata.url().toLocalFile()));
metadata.ToProtobuf(req->mutable_metadata());
const QByteArray filename_data = metadata.url().toLocalFile().toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
metadata.ToProtobuf(request->mutable_metadata());
return worker_pool_->SendMessageWithReply(&message);
@@ -190,7 +215,7 @@ bool TagReaderClient::IsMediaFileBlocking(const QString &filename) {
if (reply->WaitForFinished()) {
ret = reply->message().is_media_file_response().success();
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
return ret;
@@ -204,21 +229,21 @@ void TagReaderClient::ReadFileBlocking(const QString &filename, Song *song) {
if (reply->WaitForFinished()) {
song->InitFromProtobuf(reply->message().read_file_response().metadata());
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
}
bool TagReaderClient::SaveFileBlocking(const QString &filename, const Song &metadata) {
bool TagReaderClient::SaveFileBlocking(const QString &filename, const Song &metadata, const SaveTags save_tags, const SavePlaycount save_playcount, const SaveRating save_rating, const SaveCoverOptions &save_cover_options) {
Q_ASSERT(QThread::currentThread() != thread());
bool ret = false;
TagReaderReply *reply = SaveFile(filename, metadata);
TagReaderReply *reply = SaveFile(filename, metadata, save_tags, save_playcount, save_rating, save_cover_options);
if (reply->WaitForFinished()) {
ret = reply->message().save_file_response().success();
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
return ret;
@@ -235,7 +260,7 @@ QByteArray TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename) {
const std::string &data_str = reply->message().load_embedded_art_response().data();
ret = QByteArray(data_str.data(), static_cast<qint64>(data_str.size()));
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
return ret;
@@ -252,23 +277,23 @@ QImage TagReaderClient::LoadEmbeddedArtAsImageBlocking(const QString &filename)
const std::string &data_str = reply->message().load_embedded_art_response().data();
ret.loadFromData(QByteArray(data_str.data(), static_cast<qint64>(data_str.size())));
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
return ret;
}
bool TagReaderClient::SaveEmbeddedArtBlocking(const QString &filename, const QByteArray &data) {
bool TagReaderClient::SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options) {
Q_ASSERT(QThread::currentThread() != thread());
bool success = false;
TagReaderReply *reply = SaveEmbeddedArt(filename, data);
TagReaderReply *reply = SaveEmbeddedArt(filename, save_cover_options);
if (reply->WaitForFinished()) {
success = reply->message().save_embedded_art_response().success();
}
QMetaObject::invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
reply->deleteLater();
return success;

View File

@@ -53,22 +53,48 @@ class TagReaderClient : public QObject {
void Start();
void ExitAsync();
ReplyType *ReadFile(const QString &filename);
ReplyType *SaveFile(const QString &filename, const Song &metadata);
enum class SaveTags {
Off,
On
};
enum class SavePlaycount {
Off,
On
};
enum class SaveRating {
Off,
On
};
class SaveCoverOptions {
public:
explicit SaveCoverOptions(const bool _enabled = false, const bool _is_jpeg = false, const QString &_cover_filename = QString(), const QByteArray &_cover_data = QByteArray()) : enabled(_enabled), is_jpeg(_is_jpeg), cover_filename(_cover_filename), cover_data(_cover_data) {}
explicit SaveCoverOptions(const QString &_cover_filename) : enabled(true), is_jpeg(false), cover_filename(_cover_filename) {}
explicit SaveCoverOptions(const QByteArray &_cover_data) : enabled(true), is_jpeg(false), cover_data(_cover_data) {}
bool enabled;
bool is_jpeg;
QString cover_filename;
QByteArray cover_data;
};
ReplyType *IsMediaFile(const QString &filename);
ReplyType *ReadFile(const QString &filename);
ReplyType *SaveFile(const QString &filename, const Song &metadata, const SaveTags save_tags = SaveTags(), const SavePlaycount save_playcount = SavePlaycount(), const SaveRating save_rating = SaveRating(), const SaveCoverOptions &save_cover_options = SaveCoverOptions());
ReplyType *LoadEmbeddedArt(const QString &filename);
ReplyType *SaveEmbeddedArt(const QString &filename, const QByteArray &data);
ReplyType *SaveEmbeddedArt(const QString &filename, const SaveCoverOptions &save_cover_options);
ReplyType *UpdateSongPlaycount(const Song &metadata);
ReplyType *UpdateSongRating(const Song &metadata);
// Convenience functions that call the above functions and wait for a response.
// These block the calling thread with a semaphore, and must NOT be called from the TagReaderClient's thread.
void ReadFileBlocking(const QString &filename, Song *song);
bool SaveFileBlocking(const QString &filename, const Song &metadata);
bool SaveFileBlocking(const QString &filename, const Song &metadata, const SaveTags save_tags = SaveTags(), const SavePlaycount save_playcount = SavePlaycount(), const SaveRating save_rating = SaveRating(), const SaveCoverOptions &save_cover_options = SaveCoverOptions());
bool IsMediaFileBlocking(const QString &filename);
QByteArray LoadEmbeddedArtBlocking(const QString &filename);
QImage LoadEmbeddedArtAsImageBlocking(const QString &filename);
bool SaveEmbeddedArtBlocking(const QString &filename, const QByteArray &data);
bool SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options);
bool UpdateSongPlaycountBlocking(const Song &metadata);
bool UpdateSongRatingBlocking(const Song &metadata);