9 KiB
Podcasts: Strom, Merkliste, Wiedergabeliste
The plan agreed on 2026-10-19 (with the user, from their listening history). It replaces
the inbox model ("Neu" to work through, "Folgen" beside it, one queue that is both a store
and the play order). Server: diora-web (podcasts/). The podcast UI is diora-android's
web-native/static/native-podcast.js, copied into static/podcasts/ — built there.
Why
- The subscriptions (about 540) are a self-chosen part of the podcast world: episodes to see go by and dip into now and then — not a list to work through. "Neu" had 3360 unplayed episodes; nobody works through that.
- The queue held 74 episodes (100+ hours): a store of "want to hear some day", yet the player played it in order as if it were the play order.
- In AntennaPod "download automatically" and "add to queue automatically" were one thing for the user: a regular podcast is one whose new episodes are on the phone. diora split them (177 feeds downloading, 8 queueing).
- "Silent" subscriptions came from AntennaPod fetching every feed on the phone, slowly. The server fetches all feeds anyway now (conditional, every 15 minutes).
The lists
Wiedergabeliste — short, what plays next. The player goes through it in order and stops when it is empty (it does not take from the Merkliste). Every episode in it is downloaded. Filled by hand: ▶ on an episode (plays it, puts it first), or "In die Wiedergabeliste" from the Merkliste or the Strom.
Merkliste — long, "want to hear". It may grow; it never expires. Grouped by podcast, each podcast collapsible (newest first inside). New episodes of Stamm subscriptions land here and are downloaded; so is what the user adds with +. An episode played from it moves to the front of the Wiedergabeliste; finished, it leaves both.
When an episode leaves both lists (heard, removed), its download is deleted (setting
delete_dequeue). Downloads follow the lists, not the subscriptions.
Subscriptions: three levels
| level | new episodes | today's mapping |
|---|---|---|
| Stamm | into the Merkliste and downloaded (per feed: or straight into the Wiedergabeliste — an option, used by none yet) | new_episodes='queue' (8) plus the regularly heard ones (about 50, list below) |
| Strom (default) | in the Strom; nothing to work through, nothing downloaded | 'inbox', and the former silent 'ignore' ones |
| Auf Abruf | not in the Strom; on the podcast's page and in search | — (for exceptions) |
The per-feed "download automatically" switch goes: Stamm means it.
Fetch interval per subscription: Automatisch (by publishing rhythm, interval_days:
daily publishers hourly, rare ones daily), stündlich, täglich, wöchentlich. The former
silent subscriptions become Strom with täglich.
The Strom (replaces "Neu" and "Folgen")
All episodes of all Strom and Stamm subscriptions, by period (Heute, Gestern, Diese Woche,
…). Within a period the podcasts are ordered by how much the user has listened to them
(episodes heard): every podcast with at least one heard episode before every one without —
those count for infinitely more (heard in the last 12 months, heard_365); among the never-heard ones, rarely publishing first.
Each podcast appears once per period, always bundled: its newest episode, and "+n ›" for the others. Episodes new since
they were last seen carry a small mark (no counts). Per episode: ▶ (Wiedergabeliste,
first), + (Merkliste), ★. Filters stay: Favoriten, Verlauf, Angefangen, Gehört.
Tabs: Wiedergabeliste · Merkliste · Strom · Abos.
Favourite subscriptions
★ on a subscription (PodcastFeed.favorite): a mark and a filter (Abos: only the favourites;
episode lists: filter=fave_feeds), whatever its level — it puts nothing on the Merkliste by
itself. Started with the Stamm subscriptions marked.
Stöbern (decided 2026-10-19)
Within the subscriptions:
- Search in titles and shownotes:
GET /podcasts/discover/search/?q=…&stream=1(all words, the last as a prefix; titles weigh more). SQLite FTS5 index, contentless (podcasts/discover.py), filled after every refresh;manage.py index_episodesfills it. - Similar episodes from the other podcasts:
GET /podcasts/discover/similar/<id>/. - People (who talks, who gives a talk), from episode titles only (
podcasts/people.py):GET /podcasts/discover/people/→[{name, episodes, feeds, score}], the ones in the most listened podcasts first;GET /podcasts/discover/person/?name=→ their episodes. - Topics of the Strom's last days, chips as for the news:
GET /podcasts/discover/topics/?days=7→[{label, ids}]. - Forgotten:
GET /podcasts/discover/forgotten/→{loved: [...], rare: [...]}— unheard episodes older than 60 days of the podcasts listened to most, and of the rare ones; the same all day.
One controlled step out (Apple's episode search and fyyd; no key):
- People elsewhere:
GET /podcasts/discover/elsewhere/?name=→ episodes naming the person in podcasts the user doesn't have ({source, podcast, feed_url, title, audio_url, guid, pub_date, duration_seconds, artwork_url, link}).POST /podcasts/discover/take/with such an item puts the episode on the Merkliste; its podcast becomes an Auf Abruf subscription. - Recommendations:
GET /podcasts/discover/recommend/→ podcasts like the most listened ones (fyyd), recommended for several first:{title, feed_url, artwork_url, description, link, because: [titles], count}. Subscribing: the usual feeds/add/ (then Auf Abruf or Strom).
Show search stays on Apple (best coverage).
Moving over
- Today's queue (74) becomes the Merkliste; the Wiedergabeliste starts empty.
- Stamm: the 8 automatic subscriptions and the regularly heard ones (≥ 8 episodes a year or ≥ 3 in 90 days, 2026-10-19): Ones and Tooze, Die Neuen Zwanziger – Der Salon, Surplus, The Deprogram, junge Welt – Tagesüberblick, Speakeasy-Bar, jwpod – Forschung, RLS Audiodokumente, The Reaction Pod, Geschichten aus der Geschichte, Märkte Mächte Emissionen, friendlyjordies, Second Thought, Neben der Spur, Forschungsquartett, GegenStandpunkt – Artikel, Hörsaal, Hakim, Marxistische Vorträge, Blätter, Kontextfenster, New Left Review, Kommunisten Kneipe, Jung & Naiv, HealthyGamerGG, Dekarldent, Daily Dose Of Dekarldent, Eine Stunde History, Übertage, Eigentlich, Zwischen Zwei Deckeln, UNFASSBAR, Über Rechts, TitosLeftLung, New Books in History, ARTEde, Irgendwas mit ARTE, Zwei wie Pech & Schwafel, Geschichte der kommenden Welten, Vollbild, Zeitfragen, Zeitfragen. Feature, Die Filmanalyse, Reaction, Wirtschaft am Mittag.
- Everything else: Strom; the silent ones with täglich.
Order of work
- Wiedergabeliste and Merkliste (server: a second list, moving the queue over, the player's order; UI: two tabs, grouped Merkliste).
- The three levels and the fetch interval; downloads following the lists.
- The Strom replacing "Neu" and "Folgen".
- Browsing — after more thought.
API (server, since 2026-10-19)
PodcastSettings.lists_v2turns the two lists on.POST /podcasts/lists/ {"on": true}moves the queue into the Merkliste (the Wiedergabeliste starts empty);{"on": false}puts the Merkliste back at the end of the queue. Off, everything works as before (apps not updated yet).- Wiedergabeliste = the queue as it was:
state.queue,/podcasts/queue/add|remove|reorder/. The player goes through it and stops at its end. - Merkliste =
PodcastLater:state.later(rows like the queue's, newest first, plusadded_at,auto— added by a Stamm feed — andin_queue),POST /podcasts/later/add/and/podcasts/later/remove/{"episode_ids": [...]}. Heard (progress at the end, mark-played) and "Folge löschen" take an episode off it. Episode lists carryin_later; filterlater. - Levels:
PodcastFeed.new_episodes—queue= Stamm into the Merkliste (with lists_v2; without it: the queue),play= Stamm into the Wiedergabeliste,inbox= Strom,ignore= Auf Abruf.auto_queuestays true for queue and play. - Fetch interval:
PodcastFeed.refresh_interval—''(by rhythm: everyinterval_dayshours, within 15 minutes and 24 hours),hourly,daily,weekly; the cron'srefresh_feedsonly fetches feeds that are due (views.refresh_due). - Strom:
GET /podcasts/episodes/?filter=stream(all but Auf Abruf; withfolder=,sort=); episodes carrydiscovered_at. Per feed instate.feeds:heard_365(episodes heard in the last 12 months: played, or listened into for more than a minute) andinterval_days.POST /podcasts/stream/seen/→{before, now};state.stream_seen_at. An episode is marked when itsdiscovered_atis after the time before the current look. - Web host (
static/js/podcasts-web.js,DioraPodcastHost):podcastLaterAdd(json episodes),podcastLaterRemove(json ids),podcastListsSwitch(on),podcastStreamSeen()(keeps the previous time asstate.stream_seen_beforefor the marks).