move ffprobe stuff to its own module
This commit is contained in:
parent
84e79f6cc6
commit
bb4f776538
|
@ -40,7 +40,6 @@ taglib = { path = "./modules/taglib", optional = true }
|
||||||
html-escape = "0.2"
|
html-escape = "0.2"
|
||||||
urlencoding = "2"
|
urlencoding = "2"
|
||||||
|
|
||||||
|
|
||||||
# error handling
|
# error handling
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
string-error = "0.1"
|
string-error = "0.1"
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
version = "latest";
|
version = "latest";
|
||||||
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoLock = {lockFile = ./Cargo.lock;};
|
cargoLock.lockFile = ./Cargo.lock;
|
||||||
|
|
||||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
|
|
||||||
|
|
35
src/utils/ffprobe/errors.rs
Normal file
35
src/utils/ffprobe/errors.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::{fmt, io, process};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AnalyzeError {
|
||||||
|
FFProbeError(FFProbeError),
|
||||||
|
IOError(io::Error),
|
||||||
|
ParseError(serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AnalyzeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
AnalyzeError::FFProbeError(err) => write!(f, "{}", err),
|
||||||
|
AnalyzeError::IOError(err) => write!(f, "{}", err),
|
||||||
|
AnalyzeError::ParseError(err) => write!(f, "{}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FFProbeError {
|
||||||
|
pub exit_status: process::ExitStatus,
|
||||||
|
pub stderr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FFProbeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ffprobe exited with error code {}, stderr: {}",
|
||||||
|
self.exit_status.code().unwrap(),
|
||||||
|
self.stderr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
35
src/utils/ffprobe/ffprobe_output.rs
Normal file
35
src/utils/ffprobe/ffprobe_output.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct FFProbeOutput {
|
||||||
|
pub format: FFProbeOutputFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct FFProbeOutputFormat {
|
||||||
|
pub tags: FFProbeOutputTags,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct FFProbeOutputTags {
|
||||||
|
#[serde(alias = "TITLE")]
|
||||||
|
pub title: String,
|
||||||
|
#[serde(default, alias = "ARTIST")]
|
||||||
|
pub artist: String,
|
||||||
|
|
||||||
|
#[serde(default, alias = "REPLAYGAIN_TRACK_PEAK")]
|
||||||
|
pub replaygain_track_peak: Option<String>,
|
||||||
|
#[serde(default, alias = "REPLAYGAIN_TRACK_GAIN")]
|
||||||
|
pub replaygain_track_gain: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FFProbeOutputTags {
|
||||||
|
fn default() -> Self {
|
||||||
|
FFProbeOutputTags {
|
||||||
|
title: "".to_string(),
|
||||||
|
artist: "".to_string(),
|
||||||
|
replaygain_track_peak: None,
|
||||||
|
replaygain_track_gain: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/utils/ffprobe/mod.rs
Normal file
47
src/utils/ffprobe/mod.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
mod ffprobe_output;
|
||||||
|
pub mod errors;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
convert::Into,
|
||||||
|
path::PathBuf,
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
use self::errors::{AnalyzeError, FFProbeError};
|
||||||
|
|
||||||
|
pub fn analyze(path: &PathBuf) -> Result<types::FFProbeData, AnalyzeError> {
|
||||||
|
let output = Command::new(crate::meta::FFPROBE)
|
||||||
|
.args([
|
||||||
|
"-v",
|
||||||
|
"quiet",
|
||||||
|
"-print_format",
|
||||||
|
"json",
|
||||||
|
"-show_format",
|
||||||
|
path.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
if let Err(err) = output {
|
||||||
|
return Err(AnalyzeError::IOError(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = output.unwrap();
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(AnalyzeError::FFProbeError(FFProbeError {
|
||||||
|
exit_status: output.status,
|
||||||
|
stderr: String::from_utf8(output.stderr).unwrap(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = String::from_utf8(output.stdout).unwrap();
|
||||||
|
let ffprobe_out: serde_json::Result<ffprobe_output::FFProbeOutput> =
|
||||||
|
serde_json::from_str(output.as_str());
|
||||||
|
|
||||||
|
let ffprobe_out = ffprobe_out.unwrap();
|
||||||
|
|
||||||
|
return Ok(types::FFProbeData {
|
||||||
|
tags: ffprobe_out.format.tags.into(),
|
||||||
|
});
|
||||||
|
}
|
27
src/utils/ffprobe/types.rs
Normal file
27
src/utils/ffprobe/types.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ffprobe_output;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct FFProbeTags {
|
||||||
|
pub title: String,
|
||||||
|
pub artist: String,
|
||||||
|
pub replaygain_track_peak: Option<String>,
|
||||||
|
pub replaygain_track_gain: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<FFProbeTags> for ffprobe_output::FFProbeOutputTags {
|
||||||
|
fn into(self) -> FFProbeTags {
|
||||||
|
FFProbeTags {
|
||||||
|
title: self.title,
|
||||||
|
artist: self.artist,
|
||||||
|
replaygain_track_peak: self.replaygain_track_peak,
|
||||||
|
replaygain_track_gain: self.replaygain_track_gain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct FFProbeData {
|
||||||
|
pub tags: FFProbeTags,
|
||||||
|
}
|
|
@ -3,12 +3,11 @@ use std::{
|
||||||
process::Command,
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::Deserialize;
|
use string_error::into_err;
|
||||||
use string_error::static_err;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{AudioFileInfo, ReplayGainData, ReplayGainRawData, Tags},
|
types::{AudioFileInfo, ReplayGainData, ReplayGainRawData, Tags},
|
||||||
utils::format_detection::FileFormat,
|
utils::{format_detection::FileFormat, ffprobe},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AudioContainerFormat, AudioFormatError, BoxedError};
|
use super::{AudioContainerFormat, AudioFormatError, BoxedError};
|
||||||
|
@ -26,40 +25,6 @@ struct ExtractedData {
|
||||||
replaygain_data: Option<ReplayGainData>,
|
replaygain_data: Option<ReplayGainData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
struct FFProbeOutput {
|
|
||||||
pub format: FFProbeFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
struct FFProbeFormat {
|
|
||||||
pub tags: FFProbeTags,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
struct FFProbeTags {
|
|
||||||
#[serde(alias = "TITLE")]
|
|
||||||
pub title: String,
|
|
||||||
#[serde(default, alias = "ARTIST")]
|
|
||||||
pub artist: String,
|
|
||||||
|
|
||||||
#[serde(default, alias = "REPLAYGAIN_TRACK_PEAK")]
|
|
||||||
pub replaygain_track_peak: Option<String>,
|
|
||||||
#[serde(default, alias = "REPLAYGAIN_TRACK_GAIN")]
|
|
||||||
pub replaygain_track_gain: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FFProbeTags {
|
|
||||||
fn default() -> Self {
|
|
||||||
FFProbeTags {
|
|
||||||
title: "".to_string(),
|
|
||||||
artist: "".to_string(),
|
|
||||||
replaygain_track_peak: None,
|
|
||||||
replaygain_track_gain: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GenericFFMpegAudioFormat {
|
pub struct GenericFFMpegAudioFormat {
|
||||||
format_type: FileFormat,
|
format_type: FileFormat,
|
||||||
path: Box<PathBuf>,
|
path: Box<PathBuf>,
|
||||||
|
@ -70,36 +35,23 @@ pub struct GenericFFMpegAudioFormat {
|
||||||
|
|
||||||
impl GenericFFMpegAudioFormat {
|
impl GenericFFMpegAudioFormat {
|
||||||
fn analyze(&mut self) -> Result<(), BoxedError> {
|
fn analyze(&mut self) -> Result<(), BoxedError> {
|
||||||
let output = Command::new(crate::meta::FFPROBE)
|
let output = ffprobe::analyze(&self.path);
|
||||||
.args([
|
|
||||||
"-v",
|
|
||||||
"quiet",
|
|
||||||
"-print_format",
|
|
||||||
"json",
|
|
||||||
"-show_format",
|
|
||||||
&self.path.to_string_lossy(),
|
|
||||||
])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
if let Err(err) = output {
|
||||||
print!("{:?}", String::from_utf8(output.stderr).unwrap());
|
return Err(into_err(format!("{}", err)));
|
||||||
return Err(static_err("FFprobe Crashed"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_str = String::from_utf8(output.stdout).unwrap();
|
let output = output.unwrap();
|
||||||
let ffprobe_out: FFProbeOutput = serde_json::from_str(output_str.as_str())?;
|
|
||||||
|
|
||||||
let tags = ffprobe_out.format.tags;
|
|
||||||
|
|
||||||
self.extracted_data.tags = Tags {
|
self.extracted_data.tags = Tags {
|
||||||
title: tags.title,
|
title: output.tags.title,
|
||||||
artist: tags.artist,
|
artist: output.tags.artist,
|
||||||
};
|
};
|
||||||
|
|
||||||
if tags.replaygain_track_gain.is_some() && tags.replaygain_track_peak.is_some() {
|
if output.tags.replaygain_track_gain.is_some() && output.tags.replaygain_track_peak.is_some() {
|
||||||
self.extracted_data.replaygain_data = Some(ReplayGainData {
|
self.extracted_data.replaygain_data = Some(ReplayGainData {
|
||||||
track_gain: tags.replaygain_track_gain.unwrap(),
|
track_gain: output.tags.replaygain_track_gain.unwrap(),
|
||||||
track_peak: tags.replaygain_track_peak.unwrap(),
|
track_peak: output.tags.replaygain_track_peak.unwrap(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.extracted_data.replaygain_data = None;
|
self.extracted_data.replaygain_data = None;
|
||||||
|
@ -234,6 +186,7 @@ pub fn new_generic_ffmpeg_format_handler(
|
||||||
extracted_data: ExtractedData::default(),
|
extracted_data: ExtractedData::default(),
|
||||||
changes: Changes::default(),
|
changes: Changes::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.analyze()?;
|
handler.analyze()?;
|
||||||
|
|
||||||
Ok(handler)
|
Ok(handler)
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub mod ascii_reduce;
|
||||||
pub mod format_detection;
|
pub mod format_detection;
|
||||||
pub mod replaygain;
|
pub mod replaygain;
|
||||||
pub mod transcoder;
|
pub mod transcoder;
|
||||||
|
pub mod ffprobe;
|
||||||
|
|
||||||
pub mod formats;
|
pub mod formats;
|
||||||
pub(self) mod music_scanner;
|
pub(self) mod music_scanner;
|
||||||
|
|
Loading…
Reference in a new issue