mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Add toggles to override direct playback (#24)
Fixes #10
Adds toggles to disable direct play of AC3 audio, ASS subtitles, and PGS
subtitles. Also a toggle to force down mixing audio to stereo.
This copies the behavior from the official client. I'm not sure if there
is a way to determine if direct playing AC3 is possible because it can
depend on devices other than the Android TV device such as a sound bar.
So it's up to the user to toggle off support if necessary.
Attribution: This PR uses on [code from the official
client](c775603df4/app/src/main/java/org/jellyfin/androidtv/util/profile/deviceProfile.kt).
This commit is contained in:
parent
bd4d4d5092
commit
f220aeee44
7 changed files with 190 additions and 10 deletions
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation3.runtime.rememberNavBackStack
|
import androidx.navigation3.runtime.rememberNavBackStack
|
||||||
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
|
@ -34,9 +35,13 @@ import com.github.damontecres.wholphin.ui.nav.ApplicationContent
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
|
import com.github.damontecres.wholphin.util.AppUpgradeHandler
|
||||||
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.UpdateChecker
|
import com.github.damontecres.wholphin.util.UpdateChecker
|
||||||
import com.github.damontecres.wholphin.util.profile.createDeviceProfile
|
import com.github.damontecres.wholphin.util.profile.createDeviceProfile
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -59,10 +64,16 @@ class MainActivity : AppCompatActivity() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var updateChecker: UpdateChecker
|
lateinit var updateChecker: UpdateChecker
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var appUpgradeHandler: AppUpgradeHandler
|
||||||
|
|
||||||
@OptIn(ExperimentalTvMaterial3Api::class)
|
@OptIn(ExperimentalTvMaterial3Api::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Timber.i("MainActivity.onCreate")
|
Timber.i("MainActivity.onCreate")
|
||||||
|
lifecycleScope.launch(Dispatchers.IO + ExceptionHandler()) {
|
||||||
|
appUpgradeHandler.run()
|
||||||
|
}
|
||||||
setContent {
|
setContent {
|
||||||
CoilConfig(okHttpClient, false)
|
CoilConfig(okHttpClient, false)
|
||||||
val appPreferences by userPreferencesDataStore.data.collectAsState(null)
|
val appPreferences by userPreferencesDataStore.data.collectAsState(null)
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,51 @@ sealed interface AppPreference<T> {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val Ac3Supported =
|
||||||
|
AppSwitchPreference(
|
||||||
|
title = R.string.ac3_supported,
|
||||||
|
defaultValue = true,
|
||||||
|
getter = { it.playbackPreferences.overrides.ac3Supported },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updatePlaybackOverrides { ac3Supported = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.enabled,
|
||||||
|
summaryOff = R.string.disabled,
|
||||||
|
)
|
||||||
|
val DownMixStereo =
|
||||||
|
AppSwitchPreference(
|
||||||
|
title = R.string.downmix_stereo,
|
||||||
|
defaultValue = false,
|
||||||
|
getter = { it.playbackPreferences.overrides.downmixStereo },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updatePlaybackOverrides { downmixStereo = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.enabled,
|
||||||
|
summaryOff = R.string.disabled,
|
||||||
|
)
|
||||||
|
val DirectPlayAss =
|
||||||
|
AppSwitchPreference(
|
||||||
|
title = R.string.direct_play_ass,
|
||||||
|
defaultValue = true,
|
||||||
|
getter = { it.playbackPreferences.overrides.directPlayAss },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updatePlaybackOverrides { directPlayAss = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.enabled,
|
||||||
|
summaryOff = R.string.disabled,
|
||||||
|
)
|
||||||
|
val DirectPlayPgs =
|
||||||
|
AppSwitchPreference(
|
||||||
|
title = R.string.direct_play_pgs,
|
||||||
|
defaultValue = true,
|
||||||
|
getter = { it.playbackPreferences.overrides.directPlayPgs },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updatePlaybackOverrides { directPlayPgs = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.enabled,
|
||||||
|
summaryOff = R.string.disabled,
|
||||||
|
)
|
||||||
|
|
||||||
val RememberSelectedTab =
|
val RememberSelectedTab =
|
||||||
AppSwitchPreference(
|
AppSwitchPreference(
|
||||||
title = R.string.remember_selected_tab,
|
title = R.string.remember_selected_tab,
|
||||||
|
|
@ -521,6 +566,16 @@ val advancedPreferences =
|
||||||
AppPreference.PlaybackDebugInfo,
|
AppPreference.PlaybackDebugInfo,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PreferenceGroup(
|
||||||
|
title = R.string.playback_overrides,
|
||||||
|
preferences =
|
||||||
|
listOf(
|
||||||
|
AppPreference.DownMixStereo,
|
||||||
|
AppPreference.Ac3Supported,
|
||||||
|
AppPreference.DirectPlayAss,
|
||||||
|
AppPreference.DirectPlayPgs,
|
||||||
|
),
|
||||||
|
),
|
||||||
PreferenceGroup(
|
PreferenceGroup(
|
||||||
title = R.string.updates,
|
title = R.string.updates,
|
||||||
preferences =
|
preferences =
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,16 @@ 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
|
||||||
|
|
||||||
|
overrides =
|
||||||
|
PlaybackOverrides
|
||||||
|
.newBuilder()
|
||||||
|
.apply {
|
||||||
|
ac3Supported = AppPreference.Ac3Supported.defaultValue
|
||||||
|
downmixStereo = AppPreference.DownMixStereo.defaultValue
|
||||||
|
directPlayAss = AppPreference.DirectPlayAss.defaultValue
|
||||||
|
directPlayPgs = AppPreference.DirectPlayPgs.defaultValue
|
||||||
|
}.build()
|
||||||
}.build()
|
}.build()
|
||||||
homePagePreferences =
|
homePagePreferences =
|
||||||
HomePagePreferences
|
HomePagePreferences
|
||||||
|
|
@ -81,6 +91,11 @@ inline fun AppPreferences.updatePlaybackPreferences(block: PlaybackPreferences.B
|
||||||
playbackPreferences = playbackPreferences.toBuilder().apply(block).build()
|
playbackPreferences = playbackPreferences.toBuilder().apply(block).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun AppPreferences.updatePlaybackOverrides(block: PlaybackOverrides.Builder.() -> Unit): AppPreferences =
|
||||||
|
updatePlaybackPreferences {
|
||||||
|
overrides = overrides.toBuilder().apply(block).build()
|
||||||
|
}
|
||||||
|
|
||||||
inline fun AppPreferences.updateHomePagePreferences(block: HomePagePreferences.Builder.() -> Unit): AppPreferences =
|
inline fun AppPreferences.updateHomePagePreferences(block: HomePagePreferences.Builder.() -> Unit): AppPreferences =
|
||||||
update {
|
update {
|
||||||
homePagePreferences = homePagePreferences.toBuilder().apply(block).build()
|
homePagePreferences = homePagePreferences.toBuilder().apply(block).build()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.github.damontecres.wholphin.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.github.damontecres.wholphin.WholphinApplication
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
|
import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AppUpgradeHandler
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@param:ApplicationContext private val context: Context,
|
||||||
|
private val appPreferences: DataStore<AppPreferences>,
|
||||||
|
) {
|
||||||
|
suspend fun run() {
|
||||||
|
val pkgInfo = WholphinApplication.instance.packageManager.getPackageInfo(WholphinApplication.instance.packageName, 0)
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val previousVersion = prefs.getString(VERSION_NAME_CURRENT_KEY, null)
|
||||||
|
val previousVersionCode = prefs.getLong(VERSION_CODE_CURRENT_KEY, -1)
|
||||||
|
|
||||||
|
val newVersion = pkgInfo.versionName!!
|
||||||
|
val newVersionCode =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
pkgInfo.longVersionCode
|
||||||
|
} else {
|
||||||
|
pkgInfo.versionCode.toLong()
|
||||||
|
}
|
||||||
|
if (newVersion != previousVersion || newVersionCode != previousVersionCode) {
|
||||||
|
Timber.i(
|
||||||
|
"App updated: $previousVersion=>$newVersion, $previousVersionCode=>$newVersionCode",
|
||||||
|
)
|
||||||
|
prefs.edit(true) {
|
||||||
|
putString(VERSION_NAME_PREVIOUS_KEY, previousVersion)
|
||||||
|
putLong(VERSION_CODE_PREVIOUS_KEY, previousVersionCode)
|
||||||
|
putString(VERSION_NAME_CURRENT_KEY, newVersion)
|
||||||
|
putLong(VERSION_CODE_CURRENT_KEY, newVersionCode)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
upgradeApp(
|
||||||
|
context,
|
||||||
|
Version.fromString(previousVersion ?: "0.0.0"),
|
||||||
|
Version.fromString(newVersion),
|
||||||
|
appPreferences,
|
||||||
|
)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Exception during app upgrade")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val VERSION_NAME_PREVIOUS_KEY = "version.previous.name"
|
||||||
|
const val VERSION_CODE_PREVIOUS_KEY = "version.previous.code"
|
||||||
|
const val VERSION_NAME_CURRENT_KEY = "version.current.name"
|
||||||
|
const val VERSION_CODE_CURRENT_KEY = "version.current.code"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun upgradeApp(
|
||||||
|
context: Context,
|
||||||
|
previous: Version,
|
||||||
|
current: Version,
|
||||||
|
appPreferences: DataStore<AppPreferences>,
|
||||||
|
) {
|
||||||
|
if (previous.isEqualOrBefore(Version.fromString("0.1.0-2-g0"))) {
|
||||||
|
appPreferences.updateData {
|
||||||
|
it.updatePlaybackOverrides {
|
||||||
|
ac3Supported = AppPreference.Ac3Supported.defaultValue
|
||||||
|
downmixStereo = AppPreference.DownMixStereo.defaultValue
|
||||||
|
directPlayAss = AppPreference.DirectPlayAss.defaultValue
|
||||||
|
directPlayPgs = AppPreference.DirectPlayPgs.defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,20 +45,21 @@ private val supportedAudioCodecs =
|
||||||
Codec.Audio.VORBIS,
|
Codec.Audio.VORBIS,
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO use preferences
|
|
||||||
fun createDeviceProfile(
|
fun createDeviceProfile(
|
||||||
context: Context,
|
context: Context,
|
||||||
userPreferences: UserPreferences,
|
userPreferences: UserPreferences,
|
||||||
disableDirectPlay: Boolean = false,
|
disableDirectPlay: Boolean = false,
|
||||||
) = createDeviceProfile(
|
) = userPreferences.appPreferences.playbackPreferences.let { prefs ->
|
||||||
mediaTest = MediaCodecCapabilitiesTest(context),
|
createDeviceProfile(
|
||||||
maxBitrate = 100_000_000,
|
mediaTest = MediaCodecCapabilitiesTest(context),
|
||||||
disableDirectPlay = disableDirectPlay,
|
maxBitrate = 100_000_000,
|
||||||
isAC3Enabled = true,
|
disableDirectPlay = disableDirectPlay,
|
||||||
downMixAudio = false,
|
isAC3Enabled = prefs.overrides.ac3Supported,
|
||||||
assDirectPlay = true,
|
downMixAudio = prefs.overrides.downmixStereo,
|
||||||
pgsDirectPlay = true,
|
assDirectPlay = prefs.overrides.directPlayAss,
|
||||||
)
|
pgsDirectPlay = prefs.overrides.directPlayPgs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun createDeviceProfile(
|
fun createDeviceProfile(
|
||||||
mediaTest: MediaCodecCapabilitiesTest,
|
mediaTest: MediaCodecCapabilitiesTest,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,13 @@ enum SkipSegmentBehavior{
|
||||||
ASK_TO_SKIP = 2;
|
ASK_TO_SKIP = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message PlaybackOverrides{
|
||||||
|
bool ac3_supported = 1;
|
||||||
|
bool downmix_stereo = 2;
|
||||||
|
bool direct_play_ass = 3;
|
||||||
|
bool direct_play_pgs = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message PlaybackPreferences {
|
message PlaybackPreferences {
|
||||||
int64 skip_forward_ms = 1;
|
int64 skip_forward_ms = 1;
|
||||||
int64 skip_back_ms = 2;
|
int64 skip_back_ms = 2;
|
||||||
|
|
@ -25,6 +32,8 @@ message PlaybackPreferences {
|
||||||
SkipSegmentBehavior skip_commercials = 12;
|
SkipSegmentBehavior skip_commercials = 12;
|
||||||
SkipSegmentBehavior skip_recaps = 13;
|
SkipSegmentBehavior skip_recaps = 13;
|
||||||
SkipSegmentBehavior skip_previews = 14;
|
SkipSegmentBehavior skip_previews = 14;
|
||||||
|
|
||||||
|
PlaybackOverrides overrides = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
message HomePagePreferences{
|
message HomePagePreferences{
|
||||||
|
|
|
||||||
|
|
@ -77,5 +77,10 @@
|
||||||
<string name="clear_image_cache">Clear image cache</string>
|
<string name="clear_image_cache">Clear image cache</string>
|
||||||
<string name="shuffle">Shuffle</string>
|
<string name="shuffle">Shuffle</string>
|
||||||
<string name="combine_continue_next"><![CDATA[Combine Continue Watching & Next Up]]></string>
|
<string name="combine_continue_next"><![CDATA[Combine Continue Watching & Next Up]]></string>
|
||||||
|
<string name="ac3_supported">Device supports AC3/Dolby Digital</string>
|
||||||
|
<string name="downmix_stereo">Always downmix to stereo</string>
|
||||||
|
<string name="playback_overrides">Playback overrides</string>
|
||||||
|
<string name="direct_play_ass">Direct play ASS subtitles</string>
|
||||||
|
<string name="direct_play_pgs">Direct play PGS subtitles</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue