Allow changing the cover art from the edit tag dialog
This commit is contained in:
parent
4e2d82f3fb
commit
2a63a746fe
|
@ -44,6 +44,10 @@
|
|||
#include <QTimer>
|
||||
|
||||
const char* AlbumCoverManager::kSettingsGroup = "CoverManager";
|
||||
const char* AlbumCoverManager::kImageFileFilter =
|
||||
QT_TR_NOOP("Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm *.tiff)");
|
||||
const char* AlbumCoverManager::kAllFilesFilter =
|
||||
QT_TR_NOOP("All files (*)");
|
||||
|
||||
AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
|
||||
QNetworkAccessManager* network)
|
||||
|
@ -53,7 +57,7 @@ AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
|
|||
backend_(backend),
|
||||
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
|
||||
cover_fetcher_(new AlbumCoverFetcher(this, network)),
|
||||
cover_searcher_(new AlbumCoverSearcher(this)),
|
||||
cover_searcher_(NULL),
|
||||
artist_icon_(IconLoader::Load("x-clementine-artist")),
|
||||
all_artists_icon_(IconLoader::Load("x-clementine-album")),
|
||||
context_menu_(new QMenu(this)),
|
||||
|
@ -87,6 +91,8 @@ AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
|
|||
p.end();
|
||||
no_cover_icon_ = QPixmap::fromImage(square_nocover);
|
||||
|
||||
cover_searcher_ = new AlbumCoverSearcher(no_cover_icon_, this);
|
||||
|
||||
// Set up the status bar
|
||||
statusBar()->addPermanentWidget(progress_bar_);
|
||||
progress_bar_->hide();
|
||||
|
@ -387,10 +393,8 @@ void AlbumCoverManager::AlbumCoverFetched(quint64 id, const QImage &image) {
|
|||
UpdateStatusText();
|
||||
}
|
||||
|
||||
void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &image) {
|
||||
const QString artist = item->data(Role_ArtistName).toString();
|
||||
const QString album = item->data(Role_AlbumName).toString();
|
||||
|
||||
QString AlbumCoverManager::SaveCoverInCache(
|
||||
const QString& artist, const QString& album, const QImage& image) {
|
||||
// Hash the artist and album into a filename for the image
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
hash.addData(artist.toLower().toUtf8().constData());
|
||||
|
@ -406,6 +410,15 @@ void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &ima
|
|||
// Save the image to disk
|
||||
image.save(path, "JPG");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &image) {
|
||||
const QString artist = item->data(Role_ArtistName).toString();
|
||||
const QString album = item->data(Role_AlbumName).toString();
|
||||
|
||||
QString path = SaveCoverInCache(artist, album, image);
|
||||
|
||||
// Save the image in the database
|
||||
backend_->UpdateManualAlbumArtAsync(artist, album, path);
|
||||
|
||||
|
@ -514,8 +527,7 @@ void AlbumCoverManager::ChooseManualCover() {
|
|||
|
||||
QString cover = QFileDialog::getOpenFileName(
|
||||
this, tr("Choose manual cover"), dir,
|
||||
tr("Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm *.tiff)") + ";;" +
|
||||
tr("All files (*)"));
|
||||
tr(kImageFileFilter) + ";;" + tr(kAllFilesFilter));
|
||||
if (cover.isNull())
|
||||
return;
|
||||
|
||||
|
@ -536,10 +548,10 @@ void AlbumCoverManager::ChooseManualCover() {
|
|||
}
|
||||
|
||||
QString AlbumCoverManager::InitialPathForOpenCoverDialog(const QString& path_automatic, const QString& first_file_name) const {
|
||||
if(!path_automatic.isEmpty() && path_automatic != "embedded") {
|
||||
if(!path_automatic.isEmpty() && path_automatic != AlbumCoverLoader::kEmbeddedCover) {
|
||||
return path_automatic;
|
||||
} else if (!first_file_name.isEmpty() && first_file_name.contains('/')) {
|
||||
// we get rid of the filename because it's extension is screwing with the dialog's
|
||||
// we get rid of the filename because its extension is screwing with the dialog's
|
||||
// filters
|
||||
return first_file_name.section('/', 0, -2);
|
||||
} else {
|
||||
|
|
|
@ -47,6 +47,8 @@ class AlbumCoverManager : public QMainWindow {
|
|||
~AlbumCoverManager();
|
||||
|
||||
static const char* kSettingsGroup;
|
||||
static const char* kImageFileFilter;
|
||||
static const char* kAllFilesFilter;
|
||||
|
||||
LibraryBackend* backend() const { return backend_; }
|
||||
QIcon no_cover_icon() const { return no_cover_icon_; }
|
||||
|
@ -57,6 +59,9 @@ class AlbumCoverManager : public QMainWindow {
|
|||
SongList GetSongsInAlbum(const QModelIndex& index) const;
|
||||
SongList GetSongsInAlbums(const QModelIndexList& indexes) const;
|
||||
|
||||
static QString SaveCoverInCache(
|
||||
const QString& artist, const QString& album, const QImage& image);
|
||||
|
||||
signals:
|
||||
void AddSongsToPlaylist(const SongList& songs);
|
||||
void LoadSongsToPlaylist(const SongList& songs);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "albumcovermanager.h"
|
||||
#include "albumcoversearcher.h"
|
||||
#include "ui_albumcoversearcher.h"
|
||||
#include "core/albumcoverfetcher.h"
|
||||
|
@ -24,10 +23,10 @@
|
|||
#include <QKeyEvent>
|
||||
#include <QListWidgetItem>
|
||||
|
||||
AlbumCoverSearcher::AlbumCoverSearcher(AlbumCoverManager* parent)
|
||||
AlbumCoverSearcher::AlbumCoverSearcher(const QIcon& no_cover_icon, QWidget* parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_AlbumCoverSearcher),
|
||||
manager_(parent),
|
||||
no_cover_icon_(no_cover_icon),
|
||||
fetcher_(NULL),
|
||||
id_(0)
|
||||
{
|
||||
|
@ -62,7 +61,7 @@ QImage AlbumCoverSearcher::Exec(const QString &query) {
|
|||
return QImage();
|
||||
|
||||
QIcon icon = ui_->covers->currentItem()->icon();
|
||||
if (icon.cacheKey() == manager_->no_cover_icon().cacheKey())
|
||||
if (icon.cacheKey() == no_cover_icon_.cacheKey())
|
||||
return QImage();
|
||||
|
||||
return icon.pixmap(icon.availableSizes()[0]).toImage();
|
||||
|
@ -95,7 +94,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const AlbumCoverFetcher::Sea
|
|||
quint64 id = loader_->LoadImageAsync(result.image_url, QString());
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(ui_->covers);
|
||||
item->setIcon(manager_->no_cover_icon());
|
||||
item->setIcon(no_cover_icon_);
|
||||
item->setText(result.artist + " - " + result.album);
|
||||
item->setData(Role_ImageURL, result.image_url);
|
||||
item->setData(Role_ImageRequestId, id);
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
#include "core/albumcoverfetcher.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QIcon>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
class AlbumCoverLoader;
|
||||
class AlbumCoverManager;
|
||||
class Ui_AlbumCoverSearcher;
|
||||
|
||||
class QListWidgetItem;
|
||||
|
@ -35,7 +35,7 @@ class AlbumCoverSearcher : public QDialog {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AlbumCoverSearcher(AlbumCoverManager* parent);
|
||||
AlbumCoverSearcher(const QIcon& no_cover_icon, QWidget* parent);
|
||||
~AlbumCoverSearcher();
|
||||
|
||||
enum Role {
|
||||
|
@ -61,7 +61,7 @@ private slots:
|
|||
private:
|
||||
Ui_AlbumCoverSearcher* ui_;
|
||||
|
||||
AlbumCoverManager* manager_;
|
||||
QIcon no_cover_icon_;
|
||||
boost::shared_ptr<AlbumCoverLoader> loader_;
|
||||
AlbumCoverFetcher* fetcher_;
|
||||
|
||||
|
|
|
@ -15,15 +15,21 @@
|
|||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "albumcovermanager.h"
|
||||
#include "albumcoversearcher.h"
|
||||
#include "edittagdialog.h"
|
||||
#include "ui_edittagdialog.h"
|
||||
#include "core/albumcoverfetcher.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/utilities.h"
|
||||
#include "library/library.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "playlist/playlistdelegates.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFileDialog>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QtDebug>
|
||||
|
||||
const char* EditTagDialog::kHintText = QT_TR_NOOP("(different across multiple songs)");
|
||||
|
@ -31,14 +37,19 @@ const char* EditTagDialog::kHintText = QT_TR_NOOP("(different across multiple so
|
|||
EditTagDialog::EditTagDialog(QWidget* parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_EditTagDialog),
|
||||
backend_(NULL),
|
||||
ignore_edits_(false),
|
||||
cover_searcher_(new AlbumCoverSearcher(QIcon(":/nocover.png"), this)),
|
||||
cover_fetcher_(new AlbumCoverFetcher(this)),
|
||||
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
|
||||
cover_art_id_(0),
|
||||
ignore_edits_(false)
|
||||
cover_art_is_set_(false)
|
||||
{
|
||||
cover_loader_->Start(true);
|
||||
cover_loader_->Worker()->SetDefaultOutputImage(QImage(":nocover.png"));
|
||||
connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)),
|
||||
SLOT(ArtLoaded(quint64,QImage)));
|
||||
cover_searcher_->Init(cover_loader_->Worker(), cover_fetcher_);
|
||||
|
||||
ui_->setupUi(this);
|
||||
ui_->splitter->setSizes(QList<int>() << 200 << width() - 200);
|
||||
|
@ -70,6 +81,22 @@ EditTagDialog::EditTagDialog(QWidget* parent)
|
|||
connect(ui_->song_list->selectionModel(),
|
||||
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||
SLOT(SelectionChanged()));
|
||||
|
||||
// Set up the album cover menu
|
||||
cover_menu_ = new QMenu(this);
|
||||
choose_cover_ = cover_menu_->addAction(
|
||||
IconLoader::Load("document-open"), tr("Load cover from disk..."),
|
||||
this, SLOT(LoadCoverFromFile()));
|
||||
download_cover_ = cover_menu_->addAction(
|
||||
IconLoader::Load("download"), tr("Search for album covers..."),
|
||||
this, SLOT(SearchCover()));
|
||||
unset_cover_ = cover_menu_->addAction(
|
||||
IconLoader::Load("list-remove"), tr("Unset cover"),
|
||||
this, SLOT(UnsetCover()));
|
||||
show_cover_ = cover_menu_->addAction(
|
||||
IconLoader::Load("zoom-in"), tr("Show fullsize..."),
|
||||
this, SLOT(ZoomCover()));
|
||||
ui_->summary_art_button->setMenu(cover_menu_);
|
||||
}
|
||||
|
||||
EditTagDialog::~EditTagDialog() {
|
||||
|
@ -109,6 +136,7 @@ bool EditTagDialog::SetSongs(const SongList& s) {
|
|||
}
|
||||
|
||||
void EditTagDialog::SetTagCompleter(LibraryBackend* backend) {
|
||||
backend_ = backend;
|
||||
new TagCompleter(backend, Playlist::Column_Artist, ui_->artist);
|
||||
new TagCompleter(backend, Playlist::Column_Album, ui_->album);
|
||||
}
|
||||
|
@ -248,8 +276,10 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) {
|
|||
|
||||
QString summary = "<b>" + Qt::escape(song.PrettyTitleWithArtist()) + "</b><br/>";
|
||||
|
||||
bool art_is_set = true;
|
||||
if (song.art_manual() == AlbumCoverLoader::kManuallyUnsetCover) {
|
||||
summary += Qt::escape(tr("Cover art manually unset"));
|
||||
art_is_set = false;
|
||||
} else if (!song.art_manual().isEmpty()) {
|
||||
summary += Qt::escape(tr("Cover art set from %1").arg(song.art_manual()));
|
||||
} else if (song.art_automatic() == AlbumCoverLoader::kEmbeddedCover) {
|
||||
|
@ -258,10 +288,15 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) {
|
|||
summary += Qt::escape(tr("Cover art loaded automatically from %1").arg(song.art_manual()));
|
||||
} else {
|
||||
summary += Qt::escape(tr("Cover art not set"));
|
||||
art_is_set = false;
|
||||
}
|
||||
|
||||
ui_->summary->setText(summary);
|
||||
|
||||
unset_cover_->setEnabled(art_is_set);
|
||||
show_cover_->setEnabled(art_is_set);
|
||||
ui_->summary_art_button->setEnabled(song.id() != -1);
|
||||
|
||||
ui_->length->setText(Utilities::PrettyTime(song.length()));
|
||||
SetText(ui_->bpm, song.bpm(), tr("bpm"));
|
||||
SetText(ui_->samplerate, song.samplerate(), "Hz");
|
||||
|
@ -326,3 +361,87 @@ void EditTagDialog::ResetField() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::LoadCoverFromFile() {
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
const Song& song = data_[sel.first().row()].original_;
|
||||
|
||||
// Figure out the initial path. Logic copied from
|
||||
// AlbumCoverManager::InitialPathForOpenCoverDialog
|
||||
QString dir;
|
||||
if (!song.art_automatic().isEmpty() && song.art_automatic() != AlbumCoverLoader::kEmbeddedCover) {
|
||||
dir = song.art_automatic();
|
||||
} else {
|
||||
dir = song.filename().section('/', 0, -1);
|
||||
}
|
||||
|
||||
QString cover = QFileDialog::getOpenFileName(
|
||||
this, tr("Choose manual cover"), dir,
|
||||
tr(AlbumCoverManager::kImageFileFilter) + ";;" + tr(AlbumCoverManager::kAllFilesFilter));
|
||||
if (cover.isNull())
|
||||
return;
|
||||
|
||||
// Can we load the image?
|
||||
QImage image(cover);
|
||||
if (image.isNull())
|
||||
return;
|
||||
|
||||
// Update database
|
||||
SetAlbumArt(cover);
|
||||
}
|
||||
|
||||
void EditTagDialog::SearchCover() {
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
const Song& song = data_[sel.first().row()].original_;
|
||||
|
||||
// Get something sensible to stick in the search box
|
||||
QString query = song.artist();
|
||||
if (!query.isEmpty())
|
||||
query += " ";
|
||||
query += song.album();
|
||||
|
||||
QImage image = cover_searcher_->Exec(query);
|
||||
if (image.isNull())
|
||||
return;
|
||||
|
||||
SetAlbumArt(AlbumCoverManager::SaveCoverInCache(song.artist(), song.album(), image));
|
||||
}
|
||||
|
||||
void EditTagDialog::UnsetCover() {
|
||||
SetAlbumArt(AlbumCoverLoader::kManuallyUnsetCover);
|
||||
}
|
||||
|
||||
void EditTagDialog::ZoomCover() {
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
const Song& song = data_[sel.first().row()].original_;
|
||||
|
||||
QDialog* dialog = new QDialog(this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
dialog->setWindowTitle(song.title());
|
||||
|
||||
QLabel* label = new QLabel(dialog);
|
||||
label->setPixmap(AlbumCoverLoader::TryLoadPixmap(
|
||||
song.art_automatic(), song.art_manual()));
|
||||
|
||||
dialog->resize(label->pixmap()->size());
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void EditTagDialog::SetAlbumArt(const QString& path) {
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
Song& song = data_[sel.first().row()].original_;
|
||||
if (!song.is_valid() || song.id() == -1)
|
||||
return;
|
||||
|
||||
song.set_art_manual(path);
|
||||
backend_->UpdateManualAlbumArtAsync(song.artist(), song.album(), path);
|
||||
UpdateSummaryTab(song);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
#include "core/song.h"
|
||||
#include "widgets/lineedit.h"
|
||||
|
||||
class AlbumCoverFetcher;
|
||||
class AlbumCoverLoader;
|
||||
class AlbumCoverSearcher;
|
||||
class LibraryBackend;
|
||||
class Ui_EditTagDialog;
|
||||
|
||||
|
@ -51,6 +53,11 @@ private slots:
|
|||
|
||||
void ArtLoaded(quint64 id, const QImage& image);
|
||||
|
||||
void LoadCoverFromFile();
|
||||
void SearchCover();
|
||||
void UnsetCover();
|
||||
void ZoomCover();
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
Data(const Song& song = Song()) : original_(song), current_(song) {}
|
||||
|
@ -85,16 +92,28 @@ private:
|
|||
void UpdateSummaryTab(const Song& song);
|
||||
void UpdateStatisticsTab(const Song& song);
|
||||
|
||||
void SetAlbumArt(const QString& path);
|
||||
|
||||
private:
|
||||
Ui_EditTagDialog* ui_;
|
||||
|
||||
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||
quint64 cover_art_id_;
|
||||
LibraryBackend* backend_;
|
||||
|
||||
QList<Data> data_;
|
||||
QList<FieldData> fields_;
|
||||
|
||||
bool ignore_edits_;
|
||||
|
||||
AlbumCoverSearcher* cover_searcher_;
|
||||
AlbumCoverFetcher* cover_fetcher_;
|
||||
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||
quint64 cover_art_id_;
|
||||
bool cover_art_is_set_;
|
||||
|
||||
QMenu* cover_menu_;
|
||||
QAction* choose_cover_;
|
||||
QAction* download_cover_;
|
||||
QAction* unset_cover_;
|
||||
QAction* show_cover_;
|
||||
};
|
||||
|
||||
#endif // EDITTAGDIALOG_H
|
||||
|
|
Loading…
Reference in New Issue