Support collections

This commit is contained in:
Damontecres 2025-10-13 21:58:55 -04:00
parent a93f183f5f
commit b40db0074f
No known key found for this signature in database
11 changed files with 64 additions and 14 deletions

View file

@ -98,7 +98,7 @@ fun GridCard(
.fillMaxWidth(), .fillMaxWidth(),
) { ) {
Text( Text(
text = item?.name ?: "", text = item?.title ?: "",
maxLines = 1, maxLines = 1,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@ -109,7 +109,7 @@ fun GridCard(
.enableMarquee(focusedAfterDelay), .enableMarquee(focusedAfterDelay),
) )
Text( Text(
text = item?.data?.productionYear?.toString() ?: "", text = item?.subtitle ?: "",
maxLines = 1, maxLines = 1,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = modifier =

View file

@ -72,6 +72,7 @@ class CollectionFolderViewModel
itemId: UUID, itemId: UUID,
potential: BaseItem?, potential: BaseItem?,
sortAndDirection: SortAndDirection, sortAndDirection: SortAndDirection,
recursive: Boolean,
): Job = ): Job =
viewModelScope.launch( viewModelScope.launch(
LoadingExceptionHandler( LoadingExceptionHandler(
@ -80,10 +81,13 @@ class CollectionFolderViewModel
) + Dispatchers.IO, ) + Dispatchers.IO,
) { ) {
fetchItem(itemId, potential) fetchItem(itemId, potential)
loadResults(sortAndDirection) loadResults(sortAndDirection, recursive)
} }
fun loadResults(sortAndDirection: SortAndDirection) { fun loadResults(
sortAndDirection: SortAndDirection,
recursive: Boolean,
) {
item.value?.let { item -> item.value?.let { item ->
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -96,6 +100,15 @@ 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)
CollectionType.MUSIC ->
listOf(
BaseItemKind.AUDIO,
BaseItemKind.MUSIC_ARTIST,
BaseItemKind.MUSIC_ALBUM,
)
CollectionType.BOXSETS -> listOf(BaseItemKind.BOX_SET)
CollectionType.PLAYLISTS -> listOf(BaseItemKind.PLAYLIST)
else -> listOf() else -> listOf()
} }
@ -104,7 +117,8 @@ class CollectionFolderViewModel
parentId = item.id, parentId = item.id,
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB), enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB),
includeItemTypes = includeItemTypes, includeItemTypes = includeItemTypes,
recursive = true, recursive = recursive,
excludeItemIds = listOf(item.id),
sortBy = sortBy =
listOf( listOf(
sortAndDirection.sort, sortAndDirection.sort,
@ -120,7 +134,13 @@ class CollectionFolderViewModel
fields = DefaultItemFields, fields = DefaultItemFields,
) )
val newPager = val newPager =
ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope) ApiRequestPager(
api,
request,
GetItemsRequestHandler,
viewModelScope,
useSeriesForPrimary = true,
)
newPager.init() newPager.init()
if (newPager.isNotEmpty()) newPager.getBlocking(0) if (newPager.isNotEmpty()) newPager.getBlocking(0)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -164,6 +184,7 @@ class CollectionFolderViewModel
fun CollectionFolderGrid( fun CollectionFolderGrid(
preferences: UserPreferences, preferences: UserPreferences,
destination: Destination.MediaItem, destination: Destination.MediaItem,
recursive: Boolean,
onClickItem: (BaseItem) -> Unit, onClickItem: (BaseItem) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: CollectionFolderViewModel = hiltViewModel(), viewModel: CollectionFolderViewModel = hiltViewModel(),
@ -176,7 +197,7 @@ fun CollectionFolderGrid(
positionCallback: ((columns: Int, position: Int) -> Unit)? = null, positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
) { ) {
OneTimeLaunchedEffect { OneTimeLaunchedEffect {
viewModel.init(destination.itemId, destination.item, initialSortAndDirection) viewModel.init(destination.itemId, destination.item, initialSortAndDirection, recursive)
} }
val sortAndDirection by viewModel.sortAndDirection.observeAsState(initialSortAndDirection) val sortAndDirection by viewModel.sortAndDirection.observeAsState(initialSortAndDirection)
val loading by viewModel.loading.observeAsState(LoadingState.Loading) val loading by viewModel.loading.observeAsState(LoadingState.Loading)
@ -198,7 +219,7 @@ fun CollectionFolderGrid(
modifier = modifier, modifier = modifier,
onClickItem = onClickItem, onClickItem = onClickItem,
onSortChange = { onSortChange = {
viewModel.loadResults(it) viewModel.loadResults(it, recursive)
}, },
showTitle = showTitle, showTitle = showTitle,
positionCallback = positionCallback, positionCallback = positionCallback,
@ -263,7 +284,7 @@ fun CollectionFolderGridContent(
CardGrid( CardGrid(
pager = pager, pager = pager,
onClickItem = onClickItem, onClickItem = onClickItem,
longClicker = {}, onLongClickItem = {},
letterPosition = letterPosition, letterPosition = letterPosition,
gridFocusRequester = gridFocusRequester, gridFocusRequester = gridFocusRequester,
showJumpButtons = false, // TODO add preference showJumpButtons = false, // TODO add preference

View file

@ -69,7 +69,7 @@ private const val DEBUG = false
fun CardGrid( fun CardGrid(
pager: List<BaseItem?>, pager: List<BaseItem?>,
onClickItem: (BaseItem) -> Unit, onClickItem: (BaseItem) -> Unit,
longClicker: (BaseItem) -> Unit, onLongClickItem: (BaseItem) -> Unit,
letterPosition: suspend (Char) -> Int, letterPosition: suspend (Char) -> Int,
gridFocusRequester: FocusRequester, gridFocusRequester: FocusRequester,
showJumpButtons: Boolean, showJumpButtons: Boolean,
@ -257,7 +257,7 @@ fun CardGrid(
onClickItem.invoke(item) onClickItem.invoke(item)
} }
}, },
onLongClick = { if (item != null) longClicker.invoke(item) }, onLongClick = { if (item != null) onLongClickItem.invoke(item) },
modifier = modifier =
mod mod
.ifElse(index == 0, Modifier.focusRequester(zeroFocus)) .ifElse(index == 0, Modifier.focusRequester(zeroFocus))

View file

@ -18,6 +18,7 @@ import com.github.damontecres.dolphin.ui.preferences.PreferencesViewModel
fun CollectionFolderGeneric( fun CollectionFolderGeneric(
preferences: UserPreferences, preferences: UserPreferences,
destination: Destination.MediaItem, destination: Destination.MediaItem,
recursive: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
preferencesViewModel: PreferencesViewModel = hiltViewModel(), preferencesViewModel: PreferencesViewModel = hiltViewModel(),
) { ) {
@ -27,6 +28,7 @@ fun CollectionFolderGeneric(
onClickItem = { preferencesViewModel.navigationManager.navigateTo(it.destination()) }, onClickItem = { preferencesViewModel.navigationManager.navigateTo(it.destination()) },
destination = destination, destination = destination,
showTitle = showHeader, showTitle = showHeader,
recursive = recursive,
modifier = modifier =
modifier modifier
.padding(start = 16.dp), .padding(start = 16.dp),

View file

@ -156,6 +156,7 @@ fun CollectionFolderMovie(
onClickItem = onClickItem, onClickItem = onClickItem,
destination = destination, destination = destination,
showTitle = false, showTitle = false,
recursive = true,
modifier = modifier =
Modifier Modifier
.padding(start = 16.dp) .padding(start = 16.dp)

View file

@ -155,6 +155,7 @@ fun CollectionFolderTv(
preferences = preferences, preferences = preferences,
destination = destination, destination = destination,
showTitle = false, showTitle = false,
recursive = true,
modifier = modifier =
Modifier Modifier
.padding(start = 16.dp) .padding(start = 16.dp)

View file

@ -61,13 +61,14 @@ abstract class ItemViewModel<T>(
abstract class LoadingItemViewModel<T>( abstract class LoadingItemViewModel<T>(
api: ApiClient, api: ApiClient,
) : ItemViewModel<T>(api) { ) : ItemViewModel<T>(api) {
val loading = MutableLiveData<LoadingState>(LoadingState.Loading) val loading = MutableLiveData<LoadingState>(LoadingState.Pending)
open fun init( open fun init(
itemId: UUID, itemId: UUID,
potential: BaseItem?, potential: BaseItem?,
): Job? = ): Job? {
viewModelScope.launch( loading.value = LoadingState.Loading
return viewModelScope.launch(
LoadingExceptionHandler( LoadingExceptionHandler(
loading, loading,
"Error loading item $itemId", "Error loading item $itemId",
@ -86,3 +87,4 @@ abstract class LoadingItemViewModel<T>(
} }
} }
} }
}

View file

@ -106,6 +106,14 @@ fun DestinationContent(
modifier, modifier,
) )
BaseItemKind.BOX_SET ->
CollectionFolderGeneric(
preferences,
destination,
false,
modifier,
)
BaseItemKind.COLLECTION_FOLDER -> { BaseItemKind.COLLECTION_FOLDER -> {
when (destination.item?.data?.collectionType) { when (destination.item?.data?.collectionType) {
CollectionType.TVSHOWS -> CollectionType.TVSHOWS ->
@ -122,10 +130,19 @@ fun DestinationContent(
modifier, modifier,
) )
CollectionType.BOXSETS ->
CollectionFolderGeneric(
preferences,
destination,
true,
modifier,
)
else -> else ->
CollectionFolderGeneric( CollectionFolderGeneric(
preferences, preferences,
destination, destination,
false,
modifier, modifier,
) )
} }
@ -141,6 +158,7 @@ fun DestinationContent(
CollectionFolderGeneric( CollectionFolderGeneric(
preferences, preferences,
destination, destination,
true,
modifier, modifier,
) )

View file

@ -318,6 +318,8 @@ fun NavigationDrawerScope.LibraryNavItem(
CollectionType.HOMEVIDEOS -> R.string.fa_video CollectionType.HOMEVIDEOS -> R.string.fa_video
CollectionType.LIVETV -> R.string.fa_tv CollectionType.LIVETV -> R.string.fa_tv
CollectionType.MUSIC -> R.string.fa_music CollectionType.MUSIC -> R.string.fa_music
CollectionType.BOXSETS -> R.string.fa_open_folder
CollectionType.PLAYLISTS -> R.string.fa_list_ul
else -> R.string.fa_film else -> R.string.fa_film
} }
val isFocused = interactionSource.collectIsFocusedAsState().value val isFocused = interactionSource.collectIsFocusedAsState().value

View file

@ -28,4 +28,5 @@ val supportedCollectionTypes =
CollectionType.TVSHOWS, CollectionType.TVSHOWS,
CollectionType.HOMEVIDEOS, CollectionType.HOMEVIDEOS,
CollectionType.PLAYLISTS, CollectionType.PLAYLISTS,
CollectionType.BOXSETS,
) )

View file

@ -32,4 +32,6 @@
<string name="fa_eye_slash" translatable="false">&#xf070;</string> <string name="fa_eye_slash" translatable="false">&#xf070;</string>
<string name="fa_arrow_left_arrow_right" translatable="false">&#xf0ec;</string> <string name="fa_arrow_left_arrow_right" translatable="false">&#xf0ec;</string>
<string name="fa_shuffle" translatable="false">&#xf074;</string> <string name="fa_shuffle" translatable="false">&#xf074;</string>
<string name="fa_open_folder" translatable="false">&#xf07c;</string>
<string name="fa_list_ul" translatable="false">&#xf0ca;</string>
</resources> </resources>