Rewrite album cover loader

This commit is contained in:
Jonas Kvinge
2023-05-14 11:34:55 +02:00
parent 3c160c2f13
commit 331aa382f9
68 changed files with 2948 additions and 2565 deletions

View File

@@ -48,7 +48,7 @@
#include "scopedtransaction.h"
const char *Database::kDatabaseFilename = "strawberry.db";
const int Database::kSchemaVersion = 16;
const int Database::kSchemaVersion = 17;
const int Database::kMinSupportedSchemaVersion = 10;
const char *Database::kMagicAllSongsTables = "%allsongstables";

View File

@@ -1177,6 +1177,7 @@ void MainWindow::ReloadAllSettings() {
collection_view_->ReloadSettings();
ui_->playlist->view()->ReloadSettings();
app_->playlist_manager()->playlist_container()->ReloadSettings();
app_->current_albumcover_loader()->ReloadSettingsAsync();
album_cover_choice_controller_->ReloadSettings();
context_view_->ReloadSettings();
file_view_->ReloadSettings();
@@ -1381,14 +1382,14 @@ void MainWindow::SongChanged(const Song &song) {
SendNowPlaying();
const bool enable_change_art = song.is_collection_song() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
album_cover_choice_controller_->show_cover_action()->setEnabled(song.has_valid_art() && !song.has_manually_unset_cover());
album_cover_choice_controller_->cover_to_file_action()->setEnabled(song.has_valid_art() && !song.has_manually_unset_cover());
album_cover_choice_controller_->show_cover_action()->setEnabled(song.has_valid_art() && !song.art_unset());
album_cover_choice_controller_->cover_to_file_action()->setEnabled(song.has_valid_art() && !song.art_unset());
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_change_art);
album_cover_choice_controller_->cover_from_url_action()->setEnabled(enable_change_art);
album_cover_choice_controller_->search_for_cover_action()->setEnabled(app_->cover_providers()->HasAnyProviders() && enable_change_art);
album_cover_choice_controller_->unset_cover_action()->setEnabled(enable_change_art && !song.has_manually_unset_cover());
album_cover_choice_controller_->unset_cover_action()->setEnabled(enable_change_art && !song.art_unset());
album_cover_choice_controller_->clear_cover_action()->setEnabled(enable_change_art && !song.art_manual().isEmpty());
album_cover_choice_controller_->delete_cover_action()->setEnabled(enable_change_art && song.has_valid_art() && !song.has_manually_unset_cover());
album_cover_choice_controller_->delete_cover_action()->setEnabled(enable_change_art && (song.art_embedded() || !song.art_automatic().isEmpty() || !song.art_manual().isEmpty()));
}
@@ -3072,14 +3073,14 @@ void MainWindow::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult
emit AlbumCoverReady(song, result.album_cover.image);
const bool enable_change_art = song.is_collection_song() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
album_cover_choice_controller_->show_cover_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::ManuallyUnset);
album_cover_choice_controller_->cover_to_file_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::ManuallyUnset);
album_cover_choice_controller_->show_cover_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
album_cover_choice_controller_->cover_to_file_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_change_art);
album_cover_choice_controller_->cover_from_url_action()->setEnabled(enable_change_art);
album_cover_choice_controller_->search_for_cover_action()->setEnabled(app_->cover_providers()->HasAnyProviders() && enable_change_art);
album_cover_choice_controller_->unset_cover_action()->setEnabled(enable_change_art && !song.has_manually_unset_cover());
album_cover_choice_controller_->unset_cover_action()->setEnabled(enable_change_art && !song.art_unset());
album_cover_choice_controller_->clear_cover_action()->setEnabled(enable_change_art && !song.art_manual().isEmpty());
album_cover_choice_controller_->delete_cover_action()->setEnabled(enable_change_art && result.success && result.type != AlbumCoverLoaderResult::Type::ManuallyUnset);
album_cover_choice_controller_->delete_cover_action()->setEnabled(enable_change_art && result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
GetCoverAutomatically();
@@ -3089,7 +3090,8 @@ void MainWindow::GetCoverAutomatically() {
// Search for cover automatically?
const bool search = album_cover_choice_controller_->search_cover_auto_action()->isChecked() &&
!song_.has_manually_unset_cover() &&
!song_.art_unset() &&
!song_.art_embedded() &&
!song_.art_automatic_is_valid() &&
!song_.art_manual_is_valid() &&
!song_.effective_albumartist().isEmpty() &&

View File

@@ -401,14 +401,16 @@ void Mpris2::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult &re
else if (result.temp_cover_url.isValid() && result.temp_cover_url.isLocalFile()) {
cover_url = result.temp_cover_url;
}
else if (song.art_manual().isValid() && song.art_manual().isLocalFile() && song.art_manual().path() != Song::kManuallyUnsetCover && song.art_manual().path() != Song::kEmbeddedCover) {
else if (song.art_manual().isValid() && song.art_manual().isLocalFile()) {
cover_url = song.art_manual();
}
else if (song.art_automatic().isValid() && song.art_automatic().isLocalFile() && song.art_automatic().path() != Song::kManuallyUnsetCover && song.art_automatic().path() != Song::kEmbeddedCover) {
else if (song.art_automatic().isValid() && song.art_automatic().isLocalFile()) {
cover_url = song.art_automatic();
}
if (cover_url.isValid()) AddMetadata("mpris:artUrl", cover_url.toString(), &last_metadata_);
if (cover_url.isValid()) {
AddMetadata("mpris:artUrl", cover_url.toString(), &last_metadata_);
}
AddMetadata("year", song.year(), &last_metadata_);
AddMetadata("bitrate", song.bitrate(), &last_metadata_);

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2023, 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
@@ -109,10 +109,6 @@ class Song {
Stream = 91
};
Song(const Source source = Source::Unknown);
Song(const Song &other);
~Song();
static const QStringList kColumns;
static const QString kColumnSpec;
static const QString kBindSpec;
@@ -123,9 +119,6 @@ class Song {
static const QString kFtsBindSpec;
static const QString kFtsUpdateSpec;
static const QString kManuallyUnsetCover;
static const QString kEmbeddedCover;
static const QRegularExpression kAlbumRemoveDisc;
static const QRegularExpression kAlbumRemoveMisc;
static const QRegularExpression kTitleRemoveMisc;
@@ -134,77 +127,22 @@ class Song {
static const QStringList kAcceptedExtensions;
static QString JoinSpec(const QString &table);
Song(const Source source = Source::Unknown);
Song(const Song &other);
~Song();
static Source SourceFromURL(const QUrl &url);
static QString TextForSource(const Source source);
static QString DescriptionForSource(const Source source);
static Source SourceFromText(const QString &source);
static QIcon IconForSource(const Source source);
static QString TextForFiletype(const FileType filetype);
static QString ExtensionForFiletype(const FileType filetype);
static QIcon IconForFiletype(const FileType filetype);
QString TextForSource() const { return TextForSource(source()); }
QString DescriptionForSource() const { return DescriptionForSource(source()); }
QIcon IconForSource() const { return IconForSource(source()); }
QString TextForFiletype() const { return TextForFiletype(filetype()); }
QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
bool IsFileLossless() const;
static FileType FiletypeByMimetype(const QString &mimetype);
static FileType FiletypeByDescription(const QString &text);
static FileType FiletypeByExtension(const QString &ext);
static QString ImageCacheDir(const Source source);
// Sort songs alphabetically using their pretty title
static int CompareSongsName(const Song &song1, const Song &song2);
static void SortSongsListAlphabetically(QList<Song> *songs);
// Constructors
void Init(const QString &title, const QString &artist, const QString &album, const qint64 length_nanosec);
void Init(const QString &title, const QString &artist, const QString &album, const qint64 beginning, const qint64 end);
void InitFromProtobuf(const spb::tagreader::SongMetadata &pb);
void InitFromQuery(const SqlRow &query, const bool reliable_metadata);
void InitFromFilePartial(const QString &filename, const QFileInfo &fileinfo);
void InitArtManual();
void InitArtAutomatic();
bool MergeFromEngineMetadata(const EngineMetadata &engine_metadata);
#ifdef HAVE_LIBGPOD
void InitFromItdb(_Itdb_Track *track, const QString &prefix);
void ToItdb(_Itdb_Track *track) const;
#endif
#ifdef HAVE_LIBMTP
void InitFromMTP(const LIBMTP_track_struct *track, const QString &host);
void ToMTP(LIBMTP_track_struct *track) const;
#endif
// Copies important statistics from the other song to this one, overwriting any data that already exists.
// Useful when you want updated tags from disk but you want to keep user stats.
void MergeUserSetData(const Song &other, const bool merge_playcount, const bool merge_rating);
// Save
void BindToQuery(SqlQuery *query) const;
void BindToFtsQuery(SqlQuery *query) const;
void ToXesam(QVariantMap *map) const;
void ToProtobuf(spb::tagreader::SongMetadata *pb) const;
bool operator==(const Song &other) const;
bool operator!=(const Song &other) const;
Song &operator=(const Song &other);
// Simple accessors
bool is_valid() const;
bool is_unavailable() const;
int id() const;
bool is_valid() const;
const QString &title() const;
const QString &title_sortable() const;
const QString &album() const;
const QString &album_sortable() const;
const QString &artist() const;
const QString &artist_sortable() const;
const QString &albumartist() const;
const QString &albumartist_sortable() const;
int track() const;
int disc() const;
int year() const;
@@ -237,6 +175,7 @@ class Song {
qint64 filesize() const;
qint64 mtime() const;
qint64 ctime() const;
bool unavailable() const;
QString fingerprint() const;
@@ -246,14 +185,15 @@ class Song {
qint64 lastseen() const;
bool compilation_detected() const;
bool compilation_off() const;
bool compilation_on() const;
bool compilation_off() const;
bool art_embedded() const;
const QUrl &art_automatic() const;
const QUrl &art_manual() const;
bool art_unset() const;
const QString &cue_path() const;
bool has_cue() const;
float rating() const;
@@ -271,74 +211,16 @@ class Song {
const QString &musicbrainz_release_group_id() const;
const QString &musicbrainz_work_id() const;
const QString &effective_album() const;
int effective_originalyear() const;
const QString &effective_albumartist() const;
const QString &effective_albumartist_sortable() const;
bool is_collection_song() const;
bool is_stream() const;
bool is_radio() const;
bool is_cdda() const;
bool is_metadata_good() const;
bool art_automatic_is_valid() const;
bool art_manual_is_valid() const;
bool has_valid_art() const;
bool is_compilation() const;
bool stream_url_can_expire() const;
bool is_module_music() const;
// Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:
const QString &playlist_albumartist() const;
const QString &playlist_albumartist_sortable() const;
// Returns true if this Song had it's cover manually unset by user.
bool has_manually_unset_cover() const;
// This method represents an explicit request to unset this song's cover.
void set_manually_unset_cover();
// Returns true if this song (it's media file) has an embedded cover.
bool has_embedded_cover() const;
// Sets a flag saying that this song (it's media file) has an embedded cover.
void set_embedded_cover();
void clear_art_automatic();
void clear_art_manual();
static bool save_embedded_cover_supported(const FileType filetype);
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
bool additional_tags_supported() const;
bool albumartist_supported() const;
bool composer_supported() const;
bool performer_supported() const;
bool grouping_supported() const;
bool genre_supported() const;
bool compilation_supported() const;
bool rating_supported() const;
bool comment_supported() const;
bool lyrics_supported() const;
const QUrl &stream_url() const;
const QUrl &effective_stream_url() const;
bool init_from_file() const;
// Pretty accessors
QString PrettyTitle() const;
QString PrettyTitleWithArtist() const;
QString PrettyLength() const;
QString PrettyYear() const;
QString PrettyOriginalYear() const;
const QString &title_sortable() const;
const QString &album_sortable() const;
const QString &artist_sortable() const;
const QString &albumartist_sortable() const;
QString TitleWithCompilationArtist() const;
QString SampleRateBitDepthToText() const;
QString PrettyRating() const;
const QUrl &stream_url() const;
// Setters
bool IsEditable() const;
void set_id(const int id);
void set_valid(const bool v);
@@ -391,8 +273,10 @@ class Song {
void set_compilation_on(const bool v);
void set_compilation_off(const bool v);
void set_art_embedded(const bool v);
void set_art_automatic(const QUrl &v);
void set_art_manual(const QUrl &v);
void set_art_unset(const bool v);
void set_cue_path(const QString &v);
@@ -414,6 +298,61 @@ class Song {
void set_stream_url(const QUrl &v);
const QUrl &effective_stream_url() const;
const QString &effective_albumartist() const;
const QString &effective_albumartist_sortable() const;
const QString &effective_album() const;
int effective_originalyear() const;
const QString &playlist_albumartist() const;
const QString &playlist_albumartist_sortable() const;
bool is_metadata_good() const;
bool is_collection_song() const;
bool is_stream() const;
bool is_radio() const;
bool is_cdda() const;
bool is_compilation() const;
bool stream_url_can_expire() const;
bool is_module_music() const;
bool has_cue() const;
bool art_automatic_is_valid() const;
bool art_manual_is_valid() const;
bool has_valid_art() const;
void clear_art_automatic();
void clear_art_manual();
bool additional_tags_supported() const;
bool albumartist_supported() const;
bool composer_supported() const;
bool performer_supported() const;
bool grouping_supported() const;
bool genre_supported() const;
bool compilation_supported() const;
bool rating_supported() const;
bool comment_supported() const;
bool lyrics_supported() const;
static bool save_embedded_cover_supported(const FileType filetype);
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
static QString JoinSpec(const QString &table);
// Pretty accessors
QString PrettyTitle() const;
QString PrettyTitleWithArtist() const;
QString PrettyLength() const;
QString PrettyYear() const;
QString PrettyOriginalYear() const;
QString TitleWithCompilationArtist() const;
QString SampleRateBitDepthToText() const;
QString PrettyRating() const;
bool IsEditable() const;
// Comparison functions
bool IsMetadataEqual(const Song &other) const;
bool IsPlayStatisticsEqual(const Song &other) const;
@@ -427,16 +366,69 @@ class Song {
bool IsOnSameAlbum(const Song &other) const;
bool IsSimilar(const Song &other) const;
bool operator==(const Song &other) const;
bool operator!=(const Song &other) const;
static Source SourceFromURL(const QUrl &url);
static QString TextForSource(const Source source);
static QString DescriptionForSource(const Source source);
static Source SourceFromText(const QString &source);
static QIcon IconForSource(const Source source);
static QString TextForFiletype(const FileType filetype);
static QString ExtensionForFiletype(const FileType filetype);
static QIcon IconForFiletype(const FileType filetype);
QString TextForSource() const { return TextForSource(source()); }
QString DescriptionForSource() const { return DescriptionForSource(source()); }
QIcon IconForSource() const { return IconForSource(source()); }
QString TextForFiletype() const { return TextForFiletype(filetype()); }
QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
bool IsFileLossless() const;
static FileType FiletypeByMimetype(const QString &mimetype);
static FileType FiletypeByDescription(const QString &text);
static FileType FiletypeByExtension(const QString &ext);
static QString ImageCacheDir(const Source source);
// Sort songs alphabetically using their pretty title
static int CompareSongsName(const Song &song1, const Song &song2);
static void SortSongsListAlphabetically(QList<Song> *songs);
// Constructors
void Init(const QString &title, const QString &artist, const QString &album, const qint64 length_nanosec);
void Init(const QString &title, const QString &artist, const QString &album, const qint64 beginning, const qint64 end);
void InitFromProtobuf(const spb::tagreader::SongMetadata &pb);
void InitFromQuery(const SqlRow &query, const bool reliable_metadata);
void InitFromFilePartial(const QString &filename, const QFileInfo &fileinfo);
void InitArtManual();
void InitArtAutomatic();
#ifdef HAVE_LIBGPOD
void InitFromItdb(_Itdb_Track *track, const QString &prefix);
void ToItdb(_Itdb_Track *track) const;
#endif
#ifdef HAVE_LIBMTP
void InitFromMTP(const LIBMTP_track_struct *track, const QString &host);
void ToMTP(LIBMTP_track_struct *track) const;
#endif
// Save
void BindToQuery(SqlQuery *query) const;
void BindToFtsQuery(SqlQuery *query) const;
void ToXesam(QVariantMap *map) const;
void ToProtobuf(spb::tagreader::SongMetadata *pb) const;
bool MergeFromEngineMetadata(const EngineMetadata &engine_metadata);
// Copies important statistics from the other song to this one, overwriting any data that already exists.
// Useful when you want updated tags from disk but you want to keep user stats.
void MergeUserSetData(const Song &other, const bool merge_playcount, const bool merge_rating);
// Two songs that are on the same album will have the same AlbumKey.
// 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;
Song &operator=(const Song &other);
private:
static const QString kManuallyUnsetCover;
static const QString kEmbeddedCover;
struct Private;
static QString sortable(const QString &v);

View File

@@ -30,6 +30,7 @@
#include <QIcon>
#include "covermanager/albumcoverloader.h"
#include "covermanager/albumcoverloaderoptions.h"
#include "standarditemiconloader.h"
StandardItemIconLoader::StandardItemIconLoader(AlbumCoverLoader *cover_loader, QObject *parent)
@@ -37,8 +38,6 @@ StandardItemIconLoader::StandardItemIconLoader(AlbumCoverLoader *cover_loader, Q
cover_loader_(cover_loader),
model_(nullptr) {
cover_options_.desired_height_ = 16;
QObject::connect(cover_loader_, &AlbumCoverLoader::AlbumCoverLoaded, this, &StandardItemIconLoader::AlbumCoverLoaded);
}
@@ -58,14 +57,20 @@ void StandardItemIconLoader::SetModel(QAbstractItemModel *model) {
void StandardItemIconLoader::LoadIcon(const QUrl &art_automatic, const QUrl &art_manual, QStandardItem *for_item) {
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, art_automatic, art_manual);
pending_covers_[id] = for_item;
AlbumCoverLoaderOptions cover_options(AlbumCoverLoaderOptions::Option::ScaledImage);
cover_options.desired_scaled_size = QSize(16, 16);
const quint64 id = cover_loader_->LoadImageAsync(cover_options, false, art_automatic, art_manual, false);
pending_covers_.insert(id, for_item);
}
void StandardItemIconLoader::LoadIcon(const Song &song, QStandardItem *for_item) {
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, song);
pending_covers_[id] = for_item;
AlbumCoverLoaderOptions cover_options(AlbumCoverLoaderOptions::Option::ScaledImage);
cover_options.desired_scaled_size = QSize(16, 16);
const quint64 id = cover_loader_->LoadImageAsync(cover_options, song);
pending_covers_.insert(id, for_item);
}
void StandardItemIconLoader::RowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) {
@@ -76,7 +81,7 @@ void StandardItemIconLoader::RowsAboutToBeRemoved(const QModelIndex &parent, int
if (item_parent && item_parent->index() == parent && item->index().row() >= begin && item->index().row() <= end) {
cover_loader_->CancelTask(it.key());
it = pending_covers_.erase(it); // clazy:exclude=strict-iterators
it = pending_covers_.erase(it);
}
else {
++it;
@@ -103,7 +108,7 @@ void StandardItemIconLoader::AlbumCoverLoaded(const quint64 id, const AlbumCover
QStandardItem *item = pending_covers_.take(id);
if (!item) return;
if (result.success && !result.image_scaled.isNull() && result.type != AlbumCoverLoaderResult::Type::ManuallyUnset) {
if (result.success && !result.image_scaled.isNull() && result.type != AlbumCoverLoaderResult::Type::Unset) {
item->setIcon(QIcon(QPixmap::fromImage(result.image_scaled)));
}

View File

@@ -29,7 +29,6 @@
#include <QUrl>
#include <QImage>
#include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h"
class QAbstractItemModel;
@@ -38,8 +37,6 @@ class QStandardItem;
class Song;
class AlbumCoverLoader;
class QModelIndex;
// Uses an AlbumCoverLoader to asynchronously load and set an icon on a QStandardItem.
class StandardItemIconLoader : public QObject {
Q_OBJECT
@@ -47,8 +44,6 @@ class StandardItemIconLoader : public QObject {
public:
explicit StandardItemIconLoader(AlbumCoverLoader *cover_loader, QObject *parent = nullptr);
AlbumCoverLoaderOptions *options() { return &cover_options_; }
void SetModel(QAbstractItemModel *model);
void LoadIcon(const QUrl &art_automatic, const QUrl &art_manual, QStandardItem *for_item);
@@ -61,10 +56,7 @@ class StandardItemIconLoader : public QObject {
private:
AlbumCoverLoader *cover_loader_;
AlbumCoverLoaderOptions cover_options_;
QAbstractItemModel *model_;
QMap<quint64, QStandardItem*> pending_covers_;
};

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2023, 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
@@ -93,25 +93,30 @@ TagReaderReply *TagReaderClient::ReadFile(const QString &filename) {
}
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) {
TagReaderReply *TagReaderClient::SaveFile(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();
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);
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 = filename.toUtf8();
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_data.length() > 0) {
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
}
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());
}
metadata.ToProtobuf(request->mutable_metadata());
ReplyType *reply = worker_pool_->SendMessageWithReply(&message);
@@ -139,15 +144,17 @@ TagReaderReply *TagReaderClient::SaveEmbeddedArt(const QString &filename, const
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();
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_data.length() > 0) {
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
}
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());
}
return worker_pool_->SendMessageWithReply(&message);
@@ -225,13 +232,13 @@ void TagReaderClient::ReadFileBlocking(const QString &filename, Song *song) {
}
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) {
bool TagReaderClient::SaveFileBlocking(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
Q_ASSERT(QThread::currentThread() != thread());
bool ret = false;
TagReaderReply *reply = SaveFile(filename, metadata, save_tags, save_playcount, save_rating, save_cover_options);
TagReaderReply *reply = SaveFile(filename, metadata, save_types, save_cover_options);
if (reply->WaitForFinished()) {
ret = reply->message().save_file_response().success();
}

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2011, David Sansome <me@davidsansome.com>
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2023, 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
@@ -53,35 +53,28 @@ class TagReaderClient : public QObject {
void Start();
void ExitAsync();
enum class SaveTags {
Off,
On
};
enum class SavePlaycount {
Off,
On
};
enum class SaveRating {
Off,
On
enum class SaveType {
NoType = 0,
Tags = 1,
PlayCount = 2,
Rating = 4,
Cover = 8
};
Q_DECLARE_FLAGS(SaveTypes, SaveType)
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;
explicit SaveCoverOptions(const QString &_cover_filename = QString(), const QByteArray &_cover_data = QByteArray(), const QString &_mime_type = QString()) : cover_filename(_cover_filename), cover_data(_cover_data), mime_type(_mime_type) {}
explicit SaveCoverOptions(const QString &_cover_filename, const QString &_mime_type = QString()) : cover_filename(_cover_filename), mime_type(_mime_type) {}
explicit SaveCoverOptions(const QByteArray &_cover_data, const QString &_mime_type = QString()) : cover_data(_cover_data), mime_type(_mime_type) {}
QString cover_filename;
QByteArray cover_data;
QString mime_type;
};
ReplyType *IsMediaFile(const QString &filename);
ReplyType *ReadFile(const QString &filename);
ReplyType *SaveFile(const QString &filename, const Song &metadata, const SaveTags save_tags = SaveTags::On, const SavePlaycount save_playcount = SavePlaycount::Off, const SaveRating save_rating = SaveRating::Off, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
ReplyType *SaveFile(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);
@@ -90,7 +83,7 @@ class TagReaderClient : public QObject {
// 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 SaveTags save_tags = SaveTags::On, const SavePlaycount save_playcount = SavePlaycount::Off, const SaveRating save_rating = SaveRating::Off, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
bool SaveFileBlocking(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);