feat: eigene Sammlung — „hab ich" mit Quelle
CI / pruefen (push) Canceled after 0s
Release / veroeffentlichen (push) Canceled after 0s

Streaming-Verfügbarkeit beantwortet nur die halbe Frage: manche Filme laufen
nirgends mehr, die hat man gekauft und liegen zu Hause — in Jellyfin, Plex,
Emby, auf dem NAS oder als Scheibe.

Bewusst getrennt vom Feld „wo schaue ich das": das eine sagt, wo ein Titel
gerade läuft, und das ändert sich ständig; das andere sagt, was einem gehört,
und das bleibt. Auf der Detailseite steht die Sammlung deshalb vor der
Verfügbarkeit — was man hat, ist die verlässlichere Antwort.

Durchgezogen bis in Filter, Export, CSV-Import und REST-API.
This commit is contained in:
Florian Wilde
2026-09-01 21:15:53 +02:00
parent cd9953c47c
commit 9ee8664233
26 changed files with 467 additions and 52 deletions
+12 -1
View File
@@ -19,6 +19,8 @@ export type LibraryFilters = {
genre?: string;
tag?: string;
provider?: string;
/** Nur Titel, die in der eigenen Sammlung liegen. */
owned?: boolean;
sort?: LibrarySort;
};
@@ -42,6 +44,8 @@ export type LibraryItem = {
favorite: boolean;
currentRun: number;
whereToWatch: string | null;
owned: boolean;
ownedSource: string | null;
tags: string[];
watched: number;
total: number;
@@ -56,6 +60,7 @@ export async function getLibrary(
userId,
deletedAt: null,
...(filters.status ? { status: filters.status } : {}),
...(filters.owned ? { owned: true } : {}),
...(filters.tag ? { tags: { some: { tag: { name: filters.tag } } } } : {}),
title: {
...(filters.type ? { type: filters.type } : {}),
@@ -116,6 +121,8 @@ export async function getLibrary(
favorite: entry.favorite,
currentRun: entry.currentRun,
whereToWatch: entry.whereToWatch,
owned: entry.owned,
ownedSource: entry.ownedSource,
tags: entry.tags.map((link) => link.tag.name),
watched,
total,
@@ -162,11 +169,13 @@ export type LibraryFacets = {
tags: string[];
providers: string[];
counts: Record<EntryStatus | "ALLE", number>;
/** Wie viele Titel in der eigenen Sammlung liegen. */
owned: number;
};
/** Werte für die Filterleiste — nur das, was in der Bibliothek wirklich vorkommt. */
export async function getLibraryFacets(userId: string): Promise<LibraryFacets> {
const [genreRows, tagRows, providerRows, statusRows, total] = await Promise.all([
const [genreRows, tagRows, providerRows, statusRows, total, owned] = await Promise.all([
prisma.$queryRaw<{ genre: string }[]>`
SELECT DISTINCT unnest(t."genres") AS genre
FROM "LibraryEntry" e
@@ -193,6 +202,7 @@ export async function getLibraryFacets(userId: string): Promise<LibraryFacets> {
_count: { _all: true },
}),
prisma.libraryEntry.count({ where: { userId, deletedAt: null } }),
prisma.libraryEntry.count({ where: { userId, deletedAt: null, owned: true } }),
]);
const counts = {
@@ -211,6 +221,7 @@ export async function getLibraryFacets(userId: string): Promise<LibraryFacets> {
tags: tagRows.map((row) => row.name),
providers: providerRows.map((row) => row.name),
counts,
owned,
};
}