Skip to content

Instantly share code, notes, and snippets.

@jumoog
Created January 3, 2026 12:00
Show Gist options
  • Select an option

  • Save jumoog/6f2c3796b333c73752e3230156c0cc4f to your computer and use it in GitHub Desktop.

Select an option

Save jumoog/6f2c3796b333c73752e3230156c0cc4f to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MKV Chapter Extractor (XML)</title>
<style>
body {
font-family: sans-serif;
max-width: 700px;
margin: 40px auto;
}
button {
padding: 8px 14px;
font-size: 14px;
}
#log {
white-space: pre-wrap;
background: #f4f4f4;
padding: 10px;
margin-top: 15px;
font-size: 13px;
height: 200px;
overflow: auto;
}
</style>
</head>
<body>
<h2>Extract MKV Chapters as XML</h2>
<input type="file" id="fileInput" accept=".mkv">
<br><br>
<button id="extractBtn" disabled>Extract Chapters</button>
<div id="log"></div>
<script type="module">
import { FFmpeg } from "https://unpkg.com/@ffmpeg/ffmpeg@0.12.10/dist/ffmpeg.min.js";
import { fetchFile } from "https://unpkg.com/@ffmpeg/util@0.12.1/dist/util.min.js";
const logEl = document.getElementById("log");
const fileInput = document.getElementById("fileInput");
const extractBtn = document.getElementById("extractBtn");
const ffmpeg = new FFmpeg();
function log(msg) {
logEl.textContent += msg + "\n";
logEl.scrollTop = logEl.scrollHeight;
}
fileInput.addEventListener("change", () => {
extractBtn.disabled = !fileInput.files.length;
});
extractBtn.addEventListener("click", async () => {
const file = fileInput.files[0];
if (!file) return;
extractBtn.disabled = true;
log("Loading FFmpeg...");
if (!ffmpeg.loaded) {
await ffmpeg.load({
coreURL: "https://unpkg.com/@ffmpeg/core@0.12.6/dist/ffmpeg-core.js",
wasmURL: "https://unpkg.com/@ffmpeg/core@0.12.6/dist/ffmpeg-core.wasm"
});
}
log("Writing MKV to virtual filesystem...");
await ffmpeg.writeFile("input.mkv", await fetchFile(file));
log("Extracting chapters...");
await ffmpeg.exec([
"-i", "input.mkv",
"-map_chapters", "0",
"-f", "ffmetadata",
"chapters.xml"
]);
log("Reading XML output...");
const data = await ffmpeg.readFile("chapters.xml");
const blob = new Blob([data.buffer], { type: "application/xml" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = file.name.replace(/\.mkv$/i, "") + "_chapters.xml";
a.click();
URL.revokeObjectURL(url);
log("Done ✔");
extractBtn.disabled = false;
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment