680 Commits
Author SHA1 Message Date
Miguel Ángel de6ec3811e fix: cache paths follow HYPERFRAMES_CACHE_DIR (#6716)
* fix: cache paths follow HYPERFRAMES_CACHE_DIR

Every place HyperFrames keeps downloaded or built files now takes its
folder from one helper, hyperframesCacheDir() in @hyperframes/core. The
default stays ~/.cache/hyperframes; HYPERFRAMES_CACHE_DIR moves it.

* fix: register the cache-dir subpath for bun and node, and build inspector from core source

* test: the cache-dir override test uses a folder that is absolute on every platform

* fix: the opencv cache follows HYPERFRAMES_CACHE_DIR

* fix(cli): npm installs run in their staging dir, so no cache path reaches a command line

* fix(cli): whisper.cpp is cloned inside its build dir, so no cache path is a git argument

* test(cli): the whisper clone test finds no system whisper on Windows either
2026-11-01 22:44:11 +00:00
Miguel Ángel d1d631f60a fix(producer): an export over the ceiling limits its peaks and keeps the mix's level (#6940) 2026-11-01 17:18:48 +00:00
Miguel Ángel 90f7fe624f fix(engine): GPU renders use the NVENC preset names the host's ffmpeg lists (#6930)
* fix(engine): GPU renders use the NVENC preset names the host's ffmpeg lists

* fix(engine): one ffmpeg stdout reader for encoder detection and preset listing

* ci(engine): raise the engine unpacked size budget for the NVENC preset reader
2026-11-01 15:46:57 +00:00
Miguel Ángel 9aaa3ae675 fix(engine): a video with no source no longer holds capture for 45 seconds (#6926) 2026-11-01 11:16:43 +00:00
Miguel Ángel 58428b44bc fix(engine): passthrough keeps frame count and accepts films whose audio outlasts the picture (#6920)
* fix(engine): passthrough output follows the page frames and holds a short video's last frame

* test(engine): a film whose audio outlasts its video still qualifies for passthrough

* test(engine): passthrough encodes one output frame per page frame when the video is shorter

* test(engine): end the ffprobe options before the file in the passthrough frame count test
2026-11-01 10:42:32 +00:00
Miguel Ángel 98520366a6 feat(producer): opt-in video layer passthrough for a lone full-canvas video (#6914)
* feat(engine): composite a source video inside the streaming encoder

* feat(engine): opt-in flag and rule for compositing a lone full-timeline video

* feat(producer): opt-in video layer passthrough for a lone full-canvas video

* feat(producer): launch the probe, drop its page and capture PNG when passthrough is on

* refactor(engine): trim the passthrough doc comments to one line

* fix(producer): refuse passthrough when a wrapper or sub-composition paints a background

* chore(producer): mark the in-page layout check as one deliberate unit

* fix(engine): a video left out of capture no longer blocks static-frame dedup

* refactor(engine): shorten two long comments in the static-frame eligibility scan
2026-11-01 05:17:00 +00:00
Miguel Ángel 94849bf1e0 refactor(engine): one builder for the video frame filter chain (#6908)
* refactor(engine): one builder for the video frame filter chain

* refactor(engine): keep the filter chain builder module-private until it has a caller
2026-11-01 00:54:54 +00:00
Miguel Ángel f486ad6d49 fix(engine): ffprobe read intervals never carry an exponent (#6909) 2026-11-01 00:53:35 +00:00
Miguel Ángel ef0007b003 test(engine): run the non-PNG probe test on a fake clock (#6907) 2026-11-01 00:16:48 +00:00
Miguel Ángel 32de0196d5 fix(producer): a failed video extraction names which clip files failed (#6905)
* fix(producer): a failed video extraction names which clip files failed

* fix(producer): the extraction source label hides scheme payloads and drive paths

* style(producer): format the extraction source label
2026-11-01 00:09:14 +00:00
Miguel Ángel f3cdf2b473 fix(engine): scale static-frame verification budget with the job (#6901)
* fix(engine): scale static-frame verification budget with the job

* fix(engine): keep comment share flat

* fix(engine): drop added comment

* fix(engine): time seek in verify budget, state the policy
2026-10-31 22:48:26 +00:00
Miguel Ángel bb3c3b2118 fix(producer): render telemetry reports beyond-viewport capture as the workers used it (#6702)
* fix(producer): render telemetry reports beyond-viewport capture as the workers used it

* fix(producer): apply the sessions' beyond-viewport value at the summary, on every capture path

* chore(engine): drop a comment so the types file's comment share does not rise
2026-10-31 21:42:26 +00:00
Miguel Ángel 47fe68562f fix(engine): nested video clips on a frame boundary export every source frame (#6893)
* fix(engine): nested video clips on a frame boundary export every source frame

* fix(engine): drop comment from ffmpeg time formatter
2026-10-31 20:08:23 +00:00
Miguel Ángel 60f711686c fix(producer): auto renders with color-graded media stay SDR instead of promoting to HDR (#6671)
* fix(producer): auto renders with color-graded media stay SDR instead of promoting to HDR

* fix(producer): keep auto renders SDR when a graded canvas is present
2026-10-31 16:16:06 +00:00
Jerrai 1116077dab fix(cli): capture treats only 192.0.0.0/24 and 192.0.2.0/24 as reserved, via one shared list (#6853)
* fix(cli): capture's public-address check uses the engine's list; 192.0/16 is not all reserved

Only 192.0.0.0/24 and 192.0.2.0/24 are special-use. The capture proxy blocked all of
192.0.0.0/16, so sites on WordPress VIP (192.0.64.0/18, e.g. nasa.gov at 192.0.66.108)
failed every capture with net::ERR_TUNNEL_CONNECTION_FAILED. isPublicIPv4 now reads the
engine's isBlockedNetworkHost, so capture and asset downloads share one list.

— Jerrai (cutroom-debug)

* refactor(engine): one special-use network list, in a light subpath capture imports

Importing all of @hyperframes/engine into capture's publicNetwork slowed the lookup child's
start-up past publicNetwork.process.test's 6 s mid-lookup exit. The list and
isBlockedNetworkHost move to engine/src/utils/networkPolicy.ts, exported as
@hyperframes/engine/network-policy; urlDownloader and the engine index read it there.

— Jerrai (cutroom-debug)

* test(engine): pin 192.0.0.0/24 and 192.0.2.0/24 blocked, the rest of 192.0/16 public

— Jerrai (cutroom-debug)

* fix(cli): alias the engine's network-policy subpath in the CLI bundle

esbuild's root alias rewrote @hyperframes/engine/network-policy to src/index.ts/network-policy;
the exact-subpath list now carries it. The publicNetwork docstring is cut to keep the file's
comment share under its base.

— Jerrai (cutroom-debug)
2026-10-30 16:21:34 +00:00
Miguel Ángel 4da50e2218 feat(telemetry): render_complete splits capture time into seek, page, screenshot and encoder wait (#6839) 2026-10-30 14:55:06 +00:00
Miguel Ángel 536209da07 fix(producer): report extract_video progress while ffmpeg extracts frames (#6822)
Frames ffmpeg has written now reach the render's stage progress, so a long source video no longer leaves the render silent between extract_video and capture.
2026-10-30 11:23:59 +00:00
Miguel Ángel 279d40dcd7 test(engine): the rotated-clip fixture builds on ffmpeg older than 6 (#6811) 2026-10-30 09:27:00 +00:00
Miguel Ángel 10a4ed56cb fix(engine): a video clip starting between frames shows its media on the right frame (#6759)
* fix(engine): a video clip starting between frames shows its media on the right frame

* chore(engine): trim the helper comment to one line

* chore(engine): drop the helper comment

* test(engine): extract once per start in the off-grid clip test

* fix(engine): a looped clip keeps its loop origin when the start is off the frame grid

* test(engine): three off-grid starts cover the failure modes and keep the flake gate under its limit
2026-10-30 09:01:21 +00:00
Miguel Ángel 47a1a92064 fix(cli): a missing registry item names the closest ones, and a blocked browser says how to allow it (#6799)
* fix(cli): a missing registry item names the closest ones, and a blocked browser says how to allow it

* fix(cli): let closestNames carry its rule in its name
2026-10-30 06:15:03 +00:00
Miguel Ángel 54c98b5906 fix(engine): a video on a non-square pixel grid renders at its display shape (#6772)
* fix(engine): a video on a non-square pixel grid renders at its display shape

* chore(engine): drop comments the names already carry

* fix(engine): a rotated clip with a non-square pixel grid keeps its display shape

* test(engine): the frame probes end their options with -- like every ffprobe call
2026-10-30 03:00:25 +00:00
Miguel Ángel 8fb08f2a7c fix(engine): a render no longer waits for a sub-composition timeline no script can register (#6764)
* fix(engine): a render no longer waits for a sub-composition timeline no script can register

* fix(engine): the timeline wait matches a twice-mounted id and a template-literal key
2026-10-30 02:56:22 +00:00
Miguel Ángel a3ff1c4902 revert(engine): late-starting videos play to their last frame (#6746)
Reverts #6736. Its end-of-clip read fails intermittently on Windows, and the fallback
then expects frames past the clip's end.
2026-10-29 14:14:21 +00:00
Miguel Ángel 22b53c0115 fix(engine): late-starting videos play to their last frame (#6736)
* fix(engine): late-starting videos play to their last frame

A video whose first frame is after timestamp 0 could end early (frozen
tail) or late: ffprobe's stream duration does not run from the first
frame in every container (fragmented MP4, Matroska). Such a video's end
is now read from its last packet, once, where its facts are probed.

* test(engine): the WebM-with-audio case measures its first frame from the file

* test(engine): the first-frame probe ends its arguments with the input file
2026-10-29 12:05:27 +00:00
Miguel Ángel e1a8c7f173 fix(engine): long films show progress during the effect check and never wait minutes on it (#6697)
* fix(engine): long films start capturing at once instead of after a full effect scan

* fix(engine): give the effect scan two minutes, under the app's stall limit

* fix(engine): show effect scan progress so a long scan reads as preparing, not stalled
2026-10-29 10:35:12 +00:00
Miguel Ángel be05c86932 perf(engine): encode high quality with the x264 fast preset (#6640)
* perf(engine): encode high quality with the x264 fast preset

* test(engine): name the NVENC p5 case by its preset, not the high tier
2026-10-29 06:40:21 +00:00
WaterrrForever cd003e2010 perf(engine): size render workers from measured heap and the container's CPU quota (#6641)
* perf(cli): give render the heap its auto workers need in small containers

Auto worker sizing caps capture workers at (V8 heap - 1024 MB) / 640 MB.
Node sizes its default heap from visible memory, so in a 10 GB container
the heap is 2,096 MB and every render captures with one worker, whatever
the core count.

When no --workers is given, no --docker, no heap was set by hand, and the
heap is the only bound holding workers back, render relaunches itself once
with --max-old-space-size set to what CPU and memory allow (capped at half
of memory). The engine exposes heapMbForAutoWorkers() for this.

* fix(cli): count a heap-relaunched render once, keep detached renders alive

The relaunched child skips the CLI's command telemetry, update and skills
notices, and app-history notice; the parent already records and prints
them. A detached render no longer gets SIGHUP forwarded, which its
default handler would treat as a kill. A heap set as --max_old_space_size
counts as set by hand.

* fix(engine): keep the desktop app's per-worker heap figure at 640 MB

Inside Electron the heap is a hard 4,192 MB cage, and that is where the
field OOM behind the old figure happened (#5149: 5 auto workers on a heavy
export). The new 320 MB figure is measured only in a system Node, so the
desktop app keeps 640 MB (4 workers there) until a heavy desktop export is
measured. Containers and plain Node keep 320.

* fix(engine): know the packaged desktop app by its client tag, which runs the CLI on plain Node
2026-10-29 06:17:07 +00:00
Miguel Ángel a5496b1887 fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot (#6701)
* fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot

* test(cli): cover the Chrome launch failure messages a failed render prints
2026-10-29 00:49:04 +00:00
Miguel Ángel 9830059bdb fix(engine): downloads refuse hosts that resolve to private addresses (#6571)
* fix(engine): downloads refuse hosts that resolve to private addresses

* fix(core): shorten the env proxy doc comment

* test(producer): remote-media tests stub the download fetch seam
2026-10-28 20:08:50 +00:00
Miguel Ángel 3ca1005a81 feat(producer): report which element set the film length (#6689)
* feat(producer): report which element set the film length

* fix(core): keep the duration guard and comment share in step with the length helper
2026-10-28 19:05:59 +00:00
Miguel Ángel cdc86117c7 fix(engine): a render from an app that bundles the engine stamps the engine's version (#6686)
* fix(engine): a render from an app that bundles the engine stamps the engine's version

* test(engine): the bundled-layout fixture names a generic app
2026-10-28 17:51:43 +00:00
Vance IngallsandClaude Opus 5.5 c569760a7a fix(core): grade groups resolve per composition; freeze frames keep their lane (#6685)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:51:17 -07:00
Vance IngallsandClaude Opus 5.5 09b1290474 feat(core): grade groups grade a whole lane from one hf-color-group (#6678)
An inert <hf-color-group> holds a lane's data-color-grading; <video>, <img>
and <canvas> join with data-color-group. A member's own grading overrides
its group's. The runtime discovers members, regrades them when the group's
grading or a member's data-color-group changes, and ungrades them when the
group element or its grading goes, drawing only members on screen.

Render keeps group-graded video at full size like directly graded video,
and lint accepts (and still validates) grading on hf-color-group.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:10:42 -07:00
Miguel Ángel 4db1214e7d test(engine): the multi-chunk encode test gets the 30 s the other ffmpeg tests have (#6663)
* test(engine): the multi-chunk encode test gets the 30 s the other ffmpeg tests have

* test(engine): the mixed-path concat test gets the same 30 s
2026-10-28 13:13:18 +00:00
Miguel Ángel 437784038b fix(engine): late-starting MP4 and WebM videos render every frame the preview shows (#6664)
* fix(engine): late-starting MP4 and WebM videos render every frame the preview shows

The browser counts an MP4 or WebM from timestamp 0, but FFmpeg seeks from the file's
earliest stream start. A file whose first frame comes after 0 (a fragment cut from a
longer stream, an edit-list offset, a WebM with a start offset) was extracted early and
short, so renders tripped the video coverage gate. Every video seek now maps media time
to FFmpeg's timeline, and the video begins where the browser shows it.

* fix(engine): audio from late-starting MP4 and WebM plays when the preview plays it

The mixer seeked and trimmed audio by FFmpeg's clock, which starts at the file's earliest
stream. In an MP4 or WebM whose streams all start after 0, the browser plays that audio
later, so the render heard it early by the same amount. The mixer now maps media time onto
FFmpeg's clock with the same offset frame extraction uses.

* fix(engine): a late-starting file's picture shows on the clock its audio plays on

* test(producer): vfr-screen-recording baseline shows each frame at its timestamp
2026-10-28 13:09:53 +00:00
Miguel Ángel b6eaed7cae fix(cli): a check page forces a WebGL drawing buffer but keeps no list of 2d canvases (#6650) 2026-10-28 10:41:22 +00:00
Miguel Ángel 7758d9b8fd fix(engine): a Windows browser that will not start says where and why (#6647)
* fix(cli): a Windows browser that will not start says where and why, not just spawn UNKNOWN

* fix(engine): the launch diagnosis names a browser file Windows refuses to run

* fix(engine): the launch diagnosis names a browser file Windows refuses to run (engine part)
2026-10-28 09:41:17 +00:00
Miguel Ángel 6553b1f6ba perf(producer): --gpu renders encode while frames are captured (#6631) 2026-10-28 09:24:27 +00:00
Miguel Ángel 8e63faf846 perf(engine): a --gpu render probes the GPU encoders once, not twice (#6638)
* perf(engine,cli): a machine's GPU encoder probe runs once, not on every render

* refactor(engine): one shared probe per process, no answer kept on disk
2026-10-28 08:44:28 +00:00
Miguel Ángel e6476c2cd5 perf(engine): Macs with a hardware GPU render with more capture workers (#6636)
* perf(engine): Macs with a hardware GPU render with more capture workers

* perf(engine): Apple-GPU worker sizing skips layered renders; fewer comment lines

* test(producer): format
2026-10-28 08:42:31 +00:00
Miguel Ángel 6cf4b56697 fix(engine): a render on Windows opens no console windows (#6613)
* ci(windows): probe that a render opens no visible window

* ci(windows): the probe names what opened each window

* ci(windows): the probe spawns each binary alone, with and without detached

* ci(windows): the probe logs the processes started during the render

* ci(windows): the probe also spawns Chrome one consoleless hop down

* ci(windows): the probe logs each child started without windowsHide

* ci(windows): the probe logs every child process with its windowsHide and detached

* fix(engine): Chrome keeps the CLI's console on Windows, so a render opens no window

* fix(cli): every child the CLI starts on Windows is hidden unless it opts out

* ci(windows): the probe bisects Chrome's flags and keeps each command's output

* ci(windows): the probe names the Chrome process behind each window

* ci(windows): the probe loads a page in Chrome directly and one hidden hop down

* ci(windows): the probe prints the Chrome main command line and renders with the reporter's flags

* ci(windows): the probe narrows the CLI's Chrome arguments to the ones that open a window

* ci(windows): the probe launches the CLI's Chrome arguments one hidden hop down

* ci(windows): the probe launches Chrome through the engine's browser pool

* ci(windows): the probe varies what the engine passes to Puppeteer

* fix(engine): renders on Windows launch Chrome with a Puppeteer that keeps it attached

* refactor(cli): hideChildConsoles copies its arguments and keeps one comment line
2026-10-28 07:48:40 +00:00
Miguel Ángel bf7ebe961e fix(cli): local servers never bind a port Chromium refuses to load from (#6620) 2026-10-28 05:16:42 +00:00
Miguel Ángel 313792408d fix(producer): HDR sources are tone-mapped for formats that cannot carry HDR (#6597)
* fix(producer): HDR sources are tone-mapped for formats that cannot carry HDR

* refactor(producer): one owner for which output formats can carry HDR

* refactor(producer): drop comments the helper names already carry

* fix(engine): SDR clips are not lifted to HDR when the render tone-maps to SDR
2026-10-27 23:09:15 +00:00
Miguel Ángel 3b2058a2b7 fix(engine): extraction waits out the decode up to a deep seek in a long-GOP video (#6583)
* fix(engine): extraction waits out the decode up to a deep seek in a long-GOP video

* chore(engine): shorten the keyframe gap doc comment

* fix(engine): cancelling an extraction during the keyframe probe reports a cancel

* fix(engine): a segment stopped by another's failure reads as stopped, not cancelled
2026-10-27 20:09:05 +00:00
Miguel Ángel 957df5c268 feat(cli): render --prores-profile picks ProRes 4444, hq, 422, lt or proxy for MOV (#6581)
* feat(cli): render --prores-profile picks ProRes 4444, hq, 422, lt or proxy for MOV

* fix(cli): drop a comment line that raised the comment share

* fix(cli): the render plan args type carries --prores-profile
2026-10-27 19:28:49 +00:00
Akshar Patel 1d4aadc1fb fix(engine): mux uppercase WebM exports with Opus audio (#6579)
* fix(engine): mux uppercase WebM exports with Opus audio

* test(engine): terminate ffprobe options before input
2026-10-27 18:15:39 +00:00
Miguel Ángel c391b50163 test(engine): the test Chrome warms its first page in beforeAll; browser hooks get 30 s (#6568)
A first page of a fresh Chrome stalled past vitest's 10 s hook limit on a contended Windows runner and failed the first test of its file. The first page is now opened inside beforeAll, with the launch and first-page times logged, and the browser project's hooks get the 30 s the CLI's Chrome project uses.
2026-10-27 15:49:13 +00:00
Miguel Ángel d6e16aa187 fix(studio-server): undo and redo on Windows wait out a briefly held file (#6559)
* fix(studio-server): undo and redo on Windows wait out a briefly held file

* style(core): format the atomic write test
2026-10-27 14:28:43 +00:00
Miguel Ángel c1da4a760e fix(engine): audio effects launch Chrome like every other render, sandbox retry included (#6550)
Audio effects launched Chrome with its own two flags and no retry, so inside
an agent's macOS seatbelt Chrome died at startup with "Target closed" while
the CLI's own browser launch recovered. The sandbox launch policy now lives
in the engine; the engine's Chrome launch retries once in one process over a
pipe when the sandbox refuses it, and audio effects use the shared Chrome
flags in software GPU mode. The CLI imports the same policy from the engine.
2026-10-27 13:26:01 +00:00
Miguel Ángel cfef528d3a fix: phone videos with an undecodable first audio track check, preview and render (#6551)
* fix: phone videos with an undecodable first audio track check, preview and render

* fix(engine): a rate-lane clip bakes its ramp from the decodable audio track

* test: phone-clip tests end ffprobe options before the input
2026-10-27 13:09:02 +00:00