Add better error handling for Tag reader

This commit is contained in:
Jonas Kvinge
2024-07-01 02:06:39 +02:00
parent ad9f3ce078
commit 32baa95500
38 changed files with 1109 additions and 730 deletions

View File

@@ -2249,7 +2249,7 @@ void MainWindow::RenumberTracks() {
Song song = item->OriginalMetadata();
if (song.IsEditable()) {
song.set_track(track);
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
TagReaderReply *reply = TagReaderClient::Instance()->WriteFile(song.url().toLocalFile(), song);
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
}
@@ -2280,7 +2280,7 @@ void MainWindow::SelectionSetValue() {
Song song = item->OriginalMetadata();
if (!song.is_valid()) continue;
if (song.url().isLocalFile() && Playlist::set_column_value(song, column, column_value)) {
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
TagReaderReply *reply = TagReaderClient::Instance()->WriteFile(song.url().toLocalFile(), song);
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
}

View File

@@ -1302,27 +1302,27 @@ void Song::InitFromProtobuf(const spb::tagreader::SongMetadata &pb) {
d->init_from_file_ = true;
d->valid_ = pb.valid();
set_title(QString::fromUtf8(pb.title().data(), static_cast<qint64>(pb.title().size())));
set_album(QString::fromUtf8(pb.album().data(), static_cast<qint64>(pb.album().size())));
set_artist(QString::fromUtf8(pb.artist().data(), static_cast<qint64>(pb.artist().size())));
set_albumartist(QString::fromUtf8(pb.albumartist().data(), static_cast<qint64>(pb.albumartist().size())));
set_title(QString::fromStdString(pb.title()));
set_album(QString::fromStdString(pb.album()));
set_artist(QString::fromStdString(pb.artist()));
set_albumartist(QString::fromStdString(pb.albumartist()));
d->track_ = pb.track();
d->disc_ = pb.disc();
d->year_ = pb.year();
d->originalyear_ = pb.originalyear();
d->genre_ = QString::fromUtf8(pb.genre().data(), static_cast<qint64>(pb.genre().size()));
d->genre_ = QString::fromStdString(pb.genre());
d->compilation_ = pb.compilation();
d->composer_ = QString::fromUtf8(pb.composer().data(), static_cast<qint64>(pb.composer().size()));
d->performer_ = QString::fromUtf8(pb.performer().data(), static_cast<qint64>(pb.performer().size()));
d->grouping_ = QString::fromUtf8(pb.grouping().data(), static_cast<qint64>(pb.grouping().size()));
d->comment_ = QString::fromUtf8(pb.comment().data(), static_cast<qint64>(pb.comment().size()));
d->lyrics_ = QString::fromUtf8(pb.lyrics().data(), static_cast<qint64>(pb.lyrics().size()));
d->composer_ = QString::fromStdString(pb.composer());
d->performer_ = QString::fromStdString(pb.performer());
d->grouping_ = QString::fromStdString(pb.grouping());
d->comment_ = QString::fromStdString(pb.comment());
d->lyrics_ = QString::fromStdString(pb.lyrics());
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_ = QString::fromUtf8(pb.basefilename().data(), static_cast<qint64>(pb.basefilename().size()));
set_url(QUrl::fromEncoded(QString::fromStdString(pb.url()).toUtf8()));
d->basefilename_ = QString::fromStdString(pb.basefilename());
d->filetype_ = static_cast<FileType>(pb.filetype());
d->filesize_ = pb.filesize();
d->mtime_ = pb.mtime();
@@ -1340,19 +1340,19 @@ void Song::InitFromProtobuf(const spb::tagreader::SongMetadata &pb) {
d->art_embedded_ = pb.has_art_embedded();
d->acoustid_id_ = QString::fromUtf8(pb.acoustid_id().data(), static_cast<qint64>(pb.acoustid_id().size()));
d->acoustid_fingerprint_ = QString::fromUtf8(pb.acoustid_fingerprint().data(), static_cast<qint64>(pb.acoustid_fingerprint().size()));
d->acoustid_id_ = QString::fromStdString(pb.acoustid_id());
d->acoustid_fingerprint_ = QString::fromStdString(pb.acoustid_fingerprint());
d->musicbrainz_album_artist_id_ = QString::fromUtf8(pb.musicbrainz_album_artist_id().data(), static_cast<qint64>(pb.musicbrainz_album_artist_id().size()));
d->musicbrainz_artist_id_ = QString::fromUtf8(pb.musicbrainz_artist_id().data(), static_cast<qint64>(pb.musicbrainz_artist_id().size()));
d->musicbrainz_original_artist_id_ = QString::fromUtf8(pb.musicbrainz_original_artist_id().data(), static_cast<qint64>(pb.musicbrainz_original_artist_id().size()));
d->musicbrainz_album_id_ = QString::fromUtf8(pb.musicbrainz_album_id().data(), static_cast<qint64>(pb.musicbrainz_album_id().size()));
d->musicbrainz_original_album_id_ = QString::fromUtf8(pb.musicbrainz_original_album_id().data(), static_cast<qint64>(pb.musicbrainz_original_album_id().size()));
d->musicbrainz_recording_id_ = QString::fromUtf8(pb.musicbrainz_recording_id().data(), static_cast<qint64>(pb.musicbrainz_recording_id().size()));
d->musicbrainz_track_id_ = QString::fromUtf8(pb.musicbrainz_track_id().data(), static_cast<qint64>(pb.musicbrainz_track_id().size()));
d->musicbrainz_disc_id_ = QString::fromUtf8(pb.musicbrainz_disc_id().data(), static_cast<qint64>(pb.musicbrainz_disc_id().size()));
d->musicbrainz_release_group_id_ = QString::fromUtf8(pb.musicbrainz_release_group_id().data(), static_cast<qint64>(pb.musicbrainz_release_group_id().size()));
d->musicbrainz_work_id_ = QString::fromUtf8(pb.musicbrainz_work_id().data(), static_cast<qint64>(pb.musicbrainz_work_id().size()));
d->musicbrainz_album_artist_id_ = QString::fromStdString(pb.musicbrainz_album_artist_id());
d->musicbrainz_artist_id_ = QString::fromStdString(pb.musicbrainz_artist_id().data());
d->musicbrainz_original_artist_id_ = QString::fromStdString(pb.musicbrainz_original_artist_id());
d->musicbrainz_album_id_ = QString::fromStdString(pb.musicbrainz_album_id());
d->musicbrainz_original_album_id_ = QString::fromStdString(pb.musicbrainz_original_album_id());
d->musicbrainz_recording_id_ = QString::fromStdString(pb.musicbrainz_recording_id());
d->musicbrainz_track_id_ = QString::fromStdString(pb.musicbrainz_track_id());
d->musicbrainz_disc_id_ = QString::fromStdString(pb.musicbrainz_disc_id());
d->musicbrainz_release_group_id_ = QString::fromStdString(pb.musicbrainz_release_group_id());
d->musicbrainz_work_id_ = QString::fromStdString(pb.musicbrainz_work_id());
d->suspicious_tags_ = pb.suspicious_tags();

View File

@@ -354,8 +354,11 @@ void SongLoader::EffectiveSongLoad(Song *song) {
}
else {
// It's a normal media file
QString filename = song->url().toLocalFile();
TagReaderClient::Instance()->ReadFileBlocking(filename, song);
const QString filename = song->url().toLocalFile();
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(filename, song);
if (!result.success()) {
qLog(Error) << "Could not read file" << song->url() << result.error;
}
}
}

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019-2023, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -73,11 +73,7 @@ void TagReaderClient::WorkerFailedToStart() {
TagReaderReply *TagReaderClient::IsMediaFile(const QString &filename) {
spb::tagreader::Message message;
spb::tagreader::IsMediaFileRequest *request = message.mutable_is_media_file_request();
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
message.mutable_is_media_file_request()->set_filename(filename.toStdString());
return worker_pool_->SendMessageWithReply(&message);
}
@@ -85,39 +81,33 @@ TagReaderReply *TagReaderClient::IsMediaFile(const QString &filename) {
TagReaderReply *TagReaderClient::ReadFile(const QString &filename) {
spb::tagreader::Message message;
spb::tagreader::ReadFileRequest *request = message.mutable_read_file_request();
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
message.mutable_read_file_request()->set_filename(filename.toStdString());
return worker_pool_->SendMessageWithReply(&message);
}
TagReaderReply *TagReaderClient::SaveFile(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
TagReaderReply *TagReaderClient::WriteFile(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
spb::tagreader::Message message;
spb::tagreader::SaveFileRequest *request = message.mutable_save_file_request();
spb::tagreader::WriteFileRequest *request = message.mutable_write_file_request();
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
request->set_filename(filename.toStdString());
request->set_save_tags(save_types.testFlag(SaveType::Tags));
request->set_save_playcount(save_types.testFlag(SaveType::PlayCount));
request->set_save_rating(save_types.testFlag(SaveType::Rating));
request->set_save_cover(save_types.testFlag(SaveType::Cover));
if (save_cover_options.cover_filename.length() > 0) {
const QByteArray cover_filename = save_cover_options.cover_filename.toUtf8();
request->set_cover_filename(cover_filename.constData(), cover_filename.length());
if (!save_cover_options.cover_filename.isEmpty()) {
request->set_cover_filename(save_cover_options.cover_filename.toStdString());
}
if (save_cover_options.cover_data.length() > 0) {
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
if (!save_cover_options.cover_data.isEmpty()) {
request->set_cover_data(save_cover_options.cover_data.toStdString());
}
if (save_cover_options.mime_type.length() > 0) {
const QByteArray cover_mime_type = save_cover_options.mime_type.toUtf8();
request->set_cover_mime_type(cover_mime_type.constData(), cover_mime_type.length());
if (!save_cover_options.mime_type.isEmpty()) {
request->set_cover_mime_type(save_cover_options.mime_type.toStdString());
}
metadata.ToProtobuf(request->mutable_metadata());
ReplyType *reply = worker_pool_->SendMessageWithReply(&message);
@@ -131,8 +121,7 @@ TagReaderReply *TagReaderClient::LoadEmbeddedArt(const QString &filename) {
spb::tagreader::Message message;
spb::tagreader::LoadEmbeddedArtRequest *request = message.mutable_load_embedded_art_request();
const QByteArray filename_data = filename.toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
request->set_filename(filename.toStdString());
return worker_pool_->SendMessageWithReply(&message);
@@ -143,63 +132,59 @@ TagReaderReply *TagReaderClient::SaveEmbeddedArt(const QString &filename, const
spb::tagreader::Message message;
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());
if (save_cover_options.cover_filename.length() > 0) {
const QByteArray cover_filename = save_cover_options.cover_filename.toUtf8();
request->set_cover_filename(cover_filename.constData(), cover_filename.length());
request->set_filename(filename.toStdString());
if (!save_cover_options.cover_filename.isEmpty()) {
request->set_cover_filename(save_cover_options.cover_filename.toStdString());
}
if (save_cover_options.cover_data.length() > 0) {
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
if (!save_cover_options.cover_data.isEmpty()) {
request->set_cover_data(save_cover_options.cover_data.toStdString());
}
if (save_cover_options.mime_type.length() > 0) {
const QByteArray cover_mime_type = save_cover_options.mime_type.toUtf8();
request->set_cover_mime_type(cover_mime_type.constData(), cover_mime_type.length());
if (!save_cover_options.mime_type.isEmpty()) {
request->set_cover_mime_type(save_cover_options.mime_type.toStdString());
}
return worker_pool_->SendMessageWithReply(&message);
}
TagReaderReply *TagReaderClient::UpdateSongPlaycount(const Song &metadata) {
TagReaderReply *TagReaderClient::SaveSongPlaycount(const QString &filename, const uint playcount) {
spb::tagreader::Message message;
spb::tagreader::SaveSongPlaycountToFileRequest *request = message.mutable_save_song_playcount_to_file_request();
const QByteArray filename_data = metadata.url().toLocalFile().toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
metadata.ToProtobuf(request->mutable_metadata());
request->set_filename(filename.toStdString());
request->set_playcount(playcount);
return worker_pool_->SendMessageWithReply(&message);
}
void TagReaderClient::UpdateSongsPlaycount(const SongList &songs) {
void TagReaderClient::SaveSongsPlaycount(const SongList &songs) {
for (const Song &song : songs) {
TagReaderReply *reply = UpdateSongPlaycount(song);
TagReaderReply *reply = SaveSongPlaycount(song.url().toLocalFile(), song.playcount());
QObject::connect(reply, &TagReaderReply::Finished, reply, &TagReaderReply::deleteLater);
}
}
TagReaderReply *TagReaderClient::UpdateSongRating(const Song &metadata) {
TagReaderReply *TagReaderClient::SaveSongRating(const QString &filename, const float rating) {
spb::tagreader::Message message;
spb::tagreader::SaveSongRatingToFileRequest *request = message.mutable_save_song_rating_to_file_request();
const QByteArray filename_data = metadata.url().toLocalFile().toUtf8();
request->set_filename(filename_data.constData(), filename_data.length());
metadata.ToProtobuf(request->mutable_metadata());
request->set_filename(filename.toStdString());
request->set_rating(rating);
return worker_pool_->SendMessageWithReply(&message);
}
void TagReaderClient::UpdateSongsRating(const SongList &songs) {
void TagReaderClient::SaveSongsRating(const SongList &songs) {
for (const Song &song : songs) {
TagReaderReply *reply = UpdateSongRating(song);
TagReaderReply *reply = SaveSongRating(song.url().toLocalFile(), song.rating());
QObject::connect(reply, &TagReaderReply::Finished, reply, &TagReaderReply::deleteLater);
}
@@ -209,124 +194,180 @@ bool TagReaderClient::IsMediaFileBlocking(const QString &filename) {
Q_ASSERT(QThread::currentThread() != thread());
bool ret = false;
bool success = false;
TagReaderReply *reply = IsMediaFile(filename);
if (reply->WaitForFinished()) {
ret = reply->message().is_media_file_response().success();
const spb::tagreader::IsMediaFileResponse &response = reply->message().is_media_file_response();
if (response.has_success()) {
success = response.success();
}
}
reply->deleteLater();
return ret;
return success;
}
void TagReaderClient::ReadFileBlocking(const QString &filename, Song *song) {
TagReaderClient::Result TagReaderClient::ReadFileBlocking(const QString &filename, Song *song) {
Q_ASSERT(QThread::currentThread() != thread());
Result result(Result::ErrorCode::Failure);
TagReaderReply *reply = ReadFile(filename);
if (reply->WaitForFinished()) {
song->InitFromProtobuf(reply->message().read_file_response().metadata());
const spb::tagreader::ReadFileResponse &response = reply->message().read_file_response();
if (response.has_success()) {
if (response.success()) {
result.error_code = Result::ErrorCode::Success;
if (response.has_metadata()) {
song->InitFromProtobuf(response.metadata());
}
}
else {
result.error_code = Result::ErrorCode::Failure;
if (response.has_error()) {
result.error = QString::fromStdString(response.error());
}
}
}
}
reply->deleteLater();
return result;
}
bool TagReaderClient::SaveFileBlocking(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
TagReaderClient::Result TagReaderClient::WriteFileBlocking(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
Q_ASSERT(QThread::currentThread() != thread());
bool ret = false;
Result result(Result::ErrorCode::Failure);
TagReaderReply *reply = SaveFile(filename, metadata, save_types, save_cover_options);
TagReaderReply *reply = WriteFile(filename, metadata, save_types, save_cover_options);
if (reply->WaitForFinished()) {
ret = reply->message().save_file_response().success();
const spb::tagreader::WriteFileResponse &response = reply->message().write_file_response();
if (response.has_success()) {
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
if (response.has_error()) {
result.error = QString::fromStdString(response.error());
}
}
}
reply->deleteLater();
return ret;
return result;
}
QByteArray TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename) {
TagReaderClient::Result TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename, QByteArray &data) {
Q_ASSERT(QThread::currentThread() != thread());
QByteArray ret;
Result result(Result::ErrorCode::Failure);
TagReaderReply *reply = LoadEmbeddedArt(filename);
if (reply->WaitForFinished()) {
const std::string &data_str = reply->message().load_embedded_art_response().data();
ret = QByteArray(data_str.data(), static_cast<qint64>(data_str.size()));
const spb::tagreader::LoadEmbeddedArtResponse &response = reply->message().load_embedded_art_response();
if (response.has_success()) {
if (response.success()) {
result.error_code = Result::ErrorCode::Success;
if (response.has_data()) {
data = QByteArray(response.data().data(), static_cast<qint64>(response.data().size()));
}
}
else {
result.error_code = Result::ErrorCode::Failure;
if (response.has_error()) {
result.error = QString::fromStdString(response.error());
}
}
}
}
reply->deleteLater();
return ret;
return result;
}
QImage TagReaderClient::LoadEmbeddedArtAsImageBlocking(const QString &filename) {
TagReaderClient::Result TagReaderClient::LoadEmbeddedArtAsImageBlocking(const QString &filename, QImage &image) {
Q_ASSERT(QThread::currentThread() != thread());
QImage ret;
TagReaderReply *reply = LoadEmbeddedArt(filename);
if (reply->WaitForFinished()) {
const std::string &data_str = reply->message().load_embedded_art_response().data();
ret.loadFromData(QByteArray(data_str.data(), static_cast<qint64>(data_str.size())));
QByteArray data;
Result result = LoadEmbeddedArtBlocking(filename, data);
if (result.error_code == Result::ErrorCode::Success && !image.loadFromData(data)) {
result.error_code = Result::ErrorCode::Failure;
result.error = QObject::tr("Failed to load image from data for %1").arg(filename);
}
reply->deleteLater();
return ret;
return result;
}
bool TagReaderClient::SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options) {
TagReaderClient::Result TagReaderClient::SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options) {
Q_ASSERT(QThread::currentThread() != thread());
bool success = false;
Result result(Result::ErrorCode::Failure);
TagReaderReply *reply = SaveEmbeddedArt(filename, save_cover_options);
if (reply->WaitForFinished()) {
success = reply->message().save_embedded_art_response().success();
const spb::tagreader::SaveEmbeddedArtResponse &response = reply->message().save_embedded_art_response();
if (response.has_success()) {
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
if (response.has_error()) {
result.error = QString::fromStdString(response.error());
}
}
}
reply->deleteLater();
return success;
return result;
}
bool TagReaderClient::UpdateSongPlaycountBlocking(const Song &metadata) {
TagReaderClient::Result TagReaderClient::SaveSongPlaycountBlocking(const QString &filename, const uint playcount) {
Q_ASSERT(QThread::currentThread() != thread());
bool success = false;
Result result(Result::ErrorCode::Failure);
TagReaderReply *reply = UpdateSongPlaycount(metadata);
TagReaderReply *reply = SaveSongPlaycount(filename, playcount);
if (reply->WaitForFinished()) {
success = reply->message().save_song_playcount_to_file_response().success();
const spb::tagreader::SaveSongPlaycountToFileResponse &response = reply->message().save_song_playcount_to_file_response();
if (response.has_success()) {
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
if (response.has_error()) {
result.error = QString::fromStdString(response.error());
}
}
}
reply->deleteLater();
return success;
return result;
}
bool TagReaderClient::UpdateSongRatingBlocking(const Song &metadata) {
TagReaderClient::Result TagReaderClient::SaveSongRatingBlocking(const QString &filename, const float rating) {
Q_ASSERT(QThread::currentThread() != thread());
bool success = false;
Result result(Result::ErrorCode::Failure);
TagReaderReply *reply = UpdateSongRating(metadata);
TagReaderReply *reply = SaveSongRating(filename, rating);
if (reply->WaitForFinished()) {
success = reply->message().save_song_rating_to_file_response().success();
const spb::tagreader::SaveSongRatingToFileResponse &response = reply->message().save_song_rating_to_file_response();
if (response.has_success()) {
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
if (response.has_error()) {
result.error = QString::fromStdString(response.error());
}
}
}
reply->deleteLater();
return success;
return result;
}

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2011, David Sansome <me@davidsansome.com>
* Copyright 2019-2023, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -70,24 +70,45 @@ class TagReaderClient : public QObject {
QString mime_type;
};
class Result {
public:
enum class ErrorCode {
Success,
Unsupported,
Failure,
};
Result(const ErrorCode _error_code, const QString &_error = QString()) : error_code(_error_code), error(_error) {}
ErrorCode error_code;
QString error;
bool success() const { return error_code == TagReaderClient::Result::ErrorCode::Success; }
};
class Cover {
public:
explicit Cover(const QByteArray &_data = QByteArray(), const QString &_mime_type = QString()) : data(_data), mime_type(_mime_type) {}
QByteArray data;
QString mime_type;
QString error;
};
ReplyType *IsMediaFile(const QString &filename);
ReplyType *ReadFile(const QString &filename);
ReplyType *SaveFile(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
ReplyType *WriteFile(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
ReplyType *LoadEmbeddedArt(const QString &filename);
ReplyType *SaveEmbeddedArt(const QString &filename, const SaveCoverOptions &save_cover_options);
ReplyType *UpdateSongPlaycount(const Song &metadata);
ReplyType *UpdateSongRating(const Song &metadata);
ReplyType *SaveSongPlaycount(const QString &filename, const uint playcount);
ReplyType *SaveSongRating(const QString &filename, const float rating);
// 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, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
Result ReadFileBlocking(const QString &filename, Song *song);
Result WriteFileBlocking(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, 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 SaveCoverOptions &save_cover_options);
bool UpdateSongPlaycountBlocking(const Song &metadata);
bool UpdateSongRatingBlocking(const Song &metadata);
Result LoadEmbeddedArtBlocking(const QString &filename, QByteArray &data);
Result LoadEmbeddedArtAsImageBlocking(const QString &filename, QImage &image);
Result SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options);
Result SaveSongPlaycountBlocking(const QString &filename, const uint playcount);
Result SaveSongRatingBlocking(const QString &filename, const float rating);
// TODO: Make this not a singleton
static TagReaderClient *Instance() { return sInstance; }
@@ -100,8 +121,8 @@ class TagReaderClient : public QObject {
void WorkerFailedToStart();
public slots:
void UpdateSongsPlaycount(const SongList &songs);
void UpdateSongsRating(const SongList &songs);
void SaveSongsPlaycount(const SongList &songs);
void SaveSongsRating(const SongList &songs);
private:
static TagReaderClient *sInstance;