How to extract chapter markers from a video as json
- Step 1Drop your video file — Drag an MP4, MKV, MOV, M4V, or WebM onto the tool. Single file at a time — chapter extraction is a per-file metadata read, not a batch job. Free tier accepts up to 1 GB; the bytes stay in the browser tab.
- Step 2Let FFmpeg.wasm parse the header — JAD runs
ffmpeg -i <file> -c copy -t 0 -f null -, which reads the container metadata and prints the chapter atoms without decoding a single frame. On a long film this finishes in seconds because no transcode happens. - Step 3JAD scrapes the chapter lines into objects — Each
Chapter #0:N: start S, end Eline plus its optionaltitle :line becomes one object{ start, end, title }. Times are parsed to floating-point seconds; a missing title becomesnull. - Step 4Review the detected chapters — The result panel shows the parsed list. If the array is empty, the file simply has no embedded chapters — there is nothing to recover (re-encoding won't create them). Use the scene-detector to generate cut timestamps instead.
- Step 5Download the JSON — The output saves as
<filename>-chapters.jsonwith the exact{ chapters: [...] }structure, pretty-printed with two-space indentation so it is diff- and review-friendly. - Step 6Pipe it into your code or RSS / description builder — Iterate the array to emit YouTube
MM:SS Titlelines, podcast<podcast:chapters>JSON, or an editor's marker import. Because times are seconds, formatting to any timecode is a one-liner in your own script.
The output JSON schema
The tool always emits this exact shape. There are no format options — JSON is the only output.
| Field | Type | Meaning | When it can surprise you |
|---|---|---|---|
chapters | array | Top-level list of chapter objects, in file order | Empty [] when the file has no embedded chapters — not an error |
chapters[].start | number (seconds) | Chapter start time as floating-point seconds, e.g. 90.5 | Not HH:MM:SS — you format it yourself if you need timecode |
chapters[].end | number (seconds) | Chapter end time as floating-point seconds | Usually equals the next chapter's start; the last chapter's end is the file duration |
chapters[].title | string or null | Embedded chapter title, UTF-8, verbatim | null (not "") when the marker carries no title — common in DVD/Blu-ray rips |
Container support for chapter metadata
Whether a chapter array comes back depends on what the container can store and what the encoder wrote.
| Container | Chapter mechanism | Titles? | Typical source |
|---|---|---|---|
| MKV / WebM | Matroska Chapters element (ChapterAtom) | Yes — multi-language possible; JAD reads the default | Rips, yt-dlp --embed-chapters, mkvtoolnix |
| MP4 / M4V | QuickTime chapter atom / Nero chapter track | Yes | HandBrake, MP4Box, iTunes, podcast exports |
| MOV | QuickTime text-track chapters | Yes | Final Cut Pro, ScreenFlow exports |
| AVI / MPG / TS | No standard chapter store | n/a | Returns empty [] — the format can't hold chapters |
Cookbook
Real JSON outputs from real files. Times are seconds (floats); titles are verbatim or null.
A HandBrake MP4 with titled chapters
HandBrake writes a chapter atom with auto-numbered titles (or custom ones if you renamed them on the Chapters tab). This is the clean, fully-populated case.
Input: lecture-final.mp4 (HandBrake, 3 chapters)
Output: lecture-final-chapters.json
{
"chapters": [
{ "start": 0, "end": 612.3, "title": "Intro" },
{ "start": 612.3, "end": 1840.0, "title": "Core concepts" },
{ "start": 1840.0,"end": 2705.5, "title": "Q&A" }
]
}A rip with chapters but no titles
DVD/Blu-ray rips and some yt-dlp pulls carry chapter boundaries with no names. The boundaries are real; the titles are null. You add labels downstream.
Input: feature.mkv (chapter atoms, no titles)
Output: feature-chapters.json
{
"chapters": [
{ "start": 0, "end": 845.2, "title": null },
{ "start": 845.2, "end": 1690.4, "title": null }
]
}No chapters at all → empty array
A plain camera or screen-recorder MP4 has no chapter metadata. The tool returns an empty array — this is the documented 'nothing to extract' result, not a failure.
Input: zoom-recording.mp4 (no embedded chapters)
Output: zoom-recording-chapters.json
{
"chapters": []
}
→ To create markers from visual cuts instead, use scene-detector.Converting the JSON to YouTube description timestamps
Iterate the array, format seconds to MM:SS (first marker must read 00:00), and join with titles. The tool gives you seconds; the formatting is your script.
// node, using <filename>-chapters.json
const { chapters } = require('./lecture-final-chapters.json');
const mmss = s => `${String(Math.floor(s/60)).padStart(2,'0')}:${String(Math.floor(s%60)).padStart(2,'0')}`;
const lines = chapters.map(c => `${mmss(c.start)} ${c.title ?? 'Chapter'}`);
console.log(lines.join('\n'));
// →
00:00 Intro
10:12 Core concepts
30:40 Q&AComputing per-chapter durations
Because start/end are numeric seconds, duration is plain subtraction — no timecode parsing. Handy for spotting an over-long segment to split.
const { chapters } = require('./feature-chapters.json');
for (const c of chapters) {
console.log((c.title ?? '(untitled)'), '→', (c.end - c.start).toFixed(1), 'sec');
}
// →
(untitled) → 845.2 sec
(untitled) → 845.2 secEdge cases and what actually happens
File has no embedded chapters
Empty arrayThe tool returns { "chapters": [] }. This is by design — chapters are metadata that an encoder either wrote or didn't. Re-encoding won't conjure them. To generate markers from visual content, use the scene-detector; to place a grid of frames for a quick overview, use the frame-grid-maker.
Chapter marker has a start but no title
title: nullCommon in rips and raw camera exports. The object's title is set to null (not an empty string) so your code can branch on c.title ?? fallback cleanly. The start/end boundaries are still accurate.
AVI / MPEG-PS / TS input
Empty arrayThese containers have no standard place to store chapters, so FFmpeg prints none and the array comes back empty. There is nothing to recover — the data was never there. Convert with a real container only if the source had chapters to begin with.
MKV with multi-language chapter titles
Default readMatroska can store the same chapter list in several languages. FFmpeg's metadata read surfaces the default/first title, so you get one title per chapter, not an array of translations. If you need a specific language track, that selection is not exposed by this tool.
Title contains non-ASCII characters
PreservedTitles are read as UTF-8 and written verbatim into the JSON. Accented and CJK titles round-trip correctly. Open the downloaded .json in a UTF-8-aware editor; legacy Windows-1252 viewers may show mojibake even though the bytes are correct.
Very long file (multi-GB)
SupportedBecause extraction uses -c copy -t 0 -f null -, FFmpeg.wasm only parses the header and demuxes nothing. A 4-hour film returns its chapter list in seconds. Free tier caps the file at 1 GB; Pro 10 GB; Pro-media / Developer 100 GB.
Corrupt or truncated container
May failIf the header is damaged enough that FFmpeg can't open the file, no chapters print and you may see a parse error. Re-download or remux the source. A merely truncated tail (incomplete recording) usually still yields the chapters written in the header.
You wanted the video split at chapter boundaries
Different toolThis tool reads markers to JSON only — it does not cut the video. To slice a file into pieces, use the video-splitter; for a single precise lossless cut at a keyframe, use the lossless-trimmer.
You wanted to add or write chapters
Read-onlyChapter extraction is read/export only — it never modifies the source. There is no in-tool chapter editor or writer. For YouTube, paste MM:SS Title lines into the description; the platform builds the markers from that text.
Frequently asked questions
What exactly does the output JSON look like?
Always { "chapters": [ { "start": <seconds>, "end": <seconds>, "title": <string or null> } ] }, pretty-printed with two-space indentation. start and end are floating-point seconds, and title is the embedded string or null if the marker has none. The file downloads as <your-filename>-chapters.json.
Why are the times in seconds and not HH:MM:SS?
Seconds are the lossless, machine-friendly form — you can subtract them for durations and format them however you like. The tool deliberately hands you raw seconds so your script controls the timecode style (YouTube MM:SS, SRT HH:MM:SS,mmm, etc.).
Can I export to plain text or FFmpeg metadata format instead?
No — JSON is the only output format, and there are no options to change it. If you need a different shape, transform the JSON in a few lines of code (the cookbook shows a YouTube-description example). The single-format design keeps the output predictable for automation.
Does it split the video into one file per chapter?
No. This tool only reads markers and writes JSON; it never cuts or re-encodes. To split a video use the video-splitter, or the lossless-trimmer for a single stream-copy cut.
What happens if my file has no chapters?
You get { "chapters": [] }. Chapters are metadata an encoder either wrote or didn't, so there is nothing to recover by re-processing. Generate markers from content instead with the scene-detector.
Which containers carry chapter data?
MKV and WebM (Matroska chapter elements), MP4 / M4V (QuickTime / Nero chapter atoms), and MOV (QuickTime text-track chapters). AVI, MPEG-PS, and TS have no standard chapter store and return an empty array.
Are the video bytes uploaded anywhere?
No. Extraction runs in your browser via FFmpeg.wasm. Only the container header is parsed (-c copy -t 0), and the bytes never leave the tab. The sole server interaction is an anonymous processed-counter for signed-in dashboard stats.
Why is it so fast on a huge file?
Because it demuxes nothing. The command -c copy -t 0 -f null - tells FFmpeg to read metadata and stop without decoding frames, so a multi-GB film returns its chapters in seconds rather than minutes.
Will the title's accented or CJK characters survive?
Yes. Titles are read as UTF-8 and written verbatim into the JSON. Open the file in a UTF-8-aware editor; you only see mojibake if your viewer assumes a legacy code page.
Can I extract chapters from several files at once?
Not in one drop — chapter extraction is single-file. Run each file in turn. Your tier's batch count applies to tools that support batching; this one reads one container at a time.
Does the empty-title case use null or an empty string?
null. That lets you write c.title ?? 'Chapter ' + i without confusing a real empty title with a missing one. Rips frequently produce null titles with valid start/end boundaries.
How big a file can I process per tier?
Free: 1 GB. Pro: 10 GB. Pro-media: 100 GB. Developer: 100 GB. Limits are file-size based (streaming), not duration — there is no minutes cap. Since only the header is read, even the largest allowed file finishes quickly.
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.