mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 08:30:44 +00:00
See if playlists already contain a stream from db
This commit is contained in:
parent
b3554a6a49
commit
ef4a6238c8
@ -0,0 +1,24 @@
|
|||||||
|
package org.schabi.newpipe.database.playlist;
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class adds a field to {@link PlaylistMetadataEntry} that contains an integer representing
|
||||||
|
* how many times a specific stream is already contained inside a local playlist. Used to be able
|
||||||
|
* to grey out playlists which already contain the current stream in the playlist append dialog.
|
||||||
|
* @see org.schabi.newpipe.local.playlist.LocalPlaylistManager#getPlaylistDuplicates(String)
|
||||||
|
*/
|
||||||
|
public class PlaylistDuplicatesEntry extends PlaylistMetadataEntry {
|
||||||
|
public static final String PLAYLIST_TIMES_STREAM_IS_CONTAINED = "timesStreamIsContained";
|
||||||
|
@ColumnInfo(name = PLAYLIST_TIMES_STREAM_IS_CONTAINED)
|
||||||
|
public final long timesStreamIsContained;
|
||||||
|
|
||||||
|
public PlaylistDuplicatesEntry(final long uid,
|
||||||
|
final String name,
|
||||||
|
final String thumbnailUrl,
|
||||||
|
final long streamCount,
|
||||||
|
final long timesStreamIsContained) {
|
||||||
|
super(uid, name, thumbnailUrl, streamCount);
|
||||||
|
this.timesStreamIsContained = timesStreamIsContained;
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import androidx.room.RewriteQueriesToDropUnusedColumns;
|
|||||||
import androidx.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
|
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
||||||
@ -14,6 +15,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import io.reactivex.rxjava3.core.Flowable;
|
import io.reactivex.rxjava3.core.Flowable;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry.PLAYLIST_TIMES_STREAM_IS_CONTAINED;
|
||||||
import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT;
|
import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
|
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
|
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
|
||||||
@ -50,23 +52,6 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> {
|
|||||||
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId")
|
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId")
|
||||||
void deleteBatch(long playlistId);
|
void deleteBatch(long playlistId);
|
||||||
|
|
||||||
@Query("SELECT COALESCE(COUNT(*), 0)"
|
|
||||||
+ " FROM " + STREAM_TABLE
|
|
||||||
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
|
|
||||||
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
|
|
||||||
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId "
|
|
||||||
+ " AND " + STREAM_URL + " = :streamURL"
|
|
||||||
)
|
|
||||||
Flowable<Integer> getDuplicateCount(long playlistId, String streamURL);
|
|
||||||
|
|
||||||
@Query("SELECT " + JOIN_PLAYLIST_ID
|
|
||||||
+ " FROM " + STREAM_TABLE
|
|
||||||
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
|
|
||||||
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
|
|
||||||
+ " WHERE " + STREAM_URL + " = :streamURL"
|
|
||||||
)
|
|
||||||
Flowable<List<Long>> getDuplicatePlaylists(String streamURL);
|
|
||||||
|
|
||||||
@Query("SELECT COALESCE(MAX(" + JOIN_INDEX + "), -1)"
|
@Query("SELECT COALESCE(MAX(" + JOIN_INDEX + "), -1)"
|
||||||
+ " FROM " + PLAYLIST_STREAM_JOIN_TABLE
|
+ " FROM " + PLAYLIST_STREAM_JOIN_TABLE
|
||||||
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId")
|
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId")
|
||||||
@ -111,4 +96,24 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> {
|
|||||||
+ " GROUP BY " + PLAYLIST_ID
|
+ " GROUP BY " + PLAYLIST_ID
|
||||||
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
|
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
|
||||||
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
|
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", "
|
||||||
|
+ PLAYLIST_NAME + ", "
|
||||||
|
+ PLAYLIST_TABLE + "." + PLAYLIST_THUMBNAIL_URL + ", "
|
||||||
|
+ "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT + ", "
|
||||||
|
+ "COALESCE(SUM(" + STREAM_URL + " = :streamUrl), 0) AS "
|
||||||
|
+ PLAYLIST_TIMES_STREAM_IS_CONTAINED
|
||||||
|
|
||||||
|
+ " FROM " + PLAYLIST_TABLE
|
||||||
|
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
|
||||||
|
+ " ON " + PLAYLIST_TABLE + "." + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID
|
||||||
|
|
||||||
|
+ " LEFT JOIN " + STREAM_TABLE
|
||||||
|
+ " ON " + STREAM_TABLE + "." + STREAM_ID + " = " + JOIN_STREAM_ID
|
||||||
|
+ " AND :streamUrl = :streamUrl"
|
||||||
|
|
||||||
|
+ " GROUP BY " + JOIN_PLAYLIST_ID
|
||||||
|
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
|
||||||
|
Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicatesMetadata(String streamUrl);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.local.LocalItemListAdapter;
|
import org.schabi.newpipe.local.LocalItemListAdapter;
|
||||||
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
||||||
@ -26,12 +26,8 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
|||||||
public final class PlaylistAppendDialog extends PlaylistDialog {
|
public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
||||||
|
|
||||||
private static final float DEFAULT_ALPHA = 1f;
|
|
||||||
private static final float GRAYED_OUT_ALPHA = 0.3f;
|
|
||||||
|
|
||||||
private RecyclerView playlistRecyclerView;
|
private RecyclerView playlistRecyclerView;
|
||||||
private LocalItemListAdapter playlistAdapter;
|
private LocalItemListAdapter playlistAdapter;
|
||||||
private List<Long> duplicateIds;
|
|
||||||
|
|
||||||
private final CompositeDisposable playlistDisposables = new CompositeDisposable();
|
private final CompositeDisposable playlistDisposables = new CompositeDisposable();
|
||||||
|
|
||||||
@ -64,15 +60,13 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||||||
final LocalPlaylistManager playlistManager =
|
final LocalPlaylistManager playlistManager =
|
||||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(requireContext()));
|
new LocalPlaylistManager(NewPipeDatabase.getInstance(requireContext()));
|
||||||
|
|
||||||
duplicateIds = playlistManager.getDuplicatePlaylists(getStreamEntities().get(0).getUrl())
|
|
||||||
.blockingFirst();
|
|
||||||
|
|
||||||
playlistAdapter = new LocalItemListAdapter(getActivity());
|
playlistAdapter = new LocalItemListAdapter(getActivity());
|
||||||
playlistAdapter.setHasStableIds(true);
|
playlistAdapter.setHasStableIds(true);
|
||||||
playlistAdapter.setSelectedListener(selectedItem -> {
|
playlistAdapter.setSelectedListener(selectedItem -> {
|
||||||
final List<StreamEntity> entities = getStreamEntities();
|
final List<StreamEntity> entities = getStreamEntities();
|
||||||
if (selectedItem instanceof PlaylistMetadataEntry && entities != null) {
|
if (selectedItem instanceof PlaylistDuplicatesEntry && entities != null) {
|
||||||
onPlaylistSelected(playlistManager, (PlaylistMetadataEntry) selectedItem, entities);
|
onPlaylistSelected(playlistManager,
|
||||||
|
(PlaylistDuplicatesEntry) selectedItem, entities);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,7 +77,8 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||||||
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
||||||
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
||||||
|
|
||||||
playlistDisposables.add(playlistManager.getPlaylists()
|
playlistDisposables.add(playlistManager
|
||||||
|
.getPlaylistDuplicates(getStreamEntities().get(0).getUrl())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(this::onPlaylistsReceived));
|
.subscribe(this::onPlaylistsReceived));
|
||||||
}
|
}
|
||||||
@ -125,63 +120,24 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||||||
requireDialog().dismiss();
|
requireDialog().dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) {
|
private void onPlaylistsReceived(@NonNull final List<PlaylistDuplicatesEntry> playlists) {
|
||||||
if (playlistAdapter != null && playlistRecyclerView != null) {
|
if (playlistAdapter != null && playlistRecyclerView != null) {
|
||||||
playlistAdapter.clearStreamItemList();
|
playlistAdapter.clearStreamItemList();
|
||||||
playlistAdapter.addItems(playlists);
|
playlistAdapter.addItems(playlists);
|
||||||
playlistRecyclerView.setVisibility(View.VISIBLE);
|
playlistRecyclerView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
playlistRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
|
||||||
@Override
|
|
||||||
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx,
|
|
||||||
final int dy) {
|
|
||||||
showDuplicateIndicators(recyclerView);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
initDuplicateIndicators(playlistRecyclerView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initDuplicateIndicators(@NonNull final RecyclerView view) {
|
|
||||||
showDuplicateIndicators(view);
|
|
||||||
|
|
||||||
if (!duplicateIds.isEmpty()) {
|
|
||||||
final View indicatorExplanation = getView()
|
|
||||||
.findViewById(R.id.playlist_duplicate);
|
|
||||||
indicatorExplanation.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDuplicateIndicators(@NonNull final RecyclerView view) {
|
|
||||||
if (view.getAdapter() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int count = view.getAdapter().getItemCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
|
|
||||||
final RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(i);
|
|
||||||
if (viewHolder != null) {
|
|
||||||
if (duplicateIds.contains(view.getAdapter().getItemId(i))) {
|
|
||||||
viewHolder.itemView.setAlpha(GRAYED_OUT_ALPHA);
|
|
||||||
} else {
|
|
||||||
viewHolder.itemView.setAlpha(DEFAULT_ALPHA);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager,
|
private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager,
|
||||||
@NonNull final PlaylistMetadataEntry playlist,
|
@NonNull final PlaylistDuplicatesEntry playlist,
|
||||||
@NonNull final List<StreamEntity> streams) {
|
@NonNull final List<StreamEntity> streams) {
|
||||||
|
|
||||||
final int numOfDuplicates = manager.getPlaylistDuplicateCount(playlist.uid,
|
final String toastText;
|
||||||
streams.get(0).getUrl()).blockingFirst();
|
if (playlist.timesStreamIsContained > 0) {
|
||||||
String toastText = getString(R.string.playlist_add_stream_success);
|
toastText = getString(R.string.playlist_add_stream_success_duplicate,
|
||||||
|
playlist.timesStreamIsContained);
|
||||||
if (numOfDuplicates > 0) {
|
} else {
|
||||||
toastText = getString(R.string.playlist_add_stream_success_duplicate, numOfDuplicates);
|
toastText = getString(R.string.playlist_add_stream_success);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Toast successToast = Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT);
|
final Toast successToast = Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT);
|
||||||
|
@ -4,6 +4,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
|
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
@ -13,6 +14,9 @@ import org.schabi.newpipe.util.Localization;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
||||||
|
|
||||||
|
private static final float GRAYED_OUT_ALPHA = 0.6f;
|
||||||
|
|
||||||
public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) {
|
public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) {
|
||||||
super(infoItemBuilder, parent);
|
super(infoItemBuilder, parent);
|
||||||
}
|
}
|
||||||
@ -38,6 +42,13 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
|||||||
|
|
||||||
PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView);
|
PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView);
|
||||||
|
|
||||||
|
if (item instanceof PlaylistDuplicatesEntry
|
||||||
|
&& ((PlaylistDuplicatesEntry) item).timesStreamIsContained > 0) {
|
||||||
|
itemView.setAlpha(GRAYED_OUT_ALPHA);
|
||||||
|
} else {
|
||||||
|
itemView.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter);
|
super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.AppDatabase;
|
import org.schabi.newpipe.database.AppDatabase;
|
||||||
|
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||||
import org.schabi.newpipe.database.playlist.dao.PlaylistDAO;
|
import org.schabi.newpipe.database.playlist.dao.PlaylistDAO;
|
||||||
@ -87,19 +88,22 @@ public class LocalPlaylistManager {
|
|||||||
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
|
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get playlists with attached information about how many times the provided stream is already
|
||||||
|
* contained in each playlist.
|
||||||
|
*
|
||||||
|
* @param streamUrl the stream url for which to check for duplicates
|
||||||
|
* @return a list of {@link PlaylistDuplicatesEntry}
|
||||||
|
*/
|
||||||
|
public Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicates(final String streamUrl) {
|
||||||
|
return playlistStreamTable.getPlaylistDuplicatesMetadata(streamUrl)
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
public Flowable<List<PlaylistStreamEntry>> getPlaylistStreams(final long playlistId) {
|
public Flowable<List<PlaylistStreamEntry>> getPlaylistStreams(final long playlistId) {
|
||||||
return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io());
|
return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flowable<Integer> getPlaylistDuplicateCount(final long playlistId,
|
|
||||||
final String streamURL) {
|
|
||||||
return playlistStreamTable.getDuplicateCount(playlistId, streamURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Flowable<List<Long>> getDuplicatePlaylists(final String streamURL) {
|
|
||||||
return playlistStreamTable.getDuplicatePlaylists(streamURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Single<Integer> deletePlaylist(final long playlistId) {
|
public Single<Integer> deletePlaylist(final long playlistId) {
|
||||||
return Single.fromCallable(() -> playlistTable.deletePlaylist(playlistId))
|
return Single.fromCallable(() -> playlistTable.deletePlaylist(playlistId))
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
|
Loading…
Reference in New Issue
Block a user