mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 07:50:31 +05:30
playback queue
This commit is contained in:
parent
c4d9de3566
commit
ed87a7b33a
@ -16,4 +16,7 @@ object Globals {
|
|||||||
|
|
||||||
// for playlists
|
// for playlists
|
||||||
var SELECTED_PLAYLIST_ID: String? = null
|
var SELECTED_PLAYLIST_ID: String? = null
|
||||||
|
|
||||||
|
// history of played videos in the current lifecycle
|
||||||
|
val videoIds = mutableListOf<String>()
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package com.github.libretube.dialogs
|
package com.github.libretube.dialogs
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.github.libretube.Globals
|
||||||
|
import com.github.libretube.PLAYER_NOTIFICATION_ID
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.util.BackgroundHelper
|
import com.github.libretube.util.BackgroundHelper
|
||||||
@ -27,12 +31,22 @@ class VideoOptionsDialog(
|
|||||||
/**
|
/**
|
||||||
* List that stores the different menu options. In the future could be add more options here.
|
* List that stores the different menu options. In the future could be add more options here.
|
||||||
*/
|
*/
|
||||||
val optionsList = listOf(
|
val optionsList = mutableListOf(
|
||||||
context?.getString(R.string.playOnBackground),
|
context?.getString(R.string.playOnBackground),
|
||||||
context?.getString(R.string.addToPlaylist),
|
context?.getString(R.string.addToPlaylist),
|
||||||
context?.getString(R.string.share)
|
context?.getString(R.string.share)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the player is running by observing the notification
|
||||||
|
*/
|
||||||
|
val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
notificationManager.activeNotifications.forEach {
|
||||||
|
if (it.id == PLAYER_NOTIFICATION_ID) {
|
||||||
|
optionsList += context?.getString(R.string.add_to_queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return MaterialAlertDialogBuilder(requireContext())
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setAdapter(
|
.setAdapter(
|
||||||
@ -68,6 +82,9 @@ class VideoOptionsDialog(
|
|||||||
// using parentFragmentManager is important here
|
// using parentFragmentManager is important here
|
||||||
shareDialog.show(parentFragmentManager, ShareDialog::class.java.name)
|
shareDialog.show(parentFragmentManager, ShareDialog::class.java.name)
|
||||||
}
|
}
|
||||||
|
context?.getString(R.string.add_to_queue) -> {
|
||||||
|
Globals.videoIds += videoId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
|
@ -27,7 +27,6 @@ import androidx.constraintlayout.motion.widget.MotionLayout
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.os.postDelayed
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
@ -176,11 +175,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
*/
|
*/
|
||||||
private lateinit var nowPlayingNotification: NowPlayingNotification
|
private lateinit var nowPlayingNotification: NowPlayingNotification
|
||||||
|
|
||||||
/**
|
|
||||||
* history of played videos in the current lifecycle
|
|
||||||
*/
|
|
||||||
val videoIds = mutableListOf<String>()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let {
|
arguments?.let {
|
||||||
@ -754,7 +748,7 @@ class PlayerFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
videoIds += videoId!!
|
Globals.videoIds += videoId!!
|
||||||
}
|
}
|
||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
@ -763,24 +757,10 @@ class PlayerFragment : BaseFragment() {
|
|||||||
* set the videoId of the next stream for autoplay
|
* set the videoId of the next stream for autoplay
|
||||||
*/
|
*/
|
||||||
private fun setNextStream() {
|
private fun setNextStream() {
|
||||||
// don't play a video if it got played before already
|
if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId)
|
||||||
var index = 0
|
|
||||||
while (nextStreamId == null || nextStreamId == videoId!! ||
|
|
||||||
(
|
|
||||||
videoIds.contains(nextStreamId) &&
|
|
||||||
videoIds.indexOf(videoId) > videoIds.indexOf(nextStreamId)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
nextStreamId = streams.relatedStreams!![index].url.toID()
|
|
||||||
if (index + 1 < streams.relatedStreams!!.size) index += 1
|
|
||||||
else break
|
|
||||||
}
|
|
||||||
if (playlistId == null) return
|
|
||||||
if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!)
|
|
||||||
// search for the next videoId in the playlist
|
// search for the next videoId in the playlist
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val nextId = autoPlayHelper.getNextPlaylistVideoId(videoId!!)
|
nextStreamId = autoPlayHelper.getNextVideoId(videoId!!, streams.relatedStreams!!)
|
||||||
if (nextId != null) nextStreamId = nextId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1073,13 +1053,13 @@ class PlayerFragment : BaseFragment() {
|
|||||||
|
|
||||||
// next and previous buttons
|
// next and previous buttons
|
||||||
playerBinding.skipPrev.visibility = if (
|
playerBinding.skipPrev.visibility = if (
|
||||||
skipButtonsEnabled && videoIds.indexOf(videoId!!) != 0
|
skipButtonsEnabled && Globals.videoIds.indexOf(videoId!!) != 0
|
||||||
) View.VISIBLE else View.INVISIBLE
|
) View.VISIBLE else View.INVISIBLE
|
||||||
playerBinding.skipNext.visibility = if (skipButtonsEnabled) View.VISIBLE else View.INVISIBLE
|
playerBinding.skipNext.visibility = if (skipButtonsEnabled) View.VISIBLE else View.INVISIBLE
|
||||||
|
|
||||||
playerBinding.skipPrev.setOnClickListener {
|
playerBinding.skipPrev.setOnClickListener {
|
||||||
val index = videoIds.indexOf(videoId!!) - 1
|
val index = Globals.videoIds.indexOf(videoId!!) - 1
|
||||||
videoId = videoIds[index]
|
videoId = Globals.videoIds[index]
|
||||||
playVideo()
|
playVideo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ class BackgroundMode : Service() {
|
|||||||
if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!)
|
if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!)
|
||||||
// search for the next videoId in the playlist
|
// search for the next videoId in the playlist
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val nextId = autoPlayHelper.getNextPlaylistVideoId(videoId)
|
val nextId = autoPlayHelper.getNextVideoId(videoId)
|
||||||
if (nextId != null) nextStreamId = nextId
|
if (nextId != null) nextStreamId = nextId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,55 @@
|
|||||||
package com.github.libretube.util
|
package com.github.libretube.util
|
||||||
|
|
||||||
|
import com.github.libretube.Globals
|
||||||
|
import com.github.libretube.obj.StreamItem
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class AutoPlayHelper(
|
class AutoPlayHelper(
|
||||||
private val playlistId: String
|
private val playlistId: String?
|
||||||
) {
|
) {
|
||||||
private val TAG = "AutoPlayHelper"
|
private val TAG = "AutoPlayHelper"
|
||||||
|
|
||||||
private val playlistStreamIds = mutableListOf<String>()
|
private val playlistStreamIds = mutableListOf<String>()
|
||||||
private var playlistNextPage: String? = null
|
private var playlistNextPage: String? = null
|
||||||
|
|
||||||
suspend fun getNextPlaylistVideoId(currentVideoId: String): String? {
|
suspend fun getNextVideoId(
|
||||||
|
currentVideoId: String,
|
||||||
|
relatedStreams: List<StreamItem>
|
||||||
|
): String? {
|
||||||
|
val currentVideoIndex = Globals.videoIds.indexOf(currentVideoId)
|
||||||
|
return if (Globals.videoIds.size > currentVideoIndex + 1) {
|
||||||
|
Globals.videoIds[currentVideoIndex + 1]
|
||||||
|
} else if (playlistId == null) getNextTrendingVideoId(
|
||||||
|
currentVideoId,
|
||||||
|
relatedStreams
|
||||||
|
) else {
|
||||||
|
getNextPlaylistVideoId(
|
||||||
|
currentVideoId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNextTrendingVideoId(videoId: String, relatedStreams: List<StreamItem>): String? {
|
||||||
|
// don't play a video if it got played before already
|
||||||
|
var index = 0
|
||||||
|
var nextStreamId: String? = null
|
||||||
|
while (nextStreamId == null ||
|
||||||
|
(
|
||||||
|
Globals.videoIds.contains(nextStreamId) &&
|
||||||
|
Globals.videoIds.indexOf(videoId) > Globals.videoIds.indexOf(
|
||||||
|
nextStreamId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
nextStreamId = relatedStreams[index].url.toID()
|
||||||
|
if (index + 1 < relatedStreams.size) index += 1
|
||||||
|
else break
|
||||||
|
}
|
||||||
|
return nextStreamId
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getNextPlaylistVideoId(currentVideoId: String): String? {
|
||||||
// if the playlists contain the video, then save the next video as next stream
|
// if the playlists contain the video, then save the next video as next stream
|
||||||
if (playlistStreamIds.contains(currentVideoId)) {
|
if (playlistStreamIds.contains(currentVideoId)) {
|
||||||
val index = playlistStreamIds.indexOf(currentVideoId)
|
val index = playlistStreamIds.indexOf(currentVideoId)
|
||||||
@ -24,9 +62,9 @@ class AutoPlayHelper(
|
|||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
// fetch the playlists or its nextPage's videos
|
// fetch the playlists or its nextPage's videos
|
||||||
val playlist =
|
val playlist =
|
||||||
if (playlistNextPage == null) RetrofitInstance.authApi.getPlaylist(playlistId)
|
if (playlistNextPage == null) RetrofitInstance.authApi.getPlaylist(playlistId!!)
|
||||||
else RetrofitInstance.authApi.getPlaylistNextPage(
|
else RetrofitInstance.authApi.getPlaylistNextPage(
|
||||||
playlistId,
|
playlistId!!,
|
||||||
playlistNextPage!!
|
playlistNextPage!!
|
||||||
)
|
)
|
||||||
// save the playlist urls to the list
|
// save the playlist urls to the list
|
||||||
|
@ -297,4 +297,5 @@
|
|||||||
<string name="history_size">Maximum history size</string>
|
<string name="history_size">Maximum history size</string>
|
||||||
<string name="unlimited">Unlimited</string>
|
<string name="unlimited">Unlimited</string>
|
||||||
<string name="background_mode">Background mode</string>
|
<string name="background_mode">Background mode</string>
|
||||||
|
<string name="add_to_queue">Add to queue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user