diff --git a/src/project.rs b/src/project.rs new file mode 100644 index 0000000000000000000000000000000000000000..46ed082ad79abc6d096f8833a4dc9cd24e55a8d2 --- /dev/null +++ b/src/project.rs @@ -0,0 +1,162 @@ +use crate::{ + iotro::Language, + render::ffmpeg::FfmpegOutputFormat, + time::{Date, Time} +}; +use rational::Rational; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; +use std::{collections::BTreeSet, str::FromStr}; + +macro_rules! resolutions { + ($($res:ident: $width:literal x $height:literal at $bitrate:literal in $format:ident),+) => { + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] + pub(crate) enum Resolution { + $( + #[doc = concat!(stringify!($width), "x", stringify!($height))] + $res + ),+ + } + + const NUM_RESOLUTIONS: usize = { + let mut num = 0; + $(num += 1; stringify!($res);)+ + num + }; + + impl Resolution { + pub(crate) fn values() -> [Self; NUM_RESOLUTIONS] { + [$(Self::$res),+] + } + + pub(crate) fn width(self) -> usize { + match self { + $(Self::$res => $width),+ + } + } + + pub(crate) fn height(self) -> usize { + match self { + $(Self::$res => $height),+ + } + } + + pub(crate) fn bitrate(self) -> u64 { + match self { + $(Self::$res => $bitrate),+ + } + } + + pub(crate) fn format(self) -> FfmpegOutputFormat { + match self { + $(Self::$res => FfmpegOutputFormat::$format),+ + } + } + } + + impl FromStr for Resolution { + type Err = anyhow::Error; + + fn from_str(s: &str) -> anyhow::Result<Self> { + Ok(match s { + $(concat!(stringify!($height), "p") => Self::$res,)+ + _ => anyhow::bail!("Unknown Resolution: {s:?}") + }) + } + } + } +} + +resolutions! { + nHD: 640 x 360 at 500_000 in AvcAac, + HD: 1280 x 720 at 1_000_000 in AvcAac, + FullHD: 1920 x 1080 at 750_000 in Av1Opus, + WQHD: 2560 x 1440 at 1_000_000 in Av1Opus, + // TODO qsx muss mal sagen wieviel bitrate für 4k + UHD: 3840 x 2160 at 2_000_000 in Av1Opus +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct Project { + pub(crate) lecture: ProjectLecture, + pub(crate) source: ProjectSource, + pub(crate) progress: ProjectProgress +} + +#[serde_as] +#[derive(Deserialize, Serialize)] +pub(crate) struct ProjectLecture { + pub(crate) course: String, + pub(crate) label: String, + pub(crate) docent: String, + #[serde_as(as = "DisplayFromStr")] + pub(crate) date: Date, + #[serde(default = "Default::default")] + #[serde_as(as = "DisplayFromStr")] + pub(crate) lang: Language<'static> +} + +#[serde_as] +#[derive(Deserialize, Serialize)] +pub(crate) struct ProjectSource { + pub(crate) files: Vec<String>, + pub(crate) stereo: bool, + + #[serde_as(as = "Option<DisplayFromStr>")] + pub(crate) start: Option<Time>, + #[serde_as(as = "Option<DisplayFromStr>")] + pub(crate) end: Option<Time>, + + #[serde(default)] + #[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] + pub(crate) fast: Vec<(Time, Time)>, + + #[serde(default)] + #[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr, _)>")] + pub(crate) questions: Vec<(Time, Time, String)>, + + pub(crate) metadata: Option<ProjectSourceMetadata> +} + +#[serde_as] +#[derive(Deserialize, Serialize)] +pub(crate) struct ProjectSourceMetadata { + /// The duration of the source video. + #[serde_as(as = "DisplayFromStr")] + pub(crate) source_duration: Time, + /// The FPS of the source video. + #[serde_as(as = "DisplayFromStr")] + pub(crate) source_fps: Rational, + /// The time base of the source video. + #[serde_as(as = "DisplayFromStr")] + pub(crate) source_tbn: Rational, + /// The resolution of the source video. + pub(crate) source_res: Resolution, + /// The sample rate of the source audio. + pub(crate) source_sample_rate: u32 +} + +#[derive(Default, Deserialize, Serialize)] +pub(crate) struct ProjectProgress { + #[serde(default)] + pub(crate) preprocessed: bool, + + #[serde(default)] + pub(crate) asked_start_end: bool, + + #[serde(default)] + pub(crate) asked_fast: bool, + + #[serde(default)] + pub(crate) asked_questions: bool, + + #[serde(default)] + pub(crate) rendered_assets: bool, + + #[serde(default)] + pub(crate) rendered: bool, + + #[serde(default)] + pub(crate) transcoded: BTreeSet<Resolution> +}