Commit Graph
277 Commits
Author SHA1 Message Date
webadderall 0abc7a3098 feat: bundle cursor motion and temporal blur updates 2026-05-04 18:12:47 +10:00
webadderall 6cc83b31e9 feat: add writable cursor telemetry API 2026-05-04 17:28:10 +10:00
wiiiii123 23f17f2772 Merge main into native GPU export PR 2026-05-04 11:42:27 +07:00
wiiiii123 94bbd0280f Add NVIDIA CUDA export compositor 2026-05-04 09:59:41 +07:00
wiiiii123 9c81006e5c Add native GPU static layout export path 2026-05-03 22:43:23 +07:00
shafeq 01f61dfada fix(export): return temp path from buffer-mode FFmpeg muxer (>2 GiB)
Exports of recordings whose muxed output exceeds 2 GiB failed with
RangeError [ERR_FS_FILE_TOO_LARGE]: Node's fs.readFile rejects files
larger than kIoMaxLength (2 ** 31 - 1). The legacy export pipeline hit
this in muxExportedVideoAudioBuffer, which called
  await fs.readFile(finalized.outputPath)
to ship the muxed bytes back to the renderer.

Mirror the path-based contract that mux-exported-video-audio-from-path
already uses:

- muxExportedVideoAudioBuffer now returns { outputPath, metrics } and
  collects byte size via fs.stat instead of fs.readFile. The unmuxed
  intermediate is still cleaned up; the muxed output is left for the
  IPC handler to register and the renderer to finalize.
- The mux-exported-video-audio IPC handler registers the muxed output
  via registerOwnedExportPath and returns { tempPath, metrics }.
- preload.ts and electron-env.d.ts: tempPath replaces data in the
  renderer-facing return type.
- videoExporter.ts and modernVideoExporter.ts (the
  finalizeExportWithFfmpegAudio fallback paths) now return
  { tempFilePath } so VideoEditor's existing finalize-exported-video
  flow handles the move — the same path the modern stream-mode export
  already takes.

