Library screen

This commit is contained in:
2026-10-14 17:42:04 +03:00
parent 1032fe3747
commit 757ef213f8
16 changed files with 3254 additions and 564 deletions

View File

@@ -21,6 +21,7 @@ import kotlinx.serialization.json.Json
import ru.stenfredd.movies.core.data.model.ItemQuery
import ru.stenfredd.movies.core.data.model.ItemSummary
import ru.stenfredd.movies.core.data.model.LibraryView
import ru.stenfredd.movies.core.data.model.itemKinds
import ru.stenfredd.movies.core.jellyfin.JellyfinSession
import timber.log.Timber
import java.io.File
@@ -115,7 +116,7 @@ class HomeRepository @Inject constructor(
val counted = fresh.copy(
libraries = coroutineScope {
fresh.libraries.map { row ->
async { row.copy(total = runCatching { library.count(ItemQuery(parentId = row.library.id)) }.getOrNull() ?: row.total) }
async { row.copy(total = runCatching { library.count(ItemQuery(parentId = row.library.id, kinds = row.library.itemKinds)) }.getOrNull() ?: row.total) }
}.awaitAll()
},
)

View File

@@ -26,37 +26,38 @@ class WatchRepository @Inject constructor(
) {
private val userId get() = session.requireSession().userId
/** Started movies and episodes, most recently watched first. */
suspend fun resume(limit: Int = 20): List<ItemSummary> = cache.get("resume:$limit", Kind.UserData) {
/** Started movies and episodes, most recently watched first; of one library when [parentId] is given. */
suspend fun resume(limit: Int = 20, parentId: UUID? = null): List<ItemSummary> = cache.get("resume:$limit:$parentId", Kind.UserData) {
session.call {
libraryApi.getResumeItems(userId = userId, limit = limit, mediaTypes = listOf(MediaType.VIDEO), fields = FEATURED_FIELDS).content.items
libraryApi.getResumeItems(userId = userId, parentId = parentId, limit = limit, mediaTypes = listOf(MediaType.VIDEO), fields = FEATURED_FIELDS).content.items
}.map { it.toSummary() }
}
/** The next unwatched episode of each series in progress. */
suspend fun nextUp(limit: Int = 20): List<ItemSummary> = cache.get("nextup:$limit", Kind.UserData) {
/** The next unwatched episode of each series in progress; of one library when [parentId] is given. */
suspend fun nextUp(limit: Int = 20, parentId: UUID? = null): List<ItemSummary> = cache.get("nextup:$limit:$parentId", Kind.UserData) {
session.call {
showApi.getNextUp(userId = userId, limit = limit, fields = FEATURED_FIELDS, enableResumable = false, enableRewatching = false).content.items
showApi.getNextUp(userId = userId, parentId = parentId, limit = limit, fields = FEATURED_FIELDS, enableResumable = false, enableRewatching = false).content.items
}.map { it.toSummary() }
}
/**
* "Continue watching": started items first (most recent first), then the next episode of each series in progress
* that isn't already there.
* that isn't already there. Of one library when [parentId] is given.
*/
suspend fun continueWatching(limit: Int = 20): List<ItemSummary> = coroutineScope {
val resume = async { resume(limit) }
val nextUp = async { nextUp(limit) }
suspend fun continueWatching(limit: Int = 20, parentId: UUID? = null): List<ItemSummary> = coroutineScope {
val resume = async { resume(limit, parentId) }
val nextUp = async { nextUp(limit, parentId) }
val started = resume.await()
val seriesStarted = started.mapNotNullTo(HashSet()) { it.seriesId }
(started + nextUp.await().filter { it.seriesId !in seriesStarted && started.none { s -> s.id == it.id } }).take(limit)
}
/** Watched movies and episodes, most recent first. */
suspend fun history(start: Int, limit: Int): Page<ItemSummary> = library.items(
ItemQuery(kinds = setOf(ItemKind.Movie, ItemKind.Episode), sort = ItemSort.DatePlayed, descending = true, playedOnly = true),
/** Watched movies and episodes, most recent first; of one library when [parentId] is given. */
suspend fun history(start: Int, limit: Int, parentId: UUID? = null): Page<ItemSummary> = library.items(
ItemQuery(parentId = parentId, kinds = setOf(ItemKind.Movie, ItemKind.Episode), sort = ItemSort.DatePlayed, descending = true, playedOnly = true),
start,
limit,
withTotal = false,
)
suspend fun favorites(start: Int, limit: Int, kinds: Set<ItemKind> = setOf(ItemKind.Movie, ItemKind.Series)): Page<ItemSummary> =

View File

@@ -78,6 +78,17 @@ data class LibraryView(val id: UUID, val name: String, val type: LibraryType, va
enum class LibraryType { Movies, Shows, Mixed, Other }
/**
* What a library's lists and counts ask for. One type where the library has one: the server takes ~10× longer
* (0.2 s instead of 0.02) to list or count "movies and series" than just movies.
*/
val LibraryView.itemKinds: Set<ItemKind>
get() = when (type) {
LibraryType.Movies -> setOf(ItemKind.Movie)
LibraryType.Shows -> setOf(ItemKind.Series)
else -> setOf(ItemKind.Movie, ItemKind.Series)
}
@Serializable
data class Person(val id: UUID, val name: String, val role: String?, val kind: PersonKind, val image: ImageRef?)