diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt index 2805cbbd..5ef7a763 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt @@ -602,6 +602,19 @@ sealed interface AppPreference { summaryOn = R.string.enabled, summaryOff = R.string.nav_drawer_switch_on_focus_summary_off, ) + + val ShowNextUpTiming = + AppChoicePreference( + title = R.string.show_next_up_when, + defaultValue = ShowNextUpWhen.END_OF_PLAYBACK, + getter = { it.playbackPreferences.showNextUpWhen }, + setter = { prefs, value -> + prefs.updatePlaybackPreferences { showNextUpWhen = value } + }, + displayValues = R.array.show_next_up_when_options, + indexToValue = { ShowNextUpWhen.forNumber(it) }, + valueToIndex = { it.number }, + ) } } @@ -613,7 +626,6 @@ val basicPreferences = listOf( AppPreference.HomePageItems, AppPreference.RewatchNextUp, - AppPreference.CombineContinueNext, AppPreference.PlayThemeMusic, AppPreference.RememberSelectedTab, AppPreference.ThemeColors, @@ -625,11 +637,17 @@ val basicPreferences = listOf( AppPreference.SkipForward, AppPreference.SkipBack, - AppPreference.ControllerTimeout, + AppPreference.SkipBackOnResume, + ), + ), + PreferenceGroup( + title = R.string.next_up, + preferences = + listOf( + AppPreference.ShowNextUpTiming, AppPreference.AutoPlayNextUp, AppPreference.AutoPlayNextDelay, AppPreference.PassOutProtection, - AppPreference.SkipBackOnResume, ), ), PreferenceGroup( @@ -664,7 +682,9 @@ val advancedPreferences = title = R.string.ui_interface, preferences = listOf( + AppPreference.CombineContinueNext, AppPreference.NavDrawerSwitchOnFocus, + AppPreference.ControllerTimeout, ), ), PreferenceGroup( diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt index 4fce1470..bb90e4a8 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt @@ -43,6 +43,7 @@ class AppPreferencesSerializer skipRecaps = AppPreference.SkipRecaps.defaultValue passOutProtectionMs = AppPreference.PassOutProtection.defaultValue.hours.inWholeMilliseconds + showNextUpWhen = AppPreference.ShowNextUpTiming.defaultValue overrides = PlaybackOverrides diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt index 50a1d81b..ec9dc464 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt @@ -69,9 +69,11 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.tryRequestFocus +import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.seasonEpisode import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.DeviceProfile import org.jellyfin.sdk.model.extensions.ticks import java.util.UUID @@ -162,7 +164,6 @@ fun PlaybackPage( val scaledModifier = Modifier.resizeWithContentScale(contentScale, presentationState.videoSizeDp) val focusRequester = remember { FocusRequester() } - val controllerFocusRequester = remember { FocusRequester() } val playPauseState = rememberPlayPauseButtonState(player) val seekBarState = rememberSeekBarState(player, scope) @@ -215,9 +216,9 @@ fun PlaybackPage( Box( modifier - .background(Color.Black), + .background(if (nextUp == null) Color.Black else MaterialTheme.colorScheme.background), ) { - val playerSize by animateFloatAsState(if (nextUp == null) 1f else .66f) + val playerSize by animateFloatAsState(if (nextUp == null) 1f else .6f) Box( modifier = Modifier @@ -418,7 +419,13 @@ fun PlaybackPage( // Next up episode BackHandler(nextUp != null) { - viewModel.navigationManager.goBack() + if (player.isPlaying) { + scope.launch(ExceptionHandler()) { + viewModel.cancelUpNextEpisode() + } + } else { + viewModel.navigationManager.goBack() + } } AnimatedVisibility( nextUp != null, @@ -470,7 +477,7 @@ fun PlaybackPage( Modifier .padding(8.dp) // .height(128.dp) - .fillMaxHeight(.5f) + .fillMaxHeight(1 - playerSize) .fillMaxWidth(.66f) .align(Alignment.BottomCenter) .background( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt index 1510830d..d1b523a6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt @@ -33,6 +33,7 @@ import com.github.damontecres.wholphin.data.model.chooseStream import com.github.damontecres.wholphin.preferences.AppPreference import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.MediaExtensionStatus +import com.github.damontecres.wholphin.preferences.ShowNextUpWhen import com.github.damontecres.wholphin.preferences.SkipSegmentBehavior import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.launchIO @@ -97,12 +98,6 @@ import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -enum class TranscodeType { - DIRECT_PLAY, - DIRECT_STREAM, - TRANSCODE, -} - data class StreamDecision( val itemId: UUID, val type: PlayMethod, @@ -159,6 +154,7 @@ class PlaybackViewModel val trickplay = MutableLiveData(null) val chapters = MutableLiveData>(listOf()) val currentSegment = EqualityMutableLiveData(null) + private val autoSkippedSegments = mutableSetOf() private lateinit var preferences: UserPreferences private lateinit var deviceProfile: DeviceProfile @@ -244,6 +240,7 @@ class PlaybackViewModel ): Boolean = withContext(Dispatchers.IO) { Timber.i("Playing ${base.id}") + autoSkippedSegments.clear() if (base.type !in supportItemKinds) { showToast( context, @@ -358,11 +355,9 @@ class PlaybackViewModel audioIndex = audioIndex ?: TrackIndex.UNSPECIFIED, subtitleIndex = subtitleIndex ?: TrackIndex.UNSPECIFIED, ) - withContext(Dispatchers.Main) { - this@PlaybackViewModel.currentItemPlayback.value = itemPlaybackToUse - } withContext(Dispatchers.Main) { + this@PlaybackViewModel.currentItemPlayback.value = itemPlaybackToUse this@PlaybackViewModel.audioStreams.value = audioStreams this@PlaybackViewModel.subtitleStreams.value = subtitleStreams @@ -664,39 +659,53 @@ class PlaybackViewModel .firstOrNull { it.type != MediaSegmentType.UNKNOWN && currentTicks >= it.startTicks && currentTicks < it.endTicks } - if (currentSegment != null) { + if (currentSegment != null && autoSkippedSegments.add(currentSegment.id)) { Timber.d( "Found media segment for %s: %s, %s", currentSegment.itemId, currentSegment.id, currentSegment.type, ) - val behavior = - when (currentSegment.type) { - MediaSegmentType.COMMERCIAL -> prefs.skipCommercials - MediaSegmentType.PREVIEW -> prefs.skipPreviews - MediaSegmentType.RECAP -> prefs.skipRecaps - MediaSegmentType.OUTRO -> prefs.skipOutros - MediaSegmentType.INTRO -> prefs.skipIntros - MediaSegmentType.UNKNOWN -> SkipSegmentBehavior.IGNORE + val playlist = this@PlaybackViewModel.playlist.value + + if (currentSegment.type == MediaSegmentType.OUTRO && + prefs.showNextUpWhen == ShowNextUpWhen.DURING_CREDITS && + playlist != null && playlist.hasNext() + ) { + val nextItem = playlist.peek() + Timber.v("Setting next up during outro to ${nextItem?.id}") + withContext(Dispatchers.Main) { + nextUp.value = nextItem } - withContext(Dispatchers.Main) { - when (behavior) { - SkipSegmentBehavior.AUTO_SKIP -> { - this@PlaybackViewModel.currentSegment.value = null - player.seekTo(currentSegment.endTicks.ticks.inWholeMilliseconds + 1) + } else { + val behavior = + when (currentSegment.type) { + MediaSegmentType.COMMERCIAL -> prefs.skipCommercials + MediaSegmentType.PREVIEW -> prefs.skipPreviews + MediaSegmentType.RECAP -> prefs.skipRecaps + MediaSegmentType.OUTRO -> prefs.skipOutros + MediaSegmentType.INTRO -> prefs.skipIntros + MediaSegmentType.UNKNOWN -> SkipSegmentBehavior.IGNORE } + withContext(Dispatchers.Main) { + when (behavior) { + SkipSegmentBehavior.AUTO_SKIP -> { + this@PlaybackViewModel.currentSegment.value = null + player.seekTo(currentSegment.endTicks.ticks.inWholeMilliseconds + 1) + } - SkipSegmentBehavior.ASK_TO_SKIP -> { - this@PlaybackViewModel.currentSegment.value = currentSegment - } + SkipSegmentBehavior.ASK_TO_SKIP -> { + this@PlaybackViewModel.currentSegment.value = + currentSegment + } - else -> { - this@PlaybackViewModel.currentSegment.value = null + else -> { + this@PlaybackViewModel.currentSegment.value = null + } } } } - } else { + } else if (currentSegment == null) { withContext(Dispatchers.Main) { this@PlaybackViewModel.currentSegment.value = null } diff --git a/app/src/main/proto/WholphinDataStore.proto b/app/src/main/proto/WholphinDataStore.proto index f27f146a..f6023e49 100644 --- a/app/src/main/proto/WholphinDataStore.proto +++ b/app/src/main/proto/WholphinDataStore.proto @@ -3,6 +3,11 @@ syntax = "proto3"; option java_package = "com.github.damontecres.wholphin.preferences"; option java_multiple_files = true; +enum ShowNextUpWhen{ + END_OF_PLAYBACK = 0; + DURING_CREDITS = 1; +} + enum SkipSegmentBehavior{ IGNORE = 0; AUTO_SKIP = 1; @@ -52,6 +57,7 @@ message PlaybackPreferences { PlaybackOverrides overrides = 15; int64 pass_out_protection_ms = 16; PrefContentScale global_content_scale = 17; + ShowNextUpWhen show_next_up_when = 18; } message HomePagePreferences{ diff --git a/app/src/main/res/values/preferences.xml b/app/src/main/res/values/preferences.xml index bc8574f4..6e38778a 100644 --- a/app/src/main/res/values/preferences.xml +++ b/app/src/main/res/values/preferences.xml @@ -37,4 +37,9 @@ Never use FFmpeg decoders + + At the end of playback + During end credits/outro + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3e51cc61..f2ca9395 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,5 +100,7 @@ Use FFmpeg decoder module Switch nav drawer pages on focus Click to switch pages + Show next up + Next Up