1use std::path::{Path, PathBuf};
2
3#[derive(thiserror::Error, Debug)]
4pub enum FfmpegError {
5 #[error("I/O error")]
6 IoError(#[from] std::io::Error),
7}
8
9pub struct Metadata {
10 pub title: Option<String>,
11 pub description: Option<String>,
12 pub thumbnail: Option<PathBuf>,
13 pub date: Option<String>,
14 pub video_id: Option<String>,
15 pub faststart: bool,
16}
17
18pub async fn mux(input: &Path, metadata: &Metadata, output: &Path) -> Result<(), FfmpegError> {
19 info!("Muxing {} to {}", input.display(), output.display());
20
21 let mut child = tokio::process::Command::new("ffmpeg");
22
23 child
24 .arg("-hide_banner")
25 .arg("-loglevel")
26 .arg("error")
27 .arg("-y");
28
29 child.arg("-i").arg(input);
31
32 if let Some(thumbnail) = &metadata.thumbnail {
34 child
35 .arg("-i")
36 .arg(thumbnail)
37 .arg("-map")
38 .arg("0")
39 .arg("-map")
40 .arg("1");
41 }
42 if let Some(title) = &metadata.title {
43 child.arg("-metadata").arg(format!("title={}", title));
44 }
45 if let Some(description) = &metadata.description {
46 child
47 .arg("-metadata")
48 .arg(format!("description={}", description));
49 }
50 if let Some(date) = &metadata.date {
51 child.arg("-metadata").arg(format!("date={}", date));
52 }
53 if let Some(video_id) = &metadata.video_id {
54 child
55 .arg("-metadata")
56 .arg(format!("episode_id={}", video_id));
57 }
58
59 child.arg("-c").arg("copy");
60
61 if metadata.thumbnail.is_some() {
62 child.arg("-disposition:v:1").arg("attached_pic");
63 }
64
65 if metadata.faststart {
66 child.arg("-movflags").arg("+faststart");
67 }
68
69 child.arg(output);
71
72 child.spawn().map_err(FfmpegError::IoError)?.wait().await?;
73 info!("Muxing complete");
74
75 Ok(())
76}