From 2211716d0403ac8ab97b42756fe7607379634c68 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 11 Mar 2019 23:07:11 +0100 Subject: [PATCH] Add option to save album cover in album directory --- src/core/mainwindow.cpp | 3 +- .../albumcoverchoicecontroller.cpp | 119 +++++++++++++++--- src/covermanager/albumcoverchoicecontroller.h | 19 ++- src/covermanager/albumcovermanager.cpp | 12 +- src/covermanager/albumcovermanager.h | 1 + src/dialogs/edittagdialog.cpp | 2 +- src/settings/collectionsettingspage.cpp | 99 +++++++++++---- src/settings/collectionsettingspage.h | 6 + src/settings/collectionsettingspage.ui | 102 +++++++++++++-- 9 files changed, 308 insertions(+), 55 deletions(-) diff --git a/src/core/mainwindow.cpp b/src/core/mainwindow.cpp index 2d9dc039f..2a392c05c 100644 --- a/src/core/mainwindow.cpp +++ b/src/core/mainwindow.cpp @@ -860,6 +860,7 @@ void MainWindow::ReloadAllSettings() { osd_->ReloadSettings(); collection_view_->ReloadSettings(); ui_->playlist->view()->ReloadSettings(); + if (cover_manager_.get()) cover_manager_->ReloadSettings(); #ifdef HAVE_STREAM_TIDAL tidal_search_view_->ReloadSettings(); #endif @@ -2326,7 +2327,7 @@ void MainWindow::SearchForCover() { } void MainWindow::SaveCoverToFile() { - album_cover_choice_controller_->SaveCoverToFile(song_, image_original_); + album_cover_choice_controller_->SaveCoverToFileManual(song_, image_original_); } void MainWindow::UnsetCover() { diff --git a/src/covermanager/albumcoverchoicecontroller.cpp b/src/covermanager/albumcoverchoicecontroller.cpp index cd967c409..1ff61dd03 100644 --- a/src/covermanager/albumcoverchoicecontroller.cpp +++ b/src/covermanager/albumcoverchoicecontroller.cpp @@ -51,6 +51,8 @@ #include "core/application.h" #include "collection/collectionbackend.h" +#include "settings/collectionsettingspage.h" +#include "organise/organiseformat.h" #include "albumcoverchoicecontroller.h" #include "albumcoverfetcher.h" #include "albumcoverloader.h" @@ -70,7 +72,13 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent) : cover_searcher_(nullptr), cover_fetcher_(nullptr), save_file_dialog_(nullptr), - cover_from_url_dialog_(nullptr) { + cover_from_url_dialog_(nullptr), + cover_album_dir_(false), + cover_filename_(CollectionSettingsPage::SaveCover_Hash), + cover_overwrite_(false), + cover_lowercase_(true), + cover_replace_spaces_(true) + { cover_from_file_ = new QAction(IconLoader::Load("document-open"), tr("Load cover from disk..."), this); cover_to_file_ = new QAction(IconLoader::Load("document-save"), tr("Save cover to disk..."), this); @@ -90,6 +98,20 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent) : AlbumCoverChoiceController::~AlbumCoverChoiceController() {} +void AlbumCoverChoiceController::ReloadSettings() { + + QSettings s; + s.beginGroup(CollectionSettingsPage::kSettingsGroup); + cover_album_dir_ = s.value("cover_album_dir", false).toBool(); + cover_filename_ = CollectionSettingsPage::SaveCover(s.value("cover_filename", CollectionSettingsPage::SaveCover_Hash).toInt()); + cover_pattern_ = s.value("cover_pattern", "%albumartist-%album").toString(); + cover_overwrite_ = s.value("cover_overwrite", false).toBool(); + cover_lowercase_ = s.value("cover_lowercase", false).toBool(); + cover_replace_spaces_ = s.value("cover_replace_spaces", false).toBool(); + s.endGroup(); + +} + void AlbumCoverChoiceController::SetApplication(Application *app) { app_ = app; @@ -125,9 +147,17 @@ QString AlbumCoverChoiceController::LoadCoverFromFile(Song *song) { } -void AlbumCoverChoiceController::SaveCoverToFile(const Song &song, const QImage &image) { +void AlbumCoverChoiceController::SaveCoverToFileManual(const Song &song, const QImage &image) { - QString initial_file_name = "/" + (song.effective_album().isEmpty() ? tr("Unknown") : song.effective_album()) + ".jpg"; + QString initial_file_name = "/"; + + if (!song.effective_albumartist().isEmpty()) { + initial_file_name = initial_file_name + song.effective_albumartist(); + } + initial_file_name = initial_file_name + "-" + (song.effective_album().isEmpty() ? tr("unknown") : song.effective_album()) + ".jpg"; + initial_file_name = initial_file_name.toLower(); + initial_file_name.replace(QRegExp("\\s"), "-"); + initial_file_name.remove(OrganiseFormat::kValidFatCharacters); QString save_filename = QFileDialog::getSaveFileName(this, tr("Save album cover"), GetInitialPathForFileDialog(song, initial_file_name), tr(kSaveImageFileFilter) + ";;" + tr(kAllFilesFilter)); @@ -168,9 +198,9 @@ QString AlbumCoverChoiceController::LoadCoverFromURL(Song *song) { QImage image = cover_from_url_dialog_->Exec(); if (!image.isNull()) { - QString cover = SaveCoverInCache(song->artist(), song->album(), image); + QString cover = SaveCoverToFileAutomatic(song, image); + if (cover.isEmpty()) return QString(); SaveCover(song, cover); - return cover; } else { return QString(); } @@ -187,7 +217,8 @@ QString AlbumCoverChoiceController::SearchForCover(Song *song) { QImage image = cover_searcher_->Exec(song->effective_albumartist(), album); if (!image.isNull()) { - QString cover = SaveCoverInCache(song->artist(), song->album(), image); + QString cover = SaveCoverToFileAutomatic(song, image); + if (cover.isEmpty()) return QString(); SaveCover(song, cover); return cover; @@ -282,7 +313,8 @@ void AlbumCoverChoiceController::AlbumCoverFetched(quint64 id, const QImage &ima } if (!image.isNull()) { - QString cover = SaveCoverInCache(song.artist(), song.album(), image); + QString cover = SaveCoverToFileAutomatic(&song, image); + if (cover.isEmpty()) return; SaveCover(&song, cover); } @@ -303,23 +335,71 @@ void AlbumCoverChoiceController::SaveCover(Song *song, const QString &cover) { } -QString AlbumCoverChoiceController::SaveCoverInCache(const QString &artist, const QString &album, const QImage &image) { +QString AlbumCoverChoiceController::SaveCoverToFileAutomatic(const Song *song, const QImage &image) { - QString album2(album); - album2.remove(Song::kAlbumRemoveDisc); + QString albumartist(song->effective_albumartist()); + QString artist(song->artist()); + QString album(song->effective_album()); + album.remove(Song::kAlbumRemoveDisc); - // Hash the artist and album into a filename for the image - QString filename(Utilities::Sha1CoverHash(artist, album2).toHex() + ".jpg"); - QString path(AlbumCoverLoader::ImageCacheDir() + "/" + filename); + return SaveCoverToFileAutomatic(albumartist, artist, album, song->url().adjusted(QUrl::RemoveFilename).path(), image); + +} + +QString AlbumCoverChoiceController::SaveCoverToFileAutomatic(const QString &albumartist, const QString &artist, const QString &album, const QString &album_dir, const QImage &image) { + + QString album_new(album); + album_new.remove(Song::kAlbumRemoveDisc); + + QString path; + QString filename; + if (cover_album_dir_) { + path = album_dir; + } + else { + path = AlbumCoverLoader::ImageCacheDir(); + } + + if (path.right(1) == QDir::separator()) { + path.chop(1); + } - // Make sure this directory exists first QDir dir; - dir.mkdir(AlbumCoverLoader::ImageCacheDir()); + if (!dir.mkpath(path)) { + qLog(Error) << "Unable to create directory" << path; + return QString(); + } - // Save the image to disk - image.save(path, "JPG"); + if (cover_album_dir_ && cover_filename_ == CollectionSettingsPage::SaveCover_Pattern && !cover_pattern_.isEmpty()) { + filename = CreateCoverFilename(albumartist, artist, album_new) + ".jpg"; + filename.remove(OrganiseFormat::kValidFatCharacters); + if (cover_lowercase_) filename = filename.toLower(); + if (cover_replace_spaces_) filename.replace(QRegExp("\\s"), "-"); + } + else { + filename = Utilities::Sha1CoverHash(albumartist, album_new).toHex() + ".jpg"; + } - return path; + QString filepath(path + "/" + filename); + + // Don't overwrite when saving in album dir if the filename is set to pattern unless the "cover_overwrite" is set. + if (QFile::exists(filepath) && !cover_overwrite_ && cover_album_dir_ && cover_filename_ == CollectionSettingsPage::SaveCover_Pattern) { + return filepath; + } + + image.save(filepath, "JPG"); + + return filepath; + +} + +QString AlbumCoverChoiceController::CreateCoverFilename(const QString &albumartist, const QString &artist, const QString &album) { + + QString filename(cover_pattern_); + filename.replace("%albumartist", albumartist); + filename.replace("%artist", artist); + filename.replace("%album", album); + return filename; } @@ -363,7 +443,8 @@ QString AlbumCoverChoiceController::SaveCover(Song *song, const QDropEvent *e) { if (e->mimeData()->hasImage()) { QImage image = qvariant_cast(e->mimeData()->imageData()); if (!image.isNull()) { - QString cover_path = SaveCoverInCache(song->artist(), song->album(), image); + QString cover_path = SaveCoverToFileAutomatic(song, image); + if (cover_path.isEmpty()) return QString(); SaveCover(song, cover_path); return cover_path; } diff --git a/src/covermanager/albumcoverchoicecontroller.h b/src/covermanager/albumcoverchoicecontroller.h index 81bcd1414..de0dc1168 100644 --- a/src/covermanager/albumcoverchoicecontroller.h +++ b/src/covermanager/albumcoverchoicecontroller.h @@ -37,6 +37,8 @@ #include #include +#include "settings/collectionsettingspage.h" + class Song; class Application; class AlbumCoverFetcher; @@ -57,6 +59,7 @@ class AlbumCoverChoiceController : public QWidget { ~AlbumCoverChoiceController(); void SetApplication(Application *app); + void ReloadSettings(); // Getters for all QActions implemented by this controller. @@ -86,7 +89,7 @@ class AlbumCoverChoiceController : public QWidget { // Shows a dialog that allows user to save the given image on disk. // The image is supposed to be the cover of the given song's album. - void SaveCoverToFile(const Song &song, const QImage &image); + void SaveCoverToFileManual(const Song &song, const QImage &image); // Downloads the cover from an URL given by user. // This returns the downloaded image or null image if something went wrong for example when user cancelled the dialog. @@ -113,8 +116,10 @@ class AlbumCoverChoiceController : public QWidget { // Saves the cover that the user picked through a drag and drop operation. QString SaveCover(Song *song, const QDropEvent *e); - // Saves the given image in cache as a cover for 'artist' - 'album'. The method returns path of the cached image. - QString SaveCoverInCache(const QString &artist, const QString &album, const QImage &image); + // Saves the given image in album directory or cache as a cover for 'album artist' - 'album'. The method returns path of the image. + QString SaveCoverToFileAutomatic(const QString &albumartist, const QString &artist, const QString &album, const QString &album_dir, const QImage &image); + QString SaveCoverToFileAutomatic(const Song *song, const QImage &image); + QString CreateCoverFilename(const QString &albumartist, const QString &artist, const QString &album); static bool CanAcceptDrag(const QDragEnterEvent *e); @@ -147,6 +152,14 @@ signals: QAction *search_cover_auto_; QMap cover_fetching_tasks_; + + bool cover_album_dir_; + CollectionSettingsPage::SaveCover cover_filename_; + QString cover_pattern_; + bool cover_overwrite_; + bool cover_lowercase_; + bool cover_replace_spaces_; + }; #endif // ALBUMCOVERCHOICECONTROLLER_H diff --git a/src/covermanager/albumcovermanager.cpp b/src/covermanager/albumcovermanager.cpp index 69880158f..81ac32c4f 100644 --- a/src/covermanager/albumcovermanager.cpp +++ b/src/covermanager/albumcovermanager.cpp @@ -138,6 +138,8 @@ AlbumCoverManager::AlbumCoverManager(Application *app, CollectionBackend *collec EnableCoversButtons(); + ReloadSettings(); + } AlbumCoverManager::~AlbumCoverManager() { @@ -145,6 +147,10 @@ AlbumCoverManager::~AlbumCoverManager() { delete ui_; } +void AlbumCoverManager::ReloadSettings() { + album_cover_choice_controller_->ReloadSettings(); +} + CollectionBackend *AlbumCoverManager::backend() const { return collection_backend_; } @@ -629,7 +635,7 @@ void AlbumCoverManager::SaveCoverToFile() { } } - album_cover_choice_controller_->SaveCoverToFile(song, image); + album_cover_choice_controller_->SaveCoverToFileManual(song, image); } @@ -777,8 +783,10 @@ void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &ima const QString artist = item->data(Role_ArtistName).toString(); const QString albumartist = item->data(Role_AlbumArtistName).toString(); const QString album = item->data(Role_AlbumName).toString(); + const QUrl url = item->data(Role_FirstUrl).toUrl(); - QString path = album_cover_choice_controller_->SaveCoverInCache(artist, album, image); + QString path = album_cover_choice_controller_->SaveCoverToFileAutomatic((!albumartist.isEmpty() ? albumartist : artist), artist, album, url.adjusted(QUrl::RemoveFilename).path(), image); + if (path.isEmpty()) return; // Save the image in the database collection_backend_->UpdateManualAlbumArtAsync(artist, albumartist, album, path); diff --git a/src/covermanager/albumcovermanager.h b/src/covermanager/albumcovermanager.h index 3d649c715..9b51793df 100644 --- a/src/covermanager/albumcovermanager.h +++ b/src/covermanager/albumcovermanager.h @@ -72,6 +72,7 @@ class AlbumCoverManager : public QMainWindow { void Reset(); void Init(); + void ReloadSettings(); void EnableCoversButtons(); void DisableCoversButtons(); diff --git a/src/dialogs/edittagdialog.cpp b/src/dialogs/edittagdialog.cpp index ab5a39727..6a063c92d 100644 --- a/src/dialogs/edittagdialog.cpp +++ b/src/dialogs/edittagdialog.cpp @@ -634,7 +634,7 @@ void EditTagDialog::SaveCoverToFile() { Song *song = GetFirstSelected(); if (!song) return; - album_cover_choice_controller_->SaveCoverToFile(*song, original_); + album_cover_choice_controller_->SaveCoverToFileManual(*song, original_); } diff --git a/src/settings/collectionsettingspage.cpp b/src/settings/collectionsettingspage.cpp index 0d78bdd26..781a06c1a 100644 --- a/src/settings/collectionsettingspage.cpp +++ b/src/settings/collectionsettingspage.cpp @@ -43,10 +43,12 @@ const char *CollectionSettingsPage::kSettingsGroup = "Collection"; -CollectionSettingsPage::CollectionSettingsPage(SettingsDialog* dialog) +CollectionSettingsPage::CollectionSettingsPage(SettingsDialog *dialog) : SettingsPage(dialog), ui_(new Ui_CollectionSettingsPage), - initialised_model_(false) { + initialised_model_(false) + { + ui_->setupUi(this); ui_->list->setItemDelegate(new NativeSeparatorsDelegate(this)); @@ -56,6 +58,11 @@ CollectionSettingsPage::CollectionSettingsPage(SettingsDialog* dialog) connect(ui_->add, SIGNAL(clicked()), SLOT(Add())); connect(ui_->remove, SIGNAL(clicked()), SLOT(Remove())); + + connect(ui_->checkbox_cover_album_dir, SIGNAL(toggled(bool)), SLOT(CoverSaveInAlbumDirChanged())); + connect(ui_->radiobutton_cover_hash, SIGNAL(toggled(bool)), SLOT(CoverSaveInAlbumDirChanged())); + connect(ui_->radiobutton_cover_pattern, SIGNAL(toggled(bool)), SLOT(CoverSaveInAlbumDirChanged())); + } CollectionSettingsPage::~CollectionSettingsPage() { delete ui_; } @@ -83,25 +90,6 @@ void CollectionSettingsPage::CurrentRowChanged(const QModelIndex& index) { ui_->remove->setEnabled(index.isValid()); } -void CollectionSettingsPage::Save() { - - QSettings s; - - s.beginGroup(kSettingsGroup); - s.setValue("auto_open", ui_->auto_open->isChecked()); - s.setValue("pretty_covers", ui_->pretty_covers->isChecked()); - s.setValue("show_dividers", ui_->show_dividers->isChecked()); - s.setValue("startup_scan", ui_->startup_scan->isChecked()); - s.setValue("monitor", ui_->monitor->isChecked()); - - QString filter_text = ui_->cover_art_patterns->text(); - QStringList filters = filter_text.split(',', QString::SkipEmptyParts); - s.setValue("cover_art_patterns", filters); - - s.endGroup(); - -} - void CollectionSettingsPage::Load() { if (!initialised_model_) { @@ -127,5 +115,74 @@ void CollectionSettingsPage::Load() { QStringList filters = s.value("cover_art_patterns", QStringList() << "front" << "cover").toStringList(); ui_->cover_art_patterns->setText(filters.join(",")); + ui_->checkbox_cover_album_dir->setChecked(s.value("cover_album_dir", false).toBool()); + SaveCover save_cover = SaveCover(s.value("cover_filename", SaveCover_Hash).toInt()); + switch (save_cover) { + case SaveCover_Hash: ui_->radiobutton_cover_hash->setChecked(true); break; + case SaveCover_Pattern: ui_->radiobutton_cover_pattern->setChecked(true); break; + } + QString cover_pattern = s.value("cover_pattern").toString(); + if (!cover_pattern.isEmpty()) ui_->lineedit_cover_pattern->setText(cover_pattern); + ui_->checkbox_cover_overwrite->setChecked(s.value("cover_overwrite", false).toBool()); + ui_->checkbox_cover_lowercase->setChecked(s.value("cover_lowercase", true).toBool()); + ui_->checkbox_cover_replace_spaces->setChecked(s.value("cover_replace_spaces", true).toBool()); + s.endGroup(); + +} + +void CollectionSettingsPage::Save() { + + QSettings s; + + s.beginGroup(kSettingsGroup); + s.setValue("auto_open", ui_->auto_open->isChecked()); + s.setValue("pretty_covers", ui_->pretty_covers->isChecked()); + s.setValue("show_dividers", ui_->show_dividers->isChecked()); + s.setValue("startup_scan", ui_->startup_scan->isChecked()); + s.setValue("monitor", ui_->monitor->isChecked()); + + QString filter_text = ui_->cover_art_patterns->text(); + QStringList filters = filter_text.split(',', QString::SkipEmptyParts); + s.setValue("cover_art_patterns", filters); + + s.setValue("cover_album_dir", ui_->checkbox_cover_album_dir->isChecked()); + SaveCover save_cover = SaveCover_Hash; + if (ui_->radiobutton_cover_hash->isChecked()) save_cover = SaveCover_Hash; + if (ui_->radiobutton_cover_pattern->isChecked()) save_cover = SaveCover_Pattern; + s.setValue("cover_filename", int(save_cover)); + s.setValue("cover_pattern", ui_->lineedit_cover_pattern->text()); + s.setValue("cover_overwrite", ui_->checkbox_cover_overwrite->isChecked()); + s.setValue("cover_lowercase", ui_->checkbox_cover_lowercase->isChecked()); + s.setValue("cover_replace_spaces", ui_->checkbox_cover_replace_spaces->isChecked()); + + s.endGroup(); + +} + +void CollectionSettingsPage::CoverSaveInAlbumDirChanged() { + + if (ui_->checkbox_cover_album_dir->isChecked()) { + if (!ui_->groupbox_cover_filename->isEnabled()) { + ui_->groupbox_cover_filename->setEnabled(true); + } + if (ui_->radiobutton_cover_pattern->isChecked()) { + if (!ui_->lineedit_cover_pattern->isEnabled()) ui_->lineedit_cover_pattern->setEnabled(true); + if (!ui_->checkbox_cover_overwrite->isEnabled()) ui_->checkbox_cover_overwrite->setEnabled(true); + if (!ui_->checkbox_cover_lowercase->isEnabled()) ui_->checkbox_cover_lowercase->setEnabled(true); + if (!ui_->checkbox_cover_replace_spaces->isEnabled()) ui_->checkbox_cover_replace_spaces->setEnabled(true); + } + else { + if (ui_->lineedit_cover_pattern->isEnabled()) ui_->lineedit_cover_pattern->setEnabled(false); + if (ui_->checkbox_cover_overwrite->isEnabled()) ui_->checkbox_cover_overwrite->setEnabled(false); + if (ui_->checkbox_cover_lowercase->isEnabled()) ui_->checkbox_cover_lowercase->setEnabled(false); + if (ui_->checkbox_cover_replace_spaces->isEnabled()) ui_->checkbox_cover_replace_spaces->setEnabled(false); + } + } + else { + if (ui_->groupbox_cover_filename->isEnabled()) { + ui_->groupbox_cover_filename->setEnabled(false); + } + } + } diff --git a/src/settings/collectionsettingspage.h b/src/settings/collectionsettingspage.h index ececc672a..913d78da1 100644 --- a/src/settings/collectionsettingspage.h +++ b/src/settings/collectionsettingspage.h @@ -43,6 +43,11 @@ public: static const char *kSettingsGroup; + enum SaveCover { + SaveCover_Hash = 1, + SaveCover_Pattern = 2 + }; + void Load(); void Save(); @@ -51,6 +56,7 @@ private slots: void Remove(); void CurrentRowChanged(const QModelIndex &index); + void CoverSaveInAlbumDirChanged(); private: Ui_CollectionSettingsPage *ui_; diff --git a/src/settings/collectionsettingspage.ui b/src/settings/collectionsettingspage.ui index e4f5920f4..5c67ebc36 100644 --- a/src/settings/collectionsettingspage.ui +++ b/src/settings/collectionsettingspage.ui @@ -7,7 +7,7 @@ 0 0 509 - 452 + 695 @@ -15,14 +15,14 @@ - + These folders will be scanned for music to make up your collection - + @@ -37,7 +37,7 @@ - + @@ -56,7 +56,7 @@ - + Qt::Vertical @@ -73,7 +73,7 @@ - + Automatic updating @@ -93,7 +93,7 @@ - + Preferred album art filenames (comma separated) @@ -111,7 +111,7 @@ If there are no matches then it will use the largest image in the directory. - + Display options @@ -140,6 +140,92 @@ If there are no matches then it will use the largest image in the directory. + + + + Saving album covers + + + + + + Save album covers in album directory + + + + + + + Filename: + + + + + + + + + Use hash + + + + + + + Use pattern + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + %albumartist-%album + + + + + + + Overwrite existing file + + + + + + + Lowercase filename + + + + + + + Replace spaces with dashes + + + + + + + + +