- Jedes Kapitel hat ✓/○ zum Umschalten. Ein bis zum Ende gehörtes, das wieder
als nicht gehört gilt, beginnt von vorn (mark-played setzt die Stelle zurück).
- Feeds, die Titel, Datum oder Nummer schon bekannter Folgen korrigieren, werden
übernommen: davon hängt die Kapitelreihenfolge ab (Das Kapital, Erster Band,
hatte Daten aus einer älteren Fassung des Feeds). Ein Aktualisieren von Hand
und das Markieren als Hörbuch lesen den Feed dafür ganz.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ein Abo lässt sich als Hörbuch (oder Hörspiel) markieren (PodcastFeed.kind).
Es verlässt dann die Podcast-Welt: keine „Neu“-Folgen, keine Warteschlangen-
Automatik, nicht in „Folgen“. In der Bibliothek steht es neben den E-Books
(static/js/audiobooks.js, eine Datei für Web, Desktop und Android):
„Weiterhören“, ein Fortschritt pro Buch, die Kapitel in Reihenfolge,
Lesezeichen mit Notiz, Schlummer bis Kapitelende.
Server: /podcasts/audiobooks/ (+<id>/), /podcasts/bookmarks/…, Migration 0019.
Player (podcasts-web.js): podcastPlayBook spielt die Kapitel nacheinander
außerhalb der Warteschlange; ⏭ markiert nicht als gehört. Vertrag:
docs/audiobooks.md. native-podcast.js aus diora-android 7b1d500.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eine YouTube-Adresse (Kanal, Playlist, Video, Feed) lässt sich wie ein
Podcast abonnieren und in der Suche einfügen. Der Server liest dafür nur
die Kanalseite (einmal) und danach den RSS-Feed des Kanals; Ton oder Video
holt er nie: Die Adresse einer Folge ist die Videoseite (watch?v=…), und
jede App sucht sich den reinen Audio-Stream selbst (docs/youtube.md).
Shorts bleiben draußen, Stille-Analyse und ID3-Kapitel gibt es für solche
Folgen nicht.
Der Player im Browser verweist auf die Apps; die Desktop-App fragt ihren
Hauptprozess (yt-dlp) nach dem Stream. Eine Dauer, die der Feed nicht hat,
meldet der Player mit dem Fortschritt (progress/save: duration_seconds).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
Ä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>
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>
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>
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>
- 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>
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>
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>
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>
- <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>
- 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>
- 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>
- 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>
- 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>
- 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>
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>
- 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>
- 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>
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>
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>
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>
- 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>
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>
- 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>
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>
- 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>
- 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>
- 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>
- Player-Sync: Aktion 'sleep' (Minuten, -n Folgen, 0 aus); Reports schicken
sleep {left_ms, episodes} mit, der State zeigt ihn (Migration 0016)
- Browser-Host: Nachtfenster sleep_from/sleep_to, Automatik auch beim
Radiostart (auch in diora desktop), Timer des anderen Geräts zählt mit,
ein Timer, der stundenlang nicht lief, verfällt
- Eingabefenster (customPrompt) liegt über Player und Menüs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- EpisodeProgress.discarded_at (Migration 0015); POST /podcasts/episodes/discard/
{"episode_ids": [...]}: aus der Warteschlange (_dequeue, auch von queue/remove benutzt)
und aus "Neu" (dismissed), discarded_at = jetzt; gehört, Stelle, Stern und updated_at
bleiben; fremde Folgen werden ausgelassen (count)
- state/ liefert "discarded": [{id, at}] der letzten 30 Tage: jedes Gerät löscht seine
ältere Datei; wieder eingereiht (_enqueue) wird discarded_at gelöscht
- Browser-Host: podcastDiscard (pausiert geladene Folge verlässt den Player, laufende spielt
zu Ende), podcastPlayEpisodeAt (Kapitel auf der Folgen-Seite)
- static/podcasts aus diora-android ee1a910 (Folgen-Seite, Folge löschen, Datei löschen,
Wischtexte und Icons, Ziehgriff)
- Tests: DiscardTests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setzt eine Seite window.DIORA_PODCAST_HOST, arbeitet podcasts-web.js trotz
vorhandenem DioraNative als ihr Podcast-Host:
- dock: 'existing' nimmt native-radio.js' #nui-dock statt einer Leiste in der
Web-Radioleiste; die Podcast-Seite lädt erst nach DOMContentLoaded
- styles: 'ui-only' nur podcast-ui.css, ohne das Browser-Layout (html.dpw)
- radio: false die Radio-Hooks (__dioraRadioNow …) kommen von native-radio.js;
der Sender legt die Folge weiterhin beiseite, Radio-Stopp meldet der Sync weiter
- deviceName/deviceKind wie der gemeinsame Player das Gerät führt
Ohne die Variable ändert sich im Browser nichts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- state/ liefert inbox_total (alle neuen Folgen) und inbox_limit neben der
gekürzten Liste; Test dazu.
- Browser: Startseite (nui_start_page) beim Öffnen, „Rückgängig“-Leiste über
der Radioleiste, helle Ansicht für die neuen Teile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PodcastSettings (Migration 0014): skip_back / skip_forward (5–60 s),
smart_played, speed_presets (0,5–3,5×), keep_skipped, delete_dequeue,
mobile_downloads, mobile_streaming (allow/ask/never), times_by_speed;
die ersten vier sind anfangs leer (null) und werden mit POST
settings/ {"initial": {...}} nur dort gesetzt, wo noch nichts steht
- GET/POST /podcasts/settings/ und state.settings mit allen Feldern,
geprüft, nichts ändert sich bei einem ungültigen Wert
- PodcastFeed: episode_sort, episode_filter, skip_silence in
feeds/<id>/settings/ und den Feeds des States
- Tempo pro Podcast bis 3,5× (Server und AntennaPod-Import)
- AntennaPod-Import: Sortierung, Filter und Stille pro Podcast, soweit
diora sie zeigen kann; nicht darstellbare werden gezählt
- Browser-Host: Sprungweiten, "gehört" und keep_skipped aus dem Konto,
Tempo bis 3,5×, neue Einstellungen werden gespeichert
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>