Commit graph

361 commits

Author SHA1 Message Date
1d6ddabb7b Podcast-Player: abgelaufene Download-Links überstehen
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m24s
Test / e2e (push) Successful in 1m48s
SoundCloud leitet die Feed-Adresse auf einen signierten Link um, der nach
zwei Minuten abläuft. Das Audio-Element fragt für jeden weiteren Teil der
Datei diesen Link an, wird abgewiesen (MEDIA_ERR_NETWORK) und blieb danach
kaputt, bis die App neu startete. Jetzt wird die Folge bei einem Fehler an
derselben Stelle neu geladen (bis zu dreimal pro Minute), und nach dem
Aufgeben beim nächsten Abspielen frisch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:28:37 +02:00
de1940b4e5 Stille überspringen: Regeln des Pausen-Kürzers, sanftere Übergänge
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m20s
Test / e2e (push) Successful in 1m53s
Der Server findet die Pausen jetzt nach den Regeln der App (PauseCutter):
10-ms-Fenster, Rauschboden als gleitendes Perzentil, ein Fünftel jeder Pause
bleibt (60–250 ms), halb davor, halb danach. Ältere Ergebnisse werden neu
berechnet (v: 2).

Der Player läuft durch diese Teile höchstens 4× schnell statt 8×: Chromium
schaltet darüber stumm, und weil currentTime in 10–20-ms-Schritten läuft,
landete der Wechsel zurück bei 8× bis zu 200 ms in der Sprache. Pro Pause
wird das Tempo gewählt, das am meisten spart und trotzdem rechtzeitig endet.
Das Tempo des Podcasts setzt die Rate während einer Pause nicht mehr zurück.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:18:16 +02:00
cac8027fb3 Stille überspringen (Browser/Desktop): Pausen schnell durchspielen statt springen; sw v109
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 8m15s
Test / e2e (push) Successful in 1m53s
Ein Sprung im gestreamten Podcast wartet aufs Netz (hörbares Haken). Jetzt
läuft eine Pause mit 4-facher Geschwindigkeit (höchstens 8x), je 0,12 s
bleiben an beiden Enden; nur Pausen über 3 s werden noch übersprungen.
Geprüft alle 40 ms statt mit timeupdate (alle 250 ms). Browser-Tab im
Hintergrund (gedrosselte Timer): weiter mit Sprüngen, sonst liefe Sprache
sekundenlang vierfach. Gemessen in der Desktop-App: Pausen 52,85–53,34 und
57,03–57,58 s schnell durchlaufen, kein Sprung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:01:57 +02:00
1c74e60224 Vorbereitete Bücher: Schlüssel enthält die Fußnoten-Erkennung der App-Schicht; sw v108
All checks were successful
Test / test (push) Successful in 8m18s
Build and push Docker image / build (push) Successful in 14s
Test / e2e (push) Successful in 2m0s
Änderungen an native-ui.js (diora-android) erneuern jetzt auch die im
IndexedDB gespeicherte Aufbereitung (window.__dioraPrepareSig).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:54:12 +02:00
a7d4d92f9e Reader: offene Notizleiste schiebt den Text nicht mehr aus der Mitte; sw v107
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m19s
Test / e2e (push) Successful in 1m52s
Die Leiste nimmt ihre Breite links weg; dieselbe Breite kommt rechts als
Abstand dazu, soweit Platz ist (die Spalte wird dafür nie schmaler). Beides
gleitet gleichzeitig, der Text bleibt stehen. Neu berechnet beim Öffnen und
Schließen der Leiste, bei Fenstergröße und Zeilenlänge/Schrift.

