feat: support for adding downloads to playback queue

This commit is contained in:
Bnyro 2025-04-25 16:36:15 +02:00
parent 66c690a6af
commit f262210ef7
No known key found for this signature in database
8 changed files with 50 additions and 19 deletions

View File

@ -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"

View File

@ -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<SessionResult> {
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,7 +267,8 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
abstract val isOfflinePlayer: Boolean
var isAudioOnlyPlayer: Boolean = false
val watchPositionsEnabled get() =
val watchPositionsEnabled
get() =
(PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer)
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? =

View File

@ -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
)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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<StreamItem>(IntentData.streamItem)!!
val videoId = streamItem.url!!.toID()
val downloadTab = arguments?.serializable<DownloadTab>(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<NoInternetActivity>(requireContext()) != null) {
options.remove(R.string.go_to_video)
if (ContextHelper.tryUnwrapActivity<NoInternetActivity>(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)
}
}
}

View File

@ -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<Int>()
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
}

View File

@ -18,6 +18,12 @@ object PlayingQueue {
private val queueJobs = mutableListOf<Job>()
/**
* 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
}