Commit graph

12 commits

Author SHA1 Message Date
5a2841e7a6 AntennaPod-Import und automatischer Download pro Podcast
- Neuer Befehl `manage.py import_antennapod <backup.db> --user <name> [--dry-run]
  [--global-auto-download on|off] [--backup-date ...]`: übernimmt aus einer
  AntennaPod-Datenbanksicherung die Abos (Abgleich über normalisierte URL,
  fehlende werden angelegt, lokale übersprungen), die Podcast-Einstellungen
  (Tempo, Intro/Outro, Lautstärke, Filter mit Phrasen in Anführungszeichen,
  Mindestlänge, neue Folgen, „nicht aktualisieren“ -> ignorieren, automatischer
  Download), den Zustand jeder Folge mit Zustand (gehört, neu, Position,
  Favorit, Zeitpunkt), die Warteschlange (AntennaPods Reihenfolge zuerst) und
  nimmt bekannte, dort nicht neue Folgen aus „Neu“. Neuere Änderungen in diora
  bleiben, Favoriten werden vereinigt. Alles in einer Transaktion; ein zweiter
  Lauf ändert nichts mehr.
- PodcastFeed.auto_download (Einstellung pro Podcast, in feeds/, state/ und
  feeds/<id>/settings/) und PodcastSettings.auto_download_default (in settings/
  und state/), mit dem neue Abos (Hinzufügen, OPML, gpodder, Import) starten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:41:04 +02:00
578b0e478b Merge: Kapitel aus der Audiodatei (ID3) und Transkripte
Some checks failed
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Failing after 30s
Migration als 0009 hinter 0008_feed_http_cache eingereiht; Tests an die
neue _fetch_feed-Schnittstelle (FeedFetch) angepasst. 202 Tests grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:59:33 +02:00
38d7f6c643 Podcasts: Kapitel aus dem ID3-Tag der Audiodatei, Transkripte
- Ohne Kapitel im Feed (kein PSC, keine chapters_url oder dort nichts)
  liest die Folgen-Detailansicht einmalig den ID3v2-Tag am Anfang der
  Audiodatei (Range-Anfrage, höchstens 4 MB, Weiterleitungen mit
  SSRF-Prüfung) und übernimmt dessen CHAP-Frames (v2.3/v2.4,
  Unsynchronisation, erweiterter Header, Synchsafe-Größen, CTOC-Reihenfolge).
  Eigener Parser nach der ID3-Spezifikation; Ergebnis wird wie die
  JSON-Kapitel zwischengespeichert, vorübergehende Fehler nicht.
- Neu: GET /podcasts/episodes/<id>/transcript/ holt das beste Transkript
  (JSON vor VTT vor SRT, Deutsch bzw. ohne Sprachangabe vor Englisch),
  zerlegt es in Abschnitte {start, end, speaker, text}, fasst kurze
  Schnipsel desselben Sprechers bis zum Satzende bzw. ~30 s zusammen und
  speichert das Ergebnis (eigene Tabelle EpisodeTranscript). HTML: 404
  {"error": "unsupported"}.
- Folgen-Detail liefert zusätzlich has_transcript.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:54:03 +02:00
9b0848c30c Merge: Podcast-Einstellungen pro Podcast und robusteres Feed-Laden
Migrationen hintereinander statt nebeneinander: 0007_feed_settings,
dann 0008_feed_http_cache. Tests der Podcast-Regeln an die neue
_fetch_feed-Schnittstelle (FeedFetch) angepasst; Feeds-Liste trägt
Einstellungen und Fehlerfelder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:52:31 +02:00
7fdcbb4a3d Podcasts: Einstellungen pro Podcast auf dem Server, Regeln für neue Folgen, Warteschlange sortiert halten
- PodcastFeed (Migration 0007_feed_settings): playback_speed (null = Tempo
  des Geräts), skip_intro, skip_outro, volume (0.2/0.5/1.0/1.5/2.0/2.5),
  new_episodes (inbox/queue/ignore), include_words, exclude_words,
  min_duration. Abos mit auto_queue bekommen new_episodes='queue';
  auto_queue bleibt gleich (new_episodes == 'queue'), auch über
  set-auto-queue/ und beim Speichern im Code.
