Refactor the SponsorBlock segment handling

This commit is contained in:
Bnyro 2023-02-12 13:17:44 +01:00
parent 7648f1822f
commit 9746d5a5fd
4 changed files with 57 additions and 67 deletions

View File

@ -65,7 +65,6 @@ object PreferenceKeys {
const val SYSTEM_CAPTION_STYLE = "system_caption_style"
const val CAPTION_SETTINGS = "caption_settings"
const val SEEK_INCREMENT = "seek_increment"
const val PLAYER_VIDEO_FORMAT = "player_video_format"
const val DEFAULT_RESOLUTION = "default_res"
const val DEFAULT_RESOLUTION_MOBILE = "default_res_mobile"
const val BUFFERING_GOAL = "buffering_goal"

View File

@ -9,10 +9,12 @@ import android.content.pm.ActivityInfo
import android.graphics.drawable.Icon
import android.os.Build
import android.view.accessibility.CaptioningManager
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import com.github.libretube.R
import com.github.libretube.api.obj.PipedStream
import com.github.libretube.api.obj.Segment
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.enums.AudioQuality
import com.github.libretube.enums.PlayerEvent
@ -214,12 +216,6 @@ object PlayerHelper {
true
)
val videoFormatPreference: String
get() = PreferenceHelper.getString(
PreferenceKeys.PLAYER_VIDEO_FORMAT,
"webm"
)
private val bufferingGoal: Int
get() = PreferenceHelper.getString(
PreferenceKeys.BUFFERING_GOAL,
@ -232,7 +228,7 @@ object PlayerHelper {
true
)
val sponsorBlockNotifications: Boolean
private val sponsorBlockNotifications: Boolean
get() = PreferenceHelper.getBoolean(
"sb_notifications_key",
true
@ -481,4 +477,32 @@ object PlayerHelper {
)
return this
}
/**
* Check for SponsorBlock segments matching the current player position
* @param context A main dispatcher context
* @param segments List of the SponsorBlock segments
* @param skipManually Whether the event gets handled by the function caller
* @return If segment found and [skipManually] is true, the end position of the segment in ms, otherwise null
*/
fun ExoPlayer.checkForSegments(context: Context, segments: List<Segment>, skipManually: Boolean = false): Long? {
segments.forEach { segment ->
val segmentStart = (segment.segment[0] * 1000f).toLong()
val segmentEnd = (segment.segment[1] * 1000f).toLong()
if (currentPosition in segmentStart until segmentEnd) {
if (!skipManually) {
if (sponsorBlockNotifications) {
runCatching {
Toast.makeText(context, R.string.segment_skipped, Toast.LENGTH_SHORT)
.show()
}
}
seekTo(segmentEnd)
} else {
return segmentEnd
}
}
}
return null
}
}

View File

@ -17,7 +17,7 @@ import androidx.lifecycle.lifecycleScope
import com.github.libretube.R
import com.github.libretube.api.JsonHelper
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.SegmentData
import com.github.libretube.api.obj.Segment
import com.github.libretube.api.obj.Streams
import com.github.libretube.constants.BACKGROUND_CHANNEL_ID
import com.github.libretube.constants.IntentData
@ -30,6 +30,7 @@ import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toStreamItem
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.PlayingQueue
@ -71,7 +72,7 @@ class BackgroundMode : LifecycleService() {
/**
* SponsorBlock Segment data
*/
private var segmentData: SegmentData? = null
private var segments: List<Segment> = listOf()
/**
* [Notification] for the player
@ -223,7 +224,7 @@ class BackgroundMode : LifecycleService() {
}
}
fetchSponsorBlockSegments()
if (PlayerHelper.sponsorBlockEnabled) fetchSponsorBlockSegments()
}
/**
@ -284,7 +285,7 @@ class BackgroundMode : LifecycleService() {
// play new video on background
this.videoId = nextVideo
this.streams = null
this.segmentData = null
this.segments = emptyList()
loadAudio(videoId, keepQueue = true)
}
@ -315,10 +316,10 @@ class BackgroundMode : LifecycleService() {
runCatching {
val categories = PlayerHelper.getSponsorBlockCategories()
if (categories.isEmpty()) return@runCatching
segmentData = RetrofitInstance.api.getSegments(
segments = RetrofitInstance.api.getSegments(
videoId,
JsonHelper.json.encodeToString(categories)
)
).segments
checkForSegments()
}
}
@ -328,24 +329,9 @@ class BackgroundMode : LifecycleService() {
* check for SponsorBlock segments
*/
private fun checkForSegments() {
Handler(Looper.getMainLooper()).postDelayed(this::checkForSegments, 100)
handler.postDelayed(this::checkForSegments, 100)
if (segmentData == null || segmentData!!.segments.isEmpty()) return
segmentData!!.segments.forEach { segment ->
val segmentStart = (segment.segment[0] * 1000f).toLong()
val segmentEnd = (segment.segment[1] * 1000f).toLong()
val currentPosition = player?.currentPosition
if (currentPosition in segmentStart until segmentEnd) {
if (PlayerHelper.sponsorBlockNotifications) {
runCatching {
Toast.makeText(this, R.string.segment_skipped, Toast.LENGTH_SHORT)
.show()
}
}
player?.seekTo(segmentEnd)
}
}
player?.checkForSegments(this, segments)
}
private fun updateQueue() {

View File

@ -46,7 +46,6 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.ChapterSegment
import com.github.libretube.api.obj.PipedStream
import com.github.libretube.api.obj.Segment
import com.github.libretube.api.obj.SegmentData
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Streams
import com.github.libretube.constants.IntentData
@ -73,6 +72,7 @@ import com.github.libretube.helpers.DashHelper
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.ShareData
@ -114,10 +114,6 @@ import com.google.android.exoplayer2.ui.StyledPlayerView
import com.google.android.exoplayer2.upstream.DefaultDataSource
import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.IOException
import java.util.*
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -125,6 +121,10 @@ import kotlinx.coroutines.withContext
import kotlinx.datetime.LocalDate
import kotlinx.serialization.encodeToString
import retrofit2.HttpException
import java.io.IOException
import java.util.*
import java.util.concurrent.Executors
import kotlin.math.abs
class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
@ -182,7 +182,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
/**
* SponsorBlock
*/
private lateinit var segmentData: SegmentData
private var segments = listOf<Segment>()
private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
val handler = Handler(Looper.getMainLooper())
@ -642,31 +642,14 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
if (!sponsorBlockEnabled) return
if (!::segmentData.isInitialized || segmentData.segments.isEmpty()) return
if (segments.isEmpty()) return
val currentPosition = exoPlayer.currentPosition
segmentData.segments.forEach { segment: Segment ->
val segmentStart = (segment.segment[0] * 1000f).toLong()
val segmentEnd = (segment.segment[1] * 1000f).toLong()
// show the button to manually skip the segment
if (currentPosition in segmentStart until segmentEnd) {
if (PlayerHelper.skipSegmentsManually) {
binding.sbSkipBtn.visibility = View.VISIBLE
binding.sbSkipBtn.setOnClickListener {
exoPlayer.seekTo(segmentEnd)
}
return
}
if (PlayerHelper.sponsorBlockNotifications) {
Toast.makeText(context, R.string.segment_skipped, Toast.LENGTH_SHORT).show()
}
// skip the segment automatically
exoPlayer.checkForSegments(requireContext(), segments, PlayerHelper.skipSegmentsManually)?.let { segmentEnd ->
binding.sbSkipBtn.visibility = View.VISIBLE
binding.sbSkipBtn.setOnClickListener {
exoPlayer.seekTo(segmentEnd)
return
}
return
}
if (PlayerHelper.skipSegmentsManually) binding.sbSkipBtn.visibility = View.GONE
@ -764,13 +747,13 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
runCatching {
val categories = PlayerHelper.getSponsorBlockCategories()
if (categories.isEmpty()) return@runCatching
segmentData =
segments =
RetrofitInstance.api.getSegments(
videoId!!,
JsonHelper.json.encodeToString(categories)
)
if (segmentData.segments.isEmpty()) return@runCatching
playerBinding.exoProgress.setSegments(segmentData.segments)
).segments
if (segments.isEmpty()) return@runCatching
playerBinding.exoProgress.setSegments(segments)
runOnUiThread {
playerBinding.sbToggle.visibility = View.VISIBLE
updateDisplayedDuration()
@ -1103,12 +1086,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
playerBinding.duration.text = DateUtils.formatElapsedTime(
exoPlayer.duration.div(1000)
)
if (!this::segmentData.isInitialized || this.segmentData.segments.isEmpty()) {
return
}
if (segments.isEmpty()) return
val durationWithSb = DateUtils.formatElapsedTime(
exoPlayer.duration.div(1000) - segmentData.segments.sumOf {
exoPlayer.duration.div(1000) - segments.sumOf {
it.segment[1] - it.segment[0]
}.toInt()
)