Commit Graph
7 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 637e95fbb1 Die Verwaltung zeichnete sich endlos neu
Gemessen am laufenden Server: nach dem Scan lief eine Schleife mit zwei
Abrufen je Runde, sichtbar als vier dauernd drehende Spinner.

Der Weg dahin: verfolgen() lief bei jedem Aufbau der Mediathekenzeile, sah
den Zustand finished, meldete das über geaendert(), und geaendert() war
mediathekenNeuLaden — also neu zeichnen. Der Neuaufbau startete verfolgen()
erneut, das wieder finished sah. Dass der Lauf längst vorbei war, machte
keinen Unterschied; der Zustand bleibt im Speicher stehen.

Zwei Dinge dagegen. verfolgen() meldet nur noch, wenn es selbst einen Lauf
angestossen hat — beim Aufbau zeigt es den Stand an und schweigt. Und die
Verwaltung frischt über zaehlerAuffrischen() nur noch die Seitenleiste auf
statt die ganze Seite neu zu zeichnen.

Dazu zwei Befunde aus dem Plan, die sich beim Nachsehen als falsch
erwiesen: die Takte werden sehr wohl abbestellt, dafür gibt es seit je
einen MutationObserver, und das Dashboard tauscht seinen Inhalt erst nach
dem Abruf. Geblieben ist der Spinner in den Listen, der bei jedem
Auffrischen erschien — der kommt jetzt nur noch beim ersten Aufbau.

Dazu der Zwischenspeicher für Ansichten. Der Router hängt sie aus statt
sie zu zerstören; beim Zurückkommen stehen sie sofort wieder da, mitsamt
Bildlauf, offenem Reiter und gesetzten Filtern, und laden leise nach.
Gemessen: null Abrufe und 4 von 4 Kacheln beim Zurückkommen aus der
Verwaltung. Höchstens fünf Ansichten, Verfall nach fünf Minuten,
weggeworfen bei allem, was den Bestand ändert. Detailseite und Verwaltung
sind ausgenommen — beide zeigen Laufendes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 12:11:06 +02:00
ScarriffleandClaude Opus 5 8c395ac051 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>
2026-10-04 01:04:27 +02:00
ScarriffleandClaude Opus 5 230beb9664 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>
2026-10-03 22:57:25 +02:00
ScarriffleandClaude Opus 5 91eb934048 Die Weboberfläche — im Zuschnitt von Plex, vom Server selbst ausgeliefert
Einrichtung, Anmeldung, Mediathek, Detailseite mit Abspielen und
Herunterladen, Zuordnung korrigieren, Verwaltung. Die Oberfläche aus der
Jellyfin-Zeit ist dabei vollständig ersetzt: `types.ts`, `prefs.ts`,
`contrast.ts`, `views/cards.ts`, `views/player.ts` und `views/settings.ts`
sprachen Jellyfins Schnittstelle und hatten hier nichts mehr zu suchen.

Seitenleiste links, waagerechte Reihen auf der Startseite, Plakate im
Verhältnis 2:3, unscharfer Hintergrund hinter dem Kopf der Detailseite —
das ist der Zuschnitt von Plex, weil er gefällt. **Die Farbe bleibt Flaros
Orchidee**, nicht Plex' Orange; der Aufbau ist das Vorbild, nicht das
Aussehen.

Welche Bibliothek gezeigt wird, entscheidet die Seitenleiste. Eine zweite
Umschaltung innerhalb der Seite wäre dieselbe Entscheidung an zwei Orten.

---

**Der Zugang liegt jetzt auch in einem Cookie.** Ein `<img>`, ein `<video>`
und ein Download-Link senden keine eigenen Kopfzeilen — eine App kann ihr
Token in jede Anfrage legen, ein Browser nicht. Das Token stattdessen in
die Adresse zu schreiben wäre der naheliegende Ausweg und stünde danach in
jedem Zugriffsprotokoll, in jedem Verlauf und in jedem weitergegebenen
Link.

`HttpOnly`, damit kein eingeschlichenes Skript herankommt. `SameSite=Strict`
gegen Anfragen von fremden Seiten. `Secure` nur über https — sonst liesse
sich die Oberfläche im Heimnetz über http gar nicht benutzen. Die Apps
benutzen weiterhin die Kopfzeile: ein zweiter Weg zum selben Token, kein
zweites Verfahren.

---

**Der Fehler, den erst der echte Container zeigte:** eine weisse Seite,
ohne Hinweis worauf.

Die Rückfallregel verlangt für jede Anfrage ohne eigenen Endpunkt einen
angemeldeten Benutzer, und `/assets/index.js` hat keinen. Die statischen
Dateien standen unter `UseAuthorization` — ein neuer Besucher bekam 401 auf
genau das Skript, das ihm die Anmeldemaske zeichnen sollte. `/` kam durch,
weil der SPA-Rückfall `AllowAnonymous` trägt; alles mit Dateiendung nicht.
Jetzt liegen sie davor. Mit Prüfung.

Beim Bau desselben Images der zweite: seit der Kontext die Projektwurzel
ist, überschrieb `COPY server/` die `obj/`-Verzeichnisse aus dem Container
mit denen vom Arbeitsrechner — die zeigen auf osx-arm64, gebaut wird
linux-arm64, `dotnet publish` brach mit NETSDK1047 ab. Dafür gibt es jetzt
eine `.dockerignore` in der Wurzel; die unter `server/` sucht Docker seit
dem Umzug nicht mehr.

---

Vier Prüfungen schlugen danach fehl, und zwar zu Recht: sie hiessen „ohne
Anmeldung gibt es nichts", benutzten aber den Client, der nach dem Anmelden
ein Cookie hält. Sie prüften also den Angemeldeten. Jetzt sagen sie
ausdrücklich, dass sie ohne Zugang fragen.

313 Tests grün. Im Container nachgewiesen: Anmelden, Plakate, Abspielen,
Staffeln und Folgen, Verwaltung — und ein Neuladen auf `/verwaltung`, das
nicht in einem 404 endet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 21:07:29 +02:00
ScarriffleandClaude Opus 5 a7cf6c5def Halo heisst ab hier Flaro — die Marke stand im Weg
Der Upload zu App Store Connect scheiterte am Namen. „Halo" ist eine
eingetragene Marke von Microsoft; im App Store heisst zwar keine App exakt
so, aber reservierte und markenrechtlich belegte Namen tauchen in keiner
Suche auf. Der einzige verlässliche Test war die Eingabe dort, und die ging
daneben.

**Der Name wurde diesmal vorher geprüft.** Zwanzig Kandidaten gegen den
deutschen und amerikanischen App Store, auf exakte Treffer und auf ähnliche
Namen. „Kavo" wäre dieselbe Wand gewesen — exakt belegt in beiden Stores, in
Deutschland von KaVo Dental GmbH. „Flare" ist mit 42 ähnlichen Apps je Store
der am dichtesten besetzte Name der ganzen Runde. **Flaro** ist in beiden
frei und liest sich als eigenes Wort, nicht als Flare mit Tippfehler.

Das bleibt eine Store-Suche und keine Markenrecherche — reservierte Namen
ohne veröffentlichte App sieht sie nicht. Aber es ist mehr Prüfung, als beim
letzten Mal stattfand.

---

2160 Textvorkommen in 273 Dateien, 49 Pfade. Betroffen ist diesmal auch der
Server: `Flaro.Api`, `Flaro.Core`, `Flaro.Data`, `Flaro.Media`,
`Flaro.Metadata`, `FlaroContext`, `FlaroPaths`, `flaro.db`, die Variablen
`FLARO_DATA`, `FLARO_CACHE`, `FLARO_TRANSCODE`, `FLARO_WRITE_SIDECARS`, und
das Image.

**Vor dem Ersetzen geprüft**, ob „halo" irgendwo als Teil eines anderen
Wortes steckt — und ob es im TMDB-Token vorkommt. Tat es nicht; ein blindes
Ersetzen hätte den sonst zerlegt.

`docs/superpowers/specs/` bleibt wie beim letzten Mal unangetastet: datierte
Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen. Das
Server-Dokument hat stattdessen einen Nachtrag bekommen, damit es niemanden
in die Irre führt.

**Die Bundle-ID wandert mit**, zum zweiten Mal an einem Tag:
`com.scarriffleservices.flaro`. Wer die vorige Fassung installiert hat, hat
jetzt zwei Karteileichen auf dem Gerät — beide löschen, dann ist Ruhe.

308 Server-Tests grün, 368 im Swift-Paket, null Warnungen. iOS und tvOS
bauen, das Image baut, und ein frischer Container läuft durch Einrichtung
und Anmeldung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 19:38:27 +02:00
ScarriffleandClaude Opus 5 4b9a1e7b67 Flixy heißt ab hier Halo
Reine Umbenennung, kein Verhalten geändert — bewusst als eigener Commit,
damit „umbenannt" und „umgebaut" im Nachhinein auseinanderzuhalten sind.

712 mal `Flixy`, 778 mal `flixy`, 7 mal `FLIXY` in 169 Dateien, dazu 25
Pfade. Ziele: `Halo`, `HaloTV`, `HaloTopShelf`, `HaloUITests`, `HaloKit` mit
`HaloDesign`, `HaloDownloads`, `HaloPlayback` und `HaloProbe`. `MediaKit`,
`JellyfinKit` und `DownloadWidget` tragen den Namen nicht und bleiben.

`docs/superpowers/specs/` ist ausgenommen. Datierte Entwurfsdokumente sind
ein Protokoll getroffener Entscheidungen; sie nachträglich umzuschreiben
hiesse, die Vergangenheit zu glätten.

**Die Bundle-ID wandert mit** — ausdrücklich so gewollt, mit bekanntem Preis:
`com.scarriffleservices.halo` ist für iOS eine *andere* App. Die installierte
Fassung bleibt daneben stehen, mit Downloads und Anmeldung in ihrem Container;
Halo startet leer. Einmal neu anmelden, heruntergeladene Folgen einmal neu
laden. Dasselbe gilt für die App-Gruppe, die sich TV-App und Top Shelf teilen.

**Eine Folge davon, die beim Entscheiden noch nicht auf dem Tisch lag:** der
Aufräumschritt aus dem vorigen Commit kommt an die alten Plex-Token nicht mehr
heran. Der Schlüsselbund grenzt nach Team und Bundle-Kennung ab — was unter
`…flixy` liegt, ist für `…halo` unerreichbar. Erledigt wird es trotzdem, nur
anders: **die alte App löschen.** iOS räumt deren Schlüsselbund-Einträge beim
Deinstallieren mit ab. Der Code bleibt stehen, weil er für jeden Stand vor
dieser Umbenennung richtig ist und im Jahr einen Startvergleich kostet.

Die Projektdatei ist aus `project.yml` neu erzeugt, der alte Ordner gelöscht
statt umbenannt — sie steht ohnehin nicht im Repo.

Das Verzeichnis des Repos selbst heißt weiter `Flixy`; das umzubenennen ist
ein Handgriff ausserhalb des Projekts.

368 Tests grün. iOS und tvOS bauen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 11:12:32 +02:00
ScarriffleandClaude Opus 5 3af6c3112f Das Web ist jetzt ein Projekt
Schritt 2 des Plans, fertig. Aus 1700 Zeilen in einer Datei sind zwölf
Module geworden: api, prefs, contrast, icons, util, types und die
Ansichten für Anmeldung, Startseite, Mediathek, Einstellungen, Kacheln
und Player. Alles typgeprüft, und die Prüfung läuft scharf —
noUncheckedIndexedAccess inbegriffen.

An einer Stelle hat der Compiler mich zu Recht aufgehalten: Object.fromEntries
verliert die Typen jedes einzelnen Feldes, und geblieben wäre nur ein
doppelter Cast, der die Prüfung stilllegt statt sie zu bestehen. Jetzt
steht es ausgeschrieben da.

hls.js wird erst beim Abspielen geladen. Es ist größer als der ganze Rest
zusammen, und wer nur stöbert, braucht es nie: der Start fällt damit von
632 auf 39 Kilobyte.

Geprüft mit einem erfundenen Medienserver dahinter: Seite kommt an,
gebaute Dateien werden ausgeliefert, Anfragen gehen weiter, und die
Startseite zeichnet Insel, Aufmacher und Reihen.

Die einzelne Datei bleibt liegen, bis das Projekt sich bewährt hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:31:56 +02:00