diff --git a/src/iotro.rs b/src/iotro.rs index 98b5a50..a4eeccb 100644 --- a/src/iotro.rs +++ b/src/iotro.rs @@ -24,7 +24,9 @@ pub struct Language<'a> { download_videos: &'a str, questions_feedback: &'a str, // metadata - pub(crate) from: &'a str + pub(crate) from: &'a str, + // questions + pub(crate) question: &'a str } pub const GERMAN: Language<'static> = Language { @@ -58,7 +60,9 @@ pub const GERMAN: Language<'static> = Language { download_videos: "Videos herunterladen", questions_feedback: "Fragen, Vorschläge und Feedback", - from: "vom" + from: "vom", + + question: "Frage" }; pub const BRITISH: Language<'static> = Language { @@ -87,7 +91,7 @@ pub const BRITISH: Language<'static> = Language { 3 | 23 => "rd", _ => "th" }; - format!("{:02}{th} {month} {:04}", d.day, d.year) + format!("{}{th} {month} {:04}", d.day, d.year) }, lecture_from: "Lecture from", @@ -98,7 +102,9 @@ pub const BRITISH: Language<'static> = Language { download_videos: "Download videos", questions_feedback: "Questions, Suggestions and Feedback", - from: "from" + from: "from", + + question: "Question" }; impl Default for Language<'static> { diff --git a/src/main.rs b/src/main.rs index 742ecd8..1a5355a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,30 +3,27 @@ #![forbid(elided_lifetimes_in_paths, unsafe_code)] mod iotro; +mod preset; +mod project; mod question; mod render; mod time; use self::{ - iotro::Language, - question::Question, - render::{ffmpeg::FfmpegOutputFormat, Renderer}, - time::{parse_date, parse_time, Date, Time} + project::{Project, ProjectLecture, ProjectSource, Resolution}, + render::Renderer, + time::{parse_date, parse_time, Time} }; +use crate::preset::Preset; use camino::Utf8PathBuf as PathBuf; use clap::Parser; use console::style; -use rational::Rational; -use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, DisplayFromStr}; #[cfg(feature = "mem_limit")] use std::sync::RwLock; use std::{ - collections::BTreeSet, fmt::Display, fs, - io::{self, BufRead as _, Write}, - str::FromStr + io::{self, BufRead as _, Write} }; #[cfg(feature = "mem_limit")] @@ -38,34 +35,24 @@ struct Args { #[clap(short = 'C', long, default_value = ".")] directory: PathBuf, - /// The slug of the course, e.g. "23ws-malo2". - #[clap(short = 'c', long, default_value = "23ws-malo2")] - course: String, - - /// The label of the course, e.g. "Mathematische Logik II". - #[clap(short, long, default_value = "Mathematische Logik II")] - label: String, - - /// The docent of the course, e.g. "Prof. E. Grädel". - #[clap(short, long, default_value = "Prof. E. Grädel")] - docent: String, - - /// The language of the lecture. Used for the intro and outro frame. - #[clap(short = 'L', long, default_value = "de")] - lang: Language<'static>, + /// The preset of the lecture. Can be a toml file or a known course slug. + #[clap(short, long)] + preset: String, #[cfg(feature = "mem_limit")] /// The memory limit for external tools like ffmpeg. #[clap(short, long, default_value = "12G")] mem_limit: String, - /// Transcode the final video clip down to the minimum resolution specified. + /// Transcode the final video clip down to the minimum resolution specified. If not + /// specified, the default value from the preset is used. #[clap(short, long)] transcode: Option, /// Transcode starts at this resolution, or the source resolution, whichever is lower. - #[clap(short = 'T', long, default_value = "1440p")] - transcode_start: Resolution, + /// If not specified, the default value from the preset is used. + #[clap(short = 'T', long)] + transcode_start: Option, /// Treat the audio as stereo. By default, only one channel from the input stereo will /// be used, assuming either the other channel is backup or the same as the used. @@ -73,159 +60,6 @@ struct Args { stereo: bool } -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)] - 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 { - fn values() -> [Self; NUM_RESOLUTIONS] { - [$(Self::$res),+] - } - - fn width(self) -> usize { - match self { - $(Self::$res => $width),+ - } - } - - fn height(self) -> usize { - match self { - $(Self::$res => $height),+ - } - } - - fn bitrate(self) -> u64 { - match self { - $(Self::$res => $bitrate),+ - } - } - - 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 { - 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)] -struct Project { - lecture: ProjectLecture, - source: ProjectSource, - progress: ProjectProgress -} - -#[serde_as] -#[derive(Deserialize, Serialize)] -struct ProjectLecture { - course: String, - label: String, - docent: String, - #[serde_as(as = "DisplayFromStr")] - date: Date, - #[serde(default = "Default::default")] - #[serde_as(as = "DisplayFromStr")] - lang: Language<'static> -} - -#[serde_as] -#[derive(Deserialize, Serialize)] -struct ProjectSource { - files: Vec, - stereo: bool, - - #[serde_as(as = "Option")] - start: Option