This repository has no description
1use crate::{
2 synchronization::sync::{SyncData, Syncable},
3 ui::MaybeProgressBar,
4};
5use anyhow::{Result, anyhow};
6use serde::Deserialize;
7use serde_aux::field_attributes::deserialize_number_from_string;
8use std::{collections::HashMap, io::Read, path::PathBuf, process::Stdio};
9
10use super::sync::TimestampMS;
11
12pub struct CueMarkersSynchronizer {
13 pub path: PathBuf,
14}
15
16#[derive(Debug, Deserialize)]
17struct FFprobeChapterTags {
18 title: String,
19}
20
21#[derive(Debug, Deserialize)]
22struct FFprobeChapter {
23 // id: usize,
24 // time_base: String,
25
26 // start: usize,
27 #[serde(deserialize_with = "deserialize_number_from_string")]
28 start_time: f32,
29
30 // end: usize,
31 // #[serde(deserialize_with = "deserialize_number_from_string")]
32 // end_time: f32,
33 tags: FFprobeChapterTags,
34}
35
36#[derive(Debug, Deserialize)]
37struct FFprobeOutput {
38 chapters: Vec<FFprobeChapter>,
39}
40
41impl Syncable for CueMarkersSynchronizer {
42 fn new(path: impl Into<PathBuf>) -> Self {
43 Self { path: path.into() }
44 }
45
46 fn load(
47 &self,
48 progress: Option<&indicatif::ProgressBar>,
49 ) -> Result<SyncData> {
50 progress.set_length(4);
51 progress.set_message("Running ffprobe");
52
53 let mut ffprobe = std::process::Command::new("ffprobe")
54 .args(["-v", "error"])
55 .args(["-i", &self.path.to_string_lossy()])
56 .args(["-output_format", "json"])
57 .arg("-show_chapters")
58 .stdout(Stdio::piped())
59 .spawn()?;
60
61 progress.inc(1);
62 progress.set_message("Getting ffprobe output");
63
64 let mut raw_output = String::new();
65 ffprobe
66 .stdout
67 .take()
68 .ok_or(anyhow!("Couldn't get stdout of ffprobe"))?
69 .read_to_string(&mut raw_output)
70 .map_err(|e| anyhow!("Couldn't read ffprobe stdout: {e:?}"))?;
71
72 progress.inc(1);
73 progress.set_message("Parsing ffprobe output");
74
75 let output: FFprobeOutput = serde_json::from_str(&raw_output)?;
76
77 progress.inc(1);
78 progress.set_message("Gathering chapters");
79
80 Ok(SyncData {
81 stems: HashMap::new(),
82 bpm: None,
83 markers: output
84 .chapters
85 .iter()
86 .map(|ch| {
87 (
88 (ch.start_time.to_owned() * 1_000.0) as TimestampMS,
89 ch.tags.title.clone(),
90 )
91 })
92 .collect(),
93 })
94 }
95}