- POST feeds/<id>/settings/: beliebige Teilmenge, prüft jeden Wert (400 mit
  Feldname, dann wird nichts geändert), nur eigene Abos (sonst 404);
  Antwort sind alle Einstellungen des Abos. feeds/ und state/ liefern sie
  für jedes Abo mit.
- Neue Folgen beim Aktualisieren: was am Filter scheitert (keins der
  Wörter, ein Ausschlusswort im Titel, kürzer als die Mindestlänge) und
  bei 'ignore' alles verlässt sofort „Neu“ (nicht gehört); 'queue' reiht
  an der auto_queue_position ein; 'inbox' wie bisher. Beim ersten Abruf
  zählt weiter nur die neueste Folge.
- PodcastSettings.keep_sorted ('', new, old, short, long, feed): danach
  wird die ganze Warteschlange nach jedem Einreihen, jedem Umsortieren und
  beim Einschalten neu sortiert; settings/ und state/ liefern den Wert.
- Tests für alles davon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:47:49 +02:00
bbe5f6c5c2 Podcasts: Feeds bedingt abrufen, Fehler speichern, Umzüge folgen, Backoff
- Bedingter Abruf: ETag/Last-Modified werden nach einer 200 gespeichert und
  als If-None-Match/If-Modified-Since mitgeschickt. Bei 304 wird nichts
  geparst und keine Folge geschrieben, nur last_refreshed_at aktualisiert und
  der Fehler gelöscht. Teilen sich mehrere Abos eine URL, gehen die Validatoren
  nur mit, wenn alle schon einmal abgerufen wurden und dieselben haben.
- Fehler (Netz, HTTP >= 400, kein Feed) landen in last_error, last_error_at
  und error_count; ein erfolgreicher Abruf setzt sie zurück. Feed-Liste und
  /podcasts/state/ liefern die drei Felder mit.
- Dauerhafte Weiterleitungen (301/308 am Anfang der Kette) ändern rss_url,
  außer der Nutzer hat die neue URL schon abonniert; 302/307 ändern nichts.
- Backoff im stündlichen Job: ab 3 Fehlern in Folge nur alle 2, 4, 8, 16,
  höchstens 24 Stunden. „Alle aktualisieren“ und --feed-id versuchen es
  trotzdem (max_age gilt weiter).
- _fetch_feed liefert jetzt ein FeedFetch-Objekt bzw. wirft FeedFetchError.
- Migration 0007_feed_http_cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:45:38 +02:00
f9b3a0ce04 Podcasts: Inbox und Gehört wie in AntennaPod
Some checks failed
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Failing after 28s
- Eingereihte und angefangene Folgen verlassen die Inbox (ausgeblendet,
  nicht gehört), statt dort neben der Warteschlange zu stehen.
- Gehört heißt: höchstens 30 s vor dem Ende, wie in den Apps, statt bei
  90 % (bei 2 h waren das 12 Minuten vor Schluss). Gilt auch für den
  gPodder-Sync.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:26:28 +02:00
f30bf2fcdc Podcasts: Favoriten, Folgenliste mit Filtern und Verlauf, Hörstatistik, OPML-Export
Some checks failed
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Failing after 27s
- EpisodeProgress.favorite (Migration 0006) und POST episodes/<id>/favorite/
  (setzen oder umschalten; ändert updated_at nicht, damit Verlauf und
  Statistik nicht verfälscht werden)
- GET episodes/: alle Folgen aller Abos, Filter all/unplayed/in_progress/
  played/favorites/history, feed_id, q, offset/limit/total; zwei Abfragen
  pro Seite, ohne Shownotes
