diff --git a/src/library/libraryplaylistitem.h b/src/library/libraryplaylistitem.h index 90896d1de..3e68a57c6 100644 --- a/src/library/libraryplaylistitem.h +++ b/src/library/libraryplaylistitem.h @@ -29,6 +29,7 @@ class LibraryPlaylistItem : public PlaylistItem { void Reload(); Song Metadata() const { return song_; } + void SetMetadata(const Song& song) { song_ = song; } QUrl Url() const; diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 23bec9760..ea43d3aa0 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -72,6 +72,7 @@ Playlist::Playlist(PlaylistBackend* backend, int id, QObject *parent) Playlist::~Playlist() { items_.clear(); + library_items_by_id_.clear(); } QVariant Playlist::headerData(int section, Qt::Orientation, int role) const { @@ -603,6 +604,13 @@ QModelIndex Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items, items_.insert(i, item); virtual_items_ << virtual_items_.count(); + if (item->type() == "Library") { + int id = item->Metadata().id(); + if (id != -1) { + library_items_by_id_.insertMulti(id, item); + } + } + if (item == current_item_) { // It's one we removed before that got re-added through an undo current_item_index_ = index(i, 0); @@ -820,6 +828,7 @@ void Playlist::Restore() { items_.clear(); virtual_items_.clear(); + library_items_by_id_.clear(); items_ = backend_->GetPlaylistItems(id_); @@ -827,7 +836,14 @@ void Playlist::Restore() { for (int i=0 ; itype() == "Library") { + int id = items_[i]->Metadata().id(); + if (id != -1) { + library_items_by_id_.insertMulti(id, items_[i]); + } + } + } reset(); @@ -852,8 +868,17 @@ PlaylistItemList Playlist::RemoveItemsWithoutUndo(int row, int count) { // Remove items PlaylistItemList ret; - for (int i=0 ; i item(items_.takeAt(row)); + ret << item; + + if (item->type() == "Library") { + int id = item->Metadata().id(); + if (id != -1) { + library_items_by_id_.remove(id, item); + } + } + } endRemoveRows(); @@ -1029,3 +1054,7 @@ quint64 Playlist::GetTotalLength() const { } return ret; } + +PlaylistItemList Playlist::library_items_by_id(int id) const { + return library_items_by_id_.values(id); +} diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index 475d4b5b9..be86f2b76 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -112,6 +112,8 @@ class Playlist : public QAbstractListModel { PlaylistItem::Options current_item_options() const; Song current_item_metadata() const; + PlaylistItemList library_items_by_id(int id) const; + SongList GetAllSongs() const; quint64 GetTotalLength() const; // in seconds @@ -200,6 +202,9 @@ class Playlist : public QAbstractListModel { PlaylistItemList items_; QList virtual_items_; // Contains the indices into items_ in the order // that they will be played. + // A map of library ID to playlist item - for fast lookups when library + // items change. + QMultiMap > library_items_by_id_; QPersistentModelIndex current_item_index_; QPersistentModelIndex last_played_item_index_; diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 2f320beb1..b129f38a0 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -18,6 +18,8 @@ #include "playlistbackend.h" #include "playlistmanager.h" #include "core/utilities.h" +#include "library/librarybackend.h" +#include "library/libraryplaylistitem.h" #include "playlistparsers/playlistparser.h" #include @@ -47,6 +49,8 @@ void PlaylistManager::Init(LibraryBackend* library_backend, playlist_backend_ = playlist_backend; sequence_ = sequence; + connect(library_backend_, SIGNAL(SongsDiscovered(SongList)), SLOT(SongsDiscovered(SongList))); + foreach (const PlaylistBackend::Playlist& p, playlist_backend->GetAllPlaylists()) { AddPlaylist(p.id, p.name); } @@ -225,3 +229,17 @@ void PlaylistManager::SelectionChanged(const QItemSelection &selection) { current_selection_ = selection; UpdateSummaryText(); } + +void PlaylistManager::SongsDiscovered(const SongList& songs) { + // Some songs might've changed in the library, let's update any playlist + // items we have that match those songs + + foreach (const Song& song, songs) { + foreach (const Data& data, playlists_) { + PlaylistItemList items = data.p->library_items_by_id(song.id()); + foreach (boost::shared_ptr item, items) { + static_cast(item.get())->SetMetadata(song); + } + } + } +} diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index 566b7c433..a179d02c6 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -98,6 +98,7 @@ signals: private slots: void UpdateSummaryText(); + void SongsDiscovered(const SongList& songs); private: Playlist* AddPlaylist(int id, const QString& name); diff --git a/tests/playlist_test.cpp b/tests/playlist_test.cpp index 7c24e1750..44afd6412 100644 --- a/tests/playlist_test.cpp +++ b/tests/playlist_test.cpp @@ -17,6 +17,7 @@ #include "test_utils.h" #include "gtest/gtest.h" +#include "library/libraryplaylistitem.h" #include "playlist/playlist.h" #include "mock_settingsprovider.h" #include "mock_playlistitem.h" @@ -434,6 +435,69 @@ TEST_F(PlaylistTest, ShuffleThenNext) { EXPECT_EQ(index-1, playlist_.previous_index()); } +TEST_F(PlaylistTest, LibraryIdMapSingle) { + Song song; + song.Init("title", "artist", "album", 123); + song.set_id(1); + + boost::shared_ptr item(new LibraryPlaylistItem(song)); + playlist_.InsertItems(PlaylistItemList() << item); + + EXPECT_EQ(0, playlist_.library_items_by_id(-1).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(0).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(2).count()); + ASSERT_EQ(1, playlist_.library_items_by_id(1).count()); + EXPECT_EQ(song.title(), playlist_.library_items_by_id(1)[0]->Metadata().title()); + + playlist_.Clear(); + + EXPECT_EQ(0, playlist_.library_items_by_id(1).count()); +} + +TEST_F(PlaylistTest, LibraryIdMapInvalid) { + Song invalid; + invalid.Init("title", "artist", "album", 123); + ASSERT_EQ(-1, invalid.id()); + + boost::shared_ptr item(new LibraryPlaylistItem(invalid)); + playlist_.InsertItems(PlaylistItemList() << item); + + EXPECT_EQ(0, playlist_.library_items_by_id(-1).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(0).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(1).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(2).count()); +} + +TEST_F(PlaylistTest, LibraryIdMapMulti) { + Song one; + one.Init("title", "artist", "album", 123); + one.set_id(1); + + Song two; + two.Init("title 2", "artist 2", "album 2", 123); + two.set_id(2); + + boost::shared_ptr item_one(new LibraryPlaylistItem(one)); + boost::shared_ptr item_two(new LibraryPlaylistItem(two)); + boost::shared_ptr item_three(new LibraryPlaylistItem(one)); + playlist_.InsertItems(PlaylistItemList() << item_one << item_two << item_three); + + EXPECT_EQ(2, playlist_.library_items_by_id(1).count()); + EXPECT_EQ(1, playlist_.library_items_by_id(2).count()); + + playlist_.removeRow(1); // item_two + EXPECT_EQ(2, playlist_.library_items_by_id(1).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(2).count()); + + playlist_.removeRow(1); // item_three + EXPECT_EQ(1, playlist_.library_items_by_id(1).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(2).count()); + + playlist_.removeRow(0); // item_one + EXPECT_EQ(0, playlist_.library_items_by_id(1).count()); + EXPECT_EQ(0, playlist_.library_items_by_id(2).count()); +} + } // namespace