Merge pull request #9707 from Jared234/1473_remove_duplicates_from_playlist
Remove duplicates from playlist feature
This commit is contained in:
commit
23a20712da
|
@ -108,6 +108,23 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> {
|
||||||
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
|
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
|
||||||
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
|
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
|
||||||
|
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT *, MIN(" + JOIN_INDEX + ")"
|
||||||
|
+ " FROM " + STREAM_TABLE + " INNER JOIN"
|
||||||
|
+ " (SELECT " + JOIN_STREAM_ID + "," + JOIN_INDEX
|
||||||
|
+ " 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 MIN(" + JOIN_INDEX + ") ASC")
|
||||||
|
Flowable<List<PlaylistStreamEntry>> getStreamsWithoutDuplicates(long playlistId);
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", "
|
@Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", "
|
||||||
+ PLAYLIST_NAME + ", "
|
+ PLAYLIST_NAME + ", "
|
||||||
|
|
|
@ -96,8 +96,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
private AtomicBoolean isLoadingComplete;
|
private AtomicBoolean isLoadingComplete;
|
||||||
/* Has the playlist been modified (e.g. items reordered or deleted) */
|
/* Has the playlist been modified (e.g. items reordered or deleted) */
|
||||||
private AtomicBoolean isModified;
|
private AtomicBoolean isModified;
|
||||||
/* Is the playlist currently being processed to remove watched videos */
|
/* Flag to prevent simultaneous rewrites of the playlist */
|
||||||
private boolean isRemovingWatched = false;
|
private boolean isRewritingPlaylist = false;
|
||||||
|
|
||||||
public static LocalPlaylistFragment getInstance(final long playlistId, final String name) {
|
public static LocalPlaylistFragment getInstance(final long playlistId, final String name) {
|
||||||
final LocalPlaylistFragment instance = new LocalPlaylistFragment();
|
final LocalPlaylistFragment instance = new LocalPlaylistFragment();
|
||||||
|
@ -354,7 +354,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
} else if (item.getItemId() == R.id.menu_item_rename_playlist) {
|
} else if (item.getItemId() == R.id.menu_item_rename_playlist) {
|
||||||
createRenameDialog();
|
createRenameDialog();
|
||||||
} else if (item.getItemId() == R.id.menu_item_remove_watched) {
|
} else if (item.getItemId() == R.id.menu_item_remove_watched) {
|
||||||
if (!isRemovingWatched) {
|
if (!isRewritingPlaylist) {
|
||||||
new AlertDialog.Builder(requireContext())
|
new AlertDialog.Builder(requireContext())
|
||||||
.setMessage(R.string.remove_watched_popup_warning)
|
.setMessage(R.string.remove_watched_popup_warning)
|
||||||
.setTitle(R.string.remove_watched_popup_title)
|
.setTitle(R.string.remove_watched_popup_title)
|
||||||
|
@ -368,6 +368,10 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
.create()
|
.create()
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
} else if (item.getItemId() == R.id.menu_item_remove_duplicates) {
|
||||||
|
if (!isRewritingPlaylist) {
|
||||||
|
openRemoveDuplicatesDialog();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -389,10 +393,10 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeWatchedStreams(final boolean removePartiallyWatched) {
|
public void removeWatchedStreams(final boolean removePartiallyWatched) {
|
||||||
if (isRemovingWatched) {
|
if (isRewritingPlaylist) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isRemovingWatched = true;
|
isRewritingPlaylist = true;
|
||||||
showLoading();
|
showLoading();
|
||||||
|
|
||||||
final var recordManager = new HistoryRecordManager(getContext());
|
final var recordManager = new HistoryRecordManager(getContext());
|
||||||
|
@ -470,7 +474,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
}
|
}
|
||||||
|
|
||||||
hideLoading();
|
hideLoading();
|
||||||
isRemovingWatched = false;
|
isRewritingPlaylist = false;
|
||||||
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
|
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
|
||||||
"Removing watched videos, partially watched=" + removePartiallyWatched))));
|
"Removing watched videos, partially watched=" + removePartiallyWatched))));
|
||||||
}
|
}
|
||||||
|
@ -629,6 +633,43 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
changeThumbnailStreamId(thumbnailStreamId, false);
|
changeThumbnailStreamId(thumbnailStreamId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(R.string.ok,
|
||||||
|
(dialog, i) -> removeDuplicatesInPlaylist())
|
||||||
|
.setNeutralButton(R.string.cancel, null);
|
||||||
|
|
||||||
|
builder.create().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeDuplicatesInPlaylist() {
|
||||||
|
if (isRewritingPlaylist) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isRewritingPlaylist = true;
|
||||||
|
showLoading();
|
||||||
|
|
||||||
|
final var streamsMaybe = playlistManager
|
||||||
|
.getDistinctPlaylistStreams(playlistId).firstElement();
|
||||||
|
|
||||||
|
|
||||||
|
disposables.add(streamsMaybe.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(itemsToKeep -> {
|
||||||
|
itemListAdapter.clearStreamItemList();
|
||||||
|
itemListAdapter.addItems(itemsToKeep);
|
||||||
|
setVideoCount(itemListAdapter.getItemsList().size());
|
||||||
|
saveChanges();
|
||||||
|
|
||||||
|
hideLoading();
|
||||||
|
isRewritingPlaylist = false;
|
||||||
|
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
|
||||||
|
"Removing duplicated streams"))));
|
||||||
|
}
|
||||||
|
|
||||||
private void deleteItem(final PlaylistStreamEntry item) {
|
private void deleteItem(final PlaylistStreamEntry item) {
|
||||||
if (itemListAdapter == null) {
|
if (itemListAdapter == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -93,6 +93,11 @@ public class LocalPlaylistManager {
|
||||||
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
|
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Flowable<List<PlaylistStreamEntry>> getDistinctPlaylistStreams(final long playlistId) {
|
||||||
|
return playlistStreamTable
|
||||||
|
.getStreamsWithoutDuplicates(playlistId).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get playlists with attached information about how many times the provided stream is already
|
* Get playlists with attached information about how many times the provided stream is already
|
||||||
* contained in each playlist.
|
* contained in each playlist.
|
||||||
|
|
|
@ -12,8 +12,14 @@
|
||||||
android:id="@+id/menu_item_rename_playlist"
|
android:id="@+id/menu_item_rename_playlist"
|
||||||
android:title="@string/rename_playlist"
|
android:title="@string/rename_playlist"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_item_remove_watched"
|
android:id="@+id/menu_item_remove_watched"
|
||||||
android:title="@string/remove_watched"
|
android:title="@string/remove_watched"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_item_remove_duplicates"
|
||||||
|
android:title="@string/remove_duplicates"
|
||||||
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -632,6 +632,9 @@
|
||||||
<string name="systems_language">System default</string>
|
<string name="systems_language">System default</string>
|
||||||
<string name="remove_watched">Remove watched</string>
|
<string name="remove_watched">Remove watched</string>
|
||||||
<string name="remove_watched_popup_title">Remove watched videos?</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.
|
<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>
|
\nAre you sure\? This cannot be undone!</string>
|
||||||
<string name="remove_watched_popup_yes_and_partially_watched_videos">Yes, and partially watched videos</string>
|
<string name="remove_watched_popup_yes_and_partially_watched_videos">Yes, and partially watched videos</string>
|
||||||
|
|
Loading…
Reference in New Issue