Skip to content

Export: split at timeline markers into named chapters from one render (#527) - #764

Merged
CaYatur merged 2 commits into
mainfrom
feat/export-chapters
Oct 16, 2026
Merged

CaYatur merged 2 commits into
mainfrom
feat/export-chapters

Conversation

@CaYatur

@CaYatur CaYatur commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Closes #527.

What changed

  • One render, cut afterwards. Markers inside the export become forced keyframes, and ffmpeg's segment muxer cuts the single render at them. Rendering each chapter on its own would start MilkDrop and other feedback layers cold at every boundary.
  • Files and names.
    • The chapters go into a folder named after the chosen file, one file per chapter, named from its marker: 01 - Intro.mp4, 02 - Drop.mp4.
    • A marker at 0 names the first chapter; without one, it is "Start" / "Başlangıç".
    • Markers that fall on the same frame are counted once.
    • Names are sanitized like any other file name.
  • No half-written output. The folder is written as .partial and renamed only when the export succeeds, as with frame sequences. If the file count does not match the plan, the job fails rather than silently missing a chapter.
  • Where the option appears. The panel has a "Split into chapters at the markers" toggle, shown only for video formats. MCP gets a chapters boolean.

Two traps, both measured

  • The segment muxer drops a cut when given the exact time. If a packet's time rounded a hair below the cut, that cut was skipped: x264 gave 2 files instead of 3. Split times are now half a frame early, and the keyframe stays at the exact frame.
  • AMD AMF ignores forced keyframes. It produced one file, and none of its options enforces an IDR at a given time. Chapters therefore pick the next encoder that honours forced keyframes: NVENC with -forced-idr 1, Quick Sync, or the CPU.

Measured

In the app, 5 s of audio, markers at 2.0 s ("Nakarat") and 3.5 s ("Son: Bitiş"), 30 fps:

encoder frames first frame a keyframe
x264 60 / 45 / 45 yes
NVENC 60 / 45 / 45 yes
x265 60 / 45 / 45 yes
VP9 60 / 45 / 45 yes
ProRes 60 / 45 / 45 yes
  • Files are named 01 - Başlangıç, 02 - Nakarat and 03 - Son Bitiş.
  • Audio is cut on AAC frame boundaries, so each file's audio is within a few tens of milliseconds of its video (2.043 / 1.486 / 1.494 s against 2.0 / 1.5 / 1.5 s).
  • A job with no markers is refused with a reason. GIF ignores the option.

Also fixed: the bench driver's first job

The panel sends its config while it loads, and that overwrote the first bench job's settings. That is where the odd first-job hash noted in #760 came from, and it is why chapters first saw no markers. The driver now holds panel updates during the bench, the same way the self-test does.

Verification

  • npm test: 3145/3145. tests/export-chapters.test.js covers the plan, names, arguments, the AMF exclusion, main-process wiring, the bench hold, and the panel and MCP. The existing smoke-hold and export-job tests were updated for the second hold caller and the extra error path.

CaYatur and others added 2 commits October 16, 2026 03:50
…ender (#527)

Forced keyframes at the markers and the segment muxer cut one render, so
feedback layers stay continuous across chapters. Segment split times sit
half a frame early; at the exact time x264 skipped a cut. AMF ignores
forced keyframes and is not picked for chapters. The bench driver now
holds panel config updates, which overwrote the first job's settings.

Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com>
Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com>
@cayadraft

cayadraft Bot commented Oct 16, 2026

Copy link
Copy Markdown

@CaYatur

CaYaDraft — pull request

Summary
Export: split at timeline markers into named chapters from one render (#527)

Files
CHANGELOG.md (modified, +6/-0)
@@ -42,6 +42,12 @@ GitHub. The full reasoning and the measurements behind each change are in [ROADM
CPU choice and the speed balance stay yours. The profile is read from the settings themselves, so
changing any of them by hand shows "Custom", and every setting stays available underneath. MCP's
sv_start_export takes a profile too, with explicit arguments taking precedence.
+- **Split…

README.md (modified, +5/-3)
@@ -17,7 +17,7 @@ layers and 40 GPU effects, an OBS overlay, Spout / Syphon, **project
Downloads
License: MIT
[![Platform](https://img.shields.io/badge/platform-Windows%20%…

README.tr.md (modified, +6/-3)
@@ -18,7 +18,7 @@ destesi**, ve MCP üzerinden yapay zekâ kontrolü.
İndirme
Lisans: MIT
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.sv…

ROADMAP.md (modified, +20/-2)
@@ -93,7 +93,7 @@ hands, and a row only moves up once it ships.
|---|:--:|---|
| Per-app audio capture on macOS and Linux | v3.1.6 | On main (#514, #517). Linux verified in WSLg with PulseAudio; macOS written on ScreenCaptureKit and compiled in CI, never run on a Mac. Windows shipped in v3.1.3 |
| Live level meter per application | v3.1.6 | Done on main (#516): a moving dB-scale bar per ap…

src/admin/admin.js (modified, +7/-0)
@@ -3425,6 +3425,12 @@
min: 0, max: 300, step: 1, fmt: (v) => (Math.round(v) > 0 ? String(Math.round(v)) : 'Zaman çizelgesi'),
show: () => exportDuration().mode === 'bars',
},

  •      {
    
  •        /* Bölümler (#527): tek render, zaman çizelgesi işaretçilerinde
    
  •           kesilip işaretçi adlarıyla bir klasöre. Yalnız video biçimleri. */
    

+…

src/main/main.js (modified, +64/-1)
@@ -4290,6 +4290,27 @@ function removePartialDir(dir, proc) {
if (proc && proc.exitCode == null && proc.signalCode == null) proc.once('close', () => setTimeout(drop, 50));
else setTimeout(drop, 50);
}
+/* Segment yazıcısının 001.mp4, 002.mp4… dosyalarına işaretçi adı verir

Note
The local model is unavailable, so this is only the title and file list.


Stopping here. I will not merge, push, or change this pull request until you ask.

/caya explain describes the diff. /caya plan previews the next edit. /caya revise adds a commit.

@CaYatur
CaYatur merged commit c289cdb into main Oct 16, 2026
5 checks passed
@CaYatur
CaYatur deleted the feat/export-chapters branch October 16, 2026 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export: split at markers into chapters

1 participant