How to extract podcast chapter markers from a video recording
- Step 1Drop the episode's video file — Drag the recording's MP4, MKV, or MOV onto the tool — the same master your editor placed markers in. One file per run. Free tier handles up to 1 GB; a long 1080p episode may need Pro (10 GB).
- Step 2FFmpeg.wasm reads the chapter atoms — JAD runs
ffmpeg -i <file> -c copy -t 0 -f null -, parsing the container header for chapter markers without decoding the episode. Even a 2-hour recording finishes in seconds. - Step 3Each marker becomes an object — Every chapter line plus its optional title becomes
{ start, end, title }, with times in floating-point seconds and a missing title asnull. - Step 4Review the segment list — Confirm the chapters match your edit. An empty array means the editor never wrote chapter markers into the video — you'd add them at the editing stage, not recover them here.
- Step 5Download the JSON — Saves as
<filename>-chapters.jsonin the standard{ chapters: [...] }shape, ready to transform. - Step 6Map into your Podcasting 2.0 chapters file — Convert each
{ start, title }into a<podcast:chapters>entry ({ startTime, title }). Host the resulting JSON and reference it in your RSS item — apps render the chapter list automatically.
From this tool's JSON to Podcasting 2.0 chapters
The mapping is nearly one-to-one because both use seconds.
| This tool's field | Podcasting 2.0 field | Transform | Notes |
|---|---|---|---|
start (seconds) | startTime (seconds) | Direct copy | Both are numeric seconds — no conversion |
title | title | Direct copy; fall back if null | Replace null with a label before publishing |
end (seconds) | (not used) | Drop | <podcast:chapters> defines segments by next startTime |
| (none) | img, url, toc | Add manually | Optional per-chapter art / links you supply yourself |
Where video-podcast chapters come from
Whether your episode carries markers depends on the tool that exported it.
| Workflow | Carries chapters? | Titles? | What to expect |
|---|---|---|---|
| Edited in Premiere / Resolve with markers exported | If exported as chapters | Yes | Full named segments |
| HandBrake-encoded master with chapter list | Yes | Yes / generic | Chapter 1… or custom |
| Raw Riverside / Zencastr download | Usually no | n/a | Often empty [] — add markers in your editor |
| Screen-share / Zoom recording | No | n/a | Empty [] — no chapter authoring |
Cookbook
Real episode JSON and the Podcasting 2.0 transform. Times are seconds; titles verbatim or null.
An edited episode master with named segments
An editor placed markers at the intro, each topic, and the outro, then exported chapters into the MP4. Clean, fully-named output.
Input: ep142-master.mp4 (chapters from editor markers)
Output: ep142-master-chapters.json
{
"chapters": [
{ "start": 0, "end": 78.0, "title": "Cold open" },
{ "start": 78.0, "end": 1620.0, "title": "Guest interview" },
{ "start": 1620.0, "end": 2040.0, "title": "Sponsor read" },
{ "start": 2040.0, "end": 2310.5, "title": "Outro" }
]
}Transforming to a <podcast:chapters> JSON sidecar
Podcasting 2.0 wants { version, chapters: [{ startTime, title }] }. Map start → startTime and copy the title; drop end.
const { chapters } = require('./ep142-master-chapters.json');
const pc = {
version: '1.2.0',
chapters: chapters.map(c => ({ startTime: c.start, title: c.title ?? 'Segment' }))
};
console.log(JSON.stringify(pc, null, 2));
// → host this file, reference it in <podcast:chapters url="..."/>A raw Riverside download with no chapters
Recording platforms export the media but rarely write chapter markers — those come from your editor. The empty array tells you to add markers at the edit stage.
Input: riverside-raw.mp4 (no embedded chapters)
Output: riverside-raw-chapters.json
{
"chapters": []
}
→ Place markers in your NLE and re-export, then extract.Generic chapter names you want to relabel
A HandBrake encode wrote Chapter 1, Chapter 2… Replace them with real segment names by index before publishing.
const { chapters } = require('./ep143-chapters.json');
const names = ['Intro', 'Main topic', 'Listener mail', 'Outro'];
const out = chapters.map((c, i) => ({ startTime: c.start, title: names[i] ?? c.title }));
console.log(JSON.stringify({ version: '1.2.0', chapters: out }, null, 2));Sanity-checking segment lengths
Spot a sponsor read that ran long or an interview segment to split, using seconds arithmetic.
const { chapters } = require('./ep142-master-chapters.json');
for (const c of chapters) {
const mins = ((c.end - c.start) / 60).toFixed(1);
console.log((c.title ?? 'segment'), '→', mins, 'min');
}
// → Guest interview → 25.7 minEdge cases and what actually happens
Episode video has no chapter markers
Empty arrayReturns { "chapters": [] }. Recording platforms (Riverside, Zencastr, Zoom) export media without chapters — those are authored in your editor. Add markers in the NLE and re-export, then extract.
Markers present, titles null
title: nullA chapterized encode without names yields null titles. The timestamps are exact; relabel by index (see the cookbook) before publishing to your feed.
You extracted from the video but publish audio-only
By designThe seconds are container-agnostic — a chapter at 1620.0s in the video is 1620.0s in the matching audio cut, as long as both share the same edit. Extract from whichever master carries the markers.
Audio-only MP3 dropped
Different inputThis tool is for video containers. MP3 chapter frames (ID3 CHAP) are an audio-tool concern, not handled here. Extract from the video master, or use a dedicated audio chapter tool for MP3s.
Title contains an em dash or emoji
PreservedTitles read as UTF-8 and write verbatim, so Q&A — listener mail 🎙️ survives into the JSON. Use a UTF-8-aware editor and a UTF-8 feed to keep it intact downstream.
Two-hour episode video
SupportedHeader-only parsing (-c copy -t 0) means duration barely affects speed — chapters come back in seconds. Free tier caps at 1 GB; a long 1080p episode may exceed that, so use Pro (10 GB) or higher.
End time missing on the last chapter
ExpectedSome encoders omit the final end; FFmpeg fills it from the file duration where it can. If end looks like the total runtime, that's the file end, which is normal for the last segment.
You want per-chapter cover art in the feed
Not in JSONPodcasting 2.0 supports img per chapter, but that art isn't stored in the video's chapter atom and isn't in this JSON. Add the img URLs yourself when you build the <podcast:chapters> sidecar.
You want this tool to write the chapters file
Read-onlyIt reads markers to JSON; it doesn't generate or host the <podcast:chapters> sidecar. Use the JSON as the input to your own publishing script (cookbook shows the map).
Frequently asked questions
How do I get from this JSON to a <podcast:chapters> file?
Map each chapter to { startTime, title } (copy start → startTime, copy the title, drop end), wrap in { version, chapters }, host the file, and reference it with <podcast:chapters url="..." type="application/json+chapters"/>. The cookbook shows the exact transform.
Why are the timestamps in seconds?
Because Podcasting 2.0 chapters use startTime in seconds too — so the mapping is a direct copy. Seconds are also easier to relabel and to compute segment lengths from.
My Riverside download has no chapters — why?
Recording platforms export the media, not chapter markers. Markers are authored in your editor. Place them in your NLE, export chapters into the master, then extract here. The empty array is the expected result for a raw download.
Can I extract chapters from an MP3 podcast file?
No — this tool reads video containers (MP4/MKV/MOV/WebM). MP3 ID3 CHAP frames are an audio-format concern. Extract from the video master that carries the markers instead.
Will my unreleased episode be uploaded?
No. FFmpeg.wasm runs in your browser and only the container header is read. The video bytes never leave your machine — relevant for embargoed guests, sponsor reads, or pre-release cuts.
What's the output filename and shape?
<filename>-chapters.json, containing { "chapters": [{ "start", "end", "title" }] }. Start/end are floating-point seconds and titles are verbatim or null.
Does it re-encode my audio or video?
No. It uses -c copy -t 0, reading metadata only — your master is untouched and nothing is decoded or transcoded.
How do I handle null or generic chapter titles?
Replace them by index with your real segment names before publishing — title: c.title ?? names[i]. Generic Chapter N names from a HandBrake encode are handled the same way.
Can I add per-chapter images for podcast apps?
Not from this tool — chapter art (img) isn't stored in the video's chapter atom. Add the image URLs yourself when assembling the <podcast:chapters> JSON sidecar.
How long an episode can I process?
There's no duration cap — only a file-size cap per tier: Free 1 GB, Pro 10 GB, Pro-media 100 GB, Developer 100 GB. Header-only reading keeps even a multi-hour episode fast.
Can it split the episode into per-segment audio files?
No — it only reads markers. To cut the media into segments use the video-splitter, or extract the audio first with the audio-track-extractor and split that.
Why does my last chapter's end equal the total runtime?
When an encoder omits the final chapter's end, FFmpeg fills it from the file duration. So the last segment's end legitimately equals the episode length — that's expected, not an error.
Privacy first
Every JAD Video tool runs entirely in your browser via WebCodecs and FFmpeg (WebAssembly). Your video files never leave your device — verified by zero outbound network requests during processing.