Merge #18: Unbekannte Jellyfin-Titel zur Auswahl stellen und nachladen
CI / pruefen (push) Canceled after 0s

Closes #16
This commit was merged in pull request #18.
This commit is contained in:
2026-09-02 11:27:26 +00:00
7 changed files with 614 additions and 14 deletions
+40
View File
@@ -18,6 +18,7 @@ import { runSync } from "@/lib/sync";
import {
connectJellyfin,
deleteConnection,
importUnassigned,
syncConnection,
testConnection,
updateConnection,
@@ -403,6 +404,45 @@ export async function syncMediaServerAction(
}
}
const auswahlSchema = z
.array(
z.object({
tmdbId: z.number().int().positive(),
typ: z.enum(["SERIES", "MOVIE"]),
}),
)
.min(1)
.max(1000);
/** Holt die ausgewählten Titel aus TMDB nach. */
export async function importMediaServerTitlesAction(
connectionId: string,
auswahl: unknown,
): Promise<MediaServerResult & { uebrig?: number }> {
const user = await getSessionUser();
if (!user) return { ok: false, message: "Bitte melde dich an." };
const daten = auswahlSchema.safeParse(auswahl);
if (!daten.success) return { ok: false, message: "Es war nichts ausgewählt." };
try {
const ergebnis = await importUnassigned(user.id, connectionId, daten.data);
revalidatePath("/einstellungen", "layout");
revalidatePath("/bibliothek", "layout");
const teile = [`${ergebnis.nachgeladen} Titel nachgeladen`];
if (ergebnis.fehler.length > 0) teile.push(`${ergebnis.fehler.length} fehlgeschlagen`);
if (ergebnis.uebrig > 0) teile.push(`${ergebnis.uebrig} bleiben für den nächsten Lauf`);
return { ok: true, message: teile.join(", "), uebrig: ergebnis.uebrig };
} catch (error) {
return {
ok: false,
message: error instanceof Error ? error.message : "Das hat nicht geklappt.",
};
}
}
export async function updateMediaServerAction(
connectionId: string,
data: { enabled?: boolean; syncOwned?: boolean; syncWatched?: boolean },
+8
View File
@@ -90,6 +90,14 @@ export default async function SettingsPage() {
syncOwned: verbindung.syncOwned,
syncWatched: verbindung.syncWatched,
lastSyncAt: verbindung.lastSyncAt?.toISOString() ?? null,
// Der letzte Bericht liegt als Json in der Datenbank; daraus
// speist sich die Auswahlliste, damit sie einen Seitenwechsel
// übersteht.
offen: (verbindung.lastSyncReport as { nichtZugeordnet?: unknown[] } | null)
?.nichtZugeordnet as never ?? [],
weitereOffen:
((verbindung.lastSyncReport as { weitereNichtZugeordnet?: number } | null)
?.weitereNichtZugeordnet ?? 0),
}))}
/>
</section>