mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	Merge branch 'dev' into 9458_faulty_playlist_thumbnail_update
This commit is contained in:
		| @@ -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 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.PlaylistStreamEntry; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; | ||||
| @@ -14,6 +15,7 @@ import java.util.List; | ||||
|  | ||||
| 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.model.PlaylistEntity.DEFAULT_THUMBNAIL; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; | ||||
| @@ -28,6 +30,7 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PL | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_THUMBNAIL_URL; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS; | ||||
| import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; | ||||
| @@ -103,4 +106,24 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> { | ||||
|             + " GROUP BY " + PLAYLIST_ID | ||||
|             + " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC") | ||||
|     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); | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import android.os.Bundle; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| @@ -13,8 +14,8 @@ import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import org.schabi.newpipe.NewPipeDatabase; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||
| import org.schabi.newpipe.database.playlist.model.PlaylistEntity; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry; | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||
| import org.schabi.newpipe.local.LocalItemListAdapter; | ||||
| import org.schabi.newpipe.local.playlist.LocalPlaylistManager; | ||||
| @@ -29,6 +30,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog { | ||||
|  | ||||
|     private RecyclerView playlistRecyclerView; | ||||
|     private LocalItemListAdapter playlistAdapter; | ||||
|     private TextView playlistDuplicateIndicator; | ||||
|  | ||||
|     private final CompositeDisposable playlistDisposables = new CompositeDisposable(); | ||||
|  | ||||
| @@ -64,8 +66,9 @@ public final class PlaylistAppendDialog extends PlaylistDialog { | ||||
|         playlistAdapter = new LocalItemListAdapter(getActivity()); | ||||
|         playlistAdapter.setSelectedListener(selectedItem -> { | ||||
|             final List<StreamEntity> entities = getStreamEntities(); | ||||
|             if (selectedItem instanceof PlaylistMetadataEntry && entities != null) { | ||||
|                 onPlaylistSelected(playlistManager, (PlaylistMetadataEntry) selectedItem, entities); | ||||
|             if (selectedItem instanceof PlaylistDuplicatesEntry && entities != null) { | ||||
|                 onPlaylistSelected(playlistManager, | ||||
|                         (PlaylistDuplicatesEntry) selectedItem, entities); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
| @@ -73,10 +76,13 @@ public final class PlaylistAppendDialog extends PlaylistDialog { | ||||
|         playlistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); | ||||
|         playlistRecyclerView.setAdapter(playlistAdapter); | ||||
|  | ||||
|         playlistDuplicateIndicator = view.findViewById(R.id.playlist_duplicate); | ||||
|  | ||||
|         final View newPlaylistButton = view.findViewById(R.id.newPlaylist); | ||||
|         newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog()); | ||||
|  | ||||
|         playlistDisposables.add(playlistManager.getPlaylists() | ||||
|         playlistDisposables.add(playlistManager | ||||
|                 .getPlaylistDuplicates(getStreamEntities().get(0).getUrl()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(this::onPlaylistsReceived)); | ||||
|     } | ||||
| @@ -118,19 +124,36 @@ public final class PlaylistAppendDialog extends PlaylistDialog { | ||||
|         requireDialog().dismiss(); | ||||
|     } | ||||
|  | ||||
|     private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) { | ||||
|         if (playlistAdapter != null && playlistRecyclerView != null) { | ||||
|     private void onPlaylistsReceived(@NonNull final List<PlaylistDuplicatesEntry> playlists) { | ||||
|         if (playlistAdapter != null | ||||
|                 && playlistRecyclerView != null | ||||
|                 && playlistDuplicateIndicator != null) { | ||||
|             playlistAdapter.clearStreamItemList(); | ||||
|             playlistAdapter.addItems(playlists); | ||||
|             playlistRecyclerView.setVisibility(View.VISIBLE); | ||||
|             playlistDuplicateIndicator.setVisibility( | ||||
|                     anyPlaylistContainsDuplicates(playlists) ? View.VISIBLE : View.GONE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean anyPlaylistContainsDuplicates(final List<PlaylistDuplicatesEntry> playlists) { | ||||
|         return playlists.stream() | ||||
|                 .anyMatch(playlist -> playlist.timesStreamIsContained > 0); | ||||
|     } | ||||
|  | ||||
|     private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager, | ||||
|                                     @NonNull final PlaylistMetadataEntry playlist, | ||||
|                                     @NonNull final PlaylistDuplicatesEntry playlist, | ||||
|                                     @NonNull final List<StreamEntity> streams) { | ||||
|         final Toast successToast = Toast.makeText(getContext(), | ||||
|                 R.string.playlist_add_stream_success, Toast.LENGTH_SHORT); | ||||
|  | ||||
|         final String toastText; | ||||
|         if (playlist.timesStreamIsContained > 0) { | ||||
|             toastText = getString(R.string.playlist_add_stream_success_duplicate, | ||||
|                     playlist.timesStreamIsContained); | ||||
|         } else { | ||||
|             toastText = getString(R.string.playlist_add_stream_success); | ||||
|         } | ||||
|  | ||||
|         final Toast successToast = Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT); | ||||
|  | ||||
|         playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
|  | ||||
| import org.schabi.newpipe.database.LocalItem; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||
| import org.schabi.newpipe.local.LocalItemBuilder; | ||||
| import org.schabi.newpipe.local.history.HistoryRecordManager; | ||||
| @@ -13,6 +14,9 @@ import org.schabi.newpipe.util.Localization; | ||||
| import java.time.format.DateTimeFormatter; | ||||
|  | ||||
| public class LocalPlaylistItemHolder extends PlaylistItemHolder { | ||||
|  | ||||
|     private static final float GRAYED_OUT_ALPHA = 0.6f; | ||||
|  | ||||
|     public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { | ||||
|         super(infoItemBuilder, parent); | ||||
|     } | ||||
| @@ -38,6 +42,13 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { | ||||
|  | ||||
|         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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package org.schabi.newpipe.local.playlist; | ||||
| import androidx.annotation.Nullable; | ||||
|  | ||||
| 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.PlaylistStreamEntry; | ||||
| import org.schabi.newpipe.database.playlist.dao.PlaylistDAO; | ||||
| @@ -92,6 +93,18 @@ public class LocalPlaylistManager { | ||||
|         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) { | ||||
|         return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io()); | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jared234
					Jared234