mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-24 10:10:05 -06:00
fix: timeline waveforms from the previous project after File > Open
Waveform peaks were cached by item id and never cleared, and item ids repeat across projects: after opening a project the timeline drew the previous project's waveforms (a 7-minute mission-audio clip showed as silent). The cache is now reset with the media pool, and an item whose source is not available is no longer left pending forever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
54e2ca0994
commit
c14fc8e1b7
@@ -250,6 +250,7 @@ impl FilmcraftApp {
|
||||
FrameServer::default_workers(),
|
||||
));
|
||||
self.textures.clear();
|
||||
self.tl.reset_media_caches();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,16 @@ pub struct TlState {
|
||||
zoom_anchor: Option<(f64, f32)>,
|
||||
}
|
||||
|
||||
impl TlState {
|
||||
/// Forget cached waveform peaks: they are keyed by item id, and ids repeat across projects
|
||||
/// (opening another project would otherwise show the old project's waveforms). Peak jobs
|
||||
/// still running finish into the old maps and are dropped.
|
||||
pub fn reset_media_caches(&mut self) {
|
||||
self.peaks = Default::default();
|
||||
self.peaks_pending = Default::default();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Drag {
|
||||
Scrub,
|
||||
@@ -544,6 +554,8 @@ fn request_peaks(app: &mut FilmcraftApp, item: ItemId) -> Option<Arc<Vec<(f32, f
|
||||
if let Some(p) = app.tl.peaks.lock().unwrap_or_else(|e| e.into_inner()).get(&item) {
|
||||
return Some(p.clone());
|
||||
}
|
||||
// No source: try again on a later frame instead of leaving the item pending forever.
|
||||
let src = app.session.source(item)?;
|
||||
{
|
||||
let mut pend = app.tl.peaks_pending.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if pend.contains(&item) {
|
||||
@@ -551,7 +563,6 @@ fn request_peaks(app: &mut FilmcraftApp, item: ItemId) -> Option<Arc<Vec<(f32, f
|
||||
}
|
||||
pend.push(item);
|
||||
}
|
||||
let src = app.session.source(item)?;
|
||||
let peaks = app.tl.peaks.clone();
|
||||
let pending = app.tl.peaks_pending.clone();
|
||||
let dur = src.info().duration;
|
||||
|
||||
@@ -442,3 +442,30 @@ fn keyboard_shortcuts_dialog_assigns_live_and_cancel_restores() {
|
||||
assert!(d.sequence()["playhead"].as_i64().unwrap() < ph, "Timeline ▸ Left steps back");
|
||||
assert_eq!(track_clips(&d.sequence(), 0).len(), n0 + 1);
|
||||
}
|
||||
|
||||
/// Waveform peaks are cached per item id; ids repeat across projects, so opening another project
|
||||
/// must not reuse the previous project's peaks (it showed a long mission-audio clip as silent).
|
||||
#[test]
|
||||
fn waveform_peaks_do_not_survive_opening_another_project() {
|
||||
let mut d = Driver::demo();
|
||||
let mut first = None;
|
||||
for _ in 0..2000 {
|
||||
d.frames(1);
|
||||
let peaks = d.harness.state().tl.peaks.lock().unwrap().iter().map(|(k, v)| (*k, v.clone())).next();
|
||||
if peaks.is_some() {
|
||||
first = peaks;
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
let (item, old) = first.expect("the demo timeline computes waveform peaks");
|
||||
// Reopen the same project from disk: same item ids, new media pool.
|
||||
let path = std::env::temp_dir().join(format!("fc-peaks-{}.fcproj", std::process::id()));
|
||||
let path = path.to_string_lossy().to_string();
|
||||
d.exec("file.saveAs", json!({"path": path}));
|
||||
d.exec("file.open", json!({"path": path}));
|
||||
d.frames(2);
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let now = d.harness.state().tl.peaks.lock().unwrap().get(&item).cloned();
|
||||
assert!(now.is_none_or(|p| !std::sync::Arc::ptr_eq(&p, &old)), "peaks from the previous project were reused");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user