e2e: Test dafür (breit: Spalte bleibt, schmal: weicht aus, bleibt gleich
breit, liegt nicht unter der Leiste); Service Worker in den Tests aus (sein
Neuladen störte mitten im Test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:43:03 +02:00
656520fe8e Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
Some checks failed
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 8m23s
Test / e2e (push) Failing after 1m54s
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.

Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
a16fcac388 Browser: beim Vorlesen bleibt die Playerleiste auch im Vollbild-Reader; sw v105
Some checks failed
Test / e2e (push) Waiting to run
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:26:40 +02:00
ffcb6fba4f Refactoring Schritt 1: Browser-Tests für den Reader, in der CI; Plan in docs/refactoring.md
Some checks failed
Test / e2e (push) Waiting to run
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Has been cancelled
- e2e/reader.spec.js: ein Test-EPUB (e2e/make_epub.py) hochladen, öffnen ohne
  zu senden, lesen und Lesestand nach Neuladen, Fußnote, Markierung und
  Notizleiste, Hell/Dunkel samt Reader, alle Tabs ohne Seitenfehler.
- e2e/env.js: E2E_PORT und E2E_PYTHON; nie einen laufenden Server
  wiederverwenden (dahinter läge eine echte Datenbank).
- CI: eigener Job e2e (Playwright + Chromium); blockiert den Docker-Build nicht.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:25:06 +02:00
fd8f20ba3a Notizleiste: ruckelfrei in langen Büchern; sw v104
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m19s
Mit offener Notizleiste kostete jeder Scroll-Schritt in Das Kapital (26.000
Blöcke, 8 Markierungen) 90–110 ms, jetzt rund 3 ms:
- resolveAnchorRange nimmt die zwischengespeicherte Blockliste (_epubBlocks)
  statt für jede Markierung alle Blöcke neu zu suchen.
- Die Position jeder Markierung im Buch wird gemerkt, bis sich das Layout
  ändert.
- Die Karten liegen auf einer Ebene in Buchkoordinaten; beim Scrollen wird
  nur diese verschoben. Gebaut und vermessen wird nur, wenn sich Markierungen
  oder Layout ändern, und dann alle Karten in einem Durchgang.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:15:58 +02:00
b8a68c63db Podcast-Oberfläche aus diora-android 6a02899 (Vorlesen in der Leiste, Zurück auf dem Podcast-Tab, Farbvariablen); sw v103
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 8m19s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:05:23 +02:00
33ca682709 Feeds und Podcast-Seite im Browser auf die Farbvariablen (docs/theme.md)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:05:16 +02:00
884a18e8ef Hell und Dunkel (docs/theme.md), Vorlese-Schnittstelle für die Playerleiste; sw v102
Some checks failed
Build and push Docker image / build (push) Successful in 19s
Test / test (push) Has been cancelled
- Farbvariablen für beide Schemata in app.css (--bg --fg --muted --card --panel
  --hover --raised --border --border-strong --accent --on-accent --scrim
  --shadow), Hell als html[data-theme=light]. Umschalter im Kopf jeder Seite
  (base.html, vor dem ersten Zeichnen): Dunkel / Hell / Automatisch, ☾☀◐ in der
  Navigation; __dioraTheme / __dioraSetTheme / Ereignis diora-theme.
- Reader: neues Standard-Theme "Wie die App" (Dunkel/Hell folgt der App).
- ◑ (Kontrast zum Hintergrundbild) nur noch mit Hintergrundbild.
- home.js auf die Variablen.
- window.__dioraTts (state/toggle/stop/next/prev) und Ereignis diora-tts für
  die Playerleiste; mit __dioraTtsDock keine eigene Vorlese-Leiste mehr.
- Reader-Hinweise über der Playerleiste, wenn sie im Reader steht.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:02:33 +02:00
080cdbde5a Podcast-Oberfläche aus diora-android 5821dfa (__dioraPlayerNow für den Bildschirmschoner); sw v101
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m21s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:05:13 +02:00
60dd4926cf Lesestand: die zuletzt gelesene Stelle gewinnt, nicht mehr die weiteste; sw v100
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m21s
docs/progress-sync.md ist der Vertrag für Server, Web, Android und Desktop.

- Server: read_at (Zeit, zu der dort gelesen wurde, Uhrfehler des Geräts per
  sent_at herausgerechnet, nie nach "jetzt"), Gerät, weiteste Stelle. Ältere
  Schreibvorgänge (offline nachgereicht, liegengelassener Tab) gewinnen nicht.
  Apps ohne read_at: nur strikt vorwärts. Antwort = Zustand nach dem Schreiben.
  /books/ mit X-Server-Now, /api/sync/ mit server_now.
- Web-Reader: Zeit einer Stelle ist der Moment, in dem man durch eigenes Tun
  hinkam (Scrollen, Blättern, Tasten, Vorlesen); unverändert Liegendes
  schickt nichts bzw. nur seine alte Zeit. Vor dem Wiederherstellen und
  während eines Blicks (Fußnote, "Weit gescrollt") wird nicht gespeichert.
  Eigene Stellen in einem Ausgang (localStorage) bis der Server sie hat.
  Angebote "Auf <Gerät> weitergelesen" und "Weiteste Stelle".
- Tests: books/tests.py (Reihenfolge, Uhrfehler, Zukunft, alte Apps, weiteste
  Stelle, Liste und Snapshot).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:31:36 +02:00
fda6c01943 Einstellungen: Cloud-Verbindung bearbeiten (Name, URL, Benutzer, Unterordner, Passwort); sw v99
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 7m52s
Bisher ließ sich eine Verbindung nur anlegen und löschen, der Startordner
also nicht nachträglich setzen. Das Passwort wird nicht angezeigt; leer
lassen behält das gespeicherte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:22:04 +02:00
53ca3b0002 Leseposition: Binärsuche über die Blöcke statt alle vom Buchanfang an messen
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 8m18s
getPositionAnchor maß jeden Block vom Buchanfang bis zur Leseposition und suchte
die Blöcke bei jedem Aufruf neu – im E-Reader-Modus bei jedem Umblättern (die
Prozentanzeige), beim Speichern des Fortschritts sowieso. In Das Kapital ab
Kapitel 15: 0,8 s bei 6-fach gedrosselter CPU; auf dem tolino filtert compat61
das :has() dazu in JavaScript.
- Blockliste samt Kapitel je Block einmal je geöffnetem Buch (verworfen bei
  jeder Änderung am Inhalt, MutationObserver)
- Binärsuche nur über die angezeigten Kapitel; gleiche Regel wie vorher
  (letzter Block mit Höhe, der an/über der Oberkante beginnt, bei gleicher Höhe
  der erste; sonst der erste darunter)
- Geprüft an 480 Stellen (Kapital, Hyperpolitik; E-Reader und Scrollen): gleiche
  Anker wie vorher; 53/281 ms → 4 ms; Umblättern 21–48 ms statt bis 0,9 s
- sw v98

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:01:40 +02:00
3f2fcec3b8 sw v97 (Fußnoten-Fix und schnelleres Öffnen nach v96 aus aa4cf23)
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 7m46s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:52:16 +02:00
8662f381ed Merge remote-tracking branch 'origin/master' 2026-10-08 18:52:10 +02:00
52d8362da9 Merge branch 'fast-open' 2026-10-08 18:51:12 +02:00
3f2e309c9c Reader: Fußnoten und Seitenzahlen in Verlags-EPUBs; sw v96
- <a id="x"/> (XHTML) blieb beim Parsen als HTML offen und umschloss den
  folgenden Text, in jedem weiteren Absatz neu (Suhrkamp/transpect): die
  inline-Elemente werden vorher geschlossen.
- Seitenzahlen der Druckausgabe (epub:type="pagebreak") als kleine graue
  Marke statt am Text klebend („3Anton Jäger“).
- Fußnoten-Popover: Absatz der Notiz auch bei id auf einem inline-Element,
  ohne eigene Nummer und Seitenzahl, mehrabsätzige Noten (Gedichte).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:51:05 +02:00
077612e7bb Bücher schneller öffnen: Kapitel parallel entpacken, vorbereitete Bücher merken
Gemessen mit Das Kapital (148 Kapitel, 26 000 Absätze) im diora Reader, CPU
6-fach gedrosselt wie auf einem E-Reader: Öffnen 2,4 s → 1,2 s.
- parseEpub entpackt alle Kapitel auf einmal statt eines nach dem anderen
  (jedes wartete auf JSZips eigene Ticks) und pausiert nach ~50 ms Arbeit statt
  alle 8 Kapitel einen Frame; ebenso das Einfügen der Kapitel
- Vorbereitete Bücher (entpackt und bereinigt) liegen verschlüsselt mit dem
  Buchschlüssel in IndexedDB („parsed“): gültig für eine Fassung der Buchdatei
  (IV) und des Vorbereitungs-Codes (Hash seines Quelltexts – eine Änderung am
  Sanitizer bereitet jedes Buch neu vor); Bilder bleiben in der Buchdatei, die
  Kapitel tragen „diora-img:<Pfad>“ statt Blob-URLs. Nach 4 Wochen ohne Öffnen weg.
  Geprüft: Text, Absätze, Bilder, Fußnotenlinks, Inhaltsverzeichnis identisch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:41:56 +02:00
Marwin Schulz
aa4cf23b58 Bücherliste: Seitenzahl neben dem Lesefortschritt
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 7m42s
PDFs liefern die echte Seitenzahl, bei EPUBs wird sie aus der Zeichenzahl
geschätzt (1800 Zeichen je Seite). Gespeichert in den verschlüsselten Metadaten
beim Öffnen des Buchs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 08:50:07 +02:00
3d659fc809 /api/sync/?only=books: nur der Bücher-Teil
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 7m39s
Die Buch-Synchronisation der Apps braucht vom Schnappschuss nur Bücher,
Fortschritt, Markierungen und Lesezeichen; der Podcast-Teil (jede je gespielte
Folge) ist für ein Konto aus AntennaPod 11 MB groß und wurde bei jedem Abgleich
mitgeladen – über mobile Daten Gigabytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:50:44 +02:00
886c526201 Blogs: eigene Ordner (mehrere je Blog, umbenennen, auflösen), Lesefortschritt
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 7m47s
- Source.folders (Etiketten, Groß/Klein egal, max. 40 Zeichen); die bisherige
  Kategorie eines Blogs wird sein erster Ordner (Migration)
- /feeds/folders/rename/ und /dissolve/; in den Blog-Einstellungen Häkchen je
  Ordner plus „Neuer Ordner“; langer Druck/Rechtsklick auf einen Ordner: öffnen,
  umbenennen, auflösen; Ordnerliste in „Feeds verwalten“
- Lesefortschritt je Post (Item.progress, geräteübergreifend): die Leseansicht
  springt zurück an die Stelle, ab 90 % gilt ein Blogpost als gelesen (Nachrichten
  weiter beim Öffnen); Fortschrittsbalken und „Weiterlesen“ oben in den Blogs
- sw v95

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:54:04 +02:00
74bb3a44df Feeds: Glocke je Blog (Benachrichtigung bei neuen Posts)
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 7m42s
- Source.notify; ein neuer Post wird beim Eintreffen markiert (Item.notify),
  nie beim ersten Abruf eines Blogs, nie älter als 2 Tage
- GET /feeds/notifications/?after=<id>: ungelesene markierte Posts von Blogs mit
  Glocke nach der zuletzt gesehenen id; ohne after nur der Startpunkt
- Glocken-Schalter in der Blog-Leiste und in den Einstellungen des Blogs;
  window.__dioraFeedsOpenItem öffnet einen Post (Antippen der Benachrichtigung)
- sw v94

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:40:27 +02:00
b063e91113 Blog-Feed nach Kino: Abschnitte, Favoriten, Seltene zuerst, Ausblenden, NEU
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 7m39s
- Abschnitte Heute/Gestern/Diese Woche/Diesen Monat/Älter; je Abschnitt bis zu
  5 Posts je Favoriten-Blog zuerst, dann „Seltene zuerst“ (mittlerer Abstand der
  letzten 10 Posts, ein Post = 30 Tage) oder chronologisch
- Blog „im Hauptfeed ausblenden“: nicht unter Alle und nicht gezählt, im Ordner da
- NEU (jünger als 24 h, ungelesen) am Post, als Zahl am Tab, Punkt am Ordner
- Chips Ungelesen/Alle, ★ Favoriten, Seltene zuerst; Schalter je Blog
- Lesezeit (Wörter/230) an jedem Post; words wird beim Speichern gezählt
- sw v93

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:32:15 +02:00
bc1b7cb25e Feeds: Abo-Anmeldung für Seiten mit Bezahlschranke (junge Welt)
Some checks failed
Build and push Docker image / build (push) Successful in 1m8s
Test / test (push) Has been cancelled
- In den Einstellungen einer Quelle: Zugangsdaten der Seite; der Server prüft
  die Anmeldung sofort, speichert das Passwort verschlüsselt (Fernet, Schlüssel
  aus SECRET_KEY) und gibt es nie wieder heraus
- Artikel werden mit der angemeldeten Sitzung geholt; hinter der Bezahlschranke
  bleibt der freie Teil (paywalled) und wird nach der Anmeldung neu geholt
- Adapter je Seite (feeds/logins.py), zuerst jungewelt.de
- sw v92

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:24:59 +02:00
c8e03f343e Feeds: Ordner für Blogs, Blogs ohne Datum in Feed-Reihenfolge
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 7m30s
- Die Kategorie eines Blogs ist sein Ordner: Ordner-Chips mit Ungelesen-Zahl in
  der Blog-Ansicht, /feeds/blogs/?folder=, Ordner im OPML-Export und -Import
- Neue Feeds ohne (brauchbare) Daten: die Reihenfolge des Feeds gilt als Zeit,
  nur die ersten 3 ungelesen; ein neuer Blog bringt 7 statt 14 Tage ungelesen
- sw v91

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:54:29 +02:00
1a17c75c1b Home: Karte „Nachrichten“ mit den Top-Themen seit dem letzten Mal, neue Meldungen und Blogartikel; sw v90
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 7m26s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:22:56 +02:00
35701260e0 Feed-Reader: Tab „Feeds“ mit Nachrichten (Briefing, Stories) und Blogs
Some checks failed
Build and push Docker image / build (push) Successful in 1m2s
Test / test (push) Has been cancelled
static/js/feeds.js für Web, Desktop und beide Android-Apps:
- Nachrichten: „Seit letztem Mal“ (Top-Themen, dann nach Ressort, „Alles
  gelesen“), Stories nach Tag, Ressort-Filter
- Blogs: Ungelesen-Liste, je Blog filterbar, „Alle gelesen“
- Artikel im eigenen Lesemodus (Volltext, Schriftgröße, merken, alle Berichte
  einer Story), Browser nur auf Wunsch
- Feeds verwalten: Seite oder Feed suchen, als Nachrichten/Blog abonnieren,
  Ressort, Volltext-Regel, OPML-Import pro Ordner, Export, Aufbewahrung
- diora Reader (ohne Tab-Leiste) bekommt eine kleine Leiste Bücher/Feeds
Server: Feed-Namen ohne Slogan, Feeds mit einem Zeitstempel für alle Einträge
(Deutschlandfunk) zählen ab dem ersten Sehen; sw v89

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:16:24 +02:00
08baf970ca Feed-Reader (Server): Nachrichten und Blogs, Volltext, Meldungen bündeln
Neue App feeds: Quellen sind entweder Nachrichten oder Blog.
- Abruf wie bei Podcasts (ETag, Body-Hash, Backoff), refresh_newsfeeds für den Cron
- Anrisstexte: der Artikel wird von der Seite geholt (trafilatura), HTML bereinigt,
  relative Links absolut
- Nachrichten: gleiche Meldung aus mehreren Quellen wird eine Story (tf-idf, je
  Artikel verglichen), Briefing „Seit letztem Mal“ (Top-Themen, dann nach Ressort),
  verfallen nach news_keep_days (Standard 2)
- Blogs: Ungelesen-Liste je Blog, bleiben bis gelesen
- API unter /feeds/: Quellen suchen/abonnieren/OPML, News, Stories, Blogs,
  Artikel, gelesen, merken

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:57:23 +02:00
4256bf6b29 Home: oben der rote diora-Schriftzug statt der Begrüßung; sw v88
All checks were successful
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Successful in 7m2s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:45:15 +02:00
8b9c28cda1 Home: neue Startseite mit Weiterlesen, Weiterhören, Radio-Favoriten und Ambient; sw v87
Some checks failed
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Has been cancelled
- static/js/home.js rendert #tab-home aus dem, was die Seite schon hat:
  zuletzt gelesenes Buch (und drei weitere), laufende oder nächste Folge mit
  Restzeit und „N neue Folgen“, Favoriten-Sender (sonst die ersten
  gespeicherten) und zuletzt gehörter Sender, Drift mit der zuletzt
  gewählten Klangwelt und Sleep-Timer. Läuft im Browser, in diora desktop und
  in der Android-App (dort mit static/js/ kopiert).
- app.js: Tab 'home' (TOP_TABS), window.__dioraAmbientStart() lädt Drift bei
  Bedarf und startet es; die gewählte Klangwelt wird gemerkt und beim Laden
  wiederhergestellt.
- podcasts-web.js: Startseite Home, wenn nichts anderes gewählt ist.
- native-podcast.js aus diora-android 7dcc0c6 (Home in der Startseiten-Wahl,
  __dioraPodcastView).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:34:02 +02:00
ae72b1d67a Stille-Suche: Folge erst in Python laden, ffmpeg liest die lokale Datei
All checks were successful
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Successful in 7m4s
Die statische ffmpeg-Build stürzt im Container beim Auflösen von Hostnamen ab (SIGSEGV).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:45:19 +02:00
c1bbaccccf Stille überspringen im Browser/Desktop (Server findet die Pausen); Ambient-Rekorder im Audio-Thread; sw v86
Some checks failed
Build and push Docker image / build (push) Successful in 52s
Test / test (push) Has been cancelled
- podcasts/silence.py: ffmpeg (imageio-ffmpeg) findet die Pausen einer Folge einmal
  (silencedetect -35 dB, ab 0,4 s), im Hintergrund, höchstens 2 zugleich; gespeichert in
  PodcastEpisode.silences (Migration 0018); GET episodes/<id>/silences/
- podcasts-web.js: skip_silence als Einstellung (Gerät, pro Podcast vom Server), springt
  beim Abspielen über die Pausen (0,15 s bleiben)
- Drift: Ringpuffer für die Körner als AudioWorklet statt ScriptProcessor im Hauptthread
  (gehäufte Aussetzer am Desktop, wenn die Seite kurz stockte)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:38:06 +02:00
6cc62fd4d1 Podcasts: Stille-Schalter im Tempo-Menü, Leiste folgt dem, was spielt, podcastPause; sw v85
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 6m58s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:25:27 +02:00
45e498eb87 Ambient-Musik auf Telefonen leichter gegen Aussetzer; sw v84
All checks were successful
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Successful in 6m57s
Auf dem S21 lagen App- und Renderer-Prozess bei je ~105 % CPU. Auf Telefonen jetzt:
höchstens 10 Stimmen zugleich, eine Säge und ein Wavetable-Zyklus pro Stimme, FM nur
deutlich, keine offline gerenderten Rückwärts-Swells (Rechenspitzen), Bild in halber
Auflösung mit ~20 Bildern/s und einfachen Partikeln.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:42:45 +02:00
14720b3669 Feeds abrufen: unveränderte Feeds nicht mehr parsen, ⟳ antwortet nach höchstens 40 s; sw v83
All checks were successful
Build and push Docker image / build (push) Successful in 17s
Test / test (push) Successful in 6m59s
Der stündliche Abruf der 502 Feeds dauerte 114 s, fast nur Parsen: viele Server
ignorieren ETag/Last-Modified und schicken jedes Mal den ganzen Feed (Lage der Nation
8,4 MB = 5,9 s). ⟳ in der App lief damit in ihr 60-s-Zeitlimit und schlug fehl.

- Fingerabdruck des Feed-Inhalts (ohne lastBuildDate/Kommentare, http_body_hash,
  Migration 0017): derselbe Inhalt zählt als unverändert und wird nicht geparst
- _refresh_feeds: ein Pool für alle Feeds (16 parallel), jeder Feed wird geschrieben,
  sobald er da ist, optionales Zeitlimit
- refresh-all wartet höchstens 40 s (done/left in der Antwort); der Browser-Host
  holt den Stand dann eine Minute später noch einmal, und alle 15 Minuten

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:22:01 +02:00
986ccad5c9 Ambient: Zugfahrt – zweiter Klack jedes Paares 2 Halbtöne höher; sw v82
All checks were successful
Build and push Docker image / build (push) Successful in 12s
Test / test (push) Successful in 6m50s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:28:44 +02:00
356a5299da Ambient: Zugfahrt „klack-klack … klack-klack“ (Drift v8, Diff eingespielt); sw v81
Some checks failed
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Has been cancelled
Zwei Achsen kurz nacheinander (3,1 m), 33 m Schiene bis zum nächsten Paar, dumpferer
Anschlag, Karosserie klingt mit zwei Moden nach. Dioras eigene Drift-Änderungen bleiben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:24:43 +02:00
6e28312947 Ambient in der Player-Leiste; weniger Aussetzer bei den Klangwelten auf Telefonen; sw v80
Some checks failed
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Has been cancelled
- app.js: __dioraAmbientInfo (spielt, zuletzt gestartet, Klangwelt), meldet Moduswechsel
  an die Leiste; Radio/Folge übernimmt sie wieder
- Drift (Telefone): Musik-Bett, Delay und Hall werden vom Ausgang getrennt, wenn eine
  Klangwelt ohne sie läuft (Braunes Rauschen …); Zugfahrt und Herzschlag planen 2,5 s voraus

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:18:06 +02:00
2c64342c53 Ambient: leichter Modus für Telefone gegen Ruckeln; sw v79
All checks were successful
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Successful in 6m54s
Auf dem S21 lag die App mit Drift bei ~185 % CPU, der Ton stockte. Auf Telefonen jetzt:
Bild in einfacher Auflösung und ~30 Bilder/s, 3,5 s statt 7 s Hall, kein Oversampling,
kein ScriptProcessor (Körner) im Hauptthread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:09:28 +02:00
bd9193c869 Ambient: Frame misst sich neu, wenn die Leiste unten wächst oder schrumpft; Bedienfeld 960 px; sw v78
Some checks failed
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:07:07 +02:00
16a1e23fcc Ambient: Ton auf Android über den normalen Medienweg; dioras Lautstärke gilt für Drift; sw v77
Some checks failed
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Has been cancelled
- Drift: AudioContext mit latencyHint 'playback' – der Niedriglatenz-Weg (AAudio/MMAP)
  blieb auf dem Samsung des Nutzers stumm, obwohl Drift lief
- Drift: cmd 'volume' (0..1) von der Seite ist die Lautstärke; eingebettet ist der eigene
  Regler ausgeblendet
- app.js: setVolume() und das Laden des Frames schicken die Lautstärke an Drift (nicht in
  der Android-App: dort gelten die Lautstärketasten)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:01:03 +02:00
3ff998f9cf Ambient: Drift v4 – Bach klingt nach Bach (glucksende Bläschengruppen), Bedienfeld auf breiten Fenstern zentriert; sw v76
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 6m52s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:41:48 +02:00
0fbb948c1a Ambient: Drift v3 – 23 Klangwelten (Rauschfarben, Natur, Technik, Fokus & Schlaf); sw v75
Some checks failed
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Has been cancelled
Aus der Session "Ambient-Synthesizer-Webapp": Ambient-Musik ist jetzt ein Modus neben
weißem/pinkem/braunem/blauem/violettem/grauem Rauschen, Regen, Gewitter, Meer, Wind, Bach,
Lagerfeuer, Sommernacht, Ventilator, Flugzeugkabine, Zugfahrt, binauralen Beats,
Klangschalen und Herzschlag. Alles live synthetisiert, offline; Schnittstelle zu diora
unverändert (neu: cmd 'mode').

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:37:09 +02:00
794dbadbf5 Ambient: Sleep-Timer zählt und stoppt Drift (Browser und App), ☾ im Ambient-Tab; sw v74
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 6m55s
- app.js: __dioraAmbientCmd, meldet Start/Stopp an die App (DioraNative.ambientState,
  sie hält Drift bei ausgeschaltetem Bildschirm am Laufen) und an den Browser-Host
- podcasts-web.js: der Sleep-Timer zählt auch, solange Ambient spielt, stoppt es beim
  Ablauf; Nachtautomatik auch beim Ambient-Start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:22:00 +02:00
b7a81f3e26 Neuer Tab „Ambient“: Drift, ein generativer Ambient-Synthesizer; sw v73
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 6m55s
- static/ambient/index.html: Drift (aus der Session "Ambient-Synthesizer-Webapp",
  eine Datei, Web Audio, offline), eingebettet per iframe (?embed=1&bg=#000)
- app.js: Tab 'ambient', iframe lädt beim ersten Öffnen und füllt den Platz bis
  zur Leiste; Radio/Podcast und Ambient wechseln sich ab (postMessage
  {type:'ambient', cmd|state})
- sw.js: Ambient im Offline-Cache (ignoreSearch für die Shell-Dateien)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:08:57 +02:00
64e9d70647 Podcasts: mehrere Folgen nacheinander wegwischen, während „Rückgängig“ steht; sw v72
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 6m51s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:40:39 +02:00
c522b22cd9 Oberfläche eingedeutscht: Radio, Bücher/Reader, Fokus, Konto, Anmeldung; sw v71
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 6m56s
- app.js, Templates, Stream-Fenster, Manifest: alle sichtbaren Texte deutsch
  (Eingabefenster "Abbrechen", Markierungs-Export, Lesezeichen, Reader-Einstellungen …)
- LANGUAGE_CODE 'de': Formular-, Passwort- und Validierungsmeldungen von Django deutsch
- Empfehlungen: "Was du unter der Woche abends oft hörst:"
- Fehlercodes der API bleiben englisch (Vertrag für Clients und Tests); was die
  Seite davon zeigt, übersetzt _germanError()

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:53:01 +02:00