The renderer already preferred tempFilePath over blob in
VideoEditor.tsx for MP4 saves (with the explicit comment "avoids ever
allocating a multi-GiB ArrayBuffer in the renderer"), so this just
removes the buffer-mode regression for large legacy exports.

Adds electron/ipc/export/native-video.test.ts asserting the new
contract: muxExportedVideoAudioBuffer returns a path, never calls
fs.readFile, and still records muxedVideoBytes via stat.

Closes #380
2026-05-02 09:48:05 +08:00
webadderall 5570c119ca [codex] Fix cursor sync after recording pause (#399)
* Fix cursor sync after recording pause

* Align recorder and cursor pause boundaries
2026-05-01 17:18:35 +10:00
webadderall 98e4c7cade Improve project autosave and media path handling 2026-04-27 20:07:32 +10:00
webadderall f58cd07250 Merge pull request #237 from Andrii-Vovk/fix/macos-screen-highlight-bounds
fix(highlight): screen highlight clipping on macOS
2026-04-27 14:43:16 +10:00
Wiii 574cefc6bd fix(export): use tempo filters for speed audio
Use FFmpeg atempo filters for native edited-track speed changes, including near-unity speed tolerance coverage.
2026-04-26 22:59:20 +07:00
Andrii-Vovk 69abb0390d fix(highlight): screen highlight clipping on macOS
On macOS, the screen highlight overlay's right and bottom edges were
   running off-screen. macOS clamps window positions below the menu bar,
   so the outward padding only appeared on the left/top while the
   right/bottom extended beyond the visible area.

   Use display workArea instead of full bounds for screen highlights on
   macOS, and keep the border/glow within the overlay window.
2026-04-26 17:31:28 +03:00
Wiii 97ba2a020c fix(recording): normalize native Windows mic loudness

2026-04-25 18:15:51 +07:00
Wiii 46f384d4d0 fix(recording): use native audio start timestamps for Windows sync

2026-04-25 17:47:39 +07:00
大彪 9ac4dbd2e8 fix(export): stream MP4 output to disk to unblock >2 GiB exports
Long recordings (35-minute screencaps were the motivating case) fail at
the 99% "Finalizing" step with a RangeError once the muxed MP4 would
exceed V8's ~2 GiB per-ArrayBuffer limit. Both export paths accumulate
the whole file in renderer memory and round-trip it through IPC, so no
output size past that point can complete:

- Legacy: src/lib/exporter/muxer.ts uses mediabunny's BufferTarget,
  which holds the entire MP4 in a single ArrayBuffer. finalize() → Blob
  → blob.arrayBuffer() → ipcRenderer.invoke('write-exported-video-to-
  path', arrayBuffer, path) — every step wants a ≥2 GiB contiguous
  allocation.
- Lightning: native-video-export-finish did fs.readFile(finalizedPath)
  and shipped the bytes back to the renderer, which re-serialized them
  again. Same ceiling.

This change moves the finished MP4 across the renderer↔main boundary
via a temp file instead of an ArrayBuffer:

- New electron/ipc/export/exportStream.ts manages streaming temp files
  via fh.write(buf, 0, len, position) so out-of-order writes (moov box
  rewrites, etc.) stay safe. Each session lives in a 0700 mkdtemp()
  directory opened with O_CREAT | O_EXCL so a hostile local user on a
  shared tempdir cannot pre-plant a symlink at the predicted path.
- New renderer-facing IPCs: export-stream-open/write/close,
  finalize-exported-video (renames temp to final path, copy+unlink
  fallback on EXDEV/EPERM/ENOTEMPTY with console.warn on leaked bytes),
  mux-exported-video-audio-from-path (FFmpeg audio fallback that takes
  a path instead of an ArrayBuffer), and discard-exported-temp. Every
  handler validates the caller-supplied path against an owned-export-
  paths registry before touching disk, so a compromised renderer cannot
  route arbitrary filesystem paths into main-process deletes/moves.
- The muxer now picks mediabunny's StreamTarget automatically when the
  Electron bridge is available (BufferTarget stays for tests and any
  non-Electron callers). finalize() returns { mode, tempFilePath,
  bytesWritten } or { mode, blob } so the exporter can branch.
- Exporters forward tempFilePath through ExportResult. Lightning's
  finish returns the ffmpeg temp path directly; the FFmpeg audio
  fallback forks on the muxer result type. modernVideoExporter's
  Lightning success branch now accepts tempFilePath (previously it
  checked blob only, which regressed every native export).
- VideoEditor.tsx dispatches on tempFilePath: finalize via the new IPC,
  keep the temp in place when the save dialog is canceled so "Save
  Again" still works without re-rendering, keep the pending-save entry
  alive on non-canceled save failures, and discard the temp on unmount
  or explicit clear. GIF and smoke-test code paths still use the
  legacy Blob path unchanged.
- app.on('before-quit') also reaps any open streaming sessions via
  cleanupAllExportStreams().

Chunk size is 16 MiB — well under Electron/Mojo IPC message limits
while keeping total writes low (~160 for a 2.5 GB export).

Tested locally: exported a 35:13 source (~2.7 GiB H.264 input) at
Original 1920×1080 + Balanced. Previously failed on finalize with a
RangeError; with this patch the Legacy pipeline produced a valid 3.7
GiB MP4 whose ffmpeg -i duration/streams match the source.

Addresses #194.
2026-04-24 16:02:37 +08:00
webadderall 62f2a52860 Fix companion audio sync for trimmed and long exports 2026-04-24 12:04:21 +10:00
webadderall 05a6d3f1b6 Fix embedded audio fallback handling and audio diagnostics (#309)
* Fix embedded audio fallback handling and audio diagnostics

* Add recording fallback diagnostics toasts

* Fix Windows audio fallback cleanup and path matching

* Format rebased recording fallback changes

* Fix embedded audio preview and export fallback handling
2026-04-24 11:30:10 +10:00
webadderall 235a01c561 Merge pull request #314 from soufian3hm/fix/windows-recording-validation
Fix invalid Windows recording finalization
2026-04-22 17:19:56 +10:00
webadderall 12683f4783 Merge pull request #310 from meiiie/fix/native-audio-lead
fix(recording): keep longer audio tracks anchored
2026-04-22 17:18:51 +10:00
webadderall bac08a3f6d Merge pull request #308 from meiiie/fix/local-media-url-approval
fix(media): approve loopback video paths when minting URLs
2026-04-22 17:18:33 +10:00
soufian3hm 726808a8ad Fix invalid Windows recording finalization 2026-04-21 17:38:37 +01:00
wiiiii123 69406176c8 fix(recording): keep longer audio tracks anchored 2026-04-21 19:53:10 +07:00
wiiiii123 7e87356cf8 fix(media): restrict loopback approvals to supported files 2026-04-21 19:15:02 +07:00
wiiiii123 c8c9386346 fix(media): approve loopback video paths when minting URLs 2026-04-21 19:00:29 +07:00
wiiiii123 383bde994e test(export): cover slowdown filtergraph inputs 2026-04-21 18:09:23 +07:00
wiiiii123 aec1dd8419 test(export): tighten native filtergraph assertions 2026-04-21 18:09:23 +07:00
wiiiii123 3b2922cfbb fix(export): harden edited-track filter validation 2026-04-21 18:09:23 +07:00
wiiiii123 2b19493bb7 fix(export): validate edited-track fast-path inputs 2026-04-21 18:09:23 +07:00
wiiiii123 fcdc842371 fix(export): fast-path simple edited audio tracks 2026-04-21 18:09:23 +07:00
webadderall d09f88abdb Merge pull request #284 from meiiie/research/export-pipeline-profiling
chore(export): expand finalization profiling
2026-04-21 18:26:03 +10:00
webadderall ce970adf5a Cleanup rename and trim handling 2026-04-21 14:58:56 +10:00
webadderall 1f5a425d34 Protect projectId from overwrite follow-up 2026-04-21 14:44:10 +10:00
webadderall 9a4c5155a9 fix(editor): address PR review feedback 2026-04-21 11:59:09 +10:00
webadderall 337569739c fix(editor): port project save UX and clip cleanup to main 2026-04-21 11:46:07 +10:00
wiiiii123 ccc0e63d1b fix(export): clean telemetry profiling rebase 2026-04-20 20:11:58 +07:00
wiiiii123 b7e5a4ab79 chore(export): add ffmpeg mux timing breakdown 2026-04-20 20:09:54 +07:00
webadderall 09f487d069 Merge pull request #282 from meiiie/fix/protect-project-recordings-from-prune
fix(recordings): preserve media referenced by saved projects
2026-04-20 20:37:07 +10:00
webadderall 0ccdc449e8 Merge pull request #278 from meiiie/fix/export-media-paths
fix(export): reopen saved videos and default to source quality
2026-04-20 20:35:21 +10:00
wiiiii123 3509ecf21c fix(recordings): fail closed during prune project scans 2026-04-20 02:03:45 +07:00
wiiiii123 f7e3494ee3 fix(recordings): preserve media referenced by saved projects 2026-04-20 01:49:40 +07:00
wiiiii123 cf676487b2 fix(recording): preserve mic companion audio when source has audio 2026-04-19 23:54:40 +07:00
wiiiii123 cac33005e3 test(export): tighten media path regression coverage 2026-04-19 23:44:10 +07:00
wiiiii123 4f739a058e fix(export): reopen saved videos and default to source quality 2026-04-19 21:42:06 +07:00
webadderall fa97c94411 fix(linux): allow full-screen capture by matching display sources by position if IDs do not match (fixes #265) 2026-04-18 22:34:54 +10:00
webadderall cbe0b9feac fix: replace real-time audio export with streaming decode + chunked offline rendering
Major rewrite of the audio export pipeline for stability and memory efficiency:

## Streaming Decode
- Primary decode path uses WebDemuxer + AudioDecoder (WebCodecs streaming)
- Falls back to bulk decodeAudioData if streaming fails
- Avoids holding full compressed file + decoded PCM simultaneously

## Chunked Offline Rendering
- Processes timeline in 30-second OfflineAudioContext chunks
- Memory bounded to ~30s of PCM per chunk regardless of recording length

## Chunked WAV Writing
- Writes PCM in ~256KB chunks instead of single massive ArrayBuffer
- Eliminates OOM for long recordings on the native/FFmpeg export path

## Chunked Encoding
- Single AudioEncoder kept alive across all chunks (clean AAC stream)
- Proper backpressure and error propagation per chunk

## scheduleBufferThroughTimeline chunk windowing
- Clips source nodes to chunk boundaries for correct cross-chunk audio
- Backwards compatible defaults when no chunk params provided

## Other export fixes
- Progress cap: 99% -> 100% in both exporters and UI
- Muxing errors: propagated instead of silently swallowed
- Mac recording: enhanced warning when no audio files for muxing
- Export timeout: eliminated by removing real-time rendering
2026-04-18 20:29:14 +10:00
webadderall 8f3a62d811 fix: check both realpath and resolve forms in media URL handler, extract sync constants
- IPC get-local-media-url now checks both fs.realpath and path.resolve forms
  against approvedLocalReadPaths, so symlinks approved via approveUserPath work
- Extract duplicated sync correction parameters into shared constants in
  audioEncoder.ts (SYNC_SEEK_THRESHOLD_SEC, SYNC_PLAYBACK_RATE_OPTIONS)
2026-04-18 19:11:01 +10:00
webadderall dec004eddd fix: use drift-compensating scheduler in recording handler, fix media URL consistency
- Replace setInterval in recording handler with startCursorSampling() so
  the drift-compensating scheduler is used for actual recordings (was missed)
- Pass realpath-resolved path to buildMediaUrl for symlink consistency
- Use resolvedWebcamVideoUrl in thumbnail/export configs with file:// fallback
2026-04-18 19:03:36 +10:00
webadderall ffc5959994 fix: address CodeRabbit review feedback
- Fix telemetry drift reset: use nextExpectedMs - now for delay instead
  of stale drift value after baseline reset (avoids 1ms rapid sample)
- Use RECORDER_TIMESLICE_MS for mic fallback recorder (was hardcoded 1000)
- Return requested timeMs in extensionHost getCursorAt boundary clamps
- Remove premature null of webcam URL to prevent flicker on path change
- Use fs.realpath in IPC get-local-media-url to match media server check
2026-04-18 19:03:36 +10:00
webadderall 9100899adb fix: improve cursor telemetry accuracy under CPU load
- Replace setInterval with drift-compensating recursive setTimeout for
  cursor sampling. Under CPU load setInterval bunches or skips callbacks,
  creating irregular gaps in telemetry data.
- Add binary search + linear interpolation to export cursor lookup
  (modernFrameRenderer) matching the playback path. Was using O(n)
  nearest-neighbor which caused visible cursor jumping in exports.
- Apply same interpolation fix to extension API getCursorAt().
2026-04-18 19:03:36 +10:00
webadderall f024c89890 fix: always apply aresample sync filter and tighten audio sync tolerances
- Always route audio through aresample=async=1:first_pts=0 filter during
  muxing on both macOS and Windows, even when duration delta is small.
  Previously skipped when delta ≤50ms, which left progressive clock drift
  (from CPU load) completely uncorrected.
- Lower sync detection threshold from 50ms to 20ms so tempo correction
  kicks in earlier.
- Use explicit 48kHz AudioContext sample rate in both browser recording
  and export rendering to prevent sample rate mismatch drift.
- Reduce MediaRecorder timeslice from 1000ms to 250ms to reduce chunk
  loss under CPU pressure.
- Tighten export audio sync: seek threshold 300ms→150ms, tolerance
  15ms→8ms, correction window 2s→0.5s, max adjustment ±8%→±12%.
- Lower companion audio start delay threshold from 50ms to 25ms.
2026-04-18 19:03:36 +10:00
webadderall 7f587a39a8 fix: address CodeRabbit review feedback
- Use videoSourcePath instead of fromFileUrl(videoPath) for cursor
  telemetry — fixes broken path when videoPath is an HTTP media-server URL
- Validate filePath against approvedLocalReadPaths in get-local-media-url
  IPC handler before returning a URL
- Use fs.realpath() instead of path.resolve() to close symlink bypass
- Fix range parser to handle suffix ranges (bytes=-500) and guard
  against NaN values
- Add CORS headers (Access-Control-Allow-Origin) to media server
  responses to prevent canvas tainting when using video frames
- Handle OPTIONS preflight requests
- Clear stale resolvedWebcamVideoUrl before resolving new URL to
  prevent flash of stale content
2026-04-18 16:09:16 +10:00