a44d55e71c8bcc4c6961aeaaf90ddd4109f78085
412
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c2fd2a86fd |
Einmal aus der Datei holen, nicht bei jedem Abruf
„Diese Untertitelspur liess sich nicht laden", und auf der zweiten Spur kam gar nichts. Nicht das Format war schuld und auch nicht das Verschieben, sondern die Zeit: jeder Abruf startete ffmpeg, ffmpeg musste die ganze Datei durchlesen, und nach dreissig Sekunden wurde abgebrochen. An einem Prüffilm von fünfzehn Megabyte fällt das nie auf; bei einem Dreistünder auf einem NAS reicht es nie. Schlimmer wurde es durch meine eigene Korrektur von gestern: seit die Adresse den Einstiegspunkt mitführt (`?ab=`), ändert sie sich bei jedem Sprung — der Browser konnte nichts mehr aus seinem Zwischenspeicher nehmen, und jeder Sprung stiess einen neuen Durchlauf an. Jetzt: - Ein ffmpeg-Durchlauf holt alle Textspuren auf einmal. Das Lesen der Datei ist der teure Teil und wird nicht mehr je Spur bezahlt. - Das Ergebnis bleibt liegen, ungeschoben; das Verschieben ist eine Zeilenersetzung und kostet nichts. Gemessen: erster Abruf 0,42 s, jeder weitere 2 bis 3 ms — und der zweite Sprung in derselben Datei trifft dieselbe Ablage statt eines neuen Durchlaufs. - Angestossen wird beim Start des Stroms, nicht erst beim Einschalten. Wer nach einer halben Stunde Untertitel will, wartet dann gar nicht. - Geschrieben wird unter `.teil` und erst danach umbenannt. Eine abgebrochene Datei am richtigen Namen gälte sonst für immer als fertig. - Die Geduld steigt von 30 Sekunden auf 30 Minuten. Sie ist jetzt eine Obergrenze gegen einen hängenden Prozess, keine Wartezeit mehr. Und der Spieler kann endlich unterscheiden, was los ist. Ein `<track>` meldet jeden Misserfolg gleich — „es gibt sie nicht", „sie enthält keinen Text" und „der Server holt sie gerade" sahen identisch aus. Geholt wird deshalb selbst, mit Token statt Keks (gemessen: ohne Keks kam jeder Abruf mit 401 zurück, ohne dass irgendwo etwas stand), und bei 503 fragt der Spieler nach, statt aufzugeben. Der Text geht als Blob an den `<track>` — dieselbe Anzeige, ein ehrlicher Weg dahin. Dazu eine Unsauberkeit im Verschieben: die Trennzeile eines weggefallenen Blocks blieb stehen. Gemessen am echten Film 52 Leerzeilen vor dem ersten Satz. Geprüft im Spieler, kalter Vorrat, Einstieg bei Sekunde 500: Blob am `<track>`, zehn Einblendungen, die erste bei 0 bis 3 s mit „Deutsch bei 500 s". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
339a6c07d0 |
Ein Client darf einen Stand fortschreiben, nicht zerstören
Ein Film stand bei 46 Minuten; nach einem Neuladen bot die Detailseite nur
noch „Abspielen" an. Gemessen am laufenden Server, was eine einzige Meldung
„Stelle 500 s, Laufzeit 60 s" anrichtet:
Stelle 500 s -> 60 s
Laufzeit 600 s -> 60 s
gesehen nein -> ja
Weiterschauen ja -> nein
Der Server beschnitt die Stelle auf die gemeldete Laufzeit, ersetzte die
richtige Laufzeit durch sie und setzte das Häkchen. Ein Film war damit weg.
Und 60 ist keine erfundene Zahl. Wer einen gerechneten Strom abspielt, misst
an einer Playlist, die noch wächst, und sieht anfangs nur einen Stummel.
Genau den meldete die App als Laufzeit — und sie meldet beim Öffnen
ausserdem die Zeit null, bevor sie überhaupt gesprungen ist.
Drei Stellen, dieselbe Regel:
- Server: die gemessene Laufzeit schlägt die gemeldete. Die Datei zuerst,
nicht der gemerkte Wert — stünde dort schon ein Stummel, bliebe er sonst
für immer stehen. So heilt sich ein verdorbener Eintrag von selbst.
- Server: eine gemeldete Null löscht keine gemerkte Stelle. Wer wirklich
von vorn anfängt, verliert nichts: zehn Sekunden später meldet derselbe
Spieler die 10, und die zählt. Zurückspulen bleibt unberührt.
- App: dieselbe Rangfolge bei der Laufzeit, und beim Öffnen wird die
Stelle gemeldet, an der der Spieler einsteigen will, statt der Null.
Im Webspieler kam eine eigene Null dazu: bei Direktwiedergabe gibt der
Server `startSeconds: 0` zurück und der Browser springt selbst, sobald die
Metadaten da sind. Kommen sie nie, weil er mit der Datei nicht zurechtkommt,
blieb `filmzeit()` auf null — und der Rückfall aufs Umkodieren, der
ausdrücklich „an der Stelle weiter, an der es hing" meint, fing von vorn an.
Der ausstehende Sprung zählt jetzt mit.
Fünf neue Prüfungen halten die gemessenen Zahlen fest, damit die Regel nicht
wieder umkippt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
51d1db4641 |
Zwei Uhren, und niemand hatte sie verglichen
Untertitel zeigten auf keiner Spur Text. Der Grund: die Zeitstempel einer WebVTT-Spur zählen ab Dateianfang, der Strom zählt ab null. Wer bei Minute 30 einstieg, bekam jeden Satz dreissig Minuten in die Zukunft geschoben — und weil ein Strom nur bis zum Filmende reicht, erschien überhaupt keiner mehr. Gemessen im Spieler, vorher: „Fortsetzen ab 8:37" geklickt, deutsche Spur gewählt, Standanzeige 8:38 — angezeigt wurde der Satz von Sekunde 0. Versatz 515 Sekunden, also genau der Einstiegspunkt. Nachher, derselbe Weg: Standanzeige 9:02, angezeigt „Deutsch bei 540 s". Verschoben wird auf dem Server, nicht im Browser: dort hiesse es, die Spur selbst zu zerlegen und als VTTCue neu aufzubauen — ein eigener Parser für ein Format, das der Browser schon kann. Hier ist es eine Zeilenersetzung, und sie lässt sich prüfen. Was vor dem Einstieg endet, fällt samt Text weg; was darüber hinwegreicht, fängt bei null an. Zwei Kleinigkeiten lagen daneben und sind mit erledigt: - `srclang` bekam „ger".slice(0, 2) und damit „ge" — kein gültiges Kürzel. Jetzt bleibt das Feld leer, wenn das Kürzel nicht zweistellig ist. - Scheiterte das Laden einer Spur, geschah nichts Sichtbares. Jetzt sagt es der Spieler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
57fff63516 |
Die Überschrift des Versatzes brach um
„TONVERSATZ — OHNE VERSATZ" am Stück passte nicht in das schmale Menü und stand auf zwei Zeilen — und sah damit nach zwei Dingen aus. Jetzt steht der Name links und der eingestellte Wert rechts in derselben Zeile, der Wert hell und ohne Versalien, damit man sieht, welches davon die Einstellung ist. Die Liste ist dafür 230 statt 180 Bildpunkte breit — „Ton 3 s früher" muss daneben passen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2f7768136b |
Der Tonversatz ist ein Regler, abgetrennt unter den Spuren
Drei Anläufe, und du hattest jedes Mal recht. **Er gehört ins Tonmenü** — aber sichtbar nicht zu den Spuren. Jetzt trennt ihn ein Strich ab, darüber steht „TONVERSATZ — Ohne Versatz" als Überschrift, darunter ein Regler. Drei Mittel, und jedes einzelne würde schon sagen, dass das keine Sprache ist. **Ein Regler statt Auswahlpunkten.** Feste Stufen hiessen: für jeden Wert ein Eintrag, und die standen zwischen den Sprachen wie weitere Sprachen. Gezogen wird von −3 bis +3 Sekunden in Hundertern; die Mitte ist als Strich markiert, sonst trifft man die Null nur zufällig. Gerechnet wird erst beim Loslassen, nicht beim Ziehen — jede Änderung baut den Strom neu auf, und das tut man nicht zwanzigmal beim Hinschieben. Daneben „Zurücksetzen", sobald etwas eingestellt ist. **Und der Lautsprecher ist zurück.** Hier stand ein eigenes Zeichen, damit er nicht zweimal in der Leiste auftaucht — links Stummschalten, rechts die Tonspur. Die Wellenform erkannte aber niemand als „Ton". Zwei Lautsprecher sind das kleinere Übel, zumal der linke beim Stummschalten durchgestrichen wird und damit anders aussieht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dc9d1cea85 |
Meine Rundung war eine Klinge, und ich bin hineingelaufen
Du hast recht, und zwar doppelt.
**Der Versatz stand im falschen Menü.** „Später (+100 ms)" unter den
Tonspuren liest sich wie der Name einer Spur. Jetzt hat er einen eigenen
Knopf in der Leiste, mit eigenem Zeichen und festen Stufen von „Ton 1 s
früher" bis „Ton 1 s später", dazwischen „Ohne Versatz" — mit Haken, wie
bei der Qualität. Im Tonmenü stehen wieder nur Sprachen.
**Und das Schlimmere: die Rundung aufs Schlüsselbild hat es verschlechtert.**
Sie musste auf die Millisekunde stimmen, sonst nimmt ffmpeg das
Schlüsselbild davor. Gemessen an einer Datei mit Schlüsselbildern alle
fünf Sekunden:
-ss 15.015 → Bild ab 0,000 | Ton ab 0,004
-ss 15.014 → Bild ab 0,000 | Ton ab 4,983
Eine Millisekunde. Und die Zeile, die den Wert an ffmpeg gibt, rundete auf
drei Nachkommastellen — bei 23,976 Bildern je Sekunde liegen
Schlüsselbilder auf Werten wie 3,336667, und die überleben das nicht
zuverlässig. Damit konnte jede Fortsetzung schlechter werden als vorher.
Das war mein Fehler, eingebaut in E8GSRE.
**Der richtige Hebel ist `-noaccurate_seek`**, und er braucht gar keine
Genauigkeit. Damit schneidet ffmpeg auch den Ton nicht auf die Sekunde,
sondern lässt ihn beim selben Schlüsselbild anfangen wie das kopierte
Bild. Gemessen, dieselbe Datei, fünf verschiedene Sprungwerte quer durch
die Bildgruppe:
15,014 → 0,015 | 17 → 0,004 | 19,9 → 0,004 | 12,5 → 0,015 | 3,2 → 0,011
Statt bis zu fünf Sekunden also überall unter zwanzig Millisekunden, und
zwar ohne dass irgendein Wert getroffen werden muss. Fünf Testfälle quer
durch die Bildgruppe halten das jetzt fest; vorher prüfte der Sprungtest
nur, ob überhaupt Segmente entstehen.
Die Schlüsselbildsuche bleibt — aber nur noch, um dem Spieler eine
ehrliche Uhr zu geben. Vergreift sie sich, geht die Uhr falsch; Bild und
Ton bleiben zusammen. Vorher hing die Ausrichtung daran, und das war der
schlechte Tausch.
Und die Sekundenangaben gehen jetzt mit sechs Nachkommastellen hinaus
statt mit drei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
6ad4deb634 |
Fünf Sekunden Bild ohne Ton — jetzt weiss ich, woher
Deine Angabe war der Schlüssel: du hast **mitten im Film die Tonspur umgeschaltet**. Dabei baut der Spieler den Strom an der aktuellen Stelle neu auf, und damit greift der Sprung — den ich gestern nur mit Spielzeugdateien gemessen hatte. Also eine Datei gebaut, die einem Film gleicht: 23,976 Bilder je Sekunde, Schlüsselbild alle fünf Sekunden, zwei Tonspuren, Deutsch als Stereo-AAC und Englisch als 5.1 in EAC3. Gemessen, erster Zeitstempel je Spur: | | Bild ab | Ton ab | |---|---|---| | Spur 2, ab Sekunde 0 | 0,021 | 0,000 | | Spur 2, ab 17 (mitten in der Bildgruppe) | 0,000 | **1,964** | | Spur 2, ab 15 (knapp vor dem Schlüsselbild) | 0,000 | **4,969** | Fast fünf Sekunden Bild ohne Ton. Zueinander sind beide dabei richtig getaktet — aber eine solche Lücke am Anfang darf jeder Spieler deuten, wie er will, und hls.js schiebt den Ton dann nach vorn. Genau das hast du gehört. Die Rundung auf das Schlüsselbild, die seit E8GSRE drin ist, nimmt das weg: dieselbe Datei, Sprung auf 17, gerundet auf 15,015 — **Bild ab 0,000, Ton ab 0,004**. Du läufst noch auf 5YZ63F; deshalb steht im Protokoll auch keine ffmpeg-Warnung, die gibt es erst ab E8GSRE. **Die Tonspur lässt sich jetzt vor dem Start wählen.** Auf der Detailseite steht neben „Abspielen" ein Knopf „Ton: Deutsch"; das Menü zeigt alle Spuren der Datei. Gemerkt wird dabei die **Sprache**, nicht die Spurnummer — welche Nummer Englisch trägt, ist von Film zu Film verschieden, dass du Englisch willst, ist es nicht. Beim nächsten Film steht die passende Spur damit schon da, und das Umschalten mitten im Film entfällt. Wer doch im Spieler wechselt, ändert dieselbe Merkstelle. **Und ein Fehler aus deinem Protokoll**, der nichts damit zu tun hat: „UNIQUE constraint failed: Sehtage.UserId, Sehtage.Tag, Sehtage.MovieId". Das ist ein Wettlauf im Sehprotokoll von vorgestern — der Spieler meldet alle zehn Sekunden, ein Sprung meldet sofort, und zwei Meldungen können sich überholen. Dann fanden beide keine Zeile, beide legten eine an. Jetzt wird in der Datenbank hochgezählt statt im Speicher, und wer dabei zu spät kommt, zählt beim zweiten Versuch mit. Mit Test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cf272f20aa |
Ton und Bild: ein Versatz zum Einstellen, und zwei gemessene Fehler weniger
Du meldest Ton, der drei, vier Sekunden neben dem Bild liegt. **Nachstellen konnte ich es nicht** — und das sage ich lieber, als eine Vermutung als Befund zu verkaufen. Gemessen habe ich alle vier Wege, jeweils mit ffprobe am Ergebnis, erster Zeitstempel je Spur: | Weg | Bild ab | Ton ab | |---|---|---| | Umpacken, ab 0 | 0,021 | 0,000 | | Umpacken, ab 12 | 0,000 | 1,979 | | Umkodieren, ab 0 | 0,021 | 0,000 | | Umkodieren, ab 12 | 0,021 | 0,000 | Alle vier sind **zueinander richtig**. Auch eine Quelle mit einem echten Loch in der Tonspur blieb ausgerichtet. Die naheliegende Erklärung — `-ss` vor `-i` mit kopiertem Bild und neu gerechnetem Ton — ist damit widerlegt. **Zwei echte Fehler hat dieselbe Messung aber gefunden**, und beide sehen aus wie „um Sekunden daneben": Zeile zwei der Tabelle. Beim Umpacken fängt das Bild beim Schlüsselbild *vor* dem Sprung an, der Ton genau beim Sprung — bei Sekunde 12 also zwei Sekunden Bild **ohne Ton** am Anfang. Zueinander stimmen beide, aber jeder Spieler darf diese Lücke deuten, wie er will. Und der Server meldete dem Spieler „beginnt bei 12", obwohl er bei 10 beginnt: die Uhr, die Zeitleiste und der gespeicherte Stand lagen um diesen Unterschied daneben. Beides geht weg, indem der Sprung **auf das Schlüsselbild gerundet** wird und derselbe Wert zurückgemeldet wird. Dann fangen Bild und Ton zusammen an, es gibt keine Stille davor, und die Uhr stimmt. Beim Umkodieren entfällt das Runden — dort schneidet das neu gerechnete Bild genau. **Und: ffmpeg durfte bisher nichts sagen.** `-loglevel error` versteckt genau die Meldungen, die einen Tonversatz ankündigen, und der Fehlerkanal wurde bei Rückgabewert 0 weggeworfen. Jetzt steht er auf `warning`, und Warnungen landen auch bei einem geglückten Lauf im Protokoll. Wenn es bei dir wieder auftritt, steht diesmal etwas da. **Der Versatz zum Einstellen** sitzt im Tonmenü des Spielers: „Später" und „Früher" in Hundertmillisekundenschritten, bis ±5 s, dazu „Zurücksetzen". Gemerkt wird er **je Datei in diesem Browser** — ein Versatz ist meist eine Eigenschaft der Datei, kann aber auch an der Anlage liegen, und über Bluetooth stimmt etwas anderes als am Fernseher. Verschoben wird serverseitig: die Datei wird ein zweites Mal mit `-itsoffset` geöffnet und von dort nur der Ton genommen. Mit einer Eingabe verschöbe sich beides gemeinsam. Der Preis: der Strom startet neu, und eine durchgereichte Datei muss dafür umgepackt werden — ohne ffmpeg lässt sich nichts verschieben. Zwei neue Läufe halten das fest: dass Bild und Ton zusammen anfangen, und dass ein halbe-Sekunde-Versatz den Ton um eine halbe Sekunde verschiebt und das Bild gar nicht. Der Sprungtest daneben lief bisher genau durch diesen Fall und sah nur nach, ob überhaupt Segmente entstehen. Ein Test musste umgeschrieben werden: er stand auf „Einstiegspunkt ist genau 2" und hielt damit die falsche Meldung fest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3fe0013be1 |
Die Auflösung wechselt den Text, statt einen Tooltip zu öffnen
Gestern wanderte der genaue Wert in den Tooltip. Ein Tooltip kommt verzögert, legt sich über die Nachbarzeile und existiert am Finger gar nicht. Jetzt steht in Ruhe „Full HD" da, und beim Überfahren tritt „1080p · 1920 × 800" an dieselbe Stelle. **Beide liegen übereinander, nicht nacheinander.** Das Feld ist so breit wie der breitere der beiden Texte — sonst rückte beim Überfahren die halbe Tabelle. Gerastert statt absolut gesetzt, damit die Zeilenhöhe von selbst stimmt. Ohne Zeiger gibt es kein Überfahren: dort steht das Genaue dauerhaft daneben, statt unerreichbar zu sein. Gemessen: in Ruhe „Full HD" bei Deckung 1 und „1080p · 1920 × 800" bei 0, unter dem Zeiger umgekehrt — und kein `title` mehr am Element. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cdea8e3b0a |
Die Höhe des Aufmachers ist jetzt einstellbar
Vier Stufen unter Einstellungen → Verwaltung → Übersicht: niedrig, mittel, hoch, sehr hoch. Mittel ist, was gestern fest verdrahtet war. **Keine Pixelzahl.** Eine einzustellen hiesse, sie an jedem Bildschirm neu einzustellen — der Aufmacher misst sich an der Fensterhöhe, nicht an einer festen Grösse. Gemessen bei 800 Bildpunkten Fensterhöhe: 496, 576, 656, 736. Oben ist Schluss, bevor von der ersten Plakatreihe nichts mehr übrig bleibt; sie soll zeigen, dass unten noch etwas kommt. Am Telefon bleibt es flacher, dort wäre auch das weg. **Sie reist mit der Systemauskunft mit**, nicht in einer eigenen Abfrage. Die holt der Client ohnehin als Erstes, noch vor der Anmeldung; käme die Höhe später, stünde der Aufmacher einen Augenblick in der Vorgabe und spränge dann — genau das, was eine einstellbare Höhe vermeiden soll. Gesetzt wird sie als Marke am `<html>`, das Regelwerk hängt vier Werte daran. Serverweit und nur für Admins, dieselbe Linie wie bei den Wiedergaberegeln: eine Entscheidung des Gastgebers, nicht des Gastes. Unbekanntes in der Spalte wird zur Vorgabe statt zu einem Fehler — eine Startseite, die an einem alten Eintrag scheitert, wäre die schlechtere Antwort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a7a4419626 |
Adminrecht geben und nehmen — und einen, dem es niemand nimmt
Ein Konto liess sich anlegen und danach nie wieder ändern: kein Weg zum Administrator, keiner zurück, keiner zum Löschen. Mit Einladungslinks füllte sich der Server also in genau eine Richtung. **Warum es einen Besitzer braucht.** Sobald Administratoren einander das Recht entziehen können, gibt es einen Zustand ohne jeden Administrator — und aus dem führt kein Weg zurück ausser dem an der Datenbank. Ein Konto, das ausgenommen ist, schliesst ihn aus. Es ist das erste, also das, mit dem eingerichtet wurde; die Migration trägt das auf bestehenden Servern nach, und weil die Kennungen zeitlich geordnet vergeben werden, ist der Älteste schlicht der mit der kleinsten. Weitergeben lässt er sich: vom Besitzer, an einen Administrator, mit Rückfrage. Nur an einen Administrator, sonst wäre der Server einen Klick von einem Besitzer entfernt, der die Verwaltung gar nicht sieht. **Drei Türen sind zugemauert**, und jede hat ihren Test: dem Besitzer das Recht nehmen, den Besitzer löschen, sich selbst das eigene Recht nehmen. Das letzte ist die unauffälligste — wer sich im Eifer entmachtet, steht vor einer Verwaltung, die er nicht mehr öffnen kann, und das Zurücknehmen braucht genau die. Gemessen und als Test festgehalten: ein frisch vergebenes Adminrecht gilt **sofort**, ohne neue Anmeldung. Die Rolle wird je Anfrage aus der Datenbank gelesen und nicht aus dem Token — ich hatte das Gegenteil befürchtet. Löschen nimmt mit, was am Konto hing: Geräte, Sehstände, Sehprotokoll, Meldungen, offene Einladungen. Alles hängt mit `Cascade` daran. Zahlen über jemanden zu führen, den es nicht mehr gibt, wäre die schlechtere Hälfte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d5e21d663f |
Einladen, statt sich für jemanden ein Passwort auszudenken
Ein Konto von Hand anzulegen heisst: sich für einen anderen ein Passwort ausdenken und es ihm durchsagen — über denselben Kanal, über den auch ein Link gegangen wäre. Jetzt gibt es den Link. Wer ihn öffnet, wählt Namen und Passwort selbst und ist danach angemeldet. Gültigkeit und Anzahl wählst du beim Erstellen: einen Tag bis einen Monat, einmal bis unbegrenzt. **Eine Rolle wählst du nicht** — wer eingeladen wird, ist gewöhnlicher Benutzer. Ein Feld, über das ein Link Adminrechte verschenken könnte, gibt es gar nicht, und damit auch keinen Weg, es versehentlich zu setzen. **Der Link steht genau einmal da.** Gespeichert ist nur sein Fingerabdruck, wie beim Gerätetoken; aus der Liste lässt sich keiner mehr bauen. Eine verlorene Einladung wird deshalb nicht wiedergefunden, sondern neu gemacht. Und gebaut wird die Adresse im Browser: der Server weiss nicht, unter welchem Namen er von aussen erreichbar ist, und hinter einem Reverse Proxy rät er falsch. **Das Rennen beim Einlösen.** Zwei Leute, die gleichzeitig denselben einmaligen Link öffnen, läsen beide „null benutzt" und schrieben beide „eins" — und hätten beide ein Konto. Der Zähler geht deshalb mit einer Bedingung hoch, die die Gültigkeit noch einmal prüft; wer dabei keine Zeile trifft, war der Zweite. Das Ganze in einer Transaktion, damit ein Tippfehler im Namen den Link nicht verbrennt. Beides steht als Test da — zwei gleichzeitige Einlösungen ergeben genau ein Konto. **Angelegt wird, angemeldet wird danach gewöhnlich.** Derselbe Zweischritt wie beim Einrichten, und der Grund ist nicht Bequemlichkeit: die Anmeldung setzt den Sitzungskeks, und ohne den lädt kein Plakat — Bilder weisen sich über ihn aus, nicht über das Token. Gemessen beim Durchspielen: nach dem Einlösen kommt ein Bild mit 200 zurück. **Die Weiche liegt im Hochfahren, nicht im Router.** Der Router läuft nie, wenn niemand angemeldet ist — ein Ausgeloggter mit Einladungslink sähe sonst die Anmeldekarte und landete nach dem Anmelden auf der Startseite, ohne je erfahren zu haben, wozu er eingeladen war. Nach dem Einrichten, damit ein nagelneuer Server zuerst seinen Administrator bekommt. Wer schon angemeldet ist, bekommt kein Formular, sondern den Hinweis und zwei Wege. Und die Gründe einer Ablehnung sind verschieden benannt: abgelaufen, zurückgezogen, schon benutzt. Verschwiegener wäre ein einheitliches „gilt nicht" — und dann fragt der Eingeladene nach, und der Gastgeber rät mit. Nebenbei korrigiert: die Erkundung meinte, dem Benutzernamen fehle ein eindeutiger Index. Er hat einen, seit der ersten Migration, sogar mit `NOCASE`. Das Namensrennen war also nie offen. 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>
|
||
|
|
168fcdfb4c |
Der Aufmacher war dir zu niedrig
`min(62vh, 560px)` → `min(72vh, 640px)`. Bei 800 Bildpunkten Fensterhöhe gemessen: 576 statt 496, und die erste Plakatreihe lugt mit ihrem Beginn bei 648 weiterhin über den Falz — sie soll ja zeigen, dass unten noch etwas kommt. Am Telefon bleibt es bei `min(52vh, 420px)`: dort ist der Aufmacher im Verhältnis zum Bildschirm ohnehin höher. Ein Satz musste mit, und der ist tragend: die Begründung, warum das Aufklappen der Inhaltsangabe nicht schaukelt, nennt die feste Höhe wörtlich. Die Zahl wird falsch, das Argument bleibt richtig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8e9da1355c |
Der Server schrieb deutsche Sätze an eine Stelle, die niemand liest
„Die Anfrage war so nicht zu gebrauchen" — das ist der Ersatzsatz des
Browsers für eine Ablehnung ohne Begründung. Dabei stand die Begründung
da: „Benutzername darf nicht leer sein, das Passwort braucht mindestens 8
Zeichen." Nur eben in `errors`, und beide Clients lesen `detail`.
Zwei Formen waren betroffen. `Results.ValidationProblem` legt den Satz
unter `errors` ab, `Results.BadRequest(new { fehler = … })` unter
`fehler` — in beiden Fällen kommt er nirgends an. Zusammen 36 Stellen,
darunter jede Zeile, die ich gestern für die Meldungen geschrieben habe.
Dass die Clients `title` und `errors` auslassen, ist dabei richtig: dort
stehen sonst die englischen Prüftexte des Rahmenwerks, und „Not Found" ist
keine Auskunft.
Jetzt eine Form, hinter `Einwand.Bitte` / `.Fehlt` / `.Belegt`. Dass es
drei benannte Türen sind statt eines Aufrufs mit Zahl, hat einen Grund:
man sieht am Wort, ob der Server um eine bessere Eingabe bittet, etwas
nicht findet oder auf etwas Bestehendes stösst.
**Auffallen konnte es nicht**, und das ist der eigentliche Fund. Die
vorhandenen Tests prüften den Rumpf als Zeichenkette — der Satz *stand* ja
drin, nur im falschen Feld. Der neue Test prüft deshalb, in welchem Feld,
und ein Wächter am Quelltext schlägt an, wenn eine der beiden alten Formen
zurückkommt. Er hat beim ersten Lauf gleich eine Stelle gefunden, die mein
Suchmuster übersehen hatte.
Dazu drei Kleinigkeiten, die beim Lesen auffielen:
**Passwörter wurden getrimmt.** Der Dialog schnitt jedem Feld die Ränder
ab, auch dem Passwort — `login.ts` sendet es ungeschoren. Ein Leerzeichen
am Ende, und das angelegte Konto war für immer unbetretbar.
**Beim Einrichten stapelten sich die Fehlerzeilen**, weil `.error`
weggeräumt wurde und `.fehler` entsteht. Drei Fehlversuche, drei rote
Kästen.
**Der Benutzerdialog versprach acht Zeichen und prüfte nichts.** Jetzt
sagt er es selbst, ohne den Umweg über die Leitung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
7cd48f5528 |
Wer etwas sieht, kann es jetzt auch sagen
Wer einen Server für andere betreibt, erfährt von Fehlern zuletzt: dass die deutsche Tonspur nach einer halben Stunde aussetzt, merkt der, der den Film sieht, und nicht der, der ihn eingeräumt hat. Bisher gab es dafür keinen Weg ausser dem Zuruf im Wohnzimmer. Gemeldet wird an einem Film, einer Serie, einer einzelnen Staffel und einer einzelnen Folge: Art (Bild, Ton, Untertitel, Angaben, Sonstiges), eine freiwillige Stelle wie 0:34:12 und der Kommentar. Pflicht ist nur der Kommentar — die beiden anderen machen aus einer Liste von Sätzen etwas, das man sortieren und nachstellen kann. **Eine Staffel gibt es als Zeile nicht**, und das prägt den Entwurf. Sie ist Serie plus Nummer, so wie überall sonst in diesem Server; die Meldung trägt deshalb drei Fremdschlüssel und daneben eine Staffelnummer. Dass die Nummer auf eine Staffel zeigt, die es gibt, prüft der Endpunkt beim Schreiben — die Datenbank kann es nicht, es gibt nichts, worauf sie zeigen könnte. **Die Rechteprüfung steht nirgends.** Wer einen Titel nicht sehen darf, findet ihn beim Melden nicht — das erledigt der Freigabefilter, ohne ein einziges `Where`. Elegant und genau deshalb leicht zu zerstören, darum steht dafür ein Test da. **Die eigene Liste überlebt fremde Entscheidungen.** Titel werden nachgeschlagen, nicht verbunden: zieht ein Administrator einen Titel zurück, nachdem jemand ihn gemeldet hat, bleibt die Meldung beim Melder stehen — nur ohne Namen. Ein Join hätte sie verschwinden lassen, und das wäre jemandes eigene Eingabe, weggenommen wegen einer fremden Entscheidung. `IgnoreQueryFilters` wäre die bequeme Abkürzung und gäbe den Namen eines unveröffentlichten Titels über einen Weg heraus, an den beim nächsten Mal niemand denkt. **Erledigt heisst abgehakt, nicht gelöscht**, als Zeitpunkt und nicht als Haken — wie `PublishAt` und `ChangedSince`. Daneben steht „Wieder öffnen"; ein Fehlgriff wäre sonst endgültig. Das Archiv wird erst auf Zuruf geholt: es wächst nur, und für den seltenen Blick dauernd zu zahlen lohnt nicht. **Der Abschnitt steht neben der Prüfliste, nicht darin.** Die versteckt sich, sobald nichts auf Freigabe wartet — das ist der Normalfall, und die Meldungen verschwänden mit ihr. Umgekehrt bleibt der Meldungsabschnitt stehen, auch wenn nichts drinsteht: ein Kanal, der sich versteckt, solange ihn niemand benutzt hat, bringt niemandem bei, dass es ihn gibt. Die Zahl an der Leiste holt sich nur, wer sie nicht ohnehin hat: steht „Zu prüfen" offen, meldet der Abschnitt sie selbst und nach jedem Abhaken. Nebenbei zwei Dinge am Dialog, die hier auffielen. Es gab **kein mehrzeiliges Feld** — und das Einsammeln der Werte hätte ein `<textarea>` still verschluckt: es ist kein `HTMLInputElement` und wäre ins Auffangnetz für Auswahlfelder gelaufen, also als `undefined` hinausgegangen. Der Server hätte „ohne Beschreibung" geantwortet, auf einem Dialog, in dem sichtbar etwas steht. Und `dialog()` meldete sich als einziger nicht beim Seitenwechsel an: ein Dialog von der Detailseite überlebte den Zurück-Knopf und stand danach über der Mediathek. Neun Testfälle auf dem Server, zwölf Läufe. Für den Web-Client gibt es in diesem Projekt keine Tests; dort ist von Hand geprüft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
02978e482c |
Die Knöpfe bleiben, die Inhaltsangabe klappt auf
Ich hatte „die Beschreibung nur beim Hovern" gelesen und daraufhin Beschreibung *und* Knöpfe ausgeblendet. Die Knöpfe gehören immer dahin: in Ruhe steht oben, was läuft und was man damit tun kann. Die Inhaltsangabe dagegen steht erst da, wenn man es will. Zwei Sekunden auf dem Titel stehenbleiben, dann fährt er hoch und darunter klappt sie auf; wer nur vorbeifährt, sieht nichts. Gibt es keine, passiert gar nichts — der Absatz fällt dann ganz aus dem Fluss. **Angefasst wird am Titel, losgelassen am ganzen Aufmacher.** Das ist die ganze Vorsicht an dieser Stelle. Beim Aufklappen wächst der Block nach oben, der Titel rückt um 77 Bildpunkte weg und steht nicht mehr unter dem Zeiger. Hinge das Zuklappen an seinem `:hover`, wäre das die erste Hälfte einer Schaukel. Der Aufmacher selbst kann das nicht: gemessen, offen wie zu, 476 px hoch ab 48 — auf den Bildpunkt derselbe Kasten. **Die Höhe wird gemessen, nicht geraten.** Nach `auto` lässt sich nicht überblenden, und eine feste Obergrenze wäre bei einer einzeiligen Angabe zu drei Vierteln Leerlauf. Gemessen wird bei jedem Folienwechsel und bei jeder Fensteränderung; `scrollHeight` ginge nicht, der meldet bei einem `-webkit-box` die ungeklammerte Höhe. **Der Rasterabstand wandert mit.** Gemessen: zugeklappt stehen zwischen Eckdaten und Knopfzeile 16 px — mit Inhaltsangabe wie ohne, und genau so viele wie vorher. Aufgeklappt sind es 12 über und 16 unter dem Text, und die Knopfzeile steht still (Unterkante 492 in beiden Zuständen). Nebenbei kommt das Abbruchsignal endlich an: `hero()` nahm eines entgegen, aber `main.ts` baute die Startseite ohne. Sie wird gemerkt — ihr Neun-Sekunden-Takt lief also weiter, während man längst woanders war. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ac9faacefb |
Im Aufmacher steht in Ruhe nur, was läuft
Inhaltsangabe und Knöpfe blenden sich beim Überfahren ein und wieder aus. In Ruhe bleibt oben nur, *was* läuft — Schriftzug oder Name, Jahr, Laufzeit, Freigabe. Was man damit tun kann und worum es geht, kommt auf Zuruf. So ist das Bild in Ruhe ein Bild und nicht eine Wand aus Text darüber. **Langsamer herein als hinaus**, 340 gegen 220 Millisekunden: das Erscheinen soll man sehen, das Verschwinden nicht abwarten müssen. Die Pfeile gehen denselben Takt mit; sie standen auf 150 ms und blitzten dagegen auf. **Der Platz bleibt reserviert.** Mit `display: none` spränge beim ersten Überfahren der ganze Block nach oben; geschaltet wird nur die Deckung. Gemessen an derselben Folie: 349,2 px hoch mit Deckung 0, 349,2 px mit Deckung 1. **Der Terminhinweis bleibt stehen.** „Ab Di., 6. Okt., 18:00 Uhr" ist keine Handlung, sondern die Auskunft, warum hier nichts zu drücken ist — die darf nicht erst auf Zuruf kommen. Darum trifft die Regel `.btn` und nicht die ganze Knopfzeile. **Der Fokus zählt wie der Zeiger.** Ein Knopf, der unsichtbar den Fokus bekommt, ist eine Sackgasse. Gemessen, mit abgeschalteten Übergängen: in Ruhe alles auf 0; Fokus im Aufmacher — Text 1, beide Knöpfe 1, beide Pfeile 0,72; nach dem Verlassen wieder 0. `hover: hover` wie bei den Pfeilen: ohne Zeiger gäbe es kein Überfahren, und „Abspielen" wäre nicht mehr erreichbar. Dort steht alles wie immer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3a0afa918d |
Die Pfeile im Aufmacher warten, bis man hinzeigt
Sie liegen über dem Bild und damit über allem, was dort steht. Bei einem einzeiligen Titel geht das gut. „Pirates of the Caribbean — Salazars Rache" bricht um, die erste Zeile rückt nach oben — und der linke Pfeil sitzt auf dem ersten Buchstaben. Ständig sichtbar ist er dort ein Fleck, auf Zuruf ein Knopf. Also erscheinen beide erst beim Überfahren des Aufmachers, in 150 ms, und verschwinden wieder. `hover: hover` ist dabei die ganze Vorsicht: auf einem Tablett gibt es kein Überfahren, und ein Knopf, der nur dabei erscheint, wäre dort schlicht nicht erreichbar. Dort bleibt alles, wie es war — unter 860 Bildpunkten gibt es die Pfeile ohnehin nicht, da wird gewischt. Gemessen: in Ruhe Deckung 0, kein Zeigerziel. Zeiger über dem Aufmacher: 0,72 und anklickbar. Zeiger auf dem Pfeil: 1, dunklerer Grund, und der Klick blättert tatsächlich weiter (Tastatur-Tabulator vom Knopf „Mehr" aus: Deckung 1, `:focus-visible` greift, Umriss 2 px) — `pointer-events: none` hält nur den Zeiger ab, nicht den Fokus. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ffc4bbe597 |
Ein Viertel des Hintergrundbildes lag hinter der Seitenleiste
Es stand auf `inset: 0` und lag damit auch hinter Seiten- und Kopfleiste — beide undurchsichtig. Bei 1280×800 gemessen: 18,8 % der Breite, 7 % der Höhe, zusammen 24,4 % der Bildfläche, die niemand je zu sehen bekam. Solange alles weichgezeichnet war, fiel das nicht auf; ein verwischter Rand sieht wie jeder andere aus. Scharf beginnt der Inhalt mitten im Motiv — bei den Bergrettern mit einem angeschnittenen Kopf direkt an der Kante der Seitenleiste. Jetzt sitzt das Bild in dem Feld, in dem es auch zu sehen ist, und wird darin ganz zugeschnitten: links die Seitenleistenbreite, oben die Kopfleistenhöhe. Der Kontrast bleibt, wo er war — 3,95∶1 scharf statt 3,97∶1, derselbe Zuschnitt in einem anderen Feld. Am Telefon gibt es keine Seitenleiste; dort nimmt die Navigationszeile oben noch einen Streifen weg, solange man ganz oben steht, und ist nach dem ersten Bildlauf weg. Die beiden älteren Begründungen über der Regel standen noch da und sagten inzwischen das Gegenteil — die eine erklärte, warum das Bild über die volle Breite gehört. Sie sind zu einer zusammengefasst, die den ganzen Weg erzählt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3c7c0d8799 |
Der Hintergrund wird erst weich, wenn etwas darüberläuft
Er war es immer: 28 Bildpunkte Weichzeichnung über die ganze Detailseite, damit der Text darüber lesbar bleibt. Oben steht aber gar nichts darüber — dort ist das Bild das Beste, was die Seite zu bieten hat, und es lag als Farbschlieren da. Unscharf gehört es erst, wenn wirklich etwas darüber hinwegläuft: die Folgen, die Besetzung, die Medienangaben. Jetzt folgt die Weichzeichnung dem Bildlauf, über etwa siebzig Prozent einer Bildschirmhöhe und quadratisch statt geradlinig — die ersten Bildpunkte sollen nichts tun, sonst wäre „scharf, solange nichts darüberliegt" nach einem Fingerbreit schon dahin. Gerundet auf ganze Bildpunkte, weil jeder neue Wert eine bildschirmfüllende Fläche neu zeichnen lässt; über die ganze Strecke sind das 28 Male. Gemessen über 58 Bilder beim Hinunterrollen: 8,3 ms im Mittel, kein Bild über 9,2 ms. **Und die Deckung geht mit, das ist der unscheinbare Teil.** Eine Weichzeichnung verrührt die hellen Stellen mit den dunklen; ein scharfes Bild hat die weisse Stelle genau dort, wo die Zeile steht. Gemessen an der dunklen Vorlage von „Dark": vorher 4,07∶1, scharf bei derselben Deckung nur noch 2,44∶1. An einer hellen — Hubschrauber vor blauem Himmel — vorher 2,75∶1. Mit 0,28 scharf und 0,45 weich stehen beide wieder bei rund 4,0∶1, die helle sogar besser als zuvor. Der Zuhörer räumt sich selbst ab: die Detailseite wird nicht gemerkt, und für ungemerkte Ansichten ruft `einhaengen` kein Abbruchsignal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6dc7be9822 |
Ein eigenes Bild bringt auch seinen Ausschnitt mit
Hochladen konnte man Plakat, Hintergrund und Schriftzug schon lange — der Knopf stand da, der Endpunkt nahm an. Nur ankommen tat nichts: die Ablage baute einen Schlüssel, den sie selbst nicht wiederfand (heute früh repariert). Was blieb, waren drei Lücken, die man erst sieht, wenn der Weg überhaupt funktioniert. **Der Ausschnitt.** Ein Plakat steht hochkant im Verhältnis 2∶3, ein Hintergrund liegt quer. Was man aussucht, passt selten von selbst — und den Rest beschnitt bisher der Browser beim Anzeigen: mittig. Jetzt wählt man selbst, zieht und vergrössert, und hochgeladen wird genau dieser Ausschnitt: 600×900 beim Plakat, 1920×1080 beim Hintergrund. Gerechnet wird im Browser; das spart die Leitung und dem Server eine Bildbibliothek. **Der Schriftzug nicht.** Er ist ein PNG mit durchsichtigem Grund. Durch dieselbe Mühle geschickt würde aus der Durchsicht ein weisses Rechteck — ein Aufkleber über dem Bild. Er geht unverändert hinaus, Byte für Byte. **Die Kachel merkte nichts.** Nach dem Hochladen fehlte der Anstoss nach aussen; die Oberfläche zeigte weiter das alte Bild, bis man sie neu lud. **Und zurück ging es gar nicht.** „Von Hand gesetzt" heisst auch: beim Auffrischen bleibt es stehen. Ohne Rückweg wäre ein Fehlgriff ein Dauerzustand — jetzt steht „Wieder automatisch" daneben, sobald etwas festgesetzt ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
327e2e9195 |
Einstellungen hat jeder, Verwaltung nicht
Die Seite hiess „Verwaltung" und war für alle anderen gar nicht erreichbar — „Meine Sprache" lag dadurch hinter einer Tür, die den meisten verschlossen ist. Jetzt heisst sie **Einstellungen**, steht jedem offen und hat zwei beschriftete Gruppen: oben, was einem selbst gehört (Mein Konto, Geschaut), darunter — nur für Administratoren — **Verwaltung**. Die Überschrift ist hier die halbe Aussage: sie sagt ohne weiteren Satz, wem der untere Teil gehört. In der Hauptleiste steht der Eintrag jetzt unten, ohne Gruppenüberschrift. Eine Überschrift über einem einzigen Eintrag ist eine Überschrift über nichts, und am Fuss sucht man Einstellungen ohnehin. `#/profil` und `#/verwaltung/…` leiten weiter; ein Lesezeichen merkt davon nichts. **Ein kurzes Menü am Bild.** Erst meldete der Klick ab — ohne Rückfrage. Dann führte er auf die eigene Seite, und zum Abmelden musste man erst dorthin. Beides sind zwei Punkte; ein Menü mit zwei Punkten ist ehrlicher als eine Tür, hinter der eine zweite liegt. **Und das Profilbild lässt sich zuschneiden.** Ein Profilbild ist rund, was hochgeladen wird selten quadratisch — den Ausschnitt dem Browser zu überlassen hiesse: mittig, und das trifft bei einem Porträt den Hals. Der Dialog zeigt den runden Ausschnitt, man zieht und vergrössert, und hochgeladen wird genau das: ein Quadrat von 512 Pixeln. Gemessen, es kommt auch so an. **Dazu die Spielerleiste.** Beide Knopfgruppen durften schrumpfen, ihre Knöpfe aber nicht — wird es eng, steht der äusserste halb hinter dem Fensterrand. Rechts sind es nur Knöpfe, da gibt es nichts nachzugeben; links schon. Dazu seitlich `env(safe-area-inset-*)`, damit auf einem Gerät mit runden Ecken nichts darunter liegt. Gemessen: vorher riss es ab 420 Pixeln, jetzt bis 320 nicht mehr. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9c2e2b5f47 |
„Kenne ich" heisst nicht „kann ich diese Datei"
Der Spieler schickte sein Profil aus `canPlayType` — und das ist eine Auskunft des `<video>`-Elements. Ein Strom läuft aber nicht durch das Element, sondern durch den MSE-Puffer, und die beiden sind sich nicht einig. Gemessen in Chromium: | | canPlayType | MediaSource.isTypeSupported | |---|---|---| | `audio/flac` | probably | **false** | | `audio/mp4; codecs="mp3"` | probably | **false** | Der Server glaubte dem Profil, packte die Datei pflichtschuldig um und liess die Spur unberührt — und der Browser stand vor etwas, das er nach eigener Auskunft können müsste. Heraus kam „Dieser Browser kann die Spuren dieser Datei nicht öffnen". Jetzt zählt ein Codec nur, wenn **beide** Auskünfte ihn bestätigen; welcher der beiden Wege es wird, entscheidet der Server erst danach. Und geprüft wird jeder Typ in der Form, in der er wirklich ankommt — `audio/mp4` und nicht `audio/flac`: genau dieser Unterschied entschied bei FLAC zwischen „geht" und „geht nicht". Gemessen: MP3 fällt dadurch aus dem Profil, der Server rechnet es um, statt es durchzureichen. **Und wenn doch etwas durchrutscht, wird gerechnet statt gemeldet.** Scheitert die Wiedergabe mit einem Codec- oder Dekodierfehler, baut der Spieler den Strom genau einmal neu auf — mit `transcode: true`, an derselben Stelle. Der Satz „über das Zahnrad lässt sich eine Fassung wählen, die er versteht" ist weg: wer das liest, weiss weder welche, noch warum der Server sie nicht gleich gegeben hat. Lief schon ein gerechneter Strom, unterbleibt der zweite Versuch — dann liegt es nicht am Codec, und dreissig Sekunden Warten auf dasselbe sind keine Hilfe. **Nicht geprüft:** dass der Rückfall im Betrieb auslöst. Jede Datei im Prüfbestand spielt; ein echtes Scheitern liess sich hier nicht herbeiführen. Gemessen ist der Serverteil (`remux` wird zu `transcode`) und das neue Profil. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e30158b84c |
Eine Sekunde heisst nicht „Weiterschauen"
Die Schwelle aus den Einstellungen wirkt serverseitig, also sofort überall: `/home/continue` liest die gespeicherte Spalte. Eine Stelle blieb trotzdem widersprüchlich — die **Beschriftung**. Web und Apps entschieden „Weiterschauen" gegen „Abspielen" selbst, allein daran, ob eine Stelle über null steht. Nach einer Sekunde stand der Titel damit nicht mehr in der Reihe, hiess aber weiter „Weiterschauen", trug einen Fortschrittsbalken unter der Kachel und bot „Aus Weiterschauen entfernen" an. Der Server hatte diese Frage längst beantwortet. `ItemStateResponse` bringt sie jetzt mit; `MediaItem.isResumable` und `.resumeProgress` sind die einzigen Stellen, die sie noch stellen. Im Simulator nachgeprüft: nach einer Sekunde heisst der Knopf „Abspielen", „Von vorn" fehlt, und der Balken unter der Kachel ist weg. Kennt ein Server das Feld nicht — Jellyfin, oder ein älterer Flaro —, gilt wie früher die blosse Stelle. **Und das Profilbild steht in den Apps.** `FlaroAccount` führt es mit, `/auth/me` bringt es bei jedem Start frisch, und der Buchstabenkreis tritt zurück, sobald eines da ist. Jellyfin bleibt beim Buchstaben — dort gibt es kein Profilbild, und das ist keine Lücke, sondern der Stand der Dinge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
579a49b73b |
Der Server wusste, was du zuletzt gesehen hast — und sonst nichts
Für „wie viel habe ich letzte Woche geschaut" gab es keine Daten. `UserItemState` ist eine Zeile je Titel: der letzte Stand. Wer einen Film zweimal sieht, überschreibt den ersten Durchgang; wann etwas lief, wusste der Server nur fürs jeweils letzte Mal. Nachträglich erfinden lässt sich das nicht. Jetzt gibt es ein Sehprotokoll: **eine Zeile je Nutzer, Tag und Titel.** Keine Sitzungen — die brauchen ein Ende, und das kommt oft nie an, wenn der Browser zugemacht oder das Telefon zugeklappt wird. Tagesbündel haben diesen Lebenslauf nicht, es wird nur addiert. **Gezählt wird, was wirklich lief.** Der Zuwachs zwischen zwei Meldungen, gedeckelt auf die Zeit, die dazwischen verstrichen ist (mal zwei als Toleranz für doppelte Geschwindigkeit und ausgefallene Meldungen). Sonst ergäbe ein Sprung von Minute 5 auf Minute 90 anderthalb Stunden Sehzeit in zehn Sekunden. Gemessen an fünf Meldungen im Abstand von elf Sekunden: 88 Sekunden statt 190. Dabei fiel ein zweiter Fall auf: war **gar keine** Zeit verstrichen, griff kein Deckel statt eines engen — eine frisch angelegte Zeile hätte ihre Anfangsstelle voll mitgezählt. Wer einen Film öffnet und sofort auf Minute fünf springt, hat keine fünf Minuten gesehen. Im Profil stehen drei Zahlen, und jede sagt, woher sie kommt: die aufgezeichnete Zeit fängt heute an, die Zahl gesehener Titel steht seit jeher fest. Dazu eine Wochenkurve, die häufigsten Genres und was zuletzt lief. **Jeder sieht nur sich selbst, auch ein Administrator** — es gibt keinen Weg, ein fremdes Konto zu meinen. Zwei Dinge auf dem Weg dorthin: **Eine stellbare Uhr für die Tests.** Zwei Minuten Film lassen sich nicht in fünf Millisekunden sehen, und das ist richtig so — prüfen liess es sich nur, indem die Zeit vergeht, ohne sie zu verbringen. **Ein Namenskonflikt im CSS.** Die leeren Wochenbalken trugen die Klasse `leer`, und die gehört längst der Fläche „hier ist nichts" — mitsamt 24 Pixeln Polster. Jeder leere Balken war dadurch 48 Pixel hoch, egal welche Höhe dastand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d8a1633cf4 |
Das eigene Bild führte aus dem Server heraus
Ein Klick auf den Buchstabenkreis meldete ab. Keine Rückfrage, kein Menü, kein Weg zurück — hinter einer Geste, die überall sonst „mein Konto" heisst. Jetzt führt er auf `#/profil`; Abmelden steht dort als Knopf, mit Nachfrage. Auf der Seite: Bild wählen, tauschen, entfernen. Dazu „Meine Sprache" — **umgezogen, nicht kopiert.** Sie stand mitten in der Verwaltung, und die zeigt der Client nur Administratoren; der Endpunkt erlaubte sie längst jedem. Wer kein Admin war, konnte seine eigene Sprache nirgends ändern. Der Warnhinweis „weicht ab" bleibt in der Verwaltung, denn dort entsteht die Verwirrung — er verweist jetzt aufs Profil. **Vorher musste ein Fehler weg, der seit Monaten dasteht.** `ImageCache.StoreStreamAsync` baute den Schlüssel aus 32 Hexzeichen plus Dateiendung; `PfadFür` verlangt genau 64 Hexzeichen und nichts sonst. Es gab keinen Wert, den beide gelten liessen: **jeder Bildupload gab `null` zurück und endete in einem 502.** Kein Test fragte je danach — jetzt vier. Damit geht nebenbei auch „eigenes Bild hochladen" für Filme und Serien wieder. Weil der Schlüssel keine Endung mehr trägt, kommt der Medientyp beim Ausliefern aus den ersten Bytes der Datei. Vorher ging alles als `image/jpeg` hinaus — bei TMDB stimmte das, bei einem hochgeladenen PNG nicht. **Und die gemerkte Sitzung war eine Kopie, die nie nachzog.** Sie wurde bei der Anmeldung geschrieben und danach nie wieder. Wer sein Bild am Telefon setzt und den Browser am Schreibtisch öffnet, fand dort den alten Buchstaben — bis zum nächsten Anmelden. Gemessen und behoben: beim Hochfahren wird das Konto abgeglichen, und das Profil meldet Änderungen an die Kopfleiste, damit sie ohne Neuladen nachzieht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9622da0284 |
Eine Sekunde ist kein Anfang
Drei Zahlen standen als Konstante im Quelltext und bestimmten, wie sich der Server anfühlt. Jetzt stehen sie in der Verwaltung, unter „Wiedergabe". **Weiterschauen ab.** Gemessen: `BelongsInContinueWatching` verlangte nur `Position.Ticks > 0` — ein Tick genügte. Wer im Aufmacher eine Sekunde hineinschaute, fand den Titel danach in „Fortsetzen". Jetzt gibt es eine Untergrenze, voreingestellt auf 1½ Minuten. Was sich dabei **nicht** ändert: die Stelle wird weiter gemerkt. Das ist der Unterschied zu Jellyfin, um dessentwillen es diesen Server gibt — die Schwelle entscheidet, ob der Titel in der Reihe steht, nicht ob man ihn wiederfindet. Nachgemessen: nach dem Umstellen ist „Heat" aus der Reihe verschwunden und steht weiterhin bei Sekunde 1. **Gesehen ab.** War 90 % als `const`, ist jetzt 85/90/95. **Umkodieren.** `-preset veryfast -crf 21` und VAAPI `-qp 23` waren fest verdrahtet, ohne Einstellung und ohne Umgebungsvariable. Drei Stufen: Qualität (medium/19, qp 21), Ausgewogen (wie bisher), Tempo (ultrafast/23, qp 26). Mit dem Warnsatz daneben, und der ist ernst gemeint: was in Echtzeit laufen muss, darf nicht langsamer rechnen als es abspielt. **Und das Vorhandene wird neu bewertet.** `InProgress` ist eine gespeicherte Spalte — die Bruchrechnung lässt sich nicht nach SQL übersetzen. Eine neue Schwelle wirkte sonst nur auf künftige Meldungen. Der Endpunkt läuft deshalb einmal in Stapeln durch und meldet zurück, wie viele Zeilen das umgeworfen hat: „Übernommen — 2 Einträge wurden neu bewertet." Ohne diesen Satz änderte sich die Startseite scheinbar von selbst. `ProgressRule` bleibt ein reiner Typ: die Regeln kommen als Wert herein. Nebenbei zieht der doppelte `zeile()`-Bauer nach `teile.ts` — er stand zweimal wortgleich da. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1e4a231d1b |
Elf Abschnitte in einer Spalte waren zehn zu viel
Die Verwaltung war eine Seite mit 1786 Zeilen. Wer das Protokoll sehen wollte, scrollte an Mediatheken, Prüfliste, Benutzern und Speicher vorbei — und jede neue Einstellung hätte sie länger gemacht. Jetzt hat sie eine eigene Leiste mit sieben Bereichen, jeder mit seiner Adresse: `#/verwaltung/protokolle` lässt sich ablegen, und ein Neuladen bleibt dort stehen. Die Leiste sieht aus wie die Hauptleiste daneben, damit sie als Navigation gelesen wird und nicht als Knopfreihe; am Telefon wird aus der Spalte ein Streifen zum Schieben. **Gebaut wird nur, was offen ist.** Das ist nicht nur sparsamer: das Abspielfenster fragt im Fünf-Sekunden-Takt nach, die Mediathekenliste beobachtet laufende Scans. Gemessen: nach dem Wechsel in einen anderen Bereich kommt zwölf Sekunden lang keine Anfrage mehr. `admin.ts` ist in acht Dateien zerlegt — eine je Bereich, dazu `teile.ts` für die gemeinsame Überschrift und `index.ts` für Leiste und Weiche. Zwei Dinge mussten dabei nachziehen: **Die Markierung in der Hauptleiste.** Sie verglich stur zwei Segmente; bei `#/verwaltung/metadaten` traf das auf keinen Eintrag, und „Verwaltung" verlor seinen Balken. Jetzt gilt auch ein Segment. **„5 Titel warten — ansehen".** Der Knopf sprang mit `scrollIntoView` zur Prüfliste. Die steht nicht mehr im selben Dokument — er wechselt jetzt den Bereich. Die Hervorhebung danach entfällt damit, und mit ihr ihr CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
94c6604e8b |
Pfeile am Bildrand, und „Mehr" bleibt, wo es ist
**Pfeile.** Der Aufmacher hatte nur die Punkte; die Pfeile, die du gedrückt hast, gehörten zur Reihe darunter. Jetzt stehen links und rechts über dem Bild zwei grosse Knöpfe. Am Telefon gibt es sie nicht — dort nähme ein Knopf über dem Bild ein Drittel der Breite —, dafür lässt sich wischen. Nur waagerecht und erst ab 48 Pixeln, sonst käme man am Aufmacher nicht mehr vorbeigescrollt. **Und die Knöpfe springen nicht mehr.** „Abspielen" ist 128 Pixel breit, „Fortsetzen ab 1:23:45" 207, der Terminhinweis 257 — alles in derselben Flexreihe, also wanderte „Mehr" bei jedem Umblättern mit, und das Karussell blättert von allein. Der vordere Platz hat jetzt eine feste Breite. Gemessen: „Mehr" steht auf allen sieben Folien an derselben Stelle. Dabei fielen zwei Dinge auf, die nicht am Umbau lagen: **Der Textblock war so breit wie sein längster Satz.** Als blosses Flexkind richtete er sich nach dem Inhalt — bei einer kurzen Inhaltsangabe schmaler als bei einer langen. Auf dem Telefon rückte „Mehr" dadurch um fünfzig Pixel, und mit dem festen Platz lief die Zeile sogar über den Bildschirm hinaus; `overflow: hidden` schnitt die rechte Hälfte des Textes ab. **Ein fehlender Schriftzug liess den Aufmacher namenlos.** Steht ein Logo in den Metadaten, wurde der Titel daneben versteckt — auch wenn das Bild gar nicht lud. Zu sehen war ein Platzhalter und sonst nichts. Jetzt tritt der Name an seine Stelle. 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>
|
||
|
|
f10fb4c1bc |
Abspielen spielt ab
Im Aufmacher der Startseite stand „Abspielen", und wer draufdrückte, landete auf der Detailseite. Gemessen am laufenden Server: beide Knöpfe riefen dieselbe Funktion — `oeffnen` —, der eine hiess nur anders. Drei Dinge waren falsch, alle an derselben Stelle: **Der Knopf log.** Jetzt startet er den Spieler, mit Fortsetzen ab der Stelle, an der man war. Ohne Datei — eine ganze Serie — heisst er „Ansehen" und führt dorthin, wo man wählen kann. **Eine angefangene Folge war kein Film.** Sie lief durch `alsHero` mit `art: 'filme'` und ihrer eigenen Kennung; der Klick ging auf `#/filme/<Folgenkennung>` und endete in „Diesen Film gibt es nicht mehr". Nachgemessen im Browser, dann `angefangenAlsHero` dafür gebaut: die Detailseite gehört der Serie, der Fortschritt der Folge. **Ein geplanter Titel sah aus wie jeder andere.** Jetzt steht statt des Knopfes „Bald ist es soweit" mit dem Termin, wie auf der Detailseite. Dafür nennt `/home/continue` und `/home/next` nun Datei, Länge und Termin, und die Vorschläge bringen ihre Dateien mit — ohne die konnte der Aufmacher gar nicht abspielen. 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> |
||
|
|
f44779c899 |
Eine Zahl ohne das, worauf sie sich bezieht
„4 warten auf Freigabe" stand in der Mediathekenzeile, und die Liste darüber war leer — sogar versteckt. Zwei Gründe, beide behoben. Die Prüfliste wurde einmal beim Öffnen geholt und nie wieder. Findet ein laufender Scan danach etwas Neues, zählt die Zeile es mit, die Liste aber nicht. Sie hört jetzt auf dieselbe Meldung wie die Seitenleiste: Scan durch, etwas freigegeben, Mediathek geändert — nachladen. Und der Knopf gab blind frei. Auf die Frage „wo sehe ich die vier Filme?" war das keine Antwort: er veröffentlichte sie, ohne sie zu zeigen. Jetzt führt er dorthin, wo sie mit Plakat, Jahr und Dateiname stehen und sich einzeln anhaken lassen — und hebt die Stelle kurz hervor, damit man nicht weitersucht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
58e06d0539 |
Hanni und Nanni ist nicht Hanni und Nanni 3
Deine drei Fälle haben eines gemeinsam, und es ist nicht das, was ich zuerst vermutet hatte: keiner der Dateinamen nennt ein Jahr. Der Riegel von gestern greift deshalb nicht. Gemessen, woran es wirklich liegt. Bei TMDB heisst der Film „Hanni & Nanni", auf der Platte „Hanni und Nanni". Das Kaufmanns-Und fällt im Vergleichs- schlüssel als Sonderzeichen heraus, das Wort „und" bleibt stehen — die Namensgleichheit griff also nie. Gewählt wurde stattdessen der erstbeste Treffer, und das war „Hanni & Nanni 3". Der richtige stand auf Platz zwei, und alle vier Treffer hatten dieselbe Beliebtheit: die Reihenfolge war Zufall. Bindewörter zwischen zwei Namen zählen jetzt nicht mehr mit — gestrichen, nicht übersetzt, sonst bliebe „Batman and Robin" aussen vor. Am Wortanfang bleiben sie stehen: „Und dann kam Polly" ist kein „dann kam Polly". Und die Suche vergleicht über den Schlüssel statt Zeichen für Zeichen. Das verhindert aber nur Neues. Was schon zusammengelegt ist, bleibt es — deshalb gibt es jetzt „Eigener Film": eine Datei löst sich aus ihrem Titel und wird ein eigener Eintrag, mit dem Titel aus dem Dateinamen und ohne Kennung. Eine zu raten hiesse, denselben Fehler zu wiederholen; so steht sie unter „Ohne Zuordnung" und ist in zwei Klicks richtig. Der Super-Mario-Fall liegt anders: beide Titel treffen heute richtig. Der steckt noch aus einem früheren Lauf fest und braucht das Trennen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c3124ba27c |
Zwei Teile, ein Treffer — und einer verschwand
Du hast drei Filme vermisst, und die Zahlen sagten schon, wo sie sind: 448 Filme plus 13 zweite Fassungen sind genau die 461 Dateien. Es fehlte keine Datei; drei Filme steckten als Fassung in einem anderen. Gemessen, warum. Der Dateinamenleser ist unschuldig — „Teil 2" bleibt im Titel. TMDB aber liefert für „Heiligtümer des Todes Teil 1" wie für „Teil 2" denselben Treffer zuerst, sobald ohne Jahr gesucht werden muss. Und weil zwei Einträge dieselbe Kennung nicht teilen dürfen, wurde der zweite geschluckt. Zwei Dateien, deren Namen verschiedene Jahre nennen, sind jetzt verschiedene Filme. Der erste Anlauf hatte ein Jahr Abstand als Toleranz erlaubt — für Dezember-Erstaufführungen — und verschluckte damit genau den Fall, um den es geht: Teil 1 ist von 2010, Teil 2 von 2011. Dazu sieht man endlich, was nicht zugeordnet werden konnte. „Nicht zugeordnet" listete bisher nur Dateien ganz ohne Titel, und die gibt es bei Filmen nie — deshalb standen dort nur Serien. Daneben eine Liste der Titel mit mehreren Dateien: zwei Fassungen sind die Regel, zwei verschiedene Filme der Fehler, und an den Dateinamen sieht man es sofort. Der Bearbeiten-Dialog hat jetzt Reiter statt Knöpfe, die weitere Fenster öffnen — ein Fenster über einem Fenster, und man wusste nicht mehr, was man zumacht. „Titel ändern" und „Bilder bearbeiten" sind aus dem Kontextmenü raus; über „Bearbeiten" kommt man an beides. Beim Prüfen gefunden: ein Dialog blieb nach einem Seitenwechsel über der neuen Seite stehen. Er hängt an body, nicht am Inhaltsbereich. 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> |
||
|
|
e70c33f7e2 |
Not Found war weder deutsch noch wahr
Beim Korrigieren einer Zuordnung geht der bearbeitete Eintrag im vorhandenen auf — er wird gelöscht. Die Antwort nannte aber nur die Daten des überlebenden, nicht seine Kennung, und so zeigte die Kachel weiter auf etwas, das es nicht mehr gab. Wer sie dann bearbeiten wollte, bekam „Not Found". Zwei Fehler in einer Meldung. Der Server sagt jetzt, welcher Eintrag überlebt hat und ob zusammengelegt wurde; die Kachel nimmt sich weg statt auf ein Loch zu zeigen, und sagt wohin. Und das Englische: in `detail` steht der Satz, den der Server selbst geschrieben hat — deutsch und mit einem Hinweis, was zu tun ist. `title` ist dagegen der Statusname aus dem Rahmenwerk, und genau der wurde angezeigt, wo `detail` fehlte. Er wird nicht mehr gelesen; wo der Server nichts zu sagen hat, schreibt der Client selbst einen deutschen Satz. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c46063637f |
Die Kachel zeigte auf die Folge und wollte zur Serie
„Nächste Folge" führte ins Leere, und es lag weder am Scan noch am Auffrischen: die Kachel trug die Folgenkennung und öffnete damit eine Serienseite — die es unter dieser Kennung nie gibt. Heraus kam ausgerechnet dort „Diese Serie gibt es nicht mehr", wo man gerade weiterschauen wollte. Der Server kannte die Serie an beiden Stellen und schickte sie nur nicht mit. Wohin die Kachel führt und was sie misst, ist jetzt getrennt. Beides auf dieselbe Kennung zu legen wäre der nächste Fehler gewesen: „Als gesehen markieren" hätte die ganze Serie abgehakt statt der einen Folge, die man zur Hälfte gesehen hat. Dazu Streichen wie im Dateimanager: gedrückt halten und darüberfahren wählt aus, rückwärts über Gewähltes nimmt wieder weg. Ein kurzer Klick bleibt, was er war. Abgetastet wird die Strecke zwischen zwei Zeigerereignissen, nicht nur deren Endpunkte — gemessen: ein Zug über vier Kacheln erwischte sonst die erste und die letzte. Plakate lassen sich nicht mehr durch die Gegend schleppen. „Abwählen" ist ein Kreuz geworden; als vierter Knopf neben drei Aktionen sah es aus, als täte es etwas mit den Titeln. In der kleinen Leiste lag das Vorschaubild über dem Fortschrittsstrich und verdeckte ihn rechts. Und ein Klick genau darauf tat nichts, obwohl das die Stelle ist, auf die man zeigt — jetzt holt auch das Bild den Film zurück. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7df9fa6c01 |
Zwei Dinge in derselben Ecke, und ein Modus, den niemand bestellt hat
Der Gesehen-Haken und die drei Punkte sassen beide oben rechts — auf dem Plakat sah das aus wie ein Fehler. Jetzt hat jede Ecke eine Aufgabe: oben links die Auswahl, oben rechts der Stand, unten links Abspielen, unten rechts das Menü. Der Knopf „Auswählen" ist weg. Gemessen, warum er stören musste: der längere Text „Auswahl beenden" brach die Filterzeile um und schob das Raster um zwanzig Bildpunkte nach unten. Und grundsätzlicher ist ein Modus ein Zustand, den man kennen muss, bevor man etwas tun kann. Jetzt trägt jede Kachel oben links ein Kästchen, das beim Überfahren erscheint — wie die drei Punkte auch. Der erste Haken fängt die Auswahl an, der letzte beendet sie. Der Spieler lässt sich verkleinern. Er rutscht in eine Leiste am unteren Rand und läuft weiter, während man die Mediathek aufräumt; ein Klick darauf holt ihn zurück. Dasselbe Videoelement, nur anders eingerahmt — ein zweiter Spieler unten hiesse, das Video auszuhängen, und dabei hört es auf zu spielen. Das Zahnrad mit acht Strahlen sah aus wie eine Sonne. Jetzt: Wellenform für den Ton, CC-Kasten für die Untertitel, Schieberegler für die Qualität. Alle drei Menüs stehen immer da — vorher verschwanden sie bei nur einer Tonspur oder ohne Untertitel, und wer sie suchte, fand nichts und wusste nicht warum. Dabei gefunden und behoben: die Wiedergabe blieb nach zwanzig Sekunden stehen, obwohl längst der ganze Rest auf der Platte lag. Results.File setzte Last-Modified, der Browser legte die Playlist mit zwei Einträgen ab, und hls.js bekam bei jedem Nachfragen dieselben zwei zurück. Eine wachsende Liste ist das Gegenteil von zwischenspeicherbar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
eda9e67802 |
Wo die Grafikeinheit eingetragen wird, und was passiert wenn sie fehlt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4853200d5c |
Er packte um, was gar nicht am Umpacken lag
Der Browser bekam stillschweigend Apples Geräteprofil — und mit ihm HEVC, AC3 und EAC3, von denen er keines öffnet. Der Server packte die Datei pflichtschuldig nach MP4 um, liess Bild und Ton dabei unberührt, und heraus kam eine tadellose Datei, mit der Chrome nichts anfangen konnte. Umpacken fasst das Bild nun einmal nicht an. Jetzt sagt das Gerät selbst, was es kann, und es gibt einen zweiten Weg neben der fertigen Datei: HLS, das nach Sekunden anfängt statt nach Minuten. Dasselbe Rohr erledigt beides — mkv nach fMP4 mit „-c:v copy", 4K nach 480p mit libx264 — und eine MP4, die der Browser ohnehin öffnet, geht ganz ohne ffmpeg hinaus. Dazu eine eigene Steuerleiste: Tonspur und Untertitel während der Wiedergabe umschaltbar, vier Auflösungsstufen, Tastenkürzel, und bei einem Fehler ein deutscher Satz statt eines schwarzen Bildes. Gemessen statt vermutet: Chromium antwortet auf canPlayType für HLS mit "maybe" und kann es dann doch nicht — deshalb entscheidet Hls.isSupported(). hls.js wiegt 372 KB und wird erst beim Abspielen geholt; die Oberfläche bleibt bei 109. 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> |
||
|
|
5a5cf3d006 |
Dieselbe Kennung zweimal zu vergeben war kein Fehler, sah aber so aus
Im Zuordnungsdialog stand „An error occurred while processing your request." — englisch, ohne Grund, ohne nächsten Schritt. Dahinter steckte der Eindeutigkeitsindex: der gewählte Film war in derselben Mediathek schon vergeben, und die Kennung wurde trotzdem blind gesetzt. Es ist aber gar kein Fehler. Zwei Dateien, die denselben Film meinen, gehören zusammengelegt — genau das tut der Scan am Ende jedes Laufs, und der Sehfortschritt wird dabei gefaltet. Die Korrektur von Hand tut es jetzt auch, für Filme wie für Serien. Dazu die zweite Hälfte: auch das Letzte, was durchrutscht, spricht Deutsch. Der Fehlerbehandler am Ende der Kette gab bisher die Vorgabe von ASP.NET aus. Jetzt bekommt jede Ausnahme einen deutschen Satz, und die Einzelheiten gehen ins Tagesprotokoll — dorthin, wo man sie sucht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0c07d4efc7 |
Ein falscher Treffer verhinderte seine eigene Korrektur
„Doctor Strange" war fälschlich auf ein Making-of von 2022 gelaufen. Beim Korrigieren hängte die Vorschlagssuche `year=2022` an — aus genau der Zuordnung, die weg sollte — und lieferte nur noch Titel von 2022. Der richtige Film von 2016 konnte gar nicht erscheinen, egal wie oft man suchte. An TMDB nachgemessen: query='Doctor Strange' ohne Jahr → 2016 Doctor Strange, 2022 Multiverse … query='Doctor Strange' mit year=2022 → genau die drei Treffer aus der Meldung Gesucht wird jetzt mit dem Jahr aus dem Dateinamen, nie mit dem aus dem Treffer. Die Oberfläche trägt ebenfalls keines mehr vor: das angezeigte stammt aus derselben falschen Zuordnung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
74c625b829 |
Das Dreipunktemenü in der Prüfliste ging gar nicht auf
Der öffnende Klick blubbert nach dem eigenen Zuhörer weiter zum Dokument, und der dortige schliesst alles, was nicht im Menü liegt — also auch dieses, im selben Augenblick. In der Mediathek stand deshalb ein `stopPropagation()` am Griff; in der Prüfliste fehlte es. Eine Regel, an die sich jeder Aufrufer erinnern muss, ist keine Regel. Deshalb nicht die Kopie nachgezogen, sondern das Menü merkt sich jetzt selbst, woran es hängt, und der globale Zuhörer lässt den Griff in Ruhe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d2fbaad871 |
Jeder Titel war seine eigene Dublette, und das seit Monaten
„Metadaten auffrischen" hat an allem, was schon zugeordnet war, schlicht nichts getan. Die Dublettenprüfung schlägt jeden Titel nach, der eine TMDB-Kennung hat — beim Auffrischen also auch den gerade bearbeiteten. Ohne Vergleich auf Gleichheit fand sich jeder als "Dublette seiner selbst", wurde übersprungen und nie fortgeschrieben. Titel, Beschreibung und Bilder blieben für immer so, wie sie beim allerersten Lauf hereinkamen. In der Oberfläche sah das aus, als wirke die Spracheinstellung nicht — und genau so wurde es gemeldet. Gemessen, Ende zu Ende: Serversprache en-US: 101 Dalmatians · The Lion King Serversprache de-DE: 101 Dalmatiner · Der König der Löwen Damit das nie wieder unbemerkt bleibt, weiss der Server jetzt, in welcher Sprache er die Titel hat: eine Spalte MetadataLanguage, gesetzt beim Anreichern, und in der Verwaltung eine Zeile mit der Verteilung. Weicht etwas ab, frischt ein Knopf genau diese Titel auf — nicht den ganzen Bestand, und ohne das Dateisystem noch einmal abzulaufen. Dazu zwei Dinge, die ohne das nicht gingen: ein Titel lässt sich von Hand setzen und wird dabei festgeschrieben wie ein Plakat, mit einem Weg zurück. Und ein Titel, der auf Freigabe wartet, trägt jetzt dasselbe Dreipunktemenü wie eine Kachel — Bilder, Zuordnung, Titel. Ausgerechnet dort fehlte es, obwohl das der Eintrag ist, den man vor der Freigabe geradeziehen will. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ce38da1376 |
Die Auswahlliste klappte hinter dem Dialog auf
Ein `<dialog>` mit `showModal()` liegt in der Top-Layer des Browsers, über allem — unabhängig von `z-index`. Die Liste hing am `body` und verschwand deshalb hinter genau dem Dialog, in dem ihr Knopf steht. Jetzt wird sie in den Dialog gehängt, wenn es einen gibt. Mit `elementFromPoint` nachgeprüft: an ihrer Stelle liegt wirklich ein Listeneintrag obenauf. Dazu zwei Stellen, die zu der Vermutung geführt haben, die Spracheinstellung wirke nicht: „Übersetzungen holen" stand stur auf en-US. Neben einem Server, der auf Deutsch steht, liest sich das wie eine Aussage über den Server. Vorgewählt wird jetzt die eigene Sprache, sonst die erste, die nicht schon die des Servers ist — und der Text nennt die Serversprache. Und „Meine Sprache" sagt jetzt deutlich, wenn sie abweicht. Sie ändert nicht, was der Server holt, nur was man selbst sieht: steht sie auf Englisch, während der Server auf Deutsch holt, sieht die ganze Mediathek englisch aus und jedes Auffrischen scheint wirkungslos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |