Merge pull request #4644 from sbird/fasterstartup

Patches to start clementine faster
This commit is contained in:
John Maguire 2014-12-09 20:02:51 +01:00
commit fdd669360f
9 changed files with 83 additions and 50 deletions

View File

@ -1446,7 +1446,7 @@ void Playlist::Save() const {
} }
namespace { namespace {
typedef QFutureWatcher<shared_ptr<PlaylistItem>> PlaylistItemFutureWatcher; typedef QFutureWatcher<QList<PlaylistItemPtr>> PlaylistItemFutureWatcher;
} }
void Playlist::Restore() { void Playlist::Restore() {
@ -1456,7 +1456,8 @@ void Playlist::Restore() {
virtual_items_.clear(); virtual_items_.clear();
library_items_by_id_.clear(); library_items_by_id_.clear();
PlaylistBackend::PlaylistItemFuture future = backend_->GetPlaylistItems(id_); QFuture<QList<PlaylistItemPtr>> future =
QtConcurrent::run(backend_, &PlaylistBackend::GetPlaylistItems, id_);
PlaylistItemFutureWatcher* watcher = new PlaylistItemFutureWatcher(this); PlaylistItemFutureWatcher* watcher = new PlaylistItemFutureWatcher(this);
watcher->setFuture(future); watcher->setFuture(future);
connect(watcher, SIGNAL(finished()), SLOT(ItemsLoaded())); connect(watcher, SIGNAL(finished()), SLOT(ItemsLoaded()));
@ -1467,7 +1468,7 @@ void Playlist::ItemsLoaded() {
static_cast<PlaylistItemFutureWatcher*>(sender()); static_cast<PlaylistItemFutureWatcher*>(sender());
watcher->deleteLater(); watcher->deleteLater();
PlaylistItemList items = watcher->future().results(); PlaylistItemList items = watcher->future().result();
// backend returns empty elements for library items which it couldn't // backend returns empty elements for library items which it couldn't
// match (because they got deleted); we don't need those // match (because they got deleted); we don't need those

View File

@ -24,7 +24,6 @@
#include <QHash> #include <QHash>
#include <QMutexLocker> #include <QMutexLocker>
#include <QSqlQuery> #include <QSqlQuery>
#include <QtConcurrentMap>
#include <QtDebug> #include <QtDebug>
#include "core/application.h" #include "core/application.h"
@ -138,7 +137,7 @@ PlaylistBackend::Playlist PlaylistBackend::GetPlaylist(int id) {
return p; return p;
} }
QList<SqlRow> PlaylistBackend::GetPlaylistRows(int playlist) { QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) {
QMutexLocker l(db_->Mutex()); QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect()); QSqlDatabase db(db_->Connect());
@ -162,42 +161,46 @@ QList<SqlRow> PlaylistBackend::GetPlaylistRows(int playlist) {
" LEFT JOIN jamendo.songs AS jamendo_songs" " LEFT JOIN jamendo.songs AS jamendo_songs"
" ON p.library_id = jamendo_songs.ROWID" " ON p.library_id = jamendo_songs.ROWID"
" WHERE p.playlist = :playlist"; " WHERE p.playlist = :playlist";
QSqlQuery q(query, db); QSqlQuery q(db);
// Forward iterations only may be faster
q.setForwardOnly(true);
q.prepare(query);
q.bindValue(":playlist", playlist); q.bindValue(":playlist", playlist);
q.exec(); q.exec();
if (db_->CheckErrors(q)) return QList<SqlRow>();
QList<SqlRow> rows; return q;
}
QList<PlaylistItemPtr> PlaylistBackend::GetPlaylistItems(int playlist) {
QSqlQuery q = GetPlaylistRows(playlist);
// Note that as this only accesses the query, not the db, we don't need the
// mutex.
if (db_->CheckErrors(q)) return QList<PlaylistItemPtr>();
// it's probable that we'll have a few songs associated with the
// same CUE so we're caching results of parsing CUEs
std::shared_ptr<NewSongFromQueryState> state_ptr(new NewSongFromQueryState());
QList<PlaylistItemPtr> playlistitems;
while (q.next()) { while (q.next()) {
rows << SqlRow(q); playlistitems << NewPlaylistItemFromQuery(SqlRow(q), state_ptr);
} }
return playlistitems;
return rows;
} }
QFuture<PlaylistItemPtr> PlaylistBackend::GetPlaylistItems(int playlist) { QList<Song> PlaylistBackend::GetPlaylistSongs(int playlist) {
QMutexLocker l(db_->Mutex()); QSqlQuery q = GetPlaylistRows(playlist);
QList<SqlRow> rows = GetPlaylistRows(playlist); // Note that as this only accesses the query, not the db, we don't need the
// mutex.
if (db_->CheckErrors(q)) return QList<Song>();
// it's probable that we'll have a few songs associated with the // it's probable that we'll have a few songs associated with the
// same CUE so we're caching results of parsing CUEs // same CUE so we're caching results of parsing CUEs
std::shared_ptr<NewSongFromQueryState> state_ptr(new NewSongFromQueryState()); std::shared_ptr<NewSongFromQueryState> state_ptr(new NewSongFromQueryState());
return QtConcurrent::mapped( QList<Song> songs;
rows, std::bind(&PlaylistBackend::NewPlaylistItemFromQuery, this, _1, while (q.next()) {
state_ptr)); songs << NewSongFromQuery(SqlRow(q), state_ptr);
} }
return songs;
QFuture<Song> PlaylistBackend::GetPlaylistSongs(int playlist) {
QMutexLocker l(db_->Mutex());
QList<SqlRow> rows = GetPlaylistRows(playlist);
// it's probable that we'll have a few songs associated with the
// same CUE so we're caching results of parsing CUEs
std::shared_ptr<NewSongFromQueryState> state_ptr(new NewSongFromQueryState());
return QtConcurrent::mapped(
rows, std::bind(&PlaylistBackend::NewSongFromQuery, this, _1, state_ptr));
} }
PlaylistItemPtr PlaylistBackend::NewPlaylistItemFromQuery( PlaylistItemPtr PlaylistBackend::NewPlaylistItemFromQuery(

View File

@ -18,7 +18,6 @@
#ifndef PLAYLISTBACKEND_H #ifndef PLAYLISTBACKEND_H
#define PLAYLISTBACKEND_H #define PLAYLISTBACKEND_H
#include <QFuture>
#include <QHash> #include <QHash>
#include <QList> #include <QList>
#include <QMutex> #include <QMutex>
@ -53,7 +52,6 @@ class PlaylistBackend : public QObject {
QString special_type; QString special_type;
}; };
typedef QList<Playlist> PlaylistList; typedef QList<Playlist> PlaylistList;
typedef QFuture<PlaylistItemPtr> PlaylistItemFuture;
static const int kSongTableJoins; static const int kSongTableJoins;
@ -61,8 +59,9 @@ class PlaylistBackend : public QObject {
PlaylistList GetAllOpenPlaylists(); PlaylistList GetAllOpenPlaylists();
PlaylistList GetAllFavoritePlaylists(); PlaylistList GetAllFavoritePlaylists();
PlaylistBackend::Playlist GetPlaylist(int id); PlaylistBackend::Playlist GetPlaylist(int id);
PlaylistItemFuture GetPlaylistItems(int playlist);
QFuture<Song> GetPlaylistSongs(int playlist); QList<PlaylistItemPtr> GetPlaylistItems(int playlist);
QList<Song> GetPlaylistSongs(int playlist);
void SetPlaylistOrder(const QList<int>& ids); void SetPlaylistOrder(const QList<int>& ids);
void SetPlaylistUiPath(int id, const QString& path); void SetPlaylistUiPath(int id, const QString& path);
@ -87,7 +86,7 @@ class PlaylistBackend : public QObject {
QMutex mutex_; QMutex mutex_;
}; };
QList<SqlRow> GetPlaylistRows(int playlist); QSqlQuery GetPlaylistRows(int playlist);
Song NewSongFromQuery(const SqlRow& row, Song NewSongFromQuery(const SqlRow& row,
std::shared_ptr<NewSongFromQueryState> state); std::shared_ptr<NewSongFromQueryState> state);

View File

@ -35,6 +35,7 @@
#include <QFuture> #include <QFuture>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QMessageBox> #include <QMessageBox>
#include <QtConcurrentRun>
#include <QtDebug> #include <QtDebug>
using smart_playlists::GeneratorPtr; using smart_playlists::GeneratorPtr;
@ -182,21 +183,22 @@ void PlaylistManager::Save(int id, const QString& filename,
// Playlist is not in the playlist manager: probably save action was // Playlist is not in the playlist manager: probably save action was
// triggered // triggered
// from the left side bar and the playlist isn't loaded. // from the left side bar and the playlist isn't loaded.
QFuture<Song> future = playlist_backend_->GetPlaylistSongs(id); QFuture<QList<Song>> future = QtConcurrent::run(
QFutureWatcher<Song>* watcher = new QFutureWatcher<Song>(this); playlist_backend_, &PlaylistBackend::GetPlaylistSongs, id);
QFutureWatcher<SongList>* watcher = new QFutureWatcher<SongList>(this);
watcher->setFuture(future); watcher->setFuture(future);
NewClosure(watcher, SIGNAL(finished()), this, NewClosure(watcher, SIGNAL(finished()), this,
SLOT(ItemsLoadedForSavePlaylist(QFutureWatcher<Song>*, QString, SLOT(ItemsLoadedForSavePlaylist(QFutureWatcher<SongList>*,
Playlist::Path)), QString, Playlist::Path)),
watcher, filename); watcher, filename);
} }
} }
void PlaylistManager::ItemsLoadedForSavePlaylist(QFutureWatcher<Song>* watcher, void PlaylistManager::ItemsLoadedForSavePlaylist(
const QString& filename, QFutureWatcher<SongList>* watcher, const QString& filename,
Playlist::Path path_type) { Playlist::Path path_type) {
SongList song_list = watcher->future().results(); SongList song_list = watcher->future().result();
parser_->Save(song_list, filename, path_type); parser_->Save(song_list, filename, path_type);
} }

