Replace the autoPlayHelper with the PlayingQueue

This commit is contained in:
Bnyro 2022-10-23 14:43:47 +02:00
parent 41162fafac
commit 02d4321a2d
7 changed files with 74 additions and 151 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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