Skip to main content

Gpodder

@beddari/gpodderv2026.10.27.1· MIT· 1d agoBEHAVIORSWORKFLOWSREPORTSACCEPTED WARNINGS
01README

A gPodder podcast library as swamp data, and a pipeline from new downloads to notes: transcripts, Claude's summaries and chapters, Markdown for a personal knowledge base, a weekly listening audit and feed health checks. Three model types. @beddari/gpodder/library reads gPodder's SQLite database read-only: subscriptions without their credentials, episodes with their files, show notes as Markdown and feed chapters; downloads returns what was downloaded since its last run, by a cursor; listening records what was played in a window; feedHealth fetches every feed, probes its newest enclosures and records incidents (dead, moved, stale, not a feed, no published transcripts, downloads gone or untranscribed), each error and warning also logged. @beddari/gpodder/transcriber turns an episode into timed segments and WebVTT with whisper.cpp on the machine or any OpenAI-compatible transcription endpoint. @beddari/gpodder/enricher has Claude read the show notes and transcript and return a schema-checked summary, topics, timestamped chapters, key points, cleaned notes and mentions, and exports every enriched episode as a Markdown note with front matter, e.g. into an Obsidian vault or a notes repository. Three workflows wire them: @beddari/gpodder-ingest (hourly, or from the gPodder hook in the README right after a download), @beddari/gpodder-feed-health (daily) and @beddari/gpodder-weekly-listening (Sundays), with a listening report and a feed health report. Nothing ever writes to gPodder.

02Release Notes

First release: gPodder library as data, whisper.cpp/OpenAI transcription, Claude enrichment with Markdown export, weekly listening and feed health workflows and reports.

03Behaviors3
@beddari/gpodder/libraryv2026.10.27.1library.ts

Global Arguments

ArgumentTypeDescription
home?stringgPodder's home directory, holding Database. Default: $GPODDER_HOME, else ~/Library/Application Support/gPodder
downloads?stringgPodder's download directory. Default: $GPODDER_DOWNLOAD_DIR, else <home>/Downloads
busyTimeoutMs?coerce.numberHow long a read waits for a gPodder that is writing its database
httpTimeoutMs?coerce.numberPer-request timeout for feeds and enclosures, in milliseconds
concurrency?coerce.numberFeeds fetched at once by feedHealth
maxFeedBytes?coerce.numberBytes of a feed read at most; a larger feed is read up to here
fn podcasts()
Record every subscription, without its credentials. Read-only.
fn episodes(state?: enum, podcastIds?: array, limit?: coerce.number)
Record episodes by state, newest first, one record each. Read-only.
ArgumentTypeDescription
state?enumdownloaded: files on disk; new: gPodder's new episodes; all: every episode
podcastIds?arrayOnly these podcasts; every podcast when omitted
limit?coerce.numberThe newest this many, by publication date
fn downloads(backfill?: coerce.number, limit?: coerce.number, pendingFor?: array, maxAttempts?: coerce.number)
Record the episodes downloaded since the last run, newest first, and advance the cursor. With pendingFor,
ArgumentTypeDescription
backfill?coerce.numberOn the first run only: also return this many of the newest downloads instead of taking them all as seen
limit?coerce.numberNew downloads returned at most per run; the rest stay new for the next
pendingFor?arrayDefinitions, e.g. a transcriber and an enricher, whose record of an episode (episodeId and guid) means it is done.
maxAttempts?coerce.numberHow often an episode is returned before it is given up on
fn listening(days?: coerce.number, until?: string, outputDir?: string)
Record what was played in a window ending now (or at until): episodes, progress and per-podcast totals.
ArgumentTypeDescription
days?coerce.numberLength of the window, ending at until
until?stringEnd of the window, ISO 8601; now when omitted
outputDir?stringAlso write the summary as <outputDir>/<YYYY-Www>.md, e.g. into a notes repository;
fn feedHealth(podcastIds?: array, includePaused?: boolean, enclosures?: coerce.number, staleDays?: coerce.number, transcriptTag?: boolean, checkDownloads?: boolean, transcriber?: string, failOn?: enum)
Fetch every subscribed feed, probe its newest enclosures, check downloads, and record the incidents;
ArgumentTypeDescription
podcastIds?arrayOnly these podcasts; every subscription when omitted
includePaused?booleanAlso check subscriptions gPodder has paused
enclosures?coerce.numberNewest enclosures probed per feed, with HEAD; 0 probes none
staleDays?coerce.numberA feed whose newest episode is older than this is stale
transcriptTag?booleanReport feeds that publish no <podcast:transcript>, as info
checkDownloads?booleanReport downloaded episodes whose file is gone
transcriber?stringName of a @beddari/gpodder/transcriber definition: report downloaded episodes it has not transcribed
failOn?enumFail the method, after recording everything, on an incident of at least this severity

