Mehrere Mediatheken, jede für sich
Bisher gab es in der Seitenleiste „Filme" und „Serien" — zwei feste Zeilen, hinter denen alles zusammenfiel, was der Server kannte. Wer „Filme" und „Dokumentationen" getrennt angelegt hatte, sah sie wieder in einem Topf. Das nahm die Entscheidung zurück, sie zu trennen. Jetzt führt die Seitenleiste **jede Mediathek einzeln**, mit Namen und Zahl, wie bei Plex und Jellyfin. Die Startseite bekommt je Mediathek eine Reihe mit dem zuletzt Hinzugefügten; steht mehr drin, als die Reihe zeigt, führt „Alle zeigen" dorthin. Die Mediathekenseite trägt den Namen im Titel, sucht innerhalb der Mediathek und lässt sich sortieren. **Am Server dafür:** `?sort=` auf `/movies` und `/series` — `title`, `added`, `year`. Der Titel immer als letzter Vergleich, sonst hätten zwei Filme aus demselben Jahr keine feste Reihenfolge und Seite 2 zeigte denselben Film noch einmal oder gar nicht. Die Liste der Bibliotheken nennt je Zeile einen Zählwert. „Dokumentationen · 0" sagt mehr als ein Eintrag, hinter dem sich nach dem Klick nichts findet — da ist der Unterschied zwischen „noch nicht gescannt" und „falscher Pfad" wenigstens sichtbar. `null` heisst „nicht gezählt", nicht „leer". Umbenennen und Entfernen, beides nur für Administratoren. **Umbenennen, mehr nicht:** die Art zu ändern hiesse, Filme in Serien zu verwandeln; den Pfad zu ändern hiesse, einen Bestand gegen einen anderen zu tauschen, während die Einträge stehenbleiben. Beides ist kein Bearbeiten, sondern eine neue Bibliothek. **Beim Entfernen bleiben die Mediendateien liegen.** Weg sind der Eintrag, die Zuordnungen und der Sehfortschritt aller Benutzer dazu; die Oberfläche sagt das vorher, und eine Prüfung hält fest, dass die Datei danach noch da ist. Ein Server, der beim Aufräumen in der Oberfläche fremde Filme löscht, ist einer, den man nicht noch einmal benutzt. --- **Der Fehler, den der Browser zeigte, und er betrifft jeden Betrieb:** nach einem Server-Update lief weiter die alte Oberfläche. Die Dateien unter `assets/` tragen eine Prüfsumme im Namen — ändert sich der Inhalt, ändert sich der Name. `index.html` trägt keine; sie nennt nur deren Namen. Lag sie im Zwischenspeicher, bekam der Browser weiter die alte App: nicht kaputt, sondern veraltet, und das fällt niemandem auf. Nachgemessen: geladen war `index-C0K3V8eg.js`, ausgeliefert wurde längst `index-DomHN4_b.js`. Jetzt zwei Lebensdauern — `no-cache` für die Seite, ein Jahr und `immutable` für die Bausteine. Mit Prüfung, und am Container nachgemessen. Der Beispielbestand bekommt zwei weitere Mediatheken, „Dokumentationen" und „Konzerte". Mit nur Filme und Serien liesse sich gar nicht prüfen, ob mehrere wirklich getrennt bleiben — zwei Bestände **derselben Art** sind der Fall, in dem es auffällt. 358 Tests grün. Am Container nachgewiesen: vier Mediatheken in der Seitenleiste mit Zählwerten, je eine Reihe auf der Startseite, und „Dokumentationen" zeigt zwei Titel statt sieben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
172b38da06
commit
230beb9664
@@ -45,12 +45,32 @@ public static class ManagementEndpoints
|
||||
var bibliotheken = app.MapGroup("/api/v1/libraries");
|
||||
|
||||
// Lesen darf jeder Angemeldete — ohne die Liste zeigt die App nichts an.
|
||||
//
|
||||
// **Mit Zählwert.** Die Seitenleiste führt jede Bibliothek einzeln
|
||||
// auf, und „Dokumentationen · 0" sagt mehr als ein Eintrag, hinter
|
||||
// dem sich nach dem Klick nichts findet — da ist der Unterschied
|
||||
// zwischen „noch nicht gescannt" und „falscher Pfad" wenigstens
|
||||
// sichtbar. Zwei Abfragen für alle zusammen, nicht eine je Zeile.
|
||||
bibliotheken.MapGet("/", async (FlaroContext db) =>
|
||||
Results.Ok(await db.Libraries
|
||||
.OrderBy(l => l.Name)
|
||||
.Select(l => new LibraryResponse(l.Id, l.Name, l.Kind.ToString(), l.Path))
|
||||
.ToListAsync()))
|
||||
.RequireAuthorization();
|
||||
{
|
||||
var liste = await db.Libraries.AsNoTracking().OrderBy(l => l.Name).ToListAsync();
|
||||
|
||||
var filme = await db.Movies.AsNoTracking()
|
||||
.GroupBy(m => m.LibraryId)
|
||||
.Select(g => new { Id = g.Key, Anzahl = g.Count() })
|
||||
.ToDictionaryAsync(x => x.Id, x => x.Anzahl);
|
||||
|
||||
var serien = await db.Series.AsNoTracking()
|
||||
.GroupBy(x => x.LibraryId)
|
||||
.Select(g => new { Id = g.Key, Anzahl = g.Count() })
|
||||
.ToDictionaryAsync(x => x.Id, x => x.Anzahl);
|
||||
|
||||
return Results.Ok(liste.Select(l => new LibraryResponse(
|
||||
l.Id, l.Name, l.Kind.ToString(), l.Path,
|
||||
l.Kind == LibraryKind.Shows
|
||||
? serien.GetValueOrDefault(l.Id)
|
||||
: filme.GetValueOrDefault(l.Id))).ToList());
|
||||
}).RequireAuthorization();
|
||||
|
||||
bibliotheken.MapPost("/", async (CreateLibraryRequest anfrage, FlaroContext db) =>
|
||||
{
|
||||
@@ -74,7 +94,45 @@ public static class ManagementEndpoints
|
||||
db.Libraries.Add(neu);
|
||||
await db.SaveChangesAsync();
|
||||
return Results.Created($"/api/v1/libraries/{neu.Id}",
|
||||
new LibraryResponse(neu.Id, neu.Name, neu.Kind.ToString(), neu.Path));
|
||||
new LibraryResponse(neu.Id, neu.Name, neu.Kind.ToString(), neu.Path, 0));
|
||||
}).RequireAuthorization(FlaroPolicies.Administrator);
|
||||
|
||||
// **Umbenennen, mehr nicht.** Die Art zu ändern hiesse, Filme in
|
||||
// Serien zu verwandeln; den Pfad zu ändern hiesse, einen ganzen
|
||||
// Bestand gegen einen anderen zu tauschen, während die Einträge
|
||||
// stehenbleiben. Beides ist kein Bearbeiten, sondern eine neue
|
||||
// Bibliothek — und die legt man an.
|
||||
bibliotheken.MapPut("/{id:guid}", async (
|
||||
Guid id, RenameLibraryRequest anfrage, FlaroContext db) =>
|
||||
{
|
||||
var name = anfrage.Name.Trim();
|
||||
if (name.Length == 0)
|
||||
return Results.ValidationProblem(new Dictionary<string, string[]>
|
||||
{
|
||||
["name"] = ["Ein Name darf nicht leer sein."],
|
||||
});
|
||||
|
||||
var vorhanden = await db.Libraries.FindAsync(id);
|
||||
if (vorhanden is null) return Results.NotFound();
|
||||
|
||||
vorhanden.Name = name;
|
||||
await db.SaveChangesAsync();
|
||||
return Results.Ok(new LibraryResponse(
|
||||
vorhanden.Id, vorhanden.Name, vorhanden.Kind.ToString(), vorhanden.Path, null));
|
||||
}).RequireAuthorization(FlaroPolicies.Administrator);
|
||||
|
||||
// **Die Mediendateien bleiben liegen.** Entfernt wird, was Flaro über
|
||||
// sie weiss: Titel, Zuordnungen, Fortschritt. Ein Server, der beim
|
||||
// Aufräumen in der Oberfläche fremde Filme löscht, ist ein Server,
|
||||
// den man nicht noch einmal benutzt.
|
||||
bibliotheken.MapDelete("/{id:guid}", async (Guid id, FlaroContext db) =>
|
||||
{
|
||||
var vorhanden = await db.Libraries.FindAsync(id);
|
||||
if (vorhanden is null) return Results.NotFound();
|
||||
|
||||
db.Libraries.Remove(vorhanden);
|
||||
await db.SaveChangesAsync();
|
||||
return Results.NoContent();
|
||||
}).RequireAuthorization(FlaroPolicies.Administrator);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user