Commit Graph
482 Commits
Author SHA1 Message Date
Miguel Ángel f3177872a1 chore: release v0.7.19 2026-06-29 21:45:34 +00:00
Miguel Ángel c811a2750a chore: release v0.7.18 2026-06-28 22:55:15 +00:00
Miguel Ángel fc0f8c3151 fix(render): avoid empty WAAPI scans and llvmpipe auto GPU (#1775)
Avoid the screenshot-path #1715 regression by skipping empty WAAPI/CSS animation scans per seek and classifying known software WebGL renderers correctly in browserGpuMode=auto.\n\nAddresses #1715.
2026-06-28 10:42:14 -04:00
miga-heygen 35a01d9058 perf(engine): reduce init overhead in headless capture sessions (#1718)
Flush the GSAP proxy queue synchronously during capture session initialization and parallelize independent media/font/tailwind readiness waits.

Closes #1715.

Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra_miga@heygen.com>
2026-06-28 10:36:21 -04:00
Miguel Angel Simon Sierra 3351fb1a6d chore: release v0.7.17 2026-06-27 13:54:58 -04:00
Miguel Ángel fb4d49d248 chore: release v0.7.16 (#1771) 2026-06-27 12:16:46 -04:00
Miguel Ángel a14d342bdb chore: release v0.7.15 2026-06-27 10:00:26 +00:00
Miguel Ángel 8f7fb49d16 chore: release v0.7.14 2026-06-27 00:39:16 +00:00
Miguel Ángel e3edbd55cf chore: release v0.7.13 2026-06-26 22:43:22 +00:00
Miguel Ángel 1d3c68ef69 chore: release v0.7.12 2026-06-26 21:49:37 +00:00
James 413d8187fd chore: release v0.7.11 2026-06-26 18:37:23 +00:00
Miguel Ángel c9e8dd3862 fix(runtime): honor render fps when seeking (#1739) 2026-06-26 12:28:41 -04:00
Miguel Ángel 0c1e236dcd chore: release v0.7.10 (#1734) 2026-06-26 00:51:34 -04:00
Miguel Angel Simon Sierra 8db190dd43 chore: release v0.7.9 2026-06-25 21:32:51 -04:00
Miguel Ángel 92385711dc fix(engine): hold last frame when a clip's media is shorter than its slot (#1726)
Renders showed the page background (a one-frame black flash) right before a cut
when a video clip's source media was a hair shorter than its data-duration slot
— the common case, since `ffmpeg -t 1.45` emits 43 frames = 1.433s at 30fps.
The frame lookup only held the last frame at the exact clip end, so the
sub-frame remainder rendered blank.

- Hold the last extracted frame for the rest of the slot once the source is
  exhausted, within a tolerance floored at the compiler's 0.05s clamp epsilon so
  the seam is covered at any fps (2 frames alone is < 0.05s above 40fps). Clips
  deliberately much shorter than their slot still blank for the tail (unchanged).
- Warn when the compiler clamps a video's data-duration down to its media length
  (slot longer than source by more than the clamp epsilon): a render-time
  `[compile]` warning in the producer, plus a matching `validate` warning that
  reads each <video>'s live duration in headless Chrome (static HTML lint can't
  see media durations). A shared `analyzeClipMediaFit` keeps both on one
  threshold.

Adds engine unit tests for the hold behavior and the analyzer.
2026-06-25 19:16:27 -04:00
Miguel Angel Simon Sierra 764aa02a3a chore: release v0.7.8 2026-06-25 18:59:53 -04:00
Miguel Ángel 1494f715fb chore: release v0.7.7 (#1719) 2026-06-25 11:54:10 -04:00
Miguel Ángel 0558b8761e fix(producer): retry probe navigation timeouts (#1713) 2026-06-25 10:52:38 -04:00
Miguel Ángel eee376cb5e chore: release v0.7.6 (#1707) 2026-06-24 20:17:20 -04:00
Miguel Ángel 89ff299a11 fix(engine): defend macOS regular Chrome screenshots
Fixes #1699.
2026-06-24 19:21:46 -04:00
Miguel Ángel 649c216394 chore: release v0.7.5 2026-06-24 04:41:56 +00:00
miga-heygenandClaude Opus 4.6 546b2d770b fix(producer): retry probe stage on transient browser errors (#1688)
* fix(producer): retry probe stage on transient browser errors (#1687)

The distributed render plan stage crashes when headless Chrome encounters
a transient frame detachment ("Navigating frame was detached") during
browser probe, with no retry logic. The plan tarball is never uploaded,
and all downstream chunk workers fail with S3 404.

Add a retry-with-fresh-session mechanism to the probe stage:

- `isTransientBrowserError()` classifier in the engine identifies 9
  known transient Puppeteer/Chrome errors (frame detached, target closed,
  session closed, protocol error, page crashed, execution context
  destroyed, etc.).

- `runProbeStage()` wraps browser session creation + initialization in a
  retry loop (max 2 attempts). On transient error: logs structured
  diagnostics (attempt, isTransient, error message, elapsed time), closes
  the crashed session cleanly, creates a fresh browser, and retries. Non-
  transient errors throw immediately without consuming retry budget.

- 17 unit tests for the error classifier, 3 integration tests for retry
  behavior (successful retry, immediate throw on non-transient, exhaust
  retry budget on persistent transient).

Closes #1687

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review feedback — widen retry scope, deduplicate patterns

- Move createCaptureSession inside the retry try/catch so browser launch
  failures (Failed to launch the browser process, ECONNREFUSED) are also
  retried — not just initializeSession errors.
- Deduplicate transient error patterns: remove "Protocol error.*Target
  closed" (subsumed by "Target closed") and "Navigation failed because
  browser has disconnected" (subsumed by "browser has disconnected").
- Add browser launch failure patterns: "Failed to launch the browser
  process" and "ECONNREFUSED".
- Add test for createCaptureSession transient throw (browser launch retry).
- Update test mock comment to document sync requirement with engine
  pattern list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-24 00:07:14 -04:00
Miguel Ángel ba77a0bd72 chore: release v0.7.4 2026-06-24 00:10:04 +00:00
Miguel Ángel f622e5a7ba fix(engine): restore fast screenshot path for viewport captures (#1670) 2026-06-23 15:13:24 -04:00
Miguel Ángel 997823b6b5 chore: release v0.7.3 2026-06-22 23:07:48 -04:00
Miguel Ángel b8d6c4aa21 chore: release v0.7.2 2026-06-22 22:44:19 +00:00
Miguel Ángel 60d3eeb1f7 test(producer): add stream duration parity check to regression harness (#1652)
Probes the rendered output for video and audio stream durations after
render and fails the test if they differ by more than 0.5s. Catches
mux-level truncation regressions like the ffmpeg -shortest bug (#1648)
where one stream gets silently cut short.

Runs on all non-png-sequence fixtures with audio — no new meta.json
field needed since this is a universal invariant, not a per-fixture
threshold.
2026-06-22 18:32:59 -04:00
Miguel Ángel e9957ecaf4 fix(engine): remove -shortest from muxVideoWithAudio (#1648) (#1650)
FFmpeg 6.0 (bundled by ffmpeg-static) has a regression where -shortest
combined with -c:v copy over-truncates the video stream while leaving
audio untouched. The flag is also redundant — the audio mixer already
pads/caps all tracks to totalDuration via apad=whole_dur and -t.

Closes #1648
2026-06-22 17:06:03 -04:00
Miguel Ángel 3a6742bd13 chore: release v0.7.1 2026-06-22 14:09:55 -04:00
Miguel Ángel a97f80433f chore: release v0.7.0 2026-06-22 11:23:33 -04:00
Miguel Ángel e385d213f3 chore: release v0.6.121 (#1627) 2026-06-21 17:59:01 -04:00
Miguel Ángel 6c71cf0240 chore: release v0.6.120 (#1624) 2026-06-21 11:17:48 -04:00
Miguel Ángel 2612b9bdfe fix: publish Node-compatible package entrypoints (#1622) 2026-06-21 10:49:35 -04:00
Vance Ingalls b33a7457a7 chore: release v0.6.119 (#1620) 2026-06-21 01:03:18 -07:00
Miguel Ángel 0d2198e654 chore: release v0.6.118 2026-06-20 18:24:11 -04:00
Miguel Ángel 12d5273812 chore: release v0.6.117 2026-06-20 18:05:59 -04:00
Miguel Ángel 6b279267cd chore: release v0.6.116 2026-06-20 21:25:34 +00:00
Miguel Ángel 0473254bdd fix(engine): preserve AAC start time during MP4 mux (#1615)
* fix(engine): copy mixed AAC during MP4 mux

* fix(producer): avoid AAC re-encode in distributed audio pad

* fix(engine): probe AAC sidecars before mux copy decision

* fix(producer): avoid temp concat file for audio padding
2026-06-20 17:22:15 -04:00
Miguel Ángel 8408a44745 fix(engine): tune VP9 cpu-used across render paths (#1614)
* fix(engine): tune VP9 cpu-used across render paths

* fix: address VP9 review feedback
2026-06-20 16:36:59 -04:00
Miguel Ángel d6135ca155 chore: release v0.6.115 2026-06-20 00:19:28 +00:00
Vance Ingalls 4e32c5e0fe chore: release v0.6.114 2026-06-19 04:38:05 -07:00
Vance Ingalls a38fc4e778 chore: release v0.6.113 2026-06-19 00:35:41 -07:00
fdb8f33fc0 fix(engine): hold the last video frame at the inclusive clip end (#1564)
* fix(engine): hold the last video frame at the inclusive clip end

The frame-lookup active set deactivated a video on an exclusive end-bound
(globalTime < end), while the runtime keeps an element visible through
currentTime <= end (core/runtime init.ts). The rendered frame landing
exactly on a clip's end went blank even though the runtime still showed
the element on its final frame: one blank frame at the end of every clip
whose end lands on a frame boundary.

Make the active window inclusive of the end to match the runtime, and at
t === end serve the last extracted frame (the runtime holds the element's
final frame there too). Mid-clip source exhaustion (t < end) stays blank,
unchanged.

* fix(engine): align getFrame boundary to match refreshActiveSet

Make getFrame's end-bound inclusive (> instead of >=) for consistency
with the refreshActiveSet changes. getFrame is currently unused
externally but should match the same contract.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Carlos Alcaraz <193642530+calcarazgre646@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-18 15:37:48 -04:00
Vance Ingalls 1bab79ef4c chore: release v0.6.112 2026-06-18 01:14:30 -07:00
Vance IngallsandClaude Opus 4.8 7310223b66 feat(engine): static-frame dedup default-on + render telemetry (#1549)
* feat(engine): static-frame dedup for screenshot capture (opt-in)

Skip re-seeking + re-screenshotting frames byte-identical to their predecessor. A
frame is dedupable iff no GSAP tween or clip cut is active in it or its predecessor
(predicted from window.__timelines + clip schedule) AND an empirical anchor-compare
confirms it. Opt-in HF_STATIC_DEDUP=true, default off.

Correctness (designed for the multi-worker / distributed render paths):
- Reuse is keyed by the ABSOLUTE composition frame (derived from the frame's time),
  NOT the captureFrameCore frameIndex arg — chunked/parallel callers pass a chunk-
  relative index. Validated lossless (PSNR=inf) on both single- and multi-worker
  renders of a static-hold comp.
- verifyStaticFramesSafe checks EVERY run (no longest-first budget truncation that
  left runs armed-but-unverified), and samples each run's FIRST reused frame, its END,
  and interior points at a stride; a hard cap disables dedup rather than trust an
  unverified set.
- Conservative arming: skipped when capture mode != screenshot (BeginFrame tick
  semantics + the verifier's screenshot path wouldn't transfer), when a before-capture
  hook is set (per-frame video injection), when page-side compositing is active (shader
  / drawElement composite the plain verification screenshot can't reproduce), and when
  any data-start is a non-numeric reference expression the clip-boundary parser can't
  protect, or duration is unknown/zero.
- Session reuse (prepareCaptureSessionForReuse) resets lastFrameBuffer + dedup counter
  so a probe/prior-render buffer can't bleed into the first static frame; the armed set
  is kept (same-composition reuse). Cost calibration bypasses dedup for its sparse,
  non-contiguous sample sweep, then restores the armed set.
- HF_STATIC_DEDUP_SAMPLES is NaN-guarded.

Disqualifies on signals the GSAP predictor can't see: video, canvas/webgl, zero
tweens, running CSS/WAAPI animation. Pays on static-hold content (title cards,
slideshow/kiosk loops, data-viz pauses); no-op on continuously-animated comps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(engine): static-frame dedup default-on + render telemetry

Flip dedup from opt-in (HF_STATIC_DEDUP=true) to default-on (opt-out
HF_STATIC_DEDUP=false). Verification (verifyStaticFramesSafe) is the
safety net that keeps reuse sound at scale.

Add end-to-end dedup observability. The capture session records
enabled / armed / skipReason / predicted; these surface via
CapturePerfSummary -> a dedupPerfs accumulator (disk sequential +
parallel AND streaming sequential + parallel) -> aggregated into
RenderPerfSummary.staticDedup (OR armed, SUM frames across workers) ->
render_complete props static_dedup_{enabled,armed,skip_reason,
predicted_frames,reused_frames}. skip_reason is a low-cardinality code:
capture_mode | video_injection | page_composite | ineligible |
verification_failed.

Distributed chunks run on Linux/beginframe where dedup never arms, so
they pass a throwaway dedupPerfs sink (no per-chunk reporting).

Tests: aggregation logic (OR/SUM/skip-reason) + opt-out passthrough.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(engine): address review on dedup default-on + telemetry

Review feedback (miga-heygen) + self-review fixes:

- Retry double-count: executeDiskCaptureWithAdaptiveRetry pushed worker
  dedup perf inside the retry loop, so an adaptive retry counted frames
  twice (reused/predicted could exceed totalFrames). Reset dedupPerfs at
  the start of each attempt — retry now REPLACES rather than accumulates;
  common no-retry path is unchanged.
- Opt-out parsing: HF_STATIC_DEDUP now disables on {false,0,off}
  case/space-insensitive (was strict !== "false", so `False`/`0` silently
  kept dedup on — the kill-switch could no-op).
- Verification budget vs drift: verifyStaticFramesSafe returns
  {badFrame, budgetExhausted}; armStaticDedup reports a distinct
  `verification_budget` skip reason so a telemetry spike means "raise
  HF_STATIC_DEDUP_SAMPLES", not "compositions are non-static".
- Index idiom: captureFrameCore now uses Math.floor(time*fps + 1e-9)
  (matches quantizeTimeToFrame) so the dedup lookup agrees with the frame
  the seek lands on even for non-exact times.
- Stale "opt-in HF_STATIC_DEDUP=true" comments -> "opt-out
  HF_STATIC_DEDUP=false" across frameCapture.ts + types.ts.
- Extract pushWorkerDedupPerfs helper (perfSummary.ts), used by the disk
  and streaming parallel paths — removes the duplicated push loop and
  drops captureStreamingStage back under the complexity threshold.
- dedupPerfs is now required (not optional) on
  executeDiskCaptureWithAdaptiveRetry — a missing arg silently dropped
  telemetry.
- Test: captureStreamingStage createInput() now provides the required
  dedupPerfs field.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(engine): address deferred dedup-review items

- Derivable state: drop session.staticDedupArmed/staticDedupPredicted;
  derive both from session.staticFrames in getCapturePerfSummary
  (armed ⟺ non-empty set, predicted === size) so they can't desync.
- Config altitude: HF_STATIC_DEDUP now resolves into
  EngineConfig.staticFrameDedup (resolveConfig, opt-out on {false,0,off}),
  alongside forceScreenshot/browserGpuMode — armStaticDedup reads config
  instead of process.env. Default-on preserved (missing config → enabled).
- Lossy aggregation: aggregateDedup now reports DISTINCT skip reasons
  (sorted, `|`-joined) across diverging unarmed workers instead of just
  the first.
- discardWarmupCapture: also snapshot/restore staticDedupCount and
  lastFrameBuffer so a warmup capture can't leak a phantom reuse or a
  stale buffer anchor into the real summary.
- Convention: perfSummary-dedup.test builds its job via createRenderJob
  instead of `as unknown as RenderJob`.
- Docs: verification_budget added to skip-reason lists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 01:12:05 -07:00
Vance Ingalls de87f3932e chore: release v0.6.111 2026-06-17 22:28:29 -07:00
Miguel Ángel fcc7b314f0 chore: release v0.6.110 2026-06-17 10:23:49 -04:00
Vance Ingalls 1028f52aa2 chore: release v0.6.109 2026-06-16 21:16:45 -07:00
Vance Ingalls cc7c206e9c chore: release v0.6.108 2026-06-16 17:56:34 -07:00
ukimsanov 48fb42e5f0 refactor(runtime): trim color grading helpers 2026-06-16 13:41:32 -07:00