Resources

podcast(infinite)— One subscription, without its credentials
episode(infinite)— One episode: its podcast, state, playback position, file and show notes
episodeList(infinite)— Which episodes one episodes run returned, and whether it stopped at its limit
cursor(infinite)— The downloaded episodes downloads has already returned
downloads(infinite)— One downloads run: which episodes were new
listening(infinite)— What was played in one window of time
feedHealth(infinite)— One feed's fetch, its newest enclosures and its incidents, from one health check
healthCheck(infinite)— One health check: every incident across the library
@beddari/gpodder/transcriberv2026.10.27.1transcriber.ts

Global Arguments

ArgumentTypeDescription
library?stringName of the @beddari/gpodder/library definition whose episode records are transcribed
engine?enumwhisper-cpp: whisper.cpp on this machine, audio never leaves it.
language?stringSpoken language as an ISO 639-1 code, e.g. en or nb; auto lets the model detect it
whisperBinary?stringwhisper.cpp's command-line program (whisper-cli; main in older builds)
whisperModel?stringwhisper.cpp model file, e.g. ~/models/ggml-large-v3-turbo.bin; required by whisper-cpp.
ffmpegBinary?stringffmpeg, which decodes the episode for whisper.cpp
threads?coerce.numberwhisper.cpp threads
apiUrl?stringBase URL of the OpenAI-compatible API, without /audio/transcriptions
apiKey?stringAPI key for the openai engine, e.g. ${{ vault.get("<vault>", "openai-api-key") }};
apiModel?stringTranscription model on that endpoint; it must return verbose_json segments for timestamps
maxUploadBytes?coerce.numberLargest file the openai engine sends; OpenAI's limit is 25 MB
timeoutMs?coerce.numberHow long one transcription may take, decoding included
fn transcribe(episodeId: coerce.number, force?: boolean)
Transcribe one downloaded episode's file and record its transcript and WebVTT;
ArgumentTypeDescription
episodeIdcoerce.numbergPodder's id of a downloaded episode, as in the library's episode-<id> records
force?booleanTranscribe again even when this episode's file was already transcribed with this engine and model

Resources

transcript(infinite)— One episode's transcript: timed segments, language, engine and model

Files

vtt(text/vtt)— One episode's transcript as WebVTT
@beddari/gpodder/enricherv2026.10.27.1enricher.ts

Global Arguments

