mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Suport passout protection (#41)
Adds a setting to configure pass out protection which, when enabled, will stop auto playing next up episodes if the user hasn't interacted with the app within the configurable 1-3 hours.
This commit is contained in:
parent
c3cc284238
commit
8904360fb5
7 changed files with 56 additions and 7 deletions
|
|
@ -8,6 +8,7 @@ import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceGroup
|
import com.github.damontecres.wholphin.ui.preferences.PreferenceGroup
|
||||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption
|
import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption
|
||||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceValidation
|
import com.github.damontecres.wholphin.ui.preferences.PreferenceValidation
|
||||||
|
import kotlin.time.Duration.Companion.hours
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.minutes
|
import kotlin.time.Duration.Companion.minutes
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
@ -267,6 +268,28 @@ sealed interface AppPreference<T> {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val PassOutProtection =
|
||||||
|
AppSliderPreference(
|
||||||
|
title = R.string.pass_out_protection,
|
||||||
|
defaultValue = 2,
|
||||||
|
min = 0,
|
||||||
|
max = 3,
|
||||||
|
interval = 1,
|
||||||
|
getter = { it.playbackPreferences.passOutProtectionMs.milliseconds.inWholeHours },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updatePlaybackPreferences {
|
||||||
|
passOutProtectionMs = value.hours.inWholeMilliseconds
|
||||||
|
}
|
||||||
|
},
|
||||||
|
summarizer = { value ->
|
||||||
|
if (value == 0L) {
|
||||||
|
"Disabled"
|
||||||
|
} else {
|
||||||
|
"$value hours"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
private const val MEGA_BIT = 1024 * 1024L
|
private const val MEGA_BIT = 1024 * 1024L
|
||||||
const val DEFAULT_BITRATE = 20 * MEGA_BIT
|
const val DEFAULT_BITRATE = 20 * MEGA_BIT
|
||||||
private val bitrateValues =
|
private val bitrateValues =
|
||||||
|
|
@ -529,6 +552,7 @@ val basicPreferences =
|
||||||
AppPreference.ControllerTimeout,
|
AppPreference.ControllerTimeout,
|
||||||
AppPreference.AutoPlayNextUp,
|
AppPreference.AutoPlayNextUp,
|
||||||
AppPreference.AutoPlayNextDelay,
|
AppPreference.AutoPlayNextDelay,
|
||||||
|
AppPreference.PassOutProtection,
|
||||||
AppPreference.SkipBackOnResume,
|
AppPreference.SkipBackOnResume,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.hours
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class AppPreferencesSerializer
|
class AppPreferencesSerializer
|
||||||
|
|
@ -42,6 +43,8 @@ class AppPreferencesSerializer
|
||||||
skipCommercials = AppPreference.SkipCommercials.defaultValue
|
skipCommercials = AppPreference.SkipCommercials.defaultValue
|
||||||
skipPreviews = AppPreference.SkipPreviews.defaultValue
|
skipPreviews = AppPreference.SkipPreviews.defaultValue
|
||||||
skipRecaps = AppPreference.SkipRecaps.defaultValue
|
skipRecaps = AppPreference.SkipRecaps.defaultValue
|
||||||
|
passOutProtectionMs =
|
||||||
|
AppPreference.PassOutProtection.defaultValue.hours.inWholeMilliseconds
|
||||||
|
|
||||||
overrides =
|
overrides =
|
||||||
PlaybackOverrides
|
PlaybackOverrides
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,11 @@ class PlaybackKeyHandler(
|
||||||
private val controllerViewState: ControllerViewState,
|
private val controllerViewState: ControllerViewState,
|
||||||
private val updateSkipIndicator: (Long) -> Unit,
|
private val updateSkipIndicator: (Long) -> Unit,
|
||||||
private val skipBackOnResume: Duration?,
|
private val skipBackOnResume: Duration?,
|
||||||
|
private val onInteraction: () -> Unit,
|
||||||
) {
|
) {
|
||||||
fun onKeyEvent(it: KeyEvent): Boolean {
|
fun onKeyEvent(it: KeyEvent): Boolean {
|
||||||
|
if (it.type == KeyEventType.KeyUp) onInteraction.invoke()
|
||||||
|
|
||||||
var result = true
|
var result = true
|
||||||
if (!controlsEnabled) {
|
if (!controlsEnabled) {
|
||||||
result = false
|
result = false
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@ fun PlaybackPage(
|
||||||
controllerViewState = controllerViewState,
|
controllerViewState = controllerViewState,
|
||||||
updateSkipIndicator = updateSkipIndicator,
|
updateSkipIndicator = updateSkipIndicator,
|
||||||
skipBackOnResume = preferences.appPreferences.playbackPreferences.skipBackOnResume,
|
skipBackOnResume = preferences.appPreferences.playbackPreferences.skipBackOnResume,
|
||||||
|
onInteraction = viewModel::reportInteraction,
|
||||||
)
|
)
|
||||||
|
|
||||||
val showSegment =
|
val showSegment =
|
||||||
|
|
@ -413,12 +414,7 @@ fun PlaybackPage(
|
||||||
.align(Alignment.BottomCenter),
|
.align(Alignment.BottomCenter),
|
||||||
) {
|
) {
|
||||||
nextUp?.let {
|
nextUp?.let {
|
||||||
var autoPlayEnabled by
|
var autoPlayEnabled by remember { mutableStateOf(viewModel.shouldAutoPlayNextUp()) }
|
||||||
remember {
|
|
||||||
mutableStateOf(
|
|
||||||
preferences.appPreferences.playbackPreferences.autoPlayNext,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
var timeLeft by remember {
|
var timeLeft by remember {
|
||||||
mutableLongStateOf(
|
mutableLongStateOf(
|
||||||
preferences.appPreferences.playbackPreferences.autoPlayNextDelaySeconds,
|
preferences.appPreferences.playbackPreferences.autoPlayNextDelaySeconds,
|
||||||
|
|
@ -452,7 +448,10 @@ fun PlaybackPage(
|
||||||
description = it.data.overview,
|
description = it.data.overview,
|
||||||
imageUrl = it.imageUrl,
|
imageUrl = it.imageUrl,
|
||||||
aspectRatio = it.data.primaryImageAspectRatio?.toFloat() ?: (16f / 9),
|
aspectRatio = it.data.primaryImageAspectRatio?.toFloat() ?: (16f / 9),
|
||||||
onClick = { viewModel.playUpNextUp() },
|
onClick = {
|
||||||
|
viewModel.reportInteraction()
|
||||||
|
viewModel.playUpNextUp()
|
||||||
|
},
|
||||||
timeLeft = if (autoPlayEnabled) timeLeft.seconds else null,
|
timeLeft = if (autoPlayEnabled) timeLeft.seconds else null,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ import org.jellyfin.sdk.model.extensions.inWholeTicks
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.Date
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
@ -649,6 +650,23 @@ class PlaybackViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var lastInteractionDate: Date = Date()
|
||||||
|
|
||||||
|
fun reportInteraction() {
|
||||||
|
Timber.v("reportInteraction")
|
||||||
|
lastInteractionDate = Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldAutoPlayNextUp(): Boolean =
|
||||||
|
preferences.appPreferences.playbackPreferences.let {
|
||||||
|
it.autoPlayNext &&
|
||||||
|
if (it.passOutProtectionMs > 0) {
|
||||||
|
(Date().time - lastInteractionDate.time) < it.passOutProtectionMs
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun playUpNextUp() {
|
fun playUpNextUp() {
|
||||||
playlist.value?.let {
|
playlist.value?.let {
|
||||||
if (it.hasNext()) {
|
if (it.hasNext()) {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ message PlaybackPreferences {
|
||||||
SkipSegmentBehavior skip_previews = 14;
|
SkipSegmentBehavior skip_previews = 14;
|
||||||
|
|
||||||
PlaybackOverrides overrides = 15;
|
PlaybackOverrides overrides = 15;
|
||||||
|
int64 pass_out_protection_ms = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message HomePagePreferences{
|
message HomePagePreferences{
|
||||||
|
|
|
||||||
|
|
@ -83,5 +83,6 @@
|
||||||
<string name="direct_play_ass">Direct play ASS subtitles</string>
|
<string name="direct_play_ass">Direct play ASS subtitles</string>
|
||||||
<string name="direct_play_pgs">Direct play PGS subtitles</string>
|
<string name="direct_play_pgs">Direct play PGS subtitles</string>
|
||||||
<string name="trailers">Trailers</string>
|
<string name="trailers">Trailers</string>
|
||||||
|
<string name="pass_out_protection">Passout Protection</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue