mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-06 02:40:58 +01:00
Add experimental settings & video tunneling (#1624)
## Description This PR introduces the concept of "experimental" settings or features. These may cause instabilities or may need further testing. They may be changed or removed any time. Also, the developers won't provide lots of support for them. The goal is expose some specialized settings that might only apply to a small subset of users or test out tweaks to playback. The first experimental feature is a toggle to enable [video tunneling](https://developer.android.com/media/media3/exoplayer/track-selection#tunneling). ### Related issues Related to #1283 ### Testing Emulator ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
21e720c420
commit
083d4e4fa8
13 changed files with 183 additions and 14 deletions
|
|
@ -1266,8 +1266,16 @@ val advancedPreferences =
|
|||
AppPreference.DebugLogging,
|
||||
AppPreference.ImageDiskCacheSize,
|
||||
AppPreference.ClearImageCache,
|
||||
ExperimentalPreference.Enable,
|
||||
AppPreference.OssLicenseInfo,
|
||||
),
|
||||
conditionalPreferences =
|
||||
listOf(
|
||||
ConditionalPreferences(
|
||||
condition = { it.experimentalPreferences.enabled },
|
||||
preferences = listOf(ExperimentalPreference.ExperimentalSettings),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,6 +174,14 @@ class AppPreferencesSerializer
|
|||
showLyrics = true
|
||||
showAlbumArt = true
|
||||
}.build()
|
||||
|
||||
experimentalPreferences =
|
||||
ExperimentalPreferences
|
||||
.newBuilder()
|
||||
.apply {
|
||||
enabled = false
|
||||
videoTunnelingEnabled = false
|
||||
}.build()
|
||||
}.build()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): AppPreferences {
|
||||
|
|
@ -252,6 +260,11 @@ inline fun AppPreferences.updateMusicPreferences(block: MusicPreferences.Builder
|
|||
musicPreferences = musicPreferences.toBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
inline fun AppPreferences.updateExperimentalPreferences(block: ExperimentalPreferences.Builder.() -> Unit): AppPreferences =
|
||||
update {
|
||||
experimentalPreferences = experimentalPreferences.toBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
fun SubtitlePreferences.Builder.resetSubtitles() {
|
||||
fontSize = SubtitleSettings.FontSize.defaultValue.toInt()
|
||||
fontColor = SubtitleSettings.FontColor.defaultValue.toArgb()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package com.github.damontecres.wholphin.preferences
|
||||
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceGroup
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption
|
||||
|
||||
object ExperimentalPreference {
|
||||
val Enable =
|
||||
AppSwitchPreference<AppPreferences>(
|
||||
title = R.string.experimental_settings,
|
||||
defaultValue = false,
|
||||
getter = { it.experimentalPreferences.enabled },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateExperimentalPreferences { enabled = value }
|
||||
},
|
||||
summaryOn = R.string.enabled,
|
||||
summaryOff = R.string.disabled,
|
||||
)
|
||||
|
||||
val ExperimentalSettings =
|
||||
AppDestinationPreference<AppPreferences>(
|
||||
title = R.string.experimental_settings,
|
||||
destination = Destination.Settings(PreferenceScreenOption.EXPERIMENTAL),
|
||||
)
|
||||
|
||||
val VideoTunneling =
|
||||
AppSwitchPreference<AppPreferences>(
|
||||
title = R.string.video_tunneling,
|
||||
defaultValue = false,
|
||||
getter = { it.experimentalPreferences.videoTunnelingEnabled },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateExperimentalPreferences { videoTunnelingEnabled = value }
|
||||
},
|
||||
summaryOn = R.string.enabled,
|
||||
summaryOff = R.string.disabled,
|
||||
)
|
||||
}
|
||||
|
||||
val experimentalPreferences =
|
||||
buildList {
|
||||
add(
|
||||
PreferenceGroup(
|
||||
title = R.string.experimental_settings,
|
||||
preferences =
|
||||
listOf(
|
||||
ExperimentalPreference.VideoTunneling,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from [ExperimentalPreference] or null if not enabled
|
||||
*/
|
||||
fun <T> ExperimentalPreferences.get(block: ExperimentalPreferences.() -> T): T? = if (enabled) block.invoke(this) else null
|
||||
|
||||
fun ExperimentalPreferences.enabled(block: ExperimentalPreferences.() -> Boolean): Boolean = enabled && block.invoke(this)
|
||||
|
|
@ -26,10 +26,11 @@ import androidx.media3.exoplayer.video.VideoRendererEventListener
|
|||
import androidx.media3.extractor.DefaultExtractorsFactory
|
||||
import androidx.media3.session.MediaSession
|
||||
import com.github.damontecres.wholphin.mpv.MpvPlayer
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.AssPlaybackMode
|
||||
import com.github.damontecres.wholphin.preferences.MediaExtensionStatus
|
||||
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
|
||||
import com.github.damontecres.wholphin.preferences.PlayerBackend
|
||||
import com.github.damontecres.wholphin.preferences.get
|
||||
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
|
||||
import com.github.damontecres.wholphin.util.WholphinDispatchers
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
|
|
@ -61,8 +62,9 @@ class PlayerFactory
|
|||
|
||||
suspend fun createVideoPlayer(
|
||||
backend: PlayerBackend,
|
||||
prefs: PlaybackPreferences,
|
||||
appPreferences: AppPreferences,
|
||||
): PlayerCreation {
|
||||
val prefs = appPreferences.playbackPreferences
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
if (currentPlayer?.isReleased == false) {
|
||||
Timber.w("Player was not released before trying to create a new one!")
|
||||
|
|
@ -105,6 +107,7 @@ class PlayerFactory
|
|||
WholphinRenderersFactory(context, decodeAv1)
|
||||
.setEnableDecoderFallback(true)
|
||||
.setExtensionRendererMode(rendererMode)
|
||||
|
||||
val mediaSourceFactory =
|
||||
if (useLibAss) {
|
||||
val renderType =
|
||||
|
|
@ -129,7 +132,9 @@ class PlayerFactory
|
|||
extractorsFactory,
|
||||
)
|
||||
}
|
||||
val trackSelector = createTrackSelector()
|
||||
val tunneling =
|
||||
appPreferences.experimentalPreferences.get { videoTunnelingEnabled }
|
||||
val trackSelector = createTrackSelector(tunneling)
|
||||
|
||||
ExoPlayer
|
||||
.Builder(context)
|
||||
|
|
@ -200,11 +205,13 @@ class PlayerFactory
|
|||
.setConstantBitrateSeekingEnabled(true)
|
||||
.setConstantBitrateSeekingAlwaysEnabled(true)
|
||||
|
||||
private fun createTrackSelector() =
|
||||
private fun createTrackSelector(tunneling: Boolean? = null) =
|
||||
DefaultTrackSelector(context).apply {
|
||||
setParameters(
|
||||
buildUponParameters()
|
||||
.setAudioOffloadPreferences(
|
||||
.apply {
|
||||
tunneling?.let { setTunnelingEnabled(tunneling) }
|
||||
}.setAudioOffloadPreferences(
|
||||
AudioOffloadPreferences
|
||||
.Builder()
|
||||
.setAudioOffloadMode(AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED)
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ class PlaybackViewModel
|
|||
val playerCreation =
|
||||
playerFactory.createVideoPlayer(
|
||||
playerBackend,
|
||||
preferences.appPreferences.playbackPreferences,
|
||||
preferences.appPreferences,
|
||||
)
|
||||
this.player = playerCreation.player
|
||||
currentPlayer.update {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ enum class PreferenceScreenOption {
|
|||
MPV,
|
||||
SCREENSAVER,
|
||||
SKIP_SEGMENTS,
|
||||
EXPERIMENTAL,
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -48,13 +48,16 @@ import coil3.imageLoader
|
|||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.AppSwitchPreference
|
||||
import com.github.damontecres.wholphin.preferences.ExoPlayerPreferences
|
||||
import com.github.damontecres.wholphin.preferences.ExperimentalPreference
|
||||
import com.github.damontecres.wholphin.preferences.MpvPreferences
|
||||
import com.github.damontecres.wholphin.preferences.PlayerBackend
|
||||
import com.github.damontecres.wholphin.preferences.ScreensaverPreference
|
||||
import com.github.damontecres.wholphin.preferences.SkipSegmentPreferences
|
||||
import com.github.damontecres.wholphin.preferences.advancedPreferences
|
||||
import com.github.damontecres.wholphin.preferences.basicPreferences
|
||||
import com.github.damontecres.wholphin.preferences.experimentalPreferences
|
||||
import com.github.damontecres.wholphin.preferences.screensaverPreferences
|
||||
import com.github.damontecres.wholphin.preferences.updatePlaybackPreferences
|
||||
import com.github.damontecres.wholphin.services.Release
|
||||
|
|
@ -148,6 +151,7 @@ fun PreferencesContent(
|
|||
PreferenceScreenOption.MPV -> MpvPreferences
|
||||
PreferenceScreenOption.SCREENSAVER -> screensaverPreferences
|
||||
PreferenceScreenOption.SKIP_SEGMENTS -> SkipSegmentPreferences
|
||||
PreferenceScreenOption.EXPERIMENTAL -> experimentalPreferences
|
||||
}
|
||||
val screenTitle =
|
||||
when (preferenceScreenOption) {
|
||||
|
|
@ -157,6 +161,7 @@ fun PreferencesContent(
|
|||
PreferenceScreenOption.MPV -> R.string.mpv_options
|
||||
PreferenceScreenOption.SCREENSAVER -> R.string.screensaver_settings
|
||||
PreferenceScreenOption.SKIP_SEGMENTS -> R.string.skip_behavior
|
||||
PreferenceScreenOption.EXPERIMENTAL -> R.string.experimental_settings
|
||||
}
|
||||
|
||||
var visible by remember { mutableStateOf(false) }
|
||||
|
|
@ -569,6 +574,46 @@ fun PreferencesContent(
|
|||
)
|
||||
}
|
||||
|
||||
ExperimentalPreference.Enable -> {
|
||||
var showConfirm by remember { mutableStateOf(false) }
|
||||
pref as AppSwitchPreference<AppPreferences>
|
||||
val value = pref.getter.invoke(preferences)
|
||||
SwitchPreference(
|
||||
title = stringResource(pref.title),
|
||||
value = value,
|
||||
onClick = {
|
||||
if (value) {
|
||||
scope.launch(ExceptionHandler()) {
|
||||
preferences =
|
||||
viewModel.preferenceDataStore.updateData { prefs ->
|
||||
pref.setter.invoke(prefs, false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showConfirm = true
|
||||
}
|
||||
},
|
||||
summaryOn = pref.summaryOn?.let { stringResource(pref.summaryOn) },
|
||||
summaryOff = pref.summaryOff?.let { stringResource(pref.summaryOff) },
|
||||
)
|
||||
if (showConfirm) {
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.confirm_enable_experimental_title),
|
||||
body = stringResource(R.string.confirm_enable_experimental_body),
|
||||
onCancel = { showConfirm = false },
|
||||
onConfirm = {
|
||||
showConfirm = false
|
||||
scope.launch(ExceptionHandler()) {
|
||||
preferences =
|
||||
viewModel.preferenceDataStore.updateData { prefs ->
|
||||
pref.setter.invoke(prefs, true)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val value = pref.getter.invoke(preferences)
|
||||
ComposablePreference(
|
||||
|
|
@ -762,6 +807,7 @@ fun PreferencesPage(
|
|||
PreferenceScreenOption.MPV,
|
||||
PreferenceScreenOption.SCREENSAVER,
|
||||
PreferenceScreenOption.SKIP_SEGMENTS,
|
||||
PreferenceScreenOption.EXPERIMENTAL,
|
||||
-> {
|
||||
PreferencesContent(
|
||||
initialPreferences,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ class SlideshowViewModel
|
|||
val playerCreation =
|
||||
playerFactory.createVideoPlayer(
|
||||
backend = PlayerBackend.EXO_PLAYER,
|
||||
appPreferences.playbackPreferences,
|
||||
appPreferences,
|
||||
)
|
||||
player = playerCreation.player
|
||||
player.addListener(this@SlideshowViewModel)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue