Commit Graph

16 Commits

Author SHA1 Message Date
Brandon Thomas e222d025cf Merge transcripts (M10.5) and clip/project commands (M3.10); clip schema renumbered to v10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:34:12 -04:00
Brandon Thomas c3d2f740d0 M3.10: Edit/Clip/File menu commands — labels, Paste/Remove Attributes, subclips, frame holds, time interpolation, audio options, Replace With Clip
Edit: Label ▸ 16 colours + Select Label Group, Paste Attributes… (⌥⌘V, with
Scale Attribute Times), Remove Attributes…, Select All Matching, Remove
Unused, Consolidate Duplicates. File: New ▸ Sequence From Clip, Bin From
Selection (⇧B), Offline File…; Close Project (⇧⌘W, asks to save), Save All.
Clip: Make Subclip… (⌘U, Restrict Trims), Edit Subclip…, Modify ▸ Audio
Channels… (⇧G) / Timecode…, Video Options ▸ Frame Hold Options…, Add Frame
Hold (now splits at the playhead), Insert Frame Hold Segment, Field
Options…, Time Interpolation (Frame Sampling / Frame Blending / Optical
Flow, blending rendered on CPU; optical flow falls back to blending),
Fit to frame / Fill frame; Audio Options ▸ Breakout to Mono, Extract Audio
(WAV next to the project, imported); Replace With Clip ▸ From Source
Monitor / Match Frame / From Bin. Hold Filters: effects animate through a
hold unless set. Menus support nested submenus (egui and native) and follow
Premiere's order. Dialogs in panels::clip_dialogs with automation ids and
ui.set {clipDialog}.

Project schema v9 (no-op migration v8→v9, v8-subclip fixture): clip
time_interpolation / hold_filters / field_options / source_channels,
Interpretation.audio_channels, Subclip.restrict_trims.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:28:37 -04:00
Brandon Thomas fc18a6962e M10.5: Text-based editing / transcripts
- crates/speech (L2): Transcriber trait, FixedTranscriber, Whisper model catalogue (pinned,
  SHA-256 verified) with optional downloader (feature `download`: ureq + rustls + RustCrypto),
  pure-Rust Whisper inference on candle with word timestamps (feature `whisper`), log-mel, VAD
  word tightening, MFCC speaker clustering. Both features off by default and for wasm.
- project: per-media-item transcripts (Project::transcripts); schema v9 with a no-op v8->v9
  migration and a v8 fixture.
- edit::transcript: sequence transcript derived from clip transcripts, search, paragraphs,
  word ranges, pause/filler detection, ripple delete of many ranges, caption layout.
- engine: transcript.* commands (generate, set, delete, inspect, search, select, extract, lift,
  renameSpeaker, removeFillers, removePauses, createCaptions, models, downloadModel);
  Session::transcriber for hosts/tests; features `whisper` / `speech-download` (also on the
  desktop and CLI apps). Without whisper, transcript.generate fails with a clear error.
- ui: Text panel Transcript tab (Transcribe button, speaker paragraphs of clickable words,
  Shift+click selection -> In/Out, extract/lift/remove fillers/create captions, search) with
  text.transcript.* automation ids; headless UI test.
- docs/transcripts.md, crates/speech/README.md, architecture and project-files updates.
  whisper-tiny measures 12.2% WER over 797 words on local samples.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:25:49 -04:00
Brandon Thomas a9278c81cc Merge M12 (multicam) into M5.5–7 (masks/presets): schema v7 = masks, v8 = multicam (WIP: CI not yet run)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:55:13 -04:00
Brandon Thomas d9eaa0fe8f M5.7: effect presets — save (scale / anchor to in / anchor to out / without keyframes), apply (undoable; intrinsics replaced, geometry scaled to the target frame), built-in presets, user library in the data dir, JSON import/export; Presets bin in the Effects panel (drag onto clips), Save Preset dialog
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:44:55 -04:00
Brandon Thomas 650a266352 M12.2: multicam and sync — Synchronize (In/Out/timecode ± hours/clip marker/audio), Merge Clips, Create Multi-Camera Source Sequence (camera names, audio camera 1/all/switch, Processed Clips bin), multi-camera clips (angle render, Switch Audio mix, GPU plan passthrough), switch angle / Ctrl+N cut / keys 1–9, live recording as one undo step, Enable/Flatten/Edit Cameras, audio follows video; nested sequence audio now plays; project schema v7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:08:45 -04:00
Brandon Thomas fbaf487237 M5.5: effect masks (ellipse / 4-point polygon / pen Bezier) with keyframable path, feather, opacity, expansion, inverted, modes; opacity masks; WGSL coverage parity; on-monitor editing
- project:😷 MaskPath (cubic Bezier, clip pixels) as ParamValue::Path (vertex-wise keyframe interpolation), Mask, MaskMode, TrackMethod; schema v7 (no-op migration)
- render:😷 analytic antialiased coverage via signed distance + feather falloff in linear light; masked effect = lerp(original, effected, coverage); opacity masks before Motion; adjustment layers
- gpu::GpuMask: same coverage + mix in WGSL compute (max |cpu-gpu| ~3e-6)
- engine masks.* commands (add/remove/set/moveVertex/translate/addVertex/removeVertex/toggleVertexSmooth/select/list); keyframe commands take "mask"
- UI: Effect Controls mask rows; Program monitor overlay (vertices, handles, feather/expansion handles, body drag, pen tool) with automation ids
- goldens: mask_blur, mask_color, mask_inverted, mask_opacity

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:48:47 -04:00
Brandon Thomas 9f4208bb08 Merge main into M11.7–9 (relink, proxies, project manager): GopCache keeps main's continuation algorithm with a same-thread reentrancy guard (nested rayon requests decode privately instead of deadlocking); ProRes export combines profile choice with HDR signalling; project schema v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:19:19 -04:00
Brandon Thomas b66074a77c M11.8: Project Manager (collect files, consolidate and transcode)
- file.projectManager {destination, mode: collect|consolidate, sequences?,
  excludeUnused, handles, preset, includeProxies, includePreviews, dryRun, wait}.
- Collect copies every file the chosen sequences use (nested sequences and subclips
  followed), optionally proxies and render previews; consolidate writes only the used
  media range plus handles with a transcode preset and re-bases media time so edits,
  keyframes and markers stay put. Unused clips can be excluded.
- Dry run reports original / resulting disk space and the file plan; the copy runs
  as a job and writes the project last with the new paths and fingerprints.
- UI: Project Manager dialog (sequences, mode, preset, options, destination,
  Calculate → sizes) with automation ids pm.*.
- Tests: collected project opens with its media moved away and renders the same
  frames; consolidate trims to used range + handles and matches (>38 dB) the original.
- ROADMAP: M11 done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:13:56 -04:00
Brandon Thomas a1cc0a6838 M11.9: proxies (create / attach / toggle / reconnect) and ingest settings
- media.createProxies: background job (jobs.list progress) transcoding with our own
  encoders — ProRes 422 Proxy ¼/½, ProRes LT ½, H.264 ¼/½ (audio included) — into
  <media dir>/Proxies or a chosen folder; attached when the job finishes
  (Session::poll_persistence applies finished media jobs).
- media.attachProxies (duration ±1 frame and frame rate must match; size/aspect may
  differ), media.detachProxies, media.reconnectFullRes, media.toggleProxies
  (Preferences ▸ Media ▸ enableProxies; bumps the view revision without dirtying the
  project), media.proxyPresets.
- Proxy frames are smaller; ProxySource reports the original size and the compositor
  derives its pixel scale from the frame, so Motion and effects render the same
  picture (44 dB PSNR vs full res with Motion + Gaussian Blur, 48 dB at ½).
  Export always renders full resolution.
- ExportSettings::prores_profile (proxy / lt / standard / hq).
- project.ingestSettings + file.import: copy (fingerprint-verified), transcode,
  create proxies, copy and create proxies.
- UI: Toggle Proxies button on both monitors (lit when on), Create Proxies dialog,
  Project panel Proxy submenu and P badges.
- Measured (one core, 4K H.264 testsrc2): CPU composite 201 → 35 ms/frame at ¼ with
  ProRes Proxy ¼, 277 → 131 ms at ½ with H.264 ¼; decode 9.2 → 0.7 ms with H.264 ¼.
- Tests: create+attach+toggle same composite modulo scale, attach checks, export
  ignores proxies, ingest copy + proxies, ignored 4K benchmark, headless UI test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:06:56 -04:00
