Do most item reloading in the background, schedule playlist saving

This commit is contained in:
Jonas Kvinge 2021-04-26 22:57:08 +02:00
parent 41b238e87b
commit 8c64d3b55c
5 changed files with 119 additions and 62 deletions

View File

@ -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) {

View File

@ -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();

View File

@ -58,6 +58,7 @@
#include <QFlags>
#include <QSettings>
#include <QtDebug>
#include <QTimer>
#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<void> future = item->BackgroundReload();
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
watcher->setFuture(future);
QObject::connect(watcher, &QFutureWatcher<void>::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<void> future = item->BackgroundReload();
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
watcher->setFuture(future);
QObject::connect(watcher, &QFutureWatcher<void>::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<int> &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<int> &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<int> &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<int> &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();
}

View File

@ -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<int> &rows);
void ReloadItemsBlocking(const QList<int> &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<QModelIndex> temp_dequeue_change_indexes_;

View File

@ -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);
};