Commit Graph
226 Commits
Author SHA1 Message Date
carlitosteso3andClaude Opus 5.5 70aa661586 #201: Speed/Duration opens its dialog; dragging an effect parameter is one undo step
- The clip menu's Speed/Duration… applied 50% at once, and Cmd+R / Clip ▸
  Speed/Duration… ran it at 100% with no dialog. It now opens Clip Speed /
  Duration (speed, the resulting duration, Reverse Speed, Ripple Edit, Time
  Interpolation), seeded from the selected clip.
- effects.setParam took one undo step per frame of a drag, so Undo after
  dragging Time Remapping's speed (or any Effect Controls value) crept back in
  small steps. It takes `merge` / `begin` like the mixer and masks, and Effect
  Controls sends them while the mouse button is down: a drag is one step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:25:44 -04:00
Brandon ThomasandClaude Opus 5.5 de70468ab6 #210 review: bound MXF reads by the file, keep legacy projects opening, allow 16K
- mxf: read_pcm bounds a request by the sample frames the file actually stores (plus 16M
  samples of zero padding) instead of a fixed 16,777,216-sample cap, so whole-clip reads of
  long stereo MXF (transcription) work again; PCM/AES3 chunk buffers are clamped to the source
  length instead of a 256 MiB cap; decode_pcm's output is bounded by its input, so its
  fixed cap goes (the AES3 >8-channel refusal stays; a request is also bounded by the source
  length divided by the channel count). read_sample keeps only the source-length bound.
- mxf: index_entries no longer fails open() past 1,048,576 entries (the sparse BTreeMap
  already bounds memory by real entries); long recordings open.
- project/format: new Sequence/Track/CaptionTrack::check_bounds() hold only the hostile-value
  bounds (settings, marks, overflowing time ranges); project load and export ranges use it, so
  projects that break only structural rules (overlap, zero duration) still open and export.
  check() = check_bounds() + the structural rules, as before.
- project: frame-size cap raised to 268,435,456 pixels, 32768 per side (16K, 16384x8192).
- engine: checked_u32_p accepts integer-valued floats (1920.0); renderable_sequence returns
  an EngineError (new Session::try_render_program_at, used by automation render) instead of
  eprintln.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:52:53 -04:00
cirocueto2013-wqandCodex 07d85b0f02 fix: validate hostile project, export and MXF inputs
Validate staged speed edits before adopting their timeline state.

Co-authored-by: Codex <noreply@openai.com>
2026-10-07 20:38:43 -04:00
Brandon Thomas a847671dbb Merge pull request #212 from storytold/test-188
#188: regression test — a copied Graphic clip pastes at the playhead
2026-10-07 20:37:34 -04:00
c57541674d #188: regression test — a copied Graphic clip pastes at the playhead
Engine test from #205 (the shortcut half of that PR is superseded by #204).

