diff --git a/.github/ISSUE_TEMPLATE/00-bug.yml b/.github/ISSUE_TEMPLATE/00-bug.yml index 1aab31da..27e43e4b 100644 --- a/.github/ISSUE_TEMPLATE/00-bug.yml +++ b/.github/ISSUE_TEMPLATE/00-bug.yml @@ -1,6 +1,6 @@ name: "Bug Report" description: Report a bug -title: "" +title: "" labels: [ "bug" ] @@ -23,6 +23,16 @@ body: 3. See error validations: required: false + - type: dropdown + id: experimental + attributes: + label: Experimental settings + description: Do you have experiment settings turn on? + options: + - "Yes" + - "No" + validations: + required: true - type: input id: app-version attributes: diff --git a/.github/ISSUE_TEMPLATE/01-playback.yml b/.github/ISSUE_TEMPLATE/01-playback.yml index cbb287b5..00ee97f3 100644 --- a/.github/ISSUE_TEMPLATE/01-playback.yml +++ b/.github/ISSUE_TEMPLATE/01-playback.yml @@ -1,6 +1,6 @@ name: "Playback Problem" description: Report an issue with playback -title: "<title>" +title: "" labels: [ "bug", "playback" ] @@ -12,12 +12,14 @@ body: description: Please enter a description of the playback issue validations: required: true - - type: textarea - id: mediainfo + - type: dropdown + id: experimental attributes: - label: "Media info" - description: Please enter details about what media you are trying to play (video or audio codecs, container, etc) - placeholder: Use mediainfo or "Send media info to server" in the app to gather this + label: Experimental settings + description: Do you have experiment settings turn on? + options: + - "Yes" + - "No" validations: required: true - type: dropdown @@ -32,6 +34,14 @@ body: - External App (please specify) validations: required: true + - type: textarea + id: mediainfo + attributes: + label: "Media info" + description: Please enter details about what media you are trying to play (video or audio codecs, container, etc) + placeholder: Use mediainfo or "Send media info to server" in the app to gather this + validations: + required: true - type: input id: app-version attributes: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index bdc7dfa5..0d818e21 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -97,3 +97,9 @@ Some of the models, such as `GetItemsFilter` or `ViewOptions`, are stored as JSO These settings are similar to Room ones and are usually stored per user. These can be accessed via `KeyValueService`. The values are `@Serializable` objects saved as JSON. This should be used sparingly. + +#### Experimental + +Some settings are considered "experimental". These may cause instabilities or may need further testing. They may be changed or removed any time. + +These are stored in the `ExperimentalPreferences` object in `AppPreferences`. Accessing them should always be gated with `ExperimentalPreferences.enabled`. There are a few helper functions for this purpose. 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 8149f8b6..f573c4a7 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 @@ -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), + ), + ), ), ) } 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 ddac936a..1e84663d 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 @@ -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() diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/ExperimentalPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/ExperimentalPreference.kt new file mode 100644 index 00000000..18e38f79 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/ExperimentalPreference.kt @@ -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) diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt index 01b82777..24736015 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt @@ -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) 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 b297c0c6..37d3e5ad 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 @@ -253,7 +253,7 @@ class PlaybackViewModel val playerCreation = playerFactory.createVideoPlayer( playerBackend, - preferences.appPreferences.playbackPreferences, + preferences.appPreferences, ) this.player = playerCreation.player currentPlayer.update { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferenceUtils.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferenceUtils.kt index 06399fe9..0a843dcb 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferenceUtils.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferenceUtils.kt @@ -37,6 +37,7 @@ enum class PreferenceScreenOption { MPV, SCREENSAVER, SKIP_SEGMENTS, + EXPERIMENTAL, ; companion object { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt index 982e2537..b08e9508 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt @@ -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, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt index 8db01751..c81ffcb7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt @@ -111,7 +111,7 @@ class SlideshowViewModel val playerCreation = playerFactory.createVideoPlayer( backend = PlayerBackend.EXO_PLAYER, - appPreferences.playbackPreferences, + appPreferences, ) player = playerCreation.player player.addListener(this@SlideshowViewModel) diff --git a/app/src/main/proto/WholphinDataStore.proto b/app/src/main/proto/WholphinDataStore.proto index d2443c60..2eb47cef 100644 --- a/app/src/main/proto/WholphinDataStore.proto +++ b/app/src/main/proto/WholphinDataStore.proto @@ -212,6 +212,11 @@ message MusicPreferences { bool show_backdrop = 4; } +message ExperimentalPreferences { + bool enabled = 1; + bool video_tunneling_enabled = 2; +} + message AppPreferences { // The currently signed in server and user IDs, mostly for restoring a session string current_server_id = 1; @@ -229,4 +234,5 @@ message AppPreferences { bool sign_in_automatically = 11; PhotoPreferences photo_preferences = 12; MusicPreferences music_preferences = 13; + ExperimentalPreferences experimental_preferences = 14; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd999158..312f78b2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -829,4 +829,8 @@ <string name="popularity">Popularity</string> <string name="production_status">Production status</string> <string name="different_for_hdr">Use different style for HDR</string> + <string name="experimental_settings">Experimental settings</string> + <string name="confirm_enable_experimental_title">Enable experimental settings?</string> + <string name="confirm_enable_experimental_body">Experimental settings may be unstable!\n\n These may removed or changed in any release.</string> + <string name="video_tunneling">Video tunneling</string> </resources>