ArgumentTypeDescription
library?stringName of the @beddari/gpodder/library definition holding the episode records
transcriber?stringName of the @beddari/gpodder/transcriber definition holding the transcripts
apiKey?stringAnthropic API key, e.g. ${{ vault.get("<vault>", "anthropic-api-key") }}.
baseUrl?stringAnthropic API base URL, for a proxy or gateway
model?stringClaude model id
effort?enumHow hard Claude thinks; medium suits summarising, high gives finer chapters on long episodes
maxTokens?coerce.numberCeiling on Claude's answer, thinking included
fallbacks?booleanOn a policy refusal, let the API retry on another model (server-side fallback, Claude API only)
topics?arrayA fixed vocabulary of topics; when given, every topic is one of these
outputLanguage?stringLanguage of summaries and chapter titles, e.g. English or Norwegian; the episode's own when omitted
exportDir?stringWhere export writes notes when its outputDir is not given, e.g. ~/Notes/Podcasts; with neither, export writes nothing
maxInputChars?coerce.numberLargest prompt sent; an episode over it fails rather than being cut
fn enrich(episodeId: coerce.number, requireTranscript?: boolean, force?: boolean)
Send one episode's show notes and transcript to Claude and record the summary, topics and chapters;
ArgumentTypeDescription
episodeIdcoerce.numbergPodder's id of the episode, as in the library's episode-<id> records
requireTranscript?booleanFail when the transcriber has no transcript of the episode, instead of working from the show notes
force?booleanAsk Claude again even when this episode was enriched from the same transcript with the same model
fn export(outputDir?: string, since?: string, includeTranscript?: boolean, includeAudioUrl?: boolean)
Write every enriched episode as a Markdown note under outputDir, one directory per podcast;
ArgumentTypeDescription
outputDir?stringDirectory the notes go in, one subdirectory per podcast, e.g. ~/Notes/Podcasts; the definition's exportDir when omitted.
since?stringOnly episodes published on or after this date, ISO 8601
includeTranscript?booleanAppend the timed transcript to each note
includeAudioUrl?booleanPut the episode's media URL in each note's front matter. Off by default: a private feed's media URLs

Resources

enrichment(infinite)— One episode's summary, topics, chapters, key points, cleaned notes and mentions
export(infinite)— One export: which notes were written and which episodes were skipped
04Workflows3
@beddari/gpodder-ingest7159c8de-7b9c-4e95-bcbb-075cea621318

Turn gPodder's new downloads into notes. Finds the episodes downloaded since the last run (the library's downloads method and its cursor), transcribes each one, has Claude summarise it and mark its chapters, and writes every enriched episode as a Markdown note into the enricher's exportDir. Uses three definitions by name: gpodder (@beddari/gpodder/library), gpodder-transcriber (@beddari/gpodder/transcriber) and gpodder-enricher (@beddari/gpodder/enricher); create them first. Each job takes only

discoverThe episodes gPodder downloaded since the last run.
1.downloadsgpodder.downloads
transcribe
1.transcribe-${{ self.episode }}gpodder-transcriber.transcribe— Speech to text for one episode; skipped when its file was already transcribed the same way.
enrich
1.enrich-${{ self.episode }}gpodder-enricher.enrich— Claude's summary, topics and chapters for one episode; skipped when already done from the same transcript.
export
1.notesgpodder-enricher.export— Every enriched episode as a Markdown note in the enricher's exportDir; unchanged notes are left alone.
@beddari/gpodder-feed-healthe73b5f77-e171-4dc9-aa51-a14b9d698d8c

Daily health check of every gPodder subscription. Fetches each feed and probes its newest enclosures, records one feedHealth record per feed and one healthCheck with every incident (unreachable or failing feeds, feeds not RSS or Atom, permanent redirects, stale and empty feeds, dead enclosures, feeds without published transcripts, downloads whose file is gone, and downloads not transcribed when a transcriber is named), logs each error and warning, and renders the feed health report. Uses the gpo

check
1.feedsgpodder.feedHealth
@beddari/gpodder-weekly-listening5de5512d-9f1c-4636-965b-7bd28b2b046d

Every Sunday evening, what was played in gPodder that week: per podcast and per episode, finished or how far, recorded as the week's listening record and rendered by the listening report. With outputDir, the week's page is also written there as YYYY-Www.md, e.g. into a private notes repository. Uses the gpodder definition (@beddari/gpodder/library). gPodder knows positions reached, not time spent, and only what its player reports.

listening
1.weekgpodder.listening
05Reports2
@beddari/gpodder/feed-health-reportmethod
feed_health_report.ts

A library's feed health check: incidents by severity

gpodderfeedshealth
@beddari/gpodder/listening-reportmethod
listening_report.ts

What was played in a library's listening window, by podcast and by episode

gpodderlisteningaudit
06Stats
A
100 / 100
Downloads
0
Archive size
238.7 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Docs keep secrets in a vault1/1earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License2/2earned
  • Verified public repository2/2earned

08Platforms
09Labels