From f262210ef74f268445f8831bd51f54ac0cb1c019 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Fri, 25 Apr 2025 16:36:15 +0200 Subject: [PATCH] feat: support for adding downloads to playback queue --- .../github/libretube/constants/IntentData.kt | 1 - .../services/AbstractPlayerService.kt | 10 +++++-- .../libretube/ui/adapters/DownloadsAdapter.kt | 3 +- .../ui/fragments/AudioPlayerFragment.kt | 5 +--- .../ui/fragments/WatchHistoryFragment.kt | 3 +- .../ui/sheets/DownloadOptionsBottomSheet.kt | 28 +++++++++++++++---- .../ui/sheets/VideoOptionsBottomSheet.kt | 8 +++--- .../com/github/libretube/util/PlayingQueue.kt | 11 ++++++++ 8 files changed, 50 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/github/libretube/constants/IntentData.kt b/app/src/main/java/com/github/libretube/constants/IntentData.kt index 1bd34b1b3..e6c2d8f8f 100644 --- a/app/src/main/java/com/github/libretube/constants/IntentData.kt +++ b/app/src/main/java/com/github/libretube/constants/IntentData.kt @@ -36,7 +36,6 @@ object IntentData { const val url = "url" const val videoStats = "videoStats" const val bitmapUrl = "bitmapUrl" - const val isCurrentlyPlaying = "isCurrentlyPlaying" const val isSubscribed = "isSubscribed" const val sortOptions = "sortOptions" const val hideWatched = "hideWatched" diff --git a/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt b/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt index 6c164c413..fd6189aff 100644 --- a/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt +++ b/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt @@ -37,6 +37,7 @@ import com.github.libretube.ui.activities.MainActivity import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PauseableTimer import com.github.libretube.util.PlayingQueue +import com.github.libretube.util.PlayingQueueMode import com.google.common.util.concurrent.ListenableFuture import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -100,6 +101,7 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio Player.STATE_ENDED -> { saveWatchPosition() } + Player.STATE_READY -> { isTransitioning = false } @@ -115,6 +117,9 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio ): ListenableFuture { when (customCommand.customAction) { START_SERVICE_ACTION -> { + PlayingQueue.queueMode = + if (isOfflinePlayer) PlayingQueueMode.OFFLINE else PlayingQueueMode.ONLINE + CoroutineScope(Dispatchers.IO).launch { onServiceCreated(args) withContext(Dispatchers.Main) { @@ -262,8 +267,9 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio abstract val isOfflinePlayer: Boolean var isAudioOnlyPlayer: Boolean = false - val watchPositionsEnabled get() = - (PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer) + val watchPositionsEnabled + get() = + (PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer) override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? = mediaLibrarySession diff --git a/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt index dbade1b62..6ea0bb7ae 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt @@ -137,8 +137,7 @@ class DownloadsAdapter( DownloadOptionsBottomSheet() .apply { arguments = bundleOf( - IntentData.videoId to download.videoId, - IntentData.channelName to download.uploader, + IntentData.streamItem to download.toStreamItem(), IntentData.downloadTab to downloadTab ) } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt index f67fc4cc0..71630bc25 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt @@ -471,10 +471,7 @@ class AudioPlayerFragment : Fragment(R.layout.fragment_audio_player), AudioPlaye val current = PlayingQueue.getCurrent() ?: return VideoOptionsBottomSheet() .apply { - arguments = bundleOf( - IntentData.streamItem to current, - IntentData.isCurrentlyPlaying to true - ) + arguments = bundleOf(IntentData.streamItem to current) } .show(childFragmentManager) } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt index b60326b08..00780dc4e 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt @@ -60,7 +60,8 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc } binding.watchHistoryRecView.setOnDismissListener { position -> - val item = viewModel.filteredWatchHistory.value?.getOrNull(position) ?: return@setOnDismissListener + val item = viewModel.filteredWatchHistory.value?.getOrNull(position) + ?: return@setOnDismissListener viewModel.removeFromHistory(item) } diff --git a/app/src/main/java/com/github/libretube/ui/sheets/DownloadOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/DownloadOptionsBottomSheet.kt index 0919321a0..77bca9290 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/DownloadOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/DownloadOptionsBottomSheet.kt @@ -4,33 +4,43 @@ import android.os.Bundle import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult import com.github.libretube.R +import com.github.libretube.api.obj.StreamItem import com.github.libretube.constants.IntentData import com.github.libretube.enums.ShareObjectType +import com.github.libretube.extensions.parcelable import com.github.libretube.extensions.serializable +import com.github.libretube.extensions.toID import com.github.libretube.helpers.BackgroundHelper import com.github.libretube.helpers.ContextHelper import com.github.libretube.helpers.NavigationHelper import com.github.libretube.obj.ShareData -import com.github.libretube.ui.activities.DownloadActivity import com.github.libretube.ui.activities.NoInternetActivity import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.fragments.DownloadTab +import com.github.libretube.util.PlayingQueue +import com.github.libretube.util.PlayingQueueMode class DownloadOptionsBottomSheet : BaseBottomSheet() { override fun onCreate(savedInstanceState: Bundle?) { - val videoId = arguments?.getString(IntentData.videoId)!! + val streamItem = arguments?.parcelable(IntentData.streamItem)!! + val videoId = streamItem.url!!.toID() val downloadTab = arguments?.serializable(IntentData.downloadTab)!! val options = mutableListOf( R.string.playOnBackground, - R.string.go_to_video, R.string.share, R.string.delete ) // can't navigate to video while in offline activity - if (ContextHelper.tryUnwrapActivity(requireContext()) != null) { - options.remove(R.string.go_to_video) + if (ContextHelper.tryUnwrapActivity(requireContext()) == null) { + options += R.string.go_to_video + } + + val isSelectedVideoCurrentlyPlaying = PlayingQueue.getCurrent()?.url?.toID() == videoId + if (!isSelectedVideoCurrentlyPlaying && PlayingQueue.isNotEmpty() && PlayingQueue.queueMode == PlayingQueueMode.OFFLINE) { + options += R.string.play_next + options += R.string.add_to_queue } setSimpleItems(options.map { getString(it) }) { which -> @@ -59,6 +69,14 @@ class DownloadOptionsBottomSheet : BaseBottomSheet() { setFragmentResult(DELETE_DOWNLOAD_REQUEST_KEY, bundleOf()) dialog?.dismiss() } + + R.string.play_next -> { + PlayingQueue.addAsNext(streamItem) + } + + R.string.add_to_queue -> { + PlayingQueue.add(streamItem) + } } } diff --git a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt index 1c0752d89..cb098d3ab 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt @@ -24,6 +24,7 @@ import com.github.libretube.ui.dialogs.AddToPlaylistDialog import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.fragments.SubscriptionsFragment import com.github.libretube.util.PlayingQueue +import com.github.libretube.util.PlayingQueueMode import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -35,18 +36,17 @@ import kotlinx.coroutines.withContext */ class VideoOptionsBottomSheet : BaseBottomSheet() { private lateinit var streamItem: StreamItem - private var isCurrentlyPlaying = false override fun onCreate(savedInstanceState: Bundle?) { streamItem = arguments?.parcelable(IntentData.streamItem)!! - isCurrentlyPlaying = arguments?.getBoolean(IntentData.isCurrentlyPlaying) ?: false val videoId = streamItem.url?.toID() ?: return setTitle(streamItem.title) val optionsList = mutableListOf() - if (!isCurrentlyPlaying) { + // these options are only available for other videos than the currently playing one + if (PlayingQueue.getCurrent()?.url?.toID() != videoId) { optionsList += getOptionsForNotActivePlayback(videoId) } @@ -136,7 +136,7 @@ class VideoOptionsBottomSheet : BaseBottomSheet() { val optionsList = mutableListOf(R.string.playOnBackground) // Check whether the player is running and add queue options - if (PlayingQueue.isNotEmpty()) { + if (PlayingQueue.isNotEmpty() && PlayingQueue.queueMode == PlayingQueueMode.ONLINE) { optionsList += R.string.play_next optionsList += R.string.add_to_queue } diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index ae5300fa1..07a503bb2 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -18,6 +18,12 @@ object PlayingQueue { private val queueJobs = mutableListOf() + /** + * Current use case of the queue. Do NOT add any offline videos while the [queueMode] is online + * or vice versa. + */ + var queueMode: PlayingQueueMode = PlayingQueueMode.ONLINE + // wrapper around PlayerHelper#repeatMode for compatibility var repeatMode: Int get() = PlayerHelper.repeatMode @@ -222,3 +228,8 @@ object PlayingQueue { add(*streams.filter { !it.isLive }.toTypedArray(), skipExisting = true) } } + +enum class PlayingQueueMode { + ONLINE, + OFFLINE +} \ No newline at end of file