Image sequences: a gap in the numbering holds the previous frame, as After Effects (#3137)

* Image sequences: a gap in the numbering holds the previous frame, as After Effects

Split out of #1876 (originally 7850cbf4), because it reverses the colour bars
documented in #297 and needs its own discussion.

After Effects 2026 shows the last existing file across a gap in a sequence's
numbering (files 1, 2, 4, 5 show frame 2 at frame 3; read back from After
Effects). EffectCraft drew colour bars there. The media pool now holds the
previous file (Footage::sequence_file_held); colour bars remain only where no
file precedes the frame. Missing-frame reports on import are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Image sequences: hold the first file of the preceding number across a gap; fix the import warning

A gap now holds the file sequence_file picks for the preceding number, so
files sharing a number do not switch at the gap. The Report Missing Frames
warning says the previous frame is held instead of "shown as placeholders".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Image sequences: Interpret Footage says a gap holds the previous frame

The Interpret Footage note for a numbered sequence with gaps still said the
missing frames are shown as colour bars; the media pool holds the previous
frame there now. The note, its comment and two test doc comments say so,
and a forms test checks the note's wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
This commit is contained in:
Valentin Klein
2026-11-07 00:40:24 +01:00
committed by GitHub
co-authored by Claude Opus 5.5 FredApps
parent 23c1f33c88
commit 52bca964f5
9 changed files with 66 additions and 22 deletions
+1 -1
View File
@@ -327,7 +327,7 @@ impl Prober {
&& self.report_missing_frames
&& let Some(gaps) = crate::sequence::missing_report(&f.sequence)
{
warning = Some(format!("{name}: {gaps} (shown as placeholders)"));
warning = Some(format!("{name}: {gaps} (the previous frame is held)"));
}
// Settings ▸ Import ▸ Interpret Unlabeled Alpha As: images only (a 3D model renders its
// own alpha, and audio and data have none).
+3 -2
View File
@@ -1,6 +1,6 @@
//! Image sequences (#297): File ▸ Import with the "<format> Sequence" option, picked ranges,
//! folders, Force Alphabetical Order, the import frame rate, Replace Footage, missing frames
//! (placeholders, as in After Effects) and Interpret Footage ▸ Start Timecode.
//! (they hold the previous frame) and Interpret Footage ▸ Start Timecode.
use effectcraft_project::{Footage, FootageKind, ItemId, ItemKind};
use effectcraft_time::{FrameRate, Tick};
@@ -62,12 +62,13 @@ fn one_picked_numbered_file_imports_its_run_as_one_sequence() {
assert_eq!(name, "shot_[0001-0005].png");
assert_eq!((f.kind, f.sequence.len(), f.alphabetical), (FootageKind::Sequence, 4, false));
assert_eq!(f.path, at(&dir, "shot_0001.png"));
// Frames 1–5 at Settings ▸ Import ▸ Sequence Footage (30 fps); frame 3 is a placeholder.
// Frames 1–5 at Settings ▸ Import ▸ Sequence Footage (30 fps); frame 3 holds frame 2.
assert_eq!(f.duration, FrameRate::FPS_30.tick_of(5));
assert_eq!(f.sequence_file(2), None);
// Settings ▸ Import ▸ Report Missing Frames.
let w = r["warnings"][0].as_str().unwrap();
assert!(w.contains("1 missing frame (3)"), "{w}");
assert!(w.contains("previous frame is held") && !w.contains("placeholder"), "{w}");
}
/// Picking every frame of a sequence used to make one item per frame (each a whole sequence on