Merge pull request #1328 from Bnyro/master

Improved playing queue
This commit is contained in:
Bnyro 2022-09-19 22:22:32 +02:00 committed by GitHub
commit de69dd3795
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 107 additions and 81 deletions

View File

@ -1,12 +0,0 @@
package com.github.libretube
/**
* Global variables can be stored here
*/
object Globals {
// for downloads
var IS_DOWNLOAD_RUNNING = false
// history of played videos in the current lifecycle
val playingQueue = mutableListOf<String>()
}

View File

@ -56,9 +56,9 @@ object DatabaseHelper {
fun removeWatchPosition(videoId: String) {
Thread {
Database.watchPositionDao().delete(
Database.watchPositionDao().findById(videoId)
)
Database.watchPositionDao().findById(videoId)?.let {
Database.watchPositionDao().delete(it)
}
}.start()
}

View File

@ -13,7 +13,7 @@ interface WatchPositionDao {
fun getAll(): List<WatchPosition>
@Query("SELECT * FROM watchPosition WHERE videoId LIKE :videoId LIMIT 1")
fun findById(videoId: String): WatchPosition
fun findById(videoId: String): WatchPosition?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg watchPositions: WatchPosition)

View File

@ -14,7 +14,7 @@ fun View?.setWatchProgressLength(videoId: String, duration: Long) {
Thread {
try {
progress = Database.watchPositionDao().findById(videoId).position
progress = Database.watchPositionDao().findById(videoId)?.position
} catch (e: Exception) {
progress = null
}

View File

@ -35,7 +35,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.libretube.Globals
import com.github.libretube.R
import com.github.libretube.activities.MainActivity
import com.github.libretube.adapters.ChaptersAdapter
@ -67,12 +66,14 @@ import com.github.libretube.obj.Segment
import com.github.libretube.obj.Segments
import com.github.libretube.obj.Streams
import com.github.libretube.services.BackgroundMode
import com.github.libretube.services.DownloadService
import com.github.libretube.util.AutoPlayHelper
import com.github.libretube.util.BackgroundHelper
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NetworkHelper
import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.PreferenceHelper
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.DefaultLoadControl
@ -210,7 +211,7 @@ class PlayerFragment : BaseFragment() {
context?.hideKeyboard(view)
// clear the playing queue
Globals.playingQueue.clear()
PlayingQueue.clear()
setUserPrefs()
@ -686,6 +687,9 @@ class PlayerFragment : BaseFragment() {
override fun onDestroy() {
super.onDestroy()
try {
// clear the playing queue
PlayingQueue.clear()
saveWatchPosition()
nowPlayingNotification.destroy()
activity?.requestedOrientation =
@ -695,6 +699,7 @@ class PlayerFragment : BaseFragment() {
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
}
} catch (e: Exception) {
e.printStackTrace()
}
}
@ -752,8 +757,9 @@ class PlayerFragment : BaseFragment() {
}
private fun playVideo() {
Globals.playingQueue += videoId!!
lifecycleScope.launchWhenCreated {
PlayingQueue.updateCurrent(videoId!!)
streams = try {
RetrofitInstance.api.getStreams(videoId!!)
} catch (e: IOException) {
@ -861,7 +867,7 @@ class PlayerFragment : BaseFragment() {
var position: Long? = null
Thread {
try {
position = Database.watchPositionDao().findById(videoId!!).position
position = Database.watchPositionDao().findById(videoId!!)?.position
// position is almost the end of the video => don't seek, start from beginning
if (position!! > streams.duration!! * 1000 * 0.9) position = null
} catch (e: Exception) {
@ -875,7 +881,7 @@ class PlayerFragment : BaseFragment() {
private fun playNextVideo() {
if (nextStreamId == null) return
// check whether there is a new video in the queue
val nextQueueVideo = autoPlayHelper.getNextPlayingQueueVideoId(videoId!!)
val nextQueueVideo = PlayingQueue.getNext()
if (nextQueueVideo != null) nextStreamId = nextQueueVideo
// by making sure that the next and the current video aren't the same
saveWatchPosition()
@ -1023,7 +1029,7 @@ class PlayerFragment : BaseFragment() {
if (response.duration > 0) {
// download clicked
binding.relPlayerDownload.setOnClickListener {
if (!Globals.IS_DOWNLOAD_RUNNING) {
if (!DownloadService.IS_DOWNLOAD_RUNNING) {
val newFragment = DownloadDialog(videoId!!)
newFragment.show(childFragmentManager, DownloadDialog::class.java.name)
} else {
@ -1105,7 +1111,7 @@ class PlayerFragment : BaseFragment() {
// next and previous buttons
playerBinding.skipPrev.visibility = if (
skipButtonsEnabled && Globals.playingQueue.indexOf(videoId!!) != 0
skipButtonsEnabled && PlayingQueue.hasPrev()
) {
View.VISIBLE
} else {
@ -1114,8 +1120,7 @@ class PlayerFragment : BaseFragment() {
playerBinding.skipNext.visibility = if (skipButtonsEnabled) View.VISIBLE else View.INVISIBLE
playerBinding.skipPrev.setOnClickListener {
val index = Globals.playingQueue.indexOf(videoId!!) - 1
videoId = Globals.playingQueue[index]
videoId = PlayingQueue.getPrev()
playVideo()
}

View File

@ -11,7 +11,6 @@ import android.os.IBinder
import android.os.Looper
import android.widget.Toast
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.libretube.Globals
import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.constants.BACKGROUND_CHANNEL_ID
@ -25,6 +24,7 @@ import com.github.libretube.obj.Streams
import com.github.libretube.util.AutoPlayHelper
import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.PreferenceHelper
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer
@ -119,7 +119,7 @@ class BackgroundMode : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try {
// clear the playing queue
Globals.playingQueue.clear()
PlayingQueue.clear()
// get the intent arguments
videoId = intent?.getStringExtra(IntentData.videoId)!!
@ -145,7 +145,7 @@ class BackgroundMode : Service() {
seekToPosition: Long = 0
) {
// append the video to the playing queue
Globals.playingQueue += videoId
PlayingQueue.add(videoId)
CoroutineScope(Dispatchers.IO).launch {
try {
streams = RetrofitInstance.api.getStreams(videoId)
@ -162,6 +162,8 @@ class BackgroundMode : Service() {
private fun playAudio(
seekToPosition: Long
) {
PlayingQueue.updateCurrent(videoId)
initializePlayer()
setMediaItem()
@ -247,7 +249,7 @@ class BackgroundMode : Service() {
*/
private fun playNextVideo() {
if (nextStreamId == null || nextStreamId == videoId) return
val nextQueueVideo = autoPlayHelper.getNextPlayingQueueVideoId(videoId)
val nextQueueVideo = PlayingQueue.getNext()
if (nextQueueVideo != null) nextStreamId = nextQueueVideo
// play new video on background
@ -331,6 +333,9 @@ class BackgroundMode : Service() {
* destroy the [BackgroundMode] foreground service
*/
override fun onDestroy() {
// clear the playing queue
PlayingQueue.clear()
// called when the user pressed stop in the notification
// stop the service from being in the foreground and remove the notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

View File

@ -11,7 +11,6 @@ import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.github.libretube.Globals
import com.github.libretube.R
import com.github.libretube.constants.DOWNLOAD_CHANNEL_ID
import com.github.libretube.constants.DOWNLOAD_FAILURE_NOTIFICATION_ID
@ -34,7 +33,7 @@ class DownloadService : Service() {
override fun onCreate() {
super.onCreate()
Globals.IS_DOWNLOAD_RUNNING = true
IS_DOWNLOAD_RUNNING = true
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -175,11 +174,16 @@ class DownloadService : Service() {
try {
unregisterReceiver(onDownloadComplete)
} catch (e: Exception) {
e.printStackTrace()
}
Globals.IS_DOWNLOAD_RUNNING = false
IS_DOWNLOAD_RUNNING = false
stopService(Intent(this, DownloadService::class.java))
super.onDestroy()
}
companion object {
var IS_DOWNLOAD_RUNNING = false
}
}

View File

@ -1,18 +1,14 @@
package com.github.libretube.sheets
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import com.github.libretube.Globals
import com.github.libretube.R
import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PLAYER_NOTIFICATION_ID
import com.github.libretube.dialogs.AddToPlaylistDialog
import com.github.libretube.dialogs.DownloadDialog
import com.github.libretube.dialogs.ShareDialog
import com.github.libretube.util.BackgroundHelper
import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.views.BottomSheet
@ -41,21 +37,13 @@ class VideoOptionsBottomSheet(
)
}
/**
* Check whether the player is running by observing the notification
* Check whether the player is running and add queue options
*/
try {
val notificationManager =
context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager.activeNotifications.forEach {
if (it.id == PLAYER_NOTIFICATION_ID) {
optionsList += context?.getString(R.string.add_to_queue)!!
}
}
}
} catch (e: Exception) {
e.printStackTrace()
if (PlayingQueue.isNotEmpty()) {
optionsList += context?.getString(R.string.play_next)!!
optionsList += context?.getString(R.string.add_to_queue)!!
}
setSimpleItems(optionsList) { which ->
@ -89,8 +77,11 @@ class VideoOptionsBottomSheet(
// using parentFragmentManager is important here
shareDialog.show(parentFragmentManager, ShareDialog::class.java.name)
}
context?.getString(R.string.play_next) -> {
PlayingQueue.playNext(videoId)
}
context?.getString(R.string.add_to_queue) -> {
Globals.playingQueue += videoId
PlayingQueue.add(videoId)
}
}
}

View File

@ -1,6 +1,5 @@
package com.github.libretube.util
import com.github.libretube.Globals
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.extensions.toID
import com.github.libretube.obj.StreamItem
@ -21,12 +20,8 @@ class AutoPlayHelper(
currentVideoId: String,
relatedStreams: List<StreamItem>?
): String? {
return if (Globals.playingQueue.last() != currentVideoId) {
val currentVideoIndex = Globals.playingQueue.indexOf(currentVideoId)
Globals.playingQueue[currentVideoIndex + 1]
} else if (playlistId == null) {
return if (playlistId == null) {
getNextTrendingVideoId(
currentVideoId,
relatedStreams
)
} else {
@ -40,21 +35,13 @@ class AutoPlayHelper(
* get the id of the next related video
*/
private fun getNextTrendingVideoId(
videoId: String,
relatedStreams: List<StreamItem>?
): String? {
// don't play a video if it got played before already
if (relatedStreams == null || relatedStreams.isEmpty()) return null
var index = 0
var nextStreamId: String? = null
while (nextStreamId == null ||
(
Globals.playingQueue.contains(nextStreamId) &&
Globals.playingQueue.indexOf(videoId) > Globals.playingQueue.indexOf(
nextStreamId
)
)
) {
while (nextStreamId == null || PlayingQueue.containsBefore(nextStreamId)) {
nextStreamId = relatedStreams[index].url!!.toID()
if (index + 1 < relatedStreams.size) {
index += 1
@ -103,18 +90,4 @@ class AutoPlayHelper(
// return null when no nextPage is found
return null
}
/**
* get the videoId of the next video in the playing queue
*/
fun getNextPlayingQueueVideoId(
currentVideoId: String
): String? {
return if (Globals.playingQueue.last() != currentVideoId) {
val currentVideoIndex = Globals.playingQueue.indexOf(currentVideoId)
Globals.playingQueue[currentVideoIndex + 1]
} else {
null
}
}
}

View File

@ -166,6 +166,7 @@ class NowPlayingNotification(
Context.NOTIFICATION_SERVICE
) as NotificationManager
notificationManager.cancel(PLAYER_NOTIFICATION_ID)
player.stop()
player.release()
}
}

View File

@ -0,0 +1,58 @@
package com.github.libretube.util
object PlayingQueue {
private val queue = mutableListOf<String>()
private var currentVideoId: String? = null
fun clear() {
queue.clear()
}
fun add(videoId: String) {
if (currentVideoId == videoId) return
if (queue.contains(videoId)) queue.remove(videoId)
queue.add(videoId)
}
fun playNext(videoId: String) {
if (currentVideoId == videoId) return
if (queue.contains(videoId)) queue.remove(videoId)
queue.add(
queue.indexOf(currentVideoId) + 1,
videoId
)
}
fun getNext(): String? {
val currentIndex = queue.indexOf(currentVideoId)
return if (currentIndex >= queue.size) {
null
} else {
queue[currentIndex + 1]
}
}
fun getPrev(): String? {
val index = queue.indexOf(currentVideoId)
return if (index > 0) queue[index - 1] else null
}
fun hasPrev(): Boolean {
return queue.indexOf(currentVideoId) > 0
}
fun contains(videoId: String): Boolean {
return queue.contains(videoId)
}
fun containsBefore(videoId: String): Boolean {
return queue.contains(videoId) && queue.indexOf(videoId) < queue.indexOf(currentVideoId)
}
fun updateCurrent(videoId: String) {
currentVideoId = videoId
queue.add(videoId)
}
fun isNotEmpty() = queue.isNotEmpty()
}

View File

@ -329,6 +329,7 @@
<string name="backup_customInstances">Custom Instances</string>
<string name="save_feed">Load feed in background</string>
<string name="save_feed_summary">Load the subscription feed in the background and prevent it from being auto-refreshed.</string>
<string name="play_next">Play next</string>
<!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string>