further refactoring of cover changing code (saving covers in controller)

This commit is contained in:
Paweł Bara 2011-01-24 17:53:31 +00:00
parent 7e5afd6bf1
commit ebfbdba8a9
8 changed files with 230 additions and 184 deletions

View File

@ -18,12 +18,14 @@
#include "core/albumcoverfetcher.h"
#include "core/albumcoverloader.h"
#include "library/librarybackend.h"
#include "ui/albumcoverchoicecontroller.h"
#include "ui/albumcovermanager.h"
#include "ui/albumcoversearcher.h"
#include "ui/coverfromurldialog.h"
#include "ui/iconloader.h"
#include <QCryptographicHash>
#include <QDialog>
#include <QFileDialog>
#include <QLabel>
@ -33,13 +35,14 @@ const char* AlbumCoverChoiceController::kImageFileFilter =
const char* AlbumCoverChoiceController::kAllFilesFilter =
QT_TR_NOOP("All files (*)");
AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget* parent)
AlbumCoverChoiceController::AlbumCoverChoiceController(LibraryBackend* library, QWidget* parent)
: QWidget(parent),
#ifdef HAVE_LIBLASTFM
cover_searcher_(new AlbumCoverSearcher(QIcon(":/nocover.png"), this)),
cover_fetcher_(new AlbumCoverFetcher(this)),
#endif
cover_from_url_dialog_(NULL)
cover_from_url_dialog_(NULL),
library_(library)
{
#ifdef HAVE_LIBLASTFM
cover_searcher_->Init(cover_fetcher_);
@ -48,21 +51,17 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget* parent)
AlbumCoverChoiceController::~AlbumCoverChoiceController()
{
if(cover_from_url_dialog_) {
delete cover_from_url_dialog_;
}
}
QString AlbumCoverChoiceController::LoadCoverFromFile(const Song& song) {
#ifdef HAVE_LIBLASTFM
QString AlbumCoverChoiceController::LoadCoverFromFile(Song* song) {
QString dir;
if (!song.art_automatic().isEmpty() && song.art_automatic() != AlbumCoverLoader::kEmbeddedCover) {
dir = song.art_automatic();
} else if (!song.filename().isEmpty() && song.filename().contains('/')) {
if (!song->art_automatic().isEmpty() && song->art_automatic() != AlbumCoverLoader::kEmbeddedCover) {
dir = song->art_automatic();
} else if (!song->filename().isEmpty() && song->filename().contains('/')) {
// we get rid of the filename because it's extension is screwing with the dialog's
// filters
dir = song.filename().section('/', 0, -2);
dir = song->filename().section('/', 0, -2);
} else {
dir = "";
}
@ -72,45 +71,64 @@ QString AlbumCoverChoiceController::LoadCoverFromFile(const Song& song) {
tr(kImageFileFilter) + ";;" + tr(kAllFilesFilter));
if (cover.isNull())
return "";
return QString();
// Can we load the image?
QImage image(cover);
if (image.isNull())
return "";
return cover;
#else
return "";
#endif
if(!image.isNull()) {
SaveCover(song, cover);
return cover;
} else {
return QString();
}
}
QImage AlbumCoverChoiceController::LoadCoverFromURL() {
QString AlbumCoverChoiceController::LoadCoverFromURL(Song* song) {
if(!cover_from_url_dialog_) {
cover_from_url_dialog_ = new CoverFromURLDialog(this);
}
QImage image = cover_from_url_dialog_->Exec();
return image;
if(!image.isNull()) {
QString cover = SaveCoverInCache(song->artist(), song->album(), image);
SaveCover(song, cover);
return cover;
} else {
return QString();
}
}
QImage AlbumCoverChoiceController::SearchForCover(const Song& song) const {
QString AlbumCoverChoiceController::SearchForCover(Song* song) {
#ifdef HAVE_LIBLASTFM
// Get something sensible to stick in the search box
QString query = song.artist();
QString query = song->artist();
if (!query.isEmpty())
query += " ";
query += song.album();
query += song->album();
QImage image = cover_searcher_->Exec(query);
return image;
if(!image.isNull()) {
QString cover = SaveCoverInCache(song->artist(), song->album(), image);
SaveCover(song, cover);
return cover;
} else {
return QString();
}
#else
return QImage();
return QString();
#endif
}
QString AlbumCoverChoiceController::UnsetCover() const {
return AlbumCoverLoader::kManuallyUnsetCover;
QString AlbumCoverChoiceController::UnsetCover(Song* song) {
QString cover = AlbumCoverLoader::kManuallyUnsetCover;
SaveCover(song, cover);
return cover;
}
void AlbumCoverChoiceController::ShowCover(const Song& song) {
@ -125,3 +143,32 @@ void AlbumCoverChoiceController::ShowCover(const Song& song) {
dialog->resize(label->pixmap()->size());
dialog->show();
}
void AlbumCoverChoiceController::SaveCover(Song* song, const QString &cover) {
if(song->is_valid() && song->id() != -1) {
song->set_art_manual(cover);
library_->UpdateManualAlbumArtAsync(song->artist(), song->album(), cover);
}
}
QString AlbumCoverChoiceController::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());
hash.addData(album.toLower().toUtf8().constData());
QString filename = hash.result().toHex() + ".jpg";
QString path = AlbumCoverLoader::ImageCacheDir() + "/" + filename;
// Make sure this directory exists first
QDir dir;
dir.mkdir(AlbumCoverLoader::ImageCacheDir());
// Save the image to disk
image.save(path, "JPG");
return path;
}

View File

@ -23,6 +23,7 @@
class AlbumCoverFetcher;
class AlbumCoverSearcher;
class CoverFromURLDialog;
class LibraryBackend;
class Song;
// Controller for the common album cover related menu options. This includes:
@ -35,42 +36,54 @@ class AlbumCoverChoiceController : public QWidget {
Q_OBJECT
public:
AlbumCoverChoiceController(QWidget* parent = 0);
AlbumCoverChoiceController(LibraryBackend* library, QWidget* parent = 0);
~AlbumCoverChoiceController();
static const char* kImageFileFilter;
static const char* kAllFilesFilter;
// Some of the methods below require a currently selected song as an
// input parameter.
// All of the methods below require a currently selected song as an
// input parameter. Also - LoadCoverFromFile, LoadCoverFromURL,
// SearchForCover, UnsetCover and SaveCover all update manual path
// of the given song in library to the new cover.
// Let's the user choose a cover from disk. If no cover will be chosen or the chosen
// Lets the user choose a cover from disk. If no cover will be chosen or the chosen
// cover will not be a proper image, this returns an empty string. Otherwise, the
// path to the chosen cover will be returned.
QString LoadCoverFromFile(const Song& song);
QString LoadCoverFromFile(Song* song);
// 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.
QImage LoadCoverFromURL();
QString LoadCoverFromURL(Song* song);
// Lets the user choose a cover among all that have been found on last.fm.
// Returns the chosen cover or null cover if user didn't choose anything.
QImage SearchForCover(const Song& song) const;
QString SearchForCover(Song* song);
// Returns a path which indicates that the cover has been unset manually.
QString UnsetCover() const;
QString UnsetCover(Song* song);
// Shows the cover of given song in it's original size.
void ShowCover(const Song& song);
// Saves the chosen cover as manual cover path of this song in library.
void SaveCover(Song* song, const QString& cover);
// 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);
private:
#ifdef HAVE_LIBLASTFM
AlbumCoverSearcher* cover_searcher_;
AlbumCoverFetcher* cover_fetcher_;
#endif
CoverFromURLDialog* cover_from_url_dialog_;
LibraryBackend* library_;
};
#endif // ALBUMCOVERCHOICECONTROLLER_H

View File

@ -29,8 +29,6 @@
#include <QActionGroup>
#include <QContextMenuEvent>
#include <QCryptographicHash>
#include <QDir>
#include <QEvent>
#include <QFileDialog>
#include <QKeySequence>
@ -52,7 +50,7 @@ AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
: QMainWindow(parent),
constructed_(false),
ui_(new Ui_CoverManager),
album_cover_choice_controller_(new AlbumCoverChoiceController(this)),
album_cover_choice_controller_(new AlbumCoverChoiceController(backend, this)),
backend_(backend),
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
cover_fetcher_(new AlbumCoverFetcher(this, network)),
@ -394,41 +392,6 @@ void AlbumCoverManager::AlbumCoverFetched(quint64 id, const QImage &image) {
UpdateStatusText();
}
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());
hash.addData(album.toLower().toUtf8().constData());
QString filename = hash.result().toHex() + ".jpg";
QString path = AlbumCoverLoader::ImageCacheDir() + "/" + filename;
// Make sure this directory exists first
QDir dir;
dir.mkdir(AlbumCoverLoader::ImageCacheDir());
// 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);
// Update the icon in our list
quint64 id = cover_loader_->Worker()->LoadImageAsync(QString(), path);
item->setData(Role_PathManual, path);
cover_loading_tasks_[id] = item;
}
void AlbumCoverManager::UpdateStatusText() {
QString message = tr("Got %1 covers out of %2 (%3 failed)")
.arg(got_covers_).arg(jobs_).arg(missing_covers_);
@ -513,7 +476,10 @@ Song AlbumCoverManager::ItemAsSong(QListWidgetItem* item) {
result.set_art_automatic(item->data(Role_PathAutomatic).toString());
result.set_art_manual(item->data(Role_PathManual).toString());
// force validity
result.set_valid(true);
result.set_id(0);
return result;
}
@ -538,6 +504,13 @@ void AlbumCoverManager::FetchSingleCover() {
UpdateStatusText();
}
void AlbumCoverManager::UpdateCoverInList(QListWidgetItem* item, const QString& cover) {
quint64 id = cover_loader_->Worker()->LoadImageAsync(QString(), cover);
item->setData(Role_PathManual, cover);
cover_loading_tasks_[id] = item;
}
void AlbumCoverManager::LoadCoverFromFile() {
Song song = GetSingleSelectionAsSong();
if(!song.is_valid())
@ -545,22 +518,11 @@ void AlbumCoverManager::LoadCoverFromFile() {
QListWidgetItem* item = context_menu_items_[0];
QString cover = album_cover_choice_controller_->LoadCoverFromFile(song);
QString cover = album_cover_choice_controller_->LoadCoverFromFile(&song);
// Can we load the image?
QImage image(cover);
if (image.isNull())
return;
// Update database
backend_->UpdateManualAlbumArtAsync(song.artist(),
song.album(),
cover);
// Update the icon in our list
quint64 id = cover_loader_->Worker()->LoadImageAsync(QString(), cover);
item->setData(Role_PathManual, cover);
cover_loading_tasks_[id] = item;
if (!cover.isEmpty()) {
UpdateCoverInList(item, cover);
}
}
void AlbumCoverManager::LoadCoverFromURL() {
@ -568,22 +530,57 @@ void AlbumCoverManager::LoadCoverFromURL() {
if(!song.is_valid())
return;
QImage image = album_cover_choice_controller_->LoadCoverFromURL();
if (image.isNull())
QListWidgetItem* item = context_menu_items_[0];
QString cover = album_cover_choice_controller_->LoadCoverFromURL(&song);
if (!cover.isEmpty()) {
UpdateCoverInList(item, cover);
}
}
void AlbumCoverManager::SearchForCover() {
Song song = GetFirstSelectedAsSong();
if(!song.is_valid())
return;
SaveAndSetCover(context_menu_items_[0], image);
QListWidgetItem* item = context_menu_items_[0];
QString cover = album_cover_choice_controller_->SearchForCover(&song);
if (cover.isEmpty())
return;
// force the found cover on all of the selected items
foreach (QListWidgetItem* current, context_menu_items_) {
// don't save the first one twice
if(current != item) {
Song current_song = ItemAsSong(current);
album_cover_choice_controller_->SaveCover(&current_song, cover);
}
UpdateCoverInList(current, cover);
}
}
void AlbumCoverManager::UnsetCover() {
QString unset = album_cover_choice_controller_->UnsetCover();
Song song = GetFirstSelectedAsSong();
if(!song.is_valid())
return;
foreach (QListWidgetItem* item, context_menu_items_) {
item->setIcon(no_cover_icon_);
item->setData(Role_PathManual, unset);
backend_->UpdateManualAlbumArtAsync(item->data(Role_ArtistName).toString(),
item->data(Role_AlbumName).toString(),
unset);
QListWidgetItem* item = context_menu_items_[0];
QString cover = album_cover_choice_controller_->UnsetCover(&song);
// force the 'none' cover on all of the selected items
foreach (QListWidgetItem* current, context_menu_items_) {
current->setIcon(no_cover_icon_);
current->setData(Role_PathManual, cover);
// don't save the first one twice
if(current != item) {
Song current_song = ItemAsSong(current);
album_cover_choice_controller_->SaveCover(&current_song, cover);
}
}
}
@ -650,16 +647,17 @@ void AlbumCoverManager::LoadSelectedToPlaylist() {
}
}
void AlbumCoverManager::SearchForCover() {
Song song = GetFirstSelectedAsSong();
if(!song.is_valid())
return;
void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &image) {
const QString artist = item->data(Role_ArtistName).toString();
const QString album = item->data(Role_AlbumName).toString();
QImage image = album_cover_choice_controller_->SearchForCover(song);
if (image.isNull())
return;
QString path = album_cover_choice_controller_->SaveCoverInCache(artist, album, image);
foreach (QListWidgetItem* item, context_menu_items_) {
SaveAndSetCover(item, image);
}
// Save the image in the database
backend_->UpdateManualAlbumArtAsync(artist, album, path);
// Update the icon in our list
quint64 id = cover_loader_->Worker()->LoadImageAsync(QString(), path);
item->setData(Role_PathManual, path);
cover_loading_tasks_[id] = item;
}

View File

@ -60,9 +60,6 @@ class AlbumCoverManager : public QMainWindow {
SongList GetSongsInAlbums(const QModelIndexList& indexes) const;
SongMimeData* GetMimeDataForAlbums(const QModelIndexList& indexes) const;
static QString SaveCoverInCache(
const QString& artist, const QString& album, const QImage& image);
signals:
void AddToPlaylist(QMimeData* data);
@ -96,6 +93,8 @@ class AlbumCoverManager : public QMainWindow {
void AddSelectedToPlaylist();
void LoadSelectedToPlaylist();
void UpdateCoverInList(QListWidgetItem* item, const QString& cover);
private:
enum ArtistItemType {
All_Artists,

View File

@ -46,7 +46,7 @@ const char* EditTagDialog::kTagFetchOnLoadText = QT_TR_NOOP("Generating audio fi
EditTagDialog::EditTagDialog(QWidget* parent)
: QDialog(parent),
ui_(new Ui_EditTagDialog),
album_cover_choice_controller_(new AlbumCoverChoiceController(this)),
album_cover_choice_controller_(NULL),
backend_(NULL),
loading_(false),
ignore_edits_(false),
@ -257,6 +257,8 @@ void EditTagDialog::SetSongsFinished() {
void EditTagDialog::SetTagCompleter(LibraryBackend* backend) {
backend_ = backend;
album_cover_choice_controller_ = new AlbumCoverChoiceController(backend, this);
new TagCompleter(backend, Playlist::Column_Artist, ui_->artist);
new TagCompleter(backend, Playlist::Column_Album, ui_->album);
new TagCompleter(backend, Playlist::Column_AlbumArtist, ui_->albumartist);
@ -491,76 +493,78 @@ void EditTagDialog::ResetField() {
}
}
Song EditTagDialog::GetFirstSelected() {
Song* EditTagDialog::GetFirstSelected() {
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return Song();
return data_[sel.first().row()].original_;
return NULL;
return &data_[sel.first().row()].original_;
}
void EditTagDialog::LoadCoverFromFile() {
Song song = GetFirstSelected();
if(!song.is_valid()) {
Song* song = GetFirstSelected();
if(!song)
return;
}
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
QString cover = album_cover_choice_controller_->LoadCoverFromFile(song);
if(!cover.isEmpty()) {
SetAlbumArt(cover);
}
if(!cover.isEmpty())
UpdateCoverOf(*song, sel, cover);
}
void EditTagDialog::LoadCoverFromURL() {
Song song = GetFirstSelected();
if(!song.is_valid()) {
return;
}
QImage image = album_cover_choice_controller_->LoadCoverFromURL();
if (image.isNull())
Song* song = GetFirstSelected();
if(!song)
return;
SetAlbumArt(AlbumCoverManager::SaveCoverInCache(song.artist(), song.album(), image));
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
QString cover = album_cover_choice_controller_->LoadCoverFromURL(song);
if(!cover.isEmpty())
UpdateCoverOf(*song, sel, cover);
}
void EditTagDialog::SearchForCover() {
Song song = GetFirstSelected();
if(!song.is_valid()) {
return;
}
QImage image = album_cover_choice_controller_->SearchForCover(song);
if (image.isNull())
Song* song = GetFirstSelected();
if(!song)
return;
SetAlbumArt(AlbumCoverManager::SaveCoverInCache(song.artist(), song.album(), image));
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
QString cover = album_cover_choice_controller_->SearchForCover(song);
if(!cover.isEmpty())
UpdateCoverOf(*song, sel, cover);
}
void EditTagDialog::UnsetCover() {
SetAlbumArt(album_cover_choice_controller_->UnsetCover());
Song* song = GetFirstSelected();
if(!song)
return;
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
QString cover = album_cover_choice_controller_->UnsetCover(song);
UpdateCoverOf(*song, sel, cover);
}
void EditTagDialog::ShowCover() {
Song song = GetFirstSelected();
if(!song.is_valid()) {
Song* song = GetFirstSelected();
if(!song) {
return;
}
album_cover_choice_controller_->ShowCover(song);
album_cover_choice_controller_->ShowCover(*song);
}
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)
void EditTagDialog::UpdateCoverOf(const Song& selected, const QModelIndexList& sel,
const QString& cover) {
if (!selected.is_valid() || selected.id() == -1)
return;
song->set_art_manual(path);
backend_->UpdateManualAlbumArtAsync(song->artist(), song->album(), path);
UpdateSummaryTab(*song);
UpdateSummaryTab(selected);
// Now check if we have any other songs cached that share that artist and
// album (and would therefore be changed as well)
@ -569,9 +573,9 @@ void EditTagDialog::SetAlbumArt(const QString& path) {
continue;
Song* other_song = &data_[i].original_;
if (song->artist() == other_song->artist() &&
song->album() == other_song->album()) {
other_song->set_art_manual(path);
if (selected.artist() == other_song->artist() &&
selected.album() == other_song->album()) {
other_song->set_art_manual(cover);
}
}
}

View File

@ -115,7 +115,9 @@ private:
QString id_;
};
Song GetFirstSelected();
Song* GetFirstSelected();
void UpdateCoverOf(const Song& selected, const QModelIndexList& sel,
const QString& cover);
bool DoesValueVary(const QModelIndexList& sel, const QString& id) const;
bool IsValueModified(const QModelIndexList& sel, const QString& id) const;
@ -127,8 +129,6 @@ private:
void UpdateSummaryTab(const Song& song);
void UpdateStatisticsTab(const Song& song);
void SetAlbumArt(const QString& path);
bool SetLoading(const QString& message);
// Called by QtConcurrentRun

View File

@ -61,10 +61,9 @@ const int NowPlayingWidget::kTopBorder = 4;
NowPlayingWidget::NowPlayingWidget(QWidget *parent)
: QWidget(parent),
album_cover_choice_controller_(new AlbumCoverChoiceController(this)),
album_cover_choice_controller_(NULL),
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
kitten_loader_(NULL),
backend_(NULL),
mode_(SmallSongDetails),
menu_(new QMenu(this)),
above_statusbar_action_(NULL),
@ -418,45 +417,35 @@ void NowPlayingWidget::EnableKittens(bool aww) {
}
void NowPlayingWidget::LoadCoverFromFile() {
QString cover = album_cover_choice_controller_->LoadCoverFromFile(metadata_);
QString cover = album_cover_choice_controller_->LoadCoverFromFile(&metadata_);
if(!cover.isEmpty()) {
SetAlbumArt(cover);
}
if(!cover.isEmpty())
NowPlaying(metadata_);
}
void NowPlayingWidget::LoadCoverFromURL() {
QImage image = album_cover_choice_controller_->LoadCoverFromURL();
if (image.isNull())
return;
QString cover = album_cover_choice_controller_->LoadCoverFromURL(&metadata_);
SetAlbumArt(AlbumCoverManager::SaveCoverInCache(
metadata_.artist(), metadata_.album(), image));
if(!cover.isEmpty())
NowPlaying(metadata_);
}
void NowPlayingWidget::SearchForCover() {
QImage image = album_cover_choice_controller_->SearchForCover(metadata_);
if (image.isNull())
return;
QString cover = album_cover_choice_controller_->SearchForCover(&metadata_);
SetAlbumArt(AlbumCoverManager::SaveCoverInCache(
metadata_.artist(), metadata_.album(), image));
if(!cover.isEmpty())
NowPlaying(metadata_);
}
void NowPlayingWidget::UnsetCover() {
SetAlbumArt(album_cover_choice_controller_->UnsetCover());
album_cover_choice_controller_->UnsetCover(&metadata_);
NowPlaying(metadata_);
}
void NowPlayingWidget::ShowCover() {
album_cover_choice_controller_->ShowCover(metadata_);
}
void NowPlayingWidget::SetAlbumArt(const QString& path) {
metadata_.set_art_manual(path);
backend_->UpdateManualAlbumArtAsync(metadata_.artist(), metadata_.album(), path);
NowPlaying(metadata_);
}
void NowPlayingWidget::SetLibraryBackend(LibraryBackend* backend) {
backend_ = backend;
album_cover_choice_controller_ = new AlbumCoverChoiceController(backend, this);
}

View File

@ -106,16 +106,12 @@ private:
void UpdateHeight(AlbumCoverLoader* loader);
void DrawContents(QPainter* p);
void SetAlbumArt(const QString& path);
private:
AlbumCoverChoiceController* album_cover_choice_controller_;
BackgroundThread<AlbumCoverLoader>* cover_loader_;
BackgroundThread<AlbumCoverLoader>* kitten_loader_;
LibraryBackend* backend_;
Mode mode_;
QMenu* menu_;