Add option for suggestions row for supported library types

This commit is contained in:
Damontecres 2026-02-04 21:16:24 -05:00
parent 2d77249099
commit b31c0044fc
No known key found for this signature in database
6 changed files with 99 additions and 9 deletions

View file

@ -136,6 +136,18 @@ sealed interface HomeRowConfig {
override fun updateViewOptions(viewOptions: HomeRowViewOptions): TvPrograms = this.copy(viewOptions = viewOptions)
}
/**
* Fetch suggestions from [com.github.damontecres.wholphin.services.SuggestionService] for the given parent ID
*/
@Serializable
@SerialName("Suggestions")
data class Suggestions(
val parentId: UUID,
override val viewOptions: HomeRowViewOptions = HomeRowViewOptions(),
) : HomeRowConfig {
override fun updateViewOptions(viewOptions: HomeRowViewOptions): Suggestions = this.copy(viewOptions = viewOptions)
}
/**
* Fetch by parent ID such as a library, collection, or playlist with optional simple sorting
*/

View file

@ -23,6 +23,7 @@ import com.github.damontecres.wholphin.util.supportedHomeCollectionTypes
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.update
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
@ -64,6 +65,7 @@ class HomeSettingsService
private val navDrawerItemRepository: NavDrawerItemRepository,
private val latestNextUpService: LatestNextUpService,
private val imageUrlService: ImageUrlService,
private val suggestionService: SuggestionService,
) {
@OptIn(ExperimentalSerializationApi::class)
val jsonParser =
@ -504,6 +506,18 @@ class HomeSettingsService
config,
)
}
is HomeRowConfig.Suggestions -> {
val name =
api.userLibraryApi
.getItem(itemId = config.parentId)
.content.name ?: ""
HomeRowConfigDisplay(
id = id,
title = context.getString(R.string.suggestions_for, name),
config,
)
}
}
/**
@ -829,6 +843,39 @@ class HomeSettingsService
)
}
}
is HomeRowConfig.Suggestions -> {
val library =
api.userLibraryApi
.getItem(itemId = row.parentId)
.content
val title = context.getString(R.string.suggestions_for, library.name ?: "")
val itemKind = SuggestionsWorker.getTypeForCollection(library.collectionType)
val suggestions =
itemKind?.let {
suggestionService
.getSuggestionsFlow(row.parentId, itemKind)
.firstOrNull()
}
if (suggestions != null && suggestions is SuggestionsResource.Success) {
Success(
title,
suggestions.items,
row.viewOptions,
)
} else if (suggestions is SuggestionsResource.Empty) {
Success(
title,
listOf(),
row.viewOptions,
)
} else {
HomeRowLoadingState.Error(
title,
message = "Unsupported type ${library.collectionType}",
)
}
}
}
companion object {

View file

@ -82,11 +82,8 @@ class SuggestionsWorker
views
.mapNotNull { view ->
val itemKind =
when (view.collectionType) {
CollectionType.MOVIES -> BaseItemKind.MOVIE
CollectionType.TVSHOWS -> BaseItemKind.SERIES
else -> return@mapNotNull null
}
getTypeForCollection(view.collectionType)
?: return@mapNotNull null
async(Dispatchers.IO) {
runCatching {
Timber.v("Fetching suggestions for view %s", view.id)
@ -226,5 +223,12 @@ class SuggestionsWorker
const val PARAM_USER_ID = "userId"
const val PARAM_SERVER_ID = "serverId"
private const val FRESH_CONTENT_RATIO = 0.4
fun getTypeForCollection(collectionType: CollectionType?): BaseItemKind? =
when (collectionType) {
CollectionType.MOVIES -> BaseItemKind.MOVIE
CollectionType.TVSHOWS -> BaseItemKind.SERIES
else -> null
}
}
}

View file

@ -20,6 +20,7 @@ import androidx.compose.ui.unit.dp
import androidx.tv.material3.ListItem
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.services.SuggestionsWorker
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.tryRequestFocus
@ -30,6 +31,7 @@ fun HomeLibraryRowTypeList(
modifier: Modifier,
firstFocus: FocusRequester = remember { FocusRequester() },
) {
val items = remember(library) { getSupportedRowTypes(library) }
LaunchedEffect(Unit) { firstFocus.tryRequestFocus() }
Column(modifier = modifier) {
TitleText(stringResource(R.string.add_row_for, library.name))
@ -41,7 +43,7 @@ fun HomeLibraryRowTypeList(
.fillMaxHeight()
.focusRestorer(firstFocus),
) {
itemsIndexed(LibraryRowType.entries) { index, rowType ->
itemsIndexed(items) { index, rowType ->
ListItem(
selected = false,
headlineContent = {
@ -60,10 +62,20 @@ fun HomeLibraryRowTypeList(
}
}
fun getSupportedRowTypes(library: Library): List<LibraryRowType> {
val itemKind = SuggestionsWorker.getTypeForCollection(library.collectionType)
return if (itemKind != null) {
LibraryRowType.entries
} else {
LibraryRowType.entries.toMutableList().apply { remove(LibraryRowType.SUGGESTIONS) }
}
}
enum class LibraryRowType(
@param:StringRes val stringId: Int,
) {
RECENTLY_ADDED(R.string.recently_added),
RECENTLY_RELEASED(R.string.recently_released),
SUGGESTIONS(R.string.suggestions),
GENRES(R.string.genres),
}

View file

@ -13,7 +13,11 @@ import com.github.damontecres.wholphin.data.model.HomePageSettings
import com.github.damontecres.wholphin.data.model.HomeRowConfig
import com.github.damontecres.wholphin.data.model.HomeRowConfig.ContinueWatching
import com.github.damontecres.wholphin.data.model.HomeRowConfig.ContinueWatchingCombined
import com.github.damontecres.wholphin.data.model.HomeRowConfig.Genres
import com.github.damontecres.wholphin.data.model.HomeRowConfig.NextUp
import com.github.damontecres.wholphin.data.model.HomeRowConfig.RecentlyAdded
import com.github.damontecres.wholphin.data.model.HomeRowConfig.RecentlyReleased
import com.github.damontecres.wholphin.data.model.HomeRowConfig.Suggestions
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.services.BackdropService
import com.github.damontecres.wholphin.services.HomePageResolvedSettings
@ -252,7 +256,7 @@ class HomeSettingsViewModel
HomeRowConfigDisplay(
id = id,
title = title,
config = HomeRowConfig.RecentlyAdded(library.itemId),
config = RecentlyAdded(library.itemId),
)
}
@ -267,7 +271,7 @@ class HomeSettingsViewModel
HomeRowConfigDisplay(
id = id,
title = title,
config = HomeRowConfig.RecentlyReleased(library.itemId),
config = RecentlyReleased(library.itemId),
)
}
@ -276,7 +280,17 @@ class HomeSettingsViewModel
HomeRowConfigDisplay(
id = id,
title = title,
config = HomeRowConfig.Genres(library.itemId),
config = Genres(library.itemId),
)
}
LibraryRowType.SUGGESTIONS -> {
val title =
library.name.let { context.getString(R.string.suggestions_for, it) }
HomeRowConfigDisplay(
id = id,
title = title,
config = Suggestions(library.itemId),
)
}
}

View file

@ -500,6 +500,7 @@
<string name="overwrite_server_settings">Overwrite settings on server?</string>
<string name="overwrite_local_settings">Overwrite local settings?</string>
<string name="for_episodes">For episodes</string>
<string name="suggestions_for">Suggestions for %1$s</string>
<string-array name="theme_song_volume">
<item>Disabled</item>