Merge pull request #4916 from Bnyro/master

fix: conflicts between chapters and video highlight
This commit is contained in:
Bnyro 2023-10-06 17:36:51 +02:00 committed by GitHub
commit bc048e015b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 37 deletions

View File

@ -330,7 +330,10 @@ object PlayerHelper {
) )
fun shouldPlayNextVideo(isPlaylist: Boolean = false): Boolean { fun shouldPlayNextVideo(isPlaylist: Boolean = false): Boolean {
return autoPlayEnabled || (isPlaylist && PreferenceHelper.getBoolean(PreferenceKeys.AUTOPLAY_PLAYLISTS, false)) return autoPlayEnabled || (isPlaylist && PreferenceHelper.getBoolean(
PreferenceKeys.AUTOPLAY_PLAYLISTS,
false
))
} }
private val handleAudioFocus private val handleAudioFocus
@ -442,7 +445,11 @@ object PlayerHelper {
* Create a basic player, that is used for all types of playback situations inside the app * Create a basic player, that is used for all types of playback situations inside the app
*/ */
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
fun createPlayer(context: Context, trackSelector: DefaultTrackSelector, isBackgroundMode: Boolean): ExoPlayer { fun createPlayer(
context: Context,
trackSelector: DefaultTrackSelector,
isBackgroundMode: Boolean
): ExoPlayer {
val cronetDataSourceFactory = CronetDataSource.Factory( val cronetDataSourceFactory = CronetDataSource.Factory(
CronetHelper.cronetEngine, CronetHelper.cronetEngine,
Executors.newCachedThreadPool() Executors.newCachedThreadPool()
@ -579,15 +586,16 @@ object PlayerHelper {
fun getCurrentChapterIndex(currentPositionMs: Long, chapters: List<ChapterSegment>): Int? { fun getCurrentChapterIndex(currentPositionMs: Long, chapters: List<ChapterSegment>): Int? {
val currentPositionSeconds = currentPositionMs / 1000 val currentPositionSeconds = currentPositionMs / 1000
return chapters return chapters
.filter {
it.highlightDrawable == null ||
// remove the video highlight if it's already longer ago than [ChapterSegment.HIGHLIGHT_LENGTH],
// otherwise the SponsorBlock highlight would be shown from its starting point to the end
(currentPositionSeconds - it.start) < ChapterSegment.HIGHLIGHT_LENGTH
}
.sortedBy { it.start } .sortedBy { it.start }
.indexOfLast { currentPositionSeconds >= it.start } .indexOfLast { currentPositionSeconds >= it.start }
.takeIf { it >= 0 } .takeIf { it >= 0 }
?.takeIf { index ->
val chapter = chapters[index]
// remove the video highlight if it's already longer ago than [ChapterSegment.HIGHLIGHT_LENGTH],
// otherwise the SponsorBlock highlight would be shown from its starting point to the end
val isWithinMaxHighlightDuration = (currentPositionSeconds - chapter.start) < ChapterSegment.HIGHLIGHT_LENGTH
chapter.highlightDrawable == null || isWithinMaxHighlightDuration
}
} }
fun getPosition(videoId: String, duration: Long?): Long? { fun getPosition(videoId: String, duration: Long?): Long? {

View File

@ -13,7 +13,7 @@ import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.ui.viewholders.ChaptersViewHolder import com.github.libretube.ui.viewholders.ChaptersViewHolder
class ChaptersAdapter( class ChaptersAdapter(
private val chapters: List<ChapterSegment>, var chapters: List<ChapterSegment>,
private val videoDuration: Long, private val videoDuration: Long,
private val seekTo: (Long) -> Unit private val seekTo: (Long) -> Unit
) : RecyclerView.Adapter<ChaptersViewHolder>() { ) : RecyclerView.Adapter<ChaptersViewHolder>() {

View File

@ -191,7 +191,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
binding.chapters.setOnClickListener { binding.chapters.setOnClickListener {
val playerService = playerService ?: return@setOnClickListener val playerService = playerService ?: return@setOnClickListener
viewModel.chapters = playerService.streams?.chapters.orEmpty().toMutableList() viewModel.chaptersLiveData.value = playerService.streams?.chapters.orEmpty()
ChaptersBottomSheet() ChaptersBottomSheet()
.show(childFragmentManager) .show(childFragmentManager)

View File

@ -15,6 +15,7 @@ import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import android.text.format.DateUtils import android.text.format.DateUtils
import android.text.util.Linkify import android.text.util.Linkify
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -44,11 +45,9 @@ import androidx.media3.common.MediaItem.SubtitleConfiguration
import androidx.media3.common.MimeTypes import androidx.media3.common.MimeTypes
import androidx.media3.common.PlaybackException import androidx.media3.common.PlaybackException
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.cronet.CronetDataSource import androidx.media3.datasource.cronet.CronetDataSource
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.hls.HlsMediaSource
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
@ -90,7 +89,6 @@ import com.github.libretube.helpers.PlayerHelper.SPONSOR_HIGHLIGHT_CATEGORY
import com.github.libretube.helpers.PlayerHelper.checkForSegments import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PlayerHelper.getVideoStats import com.github.libretube.helpers.PlayerHelper.getVideoStats
import com.github.libretube.helpers.PlayerHelper.isInSegment import com.github.libretube.helpers.PlayerHelper.isInSegment
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.PlayerNotificationData import com.github.libretube.obj.PlayerNotificationData
@ -922,7 +920,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
playerBinding.exoTitle.text = streams.title playerBinding.exoTitle.text = streams.title
// init the chapters recyclerview // init the chapters recyclerview
viewModel.chapters = streams.chapters.toMutableList() viewModel.chaptersLiveData.value = streams.chapters
// Listener for play and pause icon change // Listener for play and pause icon change
exoPlayer.addListener(object : Player.Listener { exoPlayer.addListener(object : Player.Listener {
@ -1212,16 +1210,18 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
private suspend fun initializeHighlight(highlight: Segment) { private suspend fun initializeHighlight(highlight: Segment) {
val frameReceiver = OnlineTimeFrameReceiver(requireContext(), streams.previewFrames) val frameReceiver = OnlineTimeFrameReceiver(requireContext(), streams.previewFrames)
val highlightStart = highlight.segmentStartAndEnd.first.toLong()
val frame = withContext(Dispatchers.IO) { val frame = withContext(Dispatchers.IO) {
frameReceiver.getFrameAtTime(highlight.segmentStartAndEnd.first.toLong() * 1000) frameReceiver.getFrameAtTime(highlightStart * 1000)
} }
val highlightChapter = ChapterSegment( val highlightChapter = ChapterSegment(
title = getString(R.string.chapters_videoHighlight), title = getString(R.string.chapters_videoHighlight),
start = highlight.segmentStartAndEnd.first.toLong(), start = highlightStart,
highlightDrawable = frame?.toDrawable(requireContext().resources) highlightDrawable = frame?.toDrawable(requireContext().resources)
) )
viewModel.chapters.add(highlightChapter) viewModel.chaptersLiveData.postValue(
viewModel.chapters.sortBy { it.start } viewModel.chapters.plus(highlightChapter).sortedBy { it.start }
)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
setCurrentChapterName() setCurrentChapterName()

View File

@ -17,5 +17,7 @@ class PlayerViewModel : ViewModel() {
var maxSheetHeightPx = 0 var maxSheetHeightPx = 0
var chapters: MutableList<ChapterSegment> = mutableListOf() val chaptersLiveData = MutableLiveData<List<ChapterSegment>>()
val chapters get() = chaptersLiveData.value.orEmpty()
} }

View File

@ -1,5 +1,6 @@
package com.github.libretube.ui.sheets package com.github.libretube.ui.sheets
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -10,7 +11,6 @@ import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.obj.ChapterSegment
import com.github.libretube.databinding.BottomSheetBinding import com.github.libretube.databinding.BottomSheetBinding
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.ui.adapters.ChaptersAdapter import com.github.libretube.ui.adapters.ChaptersAdapter
@ -19,14 +19,9 @@ import com.github.libretube.ui.models.PlayerViewModel
class ChaptersBottomSheet : UndimmedBottomSheet() { class ChaptersBottomSheet : UndimmedBottomSheet() {
private var _binding: BottomSheetBinding? = null private var _binding: BottomSheetBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val handler = Handler(Looper.getMainLooper())
private val playerViewModel: PlayerViewModel by activityViewModels() private val playerViewModel: PlayerViewModel by activityViewModels()
private lateinit var chapters: List<ChapterSegment>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
chapters = playerViewModel.chapters
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -37,11 +32,28 @@ class ChaptersBottomSheet : UndimmedBottomSheet() {
return binding.root return binding.root
} }
private val updatePosition = object : Runnable {
override fun run() {
val binding = _binding ?: return
handler.postDelayed(this, 200)
val player = playerViewModel.player ?: return
val currentIndex = PlayerHelper.getCurrentChapterIndex(
player.currentPosition,
playerViewModel.chapters
) ?: return
val adapter = binding.optionsRecycler.adapter as ChaptersAdapter
adapter.updateSelectedPosition(currentIndex)
}
}
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.optionsRecycler.layoutManager = LinearLayoutManager(context) binding.optionsRecycler.layoutManager = LinearLayoutManager(context)
val adapter = ChaptersAdapter(chapters, playerViewModel.player?.duration ?: 0) { val adapter = ChaptersAdapter(playerViewModel.chapters, playerViewModel.player?.duration ?: 0) {
playerViewModel.player?.seekTo(it) playerViewModel.player?.seekTo(it)
} }
binding.optionsRecycler.adapter = adapter binding.optionsRecycler.adapter = adapter
@ -49,17 +61,11 @@ class ChaptersBottomSheet : UndimmedBottomSheet() {
binding.bottomSheetTitle.text = context?.getString(R.string.chapters) binding.bottomSheetTitle.text = context?.getString(R.string.chapters)
binding.bottomSheetTitleLayout.isVisible = true binding.bottomSheetTitleLayout.isVisible = true
val handler = Handler(Looper.getMainLooper()) playerViewModel.chaptersLiveData.observe(viewLifecycleOwner) {
adapter.chapters = it
val updatePosition = object : Runnable { adapter.notifyDataSetChanged()
override fun run() {
if (_binding == null) return
handler.postDelayed(this, 200)
val player = playerViewModel.player ?: return
val currentIndex = PlayerHelper.getCurrentChapterIndex(player.currentPosition, chapters) ?: return
adapter.updateSelectedPosition(currentIndex)
}
} }
updatePosition.run() updatePosition.run()
} }