Merge pull request #4644 from sbird/fasterstartup
Patches to start clementine faster
This commit is contained in:
commit
fdd669360f
@ -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
|
||||
|
@ -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 playlistitems;
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
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));
|
||||
QList<Song> songs;
|
||||
while (q.next()) {
|
||||
songs << NewSongFromQuery(SqlRow(q), 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));
|
||||
return songs;
|
||||
}
|
||||
|
||||
PlaylistItemPtr PlaylistBackend::NewPlaylistItemFromQuery(
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
void PlaylistManager::ItemsLoadedForSavePlaylist(
|
||||
QFutureWatcher<SongList>* watcher, const QString& filename,
|
||||
Playlist::Path path_type) {
|
||||
SongList song_list = watcher->future().results();
|
||||
SongList song_list = watcher->future().result();
|
||||
parser_->Save(song_list, filename, path_type);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 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();
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistTabBar::TabMoved() {
|
||||
QList<int> ids;
|
||||
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
ids << tabData(i).toInt();
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user