From 8c64d3b55c3d31093270240e1efc249df6372c5c Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 26 Apr 2021 22:57:08 +0200 Subject: [PATCH] Do most item reloading in the background, schedule playlist saving --- src/core/mainwindow.cpp | 19 +++-- src/core/mainwindow.h | 2 +- src/playlist/playlist.cpp | 144 ++++++++++++++++++++++----------- src/playlist/playlist.h | 14 +++- src/playlist/playlistmanager.h | 2 +- 5 files changed, 119 insertions(+), 62 deletions(-) diff --git a/src/core/mainwindow.cpp b/src/core/mainwindow.cpp index e8072d71c..24a69d0b9 100644 --- a/src/core/mainwindow.cpp +++ b/src/core/mainwindow.cpp @@ -2013,13 +2013,14 @@ void MainWindow::RescanSongs() { songs << item->Metadata(); } else if (item->Metadata().source() == Song::Source_LocalFile) { - item->Reload(); + QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index); + app_->playlist_manager()->current()->ItemReload(persistent_index, item->OriginalMetadata(), false); } } - if (songs.isEmpty()) return; - - app_->collection()->Rescan(songs); + if (!songs.isEmpty()) { + app_->collection()->Rescan(songs); + } } @@ -2057,7 +2058,7 @@ void MainWindow::EditTagDialogAccepted() { // FIXME: This is really lame but we don't know what rows have changed. ui_->playlist->view()->update(); - app_->playlist_manager()->current()->Save(); + app_->playlist_manager()->current()->ScheduleSave(); } @@ -2259,8 +2260,12 @@ void MainWindow::PlaylistClearCurrent() { } -void MainWindow::PlaylistEditFinished(const QModelIndex &idx) { - if (idx == playlist_menu_index_) SelectionSetValue(); +void MainWindow::PlaylistEditFinished(const int playlist_id, const QModelIndex &idx) { + + if (app_->playlist_manager()->current() && playlist_id == app_->playlist_manager()->current()->id() && idx == playlist_menu_index_) { + SelectionSetValue(); + } + } void MainWindow::CommandlineOptionsReceived(const quint32 instanceId, const QByteArray &string_options) { diff --git a/src/core/mainwindow.h b/src/core/mainwindow.h index c989ac2d7..33daf61f4 100644 --- a/src/core/mainwindow.h +++ b/src/core/mainwindow.h @@ -158,7 +158,7 @@ class MainWindow : public QMainWindow, public PlatformInterface { void PlaylistQueuePlayNext(); void PlaylistSkip(); void PlaylistRemoveCurrent(); - void PlaylistEditFinished(const QModelIndex &idx); + void PlaylistEditFinished(const int playlist_id, const QModelIndex &idx); void PlaylistClearCurrent(); void RescanSongs(); void EditTracks(); diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index c9f704a55..b01e2dbcc 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include "core/application.h" #include "core/logging.h" @@ -116,6 +117,7 @@ Playlist::Playlist(PlaylistBackend *backend, TaskManager *task_manager, Collecti is_loading_(false), proxy_(new PlaylistFilter(this)), queue_(new Queue(this)), + timer_save_(new QTimer(this)), backend_(backend), task_manager_(task_manager), collection_(collection), @@ -154,8 +156,13 @@ Playlist::Playlist(PlaylistBackend *backend, TaskManager *task_manager, Collecti QObject::connect(queue_, &Queue::layoutChanged, this, &Playlist::QueueLayoutChanged); + QObject::connect(timer_save_, &QTimer::timeout, this, &Playlist::Save); + column_alignments_ = PlaylistView::DefaultColumnAlignment(); + timer_save_->setSingleShot(true); + timer_save_->setInterval(900); + } Playlist::~Playlist() { @@ -391,31 +398,22 @@ bool Playlist::setData(const QModelIndex &idx, const QVariant &value, int role) if (song.url().isLocalFile()) { TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song); QPersistentModelIndex persistent_index = QPersistentModelIndex(idx); - QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection); + QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index, item]() { SongSaveComplete(reply, persistent_index, item->OriginalMetadata()); }, Qt::QueuedConnection); } else if (song.source() == Song::Source_Stream) { item->SetMetadata(song); - Save(); + ScheduleSave(); } return true; } -void Playlist::SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &idx) { +void Playlist::SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &idx, const Song &old_metadata) { if (reply->is_successful() && idx.isValid()) { if (reply->message().save_file_response().success()) { - PlaylistItemPtr item = item_at(idx.row()); - if (item) { - QFuture future = item->BackgroundReload(); - QFutureWatcher *watcher = new QFutureWatcher(); - watcher->setFuture(future); - QObject::connect(watcher, &QFutureWatcher::finished, this, [this, watcher, idx]() { - ItemReloadComplete(idx); - watcher->deleteLater(); - }); - } + ItemReload(idx, old_metadata, true); } else { emit Error(tr("An error occurred writing metadata to '%1'").arg(QString::fromStdString(reply->request_message().save_file_request().filename()))); @@ -426,17 +424,43 @@ void Playlist::SongSaveComplete(TagReaderReply *reply, const QPersistentModelInd } -void Playlist::ItemReloadComplete(const QPersistentModelIndex &idx) { +void Playlist::ItemReload(const QPersistentModelIndex &idx, const Song &old_metadata, const bool metadata_edit) { if (idx.isValid()) { - PlaylistItemPtr item = item_at(idx.row()); - if (item && item->HasTemporaryMetadata()) { // Update temporary metadata. - item->UpdateTemporaryMetadata(item->OriginalMetadata()); + if (item) { + QFuture future = item->BackgroundReload(); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, [this, watcher, idx, old_metadata, metadata_edit]() { + ItemReloadComplete(idx, old_metadata, metadata_edit); + watcher->deleteLater(); + }); } + } - emit dataChanged(idx, idx); - emit EditingFinished(idx); +} + +void Playlist::ItemReloadComplete(const QPersistentModelIndex &idx, const Song &old_metadata, const bool metadata_edit) { + + if (idx.isValid()) { + PlaylistItemPtr item = item_at(idx.row()); + if (item) { + if (idx.row() == current_row()) { + const bool minor = old_metadata.title() == item->Metadata().title() && + old_metadata.albumartist() == item->Metadata().albumartist() && + old_metadata.artist() == item->Metadata().artist() && + old_metadata.album() == item->Metadata().album(); + InformOfCurrentSongChange(AutoScroll_Never, minor); + } + else { + emit dataChanged(index(idx.row(), 0), index(idx.row(), ColumnCount - 1)); + } + if (metadata_edit) { + emit EditingFinished(id_, idx); + } + ScheduleSave(); + } } } @@ -691,7 +715,7 @@ void Playlist::set_current_row(const int i, const AutoScroll autoscroll, const b if (current_item_index_.isValid()) { last_played_item_index_ = current_item_index_; - Save(); + ScheduleSave(); } UpdateScrobblePoint(); @@ -859,7 +883,8 @@ void Playlist::TurnOnDynamicPlaylist(PlaylistGeneratorPtr gen) { dynamic_playlist_ = gen; ShuffleModeChanged(PlaylistSequence::Shuffle_Off); emit DynamicModeChanged(true); - Save(); + + ScheduleSave(); } @@ -913,7 +938,8 @@ void Playlist::MoveItemsWithoutUndo(const QList &source_rows, int pos) { current_virtual_index_ = virtual_items_.indexOf(current_row()); emit layoutChanged(); - Save(); + + ScheduleSave(); } @@ -963,7 +989,8 @@ void Playlist::MoveItemsWithoutUndo(int start, const QList &dest_rows) { current_virtual_index_ = virtual_items_.indexOf(current_row()); emit layoutChanged(); - Save(); + + ScheduleSave(); } @@ -1071,7 +1098,7 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemList &items, const int p queue_->InsertFirst(indexes); } - Save(); + ScheduleSave(); if (auto_sort_) { sort(sort_column_, sort_order_); @@ -1158,7 +1185,8 @@ void Playlist::UpdateItems(SongList songs) { } } } - Save(); + + ScheduleSave(); } @@ -1378,7 +1406,8 @@ void Playlist::ReOrderWithoutUndo(const PlaylistItemList &new_items) { emit layoutChanged(); emit PlaylistChanged(); - Save(); + + ScheduleSave(); } @@ -1394,8 +1423,18 @@ void Playlist::SetCurrentIsPaused(const bool paused) { current_is_paused_ = paused; - if (current_item_index_.isValid()) + if (current_item_index_.isValid()) { emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount - 1)); + } + +} + +void Playlist::ScheduleSave() const { + + if (!backend_ || is_loading_) return; + + timer_save_->start(); + } void Playlist::Save() const { @@ -1604,7 +1643,8 @@ PlaylistItemList Playlist::RemoveItemsWithoutUndo(const int row, const int count else current_virtual_index_ = virtual_items_.indexOf(current_row()); - Save(); + ScheduleSave(); + return ret; } @@ -1695,7 +1735,7 @@ void Playlist::Clear() { TurnOffDynamicPlaylist(); - Save(); + ScheduleSave(); } @@ -1750,24 +1790,23 @@ void Playlist::ReloadItems(const QList &rows) { for (int row : rows) { PlaylistItemPtr item = item_at(row); - - Song old_metadata = item->Metadata(); - - item->Reload(); - - if (row == current_row()) { - const bool minor = old_metadata.title() == item->Metadata().title() && - old_metadata.albumartist() == item->Metadata().albumartist() && - old_metadata.artist() == item->Metadata().artist() && - old_metadata.album() == item->Metadata().album(); - InformOfCurrentSongChange(AutoScroll_Never, minor); - } - else { - emit dataChanged(index(row, 0), index(row, ColumnCount - 1)); + QPersistentModelIndex idx = index(row, 0); + if (idx.isValid()) { + ItemReload(idx, item->Metadata(), false); } } - Save(); +} + +void Playlist::ReloadItemsBlocking(const QList &rows) { + + for (int row : rows) { + PlaylistItemPtr item = item_at(row); + Song old_metadata = item->Metadata(); + item->Reload(); + QPersistentModelIndex idx = index(row, 0); + ItemReloadComplete(idx, old_metadata, false); + } } @@ -1990,7 +2029,7 @@ void Playlist::ItemChanged(PlaylistItemPtr item) { void Playlist::InformOfCurrentSongChange(const AutoScroll autoscroll, const bool minor) { - // if the song is invalid, we won't play it - there's no point in informing anybody about the change + // If the song is invalid, we won't play it - there's no point in informing anybody about the change const Song metadata(current_item_metadata()); if (metadata.is_valid()) { if (minor) { @@ -2031,8 +2070,14 @@ void Playlist::InvalidateDeletedSongs() { } } - if (!invalidated_rows.isEmpty()) - ReloadItems(invalidated_rows); + if (!invalidated_rows.isEmpty()) { + if (QThread::currentThread() == thread()) { + ReloadItems(invalidated_rows); + } + else { + ReloadItemsBlocking(invalidated_rows); + } + } } @@ -2195,7 +2240,7 @@ void Playlist::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult & if (item && item->Metadata() == song && (!item->Metadata().art_manual_is_valid() || (result.type == AlbumCoverLoaderResult::Type_ManuallyUnset && !item->Metadata().has_manually_unset_cover()))) { qLog(Debug) << "Updating art manual for local song" << song.title() << song.album() << song.title() << "to" << result.album_cover.cover_url << "in playlist."; item->SetArtManual(result.album_cover.cover_url); - Save(); + ScheduleSave(); } } @@ -2214,7 +2259,8 @@ void Playlist::TurnOffDynamicPlaylist() { } emit DynamicModeChanged(false); - Save(); + + ScheduleSave(); } diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index a5ab7ea9c..4efe8ac7c 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -51,6 +51,7 @@ class QMimeData; class QSortFilterProxyModel; class QUndoStack; +class QTimer; class CollectionBackend; class PlaylistBackend; @@ -184,7 +185,7 @@ class Playlist : public QAbstractListModel { static bool set_column_value(Song &song, Column column, const QVariant &value); // Persistence - void Save() const; + void ScheduleSave() const; void Restore(); // Accessors @@ -260,6 +261,7 @@ class Playlist : public QAbstractListModel { void StopAfter(const int row); void ReloadItems(const QList &rows); + void ReloadItemsBlocking(const QList &rows); void InformOfCurrentSongChange(const AutoScroll autoscroll, const bool minor); // Registers an object which will get notifications when new songs are about to be inserted into this playlist. @@ -297,6 +299,8 @@ class Playlist : public QAbstractListModel { void set_auto_sort(const bool auto_sort) { auto_sort_ = auto_sort; } + void ItemReload(const QPersistentModelIndex &idx, const Song &old_metadata, const bool metadata_edit); + public slots: void set_current_row(const int i, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Maybe, const bool is_stopping = false, const bool force_inform = false); void Paused(); @@ -332,7 +336,7 @@ class Playlist : public QAbstractListModel { void PlaylistLoaded(); void CurrentSongChanged(Song metadata); void SongMetadataChanged(Song metadata); - void EditingFinished(QModelIndex idx); + void EditingFinished(const int playlist_id, QModelIndex idx); void PlayRequested(QModelIndex idx, Playlist::AutoScroll autoscroll); void MaybeAutoscroll(Playlist::AutoScroll autoscroll); @@ -375,15 +379,17 @@ class Playlist : public QAbstractListModel { void TracksDequeued(); void TracksEnqueued(const QModelIndex&, const int begin, const int end); void QueueLayoutChanged(); - void SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &idx); - void ItemReloadComplete(const QPersistentModelIndex &idx); + void SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &idx, const Song &old_metadata); + void ItemReloadComplete(const QPersistentModelIndex &idx, const Song &old_metadata, const bool metadata_edit); void ItemsLoaded(); void SongInsertVetoListenerDestroyed(); + void Save() const; private: bool is_loading_; PlaylistFilter *proxy_; Queue *queue_; + QTimer *timer_save_; QList temp_dequeue_change_indexes_; diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index 21f5c84c0..fda282768 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -133,7 +133,7 @@ class PlaylistManagerInterface : public QObject { // Signals that one of manager's playlists has changed (new items, new ordering etc.) - the argument shows which. void PlaylistChanged(Playlist *playlist); - void EditingFinished(QModelIndex idx); + void EditingFinished(int playlist_id, QModelIndex idx); void PlayRequested(QModelIndex idx, Playlist::AutoScroll autoscroll); };