117 Commits

Author SHA1 Message Date
NetterSilver 3eb2f8487d Clamp negative EDL dissolve/wipe durations to 0 on import (#3827)
Found while testing crates/interchange/src/edl.rs:118 (parse_event_line, num_at parsing as i64) and :498 (import_event, tick_of(l2.dur))

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-01 15:14:50 -05:00
NetterSilver 795e3c640e Reject zero, negative and non-finite FPS in ALE import (#3790)
Found while testing crates/interchange/src/ale.rs:49 (AleDoc::rate) and :193 (row_range)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-01 15:14:31 -05:00
Whatever Industries 5ed4952d43 Add shared custom colors and complete marker editing (#1435)
* M3: Add shared custom colors and complete marker editing

* #1435: refine native colors and range endpoint flags

Use the macOS Colors panel for custom label colors and reuse the established egui picker on other hosts. Preserve draft confirmation, exact sRGB values, native text shortcuts and project ownership. Compact the swatches and center the add control, split range glyphs across their endpoints, localize the new controls, protect newer marker edits from stale dialogs, and cover the behavior with rendered UI and hit-test regressions.

Co-authored-by: Codex <noreply@openai.com>

* #1435: preserve imported palettes and marker double-click ownership

Union saved OTIO palettes across timelines with deduplication, bounded capacity,
first-name preservation and explicit overflow warnings. Keep null authors
consistent with omitted fields, and consume marker double-clicks before the
work-area gesture so opening the editor does not alter project history.

Correct the CSV URL column and schema documentation. Add six regressions for
palette retention/capacity, author handling, and physical overlapping gestures.

Validation: full cargo xtask ci (3666 passed, 45 skipped), 42 rendered UI tests,
and strict workspace/all-target Clippy on Rust 1.99.1.

Co-authored-by: Codex <noreply@openai.com>

* #1435: retain OTIO swatch names and generator labels

Ignore orphan name entries when retaining saved palette names so input ordering
does not hide valid names after the first 64 entries. Keep names bounded by the
retained palette and preserve existing valid names.

Include generator preset labels alongside custom label colors in OTIO metadata.
Add regressions for orphan-prefixed name maps and three generator kinds with
preset/custom colors through actual export/import round trips.

Validation: both regressions failed before fixes; all 72 interchange tests and
full cargo xtask ci pass (3668 tests, 45 skipped), plus strict workspace/all-target
Clippy on Rust 1.99.1.

Co-authored-by: Codex <noreply@openai.com>

* #1435: preserve distinct OTIO generator item identity

Use exported FilmCraft item IDs for generator media identity so identical
content can retain distinct source names, labels and custom colors. Repeated
references to one source item still share a single imported media entry.

Preserve content-based fallback for missing or invalid item IDs. Cover black
video, bars and tone, and color mattes with distinct items/shared references,
plus legacy and malformed identity metadata.

Validation: distinct-generator regression failed before fix; legacy compatibility
passed. All 74 interchange tests and final full cargo xtask ci pass (3670 tests,
45 skipped); strict workspace/all-target Clippy also passes on Rust 1.99.1.

Co-authored-by: Codex <noreply@openai.com>

* #1435: align Edit Marker layout and add safe marker navigation

Give the editor a wide aligned form, larger Notes area, side action column,
and grouped palette with vertical marker types. Expand the Web Link field
with the form and keep actions accessible in small windows.

Previous/Next saves valid edits as one Undo step and stays with the original
sequence/source owner, including coincident markers. Invalid drafts stay open;
unchanged navigation and Done add no history. Keep native/shared color pickers,
Author, Notes, URL validation and immediate marker creation.

Validation: all 45 rendered sequence/marker UI tests pass, including five new
navigation/owner/validation/layout regressions. Native macOS layout, picker
Cancel and exact single-step Undo restoration verified. Full standard cargo
xtask ci passes 3675 tests (45 skipped), doctests and all remaining gates;
strict Rust 1.99.1 workspace/all-target Clippy also passes.

Co-authored-by: Codex <noreply@openai.com>

* #1435: refine Edit Marker proportions and action alignment

Narrow the dialog to 680 logical pixels and keep the core order Name,
Time/Duration, Notes, Color and marker type. Place Author after the type-specific
options as secondary metadata. Preserve our palette, custom picker and range controls.

Use consistently sized, centered rounded action buttons with a primary Done action.
Shorten Delete Marker to Delete and remove the navigation helper and unused translations.
Keep the existing navigation save/validation/Undo semantics and automation IDs.

Validation: all 45 rendered marker/sequence UI tests pass, including small-window
action accessibility. Actual macOS layout and no-op Previous/Next/Cancel verified.
Full standard cargo xtask ci passes 3675 tests (45 skipped), doctests, formatting,
Clippy, translations, layers, assets and 49 WASM crates. Strict Rust 1.99.1
workspace/all-target Clippy passes separately.

Co-authored-by: Codex <noreply@openai.com>

* #1435: tighten marker layout and validate text editing

Use a narrower aligned form with Author below Name, bounded scrolling Notes,
left-aligned colors, centered actions and an inline Web Link URL/open icon.
Remove the legacy Flash Cue choice while preserving imported marker types.

Constrain timecode typing/paste to complete numeric colon-separated fields,
validate drop-frame labels and frame bounds, and disable Done for invalid timing.
Preserve high-frame-rate timecodes and point-marker semantics when Duration is
unchecked. Route native/menu clipboard and text Undo commands to focused fields;
explicit engine commands retain project Undo and selection semantics.

Validation: standard cargo xtask ci passes 3679 tests (45 standard skips),
doctests, fmt, strict workspace/all-target Clippy, i18n, layers, 69 assets and
49 WASM crates. Also passed 48 rendered marker/sequence tests and strict Rust
1.99.1 Clippy. Actual macOS clipboard, bounded Notes, point save and exact Undo
were checked. Windows/Linux desktop interaction remains pending.

Co-authored-by: Codex <noreply@openai.com>

* M0: Wait for frame-worker recovery before asserting

Do not treat a nonempty request queue as proof of worker death in the
panic-recovery regression. Keep waiting for all injected faults and enough
post-fault completions, then drain the queue without adding more seeks.
Retain the exact fault-count, successful-recovery and empty-queue assertions
and bound the wait with a two-minute deadline.

The inherited test failed twice in independent fork CI. It did not reproduce
locally: baseline passed 20 normal and 10 delayed-report runs; the repair
passed 50 normal and 10 delayed-report runs. No product or marker UI changes.

Validation: standard cargo xtask ci passed all 4485 tests (52 standard skips),
doctests, formatting, strict workspace/all-target Clippy, i18n, layers,
69 assets and 49 WASM crates.

Assisted-by: OpenAI Codex

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <codex@openai.com>
2026-11-01 03:04:14 +01:00
aiongg 8a01ebd46a Integration: #2847 #2282 adapt the interchange tests to the 7-argument clip helper and boxed MediaClip 2026-10-31 03:39:23 +00:00
aiongg 1e62308c7b Merge PR #3462: interchange: reject overflowing EDL fields without leaking event metadata
# Conflicts:
#	crates/interchange/src/edl.rs
2026-10-31 02:13:54 +00:00
aiongg 104005be04 Merge PR #3052: Fix FCPXML slideshow imports and missing-media relinking (#2837)
# Conflicts:
#	crates/interchange/README.md
2026-10-31 02:13:42 +00:00
aiongg e541a31fb3 Integration: #2847 sort the support import for rustfmt 2026-10-31 02:13:35 +00:00
aiongg b9eb9fbef4 Merge PR #2847: fix(interchange): bound subclip parent resolution to prevent cycle stack overflow
# Conflicts:
#	crates/interchange/src/common.rs
2026-10-31 02:13:30 +00:00
aiongg 0e9d0b10f1 Merge PR #2403: #2195: relative media URLs in imported timelines are percent-decoded
# Conflicts:
#	crates/interchange/src/fcp7.rs
#	crates/interchange/src/otio.rs
2026-10-31 02:13:17 +00:00
aiongg 20ea282ecc Merge PR #2284: fix(interchange): a huge duration times a speed saturates instead of overflowing
# Conflicts:
#	crates/interchange/src/common.rs
2026-10-31 02:13:06 +00:00
aiongg 4bfde06a5a Integration: #2282 port tests to tests/it layout 2026-10-31 02:12:17 +00:00
aiongg 124c6ff362 Merge PR #2282: fix(interchange): absurd frame rates and start timecodes are bounded on import
# Conflicts:
#	crates/interchange/src/fcp7.rs
2026-10-31 02:12:06 +00:00
aiongg 406f633aca Merge PR #2252: fix(interchange): FCPXML zero frameDuration no longer divides by zero on export
# Conflicts:
#	contributors/people.toml
#	crates/interchange/README.md
2026-10-31 02:11:54 +00:00
aiongg 8fce33e4e9 Merge PR #2248: Ignore out-of-range AUD channels in EDL import
# Conflicts:
#	crates/interchange/src/edl.rs
2026-10-31 02:11:41 +00:00
aiongg d7979f74c0 Merge PR #2127: Ignore unusable M2 speeds in EDL import 2026-10-31 02:11:35 +00:00
NetterSilver e6f649d4a7 Reject non-PCM WAVE_FORMAT_EXTENSIBLE files in parse_wav (#3344)
Found while testing crates/interchange/src/wav.rs:50

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-30 01:20:32 -04:00
Dylan M. Taylor 3e723263f5 Refactor interchange for strict Clippy checks 2026-10-28 04:36:48 +00:00
SevenxDev 7b25d0586b interchange: keep rejected events as grouping boundaries 2026-10-27 21:43:01 +01:00
SevenxDev 026139d982 interchange: isolate metadata after rejected EDL events 2026-10-27 21:41:44 +01:00
SevenxDev 232fe3054e fix(interchange): reject overflowing EDL timecode fields 2026-10-27 21:34:46 +01:00
aiongg 1564d36dd1 refactor(interchange): convert mod.rs files to <module>.rs 2026-10-26 18:15:16 +00:00
aiongg 48a6d6a08e Merge #2653: Stop writing an empty ref for FCPXML titles and adjustment layers (#2638)
# Conflicts:
#	crates/interchange/src/fcpxml.rs
2026-10-25 12:35:10 +00:00
aiongg 9cfbf1e2b0 Merge #2287: fix(interchange): OTIO export stops writing nested-sequence copies past a limit
# Conflicts:
#	crates/interchange/README.md
2026-10-25 12:34:09 +00:00
irgendwasmitfelix 74ca8d7d17 Merge main into fix/2195-decode-relative-media-urls
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:49:26 +02:00
Michael Butler cbcdb560d4 #2837: import asset-backed FCPXML video clips
Import standalone video elements as picture-only clips, retaining media
paths, timing, adjustments, markers, transitions and connected placement.
Warn when a video references an unsupported generator or source.

Add synthetic importer and engine regressions covering zero-duration still
assets, missing-media relinking, save/reopen, undo/redo and rendered pixels.
Document the FCPXML 1.10 specification and supported behavior.

Validation: interchange tests and the engine slideshow regression pass,
as do scoped Clippy, formatting, layers, assets and WASM. Full CI on Rust
1.95 stops at the existing ProRes nonminimal_bool lint; unrelated CI
repairs are excluded from this change.

The reporter's original XML imports all 55 slides, 54 transitions and
55 media items in a temporary local check; reporter files are not included.

Fixes #2837

Co-authored-by: Codex <noreply@openai.com>
2026-10-24 10:13:02 -04:00
Dylan M. Taylor 552f008d27 security(audio): bound sequence sample rates and AAF/OMF export channel buffers (#3082)
* security(audio): bound sequence sample rates and AAF/OMF export channel buffers

* fix(engine): bound export audio channels and samples independently
2026-10-24 05:28:22 -04:00
Dylan M. Taylor 15ca07300b security(interchange): harden FCPXML cycle detection, XML DTD entities, and imported formats (#3079) 2026-10-24 05:14:30 -04:00
Earl Co 355a2b5538 security(aaf): bound materialized streams (#2934) 2026-10-24 04:49:36 -04:00
Earl Co a8d074247a security(omf): bound metadata expansion and traversal (#2946) 2026-10-24 04:49:07 -04:00
aiongg 0a881266c8 CI: xtask ci in the dev profile, one test binary per crate, nextest (#2860)
* Tests: one integration-test binary per crate

Each crate with several tests/*.rs files keeps them as modules of tests/it/,
so cargo links one test executable per crate instead of one per file. Files
that change process-wide state or time a workload stay binaries of their own.

* Profiles: line tables for workspace crates, no debug info for dependencies

* Windows ARM64: nightly and on release pushes instead of every push to main

* xtask ci: clippy and tests in the dev profile

The release profile's thin LTO and single codegen unit made linking the test
binaries take most of the gate. The codec and audio crates stay at opt-level 3
in the dev profile, so their tests run at release speed.

* xtask ci: nextest, doctests, no examples, one wasm check

Tests run with cargo nextest when it is installed (cargo test otherwise) and
build with --lib --bins --tests, so examples aren't linked; doctests run
separately. The wasm check keeps going past the first failing crate. VP8
fixture generation locks across processes, since nextest runs each test in
its own.

* VP9 synthetic fixtures: write aside and rename

A test in another process could read a fixture ffmpeg was still writing.

* Fuzz tests: a third of each sweep by default, all of it with FILMCRAFT_FUZZ_ITERS

Every seed, mutation kind and thread count still runs. Header bytes each get
one overwrite value and two bit flips in rotation, so every value and bit
index still occurs across the region. FILMCRAFT_FUZZ_ITERS=N runs the whole
sweep plus N random rounds per seed, as before.

* Platform fallback tests: one failure point per hybrid path

The failure-anywhere test injected a failure at four points around every
restart point; the hybrid's paths differ only in the first GOP, around the
second and last restart points, at the end and never. The replay test needs
the first two GOPs.

* Export tests: shorter ranges with the same checks

The bitrate test measures 0.5–1.5 s, where each preset reaches its own
target (on 0–3 s the simple demo left two presets at the same rate). The
every-preset test exports three frames per preset.

* Audio tests: effects in parallel, a table-driven FFT for the Opus quality metric

The extreme-settings test keeps both settings and the 40 s tail for every
effect, one thread each. The Opus quality metric's FFT builds its twiddles
once and allocates nothing per transform (same algorithm and summation order).

* CI: dev-profile gate with nextest

xtask ci no longer builds with --release, so the release-profile overrides
go; nextest is installed for the test step.

* CI: apt packages through the cached install-apt-packages action

* Review fixes: fuzz tests with a panic hook in their own binaries, loud fixture lock

The fuzz tests of av1, h264, hevc, mpeg2v, vp8 and vp9 count panics through
a process-wide hook, so a panic in another test of the same process would
fail them; they are binaries of their own again. The VP8 fixture lock fails
the test instead of skipping it silently, and the full-sweep doc comments say
how many truncation points the full run samples.

* Fuzz tests: rotate the default subset per run (FILMCRAFT_FUZZ_SEED replays one)

* platform tests: decode-switch tests in their own binaries, one NVENC lock
2026-10-23 13:34:16 -04:00
Earl Co f554fdcb4f fix(interchange): confine embedded audio output names (#2956) 2026-10-23 10:54:02 -04:00
Earl Co 199b70c1d1 security(interchange): bound imported track indices (#2910) 2026-10-23 10:51:39 -04:00
skylar taylor-barrick 8e294e7d2a Merge branch 'main' into fix/h846-xml-writer-does-not-escape-tab-n 2026-10-22 22:30:50 -06:00
justdxvid d8fc823126 style(interchange): format and resolve clippy::octal_escapes in cyclic_subclips test 2026-10-22 21:43:45 -05:00
justdxvid 0c0eefe7ac test(interchange): assert chained subclip presence in cyclic timeline export 2026-10-22 15:55:19 -05:00
justdxvid d8ff31ff57 fix(interchange): bound subclip parent resolution to prevent cycle stack overflow
Bounded item_path, item_media, and base_item in interchange helpers
and ale::export_items with a 16-step bound matching crates/project.
Cyclic subclip chains (self-referencing or mutual cycles) and overly
deep chains now terminate safely without causing a stack overflow abort.

Closes #2788
2026-10-22 15:45:53 -05:00
NetterSilver 47e5f63ded Keep file URL hosts that only start with "localhost"
Found while testing crates/interchange/src/common.rs:68

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 08:14:56 +00:00
MarekSpodBiedry d1b45e960b Stop writing an empty ref for FCPXML titles and adjustment layers (#2638) 2026-10-21 10:18:33 +02:00
NetterSilver ae313a8801 Escape tab/newline/CR in XML attribute values
Found while testing crates/interchange/src/xml.rs:7 (escape, used by start_tag for attributes)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:58:22 +02:00
Wbunker 4f2c9189fe fix(interchange): bound start timecodes in AAF and OMF imports too
CodeRabbit review: import_with returns early for AAF and OMF, so the
shared bound_start_timecodes pass never ran for them and an AAF with an
absurd start timecode kept it. The AAF and OMF importers now run the
pass themselves (the test fails without it for AAF; OMF carries no
sequence start timecode).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 15:33:55 -04:00
Wbunker 81addac030 docs(interchange): say why scaled() is the only place durations are multiplied
Review feedback: note that Tick has no Mul and its Add/Sub saturate, so
this one saturating multiply closes the whole class, and why it
saturates at the i64 bounds rather than Tick::MAX.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 13:55:16 -04:00
Wbunker ad30f1e7fa fix(interchange): report a clamped FCP7 timebase and a replaced OTIO rate
Review feedback: the FCP7 timebase clamp and the OTIO rate fallback happen
while parsing, so the post-import pass saw a plausible rate and said
nothing; only the FCPXML path warned. Each importer now checks the
document once and warns when a timebase or rate is beyond the limit
(OTIO's warning names audio sample rates such as DSD, which are above it).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 13:54:50 -04:00
irgendwasmitfelix f6e15705ed #2195: an OTIO import test for a relative, percent-encoded target_url
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 07:32:28 +02:00
irgendwasmitfelix a5bc933282 #2195: relative media URLs in imported timelines are percent-decoded
FCPXML src, FCP7 pathurl and OTIO target_url are URLs, but only file://
ones were decoded: a relative src="Clip%20A.mov" looked for a file
literally named Clip%20A.mov. Relative URLs are now decoded too (plain
names from EDL / AAF are not), and the OTIO exporter writes its relative
target_url percent-encoded so a name with % or spaces round-trips.
The web relink fallbacks the issue also asks for stay open.

Refs #2195

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:53:53 +02:00
Brandon Thomas a06de0534a Merge pull request #1937 from Ni-zav/fix/media-header-overflow-s20261014t1610z
Saturate crop and field-height math on hostile Matroska/MXF/AAF headers
2026-10-18 03:20:34 -04:00
Wbunker c8be6ceb42 docs(interchange): move the frame-rate note to its own section so it stops colliding with other README edits
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 12:59:18 -04:00
Wbunker 6cb14e7c44 fix(interchange): OTIO export stops writing nested-sequence copies past a limit
OTIO has no references, so every use of a nested sequence is written in full.
Compound clips that each hold several uses of the next make that exponential:
a 3 KB FCPXML whose media nest 12 deep with 3 uses each imports as 14 items and
then makes the OTIO export run for minutes and grow to gigabytes (depth 5 is
216 MB, about 7x more per level), until memory runs out. The other formats
reference nested sequences and stay instant.

The exporter counts clips written inside nested sequences; after 25 000 (about
250 MB of JSON, far above real projects) further nested sequences are written
as gaps, with one warning. The depth-12 document now exports in seconds under
a 4 GB memory cap.

Regression tests lower the limit to keep the document tiny: without the check
all 112 stacks are written; an ordinary project is untouched.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 09:16:46 -04:00
Wbunker a117e80032 fix(interchange): a huge duration times a speed saturates instead of overflowing
scaled() multiplied a duration by an integral speed with a plain `*`. An EDL
with a record-out timecode of 2147483647 hours and an `M2 ... 72.0` speed
change overflowed it (attempt to multiply with overflow, a panic in debug
builds, wrapped time in release). The same function serves the FCP7, FCPXML
and OTIO importers, so mutation fuzzing hit it from all four text formats.

The multiply saturates, like Tick's own arithmetic.

Regression tests: the EDL imported and exported in every format
(tests/edl.rs) and scaled() unit assertions; both panic at common.rs scaled
without the fix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 08:54:50 -04:00
Wbunker c1e51b4aa1 fix(interchange): absurd frame rates and start timecodes are bounded on import
An importer accepted any positive frame rate and any start timecode. A rate
near i64::MAX per second (an FCPXML frameDuration of 1/9223372036854775807s,
an FCP7 timebase of 9223372036854775807) made frame_at clamp to i64::MAX, and
frames_round overflowed adding 1 on export as soon as the sequence had a start
timecode or a marker; an NTSC timebase overflowed multiplying by 1000 on
import. An FCP7 <frame>18446744073709551615</frame> start timecode saturates
to i64::MAX and overflowed the EDL export's rec_frames.

Rates are limited to 1 000 000 per second (far above video and the 192000 Hz
OTIO audio uses, far below the overflow): the FCP7 timebase is clamped, an
OTIO rate beyond it uses the default, and one pass after every import resets a
sequence or media rate beyond it, and start timecodes beyond 360 000 000 000
frames, with a warning in the report. The pass covers every format, so
fcpxml.rs is not touched.

Regression tests: tests/rates.rs (FCPXML absurd frameDuration, every export
format) and tests/fcp7.rs (absurd timebases with and without ntsc, absurd
start timecode). With the limits lifted they panic in frames_round, in the
NTSC multiply and in edl.rs rec_frames.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 08:46:06 -04:00
NetterSilver 217511e938 Ignore out-of-range AUD channels in EDL import
Found while testing crates/interchange/src/edl.rs:211-216 (parse, AUD handling; consumed by Ctx::track_index/lay at :341-380 and Builder::ensure_tracks in common.rs:411)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 05:38:46 +02:00