Skip to content

The show file

One folder per show. Audio as ordinary files; everything else in a single SQLite database beside them. Copy the folder to a USB stick and the show travels complete — episodes, transcripts, chapters, edits, custom fields, and which step of the workflow you had reached.

Ondes Courtes/
├── ondes-courtes.podlibre     one SQLite file: the whole model
├── artwork.jpg
└── episodes/
    └── 14-le-son-du-silence/
        ├── camille.wav        sources stay as files; the database points at them
        ├── invite.wav
        ├── musique.flac
        └── export.mp3

Audio is not stored in the database. A show is tens of gigabytes of audio and a few megabytes of everything else, and the two want different treatment: files for the audio, so any other tool can read it and a backup can deduplicate it; a transaction log for the rest, so an interrupted edit never leaves half a change behind.

The fixed part of the model

Table Holds
show One row. The podcast itself.
episode One row per episode.
track A lane in the editor: a microphone, the music bed.
clip A piece of audio on a track, with its source file and position.
chapter Title, start, optional image and link.
speaker A voice, named once per episode by the user.
segment A span of transcript: start, end, speaker, text.
dictionary_rule The user's own corrections, applied to a transcript without a model.
bookmark Somewhere to come back to: an episode, or a moment inside one.
field_def, field_value Everything else — see below.
workflow_state Which workflow this show uses.
mark Steps the user ticked off by hand, for work that leaves no other trace.
schema_version One integer, and the migrations that read it.

Bookmarks are here, in the show, rather than in Podlibre's own settings. A note saying "pickup to re-record" is about the work, not about the machine it was written on, so it travels when the show is copied to another one. A bookmark whose at_ms is null means the episode; a bookmark with one means a moment inside it.

The variable part

Almost nothing about podcast metadata is fixed. iTunes defines a set of tags, Podcasting 2.0 defines more each year, and a plugin may invent one tomorrow. So none of them are columns.

A field definition is a row describing a field: its key, what it attaches to, its type, its label, where it goes on the way out, and whether other plugins may read it.

field_def(key, on, type, label_key, rss_tag, id3_frame, owner, shared)
field_value(key, target_id, value)

Three layers, one mechanism:

Layer Examples Comes from
Base title, author, explicit, episodeType The iTunes podcast DTD, shipped
Extended podcast:transcript, podcast:chapters, podcast:person, podcast:funding Podcasting 2.0, shipped, switchable off
Added notes_html, episode_mood A plugin, at install time

None of the three is special-cased in code. Adding a field is inserting a row, and the third layer needs no release of Podlibre.

Because a definition carries its own RSS tag and ID3 frame, an exporter written before a field existed still emits it correctly. And a field marked shared is readable by every plugin: the show-notes writer declares notes_html, the Castopod publisher reads it, and neither needs to know the other exists.

Who may write

The supervisor's show store is the only writer. The UI reads directly, through a read-only connection, so drawing a list never waits for a lock; plugins read and write through the gate, which serialises changes and keeps a plugin from leaving a half-finished edit behind when it is killed.

Moving a show

Nothing in the database holds an absolute path. A clip points at episodes/14-…/camille.wav relative to the show folder, so the folder can be moved, renamed, copied to another operating system, or kept in a sync folder, and still open.