Co-Authored-By: carlitosteso3 <carlitosteso3@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:36:21 -04:00
Brandon Thomas 4f98721cab Merge pull request #209 from cirocueto2013-wq/fix/cpal-voiceover-recovery
fix: support integer CPAL devices and recover voice-over/audio failures
2026-10-07 20:35:54 -04:00
Brandon Thomas 6d904c1b37 Merge pull request #192 from cirocueto2013-wq/fix/editing-workflow-stability
Stabilize project lifecycle, timeline, audio and export workflows
2026-10-07 20:35:46 -04:00
Brandon Thomas d4ca3594ec Merge pull request #203 from carlitosteso3/fix/ripple-razor-snap-164
Ripple delete names the sync-locked track in the way; playhead snaps to cuts (#164, items 2, 3 and snap)
2026-10-07 20:35:42 -04:00
carlitosteso3andClaude Opus 5.5 41659ae769 #164: Q/W ripple trim only the selected clips' tracks
With clips selected under the playhead, Ripple Trim Previous/Next Edit to
Playhead (and the plain trims) now trim only their tracks and linked partners,
like Add Edit; with none selected they use the targeted tracks as before. All
tracks are targeted by default, so W on a V1 clip also cut the music on A2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:50:39 -05:00
carlitosteso3andClaude Opus 5.5 e7e96b247f #164: ripple delete says which sync-locked track blocks it; playhead snaps to cuts
- A refused ripple delete / ripple trim now names the sync-locked track whose
  clip is in the way and how to proceed, instead of a generic "would break sync"
  (the edit is refused the same way Premiere refuses it).
- Dragging the playhead no longer snaps onto its own position (it stuck and moved
  in SNAP_PX jumps), snaps on the initial press too, and "Snap playhead in
  Timeline when Snap is enabled" is on by default, as in Premiere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:47:21 -05:00
cirocueto2013-wqandCodex 7151d329d6 fix: support integer CPAL devices and recover voice-over/audio failures
Co-authored-by: Codex <noreply@openai.com>
2026-10-07 20:33:33 -03:00
cirocueto2013-wqandCodex 1fd30aa29d engine: bound subclip media resolution and reject cycles
Co-authored-by: Codex <noreply@openai.com>
2026-10-07 19:56:58 -03:00
Brandon ThomasandClaude Opus 5.5 853f03b1aa M-HW: NVENC on top of the shared hardware-encoding setting (#183); never-crash fixes
Merge origin/main into the NVENC branch. #183 already provides the
HardwareEncoding enum, ExportSettings.hardware_encoding, the
hardwareEncoding export param and the Export > Video > Hardware Encoding
checkbox, so the duplicates from this branch are dropped and NVENC uses
them. filmcraft-export is now an all-platform dependency of
filmcraft-platform (VideoToolbox and NVENC both register encoders).

Never-crash fixes in the NVENC backend:
- submit() rejects a driver input pitch narrower than a row (and unlocks
  the buffer); fill_nv12 uses get_mut instead of slicing rows.
- composition offsets use saturating math and i32::try_from; frame-rate,
  size and timestamp casts are checked; sizes above u16::MAX decline.
- Annex B scanning and NAL type checks use get()/first().
- A failed nvEncOpenEncodeSessionEx that returns a handle destroys it.

ffi.rs carries NVIDIA's MIT notice for nvEncodeAPI.h, listed in
ATTRIBUTION.md under a new "Third-party source code" section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:16:12 -04:00
Brandon ThomasandClaude Opus 5.5 4e4efcef12 M-relink: file.import also drops the cached slate of media it brought back (#110)
refresh() cleared the Missing badge, but the media pool kept the
MEDIA NOT FOUND slate cached under the item, so the monitors kept
showing it. The regression test now checks the rendered frame too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 17:56:33 -04:00
Brandon Thomas a4b92f1bec Merge remote-tracking branch 'origin/main' into pr-175 2026-10-07 17:54:19 -04:00
Brandon Thomas fcf1c45583 Merge pull request #141 from SaymonMaster14/feat/windows-media-foundation-hwdecode
Windows: Media Foundation/D3D11 hardware H.264 + HEVC decode

# Conflicts:
#	README.md
#	ROADMAP.md
#	crates/platform/Cargo.toml
#	crates/platform/src/lib.rs
#	docs/architecture.md
#	docs/performance.md
2026-10-07 17:48:06 -04:00
Brandon Thomas b5f197567b Merge pull request #186 from dcarnelutti/platform/videotoolbox-hevc-encode
platform: H.265 (HEVC) export with VideoToolbox

# Conflicts:
#	crates/export/src/settings_tests.rs
#	crates/ui-egui/tests/export_ui.rs
2026-10-07 17:47:10 -04:00
Brandon Thomas 84fcbeb1b9 Merge pull request #183 from dcarnelutti/platform/videotoolbox-h264-encode
platform: opt-in hardware H.264 encoding with VideoToolbox

# Conflicts:
#	crates/engine/src/export_tests.rs
2026-10-07 17:46:57 -04:00
Brandon Thomas fed7201fcf Merge pull request #185 from dcarnelutti/export/progress-eta
export: show the estimated time left of a render next to its percentage
2026-10-07 17:46:37 -04:00
Brandon Thomas ce5d365c78 Merge pull request #165 from carlitosteso3/fix/media-browser-probe-157
Media Browser: probing a file doesn't read it whole (#157)

# Conflicts:
#	crates/engine/src/media_pool.rs
2026-10-07 17:46:32 -04:00
Brandon Thomas d01e1b893d Merge pull request #145 from carlitosteso3/fix/color-matte-29
Color Matte: pick the color on creation and change it later (#29)
2026-10-07 17:46:23 -04:00
Brandon Thomas 8b3f118878 Merge pull request #173 from carlitosteso3/fix/shortcut-modifiers-164
Shortcut recorder accepts modifier chords; Add Edit cuts only selected clips (#164, items 1 and 5)
2026-10-07 17:46:20 -04:00
Brandon Thomas b4c65bdf8a Merge pull request #189 from Luna161/fix/168-project-select-all
Engine: project.selectAll and project.deselectAll for the Project panel (#168)
2026-10-07 17:46:19 -04:00
Brandon Thomas 91a36a4a3f Merge pull request #166 from justjohn12345/pr/text-box-modes
Graphics: point text and paragraph text, as Premiere has them
2026-10-07 17:44:07 -04:00
AJ DepewandClaude Opus 5.5 57069293fb Graphics: New Layer from File anchors the picture at its own centre
graphics.newFromFile resolved the Motion anchor with the sequence frame size
instead of the imported picture's size, so a picture smaller than the frame
got its anchor at the frame centre (e.g. 960,540 for a 4x2 image). Setting
Motion ▸ Position then moved the wrong point and the picture landed far from
where it was asked to go. place_video_clip now resolves auto points with the
source's own size (Project::source_size), falling back to the frame size.

The new-layer-from-file test checks the anchor is the picture's centre; it
fails before this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:35:41 -06:00
06bc7ef9ec Engine: project.selectAll and project.deselectAll for the Project panel (#168)
Root cause: the only Select All was `edit.selectAll`, which fills the
timeline clip selection (`state.selection`). Nothing filled the Project
panel's selection (`state.project_selection`), so with 161 imported
items the user had to click each one.

Fix:
- `project_panel::commands()` gets `project.selectAll` (selects every
  item in the project) and `project.deselectAll` (clears the Project
  panel selection). They are regular journalled commands with no menu
  entry and no shortcut of their own, so they add no shortcut conflicts.
- `handle_shortcuts` (ui-egui) remaps a fired `edit.selectAll` /
  `edit.deselectAll` to the `project.*` command when the Project panel
  has focus. Cmd+A / Cmd+Shift+A keep acting on timeline clips
  everywhere else, and `edit.selectAll` behaves exactly as before.

Out of scope: no menu entry. The Edit ▸ Select All menu item still
calls `edit.selectAll` directly, because menu clicks don't go through
the focus remap. The regression test checks the engine commands but not
the UI keyboard remap.

Regression test: project_panel_tests::select_all_selects_every_project_item
(fails without the fix: unknown command).

Co-Authored-By: ninso112 <ninso112@pm.me>
Co-Authored-By: Luna161 <Rosenkranzrene@gmail.com>
2026-10-07 20:21:49 +00:00
Dario CarneluttiandClaude Sonnet 5.5 790b3a0697 platform: H.265 (HEVC) export with VideoToolbox
Adds `Format::Hevc` (Export > Format > H.265 (HEVC); `"format": "hevc"` in
`file.exportMedia`): HEVC Main, 8-bit 4:2:0, in MP4 (`hvc1`) or QuickTime, with AAC audio,
encoded by the macOS hardware encoder. There is no built-in H.265 encoder (one in pure Rust
is a project of its own, and x265 is GPL), so the format exists only where the OS has one.

- `videotoolbox_encode`: the session wrapper creates HEVC sessions too (`VtProfile::HevcMain`;
  the profile also says which codec). Keyframes are the HEVC random access points (NAL types
  16 to 21); the parameter sets are VPS / SPS / PPS; the `hvcC` record in the MP4 sample
  entry is the one VideoToolbox writes (sample description extension atoms), so profile,
  level and flags are the encoder's own. `VtEncoder::uses_hardware()` asks VideoToolbox
  whether the session runs on a hardware encoder (a test checks H.264 and HEVC), so a
  requirement dropped by accident could never become a silent software encode.
- `hardware_encode`: one adapter for both codecs. Choosing the format is the opt-in (the
  `hardware_encoding` setting is for H.264). `hevc_available()` (one small hardware session,
  created once) is registered with the new `filmcraft_export::register_format_probe`, so
  `available(Format::Hevc)`, the format list of Export mode and `exportFormats` follow the
  machine. `Format::has_builtin_encoder` says which formats need such a probe.
- `export`: `Format::is_h26x` where H.264 and H.265 behave alike (MP4 or QuickTime, AAC, size
  estimate, summary line). Two-pass VBR is refused up front ("H.265 export has no two-pass
  mode") and HDR sequences export as SDR, because there is no encoder to fall back to.
- Export mode: H.265 shows H.264's multiplexer, audio, metadata and bitrate controls, without
  profile, level, two-pass or the hardware checkbox, and says "Encoder: Hardware".

Apple M1 (8 cores), 1080p25, a 9:29 timeline (camera clip, ProRes 4444 overlays, AAC,
loudness): hardware H.265 at 10 Mb/s 195 s, hardware H.264 at 20 Mb/s 204 s (CPU 1029 s and
1042 s; the same H.264 export took 193 s and 200 s on other runs). It is not faster: the codec
that compresses further is the more expensive one (raw encoders: 166 and 189 frames/s), and
the export is bound by the CPU compositor, not by the media engine.

Quality per bit, 30 s of the same timeline against a ProRes 422 HQ export (average PSNR over
the three planes, SSIM): 20 Mb/s 46.88 dB / 0.9902 (H.264) and 46.91 dB / 0.9903 (H.265);
10 Mb/s 44.96 / 0.9863 and 45.09 / 0.9863; 6 Mb/s 43.72 / 0.9833 and 44.31 / 0.9843; 4 Mb/s
42.91 / 0.9807 and 43.64 / 0.9825; 3 Mb/s 42.37 / 0.9786 and 43.05 / 0.9807. The same picture
at 10 Mb/s and above; at 3 to 6 Mb/s H.265 needs 22 to 31 % less bitrate for the same PSNR.
Both formats therefore keep the same default bitrate. Details in docs/performance.md (HW3).

Tests: tests/hardware_encode.rs gets the HEVC counterparts of the H.264 ones (what is taken
and declined, a round trip through our HEVC decoder with luma PSNR above 30 dB, keyframes, MP4
and QuickTime exports with AAC, ffmpeg / ffprobe reading `hevc` / `Main` / `hvc1` /
`yuv420p` / BT.709, exact sizes, hostile configurations, encoders dropped at any point, the
format list agreeing with the probe, sessions on the hardware encoder); settings_tests.rs
checks names, ids, defaults, the summary, two-pass refusal and the probe; export_ui.rs the
controls H.265 shows and hides.

Stacked on the hardware H.264 encoder (the first commit of this branch).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 14:00:08 -06:00
Dario CarneluttiandClaude Sonnet 5.5 283ea80a62 export: show the estimated time left of a render next to its percentage
Export mode, the status bar and the Progress panel showed "Encoding 37%" and nothing about how
long the rest would take. They now say "37% · 2:05 left".

- `Progress::eta()` (the new `pace` module) is the time left at the job's speed over the last
  15 seconds. Each call is also a reading, so the UI polling on every frame it draws the job
  feeds it, and none of the places that advance `done` had to change. A trailing window follows
  the real speed changes of an export (an intro, a banner, plain camera footage) where an
  average since the start lags; a stall makes the estimate grow instead of freezing it; a long
  idle start (the loudness pass) is not counted as the job's speed; nothing is shown until a
  second of readings has progress, and nothing once the job is done or finished.
- `format_eta`: `45 s`, `2:05`, `1:02:05`, rounded up.
- Shown in the status bar ("Exporting... 37% · 2:05 left", and the `status.job.progress`
  automation label), in the Export queue (on its own line: the column is narrow) and in the
  Progress panel. The engine adds `etaSeconds` (null while unknown) to every job of `jobs.list`
  and to the encoding items of `export.queue.list`; docs/agents.md says so.

Measured on a whole 9:29 export (193 s, Apple M1), asking every 3 s: the first estimate comes
after 6 s; from 30 s on, the predicted end of the export is on average 1.1 s early and never
more than 14.5 s off (7.5 % of the run). The status bar read "9% · 3:00 left" at 21 s and
"97% · 5 s left" at 184 s.

Tests: `pace` (steady job, speed change, stall, idle start, count going back, bounded history,
extreme values, a whole `Progress`), `format_eta`, the engine (`etaSeconds` of jobs and queue
items), and export_ui (status bar label and queue item).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 13:48:52 -06:00
Dario CarneluttiandClaude Sonnet 5.5 4cab6bc5da platform: opt-in hardware H.264 encoding with VideoToolbox
Export encodes H.264 on the CPU with the built-in encoder, which is most of the time
of an export (about two thirds of the busy CPU in a profile of a 20 s export). On
macOS the media engine can do it: a VideoToolbox compression session behind
`filmcraft_export::VideoEncoder`, registered by `filmcraft_platform::register()`.

- `ExportSettings::hardware_encoding` (`HardwareEncoding::Off` | `Auto`; Export > Video >
  Hardware Encoding, `"hardwareEncoding": "off|auto"` in `file.exportMedia`, a boolean is
  understood too), off by default: the built-in encoder's output is byte-identical on
  every machine, a hardware encoder's is not.
- `videotoolbox_encode` (FFI, same containment as the decoder: SAFETY comments, no
  panic across the callback, safe API) and `hardware_encode` (safe adapter and factory).
- The factory declines, so the built-in encoder is used, for other formats, MXF, two-pass
  VBR, HDR, odd sizes, non-square pixels and any configuration VideoToolbox cannot create a
  hardware session for. An encoder that fails mid-export stops it with an error: unlike a
  decoder, it cannot hand a half-written stream to another one.
- No B-frames: on real 1080p footage the quality is the same with and without them
  (+-0.3 dB at equal bitrate) and the bitrate lands closer to the target, and without
  them every frame is presented at its decode time (no composition offsets, no edit list).
- `export::register_encoder` ignores a factory registered twice; `encoder_registered`.

Apple M1, 8 cores, single runs: a 9:29 timeline (camera clip, ProRes 4444 overlays with
alpha, AAC, loudness) exports in 311 s instead of 793 s (2.5x faster); its densest 134 s
in 84 s instead of 395 s (4.7x). The outputs compare at SSIM 0.990 / PSNR 46.5 dB. The encoder
alone runs 200 fps at 1080p (docs/performance.md).

Tests (tests/hardware_encode.rs): what is taken and declined, a round trip through our
software decoder, an export that decodes in our decoder and in ffmpeg, the fallback for
everything hardware declines, exact output sizes, an empty range, hostile configurations
and encoders dropped at any point; the engine's `hardwareEncoding` parameter.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 13:34:51 -06:00
Davi AndreiandClaude Sonnet 5.5 6db932a9ba Windows: NVENC H.264 hardware encoding for Export (work in progress, #30)
NVENC through nvEncodeAPI64.dll (API 12.1, FFI layout checked against the C header by generated
tests), registered as an Export encoder in front of the software one; Export > Hardware encoding
(off by default). Declines two-pass VBR, HDR, MXF, unsupported sizes; B-frames, IDR/GOP, VUI, avcC.
Tests: decode with our decoder (PSNR), pipeline export vs software, decline paths.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 15:41:02 -03:00
Luna161andninso112 16cc1c808c Engine: file.import refreshes the offline-missing list (#110)
file.import created new items but never rebuilt Session::offline.missing.
The UI reads that list to drive the Program slate and the Project
panel 'Media missing' badge, so any re-imported item the project
already listed as missing stayed listed as missing until on_open ran
again (project reopen) or the caller manually relinked.

Add a single crate::relink::refresh(s) call at the end of file.import,
matching the call on_open already makes after a project opens. Closes
the slate/badge symptom without requiring reopen or per-item relink.

The deeper SharedSource cache eviction issue the reporter observed
with media.autoRelink is out of scope here; a follow-up should call
MediaPool::remove for items whose path now resolves before re-insert.

Co-Authored-By: ninso112 <ninso112@pm.me>
Co-Authored-By: Luna161 <Rosenkranzrene@gmail.com>
2026-10-07 18:20:23 +00:00
carlitosteso3andClaude Opus 5.5 d47e794437 sequence.addEdit: selected clips under the playhead are cut, and only they (#164)
Add Edit always cut every targeted track, so with one clip selected it cut
all tracks. As in Premiere, selected clips under the playhead are now cut
and nothing else; with none there, the targeted tracks are, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:37:50 -05:00
carlitosteso3andClaude Opus 5.5 a54bf13ebc Merge upstream main into fix/color-matte-29 (keep both tests at the end of clip_ops_tests.rs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:02:28 -05:00
carlitosteso3andClaude Opus 5.5 dd404568ae media browser: probing a file doesn't read it whole (#157)
Looking at a folder probes each visible file for its properties. A format
no streaming reader takes (AVI, which isn't supported at all, and WAV,
MP3 and the other byte-opened formats) fell through open_reader's
whole-file path: a 290 MB AVI took 518 MB of memory and a full read just
to report that it isn't supported, a 247 MB WAV 500 MB.

- filmcraft-media: open_reader_within(.., max_whole) refuses, after the
  sniffing head, a file no reader opener takes when it is larger than
  max_whole. open_reader is open_reader_within with no limit.
- engine: MediaPool::probe_file uses it with PROBE_WHOLE_FILE_MAX (64 MiB);
  media_browser::probe calls probe_file. Import and playback still use
  open_file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:21:53 -05:00
Brandon Thomas 2647a2d9f8 Merge remote-tracking branch 'origin/main' into pr151
# Conflicts:
#	crates/engine/src/settings_tests.rs
#	crates/engine/src/transcript_tests.rs
2026-10-07 10:44:47 -04:00
carlitosteso3andClaude Opus 5.5 b8fa786cad transcripts: disabled, not failing, in builds without speech-to-text (#89, #97, #98)
- transcript.generate is enabled only with a recogniser: one installed in
  Session::transcriber, or the build's own (feature `whisper`). Otherwise
  describe, command_list enabled_only and the menu report it disabled with
  "speech-to-text is not available in this build" as the reason (#97).
  Transcribe Sequence, which runs it, follows.
- transcript.downloadModel is enabled only with `speech-download` (#98).
- file.import with Automatically transcribe clips on no longer skips
  transcription silently when it can't run: the disabled command's reason
  lands in the import's errors as "transcription: ..." (#89).
- docs/transcripts.md describes the disabled state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 09:14:05 -05:00
Brandon Thomas be5b34b383 Merge pull request #146 from storytold/fix/97-98-89-transcripts-without-speech
Transcripts: builds without speech-to-text say so (#97, #98, #89)
2026-10-07 10:05:02 -04:00
Brandon ThomasandClaude Opus 5.5 621240f761 Transcripts: builds without speech-to-text say so (#97, #98, #89)
- transcript.generate is enabled only with an installed transcriber or the whisper feature; it
  reported enabled and then always failed with "speech-to-text is not available in this build".
- transcript.downloadModel is enabled only with the speech-download feature.
- An import with Automatically transcribe clips (all imported) on reports a
  "transcription: speech-to-text is not available in this build …" error instead of importing
  silently untranscribed.

Fixes #97
Fixes #98
Fixes #89

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 10:04:55 -04:00
carlitosteso3andClaude Opus 5.5 56eaea2fa3 Color Matte: pick the color on creation and change it later (#29)
- File ▸ New ▸ Color Matte… (and Project ▸ New Item) opens a New Color Matte
  dialog: color picker, name, width, height and duration. Called with params
  (CLI, control, MCP) it still creates the matte directly.
- project.matteColor {item?, color}: change a Color Matte's color (the
  selected one by default); every clip of it follows and it undoes.
  Double-clicking a Color Matte in the Project panel opens its color picker,
  as in Premiere.
- media pool: a generator's cache key now holds its parameters, so a matte
  whose color changes (or comes back with undo) is generated again instead of
  the pool serving the first one forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:58:01 -05:00
Brandon ThomasandClaude Opus 5.5 65c6b7365d MCP: render_frame and render_preview leave the playhead where it was (#114)
Both tools are annotated readOnlyHint, but a call with `seconds` moved the active sequence's
playhead (and, with Selection Follows Playhead on, the selection) to that time, in headless and
bridge mode alike.

- headless: Session::render_program_at renders the frame at `seconds` (frame-snapped, as the
  playhead would be) without touching the session.
- bridge: the Program monitor screenshot needs the live playhead there, so the tool records the
  playhead and selection, takes the screenshot and puts both back (the selection only if it changed,
  since selecting also leaves trim mode).

Regression test: rendering at 5 s and 8 s with Selection Follows Playhead on leaves playhead and
selection unchanged and still renders the requested frame (it moved the playhead to 8 s before).

Fixes #114

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 09:55:24 -04:00
justjohn12345andClaude Opus 5.5 8407f233c1 Graphics: point text and paragraph text, as Premiere has them
A text layer is now one of two types, each with Premiere Pro 26's handles:

- Point text (Type tool click): every handle scales the layer about its
  anchor point, both axes alike; the font size is untouched.
- Paragraph text (Type tool drag): the dragged rectangle is a box the text
  wraps in. Handles move that side or corner of the box, the opposite side
  stays, the text re-wraps at the same size. Lines below the box are not
  shown and the bottom-right handle turns into a red plus.
- The anchor point can be dragged with the Selection tool; the layer stays.
  It is picked before anything under it, so a shape dragged by its exact
  centre moves its anchor.
- Properties > Text > wrench opens Text Properties, where Text Layer Type
  switches a layer between the two (graphics.setTextType); the text stays
  where it is.

Shapes keep stretching away from their opposite side.

New: text layer parameter box_height, graphics.newText `box`, and
textType / box / overflow / anchor / scale in graphics.list. A layer saved
before a parameter existed can now be given it with graphics.set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 09:54:26 -04:00
Brandon ThomasandClaude Opus 5.5 0dd66d8aa3 Hardening: hostile MP4 edit lists and cyclic subclip chains (#66)
- codecs: a resampled MP4 audio read scales the edit-list offset to the source rate in i128 and
  saturates the window arithmetic; a hostile media_time overflowed i64.
- engine: media_duration follows a subclip's parent chain in a loop bounded at 16 hops instead of
  recursing; a cyclic or very deep chain in a damaged project overflowed the stack.

Each has a regression test that failed before (overflow panic / stack overflow).

Fixes #66

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 09:44:45 -04:00
Davi AndreiandClaude Sonnet 5.5 5e440a6607 Windows: Media Foundation / Direct3D 11 hardware H.264 + HEVC decode (#30)
New backend in crates/platform (media_foundation/): a Direct3D-aware decoder MFT is driven at the
level of single access units (Annex B), given the process's D3D11 video device through an
IMFDXGIDeviceManager so it decodes with DXVA, and its NV12 / P010 textures are read back through a
staging texture into planar Yuv8 / Yuv16. H.264 Baseline/Main/High and HEVC Main / Main 10, 4:2:0.

- Hardware is verified: non-D3D-aware or async MFTs, streams the GPU's DXVA decoder does not list,
  and system-memory output (Microsoft's MFT software fallback) are declined or fail the stream so
  HybridDecoder continues with our decoder.
- mfplat.dll is loaded at run time so Windows N editions still start.
- register() / registered() / hardware_decoder_for() cover Windows; perf.stats reports the backend.
- Tests (Windows): bit-exact parity with the software decoders at 640x360, 1080p and 2160p
  (resets, seeks, flush), forced mid-stream failure, damaged input, concurrency, repeated init.
- Bench: runs on Windows (ffmpeg on PATH, process/thread CPU clocks), HEVC Main 10 2160p row,
  hardware counters per row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 10:07:40 -03:00
Brandon Thomas cd3fe20ef9 Merge remote-tracking branch 'origin/main' into apv-105 2026-10-07 07:09:54 -04:00
Brandon Thomas 16475596d3 Merge remote-tracking branch 'origin/main' into nest-116
# Conflicts:
#	crates/engine/src/commands.rs
2026-10-07 07:09:20 -04:00
Brandon Thomas d4e438e932 Merge remote-tracking branch 'origin/main' into apv-105 2026-10-07 07:08:58 -04:00
Brandon Thomas b4caf39f5f Merge pull request #117 from justjohn12345/pr/add-tracks-dialog
Sequence: Add Tracks… opens Premiere's dialog (amount, placement, track type)
2026-10-07 07:08:21 -04:00
Brandon Thomas 005745d9ec Merge remote-tracking branch 'origin/main' into apv-105
# Conflicts:
#	crates/codecs/src/mp4.rs
2026-10-07 06:01:14 -04:00
Brandon Thomas 19d2f15148 Merge remote-tracking branch 'origin/main' into nest-116
# Conflicts:
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/panels/timeline.rs
2026-10-07 06:00:36 -04:00
Brandon Thomas 0232c53404 Merge pull request #124 from punkhop/fix/ripple-trim-l-cut-head
Ripple trim: a shorter head after an L cut keeps the earlier sound in place
2026-10-07 05:57:51 -04:00