From ef725617686ace661b8dcedd4e69bb2916bb0c4f Mon Sep 17 00:00:00 2001 From: Koen Date: Mon, 18 Dec 2023 16:28:35 +0100 Subject: [PATCH] Added support for chapter skip in casting. --- .../mainactivity/main/VideoDetailView.kt | 36 ++++++++++++++----- .../platformplayer/views/casting/CastView.kt | 36 +++++++++++++++++++ .../views/video/FutoVideoPlayer.kt | 3 +- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index 9cb27040..30c827ec 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -50,6 +50,7 @@ import com.futo.platformplayer.api.media.exceptions.ContentNotAvailableYetExcept import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException import com.futo.platformplayer.api.media.models.PlatformAuthorMembershipLink import com.futo.platformplayer.api.media.models.chapters.ChapterType +import com.futo.platformplayer.api.media.models.chapters.IChapter import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment import com.futo.platformplayer.api.media.models.live.ILiveChatWindowDescriptor import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent @@ -459,20 +460,29 @@ class VideoDetailView : ConstraintLayout { _cast.onSettingsClick.subscribe { showVideoSettings() }; _player.onVideoSettings.subscribe { showVideoSettings() }; _player.onToggleFullScreen.subscribe(::handleFullScreen); - _player.onChapterChanged.subscribe { chapter, isScrub -> + + val onChapterChanged = { chapter: IChapter?, isScrub: Boolean -> if(_layoutSkip.visibility == VISIBLE && chapter?.type != ChapterType.SKIPPABLE) _layoutSkip.visibility = GONE; if(!isScrub) { if(chapter?.type == ChapterType.SKIPPABLE) { _layoutSkip.visibility = VISIBLE; - } - else if(chapter?.type == ChapterType.SKIP) { - _player.seekTo((chapter.timeEnd * 1000).toLong()); + } else if(chapter?.type == ChapterType.SKIP) { + val ad = StateCasting.instance.activeDevice + if (ad != null) { + ad.seekVideo(chapter.timeEnd) + } else { + _player.seekTo((chapter.timeEnd * 1000).toLong()); + } + UIDialogs.toast(context, "Skipped chapter [${chapter.name}]", false); } } - } + }; + + _player.onChapterChanged.subscribe(onChapterChanged); + _cast.onChapterChanged.subscribe(onChapterChanged); _cast.onMinimizeClick.subscribe { _player.setFullScreen(false); @@ -667,9 +677,17 @@ class VideoDetailView : ConstraintLayout { }; _layoutSkip.setOnClickListener { - val currentChapter = _player.getCurrentChapter(_player.position); - if(currentChapter?.type == ChapterType.SKIPPABLE) { - _player.seekTo((currentChapter.timeEnd * 1000).toLong()); + val ad = StateCasting.instance.activeDevice; + if (ad != null) { + val currentChapter = _cast.getCurrentChapter((ad.time * 1000).toLong()); + if(currentChapter?.type == ChapterType.SKIPPABLE) { + ad.seekVideo(currentChapter.timeEnd); + } + } else { + val currentChapter = _player.getCurrentChapter(_player.position); + if(currentChapter?.type == ChapterType.SKIPPABLE) { + _player.seekTo((currentChapter.timeEnd * 1000).toLong()); + } } } } @@ -1145,10 +1163,12 @@ class VideoDetailView : ConstraintLayout { //TODO: Implement video.getContentChapters() val chapters = null ?: StatePlatform.instance.getContentChapters(video.url); _player.setChapters(chapters); + _cast.setChapters(chapters); } catch(ex: Throwable) { Logger.e(TAG, "Failed to get chapters", ex); _player.setChapters(null); + _cast.setChapters(null); /*withContext(Dispatchers.Main) { UIDialogs.toast(context, "Failed to get chapters\n" + ex.message); diff --git a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt index 087586d3..b33c62da 100644 --- a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt @@ -18,10 +18,12 @@ import androidx.media3.ui.DefaultTimeBar import androidx.media3.ui.TimeBar import com.bumptech.glide.Glide import com.futo.platformplayer.R +import com.futo.platformplayer.api.media.models.chapters.IChapter import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.casting.AirPlayCastingDevice import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.constructs.Event0 +import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.toHumanTime import com.futo.platformplayer.views.behavior.GestureControlView @@ -51,7 +53,10 @@ class CastView : ConstraintLayout { private var _scope: CoroutineScope = CoroutineScope(Dispatchers.Main); private var _updateTimeJob: Job? = null; private var _inPictureInPicture: Boolean = false; + private var _chapters: List? = null; + private var _currentChapter: IChapter? = null; + val onChapterChanged = Event2(); val onMinimizeClick = Event0(); val onSettingsClick = Event0(); val onPrevious = Event0(); @@ -129,6 +134,36 @@ class CastView : ConstraintLayout { _buttonNext.setOnClickListener { onNext.emit() }; } + private fun updateCurrentChapter(chaptPos: Long, isScrub: Boolean = false): Boolean { + val currentChapter = getCurrentChapter(chaptPos); + if(_currentChapter != currentChapter) { + _currentChapter = currentChapter; + /*runBlocking(Dispatchers.Main) { + if (currentChapter != null) { + //TODO: Add chapter controls + //_control_chapter.text = " • " + currentChapter.name; + //_control_chapter_fullscreen.text = " • " + currentChapter.name; + } else { + //TODO: Add chapter controls + //_control_chapter.text = ""; + //_control_chapter_fullscreen.text = ""; + } + }*/ + + onChapterChanged.emit(currentChapter, isScrub); + return true; + } + return false; + } + + fun setChapters(chapters: List?) { + _chapters = chapters; + } + + fun getCurrentChapter(pos: Long): IChapter? { + return _chapters?.let { chaps -> chaps.find { pos.toDouble() / 1000 > it.timeStart && pos.toDouble() / 1000 < it.timeEnd } }; + } + private fun updateNextPrevious() { val vidPrev = StatePlayer.instance.getPrevQueueItem(true); val vidNext = StatePlayer.instance.getNextQueueItem(true); @@ -225,6 +260,7 @@ class CastView : ConstraintLayout { @OptIn(UnstableApi::class) fun setTime(ms: Long) { + updateCurrentChapter(ms); _textPosition.text = ms.toHumanTime(true); _timeBar.setPosition(ms / 1000); StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), ms); diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt index 5b782037..154db811 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt @@ -458,7 +458,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase { val currentChapter = getCurrentChapter(chaptPos); if(_currentChapter != currentChapter) { _currentChapter = currentChapter; - runBlocking(Dispatchers.Main) { + + StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { if (currentChapter != null) { _control_chapter.text = " • " + currentChapter.name; _control_chapter_fullscreen.text = " • " + currentChapter.name;