mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 15:30:31 +05:30
Merge pull request #7348 from Bnyro/master
feat: support for adding downloads to playback queue
This commit is contained in:
commit
09a306eefd
@ -36,7 +36,6 @@ object IntentData {
|
|||||||
const val url = "url"
|
const val url = "url"
|
||||||
const val videoStats = "videoStats"
|
const val videoStats = "videoStats"
|
||||||
const val bitmapUrl = "bitmapUrl"
|
const val bitmapUrl = "bitmapUrl"
|
||||||
const val isCurrentlyPlaying = "isCurrentlyPlaying"
|
|
||||||
const val isSubscribed = "isSubscribed"
|
const val isSubscribed = "isSubscribed"
|
||||||
const val sortOptions = "sortOptions"
|
const val sortOptions = "sortOptions"
|
||||||
const val hideWatched = "hideWatched"
|
const val hideWatched = "hideWatched"
|
||||||
|
@ -37,6 +37,7 @@ import com.github.libretube.ui.activities.MainActivity
|
|||||||
import com.github.libretube.util.NowPlayingNotification
|
import com.github.libretube.util.NowPlayingNotification
|
||||||
import com.github.libretube.util.PauseableTimer
|
import com.github.libretube.util.PauseableTimer
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
|
import com.github.libretube.util.PlayingQueueMode
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -100,6 +101,7 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
|
|||||||
Player.STATE_ENDED -> {
|
Player.STATE_ENDED -> {
|
||||||
saveWatchPosition()
|
saveWatchPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
Player.STATE_READY -> {
|
Player.STATE_READY -> {
|
||||||
isTransitioning = false
|
isTransitioning = false
|
||||||
}
|
}
|
||||||
@ -115,6 +117,9 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
|
|||||||
): ListenableFuture<SessionResult> {
|
): ListenableFuture<SessionResult> {
|
||||||
when (customCommand.customAction) {
|
when (customCommand.customAction) {
|
||||||
START_SERVICE_ACTION -> {
|
START_SERVICE_ACTION -> {
|
||||||
|
PlayingQueue.queueMode =
|
||||||
|
if (isOfflinePlayer) PlayingQueueMode.OFFLINE else PlayingQueueMode.ONLINE
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
onServiceCreated(args)
|
onServiceCreated(args)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
@ -262,8 +267,9 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
|
|||||||
abstract val isOfflinePlayer: Boolean
|
abstract val isOfflinePlayer: Boolean
|
||||||
var isAudioOnlyPlayer: Boolean = false
|
var isAudioOnlyPlayer: Boolean = false
|
||||||
|
|
||||||
val watchPositionsEnabled get() =
|
val watchPositionsEnabled
|
||||||
(PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer)
|
get() =
|
||||||
|
(PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer)
|
||||||
|
|
||||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? =
|
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? =
|
||||||
mediaLibrarySession
|
mediaLibrarySession
|
||||||
|
@ -137,8 +137,7 @@ class DownloadsAdapter(
|
|||||||
DownloadOptionsBottomSheet()
|
DownloadOptionsBottomSheet()
|
||||||
.apply {
|
.apply {
|
||||||
arguments = bundleOf(
|
arguments = bundleOf(
|
||||||
IntentData.videoId to download.videoId,
|
IntentData.streamItem to download.toStreamItem(),
|
||||||
IntentData.channelName to download.uploader,
|
|
||||||
IntentData.downloadTab to downloadTab
|
IntentData.downloadTab to downloadTab
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -471,10 +471,7 @@ class AudioPlayerFragment : Fragment(R.layout.fragment_audio_player), AudioPlaye
|
|||||||
val current = PlayingQueue.getCurrent() ?: return
|
val current = PlayingQueue.getCurrent() ?: return
|
||||||
VideoOptionsBottomSheet()
|
VideoOptionsBottomSheet()
|
||||||
.apply {
|
.apply {
|
||||||
arguments = bundleOf(
|
arguments = bundleOf(IntentData.streamItem to current)
|
||||||
IntentData.streamItem to current,
|
|
||||||
IntentData.isCurrentlyPlaying to true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.show(childFragmentManager)
|
.show(childFragmentManager)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,8 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.watchHistoryRecView.setOnDismissListener { position ->
|
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)
|
viewModel.removeFromHistory(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,33 +4,43 @@ import android.os.Bundle
|
|||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.setFragmentResult
|
import androidx.fragment.app.setFragmentResult
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.obj.StreamItem
|
||||||
import com.github.libretube.constants.IntentData
|
import com.github.libretube.constants.IntentData
|
||||||
import com.github.libretube.enums.ShareObjectType
|
import com.github.libretube.enums.ShareObjectType
|
||||||
|
import com.github.libretube.extensions.parcelable
|
||||||
import com.github.libretube.extensions.serializable
|
import com.github.libretube.extensions.serializable
|
||||||
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.helpers.BackgroundHelper
|
import com.github.libretube.helpers.BackgroundHelper
|
||||||
import com.github.libretube.helpers.ContextHelper
|
import com.github.libretube.helpers.ContextHelper
|
||||||
import com.github.libretube.helpers.NavigationHelper
|
import com.github.libretube.helpers.NavigationHelper
|
||||||
import com.github.libretube.obj.ShareData
|
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.activities.NoInternetActivity
|
||||||
import com.github.libretube.ui.dialogs.ShareDialog
|
import com.github.libretube.ui.dialogs.ShareDialog
|
||||||
import com.github.libretube.ui.fragments.DownloadTab
|
import com.github.libretube.ui.fragments.DownloadTab
|
||||||
|
import com.github.libretube.util.PlayingQueue
|
||||||
|
import com.github.libretube.util.PlayingQueueMode
|
||||||
|
|
||||||
class DownloadOptionsBottomSheet : BaseBottomSheet() {
|
class DownloadOptionsBottomSheet : BaseBottomSheet() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
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 downloadTab = arguments?.serializable<DownloadTab>(IntentData.downloadTab)!!
|
||||||
|
|
||||||
val options = mutableListOf(
|
val options = mutableListOf(
|
||||||
R.string.playOnBackground,
|
R.string.playOnBackground,
|
||||||
R.string.go_to_video,
|
|
||||||
R.string.share,
|
R.string.share,
|
||||||
R.string.delete
|
R.string.delete
|
||||||
)
|
)
|
||||||
|
|
||||||
// can't navigate to video while in offline activity
|
// can't navigate to video while in offline activity
|
||||||
if (ContextHelper.tryUnwrapActivity<NoInternetActivity>(requireContext()) != null) {
|
if (ContextHelper.tryUnwrapActivity<NoInternetActivity>(requireContext()) == null) {
|
||||||
options.remove(R.string.go_to_video)
|
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 ->
|
setSimpleItems(options.map { getString(it) }) { which ->
|
||||||
@ -59,6 +69,14 @@ class DownloadOptionsBottomSheet : BaseBottomSheet() {
|
|||||||
setFragmentResult(DELETE_DOWNLOAD_REQUEST_KEY, bundleOf())
|
setFragmentResult(DELETE_DOWNLOAD_REQUEST_KEY, bundleOf())
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.string.play_next -> {
|
||||||
|
PlayingQueue.addAsNext(streamItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.string.add_to_queue -> {
|
||||||
|
PlayingQueue.add(streamItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import com.github.libretube.ui.dialogs.AddToPlaylistDialog
|
|||||||
import com.github.libretube.ui.dialogs.ShareDialog
|
import com.github.libretube.ui.dialogs.ShareDialog
|
||||||
import com.github.libretube.ui.fragments.SubscriptionsFragment
|
import com.github.libretube.ui.fragments.SubscriptionsFragment
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
|
import com.github.libretube.util.PlayingQueueMode
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -35,18 +36,17 @@ import kotlinx.coroutines.withContext
|
|||||||
*/
|
*/
|
||||||
class VideoOptionsBottomSheet : BaseBottomSheet() {
|
class VideoOptionsBottomSheet : BaseBottomSheet() {
|
||||||
private lateinit var streamItem: StreamItem
|
private lateinit var streamItem: StreamItem
|
||||||
private var isCurrentlyPlaying = false
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
streamItem = arguments?.parcelable(IntentData.streamItem)!!
|
streamItem = arguments?.parcelable(IntentData.streamItem)!!
|
||||||
isCurrentlyPlaying = arguments?.getBoolean(IntentData.isCurrentlyPlaying) ?: false
|
|
||||||
|
|
||||||
val videoId = streamItem.url?.toID() ?: return
|
val videoId = streamItem.url?.toID() ?: return
|
||||||
|
|
||||||
setTitle(streamItem.title)
|
setTitle(streamItem.title)
|
||||||
|
|
||||||
val optionsList = mutableListOf<Int>()
|
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)
|
optionsList += getOptionsForNotActivePlayback(videoId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ class VideoOptionsBottomSheet : BaseBottomSheet() {
|
|||||||
val optionsList = mutableListOf(R.string.playOnBackground)
|
val optionsList = mutableListOf(R.string.playOnBackground)
|
||||||
|
|
||||||
// Check whether the player is running and add queue options
|
// 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.play_next
|
||||||
optionsList += R.string.add_to_queue
|
optionsList += R.string.add_to_queue
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,12 @@ object PlayingQueue {
|
|||||||
|
|
||||||
private val queueJobs = mutableListOf<Job>()
|
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
|
// wrapper around PlayerHelper#repeatMode for compatibility
|
||||||
var repeatMode: Int
|
var repeatMode: Int
|
||||||
get() = PlayerHelper.repeatMode
|
get() = PlayerHelper.repeatMode
|
||||||
@ -222,3 +228,8 @@ object PlayingQueue {
|
|||||||
add(*streams.filter { !it.isLive }.toTypedArray(), skipExisting = true)
|
add(*streams.filter { !it.isLive }.toTypedArray(), skipExisting = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class PlayingQueueMode {
|
||||||
|
ONLINE,
|
||||||
|
OFFLINE
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user