- Folgendetails liefern zusätzlich favorite und last_played_at
- GET stats/: Hörzeit gesamt und pro Podcast, optional nur die letzten N Tage
- GET feeds/export.opml: Abos als OPML 2.0 (lässt sich wieder importieren)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:12:27 +02:00
fd0a103fb2 Podcasts: state/ ohne Shownotes in der Inbox
Some checks failed
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Failing after 28s
Seit die Shownotes content:encoded nehmen (bis 50 KB pro Folge), würde
die eine Anfrage der Apps mit 200 Inbox-Folgen sehr groß. state/ lässt
sie weg; die Apps holen sie pro Folge über episodes/<id>/. inbox/ für
das Web liefert sie weiter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:01:34 +02:00
f4b8bcc891 Podcasts: Kapitel, Shownotes und Transkripte für die Apps
- Feeds liefern jetzt Podlove-Kapitel (psc:chapters), Podcasting-2.0-Kapitel
  (podcast:chapters, JSON-URL) und Transkripte (podcast:transcript, auch
  mehrere); als Beschreibung wird content:encoded bevorzugt.
- Bekannte Folgen werden beim Aktualisieren nachgezogen, aber nur die Zeilen,
  die sich wirklich geändert haben.
- Neuer Endpunkt GET /podcasts/episodes/<id>/ mit bereinigten Shownotes,
  Zeitmarken, Kapiteln, Transkripten und Fortschritt. JSON-Kapitel werden
  einmal serverseitig geholt (nur öffentliche Adressen, max. 1 MB) und an
  der Folge gespeichert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:56:44 +02:00
a1e17081c5 Podcasts: neues Abo bringt nur die neueste Folge in die Inbox
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 2m3s
Beim ersten Abruf eines Feeds (Abonnieren, OPML-Import, gpodder) kam der
ganze Rückkatalog als „neu“ in die Inbox (bei Lage der Nation 200
Folgen). Jetzt zählt nur die neueste; die älteren sind ausgeblendet,
nicht gehört, und stehen weiter in der Folgenliste des Podcasts. Auch
automatisches Einreihen nimmt beim ersten Abruf nur diese eine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:48:45 +02:00
176771a407 Podcasts: Position in der Warteschlange einstellbar; Feeds nur noch auf dem Server holen (SW v51)
All checks were successful
Build and push Docker image / build (push) Successful in 19s
Test / test (push) Successful in 2m0s
Warteschlange
- PodcastSettings: wohin neue Folgen kommen, getrennt für Hinzufügen von
  Hand (auch beim Abspielen einer Folge außerhalb der Warteschlange) und
  für automatisch eingereihte neue Folgen: ans Ende, an den Anfang oder
  nach die laufende Folge. Voreinstellung wie bisher (Hand: Ende,
  automatisch: Anfang). GET/POST /podcasts/settings/.
- queue/add nimmt auch mehrere Folgen (episode_ids), eine Position
  (where) und die laufende Folge (current_episode_id); ohne Angabe gilt
  die zuletzt angehörte, nicht fertige Folge als laufende.
- Positionen werden lückenlos neu durchnummeriert.

Feeds
- POST /podcasts/feeds/refresh-all/ aktualisiert alle Feeds des Nutzers
  auf dem Server: Downloads parallel, jede Feed-Adresse nur einmal (auch
  über Nutzer hinweg), mit Timeout; kürzlich aktualisierte werden
  übersprungen. Der stündliche Cron-Job nutzt dasselbe.
- GET /podcasts/state/ liefert Abos, Warteschlange, Inbox und
  Einstellungen in einer Antwort (für die Apps).
- Web: „↻ All“ ist eine Anfrage statt einer pro Feed; die stündliche
  Schleife im Browser lädt nur noch die Listen neu, statt jeden Feed
  selbst anzustoßen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:26:16 +02:00