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 {
typedef QFutureWatcher<shared_ptr<PlaylistItem>> PlaylistItemFutureWatcher;
typedef QFutureWatcher<QList<PlaylistItemPtr>> PlaylistItemFutureWatcher;
}
void Playlist::Restore() {
@ -1456,7 +1456,8 @@ void Playlist::Restore() {
virtual_items_.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);
watcher->setFuture(future);
connect(watcher, SIGNAL(finished()), SLOT(ItemsLoaded()));
@ -1467,7 +1468,7 @@ void Playlist::ItemsLoaded() {
static_cast<PlaylistItemFutureWatcher*>(sender());
watcher->deleteLater();
PlaylistItemList items = watcher->future().results();
PlaylistItemList items = watcher->future().result();
// backend returns empty elements for library items which it couldn't
// match (because they got deleted); we don't need those

View File

@ -24,7 +24,6 @@
#include <QHash>
#include <QMutexLocker>
#include <QSqlQuery>
#include <QtConcurrentMap>
#include <QtDebug>
#include "core/application.h"
@ -138,7 +137,7 @@ PlaylistBackend::Playlist PlaylistBackend::GetPlaylist(int id) {
return p;
}
QList<SqlRow> PlaylistBackend::GetPlaylistRows(int playlist) {
QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@ -162,42 +161,46 @@ QList<SqlRow> PlaylistBackend::GetPlaylistRows(int playlist) {
" LEFT JOIN jamendo.songs AS jamendo_songs"
" ON p.library_id = jamendo_songs.ROWID"
" 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.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()) {
rows << SqlRow(q);
playlistitems << NewPlaylistItemFromQuery(SqlRow(q), state_ptr);
}
return rows;
return playlistitems;
}
QFuture<PlaylistItemPtr> PlaylistBackend::GetPlaylistItems(int playlist) {
QMutexLocker l(db_->Mutex());
QList<SqlRow> rows = GetPlaylistRows(playlist);
QList<Song> PlaylistBackend::GetPlaylistSongs(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<Song>();
// 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::NewPlaylistItemFromQuery, this, _1,
state_ptr));
}
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));
QList<Song> songs;
while (q.next()) {
songs << NewSongFromQuery(SqlRow(q), state_ptr);
}
return songs;
}
PlaylistItemPtr PlaylistBackend::NewPlaylistItemFromQuery(

View File

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

View File

@ -35,6 +35,7 @@
#include <QFuture>
#include <QFutureWatcher>
#include <QMessageBox>
#include <QtConcurrentRun>
#include <QtDebug>
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
// triggered
// from the left side bar and the playlist isn't loaded.
QFuture<Song> future = playlist_backend_->GetPlaylistSongs(id);
QFutureWatcher<Song>* watcher = new QFutureWatcher<Song>(this);
QFuture<QList<Song>> future = QtConcurrent::run(
playlist_backend_, &PlaylistBackend::GetPlaylistSongs, id);
QFutureWatcher<SongList>* watcher = new QFutureWatcher<SongList>(this);
watcher->setFuture(future);
NewClosure(watcher, SIGNAL(finished()), this,
SLOT(ItemsLoadedForSavePlaylist(QFutureWatcher<Song>*, QString,
Playlist::Path)),
SLOT(ItemsLoadedForSavePlaylist(QFutureWatcher<SongList>*,
QString, Playlist::Path)),
watcher, filename);
}
}
void PlaylistManager::ItemsLoadedForSavePlaylist(QFutureWatcher<Song>* watcher,
const QString& filename,
Playlist::Path path_type) {
SongList song_list = watcher->future().results();
void PlaylistManager::ItemsLoadedForSavePlaylist(
QFutureWatcher<SongList>* watcher, const QString& filename,
Playlist::Path path_type) {
SongList song_list = watcher->future().result();
parser_->Save(song_list, filename, path_type);
}

View File

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

View File

@ -44,6 +44,7 @@ PlaylistTabBar::PlaylistTabBar(QWidget* parent)
menu_(new QMenu(this)),
menu_index_(-1),
suppress_current_changed_(false),
initialized_(false),
rename_editor_(new RenameTabLineEdit(this)) {
setAcceptDrops(true);
setElideMode(Qt::ElideRight);
@ -79,6 +80,16 @@ void PlaylistTabBar::SetManager(PlaylistManager* manager) {
manager_ = manager;
connect(manager_, SIGNAL(PlaylistFavorited(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) {
@ -281,6 +292,7 @@ void PlaylistTabBar::InsertTab(int id, int index, const QString& text,
insertTab(index, text);
setTabData(index, id);
setTabToolTip(index, text);
FavoriteWidget* widget = new FavoriteWidget(id, favorite);
widget->setToolTip(
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);
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
TabMoved();
// Update playlist tab order/visibility
TabMoved();
}
}
void PlaylistTabBar::TabMoved() {
QList<int> ids;
for (int i = 0; i < count(); ++i) {
ids << tabData(i).toInt();
}

View File

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

View File

@ -39,6 +39,7 @@
#include <QMenu>
#include <QSortFilterProxyModel>
#include <QtConcurrentRun>
const char* PodcastService::kServiceName = "Podcasts";
const char* PodcastService::kSettingsGroup = "Podcasts";
@ -60,8 +61,7 @@ PodcastService::PodcastService(Application* app, InternetModel* parent)
proxy_(new PodcastSortProxyModel(this)),
context_menu_(nullptr),
root_(nullptr),
organise_dialog_(new OrganiseDialog(app_->task_manager(),
nullptr)) {
organise_dialog_(new OrganiseDialog(app_->task_manager(), nullptr)) {
icon_loader_->SetModel(model_);
proxy_->setSourceModel(model_);
proxy_->setDynamicSortFilter(true);
@ -78,8 +78,8 @@ PodcastService::PodcastService(Application* app, InternetModel* parent)
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)),
SLOT(CurrentSongChanged(Song)));
connect(organise_dialog_.get(), SIGNAL(FileCopied(int)),
this, SLOT(FileCopied(int)));
connect(organise_dialog_.get(), SIGNAL(FileCopied(int)), this,
SLOT(FileCopied(int)));
}
PodcastService::~PodcastService() {}
@ -602,6 +602,13 @@ void PodcastService::ShowConfig() {
}
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.
PodcastEpisode episode = backend_->GetEpisodeByUrlOrLocalUrl(metadata.url());
if (!episode.is_valid()) return;

View File

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