Brandon Thomas a9af6fac71 Merge M8 (LUTs, colour management, Colour Match) into M4.6 playback; project schema v5 for colour management; architecture §5.3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:05:55 -04:00
Brandon Thomas b98c886133 M11.7: offline media and relinking
- Missing media no longer fails: opening a project scans for missing files
  (`media.findMissing`, `file.open` → missingMedia). Missing clips render our own
  offline slate (render::offline: red striped field, warning triangle, "MEDIA NOT
  FOUND", file name, hint) with silent audio, and the Link Media dialog opens.
- Media identity: imports record the size and a fast content fingerprint (hash of
  size + first/last MiB) in MediaClip::identity (optional field, older builds
  ignore it, no schema bump).
- media.relink checks file name / extension / fingerprint ("Clip ID") / duration /
  media start / frame size+rate+channels, refuses a mismatched fingerprint unless
  forced, supports Align Timecode (Project::shift_media_time), and relinks the
  others automatically by the derived folder remap (Windows/macOS separators).
- media.autoRelink {from,to} | {folder}, media.search, media.replaceFootage,
  media.makeOffline {deleteFiles?}, media.offlineAll, media.status, media.linkMedia.
- Media pool caches sources per reference (path, offline flag), so relink, Make
  Offline and undo take effect at once; proxy-aware resolution and a full-res
  provider that export uses.
- codecs::gop no longer holds its lock while decoding: rayon decoders inside a
  parallel export could steal another frame of the same source and deadlock.
- UI: Link Media dialog (rows, match options, folder search with preview, Locate…,
  Link, Offline, Offline All, Cancel), Make Offline dialog, Project panel offline
  markers and context menu, media thumbnails keyed by media reference.
- Tests: moved media opens offline (slate rendered) and relinks others by folder,
  prefix remap and folder search, fingerprint mismatch refused, Make Offline,
  headless UI test of the dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:00:35 -04:00
Brandon Thomas 59629eae0f M7.4: project schema v4 for Essential Sound (older builds refuse instead of silently dropping the settings on save)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:42:02 -04:00
Brandon Thomas 07c584f1a2 Merge M3.2/M3.11 (Trim Monitor, dynamic trimming, keyboard shortcuts) into M10.1–2; project schema v3 for graphic clips so older builds refuse them cleanly
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:07:12 -04:00
Brandon Thomas 516978b4d4 M11.2: auto-save ring + crash-recovery journal
- Engine autosave module: Preferences (autoSave.enabled/intervalMinutes=5/maxVersions=20/
  saveCurrentProject/recoveryJournal/recoveryIntervalSeconds), persisted in the per-user data
  dir; prefs.get/set/reset commands.
- Background worker (filmcraft-autosave thread): the UI thread only hands over the Arc<Project>
  snapshot; JSON encoding and atomic writes happen on the worker. Premiere-style auto-saves to
  <project dir>/Auto-Save/<name>-YYYY-MM-DD_HH-MM-SS.fcproj (local time), only when changed,
  rotated to Maximum Project Versions, optionally also saving the project.
- Recovery journal: debounced snapshot of unsaved changes in <data>/Recovery/<session>/ guarded
  by an OS file lock; on launch, sessions whose lock is free and that left a snapshot are offered.
  Clean quit removes the journal (or keeps it, marked cleanExit, if changes were unsaved).
- Commands: file.recover, file.discardRecovery, file.recoveryList, file.autoSaveNow,
  file.autoSaveStatus, file.listAutoSaves.
- UI: Preferences ▸ Auto Save page (Edit ▸ Preferences, Cmd+,; FilmCraft ▸ Settings… on macOS),
  "Recover Unsaved Changes" dialog at startup, Revert confirmation; all with automation ids.
- Desktop: --recover / --no-recover / --data-dir; local-time offset via chrono (clock).
- Verified in the app: edits, kill -9, relaunch, dialog, Recover restores the edits (unsaved, original path).
- docs/project-files.md: auto-save, recovery and measured costs (2,000 clips: 2.5 ms encode,
  1.4 MB, ~30 ms atomic write+fsync, all off the UI thread). ROADMAP M11 updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:10:23 -04:00
Brandon Thomas ba4a04e031 M11.1: .fcproj schema versioning + migrations, atomic saves
- New crate filmcraft-format (L3): files are an envelope {format, schema_version, generator,
  project}; a chain of single-step JSON migrations (v1 bare Project → v2 envelope) upgrades old
  files on load; newer-than-supported files are refused with a clear "update FilmCraft" error;
  damaged/foreign files report as such.
- atomic_write: temp file in the same dir + fsync + rename + dir fsync; used for every project
  write (FsServices), so a crash mid-save never corrupts the project.
- Engine: file.save/saveAs/open go through the format crate; new file.saveCopy (Save a Copy…,
  Cmd+Alt+S) and file.revert; the first save over an upgraded file keeps
  "<name> (schema vN backup).fcproj". Project.version dropped (the envelope carries it).
- Hand-made v1 fixtures (minimal + edit without later serde-default fields) in
  crates/format/tests/fixtures; 2,000-clip encode/decode timing test.
- docs/project-files.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:54:04 -04:00