diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c255619d..7e572ff5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -233,6 +233,7 @@ dependencies { implementation(libs.androidx.media3.session) implementation(libs.androidx.media3.datasource.okhttp) implementation(libs.androidx.media3.exoplayer.hls) + implementation(libs.androidx.media3.exoplayer.dash) implementation(libs.androidx.media3.ui) implementation(libs.androidx.media3.ui.compose) 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 f719d19c..c15b7bc2 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 @@ -414,6 +414,17 @@ sealed interface AppPreference { summaryOff = R.string.disabled, ) + val DirectPlayDoviProfile7 = + AppSwitchPreference( + title = R.string.force_dovi_profile_7, + defaultValue = false, + getter = { it.playbackPreferences.overrides.directPlayDolbyVisionEL }, + setter = { prefs, value -> + prefs.updatePlaybackOverrides { directPlayDolbyVisionEL = value } + }, + summary = R.string.force_dovi_profile_7_summary, + ) + val RememberSelectedTab = AppSwitchPreference( title = R.string.remember_selected_tab, @@ -992,6 +1003,7 @@ val advancedPreferences = AppPreference.Ac3Supported, AppPreference.DirectPlayAss, AppPreference.DirectPlayPgs, + AppPreference.DirectPlayDoviProfile7, ), ), ConditionalPreferences( diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/DeviceProfileService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/DeviceProfileService.kt index 9ce70b21..95cebb3c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/DeviceProfileService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/DeviceProfileService.kt @@ -42,6 +42,7 @@ class DeviceProfileService downMixAudio = prefs.overrides.downmixStereo, assDirectPlay = prefs.overrides.directPlayAss, pgsDirectPlay = prefs.overrides.directPlayPgs, + dolbyVisionELDirectPlay = prefs.overrides.directPlayDolbyVisionEL, jellyfinTenEleven = serverVersion != null && serverVersion >= ServerVersion(10, 11, 0), ) @@ -55,6 +56,7 @@ class DeviceProfileService downMixAudio = newConfig.downMixAudio, assDirectPlay = newConfig.assDirectPlay, pgsDirectPlay = newConfig.pgsDirectPlay, + dolbyVisionELDirectPlay = newConfig.dolbyVisionELDirectPlay, jellyfinTenEleven = newConfig.jellyfinTenEleven, ) } @@ -72,5 +74,6 @@ data class DeviceProfileConfiguration( val downMixAudio: Boolean, val assDirectPlay: Boolean, val pgsDirectPlay: Boolean, + val dolbyVisionELDirectPlay: Boolean, val jellyfinTenEleven: Boolean, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt index 20270b94..fefb4d94 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt @@ -86,6 +86,7 @@ import com.github.damontecres.wholphin.ui.playback.scale import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.toServerString +import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler @@ -525,6 +526,7 @@ fun CollectionFolderGrid( positionCallback: ((columns: Int, position: Int) -> Unit)? = null, useSeriesForPrimary: Boolean = true, filterOptions: List> = DefaultFilterOptions, + focusRequesterOnEmpty: FocusRequester? = null, ) = CollectionFolderGrid( preferences, itemId.toServerString(), @@ -541,6 +543,7 @@ fun CollectionFolderGrid( positionCallback = positionCallback, useSeriesForPrimary = useSeriesForPrimary, filterOptions = filterOptions, + focusRequesterOnEmpty = focusRequesterOnEmpty, ) @Composable @@ -560,6 +563,7 @@ fun CollectionFolderGrid( positionCallback: ((columns: Int, position: Int) -> Unit)? = null, useSeriesForPrimary: Boolean = true, filterOptions: List> = DefaultFilterOptions, + focusRequesterOnEmpty: FocusRequester? = null, playlistViewModel: AddPlaylistViewModel = hiltViewModel(), viewModel: CollectionFolderViewModel = hiltViewModel( @@ -615,6 +619,7 @@ fun CollectionFolderGrid( pager = pager, sortAndDirection = sortAndDirection!!, modifier = Modifier.fillMaxSize(), + focusRequesterOnEmpty = focusRequesterOnEmpty, onClickItem = onClickItem, onLongClickItem = { position, item -> moreDialog.makePresent(PositionItem(position, item)) @@ -759,6 +764,7 @@ fun CollectionFolderGridContent( currentFilter: GetItemsFilter = GetItemsFilter(), filterOptions: List> = listOf(), onFilterChange: (GetItemsFilter) -> Unit = {}, + focusRequesterOnEmpty: FocusRequester? = null, ) { val context = LocalContext.current @@ -767,7 +773,11 @@ fun CollectionFolderGridContent( var viewOptions by remember { mutableStateOf(viewOptions) } val gridFocusRequester = remember { FocusRequester() } - RequestOrRestoreFocus(gridFocusRequester) + if (pager.isNotEmpty()) { + RequestOrRestoreFocus(gridFocusRequester) + } else { + focusRequesterOnEmpty?.tryRequestFocus() + } var backdropImageUrl by remember { mutableStateOf(null) } var position by rememberInt(initialPosition) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/TabRow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/TabRow.kt index b8607b82..25b51698 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/TabRow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/TabRow.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.focusRestorer import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned @@ -39,13 +40,16 @@ import androidx.compose.ui.unit.sp import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.wholphin.ui.PreviewTvSpec +import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.tryRequestFocus +import timber.log.Timber @Composable fun TabRow( selectedTabIndex: Int, tabs: List, + focusRequesters: List, onClick: (Int) -> Unit, modifier: Modifier = Modifier, ) { @@ -55,7 +59,6 @@ fun TabRow( state.animateScrollToItem(selectedTabIndex, -(state.layoutInfo.viewportSize.width / 3.5).toInt()) } } - val focusRequesters = remember(tabs) { List(tabs.size) { FocusRequester() } } var rowHasFocus by remember { mutableStateOf(false) } LazyRow( state = state, @@ -68,6 +71,7 @@ fun TabRow( onEnter = { // If entering from left or right, use last or first tab // Otherwise use the selected tab + Timber.v("onEnter requestedFocusDirection=$requestedFocusDirection, selectedTabIndex=$selectedTabIndex") val focusRequester = if (requestedFocusDirection == FocusDirection.Left) { focusRequesters.lastOrNull() @@ -183,6 +187,7 @@ private fun TabRowPreview() { TabRow( selectedTabIndex = 1, tabs = listOf("Tab 1", "Tab 2", "Tab 3"), + focusRequesters = listOf(), onClick = {}, ) Tab( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt index c71e0abe..6d1fdd02 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt @@ -93,14 +93,19 @@ fun CollectionFolderLiveTv( remember { viewModel.getRememberedTab(preferences, destination.itemId, 0) } val folders by viewModel.recordingFolders.observeAsState(listOf()) + val tvGuideStr = stringResource(R.string.tv_guide) + val tvDvrStr = stringResource(R.string.tv_dvr_schedule) val tabs = - listOf( - TabId(stringResource(R.string.tv_guide), UUID.randomUUID()), - TabId(stringResource(R.string.tv_dvr_schedule), UUID.randomUUID()), - ) + folders + remember(folders) { + listOf( + TabId(tvGuideStr, UUID.randomUUID()), + TabId(tvDvrStr, UUID.randomUUID()), + ) + folders + } var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } + val tabFocusRequesters = remember(tabs.size) { List(tabs.size) { FocusRequester() } } val firstTabFocusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() } @@ -133,6 +138,7 @@ fun CollectionFolderLiveTv( .focusRequester(firstTabFocusRequester), tabs = tabs.map { it.title }, onClick = { selectedTabIndex = it }, + focusRequesters = tabFocusRequesters, ) } when (selectedTabIndex) { @@ -150,10 +156,12 @@ fun CollectionFolderLiveTv( 1 -> { DvrSchedule( - true, - Modifier - .fillMaxSize() - .focusRequester(focusRequester), + requestFocusAfterLoading = true, + focusRequesterOnEmpty = tabFocusRequesters[1], + modifier = + Modifier + .fillMaxSize() + .focusRequester(focusRequester), ) } @@ -176,6 +184,7 @@ fun CollectionFolderLiveTv( }, playEnabled = false, defaultViewOptions = ViewOptions(), + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } else { ErrorMessage("Invalid tab index $selectedTabIndex", null) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt index 99dc1e7e..d3c52bf7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt @@ -59,6 +59,7 @@ fun CollectionFolderMovie( ) var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } + val tabFocusRequesters = remember { List(tabs.size) { FocusRequester() } } val firstTabFocusRequester = remember { FocusRequester() } // LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() } @@ -88,6 +89,7 @@ fun CollectionFolderMovie( .focusRequester(firstTabFocusRequester), tabs = tabs, onClick = { selectedTabIndex = it }, + focusRequesters = tabFocusRequesters, ) } when (selectedTabIndex) { @@ -136,6 +138,7 @@ fun CollectionFolderMovie( showHeader = position < columns }, playEnabled = true, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } @@ -168,6 +171,7 @@ fun CollectionFolderMovie( showHeader = position < columns }, playEnabled = false, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt index bf032813..42b13d96 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt @@ -58,6 +58,7 @@ fun CollectionFolderTv( ) var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } + val tabFocusRequesters = remember { List(tabs.size) { FocusRequester() } } val firstTabFocusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() } @@ -91,6 +92,7 @@ fun CollectionFolderTv( .focusRequester(firstTabFocusRequester), tabs = tabs, onClick = { selectedTabIndex = it }, + focusRequesters = tabFocusRequesters, ) } when (selectedTabIndex) { @@ -138,6 +140,7 @@ fun CollectionFolderTv( preferencesViewModel.navigationManager.navigateTo(item.destination()) }, playEnabled = false, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt index a62c58f5..fc337d88 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt @@ -73,6 +73,7 @@ fun FavoritesPage( stringResource(R.string.playlists), stringResource(R.string.people), ) + val tabFocusRequesters = remember { List(tabs.size) { FocusRequester() } } var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } @@ -107,6 +108,7 @@ fun FavoritesPage( .padding(start = 32.dp, top = 16.dp, bottom = 16.dp), tabs = tabs, onClick = { selectedTabIndex = it }, + focusRequesters = tabFocusRequesters, ) } // TODO playEnabled = true for movies & episodes @@ -139,6 +141,7 @@ fun FavoritesPage( }, playEnabled = false, filterOptions = DefaultForFavoritesFilterOptions, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } @@ -170,6 +173,7 @@ fun FavoritesPage( }, playEnabled = false, filterOptions = DefaultForFavoritesFilterOptions, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } @@ -202,6 +206,7 @@ fun FavoritesPage( }, playEnabled = false, filterOptions = DefaultForFavoritesFilterOptions, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } @@ -233,6 +238,7 @@ fun FavoritesPage( }, playEnabled = false, filterOptions = DefaultForFavoritesFilterOptions, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } @@ -264,6 +270,7 @@ fun FavoritesPage( }, playEnabled = false, filterOptions = DefaultForFavoritesFilterOptions, + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } @@ -300,6 +307,7 @@ fun FavoritesPage( }, playEnabled = false, filterOptions = listOf(), + focusRequesterOnEmpty = tabFocusRequesters.getOrNull(selectedTabIndex), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt index cfa7637b..f9758d5f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt @@ -114,6 +114,7 @@ class DvrScheduleViewModel @Composable fun DvrSchedule( requestFocusAfterLoading: Boolean, + focusRequesterOnEmpty: FocusRequester, modifier: Modifier = Modifier, viewModel: DvrScheduleViewModel = hiltViewModel(), ) { @@ -138,7 +139,13 @@ fun DvrSchedule( var showDialog by remember { mutableStateOf(null) } val focusRequester = remember { FocusRequester() } if (requestFocusAfterLoading) { - LaunchedEffect(Unit) { focusRequester.tryRequestFocus() } + LaunchedEffect(Unit) { + if (active.isNotEmpty() || recordings.isNotEmpty()) { + focusRequester.tryRequestFocus() + } else { + focusRequesterOnEmpty.tryRequestFocus() + } + } } DvrScheduleContent( activeRecordings = active, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt index 7f346d6c..98698826 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt @@ -107,6 +107,18 @@ fun SeriesOverviewContent( val scrollState = rememberScrollState() val scrollConnection = rememberDelayedNestedScroll() var requestFocusAfterSeason by remember { mutableStateOf(false) } + + val seasonStr = stringResource(R.string.tv_season) + val tabs = + remember(seasons) { + seasons.mapNotNull { + it?.name + ?: it?.data?.indexNumber?.let { "$seasonStr $it" } + ?: "" + } + } + val focusRequesters = remember(seasons) { List(seasons.size) { FocusRequester() } } + Box( modifier = modifier @@ -138,17 +150,13 @@ fun SeriesOverviewContent( } TabRow( selectedTabIndex = selectedTabIndex, - tabs = - seasons.mapNotNull { - it?.name - ?: it?.data?.indexNumber?.let { stringResource(R.string.tv_season) + " $it" } - ?: "" - }, + tabs = tabs, onClick = { selectedTabIndex = it onChangeSeason.invoke(it) requestFocusAfterSeason = true }, + focusRequesters = focusRequesters, modifier = Modifier .focusRequester(tabRowFocusRequester) 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 0a5b1506..151a15a2 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 @@ -11,6 +11,7 @@ import androidx.lifecycle.viewModelScope import androidx.media3.common.C import androidx.media3.common.Format import androidx.media3.common.MediaItem +import androidx.media3.common.MimeTypes import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.Tracks @@ -46,6 +47,7 @@ import com.github.damontecres.wholphin.services.PlaylistCreationResult import com.github.damontecres.wholphin.services.PlaylistCreator import com.github.damontecres.wholphin.services.RefreshRateService import com.github.damontecres.wholphin.services.StreamChoiceService +import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.onMain @@ -61,6 +63,7 @@ import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener import com.github.damontecres.wholphin.util.checkForSupport import com.github.damontecres.wholphin.util.mpv.mpvDeviceProfile +import com.github.damontecres.wholphin.util.profile.Codec import com.github.damontecres.wholphin.util.subtitleMimeTypes import com.github.damontecres.wholphin.util.supportItemKinds import dagger.hilt.android.lifecycle.HiltViewModel @@ -598,13 +601,18 @@ class PlaybackViewModel source?.let { source -> val mediaUrl = if (source.supportsDirectPlay) { - api.videosApi.getVideoStreamUrl( - itemId = itemId, - mediaSourceId = source.id, - static = true, - tag = source.eTag, - playSessionId = response.playSessionId, - ) + if (source.isRemote && source.path.isNotNullOrBlank()) { + Timber.i("Playback is remote for source: %s", source.id) + source.path + } else { + api.videosApi.getVideoStreamUrl( + itemId = itemId, + mediaSourceId = source.id, + static = true, + tag = source.eTag, + playSessionId = response.playSessionId, + ) + } } else if (source.supportsDirectStream) { source.transcodingUrl?.let(api::createUrl) } else { @@ -657,7 +665,12 @@ class PlaybackViewModel .setMediaId(itemId.toString()) .setUri(mediaUrl.toUri()) .setSubtitleConfigurations(listOfNotNull(externalSubtitle)) - .build() + .apply { + when (source.container) { + Codec.Container.HLS -> setMimeType(MimeTypes.APPLICATION_M3U8) + Codec.Container.DASH -> setMimeType(MimeTypes.APPLICATION_MPD) + } + }.build() val playback = CurrentPlayback( diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/profile/Codec.kt b/app/src/main/java/com/github/damontecres/wholphin/util/profile/Codec.kt index 03318879..ad661617 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/profile/Codec.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/profile/Codec.kt @@ -7,6 +7,7 @@ object Codec { const val `3GP` = "3gp" const val ASF = "asf" const val AVI = "avi" + const val DASH = "dash" const val DVR_MS = "dvr-ms" const val HLS = "hls" const val M2V = "m2v" diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt b/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt index b7d28db1..0f507fd3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt @@ -66,6 +66,7 @@ fun createDeviceProfile( downMixAudio: Boolean, assDirectPlay: Boolean, pgsDirectPlay: Boolean, + dolbyVisionELDirectPlay: Boolean, jellyfinTenEleven: Boolean, ) = buildDeviceProfile { val allowedAudioCodecs = @@ -156,6 +157,7 @@ fun createDeviceProfile( container( Codec.Container.ASF, + Codec.Container.DASH, Codec.Container.HLS, Codec.Container.M4V, Codec.Container.MKV, @@ -427,9 +429,11 @@ fun createDeviceProfile( if (jellyfinTenEleven) add("DOVIInvalid") if (!supportsHevcDolbyVisionEL) { - if (jellyfinTenEleven) { - add("DOVIWithEL") - if (!supportsHevcHDR10Plus && !KnownDefects.hevcDoviHdr10PlusBug) add("DOVIWithELHDR10Plus") + if (!dolbyVisionELDirectPlay) { + if (jellyfinTenEleven) { + add("DOVIWithEL") + if (!supportsHevcHDR10Plus && !KnownDefects.hevcDoviHdr10PlusBug) add("DOVIWithELHDR10Plus") + } } if (!supportsHevcDolbyVision) { diff --git a/app/src/main/proto/WholphinDataStore.proto b/app/src/main/proto/WholphinDataStore.proto index e4cd52d7..f68ecae8 100644 --- a/app/src/main/proto/WholphinDataStore.proto +++ b/app/src/main/proto/WholphinDataStore.proto @@ -45,6 +45,7 @@ message PlaybackOverrides{ bool direct_play_ass = 3; bool direct_play_pgs = 4; MediaExtensionStatus media_extensions_enabled = 5; + bool direct_play_dolby_vision_e_l = 6; } message PlaybackPreferences { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bdab1e27..670264ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -426,6 +426,8 @@ DD DD+ DTS + Direct play Dolby Vision Profile 7 + Ignores device compatibility checks Discover Request diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f5deba39..c1b0676f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -92,6 +92,7 @@ androidx-media3-datasource-okhttp = { module = "androidx.media3:media3-datasourc androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "androidx-media3" } androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "androidx-media3" } androidx-media3-exoplayer-hls = { module = "androidx.media3:media3-exoplayer-hls", version.ref = "androidx-media3" } +androidx-media3-exoplayer-dash = { module = "androidx.media3:media3-exoplayer-dash", version.ref = "androidx-media3" } androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "androidx-media3" } androidx-media3-ui-compose = { module = "androidx.media3:media3-ui-compose", version.ref = "androidx-media3" }