View File

@ -232,7 +232,7 @@ class PlaylistManager : public PlaylistManagerInterface {
void OneOfPlaylistsChanged(); void OneOfPlaylistsChanged();
void UpdateSummaryText(); void UpdateSummaryText();
void SongsDiscovered(const SongList& songs); void SongsDiscovered(const SongList& songs);
void ItemsLoadedForSavePlaylist(QFutureWatcher<Song>* watcher, void ItemsLoadedForSavePlaylist(QFutureWatcher<SongList>* watcher,
const QString& filename, const QString& filename,
Playlist::Path path_type); Playlist::Path path_type);

View File

@ -44,6 +44,7 @@ PlaylistTabBar::PlaylistTabBar(QWidget* parent)
menu_(new QMenu(this)), menu_(new QMenu(this)),
menu_index_(-1), menu_index_(-1),
suppress_current_changed_(false), suppress_current_changed_(false),
initialized_(false),
rename_editor_(new RenameTabLineEdit(this)) { rename_editor_(new RenameTabLineEdit(this)) {
setAcceptDrops(true); setAcceptDrops(true);
setElideMode(Qt::ElideRight); setElideMode(Qt::ElideRight);
@ -79,6 +80,16 @@ void PlaylistTabBar::SetManager(PlaylistManager* manager) {
manager_ = manager; manager_ = manager;
connect(manager_, SIGNAL(PlaylistFavorited(int, bool)), connect(manager_, SIGNAL(PlaylistFavorited(int, bool)),
SLOT(PlaylistFavoritedSlot(int, bool))); SLOT(PlaylistFavoritedSlot(int, bool)));
connect(manager_, SIGNAL(PlaylistManagerInitialized()), this,
SLOT(PlaylistManagerInitialized()));
}
void PlaylistTabBar::PlaylistManagerInitialized() {
// Signal that we are done loading and thus further changes should be
// committed to the db.
initialized_ = true;
disconnect(manager_, SIGNAL(PlaylistManagerInitialized()), this,
SLOT(PlaylistManagerInitialized()));
} }
void PlaylistTabBar::contextMenuEvent(QContextMenuEvent* e) { void PlaylistTabBar::contextMenuEvent(QContextMenuEvent* e) {
@ -281,6 +292,7 @@ void PlaylistTabBar::InsertTab(int id, int index, const QString& text,
insertTab(index, text); insertTab(index, text);
setTabData(index, id); setTabData(index, id);
setTabToolTip(index, text); setTabToolTip(index, text);
FavoriteWidget* widget = new FavoriteWidget(id, favorite); FavoriteWidget* widget = new FavoriteWidget(id, favorite);
widget->setToolTip( widget->setToolTip(
tr("Click here to favorite this playlist so it will be saved and remain " tr("Click here to favorite this playlist so it will be saved and remain "
@ -291,14 +303,19 @@ void PlaylistTabBar::InsertTab(int id, int index, const QString& text,
setTabButton(index, QTabBar::LeftSide, widget); setTabButton(index, QTabBar::LeftSide, widget);
suppress_current_changed_ = false; suppress_current_changed_ = false;
if (currentIndex() == index) emit CurrentIdChanged(id); // If we are still starting up, we don't need to do this, as the
// tab ordering after startup will be the same as was already in the db.
if (initialized_) {
if (currentIndex() == index) emit CurrentIdChanged(id);
// Update playlist tab order/visibility // Update playlist tab order/visibility
TabMoved(); TabMoved();
}
} }
void PlaylistTabBar::TabMoved() { void PlaylistTabBar::TabMoved() {
QList<int> ids; QList<int> ids;
for (int i = 0; i < count(); ++i) { for (int i = 0; i < count(); ++i) {
ids << tabData(i).toInt(); ids << tabData(i).toInt();
} }

View File

@ -82,6 +82,8 @@ signals:
// Used when playlist's favorite flag isn't changed from the favorite widget // Used when playlist's favorite flag isn't changed from the favorite widget
// (e.g. from the playlistlistcontainer): will update the favorite widget // (e.g. from the playlistlistcontainer): will update the favorite widget
void PlaylistFavoritedSlot(int id, bool favorite); void PlaylistFavoritedSlot(int id, bool favorite);
// Used to signal that the playlist manager is done starting up
void PlaylistManagerInitialized();
void TabMoved(); void TabMoved();
void Save(); void Save();
@ -99,6 +101,7 @@ signals:
int drag_hover_tab_; int drag_hover_tab_;
bool suppress_current_changed_; bool suppress_current_changed_;
bool initialized_;
// Editor for inline renaming // Editor for inline renaming
RenameTabLineEdit* rename_editor_; RenameTabLineEdit* rename_editor_;

View File

@ -39,6 +39,7 @@
#include <QMenu> #include <QMenu>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QtConcurrentRun>
const char* PodcastService::kServiceName = "Podcasts"; const char* PodcastService::kServiceName = "Podcasts";
const char* PodcastService::kSettingsGroup = "Podcasts"; const char* PodcastService::kSettingsGroup = "Podcasts";
@ -60,8 +61,7 @@ PodcastService::PodcastService(Application* app, InternetModel* parent)
proxy_(new PodcastSortProxyModel(this)), proxy_(new PodcastSortProxyModel(this)),
context_menu_(nullptr), context_menu_(nullptr),
root_(nullptr), root_(nullptr),
organise_dialog_(new OrganiseDialog(app_->task_manager(), organise_dialog_(new OrganiseDialog(app_->task_manager(), nullptr)) {
nullptr)) {
icon_loader_->SetModel(model_); icon_loader_->SetModel(model_);
proxy_->setSourceModel(model_); proxy_->setSourceModel(model_);
proxy_->setDynamicSortFilter(true); proxy_->setDynamicSortFilter(true);
@ -78,8 +78,8 @@ PodcastService::PodcastService(Application* app, InternetModel* parent)
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)),
SLOT(CurrentSongChanged(Song))); SLOT(CurrentSongChanged(Song)));
connect(organise_dialog_.get(), SIGNAL(FileCopied(int)), connect(organise_dialog_.get(), SIGNAL(FileCopied(int)), this,
this, SLOT(FileCopied(int))); SLOT(FileCopied(int)));
} }
PodcastService::~PodcastService() {} PodcastService::~PodcastService() {}
@ -602,6 +602,13 @@ void PodcastService::ShowConfig() {
} }
void PodcastService::CurrentSongChanged(const Song& metadata) { void PodcastService::CurrentSongChanged(const Song& metadata) {
// This does two db queries, and we are called on every song change, so run
// this off the main thread.
QtConcurrent::run(this, &PodcastService::UpdatePodcastListenedStateAsync,
metadata);
}
void PodcastService::UpdatePodcastListenedStateAsync(const Song& metadata) {
// Check whether this song is one of our podcast episodes. // Check whether this song is one of our podcast episodes.
PodcastEpisode episode = backend_->GetEpisodeByUrlOrLocalUrl(metadata.url()); PodcastEpisode episode = backend_->GetEpisodeByUrlOrLocalUrl(metadata.url());
if (!episode.is_valid()) return; if (!episode.is_valid()) return;

View File

@ -96,6 +96,7 @@ class PodcastService : public InternetService {
private: private:
void EnsureAddPodcastDialogCreated(); void EnsureAddPodcastDialogCreated();
void UpdatePodcastListenedStateAsync(const Song& metadata);
void PopulatePodcastList(QStandardItem* parent); void PopulatePodcastList(QStandardItem* parent);
void UpdatePodcastText(QStandardItem* item, int unlistened_count) const; void UpdatePodcastText(QStandardItem* item, int unlistened_count) const;
void UpdateEpisodeText( void UpdateEpisodeText(