diff --git a/src/adapters/ffmpeg.rs b/src/adapters/ffmpeg.rs index a32e6f9..b18b666 100644 --- a/src/adapters/ffmpeg.rs +++ b/src/adapters/ffmpeg.rs @@ -14,7 +14,7 @@ use writing::WritingFileAdapter; // maybe todo: read list of extensions from // ffmpeg -demuxers | tail -n+5 | awk '{print $2}' | while read demuxer; do echo MUX=$demuxer; ffmpeg -h demuxer=$demuxer | grep 'Common extensions'; done 2>/dev/null // but really, the probability of getting useful information from a .flv is low -static EXTENSIONS: &[&str] = &["mkv", "mp4", "avi", "mp3", "ogg", "flac"]; +static EXTENSIONS: &[&str] = &["mkv", "mp4", "avi", "mp3", "ogg", "flac", "webm"]; lazy_static! { static ref METADATA: AdapterMeta = AdapterMeta { @@ -54,7 +54,7 @@ struct FFprobeOutput { } #[derive(Serialize, Deserialize)] struct FFprobeStream { - codec_type: String, // video,audio,subtitle + index: i32, // stream index } #[async_trait] @@ -80,17 +80,13 @@ impl WritingFileAdapter for FFmpegAdapter { } let inp_fname = filepath_hint; let spawn_fail = |e| map_exe_error(e, "ffprobe", "Make sure you have ffmpeg installed."); - let has_subtitles = { + let subtitle_streams = { let probe = Command::new("ffprobe") .args(vec![ - "-v", - "error", - "-select_streams", - "s", - "-of", - "json", - "-show_entries", - "stream=codec_type", + "-v", "error", // show all errors + "-select_streams", "s", // show only subtitle streams + "-of", "json", // use json as output format + "-show_entries", "stream=index", // show index of subtitle streams ]) .arg("-i") .arg(&inp_fname) @@ -105,7 +101,7 @@ impl WritingFileAdapter for FFmpegAdapter { )); } let p: FFprobeOutput = serde_json::from_slice(&probe.stdout)?; - !p.streams.is_empty() + p.streams }; { // extract file metadata (especially chapter names in a greppable format) @@ -138,31 +134,34 @@ impl WritingFileAdapter for FFmpegAdapter { return Err(format_err!("ffprobe failed: {:?}", exit)); } } - if has_subtitles { - // extract subtitles - let mut cmd = Command::new("ffmpeg"); - cmd.arg("-hide_banner") - .arg("-loglevel") - .arg("panic") - .arg("-i") - .arg(&inp_fname) - .arg("-f") - .arg("webvtt") - .arg("-"); - let mut cmd = cmd.stdout(Stdio::piped()).spawn().map_err(spawn_fail)?; - let stdo = cmd.stdout.as_mut().expect("is piped"); - let time_re = Regex::new(r".*\d.*-->.*\d.*").unwrap(); - let mut time: String = "".to_owned(); - // rewrite subtitle times so they are shown as a prefix in every line - let mut lines = BufReader::new(stdo).lines(); - while let Some(line) = lines.next_line().await? { - // 09:55.195 --> 09:56.730 - if time_re.is_match(&line) { - time = line.to_owned(); - } else if line.is_empty() { - async_writeln!(oup)?; - } else { - async_writeln!(oup, "{time}: {line}")?; + if subtitle_streams.len() > 0 { + for probe_stream in subtitle_streams.iter() { + // extract subtitles + let mut cmd = Command::new("ffmpeg"); + cmd.arg("-hide_banner") + .arg("-loglevel").arg("panic") + .arg("-i") + .arg(&inp_fname) + .arg("-map") + .arg(format!("0:{}", probe_stream.index.to_string())) // 0 for first input + .arg("-f") + .arg("webvtt") + .arg("-"); + let mut cmd = cmd.stdout(Stdio::piped()).spawn().map_err(spawn_fail)?; + let stdo = cmd.stdout.as_mut().expect("is piped"); + let time_re = Regex::new(r".*\d.*-->.*\d.*").unwrap(); + let mut time: String = "".to_owned(); + // rewrite subtitle times so they are shown as a prefix in every line + let mut lines = BufReader::new(stdo).lines(); + while let Some(line) = lines.next_line().await? { + // 09:55.195 --> 09:56.730 + if time_re.is_match(&line) { + time = line.to_owned(); + } else if line.is_empty() { + async_writeln!(oup)?; + } else { + async_writeln!(oup, "{time}: {line}")?; + } } } }