diff --git a/src/commands/copy.rs b/src/commands/copy.rs index 6693af0..dba7a02 100644 --- a/src/commands/copy.rs +++ b/src/commands/copy.rs @@ -37,6 +37,8 @@ pub struct CopyCommandArgs { #[clap(long)] pub no_skip_existing: bool, #[clap(long)] + pub skip_same_extension: bool, + #[clap(long)] pub single_directory: bool, } @@ -123,7 +125,10 @@ pub fn copy_command( Ok(()) } -fn copy_file(file: &relative_file::RelativeFile, copy_args: &CopyCommandArgs) -> Result<(), Box> { +fn copy_file( + file: &relative_file::RelativeFile, + copy_args: &CopyCommandArgs, +) -> Result<(), Box> { let from_path = file.join_path_to(); let to_path_dest = PathBuf::from_str(copy_args.dest.as_str()).expect("invalid destination"); @@ -178,6 +183,12 @@ fn transcode_file( } }; + if copy_args.skip_same_extension { + if config.file_extension.as_ref().unwrap() == &file.extension().unwrap() { + return copy_file(file.as_relative_file(), copy_args); + } + } + let to_path_dest = PathBuf::from_str(copy_args.dest.as_str()).expect("invalid destination"); let to_path = match copy_args.single_directory { true => to_path_dest.join(new_filename_full), diff --git a/src/commands/tags/get.rs b/src/commands/tags/get.rs index da0cf7a..80ea8dc 100644 --- a/src/commands/tags/get.rs +++ b/src/commands/tags/get.rs @@ -19,6 +19,7 @@ pub struct GetTagsCommandArgs { struct Tags { title: String, artist: String, + album: Option, track_number: Option, } @@ -26,6 +27,7 @@ fn from_main_tags(tags: &crate::types::Tags) -> Tags { Tags { title: tags.title.clone(), artist: tags.artist.clone(), + album: tags.album.clone(), track_number: tags.track_number, } } diff --git a/src/commands/tags/set.rs b/src/commands/tags/set.rs index 81aff7d..4232945 100644 --- a/src/commands/tags/set.rs +++ b/src/commands/tags/set.rs @@ -11,6 +11,8 @@ pub struct SetTagsCommandArgs { pub title: Option, #[clap(long)] pub artist: Option, + #[clap(long)] + pub album: Option, } pub fn set_tags_command( @@ -34,6 +36,10 @@ pub fn set_tags_command( handler.set_artist(artist.clone())?; } + if let Some(album) = &add_tags_args.album { + handler.set_album(album.clone())?; + } + handler.save_changes()?; } diff --git a/src/types.rs b/src/types.rs index dc9dae1..a340b84 100644 --- a/src/types.rs +++ b/src/types.rs @@ -5,23 +5,14 @@ use serde::Deserialize; use crate::utils::format_detection::FileFormat; -#[derive(Debug, Clone)] +#[derive(Default, Debug, Clone)] pub struct Tags { pub title: String, pub artist: String, + pub album: Option, pub track_number: Option, } -impl Default for Tags { - fn default() -> Self { - Tags { - title: "".to_string(), - artist: "".to_string(), - track_number: None, - } - } -} - #[derive(Default, Debug, Clone)] pub struct AudioFileInfo { pub tags: Tags, diff --git a/src/utils/formats/handlers/ffprobe.rs b/src/utils/formats/handlers/ffprobe.rs index e229190..edc9f9e 100644 --- a/src/utils/formats/handlers/ffprobe.rs +++ b/src/utils/formats/handlers/ffprobe.rs @@ -13,11 +13,7 @@ use crate::{ #[cfg(feature = "replaygain")] use replaygain::{ReplayGainData, ReplayGainRawData}; -#[derive(Default)] -struct Changes { - title: Option, - artist: Option, -} +use super::Changes; #[derive(Default)] struct ExtractedData { @@ -47,6 +43,7 @@ impl GenericFFMpegAudioFormat { self.extracted_data.tags = Tags { title: output.tags.title, artist: output.tags.artist, + album: output.tags.album, track_number: output.tags.track_number, }; @@ -78,6 +75,10 @@ impl FormatHandler for GenericFFMpegAudioFormat { tags.artist = artist.clone(); } + if let Some(album) = &self.changes.album { + tags.album = Some(album.clone()); + } + if !allow_missing { if tags.title.is_empty() { return Err(Box::new(AudioFormatError::MissingTitle)); @@ -110,13 +111,18 @@ impl FormatHandler for GenericFFMpegAudioFormat { Ok(()) } + fn set_album(&mut self, album: String) -> Result<(), BoxedError> { + self.changes.album = Some(album); + Ok(()) + } + #[cfg(feature = "replaygain")] fn set_replaygain_data(&mut self, _data: ReplayGainRawData) -> Result<(), BoxedError> { panic!("ffprobe doesn't support setting replaygain data, check supports_replaygain()") } fn save_changes(&mut self) -> Result<(), BoxedError> { - if self.changes.title.is_none() && self.changes.artist.is_none() { + if !self.changes.changed() { return Ok(()); } @@ -148,6 +154,13 @@ impl FormatHandler for GenericFFMpegAudioFormat { ]) } + if let Some(album) = &self.changes.album { + args.extend(vec![ + "-metadata".to_string(), + format!("album={}", album.as_str()), + ]) + } + args.push(temp_file.to_string_lossy().to_string()); let output = Command::new(crate::meta::FFMPEG).args(args).output()?; diff --git a/src/utils/formats/handlers/flac.rs b/src/utils/formats/handlers/flac.rs index 4580c61..0c2b209 100644 --- a/src/utils/formats/handlers/flac.rs +++ b/src/utils/formats/handlers/flac.rs @@ -30,6 +30,8 @@ impl FormatHandler for FLACAudioFormat { fn get_tags(&self, allow_missing: bool) -> Result { let title = flac_get_first(&self.flac_tags, "TITLE"); let artist = flac_get_first(&self.flac_tags, "ARTIST"); + let album = flac_get_first(&self.flac_tags, "ALBUM"); + let mut track_number = flac_get_first(&self.flac_tags, "TRACKNUMBER"); if !allow_missing { @@ -51,6 +53,7 @@ impl FormatHandler for FLACAudioFormat { Ok(Tags { title: title.unwrap(), artist: artist.unwrap(), + album, track_number: match track_number { Some(num) => match num.parse::() { Ok(n) => Some(n), @@ -92,6 +95,13 @@ impl FormatHandler for FLACAudioFormat { Ok(()) } + fn set_album(&mut self, album: String) -> Result<(), BoxedError> { + self.flac_tags.remove_vorbis("ALBUM"); + self.flac_tags.set_vorbis("ALBUM", vec![album]); + + Ok(()) + } + #[cfg(feature = "replaygain")] fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> { self.flac_tags.remove_vorbis("REPLAYGAIN_TRACK_GAIN"); diff --git a/src/utils/formats/handlers/id3.rs b/src/utils/formats/handlers/id3.rs index fefd46d..de19c81 100644 --- a/src/utils/formats/handlers/id3.rs +++ b/src/utils/formats/handlers/id3.rs @@ -20,6 +20,7 @@ impl FormatHandler for ID3AudioFormat { fn get_tags(&self, allow_missing: bool) -> Result { let title = self.id3_tags.title(); let artist = self.id3_tags.artist(); + let album = self.id3_tags.album(); if !allow_missing { if title.is_none() { @@ -33,10 +34,10 @@ impl FormatHandler for ID3AudioFormat { Ok(Tags { title: String::from(title.unwrap()), artist: String::from(artist.unwrap()), + album: album.map(|f| f.to_string()), track_number: self .id3_tags - .track() - .map(|track_number| track_number as u64), + .track().map(|track_number| track_number as u64), }) } @@ -82,6 +83,12 @@ impl FormatHandler for ID3AudioFormat { Ok(()) } + fn set_album(&mut self, album: String) -> Result<(), BoxedError> { + self.id3_tags.set_album(album); + + Ok(()) + } + #[cfg(feature = "replaygain")] fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> { let frames = self.id3_tags.remove("TXXX"); diff --git a/src/utils/formats/handlers/mod.rs b/src/utils/formats/handlers/mod.rs index 8c04893..f734cad 100644 --- a/src/utils/formats/handlers/mod.rs +++ b/src/utils/formats/handlers/mod.rs @@ -23,6 +23,25 @@ mod taglib; )))] compile_error!("at least one extractor feature must be enabled"); +// For changed tags on implementations that call a command line utility to set tags +#[derive(Default)] + struct Changes { + title: Option, + artist: Option, + album: Option, + track_number: Option, +} + +impl Changes { + #[inline] + fn changed(&self) -> bool { + [self.title.is_some(), + self.artist.is_some(), + self.album.is_some(), + self.track_number.is_some()].contains(&true) + } +} + type NewHandlerFuncReturn = Result, BoxedError>; type NewHandlerFunc = fn(path: &PathBuf, file_format: Option) -> NewHandlerFuncReturn; diff --git a/src/utils/formats/handlers/taglib.rs b/src/utils/formats/handlers/taglib.rs index 6912f01..c2a73fb 100644 --- a/src/utils/formats/handlers/taglib.rs +++ b/src/utils/formats/handlers/taglib.rs @@ -45,6 +45,7 @@ impl FormatHandler for TaglibAudioFormat { Ok(Tags { title: title.unwrap(), artist: artist.unwrap(), + album: tags.album(), track_number: tags.track(), }) } @@ -104,6 +105,13 @@ impl FormatHandler for TaglibAudioFormat { Ok(()) } + fn set_album(&mut self, album: String) -> Result<(), BoxedError> { + let mut tags = self.file.tag()?; + tags.set_album(album); + + Ok(()) + } + #[cfg(feature = "replaygain")] fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> { if let Some(format) = self.file_format { diff --git a/src/utils/formats/mod.rs b/src/utils/formats/mod.rs index 9334843..806e52b 100644 --- a/src/utils/formats/mod.rs +++ b/src/utils/formats/mod.rs @@ -23,6 +23,7 @@ pub trait FormatHandler { fn set_title(&mut self, title: String) -> Result<(), BoxedError>; fn set_artist(&mut self, artist: String) -> Result<(), BoxedError>; + fn set_album(&mut self, artist: String) -> Result<(), BoxedError>; #[cfg(feature = "replaygain")] fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError>;