Die Oberfläche nach Plex' Vorbild — Hintergrund, Schriftzug, Besetzung
Nach den Bildern deiner Plex-Installation umgebaut. Übernommen ist der Aufbau, nicht die Farbe: Plex ist bernsteingelb, Flaro bleibt Orchidee. **Kopfleiste oben** mit Marke, Suche und Konto. Die Suche gehört dorthin und nicht in jede Mediathek: wer einen Titel sucht, weiss oft gar nicht, in welcher er liegt. Sie geht quer durch alles. **Seitenleiste mit Icons** — echte SVG, keine Zeichen aus einer Schriftart. Ein ▶ aus Unicode sieht auf jedem System anders aus, lässt sich nicht einfärben und springt in der Grundlinie. Die aktive Zeile bekommt einen schmalen Balken links statt eines leuchtenden Blocks; das liest sich in einer langen Liste ruhiger. **Die Kachel steht jetzt an einer Stelle** statt an fünfen. Sie trägt den Haken für Gesehenes in der Ecke — wie bei Plex, ein Blick über das Raster genügt —, die Zahl offener Folgen an derselben Stelle, den Fortschrittsbalken unten und beim Überfahren einen Abspielknopf. Folgen sind dreizeilig: Serie, Folgentitel, „S14 · F22". **Reihen haben Pfeile.** Zwanzig Kacheln passen nicht in den Bildschirm, und quer zu scrollen geht mit dem Rad nur mit Umschalttaste. Die Überschrift nennt die Mediathek beim Namen: „Zuletzt in Dokumentationen hinzugefügt". **Mediatheken haben drei Reiter** — Empfehlungen, Mediathek, Kategorien. Darunter eine Filterzeile mit Genre, Sortierung und der Anzahl. Die Kategorien sind farbige Kacheln; die Farbe kommt aus dem Namen, nicht aus einer festen Liste, die jedes Genre kennen müsste, das TMDB je erfindet. **Die Detailseite** hat jetzt den unscharfen Hintergrund über die ganze Seite, den **Schriftzug des Films** statt der Systemschrift, eine Zeile mit Jahr, Genres, Laufzeit, Altersfreigabe und Bewertung, runde Knöpfe für gesehen/Favorit/weglegen, die Medienangaben als Tabelle — Datei, Video, Ton, Untertitel — und zuletzt Besetzung & Stab mit runden Porträts. **Dazu am Server:** ein Name. Ohne ihn zeigten die Clients den Hostnamen, und die App schrieb „localhost" über die Anmeldung. Vorgabe ist der Rechnername des Containers. --- **Der Fehler, den das erste Bild zeigte:** die Lupe in der Suchleiste war dreihundert Bildpunkte gross und lag über der halben Seite. Ein rohes `<svg>` ohne Breite füllt seinen Container; die Regel galt nur für `.icon`. Jetzt für jedes. 368 Tests grün. Am Container nachgesehen: „Der Untergang" mit eigenem Schriftzug, FSK 12, Bewertung 7,9, Genres Drama/Historie/Kriegsfilm, beide Tonspuren in der Tabelle, 22 Porträts darunter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ede4069cd4
commit
8c395ac051
@@ -5,7 +5,10 @@ namespace Flaro.Api.Endpoints;
|
||||
// und bricht Clients, die niemand mehr aktualisiert.
|
||||
|
||||
public sealed record SystemInfo(
|
||||
string Version, bool NeedsSetup, IReadOnlyList<AttributionResponse> Attributions);
|
||||
string Version, string ServerName, bool NeedsSetup,
|
||||
IReadOnlyList<AttributionResponse> Attributions);
|
||||
|
||||
public sealed record ServerNameRequest(string Name);
|
||||
|
||||
public sealed record SetupRequest(string UserName, string Password);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flaro.Core.Users;
|
||||
using Flaro.Core;
|
||||
using Flaro.Metadata;
|
||||
using Flaro.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -16,11 +17,23 @@ public static class SystemEndpoints
|
||||
/// </remarks>
|
||||
private static readonly SemaphoreSlim EinrichtungsSchloss = new(1, 1);
|
||||
|
||||
/// <summary>Unter welchem Schlüssel der Servername liegt.</summary>
|
||||
public const string NameSchlüssel = "server.name";
|
||||
|
||||
public static void MapSystem(this IEndpointRouteBuilder app)
|
||||
{
|
||||
app.MapGet("/api/v1/system/info", async (FlaroContext db) =>
|
||||
Results.Ok(new SystemInfo(
|
||||
Version: typeof(SystemEndpoints).Assembly.GetName().Version?.ToString(3) ?? "0.0.0",
|
||||
// **Wie dieser Server heisst.** Ohne ihn zeigen die Clients
|
||||
// den Hostnamen — „localhost", „192.168.1.50" —, und wer zwei
|
||||
// Server hat, unterscheidet sie an einer Zahl.
|
||||
//
|
||||
// Vorgabe ist der Rechnername des Containers: besser als
|
||||
// nichts und besser als „Flaro", weil davon jeder einen hat.
|
||||
ServerName: (await db.Settings.AsNoTracking()
|
||||
.FirstOrDefaultAsync(e => e.Key == NameSchlüssel))?.Value
|
||||
?? Environment.MachineName,
|
||||
NeedsSetup: !await db.Users.AnyAsync(),
|
||||
// Ohne Anmeldung abrufbar: ein Client muss den Hinweis auch
|
||||
// auf dem Anmeldebildschirm zeigen können.
|
||||
@@ -30,6 +43,23 @@ public static class SystemEndpoints
|
||||
])))
|
||||
.AllowAnonymous();
|
||||
|
||||
app.MapPut("/api/v1/system/name", async (ServerNameRequest 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.Settings.FirstOrDefaultAsync(e => e.Key == NameSchlüssel);
|
||||
if (vorhanden is null) db.Settings.Add(new ServerSetting { Key = NameSchlüssel, Value = name });
|
||||
else vorhanden.Value = name;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
return Results.NoContent();
|
||||
}).RequireAuthorization(FlaroPolicies.Administrator);
|
||||
|
||||
app.MapPost("/api/v1/system/setup", async (SetupRequest anfrage, FlaroContext db) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(anfrage.UserName) || anfrage.Password.Length < 8)
|
||||
|
||||
Reference in New Issue
Block a user