Add support for jumping to chapters and bookmarks in podcast mode

This commit is contained in:
Robert Stone 2024-08-07 21:56:07 -07:00 committed by Duncan McNamara
parent 31077e2b89
commit ef65d49c7e

View File

@ -50,10 +50,13 @@ import org.videolan.tools.retrieveParent
import org.videolan.vlc.gui.helpers.MediaComparators
import org.videolan.vlc.media.MediaSessionBrowser
import org.videolan.vlc.util.PlaybackAction
import org.videolan.vlc.util.TextUtils
import org.videolan.vlc.util.VoiceSearchParams
import org.videolan.vlc.util.awaitMedialibraryStarted
import org.videolan.vlc.util.mergeSorted
import java.security.SecureRandom
import kotlin.math.abs
import kotlin.math.absoluteValue
import kotlin.math.min
private const val TAG = "VLC/MediaSessionCallback"
@ -116,8 +119,8 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
if (!prevActionSeek) {
val enabledActions = playbackService.enabledActions
when (keyEvent.keyCode) {
KeyEvent.KEYCODE_MEDIA_NEXT -> if (enabledActions.contains(PlaybackAction.ACTION_SKIP_TO_NEXT)) onSkipToNext()
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> if (enabledActions.contains(PlaybackAction.ACTION_SKIP_TO_PREVIOUS)) onSkipToPrevious()
KeyEvent.KEYCODE_MEDIA_NEXT -> onSkipToNext()
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> onSkipToPrevious()
}
}
prevActionSeek = false
@ -128,6 +131,52 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
return super.onMediaButtonEvent(mediaButtonEvent)
}
private fun jumpToTimelineEntry(previous: Boolean) {
val ctx = playbackService.applicationContext
playbackService.lifecycleScope.launch {
val currentTime = playbackService.getTime()
val entryList = getChapterList().toMutableList().apply {
mergeSorted(getBookmarkList()) { it.time }
}
if (entryList.isEmpty()) {
playbackService.displaySubtitleMessage(ctx.getString(R.string.no_bookmark))
return@launch
}
val index = entryList.binarySearchBy(currentTime) { it.time }
var eIndex = when {
index >= 0 -> index + if (previous) -1 else 1
else -> -index - if (previous) 2 else 1
}.coerceIn(entryList.indices)
// Point to the previous element if the time difference is less than 5 seconds
if (previous && (currentTime - entryList[eIndex].time) < 5_000L) {
eIndex = (eIndex - 1).coerceIn(entryList.indices)
}
// Ignore button press if within 2 seconds of the first or last entry
if ((eIndex == 0 || eIndex == entryList.size - 1) && (currentTime - entryList[eIndex].time).absoluteValue <= 2_000L)
return@launch
// Seek to the correct time
if ((!previous && entryList[eIndex].time >= currentTime) || (previous && entryList[eIndex].time <= currentTime)) {
seek(entryList[eIndex].time)
playbackService.displaySubtitleMessage("${ctx.getString(R.string.jump_to)} ${entryList[eIndex].name}")
}
}
}
private fun getChapterList(): List<TimelineEntry> {
val ctx = playbackService.applicationContext
return playbackService.getChapters(-1)?.mapIndexed { index, item ->
TimelineEntry(0, item.timeOffset, TextUtils.formatChapterTitle(ctx, index + 1, item.name))
} ?: emptyList()
}
private fun getBookmarkList(): List<TimelineEntry> {
return playbackService.currentMediaWrapper?.bookmarks?.map { bookmark ->
TimelineEntry(1, bookmark.time, bookmark.title)
} ?: emptyList()
}
data class TimelineEntry(val type: Int, val time: Long, val name: String)
/**
* The following two functions are based on the following KeyEvent captures. They may need to be updated if the behavior changes in the future.
*
@ -378,9 +427,17 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
override fun onStop() = playbackService.stop()
override fun onSkipToNext() = playbackService.next()
override fun onSkipToNext() = when {
playbackService.isPodcastMode -> jumpToTimelineEntry(false)
playbackService.hasNext() -> playbackService.next()
else -> {}
}
override fun onSkipToPrevious() = playbackService.previous(false)
override fun onSkipToPrevious() = when {
playbackService.isPodcastMode -> jumpToTimelineEntry(true)
playbackService.hasPrevious() -> playbackService.previous(false)
else -> {}
}
override fun onSeekTo(pos: Long) = seek(if (pos < 0) playbackService.getTime() + pos else pos)
@ -398,4 +455,4 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
override fun onSetPlaybackSpeed(speed: Float) = playbackService.setRate(speed.coerceIn(0.5f, 2.0f), false)
}
}