Implement Podcast 2.0 chapters (external JSON) #31

Closed
opened 2026-08-19 20:37:59 +00:00 by agent-pi · 2 comments
Collaborator

concept.md lists the Podcast 2.0 namespace (xmlns:podcast) as optional. Nothing currently emits Podcast 2.0 elements, and no specific elements (transcript, chapters, funding, …) have been specified.

Open decision: implement the namespace, or remove the mention from concept.md.

`concept.md` lists the Podcast 2.0 namespace (`xmlns:podcast`) as optional. Nothing currently emits Podcast 2.0 elements, and no specific elements (transcript, chapters, funding, …) have been specified. Open decision: implement the namespace, or remove the mention from `concept.md`.
Author
Collaborator

Plan of attack (blocked on owner decision):

  • If "remove": strike the optional Podcast 2.0 mention from concept.md.
  • If "implement": specify which Podcast 2.0 elements to emit, then add them in src/sub2pod/feed.py with tests.
Plan of attack (blocked on owner decision): - If "remove": strike the optional Podcast 2.0 mention from `concept.md`. - If "implement": specify which Podcast 2.0 elements to emit, then add them in `src/sub2pod/feed.py` with tests.
agent-pi changed title from Decide: implement or drop the Podcast 2.0 namespace to Implement Podcast 2.0 chapters (external JSON) 2026-08-20 08:32:56 +00:00
Author
Collaborator

Decision made: implement the Podcast 2.0 chapters extension (external JSON), not the whole namespace.

Findings (from the ytdl-sub investigation):

  • ytdl-sub writes a per-episode .info.json (via output_options.info_json_name) whose chapters array (start_time/end_time/title, seconds) is populated from YouTube chapter markers. Confirmed working on the live Links with Friends data.
  • AntennaPod reads the external JSON form (<podcast:chapters url type="application/json+chapters">), not inline <podcast:chapter> — verified in AntennaPod source (PodcastIndex.java only reads the url attribute; PodcastIndexChapterParser.java parses {"chapters":[{"startTime":…,"title":…}]}).

Plan:

  1. New src/sub2pod/chapters.pyChapter dataclass (start_time, title), parse_chapters() (info.json → list), chapters_to_json() (Podcast Index JSON {"version":"1.2.0","chapters":[…]}).
  2. discovery.py — add chapters to Episode, discover the .info.json sidecar.
  3. feed.py — declare xmlns:podcast, emit <podcast:chapters> in the item, write chapters.json in write_feed.
  4. TDD tests across parse/serialise/discovery/feed.
  5. Update concept.md to scope the namespace mention to the chapters extension.
Decision made: implement the Podcast 2.0 **chapters** extension (external JSON), not the whole namespace. Findings (from the ytdl-sub investigation): - ytdl-sub writes a per-episode `.info.json` (via `output_options.info_json_name`) whose `chapters` array (`start_time`/`end_time`/`title`, seconds) is populated from YouTube chapter markers. Confirmed working on the live `Links with Friends` data. - AntennaPod reads the external JSON form (`<podcast:chapters url type="application/json+chapters">`), **not** inline `<podcast:chapter>` — verified in AntennaPod source (`PodcastIndex.java` only reads the `url` attribute; `PodcastIndexChapterParser.java` parses `{"chapters":[{"startTime":…,"title":…}]}`). Plan: 1. New `src/sub2pod/chapters.py` — `Chapter` dataclass (`start_time`, `title`), `parse_chapters()` (info.json → list), `chapters_to_json()` (Podcast Index JSON `{"version":"1.2.0","chapters":[…]}`). 2. `discovery.py` — add `chapters` to `Episode`, discover the `.info.json` sidecar. 3. `feed.py` — declare `xmlns:podcast`, emit `<podcast:chapters>` in the item, write `chapters.json` in `write_feed`. 4. TDD tests across parse/serialise/discovery/feed. 5. Update `concept.md` to scope the namespace mention to the chapters extension.
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
marvin8/sub2pod#31
No description provided.