Merge #2184: Reload a numbered still as its own file (#2074) (@HEOJUNFO)

This commit is contained in:
aiongg
2026-10-24 01:12:00 +00:00
3 changed files with 29 additions and 8 deletions
+10 -5
View File
@@ -129,17 +129,22 @@ pub fn reload_items(s: &mut Session, ids: &[ItemId]) -> crate::Result<usize> {
/// writing it) is left as it was. Returns the number reloaded and the files that couldn't be read
/// (only without `undoable`).
fn reload(s: &mut Session, ids: &[ItemId], undoable: bool) -> crate::Result<(usize, BTreeSet<String>)> {
if s.importer.is_none() {
let Some(importer) = s.importer.clone() else {
return Err(crate::EngineError::Other("media import is not available in this build".into()));
}
};
let mut updates = vec![];
let mut paths = BTreeSet::new();
let mut failed = BTreeSet::new();
for i in ids {
if let Some(ItemKind::Footage(f)) = s.project.item(*i).map(|x| &x.kind) {
// Probed the way it imports by default, so a sequence is found as one. A path that
// is not followed is not read.
let probed = s.stored_paths.check(f).map_err(|(p, why)| crate::doc_path::refusal(&p, why)).and_then(|_| s.probe_footage(&f.path));
// A sequence is probed the way it imports by default, so its run is found again; any
// other item is its one file, not the run a numbered still sits in (#2074). A path
// that is not followed is not read.
let probed = s
.stored_paths
.check(f)
.map_err(|(p, why)| crate::doc_path::refusal(&p, why))
.and_then(|_| if f.kind == FootageKind::Sequence { s.probe_footage(&f.path) } else { importer.probe(&f.path) });
let nf = match probed {
Ok(p) => refreshed(s, f, p),
Err(_) if undoable => Footage { missing: true, ..f.clone() },
+17 -2
View File
@@ -8,14 +8,16 @@ use serde_json::{Value, json};
use crate::Session;
/// Probes every file as a 64×32 still (the media layer's single-file probe).
/// Probes every file as a still 64 + its byte length wide and 32 high (the media layer's
/// single-file probe).
struct Stills;
impl crate::Importer for Stills {
fn probe(&self, path: &str) -> Result<Footage, String> {
if !std::path::Path::new(path).is_file() {
return Err(format!("{path}: no such file"));
}
Ok(Footage { path: path.into(), kind: FootageKind::Still, width: 64, height: 32, has_video: true, codec: "PNG".into(), ..Default::default() })
let width = 64 + std::fs::metadata(path).map_or(0, |m| m.len() as u32);
Ok(Footage { path: path.into(), kind: FootageKind::Still, width, height: 32, has_video: true, codec: "PNG".into(), ..Default::default() })
}
}
@@ -196,6 +198,19 @@ fn replace_footage_and_reload_keep_sequences() {
assert_eq!(f.duration, FrameRate::from_f64(12.0).tick_of(5));
}
/// #2074: reloading a numbered still probed it as its run, so it took the first file's size.
#[test]
fn reloading_a_numbered_still_measures_its_own_file() {
let dir = folder(&RUN);
let mut s = session();
let item = s.execute("file.import", json!({"paths": [at(&dir, "shot_0002.png")], "sequence": false})).unwrap()["items"][0].clone();
std::fs::write(at(&dir, "shot_0002.png"), [0; 16]).unwrap();
s.execute("file.reloadFootage", json!({"items": [item]})).unwrap();
let (name, f) = footage(&s, &item);
assert_eq!((name.as_str(), f.kind, f.width, f.sequence.len()), ("shot_0002.png", FootageKind::Still, 80, 0));
assert_eq!(f.path, at(&dir, "shot_0002.png"));
}
/// #756: numbered DPX and Cineon scans import as sequences, as in After Effects (`.cin` was not
/// an image to the Media Browser, so its runs came in one file at a time).
#[test]
+2 -1
View File
@@ -148,7 +148,8 @@ including files overwritten at the same path and byte size.
File ▸ Open tries each missing file at the same place under the project's new folder, without
marking the project modified.
- **File ▸ Reload Footage** (Ctrl+Alt+L) reads the files again. A sequence picks up frames
added to its folder (a render still in progress) and keeps its interpretation.
added to its folder (a render still in progress) and keeps its interpretation; a still is
read as its one file, even when numbered files sit next to it.
**File ▸ Dependencies ▸ Relink Missing Footage…** searches a chosen folder and its subfolders
for missing source files on the desktop. It relinks unique exact filenames whose decoded type,