Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove duplicates from playlist feature #9707

Merged
merged 9 commits into from
Feb 28, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,23 @@ default Flowable<List<PlaylistStreamEntity>> listByService(final int serviceId)
+ " GROUP BY " + JOIN_PLAYLIST_ID
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();

@RewriteQueriesToDropUnusedColumns
@Transaction
@Query("SELECT *, MIN(" + JOIN_INDEX + ") FROM " + STREAM_TABLE + " INNER JOIN "
+ "(SELECT " + JOIN_STREAM_ID + "," + JOIN_INDEX
Jared234 marked this conversation as resolved.
Show resolved Hide resolved
+ " FROM " + PLAYLIST_STREAM_JOIN_TABLE
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId)"
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
+ " LEFT JOIN "
+ "(SELECT " + JOIN_STREAM_ID + " AS " + JOIN_STREAM_ID_ALIAS + ", "
+ STREAM_PROGRESS_MILLIS
+ " FROM " + STREAM_STATE_TABLE + " )"
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS
+ " GROUP BY " + STREAM_ID
+ " ORDER BY " + JOIN_INDEX + " ASC")
Jared234 marked this conversation as resolved.
Show resolved Hide resolved
Flowable<List<PlaylistStreamEntry>> getStreamsWithoutDuplicates(long playlistId);



}
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ public boolean onOptionsItemSelected(final MenuItem item) {
.create()
.show();
}
} else if (item.getItemId() == R.id.menu_item_remove_duplicates) {
openRemoveDuplicatesDialog();
} else {
return super.onOptionsItemSelected(item);
}
Expand Down Expand Up @@ -621,6 +623,31 @@ private void updateThumbnailUrl() {
changeThumbnailUrl(newThumbnailUrl);
}


private void openRemoveDuplicatesDialog() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());

builder.setTitle(R.string.remove_duplicates_title)
.setMessage(R.string.remove_duplicates_message)
.setPositiveButton(android.R.string.yes, (dialog, i) -> {
Stypox marked this conversation as resolved.
Show resolved Hide resolved
removeDuplicatesInPlaylist();
})
.setNeutralButton(R.string.cancel, null);

builder.create().show();
}

private void removeDuplicatesInPlaylist() {
final List<PlaylistStreamEntry> itemsToKeep = playlistManager
.getDistinctPlaylistStreams(playlistId).blockingFirst();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this run in a separate thread, just like removeWatchedStreams? Also, while duplicates are being removed, the loading screen should be shown.

You could rename isRemovingWatched to isRewritingPlaylist and use it to prevent concurrent calls to removeDuplicates and removeWatched. Unfortunately this has never been the best solution (since there are other functions that change the playlist that are not guarded by that boolean, and so you might still get concurrent changes to the playlist), but there's no need to rework state handling in this PR (a viewmodel would be needed), so keep it as it was before.


itemListAdapter.clearStreamItemList();
itemListAdapter.addItems(itemsToKeep);
setVideoCount(itemListAdapter.getItemsList().size());

saveChanges();
}

private void deleteItem(final PlaylistStreamEntry item) {
if (itemListAdapter == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public Flowable<List<PlaylistMetadataEntry>> getPlaylists() {
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
}

public Flowable<List<PlaylistStreamEntry>> getDistinctPlaylistStreams(final long playlistId) {
return playlistStreamTable
.getStreamsWithoutDuplicates(playlistId).subscribeOn(Schedulers.io());
}

public Flowable<List<PlaylistStreamEntry>> getPlaylistStreams(final long playlistId) {
return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io());
}
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/menu/menu_local_playlist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@
android:id="@+id/menu_item_rename_playlist"
android:title="@string/rename_playlist"
app:showAsAction="never" />

<item
android:id="@+id/menu_item_remove_watched"
android:title="@string/remove_watched"
app:showAsAction="never" />

<item
android:id="@+id/menu_item_remove_duplicates"
android:title="@string/remove_duplicates"
app:showAsAction="never" />
</menu>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,9 @@
<string name="systems_language">System default</string>
<string name="remove_watched">Remove watched</string>
<string name="remove_watched_popup_title">Remove watched videos?</string>
<string name="remove_duplicates">Remove duplicates</string>
<string name="remove_duplicates_title">Remove duplicates?</string>
<string name="remove_duplicates_message">Do you want to remove all duplicate streams in this playlist?</string>
<string name="remove_watched_popup_warning">Videos that have been watched before and after being added to the playlist will be removed.
\nAre you sure\? This cannot be undone!</string>
<string name="remove_watched_popup_yes_and_partially_watched_videos">Yes, and partially watched videos</string>
Expand Down