diff --git a/crates/engine/src/commands/file_more.rs b/crates/engine/src/commands/file_more.rs index 4f43a53a..ef26d59b 100644 --- a/crates/engine/src/commands/file_more.rs +++ b/crates/engine/src/commands/file_more.rs @@ -620,7 +620,8 @@ impl Interpretation { ), }; it.invert_alpha = super::b_p(p, "invertAlpha"); - it.loops = p.get("loop").and_then(Value::as_u64).map(|v| v.clamp(1, 9999) as u32); + // The Interpret Footage form sends numbers as floats (3.0), which `as_u64` rejects. + it.loops = f_p(p, "loop").filter(|x| x.is_finite()).map(|v| v.round().clamp(1.0, 9999.0) as u32); it.par = f_p(p, "pixelAspect").filter(|x| *x > 0.0); it.fields = match str_p(p, "fields") { Some(f) => Some(effectcraft_project::FieldOrder::parse(f).ok_or_else(|| bad(cmd, "fields: off|upper|lower"))?), diff --git a/crates/engine/src/tests_menu_cmds.rs b/crates/engine/src/tests_menu_cmds.rs index 6eeede40..cb83c163 100644 --- a/crates/engine/src/tests_menu_cmds.rs +++ b/crates/engine/src/tests_menu_cmds.rs @@ -1451,3 +1451,16 @@ fn duplicate_with_shape_items_selected_duplicates_them_in_place() { s.execute("edit.duplicate", json!({})).unwrap(); assert_eq!(s.active_comp().unwrap().layers.len(), n + 1); } + +/// The Interpret Footage form sends "Loop (times)" as a float (3.0); it must still be applied (#909). +#[test] +fn interpret_footage_loop_from_form_float() { + let mut s = comp(); + let p = s.execute("file.importPlaceholder", json!({"name": "Shot", "width": 320, "height": 240})).unwrap()["item"].as_u64().unwrap(); + s.state.project_selection = vec![effectcraft_project::ItemId(p)]; + s.execute("file.interpretFootage", json!({"loop": 3.0})).unwrap(); + match &s.project.item(effectcraft_project::ItemId(p)).unwrap().kind { + ItemKind::Footage(f) => assert_eq!(f.loop_count, 3), + _ => panic!(), + } +}