use crate::timeline::clip::TimelineClip;
use crate::timeline::transition::Transition;
use crate::types::FrameRate;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TrackKind {
Video,
Audio,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Track {
pub id: String,
pub kind: TrackKind,
pub name: Option<String>,
pub clips: Vec<TimelineClip>,
pub transitions: Vec<Transition>,
}
impl Track {
#[must_use]
pub fn new(id: String, kind: TrackKind) -> Self {
Self {
id,
kind,
name: None,
clips: Vec::new(),
transitions: Vec::new(),
}
}
pub fn add_clip(&mut self, clip: TimelineClip) {
self.clips.push(clip);
}
pub fn add_transition(&mut self, transition: Transition) {
self.transitions.push(transition);
}
#[must_use]
pub fn duration_frames(&self) -> u64 {
self.clips
.iter()
.map(super::clip::TimelineClip::duration_frames)
.sum()
}
pub fn sort_clips(&mut self) {
self.clips.sort_by_key(|c| c.timeline_in.to_frames(c.fps));
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Timeline {
pub name: String,
pub fps: FrameRate,
pub video_tracks: Vec<Track>,
pub audio_tracks: Vec<Track>,
}
impl Timeline {
#[must_use]
pub fn new(name: String, fps: FrameRate) -> Self {
Self {
name,
fps,
video_tracks: Vec::new(),
audio_tracks: Vec::new(),
}
}
pub fn add_video_track(&mut self, track: Track) {
self.video_tracks.push(track);
}
pub fn add_audio_track(&mut self, track: Track) {
self.audio_tracks.push(track);
}
#[must_use]
pub fn duration_frames(&self) -> u64 {
let video_duration = self
.video_tracks
.iter()
.map(Track::duration_frames)
.max()
.unwrap_or(0);
let audio_duration = self
.audio_tracks
.iter()
.map(Track::duration_frames)
.max()
.unwrap_or(0);
video_duration.max(audio_duration)
}
#[must_use]
pub fn duration_seconds(&self) -> f64 {
self.duration_frames() as f64 / self.fps.as_f64()
}
#[must_use]
pub fn track_count(&self) -> usize {
self.video_tracks.len() + self.audio_tracks.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused_imports)]
use crate::types::Timecode;
#[test]
fn test_track_creation() {
let track = Track::new("V1".to_string(), TrackKind::Video);
assert_eq!(track.id, "V1");
assert_eq!(track.kind, TrackKind::Video);
assert_eq!(track.clips.len(), 0);
}
#[test]
fn test_timeline_creation() {
let timeline = Timeline::new("My Timeline".to_string(), FrameRate::Fps25);
assert_eq!(timeline.name, "My Timeline");
assert_eq!(timeline.track_count(), 0);
}
#[test]
fn test_add_tracks() {
let mut timeline = Timeline::new("Test".to_string(), FrameRate::Fps25);
let v_track = Track::new("V1".to_string(), TrackKind::Video);
let a_track = Track::new("A1".to_string(), TrackKind::Audio);
timeline.add_video_track(v_track);
timeline.add_audio_track(a_track);
assert_eq!(timeline.video_tracks.len(), 1);
assert_eq!(timeline.audio_tracks.len(), 1);
assert_eq!(timeline.track_count(), 2);
}
}