Files
Jonathan Cardoso 96d8e22c2d Image-sequence export reports the stills it wrote, not the path it was given (#4018)
* fix(export): an image-sequence export reports the stills it wrote

An export to `a.png` as an image sequence writes `a000.png`, `a001.png` and so on, but the
job's result, `file.exportMedia`'s `path` and the CLI's "exported" line all named `a.png`,
which does not exist.

The report now names the first still, the name `file.import` and `probe --image-sequence`
take for a sequence. `file.exportMedia` and `export.quick` name it as `path` up front, from
the frame count `export.resolve` uses, and add `lastPath`. The CLI prints both. A single
file keeps its path.

Fixes #4009

* fix(automation): a cancelled image-sequence export is cleaned up by its stills' name

Since `file.exportMedia` names an image sequence's first still as `path`, the MCP cancel
clean-up took `a000` as the stem: it left `a001.png` and every later still, and the `a.srt`
sidecar, behind. The result now also carries `basePath`, the name the stills are numbered
from, and the clean-up goes by it.

* fix(automation): an image-sequence cancel clean-up keeps the base name and finds .jpeg stills

An image-sequence export numbers every still, a single frame included, so a file under its base
name (`a.png`) is never its own: cancelling one deleted a user's `a.png` if it was saved during
the export. The stills of a `.jpeg` sequence were left behind instead, since the extension was
read as a format name, and `jpeg` names Motion-JPEG there; `file.exportMedia` keeps `.jpeg`
for a JPEG sequence.

* fix(automation): a cancelled image sequence removes only the stills it numbered

The clean-up matched any `<stem><3+ digits>.<ext>` written during the export, so cancelling an
export to `a.jpeg` could delete `a1000.jpeg`, the first still of an export to `a1.jpeg` in the
same folder. It now takes the digits and the last index from the result's `lastPath` and removes
only those names: `a000.jpeg` to `a120.jpeg` for a 121-frame export.
2026-11-03 00:03:30 -05:00
..

FilmCraft export

The export pipeline renders sequence frames, encodes them and writes the selected container. See architecture for formats, settings, presets and queue behavior.

MP4 upload compatibility (#3459)

MP4 video exports finalize with the existing ISO BMFF muxer's fast-start layout: ftyp, then moov metadata before mdat media data. This lets progressive players and upload services read the track metadata before downloading the media. The choice follows the container, so it applies to H.264, HEVC and AV1 MP4 exports with software or hardware encoders. QuickTime MOV and audio-only export layouts are unchanged.

The muxer shifts media in place with a bounded copy buffer and updates sample offsets; it does not load the entire disk export into memory. Disk outputs therefore need read and write access. Callers supplying ExportSettings::output_file for MP4 video must reserve a readable, writable file. Buffered writes are flushed before reads through that same handle. In-memory download sinks use their existing seekable byte buffer. Read, write and seek failures propagate as export errors.

Animated GIF transparency

Animated GIF supports the existing ExportSettings::alpha option (#3396). In Export mode, choose Animated GIF and enable Video ▸ Include Alpha Channel. Engine and MCP exports use file.exportMedia with "format": "gif", "alpha": true; queue entries and presets use the same settings. The default remains off, flattening the picture over black.

GIF has binary transparency, not partial alpha. After rendering and scaling, RGBA8 alpha 0–127 becomes transparent and 128–255 becomes opaque. Visible pixels retain their straight RGB color before palette quantization; transparent pixels share a cleared color. This avoids darkening partially transparent edges, but cannot preserve smooth translucent edges or shadows. The UI displays the threshold alongside the checkbox.

The existing image GIF encoder writes the animation; no codec implementation is added. Format reference: GIF89a, 31 July 1990, §23 Graphic Control Extension.

settings_tests::gif_alpha_threshold_and_disposal_roundtrip exports synthetic moving stripes with alpha 0, 127, 128 and 255, followed by an empty frame. Decoding verifies transparency, color, frame timing and removal of previous-frame pixels, with alpha both enabled and disabled. When FFmpeg is installed, its independent decode must agree on every pixel's alpha and opaque color. settings_tests::gif_palette_quantization_preserves_isolated_transparency_and_dark_pixels checks two frames with more than 256 distinct post-conversion RGBA colors, forcing palette reduction. A single transparent pixel moves between frames, surrounded by opaque black and near-black pixels. Decoded alpha must match exactly; opaque RGB channels allow at most 16/255 quantization error. This test uses the Rust decoder and needs no FFmpeg installation.

Run the GIF export tests with cargo test -p filmcraft-export --lib gif_ -- --nocapture. Run all export unit tests with cargo test -p filmcraft-export --lib.

filmcraft-ui-egui's export_ui::gif_export_exposes_binary_alpha_option uses the control channel to select GIF, toggle alpha and inspect the explanatory label. Set FILMCRAFT_UI_SNAPSHOT_DIR to render its Export-mode screenshot.