Skip to content

Commit 23a2071

Browse files
authored
Merge pull request #9707 from Jared234/1473_remove_duplicates_from_playlist
Remove duplicates from playlist feature
2 parents 2e3490b + 43f46e2 commit 23a2071

5 files changed

Lines changed: 78 additions & 6 deletions

File tree

app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ default Flowable<List<PlaylistStreamEntity>> listByService(final int serviceId)
108108
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
109109
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
110110

111+
@RewriteQueriesToDropUnusedColumns
112+
@Transaction
113+
@Query("SELECT *, MIN(" + JOIN_INDEX + ")"
114+
+ " FROM " + STREAM_TABLE + " INNER JOIN"
115+
+ " (SELECT " + JOIN_STREAM_ID + "," + JOIN_INDEX
116+
+ " FROM " + PLAYLIST_STREAM_JOIN_TABLE
117+
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId)"
118+
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
119+
+ " LEFT JOIN "
120+
+ "(SELECT " + JOIN_STREAM_ID + " AS " + JOIN_STREAM_ID_ALIAS + ", "
121+
+ STREAM_PROGRESS_MILLIS
122+
+ " FROM " + STREAM_STATE_TABLE + " )"
123+
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS
124+
+ " GROUP BY " + STREAM_ID
125+
+ " ORDER BY MIN(" + JOIN_INDEX + ") ASC")
126+
Flowable<List<PlaylistStreamEntry>> getStreamsWithoutDuplicates(long playlistId);
127+
111128
@Transaction
112129
@Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", "
113130
+ PLAYLIST_NAME + ", "

app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
9696
private AtomicBoolean isLoadingComplete;
9797
/* Has the playlist been modified (e.g. items reordered or deleted) */
9898
private AtomicBoolean isModified;
99-
/* Is the playlist currently being processed to remove watched videos */
100-
private boolean isRemovingWatched = false;
99+
/* Flag to prevent simultaneous rewrites of the playlist */
100+
private boolean isRewritingPlaylist = false;
101101

102102
public static LocalPlaylistFragment getInstance(final long playlistId, final String name) {
103103
final LocalPlaylistFragment instance = new LocalPlaylistFragment();
@@ -354,7 +354,7 @@ public boolean onOptionsItemSelected(final MenuItem item) {
354354
} else if (item.getItemId() == R.id.menu_item_rename_playlist) {
355355
createRenameDialog();
356356
} else if (item.getItemId() == R.id.menu_item_remove_watched) {
357-
if (!isRemovingWatched) {
357+
if (!isRewritingPlaylist) {
358358
new AlertDialog.Builder(requireContext())
359359
.setMessage(R.string.remove_watched_popup_warning)
360360
.setTitle(R.string.remove_watched_popup_title)
@@ -368,6 +368,10 @@ public boolean onOptionsItemSelected(final MenuItem item) {
368368
.create()
369369
.show();
370370
}
371+
} else if (item.getItemId() == R.id.menu_item_remove_duplicates) {
372+
if (!isRewritingPlaylist) {
373+
openRemoveDuplicatesDialog();
374+
}
371375
} else {
372376
return super.onOptionsItemSelected(item);
373377
}
@@ -389,10 +393,10 @@ public void sharePlaylist() {
389393
}
390394

391395
public void removeWatchedStreams(final boolean removePartiallyWatched) {
392-
if (isRemovingWatched) {
396+
if (isRewritingPlaylist) {
393397
return;
394398
}
395-
isRemovingWatched = true;
399+
isRewritingPlaylist = true;
396400
showLoading();
397401

398402
final var recordManager = new HistoryRecordManager(getContext());
@@ -470,7 +474,7 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
470474
}
471475

472476
hideLoading();
473-
isRemovingWatched = false;
477+
isRewritingPlaylist = false;
474478
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
475479
"Removing watched videos, partially watched=" + removePartiallyWatched))));
476480
}
@@ -629,6 +633,43 @@ private void updateThumbnailUrl() {
629633
changeThumbnailStreamId(thumbnailStreamId, false);
630634
}
631635

636+
private void openRemoveDuplicatesDialog() {
637+
final AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
638+
639+
builder.setTitle(R.string.remove_duplicates_title)
640+
.setMessage(R.string.remove_duplicates_message)
641+
.setPositiveButton(R.string.ok,
642+
(dialog, i) -> removeDuplicatesInPlaylist())
643+
.setNeutralButton(R.string.cancel, null);
644+
645+
builder.create().show();
646+
}
647+
648+
private void removeDuplicatesInPlaylist() {
649+
if (isRewritingPlaylist) {
650+
return;
651+
}
652+
isRewritingPlaylist = true;
653+
showLoading();
654+
655+
final var streamsMaybe = playlistManager
656+
.getDistinctPlaylistStreams(playlistId).firstElement();
657+
658+
659+
disposables.add(streamsMaybe.subscribeOn(Schedulers.io())
660+
.observeOn(AndroidSchedulers.mainThread())
661+
.subscribe(itemsToKeep -> {
662+
itemListAdapter.clearStreamItemList();
663+
itemListAdapter.addItems(itemsToKeep);
664+
setVideoCount(itemListAdapter.getItemsList().size());
665+
saveChanges();
666+
667+
hideLoading();
668+
isRewritingPlaylist = false;
669+
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
670+
"Removing duplicated streams"))));
671+
}
672+
632673
private void deleteItem(final PlaylistStreamEntry item) {
633674
if (itemListAdapter == null) {
634675
return;

app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ public Flowable<List<PlaylistMetadataEntry>> getPlaylists() {
9393
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
9494
}
9595

96+
public Flowable<List<PlaylistStreamEntry>> getDistinctPlaylistStreams(final long playlistId) {
97+
return playlistStreamTable
98+
.getStreamsWithoutDuplicates(playlistId).subscribeOn(Schedulers.io());
99+
}
100+
96101
/**
97102
* Get playlists with attached information about how many times the provided stream is already
98103
* contained in each playlist.

app/src/main/res/menu/menu_local_playlist.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@
1212
android:id="@+id/menu_item_rename_playlist"
1313
android:title="@string/rename_playlist"
1414
app:showAsAction="never" />
15+
1516
<item
1617
android:id="@+id/menu_item_remove_watched"
1718
android:title="@string/remove_watched"
1819
app:showAsAction="never" />
20+
21+
<item
22+
android:id="@+id/menu_item_remove_duplicates"
23+
android:title="@string/remove_duplicates"
24+
app:showAsAction="never" />
1925
</menu>

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,9 @@
632632
<string name="systems_language">System default</string>
633633
<string name="remove_watched">Remove watched</string>
634634
<string name="remove_watched_popup_title">Remove watched videos?</string>
635+
<string name="remove_duplicates">Remove duplicates</string>
636+
<string name="remove_duplicates_title">Remove duplicates?</string>
637+
<string name="remove_duplicates_message">Do you want to remove all duplicate streams in this playlist?</string>
635638
<string name="remove_watched_popup_warning">Videos that have been watched before and after being added to the playlist will be removed.
636639
\nAre you sure\? This cannot be undone!</string>
637640
<string name="remove_watched_popup_yes_and_partially_watched_videos">Yes, and partially watched videos</string>

0 commit comments

Comments
 (0)