mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
WIP live tv home rows
This commit is contained in:
parent
ccb79257fd
commit
9cd3e7df4e
3 changed files with 193 additions and 12 deletions
|
|
@ -114,6 +114,28 @@ sealed interface HomeRowConfig {
|
||||||
override fun updateViewOptions(viewOptions: HomeRowViewOptions): Favorite = this.copy(viewOptions = viewOptions)
|
override fun updateViewOptions(viewOptions: HomeRowViewOptions): Favorite = this.copy(viewOptions = viewOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("Recordings")
|
||||||
|
data class Recordings(
|
||||||
|
override val viewOptions: HomeRowViewOptions = HomeRowViewOptions(),
|
||||||
|
) : HomeRowConfig {
|
||||||
|
override fun updateViewOptions(viewOptions: HomeRowViewOptions): Recordings = this.copy(viewOptions = viewOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TvPrograms")
|
||||||
|
data class TvPrograms(
|
||||||
|
override val viewOptions: HomeRowViewOptions = HomeRowViewOptions(),
|
||||||
|
) : HomeRowConfig {
|
||||||
|
override fun updateViewOptions(viewOptions: HomeRowViewOptions): TvPrograms = this.copy(viewOptions = viewOptions)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch by parent ID such as a library, collection, or playlist with optional simple sorting
|
* Fetch by parent ID such as a library, collection, or playlist with optional simple sorting
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.github.damontecres.wholphin.data.NavDrawerItemRepository
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.HomePageSettings
|
import com.github.damontecres.wholphin.data.model.HomePageSettings
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
||||||
|
import com.github.damontecres.wholphin.preferences.DefaultUserConfiguration
|
||||||
import com.github.damontecres.wholphin.preferences.HomePagePreferences
|
import com.github.damontecres.wholphin.preferences.HomePagePreferences
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
|
|
@ -18,6 +19,7 @@ import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.GetPersonsHandler
|
import com.github.damontecres.wholphin.util.GetPersonsHandler
|
||||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState.Success
|
import com.github.damontecres.wholphin.util.HomeRowLoadingState.Success
|
||||||
|
import com.github.damontecres.wholphin.util.supportedHomeCollectionTypes
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -31,7 +33,9 @@ import kotlinx.serialization.json.jsonArray
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.extensions.displayPreferencesApi
|
import org.jellyfin.sdk.api.client.extensions.displayPreferencesApi
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||||
import org.jellyfin.sdk.model.UUID
|
import org.jellyfin.sdk.model.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
|
|
@ -42,6 +46,7 @@ import org.jellyfin.sdk.model.api.request.GetGenresRequest
|
||||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||||
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
||||||
import org.jellyfin.sdk.model.api.request.GetPersonsRequest
|
import org.jellyfin.sdk.model.api.request.GetPersonsRequest
|
||||||
|
import org.jellyfin.sdk.model.api.request.GetRecordingsRequest
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -58,10 +63,12 @@ class HomeSettingsService
|
||||||
private val latestNextUpService: LatestNextUpService,
|
private val latestNextUpService: LatestNextUpService,
|
||||||
private val imageUrlService: ImageUrlService,
|
private val imageUrlService: ImageUrlService,
|
||||||
) {
|
) {
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
val jsonParser =
|
val jsonParser =
|
||||||
Json {
|
Json {
|
||||||
isLenient = true
|
isLenient = true
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
|
allowTrailingComma = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentSettings = MutableStateFlow(HomePageResolvedSettings.EMPTY)
|
val currentSettings = MutableStateFlow(HomePageResolvedSettings.EMPTY)
|
||||||
|
|
@ -201,18 +208,18 @@ class HomeSettingsService
|
||||||
Timber.w(ex, "Error loading remote settings")
|
Timber.w(ex, "Error loading remote settings")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val resolvedSettings =
|
val resolvedSettings = parseFromWebConfig(userId)!!
|
||||||
if (settings != null) {
|
if (settings != null) {
|
||||||
Timber.v("Found settings")
|
Timber.v("Found settings")
|
||||||
// Resolve
|
// Resolve
|
||||||
val resolvedRows =
|
val resolvedRows =
|
||||||
settings.rows.mapIndexed { index, config ->
|
settings.rows.mapIndexed { index, config ->
|
||||||
resolve(index, config)
|
resolve(index, config)
|
||||||
}
|
}
|
||||||
HomePageResolvedSettings(resolvedRows)
|
HomePageResolvedSettings(resolvedRows)
|
||||||
} else {
|
} else {
|
||||||
createDefault(userId)
|
createDefault(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSettings.update { resolvedSettings }
|
currentSettings.update { resolvedSettings }
|
||||||
}
|
}
|
||||||
|
|
@ -275,6 +282,112 @@ class HomeSettingsService
|
||||||
return HomePageResolvedSettings(rowConfig)
|
return HomePageResolvedSettings(rowConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun parseFromWebConfig(userId: UUID): HomePageResolvedSettings? {
|
||||||
|
val customPrefs =
|
||||||
|
api.displayPreferencesApi
|
||||||
|
.getDisplayPreferences(
|
||||||
|
displayPreferencesId = "usersettings",
|
||||||
|
userId = userId,
|
||||||
|
client = "emby",
|
||||||
|
).content.customPrefs
|
||||||
|
val userDto by api.userApi.getUserById(userId)
|
||||||
|
val config = userDto.configuration ?: DefaultUserConfiguration
|
||||||
|
val libraries =
|
||||||
|
api.userViewsApi
|
||||||
|
.getUserViews(userId = userId)
|
||||||
|
.content.items
|
||||||
|
.filter {
|
||||||
|
it.collectionType in supportedHomeCollectionTypes &&
|
||||||
|
it.id !in config.latestItemsExcludes
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (customPrefs.isNotEmpty()) {
|
||||||
|
var id = 0
|
||||||
|
val rowConfigs =
|
||||||
|
(0..9)
|
||||||
|
.mapNotNull { idx ->
|
||||||
|
val sectionType =
|
||||||
|
HomeSectionType.fromString(customPrefs["homesection$idx"]?.lowercase())
|
||||||
|
Timber.v(
|
||||||
|
"sectionType=$sectionType, %s",
|
||||||
|
customPrefs["homesection$idx"]?.lowercase(),
|
||||||
|
)
|
||||||
|
val config =
|
||||||
|
when (sectionType) {
|
||||||
|
HomeSectionType.ACTIVE_RECORDINGS -> {
|
||||||
|
// GetRecordingsRequest
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeSectionType.RESUME -> {
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
id = id++,
|
||||||
|
title = context.getString(R.string.continue_watching),
|
||||||
|
config = HomeRowConfig.ContinueWatching(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeSectionType.NEXT_UP -> {
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
id = id++,
|
||||||
|
title = context.getString(R.string.next_up),
|
||||||
|
config = HomeRowConfig.NextUp(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeSectionType.LIVE_TV -> {
|
||||||
|
if (userDto.policy?.enableLiveTvAccess == true) {
|
||||||
|
// GetRecommendedProgramsRequest
|
||||||
|
TODO()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeSectionType.LATEST_MEDIA -> {
|
||||||
|
// Handled below
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsupported
|
||||||
|
HomeSectionType.RESUME_AUDIO,
|
||||||
|
HomeSectionType.RESUME_BOOK,
|
||||||
|
-> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeSectionType.SMALL_LIBRARY_TILES,
|
||||||
|
HomeSectionType.LIBRARY_BUTTONS,
|
||||||
|
HomeSectionType.NONE,
|
||||||
|
null,
|
||||||
|
-> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sectionType == HomeSectionType.LATEST_MEDIA) {
|
||||||
|
libraries.map {
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
id = id++,
|
||||||
|
title =
|
||||||
|
context.getString(
|
||||||
|
R.string.recently_added_in,
|
||||||
|
it.name ?: "",
|
||||||
|
),
|
||||||
|
config = HomeRowConfig.RecentlyAdded(it.id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (config != null) {
|
||||||
|
listOf(config)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.flatten()
|
||||||
|
HomePageResolvedSettings(rowConfigs)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a [HomeRowConfig] into [HomeRowConfigDisplay] for UI purposes
|
* Converts a [HomeRowConfig] into [HomeRowConfigDisplay] for UI purposes
|
||||||
*/
|
*/
|
||||||
|
|
@ -363,6 +476,14 @@ class HomeSettingsService
|
||||||
val name = context.getString(R.string.favorites) // TODO "Favorite <type>"
|
val name = context.getString(R.string.favorites) // TODO "Favorite <type>"
|
||||||
HomeRowConfigDisplay(id, name, config)
|
HomeRowConfigDisplay(id, name, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.Recordings -> {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.TvPrograms -> {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -630,6 +751,15 @@ class HomeSettingsService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.Recordings -> {
|
||||||
|
GetRecordingsRequest()
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.TvPrograms -> {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
@ -659,3 +789,24 @@ data class HomePageResolvedSettings(
|
||||||
val EMPTY = HomePageResolvedSettings(listOf())
|
val EMPTY = HomePageResolvedSettings(listOf())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/jellyfin/jellyfin/blob/v10.11.6/src/Jellyfin.Database/Jellyfin.Database.Implementations/Enums/HomeSectionType.cs
|
||||||
|
enum class HomeSectionType(
|
||||||
|
val serialName: String,
|
||||||
|
) {
|
||||||
|
NONE("none"),
|
||||||
|
SMALL_LIBRARY_TILES("smalllibrarytitles"),
|
||||||
|
LIBRARY_BUTTONS("librarybuttons"),
|
||||||
|
ACTIVE_RECORDINGS("activerecordings"),
|
||||||
|
RESUME("resume"),
|
||||||
|
RESUME_AUDIO("resumeaudio"),
|
||||||
|
LATEST_MEDIA("latestmedia"),
|
||||||
|
NEXT_UP("nextup"),
|
||||||
|
LIVE_TV("livetv"),
|
||||||
|
RESUME_BOOK("resumebook"),
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromString(homeKey: String?) = homeKey?.let { entries.firstOrNull { it.serialName == homeKey } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,14 @@ val supportedCollectionTypes =
|
||||||
null, // Mixed
|
null, // Mixed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val supportedHomeCollectionTypes =
|
||||||
|
setOf(
|
||||||
|
CollectionType.MOVIES,
|
||||||
|
CollectionType.TVSHOWS,
|
||||||
|
CollectionType.HOMEVIDEOS,
|
||||||
|
null, // Mixed
|
||||||
|
)
|
||||||
|
|
||||||
val supportedPlayableTypes =
|
val supportedPlayableTypes =
|
||||||
setOf(
|
setOf(
|
||||||
BaseItemKind.MOVIE,
|
BaseItemKind.MOVIE,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue