ed13d5fa433b15512f37f67fdc5527ef90dcbcd8
31
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ed13d5fa43 |
Kratzplatz, Markenerkennung, Tonabdruck — erfundene Wörter für Fachbegriffe
„Scratch disk" wörtlich zu übersetzen ist derselbe Fehler wie „Wachhund"
für Watchdog und „Keks" für Cookie. Und „Marke" ist kein Fachbegriff für
das, was gemeint ist: Jellyfin nennt diese Abschnitte Media Segments, und
das ist auch das Wort, in dem sich darüber reden lässt.
Kratzplatz* → Transcode* (was es ist, steht in FLARO_TRANSCODE)
Marke, Marken → Segment, Segmente
Markenerkennung → Segmenterkennung
Markenart/-suche → Segmentart/-suche
Tonabdruck → Fingerprint (Chromaprint nennt es so)
Tonabgleich → Fingerprintabgleich
Schlüsselbild → Keyframe
Bildabtaster → Frameabtaster
Gewöhnliches Deutsch bleibt gewöhnliches Deutsch: Wartung, Freigabe,
Meldung, Abspann, Vorspann. Das war nie das Problem.
**Was sich nicht ändert, und zwar mit Absicht.** Die drei
Einstellungsschlüssel `wartung.kratzplatz.paket.*` bleiben, wie sie sind —
sie stehen in der Datenbank, und ein neuer Name hiesse, dass die Werte
des Nutzers stillschweigend auf die Vorgabe zurückfallen. Dasselbe für
die Endpunktpfade `/marken`: sie sind nicht sichtbar, und ein Umbenennen
wäre Diff ohne Gewinn.
**Die Migration ist von Hand geschrieben.** `dotnet ef` hat ein Gerüst
erzeugt, das beide Tabellen löscht und neu anlegt — es sieht eine
verschwundene und eine neue Tabelle, nicht eine umbenannte. Damit wäre
jedes erkannte Segment und jeder Vermerk „hier wurde schon gesucht" weg
gewesen. `RenameTable` und `RenameIndex` tun dasselbe, ohne eine Zeile
anzufassen; nachgemessen an einer echten SQLite-Datei: Zeilen vorher 1
und 1, nachher 1 und 1, Inhalt unverändert.
Nachgezogen: in `bilder.ts` und `main.ts` heisst „marke" ein Häkchen und
das Logo — die blieben unberührt. Und „Segment" ist sächlich, was eine
Blindersetzung nicht weiss: „eine Segment" stand danach an vierzehn
Stellen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
32f592b83e |
Ein Abspann darf aus Stücken bestehen
Zwei Fälle fielen durch, beide am Bestand gesehen. Bei einem Marvel-Film liegt hinter dem Abspann eine Post-Credit-Szene: die Suche begann am Dateiende, fand nur den kurzen Rest dahinter und verwarf ihn als zu kurz. Und ein Abspann, den eine Szene in der Mitte teilt, zerfiel in zwei Reste, von denen keiner lang genug war. Dieselbe Ursache: der Abspann musste eine zusammenhängende dunkle Strecke bis zum Dateiende sein. Jetzt bildet die Suche Läufe, fasst sie über Lücken bis vier Minuten zusammen — so lang ist die längste bekannte Post-Credit-Szene plus Reserve — und lässt die **Summe** über die Mindestlänge entscheiden statt jedes einzelne Stück. Ein Lauf unter fünfzehn Sekunden ist eine Blende und zählt nicht mit. Dafür mussten die eindeutigen Indizes über (Titel, Art) weichen; sie standen mit dem Vermerk „eine Art einmal je Titel" da, und genau das stimmt nicht mehr. `SchreibenAsync` ersetzt jetzt die Menge, statt eine Zeile umzuschreiben — das ist der einzige Weg, der in beide Richtungen stimmt, wenn aus zwei Stücken wieder eines wird. **Drei Stellen trugen das nicht.** Im Web hiess „Abspann überspringen" bei einem Film immer Schluss — beim ersten von zwei Stücken bekäme niemand die Szene je zu sehen; geschlossen wird jetzt nur, wenn nichts mehr folgt. Auf iOS verwarf der Spieler die Abspannmarke eines Films ganz; diese Regel gehört nicht dorthin, sondern auf den Server, und ist ersatzlos weg. Und die Gesehen-Schwelle nahm irgendeine Abspannzeile — ohne `OrderBy` entschied die Datenbank; gemeint ist immer die früheste. Dazu der Schalter „Zusätzliche Szenen mit überspringen" (Vorgabe aus) bei den Geräteeinstellungen, und ein Messwerkzeug: ein Knopf auf der Filmseite lädt die Kurve je Schlüsselbild als Tabelle — Dunkelanteil des ganzen Bildes und der beiden Randstreifen. Ob bei Outtakes im Abspann die Ränder dunkel bleiben, ist eine Vermutung; bevor daraus eine Schwelle wird, will ich die Zahlen sehen. Fünf neue Prüfungen, darunter die wichtigste: ein durchgehender Abspann bleibt ein Stück. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f79ecf2757 |
„Schon durchsucht" und „hat eine Marke" sind zwei verschiedene Dinge
Genau dazwischen fehlte die Unterscheidung. Ein Film, bei dem die Suche einmal nichts fand, trägt einen Vermerk und keine Marke — und war damit für jeden weiteren Lauf erledigt, obwohl er der interessanteste Fall ist. Es gab nur alles oder nichts, und der Knopf bei der Mediathek schickte immer „alles": 453 Filme mal zwanzig Minuten Schlüsselbilder, auch für die vierhundert, bei denen längst etwas stand. Aus `bool erneut` wird `Markenumfang` mit drei Werten. `Neue` ist, was der Scan tut, und ändert sich nicht. `Luecken` nimmt sich vor, wo keine Marke steht — gleich ob dort schon einmal gesucht wurde. `Alles` ist der alte Holzhammer. Bei der Mediathek jetzt zwei Knöpfe, weil es zwei Fragen sind: „Fehlende Marken suchen" ist die alltägliche, „Alles neu durchsuchen" die seltene und teure, und deren Rückfrage nennt die Zahl der Titel. Auf der Detailseite bleibt es bei einem Knopf mit „alles" — wer ihn bei genau diesem Titel drückt, ist mit genau diesem Titel unzufrieden. Bei einer Serie wird eine Staffel ganz gelesen, sobald einer ihrer Folgen etwas fehlt. Billiger ginge es nur, indem man die Lücke gar nicht erst angeht, und genau dafür drückt jemand den Knopf. Vier Prüfungen am Endpunkt, die messen, ob der Lauf den Film anfasst: `Neue` überspringt den Durchsuchten, `Luecken` nimmt ihn sich vor, `Alles` sowieso, und ein Film mit Marke bleibt bei `Luecken` in Ruhe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
767491ef03 |
Serie für Serie, Film für Film — statt einer Zeile mit einer GUID
Die Markensuche lief stundenlang über 95 Serien, 4213 Folgen und 453
Filme und schrieb dabei fünf Zeilen, in denen Kennungen standen statt
Namen. „Markenerkennung für 4f3a…: 12 Marken." beantwortet keine der
Fragen, die in den letzten Tagen tatsächlich aufkamen — welche Staffel
nichts bekommen hat, warum, und ob die Serie überhaupt dran war.
Jetzt nennt jede Zeile den Titel, und es gibt eine je Serie, je Staffel
und je Folge:
„Biohackers": 2 Staffel(n), 18 Folge(n).
„Biohackers", Staffel 2: Vorspann in 8 von 8 Folge(n), Abspann in 1 von 8.
Biohackers S02E01 · Neuanfang: Vorspann 0:31–1:24, Abspann ab 41:12 (über das Bild).
Biohackers S02E07 · Letzte Fahrt: Vorspann 0:31–1:24, kein Abspann.
„Biohackers" fertig: 14 Marke(n).
Die Folgenzeilen werden **aus der Datenbank gelesen, nicht mitgezählt**:
so steht dort, was danach wirklich an der Folge hängt, und nicht, was
dieser eine Lauf geschrieben hat. Nur das beantwortet „warum kommt bei
dieser Folge kein Knopf?".
**Drei Gründe statt eines `false`.** `FilmAsync` gab für „keine Datei",
„schon durchsucht" und „nichts gefunden" dasselbe zurück, und von aussen
waren sie nicht zu unterscheiden. Jetzt sagt jeder seinen Namen.
**Und eine Live-Zeile bei der Mediathek**, wie der Scan sie hat. Sie
benutzt denselben Mechanismus — `Scanschritt` mit einer neuen Phase,
derselbe Endpunkt, ein eigenes Feld. Ein eigenes, weil der Scan sich auf
„fertig" setzt, sobald er die Markensuche angestossen hat; in sein Feld
geschrieben sähe er wieder laufend aus.
**Zwei Fehler sind dabei herausgefallen.** In `NachScanAsync` stand
`if (serien.Count == 0) return;` vor dem Filmteil — eine reine
Filmmediathek bekam nach einem Scan also nie eine Abspannsuche. Und die
fpcalc-Prüfung stand ganz oben und galt für beides, obwohl der Bildweg
fpcalc gar nicht braucht: ohne fpcalc fand der Server keinen einzigen
Filmabspann.
Dazu ein Suchfeld im Protokoll und tausend Anfangszeilen statt
dreihundert. Ohne das wäre eine Zeile je Folge keine Auskunft, sondern
eine Mauer — genau der Fall, gegen den sich `ScanCoordinator` für den
Scan schon einmal entschieden hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ce4da992ea |
Einen Knopf je Serie ist bei fünfundneunzig kein Angebot
Es gab ihn: auf der Serienseite, das Uhrsymbol. Nur gilt er für genau eine Serie — und nach jeder Änderung an der Erkennung will man ohnehin alles noch einmal. Der Weg dorthin fehlte da, wo man ihn sucht: bei der Mediathek, neben „Scannen" und „Metadaten auffrischen". Der Lauf selbst war schon da. `NachScanAsync` macht genau das, Serien über den Tonabgleich und Filme über das Bild — er hing bloss am Scan und liess sich nicht von Hand anstossen. Jetzt nimmt er ein „erneut" entgegen und ist über einen Endpunkt erreichbar. Die Antwort kommt sofort: über einen grossen Bestand dauert das Stunden, und eine Anfrage so lange offen zu halten hiesse, sie in ein Zeitlimit laufen zu lassen. Wie weit es ist, steht im Protokoll von heute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7673c73757 |
Vier Sekunden, die ffprobe zu früh aufhörte zu lesen
Die Untertitel kamen vier Sekunden zu spät. Nicht an ihnen lag es: der
Server meldete dem Spieler einen Anfang, der vor dem lag, an dem der
Strom wirklich begann, und genau diese Differenz landet eins zu eins im
Versatz.
Die Schlüsselbildsuche las mit `-read_intervals` bis eine halbe Sekunde
hinter den Wunsch. ffprobe bricht am Ende eines Fensters auf Paketebene
ab — das letzte Schlüsselbild darin kam regelmässig gar nicht mehr
heraus, und die Suche gab das davor zurück. Bei Bildgruppen von vier
Sekunden sind das vier Sekunden.
Nachgemessen gegen die Stelle, an der ffmpeg tatsächlich anfängt, an
einer Datei mit Schlüsselbildern alle vier Sekunden:
Wunsch Suche (0,5 s) ffmpeg Abweichung
52,0 48 52,0 −4,0
73,3 68 72,0 −4,0
100,2 96 100,0 −4,0
Mit zehn Sekunden Nachlauf lag dieselbe Messung bei allen sieben Werten
auf 0,000. Was über den Wunsch hinausgelesen wird, verwirft die
Filterung ohnehin.
Die Prüfung vergleicht nicht gegen eine erwartete Zahl, sondern gegen
ffmpeg selbst — mit dem alten Wert fällt sie um.
Dazu der Filmteil der Abspannerkennung: `Abspannkante` als reine
Rechnung mit elf Prüfungen, `Bildabtaster` über ffmpeg, Marken auch an
Filmen, Knöpfe zum Suchen und Verwerfen auf der Filmseite. An einer
erzeugten Datei trifft die Kante auf vier Sekunden genau.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1d70be0f99 |
Fünf Aufmacher je Titel, und der Abspann zählt als Schluss
**Der Aufmacher darf wechseln.** Bisher hing hinter jedem Titel genau ein Hintergrundbild, und das Karussell war nach einer Woche ein Standbild. Jetzt lassen sich bis zu fünf Aufnahmen aussuchen — im Bilderdialog unter einem vierten Reiter, gespeist aus denselben Hintergründen. Welches gerade gilt, entscheidet der Server aus Uhrzeit und Kennung: alle fünf Stunden ein anderes, überall dasselbe — im Web, in der App, nach einem Neustart. Ein Zufall je Abruf hiesse, dass das Bild beim Blättern springt; eine gespeicherte Wahl hiesse einen Schreibvorgang je Titel und Fenster. Die Kennung geht mit in die Rechnung, damit nicht alle Titel im selben Augenblick umschalten. Plakat und Hintergrund bleiben unberührt. Sie stehen hinter Kacheln und Kopfzeilen, und dort soll nichts wechseln. **Und der Abspann zählt als Schluss.** Eine Serie hängt sechs Minuten Abspann an eine Folge von fünfundvierzig; neunzig Prozent liegen dann mittendrin, und die Folge gilt erst als gesehen, wenn man ihn sich ganz ansieht. Die Schwelle herunterzudrehen hiesse, jeden anderen Titel zu früh abzuhaken. Der erkannte Abspann ist die genauere Antwort auf dieselbe Frage — er zieht die Schwelle nach vorn, nie nach hinten. Dazu der Verlauf über dem Aufmacher: er war immer noch zu dunkel. Jetzt trägt der Schattenwurf der Schrift, was vorher eine Decke über dem ganzen Bild tat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8cf2118723 |
Vorspann und Abspann — erkannt, abgelegt, überspringbar
Beides mit derselben Rechnung, nur in einem anderen Fenster: vorn die ersten fünfzehn Minuten, hinten die letzten zehn. Was `Tonabgleich` findet, hängt allein daran, welchen Ausschnitt man ihm gibt — eine zweite Erkennung für den Abspann wäre dieselbe Suche ein zweites Mal. Je Staffel verglichen, nicht je Serie: Serien wechseln ihren Vorspann zwischen Staffeln, und über alle hinweg fände man nichts oder das Falsche. Gefundenes über fünf Minuten wird verworfen — zwei Folgen, die dieselbe Datei tragen, teilen vierzig, und darüber stünde sonst ein Überspringen-Knopf. Der Lauf geht auf Zuruf, nicht bei jedem Scan: er liest von jeder Folge ein Stück Ton und dauert Minuten. Der Knopf dafür steht auf der Serienseite, bei den Verwalterknöpfen. Die Marken gehen im Folgenvertrag mit — als „intro" und „credits", die Wörter, die die Apps von Jellyfin her ohnehin kennen. In der App zeichnet sie der vorhandene Überspringen-Knopf, der bisher nie etwas zu tun hatte; im Web ist er neu, rechtsbündig über der Zeitleiste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
acba683c94 |
Eine Folge weiter, eine zurück — ohne den Spieler zu verlassen
Wer eine Serie schaut, will blättern können. Bisher gab es genau einen Knopf dafür, und der erschien erst am Ende: „Nächste Folge", wenn der Abspann durchgelaufen war. Zurück ging gar nicht — nur über die Serienseite, und die beendet die Wiedergabe. Jetzt stehen beide Knöpfe in der Bedienleiste, in der App wie im Web, links und rechts vom Abspielknopf. Nur bei Serien: bei einem Film antwortet der Server mit 404, bei der letzten Folge mit 204, und beides heisst hier „kein Knopf". Sie erscheinen also von selbst nur dort, wo sie etwas tun. Der Server kannte nur den Weg vorwärts. Jetzt gibt es `/previous` daneben, und beide Richtungen kommen aus derselben Rechnung — als zwei Endpunkte abgeschrieben liefen sie auseinander, sobald einer etwas dazubekommt. Jellyfin hat kein Gegenstück zu „NextUp"; dort entsteht die vorige Folge aus der Folgenliste der Serie. Beim Wechsel gilt weiter, was für die Serie an Sprache eingestellt ist. Ohne das spränge die nächste Folge auf die erste Tonspur der Datei zurück — bei deutschen Veröffentlichungen also auf Deutsch, mitten in einer Serie, die man im Original schaut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fb8ad1c831 |
Der Stand lag daneben und wurde trotzdem weggeworfen
Der Fortschrittsbalken ist an **einer** Stelle gebaut und wird in jeder
Kachel verwendet — Web wie App. Fehlte er, dann nicht an der Kachel,
sondern weil der Server `State: null` schickte. Drei Listen taten das,
obwohl die Stände in derselben Abfrage danebenlagen:
/home/suggestions — „Weil du Ähnliches gesehen hast"
/home/seasonal — die saisonale Reihe
/movies/{id}/similar — „Ähnliche Titel"
Ausgerechnet die erste entsteht aus dem Sehverhalten: ein halb gesehener
Film sah dort aus wie ein unangetasteter, während dieselbe Kachel in der
Mediathek einen Balken trug. `ZuständeZu` gab es längst, es war nur
privat.
**Und „Als Nächstes" lässt sich jetzt räumen.** Das vorhandene Weglegen
reichte nicht dorthin: die Reihe liest `Watched` und `InProgress`, nie
`Dismissed` — und bei einer nie gestarteten Folge ist `InProgress` ohnehin
`false`. Weggelegt wird deshalb die **Serie**, nicht die Folge: beim
nächsten Laden rückte sonst die übernächste nach, und man legte
zwanzigmal weg. Den Zustand dafür gibt es schon, er wurde nur nie
gelesen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
22be288fc3 |
Welche Sprachen eine Serie durchgehend führt
Das Menü auf der Serienseite bot zwanzig Sprachen an, von denen die Serie
zwei hatte. Um das zu ändern, muss jemand wissen, welche es wirklich
gibt — und zwar in einer Abfrage: zwanzig Folgen einzeln zu holen wären
zwanzig Rundreisen, und am Ende müsste der Client dieselbe Rechnung
anstellen.
`GET /api/v1/series/{id}/languages` nennt Ton- und Textsprachen als
**Schnittmenge** über alle Folgen. Eine Sprache anzubieten, die es nur in
Staffel 1 gibt, hiesse: eine Einstellung für die Serie, die ab Staffel 2
stillschweigend etwas anderes tut. Was dabei wegfällt, wird gezählt —
damit die Oberfläche es sagen kann, statt es zu verschweigen.
Zwei Dinge, die sonst falsche Ergebnisse gäben:
Gruppiert wird nach dem **Namen**, nicht nach dem Kürzel. Eine gewachsene
Sammlung trägt „ger" und „deu" nebeneinander; nach dem Kürzel gezählt
fiele Deutsch aus der Schnittmenge, obwohl es überall liegt.
Gezählt werden nur Folgen, die eine **Datei** haben. Eine angekündigte
Folge kann keine Sprache beisteuern; sie als fehlend zu zählen liesse das
Menü bei jeder laufenden Serie leer.
Die Rechnung steht in `Flaro.Core`, nicht im Endpunkt: sie hängt an keiner
Datenbank und lässt sich so prüfen, ohne eine Serie anzulegen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e05bbfc2cd |
Einen einzelnen Titel neu holen, statt achthundert
Eine neue Quelle wirkt erst beim nächsten Holen der Metadaten. Das ging bisher nur je Mediathek: achthundert Titel, eine Viertelstunde — nur um an einem Film zu sehen, ob ein Schlüssel etwas bringt. Wer auf einer Detailseite bemerkt, dass IMDb und Rotten Tomatoes fehlen, hatte von dort keinen Weg dorthin. Der Server kann eine Auswahl längst; es fehlte bloss die Kennung der Mediathek in der Antwort, damit die Seite weiss, wohin ihr Titel gehört. Der runde Knopf holt diesen einen Titel, wartet den Lauf ab und zeichnet die Seite neu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
af2b844d65 |
IMDb, Rotten Tomatoes und Metacritic neben der TMDB-Wertung
IMDbs eigene Schnittstelle kostet Geld, Rotten Tomatoes vergibt Zugänge nur an Partner, Letterboxd hat gar keine offene. OMDb liefert alle drei in einer Antwort, über die IMDb-Kennung, die ohnehin schon an jedem Film steht. **Eine Tabelle und kein Feld je Quelle.** `Movie.Rating` ist ein Skalar ohne Quellenangabe, und das war richtig, solange es eine Quelle gab. Für drei weitere wären es drei Spalten — und bei der vierten wieder eine Migration. Jetzt ist jede Quelle eine Zeile, je Titel und Quelle eine, mit eindeutigem Index darauf: ein zweiter Lauf schreibt um statt anzuhäufen. **Zwei Werte je Bewertung, und beide gebraucht.** Der normierte Wert zum Vergleichen und Sortieren, die Anzeige zum Hinschreiben. Rotten Tomatoes in „8,7 von 10" umzurechnen wäre eine Lüge über die Quelle — dort steht 87 %, und genau so kennt man es. **Wieder keine zweite Metadatenquelle.** `IBewertungsquelle` liefert Bewertungen, sonst nichts. Bei Titel und Beschreibung wären zwei Anbieter zwei Wahrheiten; bei einer Bewertung ist jede Quelle eine eigene Zahl, und da sind mehrere kein Widerspruch, sondern der Sinn der Sache. **Ohne Kennung wird nicht gefragt.** OMDb kennt nur den Weg über die IMDb-Kennung; nach einem Titel zu suchen hiesse, denselben Zuordnungsfehler ein zweites Mal zu riskieren. Und auf Unbekanntes antwortet OMDb mit 200 und `Response: "False"` — das als Treffer durchzulassen legte eine leere Bewertung ab, die danach als „keine Wertung" dastünde. Angezeigt wird mit Kürzel statt Stern: vier gleich aussehende Zahlen nebeneinander sagen nicht, woher sie kommen. Kein Logo — die Marken von IMDb und Rotten Tomatoes mitzuliefern hiesse, fremde Zeichen auszuliefern. Kein mitgelieferter Schlüssel, aus demselben Grund wie bei fanart.tv: OMDb begrenzt auf tausend Abrufe am Tag je Schlüssel. Einzutragen unter Einstellungen → Übersicht → Metadaten. 740 Tests grün, davon acht neue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c65356c16f |
4K, Full HD, HD, SD — die Zahl steht beim Überfahren
„1080p" ist eine Angabe für den, der eine Stufe einstellt. Wer sich fragt,
ob es sich lohnt, dafür den Fernseher anzumachen, liest „Full HD". Die
Zeile sagt jetzt das Wort, der Tooltip den genauen Wert und die gemessene
Grösse: `1080p · 1920 × 800`.
**Es gab zwei Leitern, und sie waren sich uneinig.** `Benennung.AusHoehe`
machte aus 2160 ein „2160p", `FileNames.FassungAusHoehe` ein „4K" — auf
derselben Detailseite stand beides übereinander, ein paar Zeilen
auseinander. Die zweite ist weg; sie hatte ausserdem nur die Höhe und
vergab sich damit beim Kinoformat.
Gemessen an zwei echten Dateien: 1920×800 heisst „Full HD" (nur an der
Höhe gemessen hiesse es „HD", und daneben stünde ein 1280×720 mit
demselben Wort), 720×576 heisst „SD".
**1440p fällt unter Full HD.** „QHD" kennt kaum jemand, und ein fünftes
Wort für etwas zwischen zwei bekannten hilft beim Entscheiden nicht.
„8K" steht nur da, damit eine 8K-Datei nicht als 4K gemeldet wird.
**Der Client zerlegt nichts mehr.** Er las die Stufe bisher mit
`display.split(' · ')[0]` heraus — richtig, solange vor der Auflösung nie
etwas anderes steht. Jetzt kommen Wort und genauer Wert als eigene Felder.
**Die Fassung wird nicht mehr gespeichert, sondern gerechnet.** Sie stand
in der Spalte und war damit das, was beim letzten Scan galt: ein neues
Wort hätte keine bestehende Zeile erreicht. Jetzt hält die Spalte nur
noch, was im Dateinamen stand („Mobil"), und die Messung kommt frisch
dazu. Eine Migration räumt die alten gerechneten Werte weg.
**Nicht angefasst:** was eine *gewählte* Stufe ist statt einer gemessenen
— die Umkodierleiter, der Qualitätswähler im Spieler, die Stufen in den
Apps. Wer eine Stufe wählt, wählt eine Zahl.
Die Apps rechnen sich das Wort ohnehin selbst aus und sagen seit jeher
„4K" und „Full HD"; der Server ist ihnen jetzt angeglichen, nicht
umgekehrt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
10a5070e58 |
Eine Folge ist weder ein Film noch eine Serie
Die App meldete auf der Startseite „Dieser Titel ist auf dem Server nicht
mehr vorhanden" — über eine Folge, die sie selbst gerade anzeigte.
Gemessen im Quelltext: `item(id:)` probiert `/movies/{id}`, dann
`/series/{id}`, und wirft sonst `.notFound`. In „Weiterschauen" steht
eine Folge mit ihrer eigenen Kennung, und einen Endpunkt für eine
einzelne Folge gab es gar nicht. Jetzt gibt es `GET /api/v1/episodes/{id}`
— mit `SeriesId`, denn ohne die Serie fehlen der Folge Titel und Bild.
Im Simulator nachgestellt und nachgeprüft: die Seite steht.
**Zuletzt hinzugefügt.** Die App zeigte „Filme" und „Serien" — den Anfang
der Mediathek in der Reihenfolge der Datenbank, also immer denselben.
Jetzt je Mediathek eine Reihe „Neu in …", sortiert nach Zugang. Die
Kennungen beginnen nun mit `latest`, und daran hängen zwei Einstellungen,
die an einem Flaro-Server deshalb nichts taten: „Gesehenes in Neuzugängen
ausblenden" und „Aufmacher nur aus Neuzugängen".
**Geplante Titel.** `MediaItem` kennt jetzt `availableFrom`. Aufmacher und
Detailseite zeigen statt des Knopfes „Bald ist es soweit" mit dem Termin.
Die Kachel spielte trotzdem los — im Simulator kam der Spieler bis „Wird
vorbereitet …" —, also zählt ein geplanter Titel jetzt wie eine Sammlung:
nichts zu starten, der Tipp führt auf die Seite.
**Und das Bild im Aufmacher.** Bei einer angefangenen Folge stand dort ihr
Standbild statt des Bildes der Serie; wer der Serie eines ausgesucht
hatte, fand ein anderes vor. Die Kachel behält das Standbild — sie zeigt,
wo man steht —, der Aufmacher bekommt `SeriesBackdropImage`.
**Die Punkte im Karussell waren 26 × 3 Pixel.** Von drei Klicks auf
denselben Punkt traf nur der auf der Mittellinie; fünf Pixel daneben
geschah nichts. Der Knopf ist jetzt 27 Pixel hoch, sichtbar bleiben 3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
343b0cdcfb |
Ein Knopf, der nichts tut, ist schlimmer als keiner
Der geplante Film zeigte weiter „Abspielen" und „Herunterladen", und wer
drückte, bekam „Dieser Titel ist ab dem 6.10. um 18 Uhr verfügbar". Die
Sperre im Server war richtig; die Oberfläche wusste nur nichts davon.
Gemessen: die Liste führte `PublishAt` seit jeher mit, die Einzelabfrage
`GET /api/v1/movies/{id}` nicht — und die Detailseite entscheidet genau
daran. `publishAt` kam also immer als `null` an, die Prüfung lief ins
Leere. Dasselbe fehlte bei `GET /api/v1/series/{id}` und bei den Folgen.
Jetzt steht dort, wo sonst der Knopf stünde: „Bald ist es soweit", mit
dem Termin darunter. Kein Versprechen, das beim Klicken zurückgenommen
wird.
Der Test prüfte bisher nur die Liste — darum fiel es nicht auf. Er fragt
jetzt die Detailseite und schlägt ohne die Korrektur fehl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
255eb7c2f0 |
Spider-Man ist nicht 720p, er ist breit
Die Auflösung wurde allein aus der Höhe gebildet. Ein Kinoformat von 2.39:1 liegt als 1920×800 vor — nach der Höhe gerechnet kam „720p" heraus, und das stand so in der Oberfläche. Jetzt zählt die grössere von Breite und Höhe; Hochformat kippt damit nicht, und 4K-Breitbild heisst nicht mehr 1440p. Die Detailansicht hat zwei Leserschaften, und bisher bekamen beide dasselbe. Wer schauen will, sieht jetzt Laufzeit, Auflösung und die Sprachen — jede einmal, statt „Englisch · Englisch · Englisch · Englisch · Deutsch". Wer die Mediathek betreut, sieht darunter Dateiformat, Grösse und den Ordner. Der Ordner verrät eine falsche Zuordnung in einer Sekunde: steht über der Beschreibung „Hanni & Nanni 3" und der Ordner heisst „Hanni und Nanni", ist die Sache klar. Er kommt nur für Administratoren überhaupt vom Server — versteckt im Client wäre er weiterhin auslesbar. Ein geplanter Titel war unsichtbar. Das ist die Hälfte, die nichts bringt: dass er kommt, ist der Sinn des Planens. Er steht jetzt mit „Verfügbar ab …" in der Mediathek, und die Datei geht trotzdem nicht heraus — Download wie Strom lehnen bis zum Termin ab, auch für Administratoren. Wer vorziehen will, hat „Jetzt veröffentlichen"; eine Hintertür für eine Rolle wäre eine Regel, die manchmal gilt. Was ein Auffrischen geändert hat, steht jetzt unter „Zu prüfen" — in einer eigenen Gruppe mit „Zur Kenntnis" statt „Veröffentlichen". Diese Titel sind sichtbar; es gibt nichts freizugeben, nur etwas abzuhaken. Und das Protokoll sagt endlich, wo es knallt. „InvalidOperationException: Nullable object must have a value." ohne eine einzige Zeile Herkunft ist für niemanden eine Diagnose — weder für den Betreiber noch für mich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fcf40e4394 |
Der Orden des Phönix stand vor dem Stein der Weisen
Alphabetisch stimmt das, nur sieht man sie andersherum. Es gibt jetzt einen Sortiertitel: „Harry Potter 1", „Harry Potter 2" — und die Reihe steht in der Ordnung, in der man sie schaut. Leer gelassen bleibt alles wie bisher, beim Titel ohne Artikel davor. Er hat einen eigenen Merker, nicht den des Titels. Wer die Reihenfolge festlegt, soll den Titel trotzdem weiter automatisch bekommen; beides an einem Schalter hiesse, für das eine auf das andere zu verzichten. Eine Korrektur der Zuordnung lässt ihn ebenfalls stehen. Und WALL·E bleibt nicht mehr unter E. Die Kachel wurde nach einer Korrektur zwar neu beschriftet, blieb aber an ihrem Platz — jetzt rutscht sie dorthin, wo sie hingehört. Liegt der Platz im noch nicht geladenen Teil der Liste, geht sie weg und sagt wohin, statt falsch stehenzubleiben. Dazu ein Dialog, in dem alles zu einem Titel beieinander steht: Titel, Sortiertitel, Bilder, Zuordnung. Die vier Menüpunkte gab es schon, jeder für sich richtig — zusammen aber kein Ort. Wer einen Film geraderückt, macht selten nur eines davon. Die Abkürzungen bleiben. Beim Bauen gefunden: das Zurücksetzen des Sortiertitels endete in einer 500. SortTitle.For ist eine C#-Methode, und EF Core übersetzt sie nicht nach SQL. Es geht um eine Zeile — die wird jetzt geladen und zurückgeschrieben, statt die Regel ein zweites Mal als SQL zu schreiben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
557708b769 |
Ein Klick, der nichts tat, und niemand sagte warum
Eine Serienkachel kannte der Server als Titel nicht: „als gesehen markieren" lief in eine 404, und weil das Kontextmenü die Ablehnung verschluckte, geschah sichtbar gar nichts. Dasselbe beim Favoriten. UserItemState bekommt eine Serienkennung, das Häkchen zieht alle Folgen mit, und was wirft, erscheint als Meldung statt in der Browserkonsole. Dazu eine Mehrfachauswahl in der Mediathek — gesehen, ungesehen, Metadaten neu holen für das Gewählte, in einer Anfrage statt in zweihundert. Ein neues Plakat steht sofort auf der Kachel, und ein Titel, den es nicht mehr gibt, endet nicht mehr in einer Sackgasse. Gemessen: ein POST auf /refresh ohne Rumpf antwortete nach der Erweiterung mit 404 — also genau der Aufruf „alles auffrischen", den es länger gibt als die Auswahl. Der Rumpf wird jetzt von Hand gelesen, und beide Formen stehen in einer Prüfung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c854c4a09a |
451 gegen 461, und ein Band für das, was noch wartet
Drei Zahlen, die alle stimmen und Verschiedenes meinen: Plex zählte 451 Filme, Flaro 449, und der Scan lief über 461 Dateien. Die Mediathekszeile nennt sie jetzt getrennt — Titel, Folgen, Dateien, zweite Fassungen und nicht Zugeordnetes. Damit beantwortet sich die Frage selbst, statt offen zu bleiben. Die Rechnung dahinter war beim ersten Versuch falsch: sie verglich gegen alle Folgen und kam auf 1223 Titel bei 324 Dateien. Gezählt wird jetzt, zu wie vielen Titeln überhaupt noch eine Datei daliegt — dann geht Dateien = Titel + Fassungen + ohne auf, auch wenn etwas vermisst wird. Nachgemessen mit einer echten Plex-Kopie: 3 Titel, 4 Dateien, 1 zweite Fassung. Dazu trägt eine Kachel, die noch auf Freigabe wartet, jetzt ein Band — „Wartet" oder „Geplant". Für einen Administrator standen wartende Titel bisher mitten zwischen den freigegebenen, ohne dass etwas sie unterschied; wer einen suchte, fand ihn nur über die Prüfliste. Dafür reicht `PublishAt` jetzt bis in die Kachel durch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8809b9f46f |
Die Mediathek hörte bei I auf, und jede Folge stand doppelt
Zwei Befunde aus dem laufenden Betrieb. Die Liste holte genau eine Seite. Der Server gibt höchstens 200 Einträge je Zug — das ist richtig so —, nur fragte die Oberfläche nie nach der zweiten. Bei 455 Filmen endete die Mediathek mitten im Alphabet, ohne Hinweis, dass da noch etwas kommt. Jetzt wird beim Scrollen nachgeladen, und die Zahl sagt „200 von 455" statt einer Behauptung, die man beim Scrollen widerlegt. Dabei kam heraus, dass ein IntersectionObserver eine Seite braucht, die auch gezeichnet wird: im Fenster im Hintergrund löst er nicht aus, und beim Prüfen blieb die Liste genau deshalb bei 200 stehen. Daneben steht jetzt eine Prüfung am Scrollereignis, die überall greift. Der zweite Befund: jede Folge zeigte zwei gleich aussehende Knopfreihen. Es sind zwei Dateien — Plex' „Optimized for Mobile"-Kopien. Flaro kannte „optimized", aber nicht „Optimized for Mobile", und sah ohnehin nur den unmittelbaren Elternordner; Plex legt die Fassung aber als `Plex Versions/Optimized for Mobile/<Serie>/<Staffel>/…` ab. Jetzt wird drei Ebenen hoch gesucht, die Schublade `Plex Versions` benennt keine Serie mehr, und an der Folge steht „Mobil" statt einer zweiten, unerklärten Reihe. Nicht um Plex zu bedienen — die Dateien liegen nun einmal so auf der Platte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
98565e14b5 |
Der Ordner bestimmt die Serie, nicht der Dateiname
Aus einer Serie wurden siebenhundert. Dateien wie "S01E001_Aang.mkv" tragen vor der Folgennummer nichts, also wurde der ganze Dateiname zum Serientitel — jede Folge eine eigene Serie. Und umgekehrt ergab "Doctor Who (2005) - S12E04.mkv" im Ordner "Doctor Who" zwei Serien, weil die Jahresangabe nur im Dateinamen stand. Jetzt gilt: was unter Serien/Pingu/ liegt, gehört zu Pingu. Egal wie tief, egal wie die Datei heisst. Der Ordner ist die verlässlichere Angabe, weil ihn ein Mensch angelegt hat; so macht es Plex seit je und aus demselben Grund. Staffel und Folge liest weiterhin der Dateiname. Ohne bekannte Wurzel bleibt es beim Dateinamen — dort könnte über dem Staffelordner irgendetwas stehen. Diesen Weg gehen nur die Tests. Dazu vier Dinge aus der Benutzung: Die Mediatheken standen scheinbar in zufälliger Reihenfolge. SQLite vergleicht Text zeichengenau, und dort stehen alle Grossbuchstaben vor allen kleinen: "Serien" landete über "film". Jetzt ohne Rücksicht auf Gross- und Kleinschreibung — und umsortierbar über zwei Pfeile. Nicht über Ziehen: das ist schöner, aber mit der Tastatur unbedienbar und auf dem Telefon ein Glücksspiel. Die Sprache des Servers steckte im Dialog "Metadaten", zusammen mit dem TMDB-Schlüssel — also dort, wo niemand nach einer Sprache sucht. Wer die Mediathek auf Englisch vorfand, sah nur "Meine Sprache" und hielt das für die einzige Möglichkeit. Jetzt eine eigene Zeile. Das Kontextmenü gab es nur per Rechtsklick, und den findet nur, wer ihn vermutet — auf dem Telefon gibt es ihn gar nicht. Jetzt drei Punkte auf jeder Kachel, beim Überfahren und bei Tastaturfokus, auf Geräten ohne Zeiger dauerhaft. Und "Zuordnung korrigieren" steht endlich darin: wer zwanzig falsch erkannte Titel vor sich hat, soll sie dort geradeziehen können statt über vierzig Seitenwechsel. Folgen ohne eigenes Bild nehmen das der Serie. TMDB liefert Folgenbilder nur für einen Teil der Folgen und bei kleineren Serien für gar keine; eine graue Fläche sagt nichts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e1399c808f |
Die Detailseite trägt wieder
Drei Dinge, eins davon war mein Fehlbefund: Regie und Drehbuch werden sehr wohl angezeigt — nur standen sie irgendwo zwischen dreissig Gesichtern. Jetzt kommt der Stab zuerst, weil man beim Suchen eher die Regie im Kopf hat als den zwölften Darsteller. Bei Zeichentrick hängt TMDB "(voice)" an den Rollennamen, und so stand es auch da — englisch, mitten im deutschen Text. Daraus wird "Stimme: Gru". Ähnliche Titel als eigene Reihe, nachgeladen, damit die Seite sofort steht. Aus dem eigenen Bestand gerechnet, nicht aus TMDBs Empfehlungsliste: ein Vorschlag, den man nicht abspielen kann, ist Werbung. Gemeinsame Menschen wiegen dreifach gegenüber gemeinsamen Genres — dass zwei Filme "Drama" sind, sagt wenig; dieselbe Regie sagt viel. Kostet keine einzige fremde Anfrage, der Katalog steht ohnehin in der Datenbank. Findet sich nichts, erscheint die Reihe gar nicht. Und der Hintergrund: er begann bei left: 240px, also hinter der Seitenleiste, und war mit opacity .28 und blur(2px) weder Bild noch Fläche. Jetzt über die volle Breite, kräftiger unscharf — er taucht die Seite in die Farbe des Films, so wie bei Plex. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0cd88acf70 |
Aus "DEU EAC3 6.0" wird "Deutsch · Dolby Digital Plus · 5.1"
Die Werte gingen roh von ffprobe in die Anzeige. Jedes Stück war richtig und keines verständlich: EAC3 heisst Dolby Digital Plus, MOV_TEXT heisst schlicht Text, und sechs Kanäle sind 5.1 — "6.0" ist eine Schreibweise, die es nicht gibt. Die Auflösung stand als gemessene Höhe da, also "1040p" für ein beschnittenes Breitbild, was aussieht wie ein Fehler. Benennung macht daraus Sätze, und zwar im Kern: die Apps brauchen dieselben, und zweimal formuliert heisst dieselbe Tonspur am Fernseher anders als im Browser. CultureInfo taugte dafür nicht, zweimal gemessen: sein NativeName liefert den Namen in der Sprache selbst — "English", "français" —, und für ein erfundenes Kürzel wie "xyz" erfindet es stillschweigend eine Kultur, statt zu scheitern. Also eine eigene Liste, über das zwei- und das dreibuchstabige Kürzel, weil beide in Mediendateien vorkommen und ISO 639-2 für manche Sprachen zwei kennt. Unbekanntes bleibt stehen statt zu "Unbekannt" zu werden — die rohe Angabe ist immer noch eine Auskunft. Und "und" verschwindet ganz: ffmpeg schreibt das für unbekannt, und als Sprache angezeigt sieht es aus wie eine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
921533050b |
Zwei Titel je Film, und die Sprache lässt sich wählen
Die Mediathek sah zweisprachig aus — "Despicable Me" neben "Ich einfach unverbesserlich". Es gab nur ein Titelfeld, und welche Sprache darin landete, entschied die Einstellung zum Zeitpunkt des Scans. Movie und Series tragen jetzt zusätzlich OriginalTitle. Gesucht wird über beide: wer "Despicable Me" tippt, findet "Ich einfach unverbesserlich". Auf der Detailseite steht der Originaltitel unter dem übersetzten, wenn er abweicht. Die Sprache der Metadaten ist in der Verwaltung eine Auswahl statt eines Textfelds. Vorher musste man "de-DE" wissen, und ein Tippfehler darin nahm TMDB stillschweigend als Englisch an. Dazu eine eigene Sprache je Benutzer. Das Ehrliche daran steht im Dialog: die Daten liegen in einer Sprache vor, und wer eine andere wählt, bekommt, was übersetzt vorliegt — für den Rest die Serversprache, nicht ein leeres Feld. ItemTranslation hält die zweite Sprache, gefüllt über eine Sammelaktion. Nicht nebenbei beim Öffnen: das verteilte die Wartezeit über Wochen und träfe immer den, der gerade etwas sucht. Alle Sprachen für alle Titel vorzuhalten wäre der Bestand mal Sprachen an Abrufen und Speicher, für einen Haushalt mit zweien. Deshalb auf Ansage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f359807561 |
Der Fortschritt sagt, woran er gerade ist — und schreibt es mit
Vorher stand in der Verwaltung minutenlang "Wird durchsucht…", ohne Hinweis, ob überhaupt etwas passiert. Bei vierzigtausend Dateien ist das nicht zu unterscheiden von einem hängenden Lauf. Scanschritt trägt Phase, Gegenstand und Zählstand und bildet daraus den Satz selbst — im Kern und nicht in der Oberfläche, weil die Apps denselben brauchen und zweimal formuliert er auseinanderdriftet. Sechs Phasen von "Suche Medien in /media/Filme" über "Suche ‚Ich einfach unverbesserlich' bei der Metadatenquelle — 44 von 1204" bis "Räume auf und lege Doppeltes zusammen". Pfade werden auf die letzten zwei Bestandteile gekürzt. ScanAsync und EnrichAsync bekommen je einen optionalen IProgress; optional heisst, dass kein vorhandener Aufrufer bricht. Der Koordinator hält nur den letzten Schritt — eine Liste über vierzigtausend Titel wäre ein Leck im Arbeitsspeicher. Dazu das Tagebuch: eine Datei je Tag unter config/logs, nach lokaler Zeit und nicht nach UTC, weil ein Scan um 23 Uhr am heutigen Datum gesucht wird. Dreissig Tage Aufbewahrung, einstellbar über FLARO_LOG_TAGE — ohne Grenze läuft die config-Partition eines NAS lautlos voll. Es wirft nie: ein volles Dateisystem darf keinen Scan abbrechen. In der Verwaltung lesbar, nach Fehlern filterbar und herunterladbar. Das Containerprotokoll ist nach einem Neustart weg und nur über SSH erreichbar — die Frage "warum wurde der Film gestern Nacht nicht erkannt?" liess sich damit nicht beantworten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8b67a7ec88 |
Verschwundene Dateien werden gemeldet, nicht stillschweigend gelöscht
Vorher räumte der Scan jede Datei weg, die er nicht mehr sah, und mit der letzten Datei fiel der Titel samt Sehfortschritt aller Benutzer. Für eine wirklich gelöschte Datei ist das richtig. Für eine Platte im Ruhezustand, ein noch nicht eingehängtes Volume oder einen umbenannten Ordner ist es Datenverlust — und für den Scan sehen alle vier Fälle gleich aus. Jetzt trägt MediaFile ein MissingSince. Der Eintrag bleibt stehen, steht in der Verwaltung unter "Nicht mehr gefunden", und gelöscht wird auf Ansage. Taucht die Datei wieder auf, verschwindet die Markierung von selbst. Dazu die Notbremse, und sie ist der eigentliche Grund für den ganzen Punkt: Fehlt der Wurzelpfad, oder liegt dort keine einzige Mediendatei mehr, obwohl vorher welche da waren, bricht der Lauf ab und sagt warum. Ohne sie erklärte ein nicht eingehängtes Volume beim Start des NAS den gesamten Bestand für verschwunden — bei 458 Titeln. Der Abschnitt in der Verwaltung erscheint nur, wenn es etwas zu melden gibt. Ein dauerhaft leerer wäre Rauschen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ede4069cd4 |
Der Server weiss jetzt mehr als Titel und Plakat
Bisher standen zu einem Film vier Angaben in der Datenbank: Titel, Jahr, Beschreibung, zwei Bilder. Damit lässt sich eine Kachel zeichnen und sonst nichts — keine Detailseite, die mehr sagt als der Dateiname, keine Vorschläge, kein Filter nach Genre. Alles drei hing daran. Jetzt kommen dazu: **Laufzeit, Bewertung, Werbezeile, Altersfreigabe, Genres, Besetzung, Stab — und der Schriftzug des Titels.** Letzterer ist das, was eine Startseite teuer aussehen lässt: „Blade Runner 2049" in seiner eigenen Schrift über dem Hintergrundbild statt in der Systemschrift. **Alles in einer Anfrage.** `append_to_response` hängt Besetzung, Fremdkennungen, Freigaben und Bilder an den Einzelabruf. Vier getrennte Anfragen je Titel wären bei vierhundert Filmen sechzehnhundert, und TMDB begrenzt, wie oft man fragen darf. Der eigene Abruf der IMDb-Kennung ist dabei entfallen — sie hängt jetzt mit dran. Freigaben sind national: die FSK gilt in Deutschland, die MPAA in den USA, und TMDB führt beide nebeneinander. Genommen wird die des eingestellten Landes. Beim Schriftzug entscheidet zuerst die Sprache und erst danach die Bildbewertung — ein deutscher Titel in deutscher Schrift schlägt den besser bewerteten englischen. Vom Stab nur Regie und Drehbuch. TMDB führt dort auch Beleuchter und Caterer; eine Detailseite, die alle nennt, nennt nichts. **Genres und Personen als eigene Tabellen**, nicht als Text im Film. Sonst: filtern über `LIKE`, Tippfehler als eigenes Genre, und bei einer Umbenennung vierhundert Zeilen anfassen. Personen über die Kennung der Quelle geführt, nicht über den Namen — es gibt zwei Schauspieler namens „Chris Evans". Darauf sitzen zwei neue Endpunkte: ein Filter nach Genre (nur die, die in der Mediathek auch vorkommen — ein Knopf zu einer leeren Liste ist schlimmer als kein Knopf) und **Vorschläge** über die Genres des zuletzt Gesehenen. Keine Empfehlungsmaschine: ein Haushaltsserver hat keine Millionen Sehverläufe, aus denen sich etwas lernen liesse. Eine schlichte Regel ist nachvollziehbar, was eine undurchschaubare Empfehlung nie ist. --- **Zwei Fehler, beide fand erst der echte Lauf.** `UNIQUE constraint failed: Genres.Name`, und damit scheiterte der ganze Scan — in der Mediathek erschien kein einziger Titel. Vier Bibliotheken scannen gleichzeitig, jede mit eigenem Datenbankkontext; zwei stossen auf „Drama", keine sieht die ungespeicherte Zeile der anderen. Statt die Läufe zu serialisieren — das kostete bei zehn Bibliotheken das Zehnfache an Zeit — wird der Fehlschlag hingenommen und die Zeile des anderen benutzt. Sie steht ja genau dafür da. Und: **eine geänderte Sprache erreichte vorhandene Titel nie.** Ein zweiter Scan fragt nichts erneut, das ist Absicht. Wer aber von Englisch auf Deutsch stellt, bekam deutsche Titel erst beim nächsten neuen Film. Dafür gibt es jetzt „Metadaten auffrischen" je Bibliothek — geholt über die bekannte Kennung, nicht über eine neue Suche, damit eine Zuordnung von Hand stehenbleibt. 368 Tests grün, zehn davon neu. Am Container nachgewiesen: „Der Untergang", 156 Minuten, FSK 12, Bewertung 7,9, Genres Drama/Historie/Kriegsfilm, 22 Mitwirkende mit Rollennamen, Schriftzug und Hintergrund da — und nach dem Umstellen auf de-DE heisst er nicht mehr „Downfall". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
c4081ff251 |
Stufe 4: Fortschritt ab der ersten Sekunde, ohne Schwelle
Der Anlass für diesen Server, jetzt als Code. Fortschritt, Gesehen-Status, Weiterschauen, Nächste Folge, Favoriten und Bewertungen — alles je Benutzer. **Die Jellyfin-Werte sind jetzt belegt statt behauptet.** Das Entwurfsdokument hatte das ausdrücklich offengelassen („aus dem Gedächtnis"); hier stehen sie aus `MediaBrowser.Model/Configuration/ServerConfiguration.cs` und `Emby.Server.Implementations/Library/UserDataManager.cs`: | | Vorgabe | Was passiert | |---|---|---| | `MinResumePct` | 5 | darunter `positionTicks = 0` — Kommentar im Quelltext: „ignore progress during the beginning" | | `MaxResumePct` | 90 | darüber gesehen, Position **auf 0** | | `MinResumeDurationSeconds` | 300 | alles unter fünf Minuten gilt nach dem ersten Lebenszeichen als gesehen | Bei einem Zweistünder sind die ersten sechs Minuten damit weg. Eine vierminütige Folge gilt nach dreissig Sekunden als vollständig gesehen. Und fehlt die Laufzeit, nimmt Jellyfin an, der Titel sei durch — wörtlich: „we'll just have to assume it was fully played". **Flaro legt jede Meldung ab und wirft nie eine Position weg.** Eine Grenze fürs Häkchen gibt es trotzdem — 90 %, sonst stünde jeder zu Ende geschaute Film für immer bei 98 % in Weiterschauen. Der Unterschied ist nicht, *dass* es die Grenze gibt, sondern was sie anrichtet: hier wird ein Häkchen gesetzt, dort zusätzlich die Stelle vernichtet. Wer einen gesehenen Film noch einmal anfängt, hat beides — das Häkchen und den Punkt, an dem er steht. Ohne bekannte Laufzeit wird nichts vermutet. Das ist der Fehler, den man nicht zurücknehmen kann, ohne ihn zu bemerken. --- **Eine Spalte statt einer zweiten Regel.** „Gehört in Weiterschauen" vergleicht die Stelle mit einem Anteil der Laufzeit, und diese Rechnung lässt sich nicht nach SQL übersetzen. Die Regel daneben noch einmal als Abfrage zu schreiben wäre die Variante, bei der beide irgendwann auseinanderlaufen — unbemerkt, weil keine von beiden dabei falsch aussieht. Stattdessen hält `InProgress` das Ergebnis fest, geschrieben an genau einer Stelle. Eine Prüfung hält Reihe und Regel über vier Stände gegeneinander. **„Weiterschauen" und „Nächste Folge" überschneiden sich nicht.** Was angefangen ist, steht links; was ansteht, rechts. Jellyfin zeigt beides doppelt, und auf einer Startseite mit zwei Reihen nebeneinander sieht das nach einem Fehler aus. Dazu, weil die App sie braucht: Einzelabruf für Film und Serie, die nächste Folge nach einer gegebenen, und der Zustand hängt an jeder Liste mit dran — in einer Abfrage je Seite, nicht in einer je Titel. Ein Titel wird über seine Kennung allein angesprochen, ohne dass der Aufrufer sagen muss, ob es ein Film oder eine Folge ist. Die App hat diese Auskunft an der Stelle nicht. 348 Tests grün. Im Container nachgewiesen: fünf Minuten „Der Untergang" von 2:36 bleiben stehen, bei Minute 150 kommt das Häkchen und die Stelle bleibt, beim zweiten Anlauf steht der Film mit beidem wieder in der Reihe — und nach Folge 1 von Dark zeigt „Nächste Folge" S01E02. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
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> |