mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 15:30:31 +05:30
Refactor the SponsorBlock segment handling
This commit is contained in:
parent
7648f1822f
commit
9746d5a5fd
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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()
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user