Added support for chapter skip in casting.

This commit is contained in:
Koen 2023-12-18 16:28:35 +01:00
parent d63627bd61
commit ef72561768
3 changed files with 66 additions and 9 deletions

View file

@ -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);

View file

@ -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<IChapter>? = null;
private var _currentChapter: IChapter? = null;
val onChapterChanged = Event2<IChapter?, Boolean>();
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<IChapter>?) {
_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);

View file

@ -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;