Import: only dropped files default to stills; scripts and agents naming a frame still get its run (#431)

The UI behaves as in After Effects (the Import dialog's Sequence checkbox
starts off, dropped files are stills, dropped folders make sequences), but
file.import without sequence or drag keeps importing a numbered still's
run, so existing scripts and agents keep working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
This commit is contained in:
Claude 2026-10-10 20:15:24 +00:00
parent 8b42f970b6
commit 5e2c7560d6
No known key found for this signature in database
5 changed files with 21 additions and 13 deletions

View File

@ -688,7 +688,7 @@ pub fn specs() -> Vec<CommandSpec> {
"File...",
["File", "Import"],
Some("Cmd+I"),
"{paths: [string] (files or folders), sequence?: bool (the Sequence checkbox: true imports numbered stills of one run as one image sequence, one picked file bringing its whole run and several picked files that range; by default picked files import as stills and folders' numbered stills as sequences; false imports a folder's files individually), alphabetical?: bool (Force Alphabetical Order: every image of that type in the folder, by name), frameRate?: fps (image sequences; default Settings ▸ Import ▸ Sequence Footage), importAs?: footage|composition|compositionLayerSizes (Photoshop, PDF, Illustrator and EPS files), layer?: name|index (footage of one Photoshop layer), page?: number from 1 (PDF / Illustrator page), drag?: bool (dropped files: Settings ▸ Import ▸ Default Drag Import As), addToComp?: bool (also add them to the active comp, at time?, index?, position? as in layer.addItem), background?: bool (probe the files in a background job, jobs.list / jobs.wait; returns {job})}",
"{paths: [string] (files or folders), sequence?: bool (the Sequence checkbox: true imports numbered stills of one run as one image sequence, one picked file bringing its whole run and several picked files that range; default: a numbered still brings its run, except dropped files (drag), which import as stills as in After Effects; folders' runs import as sequences; false imports stills and a folder's files individually), alphabetical?: bool (Force Alphabetical Order: every image of that type in the folder, by name), frameRate?: fps (image sequences; default Settings ▸ Import ▸ Sequence Footage), importAs?: footage|composition|compositionLayerSizes (Photoshop, PDF, Illustrator and EPS files), layer?: name|index (footage of one Photoshop layer), page?: number from 1 (PDF / Illustrator page), drag?: bool (dropped files: Settings ▸ Import ▸ Default Drag Import As; files import as stills unless sequence), addToComp?: bool (also add them to the active comp, at time?, index?, position? as in layer.addItem), background?: bool (probe the files in a background job, jobs.list / jobs.wait; returns {job})}",
always,
import_cmd
),

View File

@ -42,15 +42,17 @@ impl SequenceOptions {
SequenceOptions { sequence, folders: sequence, alphabetical: flag("alphabetical").unwrap_or(false) }
}
/// `file.import`'s options, as in After Effects: picked or dropped files import as stills
/// unless `sequence: true` (the Import dialog's "<format> Sequence" checkbox, which is off
/// unless ticked), while a folder's numbered stills import as sequences unless
/// `sequence: false` (Alt-drag of a folder).
/// `file.import`'s options. Dropped files (`drag: true`) import as stills, as in After
/// Effects, while a folder's numbered stills import as sequences unless `sequence: false`
/// (Alt-drag of a folder). The Import dialog passes its "<format> Sequence" checkbox (off
/// unless ticked) as `sequence`. Without `sequence` or `drag` (scripts, agents) a numbered
/// still brings its run, as it always has.
pub fn for_import(p: &serde_json::Value) -> SequenceOptions {
let flag = |k: &str| p.get(k).and_then(serde_json::Value::as_bool);
let (sequence, alphabetical) = (flag("sequence"), flag("alphabetical").unwrap_or(false));
let dropped = flag("drag").unwrap_or(false);
// Force Alphabetical Order is part of the Sequence option.
SequenceOptions { sequence: sequence.unwrap_or(alphabetical), folders: sequence.unwrap_or(true), alphabetical }
SequenceOptions { sequence: sequence.unwrap_or(alphabetical || !dropped), folders: sequence.unwrap_or(true), alphabetical }
}
}

View File

@ -97,11 +97,15 @@ fn numbered_stills_import_as_stills_by_default() {
let r = s.execute("file.import", json!({"paths": [at(&dir, "shot_0002.png")], "drag": true})).unwrap();
let (name, f) = footage(&s, &r["items"][0]);
assert_eq!((name.as_str(), f.kind), ("shot_0002.png", FootageKind::Still));
let r = s.execute("file.import", json!({"paths": [at(&dir, "shot_0001.png"), at(&dir, "shot_0004.png")]})).unwrap();
let r = s.execute("file.import", json!({"paths": [at(&dir, "shot_0001.png"), at(&dir, "shot_0004.png")], "drag": true})).unwrap();
assert_eq!(r["items"].as_array().unwrap().len(), 2, "{r}");
let r = s.execute("file.import", json!({"paths": [dir.clone()], "drag": true})).unwrap();
let (name, f) = footage(&s, &r["items"][0]);
assert_eq!((name.as_str(), f.kind), ("shot_[0001-0005].png", FootageKind::Sequence));
// Scripts and agents naming one frame without `sequence` still get its run.
let r = s.execute("file.import", json!({"paths": [at(&dir, "shot_0003.png")]})).unwrap();
let (_, f) = footage(&s, &r["items"][0]);
assert_eq!(f.kind, FootageKind::Sequence);
}
#[test]

View File

@ -251,10 +251,10 @@ build real projects through these interfaces; they are worked examples of everyt
then `keys.velocity {"outSpeed":0,"outInfluence":33.33}`.
* **Gradients**: `set_property {"path":"contents/gfill/colors","value":["#0080ff","#ffff00"]}` or
`{"colors":[[0,"#0080ff"],[1,[1,1,0]]],"opacities":[[0,1],[1,0.5]]}`.
* **Image sequences**: `file.import {"paths":["/r/shot_0001.png"], "sequence": true}` imports the
file's whole numbered run as one item (several picked frames: that range; a folder, even without
`sequence`: each run in it; without `sequence` picked files import as stills, as in After
Effects; `"alphabetical": true`: Force Alphabetical Order, the files
* **Image sequences**: `file.import {"paths":["/r/shot_0001.png"]}` imports the file's whole
numbered run as one item (several picked frames: that range; a folder: each run in it;
`"sequence": false`: one still; dropped files, `"drag": true`, import as stills unless
`"sequence": true`, as in After Effects; `"alphabetical": true`: Force Alphabetical Order, the files
play one after another) at `frameRate` or 30 fps (Settings ▸ Import). Missing frames show
colour bars, as in After Effects. `file.interpretFootage {"items":[id], "frameRate":12,
"startTimecode":1001}` conforms them and sets Override Start (the item and layers that ran to its end get the new

View File

@ -52,9 +52,11 @@ Effects does, so every other frame stays at its number. Render or copy the missi
folder and choose File ▸ Reload Footage to fill the gaps.
Agents: `file.import {"paths": ["/shots/shot_0001.png"], "sequence": true, "frameRate": 24}`.
Without `sequence`, picked files import as stills and a folder's numbered runs as sequences;
Without `sequence`, a numbered file brings its whole run and a folder's numbered runs import as
sequences, except dropped files (`"drag": true`), which import as stills as in After Effects.
`"sequence": true` is the Sequence option (one file brings its whole run), `"sequence": false`
imports a folder's files one by one, and `"alphabetical": true` is Force Alphabetical Order.
imports stills and a folder's files one by one, and `"alphabetical": true` is Force Alphabetical
Order.
## Interpret Footage