mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 16:00:31 +05:30
Replace the autoPlayHelper with the PlayingQueue
This commit is contained in:
parent
41162fafac
commit
02d4321a2d
@ -24,9 +24,7 @@ import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.db.DatabaseHelper
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.extensions.awaitQuery
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.extensions.toStreamItem
|
||||
import com.github.libretube.util.AutoPlayHelper
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
import com.github.libretube.util.PlayerHelper
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
@ -81,16 +79,6 @@ class BackgroundMode : Service() {
|
||||
*/
|
||||
private lateinit var nowPlayingNotification: NowPlayingNotification
|
||||
|
||||
/**
|
||||
* The [videoId] of the next stream for autoplay
|
||||
*/
|
||||
private var nextStreamId: String? = null
|
||||
|
||||
/**
|
||||
* Helper for finding the next video in the playlist
|
||||
*/
|
||||
private lateinit var autoPlayHelper: AutoPlayHelper
|
||||
|
||||
/**
|
||||
* Autoplay Preference
|
||||
*/
|
||||
@ -133,9 +121,6 @@ class BackgroundMode : Service() {
|
||||
playlistId = intent.getStringExtra(IntentData.playlistId)
|
||||
val position = intent.getLongExtra(IntentData.position, 0L)
|
||||
|
||||
// initialize the playlist autoPlay Helper
|
||||
autoPlayHelper = AutoPlayHelper(playlistId)
|
||||
|
||||
// play the audio in the background
|
||||
loadAudio(videoId, position)
|
||||
|
||||
@ -169,7 +154,12 @@ class BackgroundMode : Service() {
|
||||
return@launch
|
||||
}
|
||||
|
||||
// add the playlist video to the queue
|
||||
if (playlistId != null && PlayingQueue.isEmpty()) {
|
||||
PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId))
|
||||
} else {
|
||||
PlayingQueue.updateCurrent(streams.toStreamItem(videoId))
|
||||
}
|
||||
|
||||
handler.post {
|
||||
playAudio(seekToPosition)
|
||||
@ -221,8 +211,6 @@ class BackgroundMode : Service() {
|
||||
player?.setPlaybackSpeed(playbackSpeed)
|
||||
|
||||
fetchSponsorBlockSegments()
|
||||
|
||||
if (PlayerHelper.autoPlayEnabled) setNextStream()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -271,32 +259,16 @@ class BackgroundMode : Service() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* set the videoId of the next stream for autoplay
|
||||
*/
|
||||
private fun setNextStream() {
|
||||
if (streams!!.relatedStreams!!.isNotEmpty()) {
|
||||
nextStreamId = streams?.relatedStreams!![0].url!!.toID()
|
||||
}
|
||||
|
||||
if (playlistId == null) return
|
||||
if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!)
|
||||
// search for the next videoId in the playlist
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
nextStreamId = autoPlayHelper.getNextVideoId(videoId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the first related video to the current (used when the playback of the current video ended)
|
||||
*/
|
||||
private fun playNextVideo() {
|
||||
if (nextStreamId == null || nextStreamId == videoId) return
|
||||
val nextQueueVideo = PlayingQueue.getNext()
|
||||
if (nextQueueVideo != null) nextStreamId = nextQueueVideo
|
||||
val nextVideo = PlayingQueue.getNext()
|
||||
|
||||
// play new video on background
|
||||
this.videoId = nextStreamId!!
|
||||
if (nextVideo != null) {
|
||||
this.videoId = nextVideo
|
||||
}
|
||||
this.segmentData = null
|
||||
loadAudio(videoId)
|
||||
}
|
||||
|
@ -4,14 +4,12 @@ import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.databinding.QueueRowBinding
|
||||
import com.github.libretube.ui.viewholders.PlayingQueueViewHolder
|
||||
import com.github.libretube.util.ImageHelper
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
|
||||
class PlayingQueueAdapter(
|
||||
private val items: List<StreamItem>
|
||||
) : RecyclerView.Adapter<PlayingQueueViewHolder>() {
|
||||
class PlayingQueueAdapter() : RecyclerView.Adapter<PlayingQueueViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayingQueueViewHolder {
|
||||
val binding = QueueRowBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
@ -22,16 +20,18 @@ class PlayingQueueAdapter(
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
return PlayingQueue.size()
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PlayingQueueViewHolder, position: Int) {
|
||||
val streamItem = items[position]
|
||||
val streamItem = PlayingQueue.getStreams()[position]
|
||||
holder.binding.apply {
|
||||
ImageHelper.loadImage(streamItem.thumbnail, thumbnail)
|
||||
title.text = streamItem.title
|
||||
uploader.text = streamItem.uploaderName
|
||||
duration.text = streamItem.duration?.let { DateUtils.formatElapsedTime(it) }
|
||||
duration.text = streamItem.duration?.let {
|
||||
DateUtils.formatElapsedTime(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import com.github.libretube.ui.adapters.ChannelAdapter
|
||||
import com.github.libretube.ui.adapters.TrendingAdapter
|
||||
import com.github.libretube.ui.base.BaseFragment
|
||||
import com.github.libretube.util.LocaleHelper
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import retrofit2.HttpException
|
||||
|
@ -67,7 +67,6 @@ import com.github.libretube.ui.dialogs.DownloadDialog
|
||||
import com.github.libretube.ui.dialogs.ShareDialog
|
||||
import com.github.libretube.ui.sheets.PlayingQueueSheet
|
||||
import com.github.libretube.ui.views.BottomSheet
|
||||
import com.github.libretube.util.AutoPlayHelper
|
||||
import com.github.libretube.util.BackgroundHelper
|
||||
import com.github.libretube.util.ImageHelper
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
@ -155,12 +154,6 @@ class PlayerFragment : BaseFragment() {
|
||||
private var token = PreferenceHelper.getToken()
|
||||
private var videoShownInExternalPlayer = false
|
||||
|
||||
/**
|
||||
* for autoplay
|
||||
*/
|
||||
private var nextStreamId: String? = null
|
||||
private lateinit var autoPlayHelper: AutoPlayHelper
|
||||
|
||||
/**
|
||||
* for the player notification
|
||||
*/
|
||||
@ -649,11 +642,20 @@ class PlayerFragment : BaseFragment() {
|
||||
return@launchWhenCreated
|
||||
}
|
||||
|
||||
if (PlayingQueue.size() == 0) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (playlistId != null) {
|
||||
PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId!!))
|
||||
} else {
|
||||
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!))
|
||||
|
||||
if (PlayingQueue.size() <= 1) PlayingQueue.add(
|
||||
PlayingQueue.add(
|
||||
*streams.relatedStreams.orEmpty().toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!))
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
// hide the button to skip SponsorBlock segments manually
|
||||
@ -678,8 +680,6 @@ class PlayerFragment : BaseFragment() {
|
||||
if (PlayerHelper.sponsorBlockEnabled) fetchSponsorBlockSegments()
|
||||
// show comments if related streams disabled
|
||||
if (!PlayerHelper.relatedStreamsEnabled) toggleComments()
|
||||
// prepare for autoplay
|
||||
if (binding.player.autoplayEnabled) setNextStream()
|
||||
|
||||
// add the video to the watch history
|
||||
if (PlayerHelper.watchHistoryEnabled) {
|
||||
@ -692,17 +692,6 @@ class PlayerFragment : BaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the videoId of the next stream for autoplay
|
||||
*/
|
||||
private fun setNextStream() {
|
||||
if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId)
|
||||
// search for the next videoId in the playlist
|
||||
lifecycleScope.launchWhenCreated {
|
||||
nextStreamId = autoPlayHelper.getNextVideoId(videoId!!)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch the segments for SponsorBlock
|
||||
*/
|
||||
@ -768,17 +757,14 @@ class PlayerFragment : BaseFragment() {
|
||||
|
||||
// used for autoplay and skipping to next video
|
||||
private fun playNextVideo() {
|
||||
if (nextStreamId == null) return
|
||||
// check whether there is a new video in the queue
|
||||
val nextQueueVideo = PlayingQueue.getNext()
|
||||
if (nextQueueVideo != null) nextStreamId = nextQueueVideo
|
||||
val nextVideo = PlayingQueue.getNext()
|
||||
// by making sure that the next and the current video aren't the same
|
||||
saveWatchPosition()
|
||||
// forces the comments to reload for the new video
|
||||
commentsLoaded = false
|
||||
binding.commentsRecView.adapter = null
|
||||
// save the id of the next stream as videoId and load the next video
|
||||
videoId = nextStreamId
|
||||
videoId = nextVideo
|
||||
playVideo()
|
||||
}
|
||||
|
||||
@ -876,7 +862,6 @@ class PlayerFragment : BaseFragment() {
|
||||
@Suppress("DEPRECATION")
|
||||
if (
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
nextStreamId != null &&
|
||||
!transitioning &&
|
||||
binding.player.autoplayEnabled
|
||||
) {
|
||||
|
@ -7,7 +7,6 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.databinding.BottomSheetBinding
|
||||
import com.github.libretube.ui.adapters.PlayingQueueAdapter
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
|
||||
class PlayingQueueSheet : BottomSheetDialogFragment() {
|
||||
@ -26,8 +25,6 @@ class PlayingQueueSheet : BottomSheetDialogFragment() {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.optionsRecycler.layoutManager = LinearLayoutManager(context)
|
||||
binding.optionsRecycler.adapter = PlayingQueueAdapter(
|
||||
PlayingQueue.getStreams()
|
||||
)
|
||||
binding.optionsRecycler.adapter = PlayingQueueAdapter()
|
||||
}
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.extensions.toID
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AutoPlayHelper(
|
||||
private val playlistId: String?
|
||||
) {
|
||||
|
||||
private val playlistStreamIds = mutableListOf<String>()
|
||||
private var playlistNextPage: String? = null
|
||||
|
||||
/**
|
||||
* get the id of the next video to be played
|
||||
*/
|
||||
suspend fun getNextVideoId(
|
||||
currentVideoId: String
|
||||
): String? {
|
||||
return if (playlistId == null) {
|
||||
null
|
||||
} else {
|
||||
getNextPlaylistVideoId(
|
||||
currentVideoId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the videoId of the next video in a playlist
|
||||
*/
|
||||
private suspend fun getNextPlaylistVideoId(currentVideoId: String): String? {
|
||||
// if the playlists contain the video, then save the next video as next stream
|
||||
if (playlistStreamIds.contains(currentVideoId)) {
|
||||
val index = playlistStreamIds.indexOf(currentVideoId)
|
||||
// check whether there's a next video
|
||||
return if (index + 1 < playlistStreamIds.size) {
|
||||
playlistStreamIds[index + 1]
|
||||
} else if (playlistNextPage == null) {
|
||||
null
|
||||
} else {
|
||||
getNextPlaylistVideoId(currentVideoId)
|
||||
}
|
||||
} else if (playlistStreamIds.isEmpty() || playlistNextPage != null) {
|
||||
// fetch the next page of the playlist
|
||||
return withContext(Dispatchers.IO) {
|
||||
// fetch the playlists or its nextPage's videos
|
||||
val playlist =
|
||||
if (playlistNextPage == null) {
|
||||
RetrofitInstance.authApi.getPlaylist(playlistId!!)
|
||||
} else {
|
||||
RetrofitInstance.authApi.getPlaylistNextPage(
|
||||
playlistId!!,
|
||||
playlistNextPage!!
|
||||
)
|
||||
}
|
||||
// save the playlist urls to the list
|
||||
playlistStreamIds += playlist.relatedStreams!!.map { it.url!!.toID() }
|
||||
// save playlistNextPage for usage if video is not contained
|
||||
playlistNextPage = playlist.nextpage
|
||||
return@withContext getNextPlaylistVideoId(currentVideoId)
|
||||
}
|
||||
}
|
||||
// return null when no nextPage is found
|
||||
return null
|
||||
}
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.extensions.toID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object PlayingQueue {
|
||||
private val queue = mutableListOf<StreamItem>()
|
||||
@ -44,7 +48,7 @@ object PlayingQueue {
|
||||
|
||||
fun updateCurrent(streamItem: StreamItem) {
|
||||
currentStream = streamItem
|
||||
queue.add(streamItem)
|
||||
if (!contains(streamItem)) queue.add(streamItem)
|
||||
}
|
||||
|
||||
fun isNotEmpty() = queue.isNotEmpty()
|
||||
@ -57,7 +61,41 @@ object PlayingQueue {
|
||||
|
||||
private fun currentIndex() = queue.indexOf(currentStream)
|
||||
|
||||
fun contains(streamItem: StreamItem) = queue.contains(streamItem)
|
||||
fun contains(streamItem: StreamItem) = queue.any { it.url?.toID() == streamItem.url?.toID() }
|
||||
|
||||
fun getStreams() = queue
|
||||
|
||||
private fun fetchMoreFromPlaylist(playlistId: String, nextPage: String?) {
|
||||
var playlistNextPage: String? = nextPage
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
while (playlistNextPage != null) {
|
||||
RetrofitInstance.authApi.getPlaylistNextPage(
|
||||
playlistId,
|
||||
playlistNextPage!!
|
||||
).apply {
|
||||
add(
|
||||
*this.relatedStreams.orEmpty().toTypedArray()
|
||||
)
|
||||
playlistNextPage = this.nextpage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun insertPlaylist(playlistId: String, newCurrentStream: StreamItem) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitInstance.authApi.getPlaylist(playlistId)
|
||||
add(
|
||||
*response.relatedStreams
|
||||
.orEmpty()
|
||||
.toTypedArray()
|
||||
)
|
||||
updateCurrent(newCurrentStream)
|
||||
fetchMoreFromPlaylist(playlistId, response.nextpage)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user