From ce8b7303c036e58624632af073ec6d14bfa7eb4b Mon Sep 17 00:00:00 2001 From: Mark Furneaux Date: Fri, 25 Apr 2014 22:50:52 -0400 Subject: [PATCH] Formatting --- src/playlist/playlist.cpp | 933 +++++++++++++++++++------------------- 1 file changed, 465 insertions(+), 468 deletions(-) diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 04fcfc589..6550f6b6c 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -1639,495 +1639,492 @@ void Playlist::StopAfter(int row) { else stop_after_ = index(row, 0); - if (old_stop_after.isValid()) - emit dataChanged( - old_stop_after, - old_stop_after.sibling(old_stop_after.row(), ColumnCount - 1)); - if (stop_after_.isValid()) - emit dataChanged(stop_after_, - stop_after_.sibling(stop_after_.row(), ColumnCount - 1)); + if (old_stop_after.isValid()) + emit dataChanged( + old_stop_after, + old_stop_after.sibling(old_stop_after.row(), ColumnCount - 1)); + if (stop_after_.isValid()) + emit dataChanged(stop_after_, + stop_after_.sibling(stop_after_.row(), ColumnCount - 1)); +} + +void Playlist::SetStreamMetadata(const QUrl& url, const Song& song) { + qLog(Debug) << "Setting metadata for" << url << "to" << song.artist() + << song.title(); + if (!current_item()) return; + + if (current_item()->Url() != url) return; + + // Don't update the metadata if it's only a minor change from before + if (current_item()->Metadata().artist() == song.artist() && + current_item()->Metadata().title() == song.title()) + return; + + current_item()->SetTemporaryMetadata(song); + UpdateScrobblePoint(); + + InformOfCurrentSongChange(); +} + +void Playlist::ClearStreamMetadata() { + if (!current_item()) return; + + current_item()->ClearTemporaryMetadata(); + UpdateScrobblePoint(); + + emit dataChanged(index(current_item_index_.row(), 0), + index(current_item_index_.row(), ColumnCount - 1)); +} + +bool Playlist::stop_after_current() const { + return stop_after_.isValid() && current_item_index_.isValid() && + stop_after_.row() == current_item_index_.row(); +} + +PlaylistItemPtr Playlist::current_item() const { + // QList[] runs in constant time, so no need to cache current_item + if (current_item_index_.isValid() && + current_item_index_.row() <= items_.length()) + return items_[current_item_index_.row()]; + return PlaylistItemPtr(); +} + +PlaylistItem::Options Playlist::current_item_options() const { + if (!current_item()) return PlaylistItem::Default; + + return current_item()->options(); +} + +Song Playlist::current_item_metadata() const { + if (!current_item()) return Song(); + + return current_item()->Metadata(); +} + +void Playlist::UpdateScrobblePoint() { + const qint64 length = current_item_metadata().length_nanosec(); + + if (length == 0) { + scrobble_point_ = 240ll * kNsecPerSec; // 4 minutes + } else { + scrobble_point_ = + qBound(31ll * kNsecPerSec, length / 2, 240ll * kNsecPerSec); } - void Playlist::SetStreamMetadata(const QUrl& url, const Song& song) { - qLog(Debug) << "Setting metadata for" << url << "to" << song.artist() - << song.title(); - if (!current_item()) return; + set_lastfm_status(LastFM_New); + have_incremented_playcount_ = false; +} - if (current_item()->Url() != url) return; +void Playlist::Clear() { + const int count = items_.count(); - // Don't update the metadata if it's only a minor change from before - if (current_item()->Metadata().artist() == song.artist() && - current_item()->Metadata().title() == song.title()) - return; - - current_item()->SetTemporaryMetadata(song); - UpdateScrobblePoint(); - - InformOfCurrentSongChange(); + if (count > kUndoItemLimit) { + // Too big to keep in the undo stack. Also clear the stack because it + // might have been invalidated. + RemoveItemsWithoutUndo(0, count); + undo_stack_->clear(); + } else { + undo_stack_->push(new PlaylistUndoCommands::RemoveItems(this, 0, count)); } - void Playlist::ClearStreamMetadata() { - if (!current_item()) return; + TurnOffDynamicPlaylist(); - current_item()->ClearTemporaryMetadata(); - UpdateScrobblePoint(); + Save(); +} - emit dataChanged(index(current_item_index_.row(), 0), - index(current_item_index_.row(), ColumnCount - 1)); +void Playlist::TurnOffDynamicPlaylist() { + dynamic_playlist_.reset(); + + if (playlist_sequence_) { + playlist_sequence_->SetUsingDynamicPlaylist(false); + ShuffleModeChanged(playlist_sequence_->shuffle_mode()); + } + emit DynamicModeChanged(false); + Save(); +} + +void Playlist::RepopulateDynamicPlaylist() { + if (!dynamic_playlist_) return; + + RemoveItemsNotInQueue(); + InsertSmartPlaylist(dynamic_playlist_); +} + +void Playlist::ExpandDynamicPlaylist() { + if (!dynamic_playlist_) return; + + InsertDynamicItems(5); +} + +void Playlist::RemoveItemsNotInQueue() { + if (queue_->is_empty()) { + RemoveItemsWithoutUndo(0, items_.count()); + return; } - bool Playlist::stop_after_current() const { - return stop_after_.isValid() && current_item_index_.isValid() && - stop_after_.row() == current_item_index_.row(); - } - - PlaylistItemPtr Playlist::current_item() const { - // QList[] runs in constant time, so no need to cache current_item - if (current_item_index_.isValid() && - current_item_index_.row() <= items_.length()) - return items_[current_item_index_.row()]; - return PlaylistItemPtr(); - } - - PlaylistItem::Options Playlist::current_item_options() const { - if (!current_item()) return PlaylistItem::Default; - - return current_item()->options(); - } - - Song Playlist::current_item_metadata() const { - if (!current_item()) return Song(); - - return current_item()->Metadata(); - } - - void Playlist::UpdateScrobblePoint() { - const qint64 length = current_item_metadata().length_nanosec(); - - if (length == 0) { - scrobble_point_ = 240ll * kNsecPerSec; // 4 minutes - } else { - scrobble_point_ = - qBound(31ll * kNsecPerSec, length / 2, 240ll * kNsecPerSec); - } - - set_lastfm_status(LastFM_New); - have_incremented_playcount_ = false; - } - - void Playlist::Clear() { - const int count = items_.count(); - - if (count > kUndoItemLimit) { - // Too big to keep in the undo stack. Also clear the stack because it - // might have been invalidated. - RemoveItemsWithoutUndo(0, count); - undo_stack_->clear(); - } else { - undo_stack_->push(new PlaylistUndoCommands::RemoveItems(this, 0, count)); - } - - TurnOffDynamicPlaylist(); - - Save(); - } - - void Playlist::TurnOffDynamicPlaylist() { - dynamic_playlist_.reset(); - - if (playlist_sequence_) { - playlist_sequence_->SetUsingDynamicPlaylist(false); - ShuffleModeChanged(playlist_sequence_->shuffle_mode()); - } - emit DynamicModeChanged(false); - Save(); - } - - void Playlist::RepopulateDynamicPlaylist() { - if (!dynamic_playlist_) return; - - RemoveItemsNotInQueue(); - InsertSmartPlaylist(dynamic_playlist_); - } - - void Playlist::ExpandDynamicPlaylist() { - if (!dynamic_playlist_) return; - - InsertDynamicItems(5); - } - - void Playlist::RemoveItemsNotInQueue() { - if (queue_->is_empty()) { - RemoveItemsWithoutUndo(0, items_.count()); - return; - } - - int start = 0; + int start = 0; + forever { + // Find a place to start - first row that isn't in the queue forever { - // Find a place to start - first row that isn't in the queue - forever { - if (start >= rowCount()) return; - if (!queue_->ContainsSourceRow(start)) break; - start++; - } - - // Figure out how many rows to remove - keep going until we find a row - // that is in the queue - int count = 1; - forever { - if (start + count >= rowCount()) break; - if (queue_->ContainsSourceRow(start + count)) break; - count++; - } - - RemoveItemsWithoutUndo(start, count); + if (start >= rowCount()) return; + if (!queue_->ContainsSourceRow(start)) break; start++; } + + // Figure out how many rows to remove - keep going until we find a row + // that is in the queue + int count = 1; + forever { + if (start + count >= rowCount()) break; + if (queue_->ContainsSourceRow(start + count)) break; + count++; + } + + RemoveItemsWithoutUndo(start, count); + start++; + } +} + +void Playlist::ReloadItems(const QList& rows) { + for (int row : rows) { + PlaylistItemPtr item = item_at(row); + + item->Reload(); + + if (row == current_row()) { + InformOfCurrentSongChange(); + } else { + emit dataChanged(index(row, 0), index(row, ColumnCount - 1)); + } } - void Playlist::ReloadItems(const QList& rows) { - for (int row : rows) { - PlaylistItemPtr item = item_at(row); + Save(); +} - item->Reload(); +void Playlist::RateSong(const QModelIndex& index, double rating) { + int row = index.row(); - if (row == current_row()) { - InformOfCurrentSongChange(); + if (has_item_at(row)) { + PlaylistItemPtr item = item_at(row); + if (item && item->IsLocalLibraryItem() && item->Metadata().id() != -1) { + library_->UpdateSongRatingAsync(item->Metadata().id(), rating); + } + } +} + +void Playlist::AddSongInsertVetoListener(SongInsertVetoListener* listener) { + veto_listeners_.append(listener); + connect(listener, SIGNAL(destroyed()), this, + SLOT(SongInsertVetoListenerDestroyed())); +} + +void Playlist::RemoveSongInsertVetoListener(SongInsertVetoListener* listener) { + disconnect(listener, SIGNAL(destroyed()), this, + SLOT(SongInsertVetoListenerDestroyed())); + veto_listeners_.removeAll(listener); +} + +void Playlist::SongInsertVetoListenerDestroyed() { + veto_listeners_.removeAll(qobject_cast(sender())); +} + +void Playlist::Shuffle() { + PlaylistItemList new_items(items_); + + int begin = 0; + if (dynamic_playlist_ && current_item_index_.isValid()) + begin += current_item_index_.row() + 1; + + const int count = items_.count(); + for (int i = begin; i < count; ++i) { + int new_pos = i + (rand() % (count - i)); + + std::swap(new_items[i], new_items[new_pos]); + } + + undo_stack_->push(new PlaylistUndoCommands::ShuffleItems(this, new_items)); +} + +namespace { +bool AlbumShuffleComparator(const QMap& album_key_positions, + const QMap& album_keys, int left, + int right) { + const int left_pos = album_key_positions[album_keys[left]]; + const int right_pos = album_key_positions[album_keys[right]]; + + if (left_pos == right_pos) return left < right; + return left_pos < right_pos; +} +} + +void Playlist::ReshuffleIndices() { + if (!playlist_sequence_) { + return; + } + + if (playlist_sequence_->shuffle_mode() == PlaylistSequence::Shuffle_Off) { + // No shuffling - sort the virtual item list normally. + std::sort(virtual_items_.begin(), virtual_items_.end()); + if (current_row() != -1) + current_virtual_index_ = virtual_items_.indexOf(current_row()); + return; + } + + // If the user is already playing a song, advance the begin iterator to + // only shuffle items that haven't been played yet. + QList::iterator begin = virtual_items_.begin(); + QList::iterator end = virtual_items_.end(); + if (current_virtual_index_ != -1) + std::advance(begin, current_virtual_index_ + 1); + + switch (playlist_sequence_->shuffle_mode()) { + case PlaylistSequence::Shuffle_Off: + // Handled above. + break; + + case PlaylistSequence::Shuffle_All: + case PlaylistSequence::Shuffle_InsideAlbum: + std::random_shuffle(begin, end); + break; + + case PlaylistSequence::Shuffle_Albums: { + QMap album_keys; // real index -> key + QSet album_key_set; // unique keys + + // Find all the unique albums in the playlist + for (QList::iterator it = begin; it != end; ++it) { + const int index = *it; + const QString key = items_[index]->Metadata().AlbumKey(); + album_keys[index] = key; + album_key_set << key; + } + + // Shuffle them + QStringList shuffled_album_keys = album_key_set.toList(); + std::random_shuffle(shuffled_album_keys.begin(), + shuffled_album_keys.end()); + + // If the user is currently playing a song, force its album to be first. + if (current_virtual_index_ != -1) { + const QString key = items_[current_row()]->Metadata().AlbumKey(); + const int pos = shuffled_album_keys.indexOf(key); + if (pos >= 1) { + std::swap(shuffled_album_keys[0], shuffled_album_keys[pos]); + } + } + + // Create album key -> position mapping for fast lookup + QMap album_key_positions; + for (int i = 0; i < shuffled_album_keys.count(); ++i) { + album_key_positions[shuffled_album_keys[i]] = i; + } + + // Sort the virtual items + std::stable_sort(begin, end, + std::bind(AlbumShuffleComparator, album_key_positions, + album_keys, _1, _2)); + + break; + } + } +} + +void Playlist::set_sequence(PlaylistSequence* v) { + playlist_sequence_ = v; + connect(v, SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)), + SLOT(ShuffleModeChanged(PlaylistSequence::ShuffleMode))); + + ShuffleModeChanged(v->shuffle_mode()); +} + +QSortFilterProxyModel* Playlist::proxy() const { return proxy_; } + +SongList Playlist::GetAllSongs() const { + SongList ret; + for (PlaylistItemPtr item : items_) { + ret << item->Metadata(); + } + return ret; +} + +PlaylistItemList Playlist::GetAllItems() const { return items_; } + +quint64 Playlist::GetTotalLength() const { + quint64 ret = 0; + for (PlaylistItemPtr item : items_) { + quint64 length = item->Metadata().length_nanosec(); + if (length > 0) ret += length; + } + return ret; +} + +PlaylistItemList Playlist::library_items_by_id(int id) const { + return library_items_by_id_.values(id); +} + +void Playlist::TracksAboutToBeDequeued(const QModelIndex&, int begin, int end) { + for (int i = begin; i <= end; ++i) { + temp_dequeue_change_indexes_ + << queue_->mapToSource(queue_->index(i, Column_Title)); + } +} + +void Playlist::TracksDequeued() { + for (const QModelIndex& index : temp_dequeue_change_indexes_) { + emit dataChanged(index, index); + } + temp_dequeue_change_indexes_.clear(); +} + +void Playlist::TracksEnqueued(const QModelIndex&, int begin, int end) { + const QModelIndex& b = + queue_->mapToSource(queue_->index(begin, Column_Title)); + const QModelIndex& e = queue_->mapToSource(queue_->index(end, Column_Title)); + emit dataChanged(b, e); +} + +void Playlist::QueueLayoutChanged() { + for (int i = 0; i < queue_->rowCount(); ++i) { + const QModelIndex& index = + queue_->mapToSource(queue_->index(i, Column_Title)); + emit dataChanged(index, index); + } +} + +void Playlist::ItemChanged(PlaylistItemPtr item) { + for (int row = 0; row < items_.count(); ++row) { + if (items_[row] == item) { + emit dataChanged(index(row, 0), index(row, ColumnCount - 1)); + return; + } + } +} + +void Playlist::InformOfCurrentSongChange() { + emit dataChanged(index(current_item_index_.row(), 0), + index(current_item_index_.row(), ColumnCount - 1)); + + // 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()) { + emit CurrentSongChanged(metadata); + } +} + +void Playlist::InvalidateDeletedSongs() { + QList invalidated_rows; + + for (int row = 0; row < items_.count(); ++row) { + PlaylistItemPtr item = items_[row]; + Song song = item->Metadata(); + + if (!song.is_stream()) { + bool exists = QFile::exists(song.url().toLocalFile()); + + if (!exists && !item->HasForegroundColor(kInvalidSongPriority)) { + // gray out the song if it's not there + item->SetForegroundColor(kInvalidSongPriority, kInvalidSongColor); + invalidated_rows.append(row); + } else if (exists && item->HasForegroundColor(kInvalidSongPriority)) { + item->RemoveForegroundColor(kInvalidSongPriority); + invalidated_rows.append(row); + } + } + } + + ReloadItems(invalidated_rows); +} + +void Playlist::RemoveDeletedSongs() { + QList rows_to_remove; + + for (int row = 0; row < items_.count(); ++row) { + PlaylistItemPtr item = items_[row]; + Song song = item->Metadata(); + + if (!song.is_stream() && !QFile::exists(song.url().toLocalFile())) { + rows_to_remove.append(row); + } + } + + removeRows(rows_to_remove); +} + +struct SongSimilarHash { + long operator()(const Song& song) const { return HashSimilar(song); } +}; + +struct SongSimilarEqual { + long operator()(const Song& song1, const Song& song2) const { + return song1.IsSimilar(song2); + } +}; + +void Playlist::RemoveDuplicateSongs() { + QList rows_to_remove; + unordered_map unique_songs; + + for (int row = 0; row < items_.count(); ++row) { + PlaylistItemPtr item = items_[row]; + const Song& song = item->Metadata(); + + bool found_duplicate = false; + + auto uniq_song_it = unique_songs.find(song); + if (uniq_song_it != unique_songs.end()) { + const Song& uniq_song = uniq_song_it->first; + + if (song.bitrate() > uniq_song.bitrate()) { + rows_to_remove.append(unique_songs[uniq_song]); + unique_songs.erase(uniq_song); + unique_songs.insert(std::make_pair(song, row)); } else { - emit dataChanged(index(row, 0), index(row, ColumnCount - 1)); - } - } - - Save(); - } - - void Playlist::RateSong(const QModelIndex& index, double rating) { - int row = index.row(); - - if (has_item_at(row)) { - PlaylistItemPtr item = item_at(row); - if (item && item->IsLocalLibraryItem() && item->Metadata().id() != -1) { - library_->UpdateSongRatingAsync(item->Metadata().id(), rating); - } - } - } - - void Playlist::AddSongInsertVetoListener(SongInsertVetoListener * listener) { - veto_listeners_.append(listener); - connect(listener, SIGNAL(destroyed()), this, - SLOT(SongInsertVetoListenerDestroyed())); - } - - void Playlist::RemoveSongInsertVetoListener(SongInsertVetoListener * - listener) { - disconnect(listener, SIGNAL(destroyed()), this, - SLOT(SongInsertVetoListenerDestroyed())); - veto_listeners_.removeAll(listener); - } - - void Playlist::SongInsertVetoListenerDestroyed() { - veto_listeners_.removeAll(qobject_cast(sender())); - } - - void Playlist::Shuffle() { - PlaylistItemList new_items(items_); - - int begin = 0; - if (dynamic_playlist_ && current_item_index_.isValid()) - begin += current_item_index_.row() + 1; - - const int count = items_.count(); - for (int i = begin; i < count; ++i) { - int new_pos = i + (rand() % (count - i)); - - std::swap(new_items[i], new_items[new_pos]); - } - - undo_stack_->push(new PlaylistUndoCommands::ShuffleItems(this, new_items)); - } - - namespace { - bool AlbumShuffleComparator(const QMap& album_key_positions, - const QMap& album_keys, int left, - int right) { - const int left_pos = album_key_positions[album_keys[left]]; - const int right_pos = album_key_positions[album_keys[right]]; - - if (left_pos == right_pos) return left < right; - return left_pos < right_pos; - } - } - - void Playlist::ReshuffleIndices() { - if (!playlist_sequence_) { - return; - } - - if (playlist_sequence_->shuffle_mode() == PlaylistSequence::Shuffle_Off) { - // No shuffling - sort the virtual item list normally. - std::sort(virtual_items_.begin(), virtual_items_.end()); - if (current_row() != -1) - current_virtual_index_ = virtual_items_.indexOf(current_row()); - return; - } - - // If the user is already playing a song, advance the begin iterator to - // only shuffle items that haven't been played yet. - QList::iterator begin = virtual_items_.begin(); - QList::iterator end = virtual_items_.end(); - if (current_virtual_index_ != -1) - std::advance(begin, current_virtual_index_ + 1); - - switch (playlist_sequence_->shuffle_mode()) { - case PlaylistSequence::Shuffle_Off: - // Handled above. - break; - - case PlaylistSequence::Shuffle_All: - case PlaylistSequence::Shuffle_InsideAlbum: - std::random_shuffle(begin, end); - break; - - case PlaylistSequence::Shuffle_Albums: { - QMap album_keys; // real index -> key - QSet album_key_set; // unique keys - - // Find all the unique albums in the playlist - for (QList::iterator it = begin; it != end; ++it) { - const int index = *it; - const QString key = items_[index]->Metadata().AlbumKey(); - album_keys[index] = key; - album_key_set << key; - } - - // Shuffle them - QStringList shuffled_album_keys = album_key_set.toList(); - std::random_shuffle(shuffled_album_keys.begin(), - shuffled_album_keys.end()); - - // If the user is currently playing a song, force its album to be first. - if (current_virtual_index_ != -1) { - const QString key = items_[current_row()]->Metadata().AlbumKey(); - const int pos = shuffled_album_keys.indexOf(key); - if (pos >= 1) { - std::swap(shuffled_album_keys[0], shuffled_album_keys[pos]); - } - } - - // Create album key -> position mapping for fast lookup - QMap album_key_positions; - for (int i = 0; i < shuffled_album_keys.count(); ++i) { - album_key_positions[shuffled_album_keys[i]] = i; - } - - // Sort the virtual items - std::stable_sort(begin, end, - std::bind(AlbumShuffleComparator, album_key_positions, - album_keys, _1, _2)); - - break; - } - } - } - - void Playlist::set_sequence(PlaylistSequence * v) { - playlist_sequence_ = v; - connect(v, SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)), - SLOT(ShuffleModeChanged(PlaylistSequence::ShuffleMode))); - - ShuffleModeChanged(v->shuffle_mode()); - } - - QSortFilterProxyModel* Playlist::proxy() const { return proxy_; } - - SongList Playlist::GetAllSongs() const { - SongList ret; - for (PlaylistItemPtr item : items_) { - ret << item->Metadata(); - } - return ret; - } - - PlaylistItemList Playlist::GetAllItems() const { return items_; } - - quint64 Playlist::GetTotalLength() const { - quint64 ret = 0; - for (PlaylistItemPtr item : items_) { - quint64 length = item->Metadata().length_nanosec(); - if (length > 0) ret += length; - } - return ret; - } - - PlaylistItemList Playlist::library_items_by_id(int id) const { - return library_items_by_id_.values(id); - } - - void Playlist::TracksAboutToBeDequeued(const QModelIndex&, int begin, - int end) { - for (int i = begin; i <= end; ++i) { - temp_dequeue_change_indexes_ - << queue_->mapToSource(queue_->index(i, Column_Title)); - } - } - - void Playlist::TracksDequeued() { - for (const QModelIndex& index : temp_dequeue_change_indexes_) { - emit dataChanged(index, index); - } - temp_dequeue_change_indexes_.clear(); - } - - void Playlist::TracksEnqueued(const QModelIndex&, int begin, int end) { - const QModelIndex& b = - queue_->mapToSource(queue_->index(begin, Column_Title)); - const QModelIndex& e = - queue_->mapToSource(queue_->index(end, Column_Title)); - emit dataChanged(b, e); - } - - void Playlist::QueueLayoutChanged() { - for (int i = 0; i < queue_->rowCount(); ++i) { - const QModelIndex& index = - queue_->mapToSource(queue_->index(i, Column_Title)); - emit dataChanged(index, index); - } - } - - void Playlist::ItemChanged(PlaylistItemPtr item) { - for (int row = 0; row < items_.count(); ++row) { - if (items_[row] == item) { - emit dataChanged(index(row, 0), index(row, ColumnCount - 1)); - return; - } - } - } - - void Playlist::InformOfCurrentSongChange() { - emit dataChanged(index(current_item_index_.row(), 0), - index(current_item_index_.row(), ColumnCount - 1)); - - // 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()) { - emit CurrentSongChanged(metadata); - } - } - - void Playlist::InvalidateDeletedSongs() { - QList invalidated_rows; - - for (int row = 0; row < items_.count(); ++row) { - PlaylistItemPtr item = items_[row]; - Song song = item->Metadata(); - - if (!song.is_stream()) { - bool exists = QFile::exists(song.url().toLocalFile()); - - if (!exists && !item->HasForegroundColor(kInvalidSongPriority)) { - // gray out the song if it's not there - item->SetForegroundColor(kInvalidSongPriority, kInvalidSongColor); - invalidated_rows.append(row); - } else if (exists && item->HasForegroundColor(kInvalidSongPriority)) { - item->RemoveForegroundColor(kInvalidSongPriority); - invalidated_rows.append(row); - } - } - } - - ReloadItems(invalidated_rows); - } - - void Playlist::RemoveDeletedSongs() { - QList rows_to_remove; - - for (int row = 0; row < items_.count(); ++row) { - PlaylistItemPtr item = items_[row]; - Song song = item->Metadata(); - - if (!song.is_stream() && !QFile::exists(song.url().toLocalFile())) { rows_to_remove.append(row); } + found_duplicate = true; } - removeRows(rows_to_remove); - } - - struct SongSimilarHash { - long operator()(const Song& song) const { return HashSimilar(song); } - }; - - struct SongSimilarEqual { - long operator()(const Song& song1, const Song& song2) const { - return song1.IsSimilar(song2); - } - }; - - void Playlist::RemoveDuplicateSongs() { - QList rows_to_remove; - unordered_map unique_songs; - - for (int row = 0; row < items_.count(); ++row) { - PlaylistItemPtr item = items_[row]; - const Song& song = item->Metadata(); - - bool found_duplicate = false; - - auto uniq_song_it = unique_songs.find(song); - if (uniq_song_it != unique_songs.end()) { - const Song& uniq_song = uniq_song_it->first; - - if (song.bitrate() > uniq_song.bitrate()) { - rows_to_remove.append(unique_songs[uniq_song]); - unique_songs.erase(uniq_song); - unique_songs.insert(std::make_pair(song, row)); - } else { - rows_to_remove.append(row); - } - found_duplicate = true; - } - - if (!found_duplicate) { - unique_songs.insert(std::make_pair(song, row)); - } - } - - removeRows(rows_to_remove); - } - - bool Playlist::ApplyValidityOnCurrentSong(const QUrl& url, bool valid) { - PlaylistItemPtr current = current_item(); - - if (current) { - Song current_song = current->Metadata(); - - // if validity has changed, reload the item - if (!current_song.is_stream() && !current_song.is_cdda() && - current_song.url() == url && - current_song.is_valid() != - QFile::exists(current_song.url().toLocalFile())) { - ReloadItems(QList() << current_row()); - } - - // gray out the song if it's now broken; otherwise undo the gray color - if (valid) { - current->RemoveForegroundColor(kInvalidSongPriority); - } else { - current->SetForegroundColor(kInvalidSongPriority, kInvalidSongColor); - } - } - - return static_cast(current); - } - - void Playlist::SetColumnAlignment(const ColumnAlignmentMap& alignment) { - column_alignments_ = alignment; - } - - void Playlist::SkipTracks(const QModelIndexList& source_indexes) { - for (const QModelIndex& source_index : source_indexes) { - PlaylistItemPtr track_to_skip = item_at(source_index.row()); - track_to_skip->SetShouldSkip(!((track_to_skip)->GetShouldSkip())); + if (!found_duplicate) { + unique_songs.insert(std::make_pair(song, row)); } } + + removeRows(rows_to_remove); +} + +bool Playlist::ApplyValidityOnCurrentSong(const QUrl& url, bool valid) { + PlaylistItemPtr current = current_item(); + + if (current) { + Song current_song = current->Metadata(); + + // if validity has changed, reload the item + if (!current_song.is_stream() && !current_song.is_cdda() && + current_song.url() == url && + current_song.is_valid() != + QFile::exists(current_song.url().toLocalFile())) { + ReloadItems(QList() << current_row()); + } + + // gray out the song if it's now broken; otherwise undo the gray color + if (valid) { + current->RemoveForegroundColor(kInvalidSongPriority); + } else { + current->SetForegroundColor(kInvalidSongPriority, kInvalidSongColor); + } + } + + return static_cast(current); +} + +void Playlist::SetColumnAlignment(const ColumnAlignmentMap& alignment) { + column_alignments_ = alignment; +} + +void Playlist::SkipTracks(const QModelIndexList& source_indexes) { + for (const QModelIndex& source_index : source_indexes) { + PlaylistItemPtr track_to_skip = item_at(source_index.row()); + track_to_skip->SetShouldSkip(!((track_to_skip)->GetShouldSkip())); + } +}