1use yog_data::{Res, Schema};
8
9mod montage;
10
11pub use montage::{Montage, PICTURE_SECONDS, PictureClip, PictureTrack};
12
13pub const DIR: &str = yog_ids::kind::VIDEO;
15
16pub const MONTAGE_FPS: f32 = 30.0;
18
19pub const BEST_QUALITY: u32 = 0;
22pub const WORST_QUALITY: u32 = 63;
23const QUALITY: u32 = 35;
25
26#[derive(Schema, Debug, Clone, PartialEq)]
28pub struct Video {
29 pub source: Option<Res>,
31 pub montage: Option<Montage>,
33 pub width: Option<u32>,
36 pub height: Option<u32>,
37 pub fps: Option<f32>,
40 #[data(default = "quality")]
42 pub quality: u32,
43 #[data(default)]
45 pub looping: bool,
46 #[data(default = "yes")]
48 pub sound: bool,
49 #[data(default = "one")]
51 pub volume: f32,
52}
53
54fn quality() -> u32 {
55 QUALITY
56}
57
58fn yes() -> bool {
59 true
60}
61
62fn one() -> f32 {
63 1.0
64}
65
66#[must_use]
69pub fn id_of(mod_id: &str, rel: &str) -> Option<String> {
70 crate::resource_id(mod_id, rel, DIR, yog_ids::Format::Video)
71}
72
73#[must_use]
75pub fn sound_event(name: &str) -> String {
76 format!("{DIR}{}{name}", yog_ids::sep::DOT)
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn a_video_is_named_by_its_path_under_video() {
85 assert_eq!(id_of("m", "video/intro.yvideo").as_deref(), Some("m:intro"));
86 assert_eq!(id_of("m", "sound/intro.yvideo"), None);
87 assert_eq!(sound_event("intro"), "video.intro");
88 }
89
90 #[test]
91 fn a_video_needs_only_its_source() {
92 let v = yog_data::from_text::<Video>(r#"{ source: "@Videos.intro" }"#)
93 .unwrap()
94 .value;
95 assert_eq!(
96 (v.quality, v.sound, v.looping, v.volume),
97 (QUALITY, true, false, 1.0)
98 );
99 }
100}