From 5e0b307c5e3168d1acdc245366a8bde6d21a70f1 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 3 Feb 2026 16:29:43 +0100 Subject: [PATCH] Allow playing local playlists directly --- .../playqueue/LocalPlaylistPlayQueue.kt | 49 +++++++++++++++++++ .../ui/components/menu/LongPressAction.kt | 22 ++++----- 2 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/playqueue/LocalPlaylistPlayQueue.kt diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/LocalPlaylistPlayQueue.kt b/app/src/main/java/org/schabi/newpipe/player/playqueue/LocalPlaylistPlayQueue.kt new file mode 100644 index 000000000..ab0be643f --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/LocalPlaylistPlayQueue.kt @@ -0,0 +1,49 @@ +package org.schabi.newpipe.player.playqueue + +import android.util.Log +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.disposables.Disposable +import io.reactivex.rxjava3.schedulers.Schedulers +import org.schabi.newpipe.App +import org.schabi.newpipe.NewPipeDatabase +import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry +import org.schabi.newpipe.local.playlist.LocalPlaylistManager + +class LocalPlaylistPlayQueue(info: PlaylistMetadataEntry) : PlayQueue(0, listOf()) { + private val playlistId: Long = info.uid + private var fetchDisposable: Disposable? = null + override var isComplete: Boolean = false + private set + + override fun fetch() { + if (isComplete) { + return + } + isComplete = true + + fetchDisposable = LocalPlaylistManager(NewPipeDatabase.getInstance(App.instance)) + .getPlaylistStreams(playlistId) + .firstOrError() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { streamEntries -> + append(streamEntries.map { PlayQueueItem(it.toStreamInfoItem()) }) + }, + { e -> + Log.e(TAG, "Error fetching local playlist", e) + notifyChange() + } + ) + } + + override fun dispose() { + super.dispose() + fetchDisposable?.dispose() + fetchDisposable = null + } + + companion object { + private val TAG: String = LocalPlaylistPlayQueue::class.java.getSimpleName() + } +} diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressAction.kt b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressAction.kt index 4e33e9282..d2099e3f4 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressAction.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressAction.kt @@ -25,6 +25,7 @@ import androidx.compose.material.icons.filled.Share import androidx.compose.ui.graphics.vector.ImageVector import androidx.core.net.toUri import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.rx3.await import kotlinx.coroutines.rx3.awaitSingle import kotlinx.coroutines.withContext @@ -47,6 +48,7 @@ import org.schabi.newpipe.local.history.HistoryRecordManager import org.schabi.newpipe.local.playlist.LocalPlaylistManager import org.schabi.newpipe.player.helper.PlayerHolder import org.schabi.newpipe.player.playqueue.ChannelTabPlayQueue +import org.schabi.newpipe.player.playqueue.LocalPlaylistPlayQueue import org.schabi.newpipe.player.playqueue.PlayQueue import org.schabi.newpipe.player.playqueue.PlayQueueItem import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue @@ -93,10 +95,7 @@ data class LongPressAction( UnsetPlaylistThumbnail(18, R.string.unset_playlist_thumbnail, Icons.Default.HideImage), Unsubscribe(19, R.string.unsubscribe, Icons.Default.Delete), ShowDetails(20, R.string.play_queue_stream_detail, Icons.Default.Info), - Remove(21, R.string.play_queue_remove, Icons.Default.Delete) - ; - - // TODO add actions that use the whole list the item belongs to (see wholeListQueue) + Remove(21, R.string.play_queue_remove, Icons.Default.Delete); fun buildAction( enabled: () -> Boolean = { true }, @@ -338,13 +337,14 @@ data class LongPressAction( onDelete: Runnable, unsetPlaylistThumbnail: Runnable? ): List { - return listOf( - Type.Rename.buildAction { onRename.run() }, - Type.Delete.buildAction { onDelete.run() }, - Type.UnsetPlaylistThumbnail.buildAction( - enabled = { unsetPlaylistThumbnail != null } - ) { unsetPlaylistThumbnail?.run() } - ) + return buildPlayerActionList { LocalPlaylistPlayQueue(item) } + + listOf( + Type.Rename.buildAction { onRename.run() }, + Type.Delete.buildAction { onDelete.run() }, + Type.UnsetPlaylistThumbnail.buildAction( + enabled = { unsetPlaylistThumbnail != null } + ) { unsetPlaylistThumbnail?.run() } + ) } @JvmStatic