move replaygain stuff into its own crate, run clippy
This commit is contained in:
parent
feee847463
commit
d5ab74d17a
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -683,6 +683,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"metaflac",
|
"metaflac",
|
||||||
"notify",
|
"notify",
|
||||||
|
"replaygain",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
@ -876,6 +877,14 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "replaygain"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
|
@ -41,6 +41,9 @@ metaflac = { version = "0.2", optional = true }
|
||||||
taglib = { path = "./modules/taglib", optional = true }
|
taglib = { path = "./modules/taglib", optional = true }
|
||||||
ffprobe = { path = "./modules/ffprobe" }
|
ffprobe = { path = "./modules/ffprobe" }
|
||||||
|
|
||||||
|
# replaygain_analysis
|
||||||
|
replaygain = { path = "./modules/replaygain", optional = true }
|
||||||
|
|
||||||
# for genhtml command
|
# for genhtml command
|
||||||
html-escape = { version = "0.2", optional = true }
|
html-escape = { version = "0.2", optional = true }
|
||||||
urlencoding = { version = "2", optional = true }
|
urlencoding = { version = "2", optional = true }
|
||||||
|
@ -73,4 +76,4 @@ ffprobe_extractor = [] # If to allow using ffmpeg/ffprobe as a fallback tag extr
|
||||||
|
|
||||||
command_genhtml = ["dep:html-escape", "dep:urlencoding"]
|
command_genhtml = ["dep:html-escape", "dep:urlencoding"]
|
||||||
|
|
||||||
replaygain = []
|
replaygain = ["dep:replaygain"]
|
|
@ -37,12 +37,12 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs");
|
let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs");
|
||||||
let mut file = BufWriter::new(File::create(&path).unwrap());
|
let mut file = BufWriter::new(File::create(path).unwrap());
|
||||||
write!(
|
write!(
|
||||||
&mut file,
|
&mut file,
|
||||||
"static MAPPINGS: phf::Map<char, &'static str> = {}",
|
"static MAPPINGS: phf::Map<char, &'static str> = {}",
|
||||||
map.build()
|
map.build()
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
write!(&mut file, ";\n").unwrap();
|
writeln!(&mut file, ";").unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
assert_eq!(crate::reduce("öwo owö 😊".to_string()), "owo owo ");
|
assert_eq!(crate::reduce("öwo owö 😊".to_string()), "owo owo ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reduce(input: String) -> String {
|
pub fn reduce(input: String) -> String {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod types;
|
|
||||||
mod ffprobe_output;
|
mod ffprobe_output;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
use std::{convert::Into, path::Path, process::Command};
|
use std::{convert::Into, path::Path, process::Command};
|
||||||
|
|
||||||
use self::errors::{AnalyzeError, FFProbeError};
|
use self::errors::{AnalyzeError, FFProbeError};
|
||||||
|
|
||||||
fn extract(path: &Path, ffprobe_command: Option<&str>) -> Result<ffprobe_output::FFProbeOutput, AnalyzeError> {
|
fn extract(
|
||||||
|
path: &Path,
|
||||||
|
ffprobe_command: Option<&str>,
|
||||||
|
) -> Result<ffprobe_output::FFProbeOutput, AnalyzeError> {
|
||||||
let output = Command::new(ffprobe_command.unwrap_or("ffprobe"))
|
let output = Command::new(ffprobe_command.unwrap_or("ffprobe"))
|
||||||
.args([
|
.args([
|
||||||
"-v",
|
"-v",
|
||||||
|
@ -39,7 +42,10 @@ fn extract(path: &Path, ffprobe_command: Option<&str>) -> Result<ffprobe_output:
|
||||||
Ok(ffprobe_out.unwrap())
|
Ok(ffprobe_out.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyze(path: &Path, ffprobe_command: Option<&str>) -> Result<types::FFProbeData, AnalyzeError> {
|
pub fn analyze(
|
||||||
|
path: &Path,
|
||||||
|
ffprobe_command: Option<&str>,
|
||||||
|
) -> Result<types::FFProbeData, AnalyzeError> {
|
||||||
let raw_data = extract(path, ffprobe_command)?;
|
let raw_data = extract(path, ffprobe_command)?;
|
||||||
|
|
||||||
let mut data = types::FFProbeData {
|
let mut data = types::FFProbeData {
|
||||||
|
|
8
modules/replaygain/Cargo.toml
Normal file
8
modules/replaygain/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "replaygain"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
|
@ -1,15 +1,75 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt,
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Command,
|
process::{self, Command},
|
||||||
};
|
};
|
||||||
|
|
||||||
use string_error::static_err;
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ReplayGainRawData {
|
||||||
|
pub track_gain: f64,
|
||||||
|
pub track_peak: f64,
|
||||||
|
}
|
||||||
|
|
||||||
use crate::types::ReplayGainRawData;
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ReplayGainData {
|
||||||
|
pub track_gain: String,
|
||||||
|
pub track_peak: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn analyze_replaygain_track(path: PathBuf) -> Result<ReplayGainRawData, Box<dyn std::error::Error>> {
|
impl ReplayGainRawData {
|
||||||
let output = Command::new(crate::meta::FFMPEG)
|
pub fn to_normal(&self, is_ogg_opus: bool) -> ReplayGainData {
|
||||||
|
if is_ogg_opus {
|
||||||
|
ReplayGainData {
|
||||||
|
track_gain: format!("{:.6}", (self.track_gain * 256.0).ceil()),
|
||||||
|
track_peak: "".to_string(), // Not Required
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReplayGainData {
|
||||||
|
track_gain: format!("{:.2} dB", self.track_gain),
|
||||||
|
track_peak: format!("{:.6}", self.track_peak),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FFMpegError {
|
||||||
|
pub exit_status: process::ExitStatus,
|
||||||
|
pub stderr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FFMpegError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ffmpeg exited with error code {}, stderr: {}",
|
||||||
|
self.exit_status.code().unwrap(),
|
||||||
|
self.stderr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
FFMpegError(FFMpegError),
|
||||||
|
ParseError(std::num::ParseFloatError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::FFMpegError(err) => write!(f, "{}", err),
|
||||||
|
Error::ParseError(err) => write!(f, "{}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn analyze_replaygain_track(
|
||||||
|
path: PathBuf,
|
||||||
|
ffmpeg_command: Option<&str>,
|
||||||
|
) -> Result<ReplayGainRawData, Error> {
|
||||||
|
let output = Command::new(ffmpeg_command.unwrap_or("ffmpeg"))
|
||||||
.args([
|
.args([
|
||||||
"-hide_banner",
|
"-hide_banner",
|
||||||
"-nostats",
|
"-nostats",
|
||||||
|
@ -25,13 +85,16 @@ pub fn analyze_replaygain_track(path: PathBuf) -> Result<ReplayGainRawData, Box<
|
||||||
"null",
|
"null",
|
||||||
"/dev/null",
|
"/dev/null",
|
||||||
])
|
])
|
||||||
.output()?;
|
.output();
|
||||||
|
|
||||||
|
let output = output.unwrap();
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
print!("{:?}", String::from_utf8(output.stderr).unwrap());
|
return Err(Error::FFMpegError(FFMpegError {
|
||||||
return Err(static_err("FFmpeg Crashed"));
|
exit_status: output.status,
|
||||||
|
stderr: String::from_utf8(output.stderr).unwrap(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// info we need is in stdout
|
// info we need is in stdout
|
||||||
let output_str = String::from_utf8(output.stderr).unwrap();
|
let output_str = String::from_utf8(output.stderr).unwrap();
|
||||||
|
|
||||||
|
@ -72,7 +135,10 @@ pub fn analyze_replaygain_track(path: PathBuf) -> Result<ReplayGainRawData, Box<
|
||||||
l.next();
|
l.next();
|
||||||
|
|
||||||
let gain = l.next().unwrap().trim().trim_end_matches(" LUFS");
|
let gain = l.next().unwrap().trim().trim_end_matches(" LUFS");
|
||||||
let gain = gain.parse::<f64>()?;
|
let gain = match gain.parse::<f64>() {
|
||||||
|
Ok(parsed) => Ok(parsed),
|
||||||
|
Err(err) => Err(Error::ParseError(err)),
|
||||||
|
}?;
|
||||||
|
|
||||||
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Gain_calculation
|
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Gain_calculation
|
||||||
// "In order to maintain backwards compatibility with RG1, RG2 uses a -18 LUFS reference, which based on lots of music, can give similar loudness compared to RG1."
|
// "In order to maintain backwards compatibility with RG1, RG2 uses a -18 LUFS reference, which based on lots of music, can give similar loudness compared to RG1."
|
||||||
|
@ -87,7 +153,10 @@ pub fn analyze_replaygain_track(path: PathBuf) -> Result<ReplayGainRawData, Box<
|
||||||
|
|
||||||
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Loudness_normalization
|
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Loudness_normalization
|
||||||
let peak = l.next().unwrap().trim().trim_end_matches(" dBFS");
|
let peak = l.next().unwrap().trim().trim_end_matches(" dBFS");
|
||||||
let peak = peak.parse::<f64>()?;
|
let peak = match peak.parse::<f64>() {
|
||||||
|
Ok(parsed) => Ok(parsed),
|
||||||
|
Err(err) => Err(Error::ParseError(err)),
|
||||||
|
}?;
|
||||||
let peak = f64::powf(10_f64, peak / 20.0_f64);
|
let peak = f64::powf(10_f64, peak / 20.0_f64);
|
||||||
track_peak = peak;
|
track_peak = peak;
|
||||||
}
|
}
|
|
@ -6,11 +6,11 @@ use crate::args::CLIArgs;
|
||||||
use crate::types::AudioFileInfo;
|
use crate::types::AudioFileInfo;
|
||||||
use crate::types::File;
|
use crate::types::File;
|
||||||
use crate::utils::formats::get_format_handler;
|
use crate::utils::formats::get_format_handler;
|
||||||
#[cfg(feature = "replaygain")]
|
|
||||||
use crate::utils::replaygain::analyze_replaygain_track;
|
|
||||||
use crate::utils::scan_for_music;
|
use crate::utils::scan_for_music;
|
||||||
|
|
||||||
use ascii_reduce::reduce;
|
use ascii_reduce::reduce;
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
|
use replaygain::analyze_replaygain_track;
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::Args)]
|
#[derive(Debug, Clone, clap::Args)]
|
||||||
pub struct ProcessCommandArgs {
|
pub struct ProcessCommandArgs {
|
||||||
|
@ -153,7 +153,8 @@ pub fn add_replaygain_tags(file: &File, force: bool) -> Result<(), Box<dyn std::
|
||||||
file.join_path_from_source()
|
file.join_path_from_source()
|
||||||
);
|
);
|
||||||
|
|
||||||
let replaygain_data = analyze_replaygain_track(file.join_path_to())?;
|
let replaygain_data = analyze_replaygain_track(file.join_path_to(), Some(crate::meta::FFMPEG))
|
||||||
|
.expect("could not analyze replaygain");
|
||||||
|
|
||||||
let mut handler = get_format_handler(file)?;
|
let mut handler = get_format_handler(file)?;
|
||||||
|
|
||||||
|
|
31
src/types.rs
31
src/types.rs
|
@ -21,39 +21,12 @@ impl Default for Tags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
|
||||||
pub struct ReplayGainData {
|
|
||||||
pub track_gain: String,
|
|
||||||
pub track_peak: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ReplayGainRawData {
|
|
||||||
pub track_gain: f64,
|
|
||||||
pub track_peak: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReplayGainRawData {
|
|
||||||
pub fn to_normal(&self, is_ogg_opus: bool) -> ReplayGainData {
|
|
||||||
if is_ogg_opus {
|
|
||||||
ReplayGainData {
|
|
||||||
track_gain: format!("{:.6}", (self.track_gain * 256.0).ceil()),
|
|
||||||
track_peak: "".to_string(), // Not Required
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ReplayGainData {
|
|
||||||
track_gain: format!("{:.2} dB", self.track_gain),
|
|
||||||
track_peak: format!("{:.6}", self.track_peak),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct AudioFileInfo {
|
pub struct AudioFileInfo {
|
||||||
pub tags: Tags,
|
pub tags: Tags,
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
pub contains_replaygain: bool,
|
pub contains_replaygain: bool,
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
pub supports_replaygain: bool,
|
pub supports_replaygain: bool,
|
||||||
pub format: Option<FileFormat>,
|
pub format: Option<FileFormat>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,15 @@ use std::{
|
||||||
use string_error::into_err;
|
use string_error::into_err;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{AudioFileInfo, ReplayGainData, ReplayGainRawData, Tags},
|
meta,
|
||||||
|
types::{AudioFileInfo, Tags},
|
||||||
utils::format_detection::{detect_format, FileFormat},
|
utils::format_detection::{detect_format, FileFormat},
|
||||||
utils::formats::{AudioFormatError, BoxedError, FormatHandler}, meta,
|
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
|
use replaygain::{ReplayGainData, ReplayGainRawData};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Changes {
|
struct Changes {
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
|
@ -20,6 +24,7 @@ struct Changes {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ExtractedData {
|
struct ExtractedData {
|
||||||
tags: Tags,
|
tags: Tags,
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
replaygain_data: Option<ReplayGainData>,
|
replaygain_data: Option<ReplayGainData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +52,7 @@ impl GenericFFMpegAudioFormat {
|
||||||
track_number: output.tags.track_number,
|
track_number: output.tags.track_number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
if output.tags.replaygain_track_gain.is_some()
|
if output.tags.replaygain_track_gain.is_some()
|
||||||
&& output.tags.replaygain_track_peak.is_some()
|
&& output.tags.replaygain_track_peak.is_some()
|
||||||
{
|
{
|
||||||
|
@ -86,10 +92,12 @@ impl FormatHandler for GenericFFMpegAudioFormat {
|
||||||
Ok(tags)
|
Ok(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn contains_replaygain_tags(&self) -> bool {
|
fn contains_replaygain_tags(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn supports_replaygain(&self) -> bool {
|
fn supports_replaygain(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -104,6 +112,7 @@ impl FormatHandler for GenericFFMpegAudioFormat {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn set_replaygain_data(&mut self, _data: ReplayGainRawData) -> Result<(), BoxedError> {
|
fn set_replaygain_data(&mut self, _data: ReplayGainRawData) -> Result<(), BoxedError> {
|
||||||
panic!("ffprobe doesn't support setting replaygain data, check supports_replaygain()")
|
panic!("ffprobe doesn't support setting replaygain data, check supports_replaygain()")
|
||||||
}
|
}
|
||||||
|
@ -158,7 +167,9 @@ impl FormatHandler for GenericFFMpegAudioFormat {
|
||||||
) -> Result<AudioFileInfo, BoxedError> {
|
) -> Result<AudioFileInfo, BoxedError> {
|
||||||
Ok(AudioFileInfo {
|
Ok(AudioFileInfo {
|
||||||
tags: self.get_tags(allow_missing_tags)?,
|
tags: self.get_tags(allow_missing_tags)?,
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
contains_replaygain: self.contains_replaygain_tags(),
|
contains_replaygain: self.contains_replaygain_tags(),
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
supports_replaygain: self.supports_replaygain(),
|
supports_replaygain: self.supports_replaygain(),
|
||||||
format: Some(self.file_format),
|
format: Some(self.file_format),
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{AudioFileInfo, ReplayGainRawData, Tags},
|
types::{AudioFileInfo, Tags},
|
||||||
utils::format_detection::FileFormat,
|
utils::format_detection::FileFormat,
|
||||||
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
|
use replaygain::ReplayGainRawData;
|
||||||
|
|
||||||
pub struct FLACAudioFormat {
|
pub struct FLACAudioFormat {
|
||||||
flac_tags: metaflac::Tag,
|
flac_tags: metaflac::Tag,
|
||||||
path: Box<PathBuf>,
|
path: Box<PathBuf>,
|
||||||
|
@ -58,6 +61,7 @@ impl FormatHandler for FLACAudioFormat {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn contains_replaygain_tags(&self) -> bool {
|
fn contains_replaygain_tags(&self) -> bool {
|
||||||
let track_gain = flac_get_first(&self.flac_tags, "REPLAYGAIN_TRACK_GAIN");
|
let track_gain = flac_get_first(&self.flac_tags, "REPLAYGAIN_TRACK_GAIN");
|
||||||
let track_peak = flac_get_first(&self.flac_tags, "REPLAYGAIN_TRACK_PEAK");
|
let track_peak = flac_get_first(&self.flac_tags, "REPLAYGAIN_TRACK_PEAK");
|
||||||
|
@ -69,6 +73,7 @@ impl FormatHandler for FLACAudioFormat {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn supports_replaygain(&self) -> bool {
|
fn supports_replaygain(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -87,6 +92,7 @@ impl FormatHandler for FLACAudioFormat {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
|
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
|
||||||
self.flac_tags.remove_vorbis("REPLAYGAIN_TRACK_GAIN");
|
self.flac_tags.remove_vorbis("REPLAYGAIN_TRACK_GAIN");
|
||||||
self.flac_tags.remove_vorbis("REPLAYGAIN_TRACK_PEAK");
|
self.flac_tags.remove_vorbis("REPLAYGAIN_TRACK_PEAK");
|
||||||
|
@ -114,7 +120,9 @@ impl FormatHandler for FLACAudioFormat {
|
||||||
) -> Result<AudioFileInfo, BoxedError> {
|
) -> Result<AudioFileInfo, BoxedError> {
|
||||||
Ok(AudioFileInfo {
|
Ok(AudioFileInfo {
|
||||||
tags: self.get_tags(allow_missing_tags)?,
|
tags: self.get_tags(allow_missing_tags)?,
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
contains_replaygain: self.contains_replaygain_tags(),
|
contains_replaygain: self.contains_replaygain_tags(),
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
supports_replaygain: self.supports_replaygain(),
|
supports_replaygain: self.supports_replaygain(),
|
||||||
format: Some(FileFormat::FLAC),
|
format: Some(FileFormat::FLAC),
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,11 +3,14 @@ use std::path::PathBuf;
|
||||||
use id3::TagLike;
|
use id3::TagLike;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{AudioFileInfo, ReplayGainRawData, Tags},
|
types::{AudioFileInfo, Tags},
|
||||||
utils::format_detection::FileFormat,
|
utils::format_detection::FileFormat,
|
||||||
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
|
use replaygain::ReplayGainRawData;
|
||||||
|
|
||||||
pub struct ID3AudioFormat {
|
pub struct ID3AudioFormat {
|
||||||
id3_tags: id3::Tag,
|
id3_tags: id3::Tag,
|
||||||
path: Box<PathBuf>,
|
path: Box<PathBuf>,
|
||||||
|
@ -37,6 +40,7 @@ impl FormatHandler for ID3AudioFormat {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn contains_replaygain_tags(&self) -> bool {
|
fn contains_replaygain_tags(&self) -> bool {
|
||||||
let frames = self.id3_tags.frames();
|
let frames = self.id3_tags.frames();
|
||||||
|
|
||||||
|
@ -61,6 +65,7 @@ impl FormatHandler for ID3AudioFormat {
|
||||||
contains_replaygain_tags
|
contains_replaygain_tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn supports_replaygain(&self) -> bool {
|
fn supports_replaygain(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -77,6 +82,7 @@ impl FormatHandler for ID3AudioFormat {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
|
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
|
||||||
let frames = self.id3_tags.remove("TXXX");
|
let frames = self.id3_tags.remove("TXXX");
|
||||||
|
|
||||||
|
@ -121,7 +127,9 @@ impl FormatHandler for ID3AudioFormat {
|
||||||
Ok(AudioFileInfo {
|
Ok(AudioFileInfo {
|
||||||
tags: self.get_tags(allow_missing_tags)?,
|
tags: self.get_tags(allow_missing_tags)?,
|
||||||
format: Some(FileFormat::MP3),
|
format: Some(FileFormat::MP3),
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
supports_replaygain: self.supports_replaygain(),
|
supports_replaygain: self.supports_replaygain(),
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
contains_replaygain: self.contains_replaygain_tags(),
|
contains_replaygain: self.contains_replaygain_tags(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,14 @@ use taglib::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{AudioFileInfo, ReplayGainRawData, Tags},
|
types::{AudioFileInfo, Tags},
|
||||||
utils::format_detection::FileFormat,
|
utils::format_detection::FileFormat,
|
||||||
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
utils::formats::{AudioFormatError, BoxedError, FormatHandler},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
|
use replaygain::ReplayGainRawData;
|
||||||
|
|
||||||
pub struct TaglibAudioFormat {
|
pub struct TaglibAudioFormat {
|
||||||
file: taglib::TagLibFile,
|
file: taglib::TagLibFile,
|
||||||
file_format: Option<FileFormat>,
|
file_format: Option<FileFormat>,
|
||||||
|
@ -46,6 +49,7 @@ impl FormatHandler for TaglibAudioFormat {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn contains_replaygain_tags(&self) -> bool {
|
fn contains_replaygain_tags(&self) -> bool {
|
||||||
if let Some(format) = self.file_format {
|
if let Some(format) = self.file_format {
|
||||||
if format == FileFormat::OggOpus {
|
if format == FileFormat::OggOpus {
|
||||||
|
@ -67,6 +71,7 @@ impl FormatHandler for TaglibAudioFormat {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn supports_replaygain(&self) -> bool {
|
fn supports_replaygain(&self) -> bool {
|
||||||
if let Some(format) = self.file_format {
|
if let Some(format) = self.file_format {
|
||||||
return matches!(
|
return matches!(
|
||||||
|
@ -99,6 +104,7 @@ impl FormatHandler for TaglibAudioFormat {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
|
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
|
||||||
if let Some(format) = self.file_format {
|
if let Some(format) = self.file_format {
|
||||||
match format {
|
match format {
|
||||||
|
@ -143,7 +149,9 @@ impl FormatHandler for TaglibAudioFormat {
|
||||||
) -> Result<AudioFileInfo, BoxedError> {
|
) -> Result<AudioFileInfo, BoxedError> {
|
||||||
Ok(AudioFileInfo {
|
Ok(AudioFileInfo {
|
||||||
tags: self.get_tags(allow_missing_tags)?,
|
tags: self.get_tags(allow_missing_tags)?,
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
contains_replaygain: self.contains_replaygain_tags(),
|
contains_replaygain: self.contains_replaygain_tags(),
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
supports_replaygain: self.supports_replaygain(),
|
supports_replaygain: self.supports_replaygain(),
|
||||||
format: self.file_format,
|
format: self.file_format,
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,19 +5,24 @@ use std::path::Path;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::types::{AudioFileInfo, File, ReplayGainRawData, Tags};
|
|
||||||
|
|
||||||
use super::format_detection::detect_format;
|
use super::format_detection::detect_format;
|
||||||
|
use crate::types::{AudioFileInfo, File, Tags};
|
||||||
|
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
|
use replaygain::ReplayGainRawData;
|
||||||
|
|
||||||
type BoxedError = Box<dyn Error>;
|
type BoxedError = Box<dyn Error>;
|
||||||
|
|
||||||
pub trait FormatHandler {
|
pub trait FormatHandler {
|
||||||
fn get_tags(&self, allow_missing: bool) -> Result<Tags, BoxedError>;
|
fn get_tags(&self, allow_missing: bool) -> Result<Tags, BoxedError>;
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn contains_replaygain_tags(&self) -> bool;
|
fn contains_replaygain_tags(&self) -> bool;
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn supports_replaygain(&self) -> bool;
|
fn supports_replaygain(&self) -> bool;
|
||||||
|
|
||||||
fn set_title(&mut self, title: String) -> Result<(), BoxedError>;
|
fn set_title(&mut self, title: String) -> Result<(), BoxedError>;
|
||||||
fn set_artist(&mut self, artist: String) -> Result<(), BoxedError>;
|
fn set_artist(&mut self, artist: String) -> Result<(), BoxedError>;
|
||||||
|
#[cfg(feature = "replaygain")]
|
||||||
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError>;
|
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError>;
|
||||||
|
|
||||||
fn save_changes(&mut self) -> Result<(), BoxedError>;
|
fn save_changes(&mut self) -> Result<(), BoxedError>;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
pub mod format_detection;
|
pub mod format_detection;
|
||||||
#[cfg(feature = "replaygain")]
|
|
||||||
pub mod replaygain;
|
|
||||||
pub mod transcoder;
|
pub mod transcoder;
|
||||||
|
|
||||||
pub mod formats;
|
pub mod formats;
|
||||||
|
|
Loading…
Reference in a new issue