use std::path::{Path, PathBuf};
use ffcine_timeline::{Clip, Effect, Item, MediaReference, Track};
#[derive(Debug, Clone)]
pub struct Placed<'a> {
pub clip: &'a Clip,
pub start: f64,
pub duration: f64,
pub source_start: f64,
pub xfade_in: f64,
pub xfade_out: f64,
pub speed: f64,
}
impl Placed<'_> {
pub fn end(&self) -> f64 {
self.start + self.duration
}
pub fn path(&self, base: Option<&Path>) -> Option<PathBuf> {
media_path(self.clip, base)
}
}
pub fn media_path(clip: &Clip, base: Option<&Path>) -> Option<PathBuf> {
let MediaReference::External(r) = clip.media_reference()? else {
return None;
};
let p = r.path()?;
Some(match base {
Some(b) if p.is_relative() => b.join(p),
_ => p,
})
}
pub fn layout(track: &Track) -> (Vec<Placed<'_>>, f64) {
let mut out: Vec<Placed<'_>> = Vec::new();
let mut t = 0.0;
let mut pending_in = 0.0f64;
let mut pending_xfade = 0.0f64;
for item in &track.children {
match item {
Item::Clip(c) => {
let range = c.source_range.as_ref().or_else(|| {
c.media_reference()
.and_then(MediaReference::available_range)
});
let Some(range) = range else { continue };
let avail0 = c
.media_reference()
.and_then(MediaReference::available_range)
.map_or(0.0, |r| r.start_time.to_seconds());
let duration = range.duration.to_seconds();
let speed = c
.effects
.iter()
.find_map(|e| match e {
Effect::LinearTimeWarp(w) => Some(w.time_scalar),
Effect::FreezeFrame(_) => Some(0.0),
_ => None,
})
.unwrap_or(1.0);
if c.enabled {
out.push(Placed {
clip: c,
start: t - pending_in,
duration: duration + pending_in,
source_start: range.start_time.to_seconds() - avail0 - pending_in * speed,
xfade_in: pending_xfade,
xfade_out: 0.0,
speed,
});
}
pending_in = 0.0;
pending_xfade = 0.0;
t += duration;
}
Item::Transition(tr) => {
let i = tr.in_offset.to_seconds();
let o = tr.out_offset.to_seconds();
if let Some(prev) = out.last_mut().filter(|p| (p.end() - t).abs() < 1e-6) {
prev.duration += o;
prev.xfade_out = i + o;
}
pending_in = i;
pending_xfade = i + o;
}
Item::Gap(g) => {
t += g
.source_range
.as_ref()
.map_or(0.0, |r| r.duration.to_seconds());
}
Item::Stack(s) => {
t += s
.source_range
.as_ref()
.map_or(0.0, |r| r.duration.to_seconds());
}
Item::Track(s) => {
t += s
.source_range
.as_ref()
.map_or(0.0, |r| r.duration.to_seconds());
}
Item::Unknown(_) => {}
}
}
(out, t)
}