diff --git a/src/constants/playlistsettings.h b/src/constants/playlistsettings.h index 1af6148292..f18dd0c2a2 100644 --- a/src/constants/playlistsettings.h +++ b/src/constants/playlistsettings.h @@ -55,6 +55,7 @@ constexpr char kStateVersion[] = "state_version"; constexpr char kState[] = "state"; constexpr char kColumnAlignments[] = "column_alignments"; constexpr char kRatingLocked[] = "rating_locked"; +constexpr char kRemoveDuplicates[] = "remove_duplicates"; constexpr char kLastSaveFilter[] = "last_save_filter"; constexpr char kLastSavePath[] = "last_save_path"; diff --git a/src/core/player.cpp b/src/core/player.cpp index a80f3f3bb1..023023d5d2 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -151,6 +151,7 @@ void Player::ReloadSettings() { s.beginGroup(PlaylistSettings::kSettingsGroup); continue_on_error_ = s.value("continue_on_error", false).toBool(); greyout_ = s.value("greyout_songs_play", true).toBool(); + playlist_manager_->update_setting(s.value(PlaylistSettings::kRemoveDuplicates, false).toBool()); s.endGroup(); s.beginGroup(BehaviourSettings::kSettingsGroup); diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 9fcd183b8b..08dc5917bb 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -132,6 +132,7 @@ Playlist::Playlist(const SharedPtr task_manager, const int id, const QString &special_type, const bool favorite, + const bool remove_duplicates, QObject *parent) : QAbstractListModel(parent), is_loading_(false), @@ -156,7 +157,8 @@ Playlist::Playlist(const SharedPtr task_manager, scrobble_point_(-1), auto_sort_(false), sort_column_(Column::Title), - sort_order_(Qt::AscendingOrder) { + sort_order_(Qt::AscendingOrder), + remove_duplicates_(remove_duplicates) { undo_stack_->setUndoLimit(kUndoStackSize); @@ -198,6 +200,10 @@ void Playlist::InsertSongItems(const SongList &songs, const int pos, const bool items << make_shared(song); } + if (remove_duplicates_) { + RemoveDuplicateSongs(items); + } + InsertItems(items, pos, play_now, enqueue, enqueue_next); } @@ -1783,7 +1789,7 @@ bool Playlist::removeRows(const int row, const int count, const QModelIndex &par } -bool Playlist::removeRows(QList &rows) { +bool Playlist::removeRows(QList &rows, PlaylistItemPtrList &items) { if (rows.isEmpty()) { return false; @@ -1801,7 +1807,11 @@ bool Playlist::removeRows(QList &rows) { } // And now we're removing the current sequence - if (!removeRows(part.last(), static_cast(part.size()))) { + if (std::addressof(items) != std::addressof(items_)) { + // I want to clean a list to be added to the current playlist, I call a specific function for that + items.remove(part.last(), part.size()); + } + else if (!removeRows(part.last(), static_cast(part.size()))) { return false; } @@ -2529,13 +2539,13 @@ struct SongSimilarEqual { } // namespace -void Playlist::RemoveDuplicateSongs() { +void Playlist::RemoveDuplicateSongs(PlaylistItemPtrList &items) { QList rows_to_remove; std::unordered_map unique_songs; - for (int row = 0; row < items_.count(); ++row) { - const PlaylistItemPtr item = items_.value(row); + for (int row = 0; row < items.count(); ++row) { + const PlaylistItemPtr item = items.value(row); const Song &song = item->EffectiveMetadata(); bool found_duplicate = false; @@ -2560,7 +2570,7 @@ void Playlist::RemoveDuplicateSongs() { } } - removeRows(rows_to_remove); + removeRows(rows_to_remove, items); } diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index 31b6892cf1..544e928add 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -75,6 +75,9 @@ using ColumnAlignmentMap = QMap; Q_DECLARE_METATYPE(Qt::Alignment) Q_DECLARE_METATYPE(ColumnAlignmentMap) +// This default value keep the current behavior : the duplicates are ignored +static constexpr bool REMOVE_DUPLICATES_DEFAULT = false; + class Playlist : public QAbstractListModel { Q_OBJECT @@ -92,6 +95,7 @@ class Playlist : public QAbstractListModel { const int id, const QString &special_type = QString(), const bool favorite = false, + const bool remove_duplicates = false, QObject *parent = nullptr); ~Playlist() override; @@ -196,6 +200,10 @@ class Playlist : public QAbstractListModel { int previous_row(const bool ignore_repeat_track = false) const; int take_previous_row(const bool ignore_repeat_track = false); + void update_setting(const bool remove_duplicates) { + remove_duplicates_ = remove_duplicates; + } + QModelIndex current_index() const; bool stop_after_current() const; @@ -250,6 +258,12 @@ class Playlist : public QAbstractListModel { // This returns true if this playlist had current item when the method was invoked. bool ApplyValidityOnCurrentSong(const QUrl &url, bool valid); + // Get the real position for skipping the grouped tracks + int get_real_pos(int pos, const int origin); + + // Update the list of the tracks moved + void update_list_to_move(QList &list); + // Removes from the playlist all local files that don't exist anymore. void RemoveDeletedSongs(); @@ -290,6 +304,8 @@ class Playlist : public QAbstractListModel { void ItemReload(const QPersistentModelIndex &idx, const bool metadata_edit); + void RemoveDuplicateSongs(PlaylistItemPtrList &items); + public Q_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(); @@ -301,7 +317,9 @@ class Playlist : public QAbstractListModel { void UpdateItems(SongList songs); void Clear(); - void RemoveDuplicateSongs(); + void RemoveDuplicateSongs() { + RemoveDuplicateSongs(items_); + } void RemoveUnavailableSongs(); void Shuffle(); @@ -359,7 +377,10 @@ class Playlist : public QAbstractListModel { void RemoveItemsNotInQueue(); // Removes rows with given indices from this playlist. - bool removeRows(QList &rows); + bool removeRows(QList &rows, PlaylistItemPtrList &items); + bool removeRows(QList &rows) { + return removeRows(rows, items_); + } void TurnOnDynamicPlaylist(PlaylistGeneratorPtr gen); void InsertDynamicItems(const int count); @@ -436,6 +457,9 @@ class Playlist : public QAbstractListModel { bool auto_sort_; Column sort_column_; Qt::SortOrder sort_order_; + + // Variables to insert tracks + bool remove_duplicates_; }; #endif // PLAYLIST_H diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 8118228f61..3aed5f7aaa 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -82,7 +82,8 @@ PlaylistManager::PlaylistManager(const SharedPtr task_manager, playlist_container_(nullptr), current_(-1), active_(-1), - playlists_loading_(0) { + playlists_loading_(0), + remove_duplicates_(REMOVE_DUPLICATES_DEFAULT) { setObjectName(QLatin1String(QObject::metaObject()->className())); @@ -95,6 +96,15 @@ PlaylistManager::~PlaylistManager() { } +void PlaylistManager::update_setting(const bool remove_duplicates) { + remove_duplicates_ = remove_duplicates; + + QList datas = playlists_.values(); + for (Data &data : datas) { + data.p->update_setting(remove_duplicates); + } +} + void PlaylistManager::Init(PlaylistSequence *sequence, PlaylistContainer *playlist_container) { sequence_ = sequence; @@ -155,7 +165,7 @@ QItemSelection PlaylistManager::selection(const int id) const { Playlist *PlaylistManager::AddPlaylist(const int id, const QString &name, const QString &special_type, const QString &ui_path, const bool favorite) { - Playlist *ret = new Playlist(task_manager_, url_handlers_, playlist_backend_, collection_backend_, tagreader_client_, id, special_type, favorite); + Playlist *ret = new Playlist(task_manager_, url_handlers_, playlist_backend_, collection_backend_, tagreader_client_, id, special_type, favorite, remove_duplicates_); ret->set_sequence(sequence_); ret->set_ui_path(ui_path); diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index be568aed22..e6740a053c 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -71,6 +71,10 @@ class PlaylistManager : public PlaylistManagerInterface { Playlist *playlist(const int id) const override { return playlists_[id].p; } Playlist *current() const override { return playlist(current_id()); } Playlist *active() const override { return playlist(active_id()); } + bool remove_duplicates() const { return remove_duplicates_; } + + // Update the grouped before queue value : we have to do more than just update the attribute + void update_setting(const bool remove_duplicates); // Returns the collection of playlists managed by this PlaylistManager. QList GetAllPlaylists() const override; @@ -179,6 +183,7 @@ class PlaylistManager : public PlaylistManagerInterface { int current_; int active_; int playlists_loading_; + bool remove_duplicates_; }; #endif // PLAYLISTMANAGER_H diff --git a/src/settings/playlistsettingspage.cpp b/src/settings/playlistsettingspage.cpp index 30d8da1ffb..e21eedc376 100644 --- a/src/settings/playlistsettingspage.cpp +++ b/src/settings/playlistsettingspage.cpp @@ -97,6 +97,7 @@ void PlaylistSettingsPage::Load() { ui_->checkbox_writemetadata->setChecked(s.value(kWriteMetadata, false).toBool()); ui_->checkbox_delete_files->setChecked(s.value(kDeleteFiles, false).toBool()); + ui_->checkbox_remove_duplicates->setChecked(s.value(kRemoveDuplicates, false).toBool()); s.endGroup(); @@ -138,6 +139,7 @@ void PlaylistSettingsPage::Save() { s.setValue(kEditMetadataInline, ui_->checkbox_editmetadatainline->isChecked()); s.setValue(kWriteMetadata, ui_->checkbox_writemetadata->isChecked()); s.setValue(kDeleteFiles, ui_->checkbox_delete_files->isChecked()); + s.setValue(kRemoveDuplicates, ui_->checkbox_remove_duplicates->isChecked()); s.setValue(kAutoSort, ui_->checkbox_auto_sort->isChecked()); s.endGroup(); diff --git a/src/settings/playlistsettingspage.ui b/src/settings/playlistsettingspage.ui index c01e1359c5..d2e9745c1e 100644 --- a/src/settings/playlistsettingspage.ui +++ b/src/settings/playlistsettingspage.ui @@ -91,6 +91,13 @@ + + + + Remove duplicates when adding tracks to the playlist + + + diff --git a/src/smartplaylists/smartplaylistsearchpreview.cpp b/src/smartplaylists/smartplaylistsearchpreview.cpp index 4d1620f09f..e705c28744 100644 --- a/src/smartplaylists/smartplaylistsearchpreview.cpp +++ b/src/smartplaylists/smartplaylistsearchpreview.cpp @@ -36,6 +36,7 @@ #include "playlist/playlist.h" #include "playlistquerygenerator.h" +#include "playlist/playlistmanager.h" using std::make_shared; @@ -72,7 +73,7 @@ void SmartPlaylistSearchPreview::Init(const SharedPtr player, collection_backend_ = collection_backend; - model_ = new Playlist(nullptr, nullptr, nullptr, collection_backend_, nullptr, -1, QString(), false, this); + model_ = new Playlist(nullptr, nullptr, nullptr, collection_backend_, nullptr, -1, QString(), false, playlist_manager->remove_duplicates(), this); ui_->tree->setModel(model_); ui_->tree->SetPlaylist(model_);