mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Update style for ktlint 1.8 (#390)
Updated `ktlint`, so lots of code style changes, no user facing changes
This commit is contained in:
parent
e9c54adaba
commit
9e7742034a
54 changed files with 577 additions and 196 deletions
|
|
@ -199,7 +199,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
val initialDestination =
|
val initialDestination =
|
||||||
when {
|
when {
|
||||||
current != null -> Destination.Home()
|
current != null -> Destination.Home()
|
||||||
!appPreferences.signInAutomatically -> Destination.ServerList // TODO user list?
|
|
||||||
|
!appPreferences.signInAutomatically -> Destination.ServerList
|
||||||
|
|
||||||
|
// TODO user list?
|
||||||
else -> Destination.ServerList
|
else -> Destination.ServerList
|
||||||
}
|
}
|
||||||
val backStack = rememberNavBackStack(initialDestination)
|
val backStack = rememberNavBackStack(initialDestination)
|
||||||
|
|
|
||||||
|
|
@ -81,15 +81,18 @@ data class BaseItem(
|
||||||
} ?: Destination.MediaItem(id, type, this)
|
} ?: Destination.MediaItem(id, type, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseItemKind.SEASON ->
|
BaseItemKind.SEASON -> {
|
||||||
Destination.SeriesOverview(
|
Destination.SeriesOverview(
|
||||||
data.seriesId!!,
|
data.seriesId!!,
|
||||||
BaseItemKind.SERIES,
|
BaseItemKind.SERIES,
|
||||||
this,
|
this,
|
||||||
SeasonEpisodeIds(id, indexNumber, null, null),
|
SeasonEpisodeIds(id, indexNumber, null, null),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> Destination.MediaItem(id, type, this)
|
else -> {
|
||||||
|
Destination.MediaItem(id, type, this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ data class GetItemsFilter(
|
||||||
-> null
|
-> null
|
||||||
|
|
||||||
FilterVideoType.BLU_RAY -> VideoType.BLU_RAY
|
FilterVideoType.BLU_RAY -> VideoType.BLU_RAY
|
||||||
|
|
||||||
FilterVideoType.DVD -> VideoType.DVD
|
FilterVideoType.DVD -> VideoType.DVD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,12 +136,13 @@ fun chooseSubtitleStream(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtitlePlaybackMode.ONLY_FORCED ->
|
SubtitlePlaybackMode.ONLY_FORCED -> {
|
||||||
if (subtitleLanguage != null) {
|
if (subtitleLanguage != null) {
|
||||||
candidates.firstOrNull { it.language == subtitleLanguage && it.isForced }
|
candidates.firstOrNull { it.language == subtitleLanguage && it.isForced }
|
||||||
} else {
|
} else {
|
||||||
candidates.firstOrNull { it.isForced }
|
candidates.firstOrNull { it.isForced }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SubtitlePlaybackMode.SMART -> {
|
SubtitlePlaybackMode.SMART -> {
|
||||||
val audioLanguage = prefs.userConfig.audioLanguagePreference
|
val audioLanguage = prefs.userConfig.audioLanguagePreference
|
||||||
|
|
@ -161,7 +162,9 @@ fun chooseSubtitleStream(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtitlePlaybackMode.NONE -> null
|
SubtitlePlaybackMode.NONE -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,14 @@ class ImageUrlService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else ->
|
else -> {
|
||||||
getItemImageUrl(
|
getItemImageUrl(
|
||||||
itemId = itemId,
|
itemId = itemId,
|
||||||
imageType = imageType,
|
imageType = imageType,
|
||||||
fillWidth = fillWidth,
|
fillWidth = fillWidth,
|
||||||
fillHeight = fillHeight,
|
fillHeight = fillHeight,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItemImageUrl(
|
fun getItemImageUrl(
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ class PlaylistCreator
|
||||||
BaseItemKind.BOX_SET,
|
BaseItemKind.BOX_SET,
|
||||||
BaseItemKind.COLLECTION_FOLDER,
|
BaseItemKind.COLLECTION_FOLDER,
|
||||||
BaseItemKind.USER_VIEW,
|
BaseItemKind.USER_VIEW,
|
||||||
->
|
-> {
|
||||||
PlaylistCreationResult.Success(
|
PlaylistCreationResult.Success(
|
||||||
createFromCollection(
|
createFromCollection(
|
||||||
item = item,
|
item = item,
|
||||||
|
|
@ -152,6 +152,7 @@ class PlaylistCreator
|
||||||
filter = filter,
|
filter = filter,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.EPISODE -> {
|
BaseItemKind.EPISODE -> {
|
||||||
val seriesId = item.seriesId
|
val seriesId = item.seriesId
|
||||||
|
|
@ -185,7 +186,7 @@ class PlaylistCreator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseItemKind.SERIES ->
|
BaseItemKind.SERIES -> {
|
||||||
PlaylistCreationResult.Success(
|
PlaylistCreationResult.Success(
|
||||||
createFromEpisode(
|
createFromEpisode(
|
||||||
seriesId = item.id,
|
seriesId = item.id,
|
||||||
|
|
@ -194,8 +195,9 @@ class PlaylistCreator
|
||||||
shuffled = shuffled,
|
shuffled = shuffled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.PLAYLIST ->
|
BaseItemKind.PLAYLIST -> {
|
||||||
PlaylistCreationResult.Success(
|
PlaylistCreationResult.Success(
|
||||||
createFromPlaylistId(
|
createFromPlaylistId(
|
||||||
item.id,
|
item.id,
|
||||||
|
|
@ -203,6 +205,7 @@ class PlaylistCreator
|
||||||
shuffled,
|
shuffled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Not support yet
|
// Not support yet
|
||||||
// BaseItemKind.AGGREGATE_FOLDER -> TODO()
|
// BaseItemKind.AGGREGATE_FOLDER -> TODO()
|
||||||
|
|
@ -212,7 +215,9 @@ class PlaylistCreator
|
||||||
// BaseItemKind.MUSIC_ALBUM -> TODO()
|
// BaseItemKind.MUSIC_ALBUM -> TODO()
|
||||||
// BaseItemKind.MUSIC_ARTIST -> TODO()
|
// BaseItemKind.MUSIC_ARTIST -> TODO()
|
||||||
|
|
||||||
else -> PlaylistCreationResult.Error(null, "Unsupported type: ${item.type}")
|
else -> {
|
||||||
|
PlaylistCreationResult.Error(null, "Unsupported type: ${item.type}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getServerPlaylists(
|
suspend fun getServerPlaylists(
|
||||||
|
|
|
||||||
|
|
@ -85,9 +85,13 @@ class ThemeSongPlayer
|
||||||
-> return
|
-> return
|
||||||
|
|
||||||
ThemeSongVolume.LOWEST -> .05f
|
ThemeSongVolume.LOWEST -> .05f
|
||||||
|
|
||||||
ThemeSongVolume.LOW -> .1f
|
ThemeSongVolume.LOW -> .1f
|
||||||
|
|
||||||
ThemeSongVolume.MEDIUM -> .25f
|
ThemeSongVolume.MEDIUM -> .25f
|
||||||
|
|
||||||
ThemeSongVolume.HIGH -> .5f
|
ThemeSongVolume.HIGH -> .5f
|
||||||
|
|
||||||
ThemeSongVolume.HIGHEST -> 75f
|
ThemeSongVolume.HIGHEST -> 75f
|
||||||
}
|
}
|
||||||
player.apply {
|
player.apply {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class TrailerService
|
||||||
navigateTo: (Destination) -> Unit,
|
navigateTo: (Destination) -> Unit,
|
||||||
) {
|
) {
|
||||||
when (trailer) {
|
when (trailer) {
|
||||||
is LocalTrailer ->
|
is LocalTrailer -> {
|
||||||
navigateTo.invoke(
|
navigateTo.invoke(
|
||||||
Destination.Playback(
|
Destination.Playback(
|
||||||
itemId = trailer.baseItem.id,
|
itemId = trailer.baseItem.id,
|
||||||
|
|
@ -70,6 +70,7 @@ class TrailerService
|
||||||
positionMs = 0L,
|
positionMs = 0L,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is RemoteTrailer -> {
|
is RemoteTrailer -> {
|
||||||
val intent = Intent(Intent.ACTION_VIEW, trailer.url.toUri())
|
val intent = Intent(Intent.ACTION_VIEW, trailer.url.toUri())
|
||||||
|
|
|
||||||
|
|
@ -299,14 +299,16 @@ class UpdateChecker
|
||||||
|
|
||||||
fun hasPermissions(): Boolean =
|
fun hasPermissions(): Boolean =
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
||||||
ContextCompat.checkSelfPermission(
|
(
|
||||||
context,
|
ContextCompat.checkSelfPermission(
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
context,
|
||||||
) == PackageManager.PERMISSION_GRANTED &&
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
ContextCompat.checkSelfPermission(
|
) == PackageManager.PERMISSION_GRANTED &&
|
||||||
context,
|
ContextCompat.checkSelfPermission(
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
context,
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete previously downloaded APKs
|
* Delete previously downloaded APKs
|
||||||
|
|
|
||||||
|
|
@ -352,11 +352,12 @@ class CollectionFolderViewModel
|
||||||
|
|
||||||
FavoriteFilter,
|
FavoriteFilter,
|
||||||
PlayedFilter,
|
PlayedFilter,
|
||||||
->
|
-> {
|
||||||
listOf(
|
listOf(
|
||||||
FilterValueOption("True", null),
|
FilterValueOption("True", null),
|
||||||
FilterValueOption("False", null),
|
FilterValueOption("False", null),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
OfficialRatingFilter -> {
|
OfficialRatingFilter -> {
|
||||||
api.localizationApi.getParentalRatings().content.map {
|
api.localizationApi.getParentalRatings().content.map {
|
||||||
|
|
@ -364,10 +365,11 @@ class CollectionFolderViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoTypeFilter ->
|
VideoTypeFilter -> {
|
||||||
FilterVideoType.entries.map {
|
FilterVideoType.entries.map {
|
||||||
FilterValueOption(it.readable, it)
|
FilterValueOption(it.readable, it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
YearFilter -> {
|
YearFilter -> {
|
||||||
api.yearsApi
|
api.yearsApi
|
||||||
|
|
@ -400,10 +402,11 @@ class CollectionFolderViewModel
|
||||||
items.toList().sorted().map { FilterValueOption("$it's", it) }
|
items.toList().sorted().map { FilterValueOption("$it's", it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
CommunityRatingFilter ->
|
CommunityRatingFilter -> {
|
||||||
(1..10).map {
|
(1..10).map {
|
||||||
FilterValueOption("$it", it)
|
FilterValueOption("$it", it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Exception get filter value options for $filterOption")
|
Timber.e(ex, "Exception get filter value options for $filterOption")
|
||||||
|
|
@ -418,7 +421,6 @@ class CollectionFolderViewModel
|
||||||
CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE)
|
CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE)
|
||||||
CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES)
|
CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES)
|
||||||
CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO)
|
CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO)
|
||||||
|
|
||||||
else -> listOf()
|
else -> listOf()
|
||||||
}
|
}
|
||||||
val request =
|
val request =
|
||||||
|
|
@ -535,10 +537,16 @@ fun CollectionFolderGrid(
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
pager?.let { pager ->
|
pager?.let { pager ->
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
|
|
@ -920,18 +928,35 @@ data class CollectionFolderGridParameters(
|
||||||
val CollectionType.baseItemKinds: List<BaseItemKind>
|
val CollectionType.baseItemKinds: List<BaseItemKind>
|
||||||
get() =
|
get() =
|
||||||
when (this) {
|
when (this) {
|
||||||
CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE)
|
CollectionType.MOVIES -> {
|
||||||
CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES)
|
listOf(BaseItemKind.MOVIE)
|
||||||
CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO)
|
}
|
||||||
CollectionType.MUSIC ->
|
|
||||||
|
CollectionType.TVSHOWS -> {
|
||||||
|
listOf(BaseItemKind.SERIES)
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionType.HOMEVIDEOS -> {
|
||||||
|
listOf(BaseItemKind.VIDEO)
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionType.MUSIC -> {
|
||||||
listOf(
|
listOf(
|
||||||
BaseItemKind.AUDIO,
|
BaseItemKind.AUDIO,
|
||||||
BaseItemKind.MUSIC_ARTIST,
|
BaseItemKind.MUSIC_ARTIST,
|
||||||
BaseItemKind.MUSIC_ALBUM,
|
BaseItemKind.MUSIC_ALBUM,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.BOXSETS -> listOf(BaseItemKind.BOX_SET)
|
CollectionType.BOXSETS -> {
|
||||||
CollectionType.PLAYLISTS -> listOf(BaseItemKind.PLAYLIST)
|
listOf(BaseItemKind.BOX_SET)
|
||||||
|
}
|
||||||
|
|
||||||
else -> listOf()
|
CollectionType.PLAYLISTS -> {
|
||||||
|
listOf(BaseItemKind.PLAYLIST)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
listOf()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -259,9 +259,11 @@ fun DialogPopupContent(
|
||||||
}
|
}
|
||||||
items(dialogItems) {
|
items(dialogItems) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is DialogItemDivider -> HorizontalDivider(Modifier.height(16.dp))
|
is DialogItemDivider -> {
|
||||||
|
HorizontalDivider(Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
|
||||||
is DialogItem ->
|
is DialogItem -> {
|
||||||
ListItem(
|
ListItem(
|
||||||
selected = false,
|
selected = false,
|
||||||
enabled = !waiting && it.enabled,
|
enabled = !waiting && it.enabled,
|
||||||
|
|
@ -278,6 +280,7 @@ fun DialogPopupContent(
|
||||||
trailingContent = it.trailingContent,
|
trailingContent = it.trailingContent,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,9 @@ fun FilterByButton(
|
||||||
|
|
||||||
FavoriteFilter,
|
FavoriteFilter,
|
||||||
PlayedFilter,
|
PlayedFilter,
|
||||||
-> (currentValue as? Boolean) == value.name.toBoolean()
|
-> {
|
||||||
|
(currentValue as? Boolean) == value.name.toBoolean()
|
||||||
|
}
|
||||||
|
|
||||||
OfficialRatingFilter -> {
|
OfficialRatingFilter -> {
|
||||||
(currentValue as? List<String>)
|
(currentValue as? List<String>)
|
||||||
|
|
@ -214,19 +216,23 @@ fun FilterByButton(
|
||||||
.contains(value.name)
|
.contains(value.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoTypeFilter ->
|
VideoTypeFilter -> {
|
||||||
(currentValue as? List<FilterVideoType>)
|
(currentValue as? List<FilterVideoType>)
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
.contains(value.value)
|
.contains(value.value)
|
||||||
|
}
|
||||||
|
|
||||||
YearFilter,
|
YearFilter,
|
||||||
DecadeFilter,
|
DecadeFilter,
|
||||||
->
|
-> {
|
||||||
(currentValue as? List<Int>)
|
(currentValue as? List<Int>)
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
.contains(value.value)
|
.contains(value.value)
|
||||||
|
}
|
||||||
|
|
||||||
CommunityRatingFilter -> (currentValue as? Int) == value.value
|
CommunityRatingFilter -> {
|
||||||
|
(currentValue as? Int) == value.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
@ -328,11 +334,12 @@ fun FilterByButton(
|
||||||
filterOption.set(newValue, current)
|
filterOption.set(newValue, current)
|
||||||
}
|
}
|
||||||
|
|
||||||
CommunityRatingFilter ->
|
CommunityRatingFilter -> {
|
||||||
filterOption.set(
|
filterOption.set(
|
||||||
value.value as? Int,
|
value.value as? Int,
|
||||||
current,
|
current,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterChange.invoke(newFilter)
|
onFilterChange.invoke(newFilter)
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,15 @@ fun GenreCardGrid(
|
||||||
when (val st = loading) {
|
when (val st = loading) {
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
-> LoadingPage(modifier.focusable())
|
-> {
|
||||||
|
LoadingPage(modifier.focusable())
|
||||||
|
}
|
||||||
|
|
||||||
is LoadingState.Error -> ErrorMessage(st, modifier.focusable())
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(st, modifier.focusable())
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success ->
|
LoadingState.Success -> {
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
LaunchedEffect(Unit) { gridFocusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { gridFocusRequester.tryRequestFocus() }
|
||||||
CardGrid(
|
CardGrid(
|
||||||
|
|
@ -132,5 +136,6 @@ fun GenreCardGrid(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,15 @@ fun ItemGrid(
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
val items by viewModel.items.observeAsState(listOf())
|
val items by viewModel.items.observeAsState(listOf())
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
|
||||||
|
|
@ -59,22 +59,24 @@ fun LoadingRow(
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
when (val r = state) {
|
when (val r = state) {
|
||||||
is RowLoadingState.Error ->
|
is RowLoadingState.Error -> {
|
||||||
LoadingRowPlaceholder(
|
LoadingRowPlaceholder(
|
||||||
title = title,
|
title = title,
|
||||||
message = r.localizedMessage,
|
message = r.localizedMessage,
|
||||||
messageColor = MaterialTheme.colorScheme.error,
|
messageColor = MaterialTheme.colorScheme.error,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
RowLoadingState.Pending,
|
RowLoadingState.Pending,
|
||||||
RowLoadingState.Loading,
|
RowLoadingState.Loading,
|
||||||
->
|
-> {
|
||||||
LoadingRowPlaceholder(
|
LoadingRowPlaceholder(
|
||||||
title = title,
|
title = title,
|
||||||
message = stringResource(R.string.loading),
|
message = stringResource(R.string.loading),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is RowLoadingState.Success -> {
|
is RowLoadingState.Success -> {
|
||||||
if (r.items.isNotEmpty()) {
|
if (r.items.isNotEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -256,8 +256,9 @@ fun StarRating(
|
||||||
if (playSoundOnFocus) playOnClickSound(context)
|
if (playSoundOnFocus) playOnClickSound(context)
|
||||||
val newRating100 =
|
val newRating100 =
|
||||||
when (precision) {
|
when (precision) {
|
||||||
StarRatingPrecision.FULL ->
|
StarRatingPrecision.FULL -> {
|
||||||
if (i == 1 && rating100 > 0 && rating100 <= 20) 0 else i * 20
|
if (i == 1 && rating100 > 0 && rating100 <= 20) 0 else i * 20
|
||||||
|
}
|
||||||
|
|
||||||
StarRatingPrecision.HALF -> {
|
StarRatingPrecision.HALF -> {
|
||||||
if (rating100 > i * 20) {
|
if (rating100 > i * 20) {
|
||||||
|
|
|
||||||
|
|
@ -127,13 +127,17 @@ fun RecommendedContent(
|
||||||
val rows by viewModel.rows.collectAsState()
|
val rows by viewModel.rows.collectAsState()
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success ->
|
LoadingState.Success -> {
|
||||||
HomePageContent(
|
HomePageContent(
|
||||||
homeRows = rows,
|
homeRows = rows,
|
||||||
onClickItem = { _, item ->
|
onClickItem = { _, item ->
|
||||||
|
|
@ -146,6 +150,7 @@ fun RecommendedContent(
|
||||||
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
moreDialog.compose { (position, item) ->
|
moreDialog.compose { (position, item) ->
|
||||||
DialogPopup(
|
DialogPopup(
|
||||||
|
|
|
||||||
|
|
@ -128,21 +128,25 @@ fun SliderActiveColor(focused: Boolean): Color {
|
||||||
AppThemeColors.BLUE,
|
AppThemeColors.BLUE,
|
||||||
AppThemeColors.GREEN,
|
AppThemeColors.GREEN,
|
||||||
AppThemeColors.ORANGE,
|
AppThemeColors.ORANGE,
|
||||||
-> MaterialTheme.colorScheme.border
|
-> {
|
||||||
|
MaterialTheme.colorScheme.border
|
||||||
|
}
|
||||||
|
|
||||||
AppThemeColors.BOLD_BLUE ->
|
AppThemeColors.BOLD_BLUE -> {
|
||||||
if (focused) {
|
if (focused) {
|
||||||
MaterialTheme.colorScheme.border
|
MaterialTheme.colorScheme.border
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.border
|
MaterialTheme.colorScheme.border
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AppThemeColors.OLED_BLACK ->
|
AppThemeColors.OLED_BLACK -> {
|
||||||
if (focused) {
|
if (focused) {
|
||||||
MaterialTheme.colorScheme.primaryContainer
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.border
|
MaterialTheme.colorScheme.border
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,23 +156,27 @@ fun SliderInactiveColor(focused: Boolean): Color {
|
||||||
return when (theme) {
|
return when (theme) {
|
||||||
AppThemeColors.UNRECOGNIZED,
|
AppThemeColors.UNRECOGNIZED,
|
||||||
AppThemeColors.PURPLE,
|
AppThemeColors.PURPLE,
|
||||||
->
|
-> {
|
||||||
MaterialTheme.colorScheme.border
|
MaterialTheme.colorScheme.border
|
||||||
.copy(alpha = .25f)
|
.copy(alpha = .25f)
|
||||||
.compositeOver(MaterialTheme.colorScheme.surfaceVariant)
|
.compositeOver(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
.copy(alpha = .66f)
|
.copy(alpha = .66f)
|
||||||
|
}
|
||||||
|
|
||||||
AppThemeColors.BLUE,
|
AppThemeColors.BLUE,
|
||||||
AppThemeColors.GREEN,
|
AppThemeColors.GREEN,
|
||||||
AppThemeColors.ORANGE,
|
AppThemeColors.ORANGE,
|
||||||
AppThemeColors.BOLD_BLUE,
|
AppThemeColors.BOLD_BLUE,
|
||||||
-> MaterialTheme.colorScheme.secondaryContainer.copy(alpha = .66f)
|
-> {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = .66f)
|
||||||
|
}
|
||||||
|
|
||||||
AppThemeColors.OLED_BLACK ->
|
AppThemeColors.OLED_BLACK -> {
|
||||||
if (focused) {
|
if (focused) {
|
||||||
MaterialTheme.colorScheme.tertiaryContainer
|
MaterialTheme.colorScheme.tertiaryContainer
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.primaryContainer
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -260,9 +260,11 @@ fun formatVideoRange(
|
||||||
when (videoRange) {
|
when (videoRange) {
|
||||||
VideoRange.UNKNOWN,
|
VideoRange.UNKNOWN,
|
||||||
VideoRange.SDR, null,
|
VideoRange.SDR, null,
|
||||||
-> null
|
-> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
VideoRange.HDR ->
|
VideoRange.HDR -> {
|
||||||
when (type) {
|
when (type) {
|
||||||
VideoRangeType.UNKNOWN,
|
VideoRangeType.UNKNOWN,
|
||||||
VideoRangeType.SDR,
|
VideoRangeType.SDR,
|
||||||
|
|
@ -270,7 +272,9 @@ fun formatVideoRange(
|
||||||
-> null
|
-> null
|
||||||
|
|
||||||
VideoRangeType.HDR10 -> "HDR10"
|
VideoRangeType.HDR10 -> "HDR10"
|
||||||
|
|
||||||
VideoRangeType.HDR10_PLUS -> "HDR10+"
|
VideoRangeType.HDR10_PLUS -> "HDR10+"
|
||||||
|
|
||||||
VideoRangeType.HLG -> "HLG"
|
VideoRangeType.HLG -> "HLG"
|
||||||
|
|
||||||
VideoRangeType.DOVI,
|
VideoRangeType.DOVI,
|
||||||
|
|
@ -279,6 +283,7 @@ fun formatVideoRange(
|
||||||
VideoRangeType.DOVI_WITH_SDR,
|
VideoRangeType.DOVI_WITH_SDR,
|
||||||
-> context.getString(R.string.dolby_vision)
|
-> context.getString(R.string.dolby_vision)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatAudioCodec(
|
fun formatAudioCodec(
|
||||||
|
|
@ -287,20 +292,32 @@ fun formatAudioCodec(
|
||||||
profile: String?,
|
profile: String?,
|
||||||
): String? =
|
): String? =
|
||||||
when {
|
when {
|
||||||
profile?.contains("Dolby Atmos", true) == true -> context.getString(R.string.dolby_atmos)
|
profile?.contains("Dolby Atmos", true) == true -> {
|
||||||
profile?.contains("DTS:X", true) == true -> "DTS:X"
|
context.getString(R.string.dolby_atmos)
|
||||||
profile?.contains("DTS:HD", true) == true -> "DTS:HD"
|
}
|
||||||
else ->
|
|
||||||
|
profile?.contains("DTS:X", true) == true -> {
|
||||||
|
"DTS:X"
|
||||||
|
}
|
||||||
|
|
||||||
|
profile?.contains("DTS:HD", true) == true -> {
|
||||||
|
"DTS:HD"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
when (codec?.lowercase()) {
|
when (codec?.lowercase()) {
|
||||||
Codec.Audio.TRUEHD -> "TrueHD"
|
Codec.Audio.TRUEHD -> "TrueHD"
|
||||||
|
|
||||||
Codec.Audio.OGG,
|
Codec.Audio.OGG,
|
||||||
Codec.Audio.OPUS,
|
Codec.Audio.OPUS,
|
||||||
Codec.Audio.VORBIS,
|
Codec.Audio.VORBIS,
|
||||||
-> codec.replaceFirstChar { it.uppercase() }
|
-> codec.replaceFirstChar { it.uppercase() }
|
||||||
|
|
||||||
null -> null
|
null -> null
|
||||||
|
|
||||||
else -> codec.uppercase()
|
else -> codec.uppercase()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatSubtitleCodec(codec: String?): String? =
|
fun formatSubtitleCodec(codec: String?): String? =
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,9 @@ fun getStringRes(sort: ItemSortBy): Int =
|
||||||
-> R.string.sort_by_name
|
-> R.string.sort_by_name
|
||||||
|
|
||||||
ItemSortBy.PREMIERE_DATE -> R.string.sort_by_date_released
|
ItemSortBy.PREMIERE_DATE -> R.string.sort_by_date_released
|
||||||
|
|
||||||
ItemSortBy.DATE_CREATED -> R.string.sort_by_date_added
|
ItemSortBy.DATE_CREATED -> R.string.sort_by_date_added
|
||||||
|
|
||||||
ItemSortBy.DATE_LAST_CONTENT_ADDED -> R.string.sort_by_date_episode_added
|
ItemSortBy.DATE_LAST_CONTENT_ADDED -> R.string.sort_by_date_episode_added
|
||||||
|
|
||||||
ItemSortBy.DATE_PLAYED,
|
ItemSortBy.DATE_PLAYED,
|
||||||
|
|
@ -115,12 +117,20 @@ fun getStringRes(sort: ItemSortBy): Int =
|
||||||
-> R.string.sort_by_date_played
|
-> R.string.sort_by_date_played
|
||||||
|
|
||||||
ItemSortBy.RANDOM -> R.string.sort_by_random
|
ItemSortBy.RANDOM -> R.string.sort_by_random
|
||||||
|
|
||||||
ItemSortBy.COMMUNITY_RATING -> R.string.community_rating
|
ItemSortBy.COMMUNITY_RATING -> R.string.community_rating
|
||||||
|
|
||||||
ItemSortBy.CRITIC_RATING -> R.string.critic_rating
|
ItemSortBy.CRITIC_RATING -> R.string.critic_rating
|
||||||
|
|
||||||
ItemSortBy.OFFICIAL_RATING -> R.string.official_rating
|
ItemSortBy.OFFICIAL_RATING -> R.string.official_rating
|
||||||
|
|
||||||
ItemSortBy.PLAY_COUNT -> R.string.play_count
|
ItemSortBy.PLAY_COUNT -> R.string.play_count
|
||||||
|
|
||||||
ItemSortBy.AIRED_EPISODE_ORDER -> R.string.aired_episode_order
|
ItemSortBy.AIRED_EPISODE_ORDER -> R.string.aired_episode_order
|
||||||
|
|
||||||
ItemSortBy.RUNTIME -> R.string.runtime_sort
|
ItemSortBy.RUNTIME -> R.string.runtime_sort
|
||||||
|
|
||||||
ItemSortBy.DEFAULT -> R.string.default_track
|
ItemSortBy.DEFAULT -> R.string.default_track
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Unsupported sort option: $sort")
|
else -> throw IllegalArgumentException("Unsupported sort option: $sort")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ fun CollectionFolderLiveTv(
|
||||||
.focusRequester(focusRequester),
|
.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
1 -> {
|
1 -> {
|
||||||
DvrSchedule(
|
DvrSchedule(
|
||||||
true,
|
true,
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ fun CollectionFolderMovie(
|
||||||
.focusRequester(focusRequester),
|
.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Library
|
// Library
|
||||||
1 -> {
|
1 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -131,6 +132,7 @@ fun CollectionFolderMovie(
|
||||||
playEnabled = true,
|
playEnabled = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collections
|
// Collections
|
||||||
2 -> {
|
2 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -158,6 +160,7 @@ fun CollectionFolderMovie(
|
||||||
playEnabled = false,
|
playEnabled = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Genres
|
// Genres
|
||||||
3 -> {
|
3 -> {
|
||||||
GenreCardGrid(
|
GenreCardGrid(
|
||||||
|
|
@ -169,7 +172,10 @@ fun CollectionFolderMovie(
|
||||||
.focusRequester(focusRequester),
|
.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
|
||||||
|
else -> {
|
||||||
|
ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ fun CollectionFolderTv(
|
||||||
.focusRequester(focusRequester),
|
.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Library
|
// Library
|
||||||
1 -> {
|
1 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -134,6 +135,7 @@ fun CollectionFolderTv(
|
||||||
playEnabled = false,
|
playEnabled = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Genres
|
// Genres
|
||||||
2 -> {
|
2 -> {
|
||||||
GenreCardGrid(
|
GenreCardGrid(
|
||||||
|
|
@ -145,7 +147,10 @@ fun CollectionFolderTv(
|
||||||
.focusRequester(focusRequester),
|
.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
|
||||||
|
else -> {
|
||||||
|
ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ fun FavoritesPage(
|
||||||
filterOptions = DefaultForFavoritesFilterOptions,
|
filterOptions = DefaultForFavoritesFilterOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TV
|
// TV
|
||||||
1 -> {
|
1 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -163,6 +164,7 @@ fun FavoritesPage(
|
||||||
filterOptions = DefaultForFavoritesFilterOptions,
|
filterOptions = DefaultForFavoritesFilterOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Episodes
|
// Episodes
|
||||||
2 -> {
|
2 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -191,6 +193,7 @@ fun FavoritesPage(
|
||||||
filterOptions = DefaultForFavoritesFilterOptions,
|
filterOptions = DefaultForFavoritesFilterOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Videos
|
// Videos
|
||||||
3 -> {
|
3 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -218,6 +221,7 @@ fun FavoritesPage(
|
||||||
filterOptions = DefaultForFavoritesFilterOptions,
|
filterOptions = DefaultForFavoritesFilterOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Playlists
|
// Playlists
|
||||||
4 -> {
|
4 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -245,6 +249,7 @@ fun FavoritesPage(
|
||||||
filterOptions = DefaultForFavoritesFilterOptions,
|
filterOptions = DefaultForFavoritesFilterOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// People
|
// People
|
||||||
5 -> {
|
5 -> {
|
||||||
CollectionFolderGrid(
|
CollectionFolderGrid(
|
||||||
|
|
@ -277,7 +282,10 @@ fun FavoritesPage(
|
||||||
filterOptions = listOf(),
|
filterOptions = listOf(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
|
||||||
|
else -> {
|
||||||
|
ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,10 +178,15 @@ fun PersonPage(
|
||||||
|
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state, modifier)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage(modifier)
|
-> {
|
||||||
|
LoadingPage(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
person?.let { person ->
|
person?.let { person ->
|
||||||
|
|
|
||||||
|
|
@ -130,9 +130,15 @@ fun PlaylistDetails(
|
||||||
var longClickDialog by remember { mutableStateOf<DialogParams?>(null) }
|
var longClickDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
|
|
||||||
when (val st = loading) {
|
when (val st = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(st, modifier)
|
is LoadingState.Error -> {
|
||||||
LoadingState.Pending, LoadingState.Loading -> LoadingPage(modifier)
|
ErrorMessage(st, modifier)
|
||||||
LoadingState.Success ->
|
}
|
||||||
|
|
||||||
|
LoadingState.Pending, LoadingState.Loading -> {
|
||||||
|
LoadingPage(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadingState.Success -> {
|
||||||
playlist?.let {
|
playlist?.let {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
|
|
@ -189,6 +195,7 @@ fun PlaylistDetails(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
longClickDialog?.let { params ->
|
longClickDialog?.let { params ->
|
||||||
DialogPopup(
|
DialogPopup(
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ fun PlaylistDialog(
|
||||||
when (val s = state) {
|
when (val s = state) {
|
||||||
PlaylistLoadingState.Pending,
|
PlaylistLoadingState.Pending,
|
||||||
PlaylistLoadingState.Loading,
|
PlaylistLoadingState.Loading,
|
||||||
->
|
-> {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
color = MaterialTheme.colorScheme.border,
|
color = MaterialTheme.colorScheme.border,
|
||||||
modifier =
|
modifier =
|
||||||
|
|
@ -224,9 +224,11 @@ fun PlaylistDialog(
|
||||||
.align(Alignment.CenterHorizontally)
|
.align(Alignment.CenterHorizontally)
|
||||||
.size(48.dp),
|
.size(48.dp),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is PlaylistLoadingState.Error ->
|
is PlaylistLoadingState.Error -> {
|
||||||
ErrorMessage(s.message, s.exception)
|
ErrorMessage(s.message, s.exception)
|
||||||
|
}
|
||||||
|
|
||||||
is PlaylistLoadingState.Success -> {
|
is PlaylistLoadingState.Success -> {
|
||||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,15 @@ fun EpisodeDetails(
|
||||||
)
|
)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
item?.let { ep ->
|
item?.let { ep ->
|
||||||
|
|
|
||||||
|
|
@ -124,11 +124,15 @@ fun DvrSchedule(
|
||||||
val active by viewModel.active.observeAsState(listOf())
|
val active by viewModel.active.observeAsState(listOf())
|
||||||
val recordings by viewModel.scheduled.observeAsState(mapOf())
|
val recordings by viewModel.scheduled.observeAsState(mapOf())
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state, modifier)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage(modifier)
|
-> {
|
||||||
|
LoadingPage(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
var showDialog by remember { mutableStateOf<BaseItem?>(null) }
|
var showDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
|
|
|
||||||
|
|
@ -76,17 +76,21 @@ fun ProgramDialog(
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
) {
|
) {
|
||||||
when (val st = loading) {
|
when (val st = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(st)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(st)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
->
|
-> {
|
||||||
CircularProgress(
|
CircularProgress(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.size(48.dp),
|
.size(48.dp),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success ->
|
LoadingState.Success -> {
|
||||||
item?.let { item ->
|
item?.let { item ->
|
||||||
val now = LocalDateTime.now()
|
val now = LocalDateTime.now()
|
||||||
val dto = item.data
|
val dto = item.data
|
||||||
|
|
@ -272,6 +276,7 @@ fun ProgramDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,9 +77,14 @@ fun TvGuideGrid(
|
||||||
// val programsByChannel by viewModel.programsByChannel.observeAsState(mapOf())
|
// val programsByChannel by viewModel.programsByChannel.observeAsState(mapOf())
|
||||||
// val fetchedRange by viewModel.fetchedRange.observeAsState(0..0)
|
// val fetchedRange by viewModel.fetchedRange.observeAsState(0..0)
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state, modifier)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage(modifier)
|
-> {
|
||||||
|
LoadingPage(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Success,
|
LoadingState.Success,
|
||||||
|
|
|
||||||
|
|
@ -133,10 +133,16 @@ fun MovieDetails(
|
||||||
)
|
)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
item?.let { movie ->
|
item?.let { movie ->
|
||||||
LifecycleStartEffect(destination.itemId) {
|
LifecycleStartEffect(destination.itemId) {
|
||||||
|
|
@ -565,7 +571,7 @@ fun TrailerRow(
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
when (item) {
|
when (item) {
|
||||||
is LocalTrailer ->
|
is LocalTrailer -> {
|
||||||
SeasonCard(
|
SeasonCard(
|
||||||
item = item.baseItem,
|
item = item.baseItem,
|
||||||
onClick = { onClickTrailer.invoke(item) },
|
onClick = { onClickTrailer.invoke(item) },
|
||||||
|
|
@ -575,6 +581,7 @@ fun TrailerRow(
|
||||||
showImageOverlay = false,
|
showImageOverlay = false,
|
||||||
modifier = cardModifier,
|
modifier = cardModifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is RemoteTrailer -> {
|
is RemoteTrailer -> {
|
||||||
val subtitle =
|
val subtitle =
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,16 @@ fun SeriesDetails(
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
item?.let { item ->
|
item?.let { item ->
|
||||||
LifecycleStartEffect(destination.itemId) {
|
LifecycleStartEffect(destination.itemId) {
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,15 @@ fun SeriesOverview(
|
||||||
val chosenStreams by viewModel.chosenStreams.observeAsState(null)
|
val chosenStreams by viewModel.chosenStreams.observeAsState(null)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
series?.let { series ->
|
series?.let { series ->
|
||||||
|
|
|
||||||
|
|
@ -172,8 +172,14 @@ fun SeriesOverviewContent(
|
||||||
|
|
||||||
key(position.seasonTabIndex) {
|
key(position.seasonTabIndex) {
|
||||||
when (val eps = episodes) {
|
when (val eps = episodes) {
|
||||||
EpisodeList.Loading -> LoadingPage()
|
EpisodeList.Loading -> {
|
||||||
is EpisodeList.Error -> ErrorMessage(eps.message, eps.exception)
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
is EpisodeList.Error -> {
|
||||||
|
ErrorMessage(eps.message, eps.exception)
|
||||||
|
}
|
||||||
|
|
||||||
is EpisodeList.Success -> {
|
is EpisodeList.Success -> {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
OneTimeLaunchedEffect {
|
OneTimeLaunchedEffect {
|
||||||
|
|
|
||||||
|
|
@ -107,11 +107,15 @@ fun HomePage(
|
||||||
}
|
}
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage()
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
var dialog by remember { mutableStateOf<DialogParams?>(null) }
|
var dialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
|
|
@ -391,7 +395,7 @@ fun HomePageContent(
|
||||||
when (loadingState) {
|
when (loadingState) {
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
->
|
-> {
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
@ -401,6 +405,7 @@ fun HomePageContent(
|
||||||
) {
|
) {
|
||||||
CircularProgress(Modifier.fillMaxSize())
|
CircularProgress(Modifier.fillMaxSize())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,24 +325,26 @@ fun LazyListScope.searchResultRow(
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
when (val r = result) {
|
when (val r = result) {
|
||||||
is SearchResult.Error ->
|
is SearchResult.Error -> {
|
||||||
SearchResultPlaceholder(
|
SearchResultPlaceholder(
|
||||||
title = title,
|
title = title,
|
||||||
message = r.ex.localizedMessage ?: "Error occurred during search",
|
message = r.ex.localizedMessage ?: "Error occurred during search",
|
||||||
messageColor = MaterialTheme.colorScheme.error,
|
messageColor = MaterialTheme.colorScheme.error,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
SearchResult.NoQuery -> {
|
SearchResult.NoQuery -> {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchResult.Searching ->
|
SearchResult.Searching -> {
|
||||||
SearchResultPlaceholder(
|
SearchResultPlaceholder(
|
||||||
title = title,
|
title = title,
|
||||||
message = stringResource(R.string.searching),
|
message = stringResource(R.string.searching),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is SearchResult.Success -> {
|
is SearchResult.Success -> {
|
||||||
if (r.items.isEmpty()) {
|
if (r.items.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -44,70 +44,84 @@ fun DestinationContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
when (destination) {
|
when (destination) {
|
||||||
is Destination.Home ->
|
is Destination.Home -> {
|
||||||
HomePage(
|
HomePage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.PlaybackList,
|
is Destination.PlaybackList,
|
||||||
is Destination.Playback,
|
is Destination.Playback,
|
||||||
->
|
-> {
|
||||||
PlaybackPage(
|
PlaybackPage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Destination.ServerList -> SwitchServerContent(modifier)
|
Destination.ServerList -> {
|
||||||
is Destination.UserList -> SwitchUserContent(destination.server, modifier)
|
SwitchServerContent(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.Settings ->
|
is Destination.UserList -> {
|
||||||
|
SwitchUserContent(destination.server, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Destination.Settings -> {
|
||||||
PreferencesPage(
|
PreferencesPage(
|
||||||
preferences.appPreferences,
|
preferences.appPreferences,
|
||||||
destination.screen,
|
destination.screen,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.SeriesOverview ->
|
is Destination.SeriesOverview -> {
|
||||||
SeriesOverview(
|
SeriesOverview(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
initialSeasonEpisode = destination.seasonEpisode,
|
initialSeasonEpisode = destination.seasonEpisode,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.MediaItem ->
|
is Destination.MediaItem -> {
|
||||||
when (destination.type) {
|
when (destination.type) {
|
||||||
BaseItemKind.SERIES ->
|
BaseItemKind.SERIES -> {
|
||||||
SeriesDetails(
|
SeriesDetails(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.MOVIE ->
|
BaseItemKind.MOVIE -> {
|
||||||
MovieDetails(
|
MovieDetails(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.VIDEO ->
|
BaseItemKind.VIDEO -> {
|
||||||
// TODO Use VideoDetails
|
// TODO Use VideoDetails
|
||||||
MovieDetails(
|
MovieDetails(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.EPISODE ->
|
BaseItemKind.EPISODE -> {
|
||||||
EpisodeDetails(
|
EpisodeDetails(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.BOX_SET ->
|
BaseItemKind.BOX_SET -> {
|
||||||
CollectionFolderBoxSet(
|
CollectionFolderBoxSet(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
itemId = destination.itemId,
|
itemId = destination.itemId,
|
||||||
|
|
@ -116,14 +130,16 @@ fun DestinationContent(
|
||||||
playEnabled = true,
|
playEnabled = true,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.PLAYLIST ->
|
BaseItemKind.PLAYLIST -> {
|
||||||
PlaylistDetails(
|
PlaylistDetails(
|
||||||
destination = destination,
|
destination = destination,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.COLLECTION_FOLDER ->
|
BaseItemKind.COLLECTION_FOLDER -> {
|
||||||
CollectionFolder(
|
CollectionFolder(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
|
|
@ -132,8 +148,9 @@ fun DestinationContent(
|
||||||
recursiveOverride = null,
|
recursiveOverride = null,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.FOLDER ->
|
BaseItemKind.FOLDER -> {
|
||||||
CollectionFolder(
|
CollectionFolder(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
|
|
@ -142,8 +159,9 @@ fun DestinationContent(
|
||||||
recursiveOverride = null,
|
recursiveOverride = null,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.USER_VIEW ->
|
BaseItemKind.USER_VIEW -> {
|
||||||
CollectionFolder(
|
CollectionFolder(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
|
|
@ -152,21 +170,24 @@ fun DestinationContent(
|
||||||
recursiveOverride = true,
|
recursiveOverride = true,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BaseItemKind.PERSON ->
|
BaseItemKind.PERSON -> {
|
||||||
PersonPage(
|
PersonPage(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
Timber.w("Unsupported item type: ${destination.type}")
|
Timber.w("Unsupported item type: ${destination.type}")
|
||||||
Text("Unsupported item type: ${destination.type}")
|
Text("Unsupported item type: ${destination.type}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.FilteredCollection ->
|
is Destination.FilteredCollection -> {
|
||||||
CollectionFolderGeneric(
|
CollectionFolderGeneric(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
itemId = destination.itemId,
|
itemId = destination.itemId,
|
||||||
|
|
@ -177,38 +198,49 @@ fun DestinationContent(
|
||||||
filterOptions = DefaultForGenresFilterOptions,
|
filterOptions = DefaultForGenresFilterOptions,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.Recordings ->
|
is Destination.Recordings -> {
|
||||||
CollectionFolderRecordings(
|
CollectionFolderRecordings(
|
||||||
preferences,
|
preferences,
|
||||||
destination.itemId,
|
destination.itemId,
|
||||||
false,
|
false,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is Destination.ItemGrid ->
|
is Destination.ItemGrid -> {
|
||||||
ItemGrid(
|
ItemGrid(
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Destination.Favorites ->
|
Destination.Favorites -> {
|
||||||
FavoritesPage(
|
FavoritesPage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Destination.UpdateApp -> InstallUpdatePage(preferences, modifier)
|
Destination.UpdateApp -> {
|
||||||
|
InstallUpdatePage(preferences, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
Destination.License -> LicenseInfo(modifier)
|
Destination.License -> {
|
||||||
|
LicenseInfo(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
Destination.Search ->
|
Destination.Search -> {
|
||||||
SearchPage(
|
SearchPage(
|
||||||
userPreferences = preferences,
|
userPreferences = preferences,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Destination.Debug -> DebugPage(preferences, modifier)
|
Destination.Debug -> {
|
||||||
|
DebugPage(preferences, modifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,21 +254,23 @@ fun CollectionFolder(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
when (collectionType) {
|
when (collectionType) {
|
||||||
CollectionType.TVSHOWS ->
|
CollectionType.TVSHOWS -> {
|
||||||
CollectionFolderTv(
|
CollectionFolderTv(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.MOVIES ->
|
CollectionType.MOVIES -> {
|
||||||
CollectionFolderMovie(
|
CollectionFolderMovie(
|
||||||
preferences,
|
preferences,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.BOXSETS ->
|
CollectionType.BOXSETS -> {
|
||||||
CollectionFolderGeneric(
|
CollectionFolderGeneric(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
itemId = destination.itemId,
|
itemId = destination.itemId,
|
||||||
|
|
@ -246,8 +280,9 @@ fun CollectionFolder(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
sortOptions = MovieSortOptions,
|
sortOptions = MovieSortOptions,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.PLAYLISTS ->
|
CollectionType.PLAYLISTS -> {
|
||||||
CollectionFolderPlaylist(
|
CollectionFolderPlaylist(
|
||||||
preferences,
|
preferences,
|
||||||
destination.itemId,
|
destination.itemId,
|
||||||
|
|
@ -255,20 +290,22 @@ fun CollectionFolder(
|
||||||
true,
|
true,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.LIVETV ->
|
CollectionType.LIVETV -> {
|
||||||
CollectionFolderLiveTv(
|
CollectionFolderLiveTv(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.HOMEVIDEOS,
|
CollectionType.HOMEVIDEOS,
|
||||||
CollectionType.MUSICVIDEOS,
|
CollectionType.MUSICVIDEOS,
|
||||||
CollectionType.MUSIC,
|
CollectionType.MUSIC,
|
||||||
CollectionType.BOOKS,
|
CollectionType.BOOKS,
|
||||||
CollectionType.PHOTOS,
|
CollectionType.PHOTOS,
|
||||||
->
|
-> {
|
||||||
CollectionFolderGeneric(
|
CollectionFolderGeneric(
|
||||||
preferences,
|
preferences,
|
||||||
destination.itemId,
|
destination.itemId,
|
||||||
|
|
@ -277,12 +314,13 @@ fun CollectionFolder(
|
||||||
playEnabled = true,
|
playEnabled = true,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CollectionType.FOLDERS,
|
CollectionType.FOLDERS,
|
||||||
CollectionType.TRAILERS,
|
CollectionType.TRAILERS,
|
||||||
CollectionType.UNKNOWN,
|
CollectionType.UNKNOWN,
|
||||||
null,
|
null,
|
||||||
->
|
-> {
|
||||||
CollectionFolderGeneric(
|
CollectionFolderGeneric(
|
||||||
preferences,
|
preferences,
|
||||||
destination.itemId,
|
destination.itemId,
|
||||||
|
|
@ -291,5 +329,6 @@ fun CollectionFolder(
|
||||||
playEnabled = false,
|
playEnabled = false,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -598,10 +598,15 @@ fun NavigationDrawerScope.NavItem(
|
||||||
val useFont = library !is ServerNavDrawerItem || library.type != CollectionType.LIVETV
|
val useFont = library !is ServerNavDrawerItem || library.type != CollectionType.LIVETV
|
||||||
val icon =
|
val icon =
|
||||||
when (library) {
|
when (library) {
|
||||||
NavDrawerItem.Favorites -> R.string.fa_heart
|
NavDrawerItem.Favorites -> {
|
||||||
NavDrawerItem.More -> R.string.fa_ellipsis
|
R.string.fa_heart
|
||||||
|
}
|
||||||
|
|
||||||
is ServerNavDrawerItem ->
|
NavDrawerItem.More -> {
|
||||||
|
R.string.fa_ellipsis
|
||||||
|
}
|
||||||
|
|
||||||
|
is ServerNavDrawerItem -> {
|
||||||
when (library.type) {
|
when (library.type) {
|
||||||
CollectionType.MOVIES -> R.string.fa_film
|
CollectionType.MOVIES -> R.string.fa_film
|
||||||
CollectionType.TVSHOWS -> R.string.fa_tv
|
CollectionType.TVSHOWS -> R.string.fa_tv
|
||||||
|
|
@ -612,6 +617,7 @@ fun NavigationDrawerScope.NavItem(
|
||||||
CollectionType.PLAYLISTS -> R.string.fa_list_ul
|
CollectionType.PLAYLISTS -> R.string.fa_list_ul
|
||||||
else -> R.string.fa_film
|
else -> R.string.fa_film
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
|
|
@ -686,7 +692,7 @@ fun navItemColor(
|
||||||
else -> .2f
|
else -> .2f
|
||||||
}
|
}
|
||||||
return when {
|
return when {
|
||||||
selected && focused ->
|
selected && focused -> {
|
||||||
when (theme) {
|
when (theme) {
|
||||||
AppThemeColors.UNRECOGNIZED,
|
AppThemeColors.UNRECOGNIZED,
|
||||||
AppThemeColors.PURPLE,
|
AppThemeColors.PURPLE,
|
||||||
|
|
@ -699,10 +705,19 @@ fun navItemColor(
|
||||||
AppThemeColors.OLED_BLACK,
|
AppThemeColors.OLED_BLACK,
|
||||||
-> MaterialTheme.colorScheme.primary
|
-> MaterialTheme.colorScheme.primary
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
selected -> MaterialTheme.colorScheme.border
|
selected -> {
|
||||||
focused -> LocalContentColor.current
|
MaterialTheme.colorScheme.border
|
||||||
else -> MaterialTheme.colorScheme.onSurface
|
}
|
||||||
|
|
||||||
|
focused -> {
|
||||||
|
LocalContentColor.current
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
}.copy(alpha = alpha)
|
}.copy(alpha = alpha)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,9 @@ fun DownloadSubtitlesContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is SubtitleSearch.Error -> Wrapper { ErrorMessage(null, s.ex, modifier) }
|
is SubtitleSearch.Error -> {
|
||||||
|
Wrapper { ErrorMessage(null, s.ex, modifier) }
|
||||||
|
}
|
||||||
|
|
||||||
is SubtitleSearch.Success -> {
|
is SubtitleSearch.Success -> {
|
||||||
val dialogItems = convertRemoteSubtitles(s.options, onClickDownload)
|
val dialogItems = convertRemoteSubtitles(s.options, onClickDownload)
|
||||||
|
|
@ -133,9 +135,11 @@ fun DownloadSubtitlesContent(
|
||||||
) {
|
) {
|
||||||
itemsIndexed(dialogItems) { index, item ->
|
itemsIndexed(dialogItems) { index, item ->
|
||||||
when (item) {
|
when (item) {
|
||||||
is DialogItemDivider -> HorizontalDivider(Modifier.height(16.dp))
|
is DialogItemDivider -> {
|
||||||
|
HorizontalDivider(Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
|
||||||
is DialogItem ->
|
is DialogItem -> {
|
||||||
ListItem(
|
ListItem(
|
||||||
selected = false,
|
selected = false,
|
||||||
enabled = item.enabled,
|
enabled = item.enabled,
|
||||||
|
|
@ -153,6 +157,7 @@ fun DownloadSubtitlesContent(
|
||||||
Modifier.focusRequester(focusRequester),
|
Modifier.focusRequester(focusRequester),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ fun PlaybackDialog(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaybackDialogType.AUDIO ->
|
PlaybackDialogType.AUDIO -> {
|
||||||
BottomDialog(
|
BottomDialog(
|
||||||
choices = settings.audioStreams.map { it.displayName },
|
choices = settings.audioStreams.map { it.displayName },
|
||||||
currentChoice = settings.audioStreams.indexOfFirstOrNull { it.index == settings.audioIndex },
|
currentChoice = settings.audioStreams.indexOfFirstOrNull { it.index == settings.audioIndex },
|
||||||
|
|
@ -134,8 +134,9 @@ fun PlaybackDialog(
|
||||||
},
|
},
|
||||||
gravity = Gravity.END,
|
gravity = Gravity.END,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PlaybackDialogType.PLAYBACK_SPEED ->
|
PlaybackDialogType.PLAYBACK_SPEED -> {
|
||||||
BottomDialog(
|
BottomDialog(
|
||||||
choices = playbackSpeedOptions,
|
choices = playbackSpeedOptions,
|
||||||
currentChoice = playbackSpeedOptions.indexOf(settings.playbackSpeed.toString()),
|
currentChoice = playbackSpeedOptions.indexOf(settings.playbackSpeed.toString()),
|
||||||
|
|
@ -152,8 +153,9 @@ fun PlaybackDialog(
|
||||||
},
|
},
|
||||||
gravity = Gravity.END,
|
gravity = Gravity.END,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PlaybackDialogType.VIDEO_SCALE ->
|
PlaybackDialogType.VIDEO_SCALE -> {
|
||||||
BottomDialog(
|
BottomDialog(
|
||||||
choices = playbackScaleOptions.values.toList(),
|
choices = playbackScaleOptions.values.toList(),
|
||||||
currentChoice = playbackScaleOptions.keys.toList().indexOf(settings.contentScale),
|
currentChoice = playbackScaleOptions.keys.toList().indexOf(settings.contentScale),
|
||||||
|
|
@ -170,5 +172,6 @@ fun PlaybackDialog(
|
||||||
},
|
},
|
||||||
gravity = Gravity.END,
|
gravity = Gravity.END,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,14 +94,29 @@ class PlaybackKeyHandler(
|
||||||
updateSkipIndicator(-seekBack.inWholeMilliseconds)
|
updateSkipIndicator(-seekBack.inWholeMilliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
Key.MediaNext -> if (player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)) player.seekToNext()
|
Key.MediaNext -> {
|
||||||
Key.MediaPrevious -> if (player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS)) player.seekToPrevious()
|
if (player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)) player.seekToNext()
|
||||||
|
}
|
||||||
|
|
||||||
Key.Captions -> onPlaybackDialogTypeClick.invoke(PlaybackDialogType.CAPTIONS)
|
Key.MediaPrevious -> {
|
||||||
Key.MediaAudioTrack -> onPlaybackDialogTypeClick.invoke(PlaybackDialogType.AUDIO)
|
if (player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS)) player.seekToPrevious()
|
||||||
Key.MediaStop -> onStop.invoke()
|
}
|
||||||
|
|
||||||
else -> result = false
|
Key.Captions -> {
|
||||||
|
onPlaybackDialogTypeClick.invoke(PlaybackDialogType.CAPTIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
Key.MediaAudioTrack -> {
|
||||||
|
onPlaybackDialogTypeClick.invoke(PlaybackDialogType.AUDIO)
|
||||||
|
}
|
||||||
|
|
||||||
|
Key.MediaStop -> {
|
||||||
|
onStop.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
result = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isEnterKey(it) && !controllerViewState.controlsVisible) {
|
} else if (isEnterKey(it) && !controllerViewState.controlsVisible) {
|
||||||
controllerViewState.showControls()
|
controllerViewState.showControls()
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ fun PlaybackOverlay(
|
||||||
// Don't use key events because this control has vertical items so up/down is tough to manage
|
// Don't use key events because this control has vertical items so up/down is tough to manage
|
||||||
)
|
)
|
||||||
when (nextState) {
|
when (nextState) {
|
||||||
OverlayViewState.CHAPTERS ->
|
OverlayViewState.CHAPTERS -> {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.chapters),
|
text = stringResource(R.string.chapters),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
|
@ -194,8 +194,9 @@ fun PlaybackOverlay(
|
||||||
if (it.isFocused) state = nextState
|
if (it.isFocused) state = nextState
|
||||||
}.focusable(),
|
}.focusable(),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
OverlayViewState.QUEUE ->
|
OverlayViewState.QUEUE -> {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.queue),
|
text = stringResource(R.string.queue),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
|
@ -206,8 +207,11 @@ fun PlaybackOverlay(
|
||||||
if (it.isFocused) state = nextState
|
if (it.isFocused) state = nextState
|
||||||
}.focusable(),
|
}.focusable(),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> Spacer(Modifier.height(32.dp))
|
else -> {
|
||||||
|
Spacer(Modifier.height(32.dp))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,15 @@ fun PlaybackPage(
|
||||||
|
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
when (val st = loading) {
|
when (val st = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(st, modifier)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(st, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
-> LoadingPage(modifier.background(Color.Black))
|
-> {
|
||||||
|
LoadingPage(modifier.background(Color.Black))
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
val prefs = preferences.appPreferences.playbackPreferences
|
val prefs = preferences.appPreferences.playbackPreferences
|
||||||
|
|
@ -177,7 +182,7 @@ fun PlaybackPage(
|
||||||
}
|
}
|
||||||
var skipPosition by remember { mutableLongStateOf(0L) }
|
var skipPosition by remember { mutableLongStateOf(0L) }
|
||||||
val updateSkipIndicator = { delta: Long ->
|
val updateSkipIndicator = { delta: Long ->
|
||||||
if (skipIndicatorDuration > 0 && delta < 0 || skipIndicatorDuration < 0 && delta > 0) {
|
if ((skipIndicatorDuration > 0 && delta < 0) || (skipIndicatorDuration < 0 && delta > 0)) {
|
||||||
skipIndicatorDuration = 0
|
skipIndicatorDuration = 0
|
||||||
}
|
}
|
||||||
skipIndicatorDuration += delta
|
skipIndicatorDuration += delta
|
||||||
|
|
@ -216,8 +221,14 @@ fun PlaybackPage(
|
||||||
showDebugInfo = !showDebugInfo
|
showDebugInfo = !showDebugInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaybackAction.ShowPlaylist -> TODO()
|
PlaybackAction.ShowPlaylist -> {
|
||||||
PlaybackAction.ShowVideoFilterDialog -> TODO()
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaybackAction.ShowVideoFilterDialog -> {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
is PlaybackAction.ToggleAudio -> {
|
is PlaybackAction.ToggleAudio -> {
|
||||||
viewModel.changeAudioStream(it.index)
|
viewModel.changeAudioStream(it.index)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,9 @@ class PlaybackViewModel
|
||||||
d.itemId
|
d.itemId
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Destination not supported: $destination")
|
else -> {
|
||||||
|
throw IllegalArgumentException("Destination not supported: $destination")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.itemId = itemId
|
this.itemId = itemId
|
||||||
viewModelScope.launch(
|
viewModelScope.launch(
|
||||||
|
|
@ -554,8 +556,11 @@ class PlaybackViewModel
|
||||||
when {
|
when {
|
||||||
// playerBackend == PlayerBackend.MPV -> PlayMethod.DIRECT_PLAY
|
// playerBackend == PlayerBackend.MPV -> PlayMethod.DIRECT_PLAY
|
||||||
source.supportsDirectPlay -> PlayMethod.DIRECT_PLAY
|
source.supportsDirectPlay -> PlayMethod.DIRECT_PLAY
|
||||||
|
|
||||||
source.supportsDirectStream -> PlayMethod.DIRECT_STREAM
|
source.supportsDirectStream -> PlayMethod.DIRECT_STREAM
|
||||||
|
|
||||||
source.supportsTranscoding -> PlayMethod.TRANSCODE
|
source.supportsTranscoding -> PlayMethod.TRANSCODE
|
||||||
|
|
||||||
else -> throw Exception("No supported playback method")
|
else -> throw Exception("No supported playback method")
|
||||||
}
|
}
|
||||||
Timber.v("Playback decision: $transcodeType")
|
Timber.v("Playback decision: $transcodeType")
|
||||||
|
|
@ -921,13 +926,14 @@ class PlaybackViewModel
|
||||||
viewModelScope.launch(Dispatchers.Main + ExceptionHandler()) {
|
viewModelScope.launch(Dispatchers.Main + ExceptionHandler()) {
|
||||||
currentPlayback.value?.let {
|
currentPlayback.value?.let {
|
||||||
when (it.playMethod) {
|
when (it.playMethod) {
|
||||||
PlayMethod.TRANSCODE ->
|
PlayMethod.TRANSCODE -> {
|
||||||
loading.setValueOnMain(
|
loading.setValueOnMain(
|
||||||
LoadingState.Error(
|
LoadingState.Error(
|
||||||
"Error during playback",
|
"Error during playback",
|
||||||
error,
|
error,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PlayMethod.DIRECT_STREAM, PlayMethod.DIRECT_PLAY -> {
|
PlayMethod.DIRECT_STREAM, PlayMethod.DIRECT_PLAY -> {
|
||||||
Timber.w("Playback error during ${it.playMethod}, falling back to transcoding")
|
Timber.w("Playback error during ${it.playMethod}, falling back to transcoding")
|
||||||
|
|
@ -968,28 +974,45 @@ class PlaybackViewModel
|
||||||
navigationManager.goBack()
|
navigationManager.goBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaystateCommand.PAUSE -> player.pause()
|
PlaystateCommand.PAUSE -> {
|
||||||
PlaystateCommand.UNPAUSE -> player.play()
|
player.pause()
|
||||||
PlaystateCommand.NEXT_TRACK -> playNextUp()
|
}
|
||||||
PlaystateCommand.PREVIOUS_TRACK -> playPrevious()
|
|
||||||
PlaystateCommand.SEEK ->
|
PlaystateCommand.UNPAUSE -> {
|
||||||
|
player.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaystateCommand.NEXT_TRACK -> {
|
||||||
|
playNextUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaystateCommand.PREVIOUS_TRACK -> {
|
||||||
|
playPrevious()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaystateCommand.SEEK -> {
|
||||||
it.seekPositionTicks?.ticks?.let {
|
it.seekPositionTicks?.ticks?.let {
|
||||||
player.seekTo(
|
player.seekTo(
|
||||||
it.inWholeMilliseconds,
|
it.inWholeMilliseconds,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PlaystateCommand.REWIND ->
|
PlaystateCommand.REWIND -> {
|
||||||
player.seekBack(
|
player.seekBack(
|
||||||
preferences.appPreferences.playbackPreferences.skipBackMs.milliseconds,
|
preferences.appPreferences.playbackPreferences.skipBackMs.milliseconds,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PlaystateCommand.FAST_FORWARD ->
|
PlaystateCommand.FAST_FORWARD -> {
|
||||||
player.seekForward(
|
player.seekForward(
|
||||||
preferences.appPreferences.playbackPreferences.skipForwardMs.milliseconds,
|
preferences.appPreferences.playbackPreferences.skipForwardMs.milliseconds,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PlaystateCommand.PLAY_PAUSE -> if (player.isPlaying) player.pause() else player.play()
|
PlaystateCommand.PLAY_PAUSE -> {
|
||||||
|
if (player.isPlaying) player.pause() else player.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,8 +153,14 @@ object TrackSelectionUtils {
|
||||||
// TODO MPV could use literal indexes because they are stored in the track format ID
|
// TODO MPV could use literal indexes because they are stored in the track format ID
|
||||||
PlayerBackend.MPV -> {
|
PlayerBackend.MPV -> {
|
||||||
when (type) {
|
when (type) {
|
||||||
MediaStreamType.VIDEO -> serverIndex - externalSubtitleCount + 1
|
MediaStreamType.VIDEO -> {
|
||||||
MediaStreamType.AUDIO -> serverIndex - externalSubtitleCount - videoStreamCount + 1
|
serverIndex - externalSubtitleCount + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaStreamType.AUDIO -> {
|
||||||
|
serverIndex - externalSubtitleCount - videoStreamCount + 1
|
||||||
|
}
|
||||||
|
|
||||||
MediaStreamType.SUBTITLE -> {
|
MediaStreamType.SUBTITLE -> {
|
||||||
if (subtitleIsExternal) {
|
if (subtitleIsExternal) {
|
||||||
serverIndex + embeddedSubtitleCount + 1
|
serverIndex + embeddedSubtitleCount + 1
|
||||||
|
|
@ -162,7 +168,10 @@ object TrackSelectionUtils {
|
||||||
serverIndex - externalSubtitleCount - videoStreamCount - audioStreamCount + 1
|
serverIndex - externalSubtitleCount - videoStreamCount - audioStreamCount + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw UnsupportedOperationException("Cannot calculate index for $type")
|
|
||||||
|
else -> {
|
||||||
|
throw UnsupportedOperationException("Cannot calculate index for $type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ fun <T> ComposablePreference(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is AppDestinationPreference ->
|
is AppDestinationPreference -> {
|
||||||
ClickPreference(
|
ClickPreference(
|
||||||
title = title,
|
title = title,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -112,8 +112,9 @@ fun <T> ComposablePreference(
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is AppClickablePreference ->
|
is AppClickablePreference -> {
|
||||||
ClickPreference(
|
ClickPreference(
|
||||||
title = title,
|
title = title,
|
||||||
onClick = { onClickPreference.invoke(preference) },
|
onClick = { onClickPreference.invoke(preference) },
|
||||||
|
|
@ -122,8 +123,9 @@ fun <T> ComposablePreference(
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is AppSwitchPreference ->
|
is AppSwitchPreference -> {
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
title = title,
|
title = title,
|
||||||
value = value as Boolean,
|
value = value as Boolean,
|
||||||
|
|
@ -132,8 +134,9 @@ fun <T> ComposablePreference(
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is AppStringPreference ->
|
is AppStringPreference -> {
|
||||||
ClickPreference(
|
ClickPreference(
|
||||||
title = title,
|
title = title,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -164,6 +167,7 @@ fun <T> ComposablePreference(
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is AppChoicePreference -> {
|
is AppChoicePreference -> {
|
||||||
val values = stringArrayResource(preference.displayValues).toList()
|
val values = stringArrayResource(preference.displayValues).toList()
|
||||||
|
|
|
||||||
|
|
@ -456,7 +456,7 @@ fun PreferencesPage(
|
||||||
PreferenceScreenOption.BASIC,
|
PreferenceScreenOption.BASIC,
|
||||||
PreferenceScreenOption.ADVANCED,
|
PreferenceScreenOption.ADVANCED,
|
||||||
PreferenceScreenOption.USER_INTERFACE,
|
PreferenceScreenOption.USER_INTERFACE,
|
||||||
->
|
-> {
|
||||||
PreferencesContent(
|
PreferencesContent(
|
||||||
initialPreferences,
|
initialPreferences,
|
||||||
preferenceScreenOption,
|
preferenceScreenOption,
|
||||||
|
|
@ -465,11 +465,13 @@ fun PreferencesPage(
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.align(Alignment.TopEnd),
|
.align(Alignment.TopEnd),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PreferenceScreenOption.SUBTITLES ->
|
PreferenceScreenOption.SUBTITLES -> {
|
||||||
SubtitleStylePage(
|
SubtitleStylePage(
|
||||||
initialPreferences,
|
initialPreferences,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,17 +71,19 @@ fun SwitchColors(): SwitchColors {
|
||||||
AppThemeColors.UNRECOGNIZED,
|
AppThemeColors.UNRECOGNIZED,
|
||||||
AppThemeColors.PURPLE,
|
AppThemeColors.PURPLE,
|
||||||
AppThemeColors.BLUE,
|
AppThemeColors.BLUE,
|
||||||
->
|
-> {
|
||||||
SwitchDefaults.colors()
|
SwitchDefaults.colors()
|
||||||
|
}
|
||||||
|
|
||||||
AppThemeColors.GREEN,
|
AppThemeColors.GREEN,
|
||||||
AppThemeColors.ORANGE,
|
AppThemeColors.ORANGE,
|
||||||
AppThemeColors.BOLD_BLUE,
|
AppThemeColors.BOLD_BLUE,
|
||||||
AppThemeColors.OLED_BLACK,
|
AppThemeColors.OLED_BLACK,
|
||||||
->
|
-> {
|
||||||
SwitchDefaults.colors(
|
SwitchDefaults.colors(
|
||||||
checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
|
checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
uncheckedThumbColor = MaterialTheme.colorScheme.onPrimary,
|
uncheckedThumbColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ object SubtitleSettings {
|
||||||
val bg = combine(backgroundColor, backgroundOpacity)
|
val bg = combine(backgroundColor, backgroundOpacity)
|
||||||
return CaptionStyleCompat(
|
return CaptionStyleCompat(
|
||||||
combine(fontColor, fontOpacity),
|
combine(fontColor, fontOpacity),
|
||||||
if (backgroundStyle == BackgroundStyle.BG_WRAP)bg else 0,
|
if (backgroundStyle == BackgroundStyle.BG_WRAP) bg else 0,
|
||||||
if (backgroundStyle == BackgroundStyle.BG_BOXED) bg else 0,
|
if (backgroundStyle == BackgroundStyle.BG_BOXED) bg else 0,
|
||||||
when (edgeStyle) {
|
when (edgeStyle) {
|
||||||
EdgeStyle.EDGE_NONE, EdgeStyle.UNRECOGNIZED -> CaptionStyleCompat.EDGE_TYPE_NONE
|
EdgeStyle.EDGE_NONE, EdgeStyle.UNRECOGNIZED -> CaptionStyleCompat.EDGE_TYPE_NONE
|
||||||
|
|
@ -355,6 +355,7 @@ object SubtitleSettings {
|
||||||
-> "outline-and-shadow"
|
-> "outline-and-shadow"
|
||||||
|
|
||||||
BackgroundStyle.BG_WRAP -> "opaque-box"
|
BackgroundStyle.BG_WRAP -> "opaque-box"
|
||||||
|
|
||||||
BackgroundStyle.BG_BOXED -> "background-box"
|
BackgroundStyle.BG_BOXED -> "background-box"
|
||||||
}
|
}
|
||||||
MPVLib.setPropertyString("sub-border-style", borderStyle)
|
MPVLib.setPropertyString("sub-border-style", borderStyle)
|
||||||
|
|
|
||||||
|
|
@ -174,10 +174,15 @@ fun InstallUpdatePage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state, modifier)
|
is LoadingState.Error -> {
|
||||||
|
ErrorMessage(state, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Loading,
|
LoadingState.Loading,
|
||||||
LoadingState.Pending,
|
LoadingState.Pending,
|
||||||
-> LoadingPage(modifier)
|
-> {
|
||||||
|
LoadingPage(modifier)
|
||||||
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
release?.let {
|
release?.let {
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,13 @@ fun ServerList(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
when (status) {
|
when (status) {
|
||||||
is ServerConnectionStatus.Success -> {}
|
is ServerConnectionStatus.Success -> {}
|
||||||
|
|
||||||
ServerConnectionStatus.Pending -> {
|
ServerConnectionStatus.Pending -> {
|
||||||
CircularProgress(
|
CircularProgress(
|
||||||
Modifier.size(IconButtonDefaults.MediumIconSize),
|
Modifier.size(IconButtonDefaults.MediumIconSize),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServerConnectionStatus.Error -> {
|
is ServerConnectionStatus.Error -> {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Warning,
|
imageVector = Icons.Default.Warning,
|
||||||
|
|
@ -88,9 +90,15 @@ fun ServerList(
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
when (status) {
|
when (status) {
|
||||||
is ServerConnectionStatus.Success -> onSwitchServer.invoke(server)
|
is ServerConnectionStatus.Success -> {
|
||||||
|
onSwitchServer.invoke(server)
|
||||||
|
}
|
||||||
|
|
||||||
ServerConnectionStatus.Pending -> {}
|
ServerConnectionStatus.Pending -> {}
|
||||||
is ServerConnectionStatus.Error -> onTestServer.invoke(server)
|
|
||||||
|
is ServerConnectionStatus.Error -> {
|
||||||
|
onTestServer.invoke(server)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,42 @@ data class TrackSupport(
|
||||||
} else {
|
} else {
|
||||||
val type =
|
val type =
|
||||||
when (codecs) {
|
when (codecs) {
|
||||||
MimeTypes.TEXT_VTT -> "vtt"
|
MimeTypes.TEXT_VTT -> {
|
||||||
MimeTypes.APPLICATION_VOBSUB -> "vobsub"
|
"vtt"
|
||||||
MimeTypes.APPLICATION_SUBRIP -> "srt"
|
}
|
||||||
MimeTypes.TEXT_SSA -> "ssa"
|
|
||||||
MimeTypes.APPLICATION_PGS -> "pgs"
|
MimeTypes.APPLICATION_VOBSUB -> {
|
||||||
MimeTypes.APPLICATION_DVBSUBS -> "dvd"
|
"vobsub"
|
||||||
MimeTypes.APPLICATION_TTML -> "ttml"
|
}
|
||||||
MimeTypes.TEXT_UNKNOWN -> "unknown"
|
|
||||||
null -> "unknown"
|
MimeTypes.APPLICATION_SUBRIP -> {
|
||||||
|
"srt"
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeTypes.TEXT_SSA -> {
|
||||||
|
"ssa"
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeTypes.APPLICATION_PGS -> {
|
||||||
|
"pgs"
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeTypes.APPLICATION_DVBSUBS -> {
|
||||||
|
"dvd"
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeTypes.APPLICATION_TTML -> {
|
||||||
|
"ttml"
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeTypes.TEXT_UNKNOWN -> {
|
||||||
|
"unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
null -> {
|
||||||
|
"unknown"
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val split = codecs.split("/")
|
val split = codecs.split("/")
|
||||||
if (split.size > 1) split[1] else codecs
|
if (split.size > 1) split[1] else codecs
|
||||||
|
|
|
||||||
|
|
@ -70,13 +70,19 @@ fun createDeviceProfile(
|
||||||
) = buildDeviceProfile {
|
) = buildDeviceProfile {
|
||||||
val allowedAudioCodecs =
|
val allowedAudioCodecs =
|
||||||
when {
|
when {
|
||||||
downMixAudio -> downmixSupportedAudioCodecs
|
downMixAudio -> {
|
||||||
!isAC3Enabled ->
|
downmixSupportedAudioCodecs
|
||||||
|
}
|
||||||
|
|
||||||
|
!isAC3Enabled -> {
|
||||||
supportedAudioCodecs
|
supportedAudioCodecs
|
||||||
.filterNot { it == Codec.Audio.EAC3 || it == Codec.Audio.AC3 }
|
.filterNot { it == Codec.Audio.EAC3 || it == Codec.Audio.AC3 }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
else -> supportedAudioCodecs
|
else -> {
|
||||||
|
supportedAudioCodecs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val supportsHevc = mediaTest.supportsHevc()
|
val supportsHevc = mediaTest.supportsHevc()
|
||||||
|
|
@ -193,8 +199,11 @@ fun createDeviceProfile(
|
||||||
|
|
||||||
conditions {
|
conditions {
|
||||||
when {
|
when {
|
||||||
!supportsAVC -> ProfileConditionValue.VIDEO_PROFILE equals "none"
|
!supportsAVC -> {
|
||||||
else ->
|
ProfileConditionValue.VIDEO_PROFILE equals "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
ProfileConditionValue.VIDEO_PROFILE inCollection
|
ProfileConditionValue.VIDEO_PROFILE inCollection
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
"high",
|
"high",
|
||||||
|
|
@ -203,6 +212,7 @@ fun createDeviceProfile(
|
||||||
"constrained baseline",
|
"constrained baseline",
|
||||||
if (supportsAVCHigh10) "main 10" else null,
|
if (supportsAVCHigh10) "main 10" else null,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,13 +286,17 @@ fun createDeviceProfile(
|
||||||
|
|
||||||
conditions {
|
conditions {
|
||||||
when {
|
when {
|
||||||
!supportsHevc -> ProfileConditionValue.VIDEO_PROFILE equals "none"
|
!supportsHevc -> {
|
||||||
else ->
|
ProfileConditionValue.VIDEO_PROFILE equals "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
ProfileConditionValue.VIDEO_PROFILE inCollection
|
ProfileConditionValue.VIDEO_PROFILE inCollection
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
"main",
|
"main",
|
||||||
if (supportsHevcMain10) "main 10" else null,
|
if (supportsHevcMain10) "main 10" else null,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue