diff --git a/src/bin/id3-image-embed.rs b/src/bin/id3-image-embed.rs index aefdab2..cfe0724 100644 --- a/src/bin/id3-image-embed.rs +++ b/src/bin/id3-image-embed.rs @@ -1,21 +1,33 @@ -use std::env; use std::process; +use std::path::PathBuf; +use structopt::StructOpt; use id3_image::embed_image; +#[derive(StructOpt, Debug)] +#[structopt(name = "id3-image-embed")] +struct Opt { + /// Verbose mode (-v, -vv, -vvv, etc.) + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] + verbose: u8, + + /// Music file to embed image into + #[structopt(name = "music-file.mp3", required = true, parse(from_os_str))] + music_filename: PathBuf, + + /// Image file to embed + #[structopt(name = "image-file.jpg", required = true, parse(from_os_str))] + image_filename: PathBuf, +} + fn main() { - let args: Vec<_> = env::args().collect(); + let opt = Opt::from_args(); - if args.len() < 3 { - eprintln!("USAGE: id3-image-embed "); - process::exit(1); - } - - let music_filename = args[1].clone(); - let image_filename = args[2].clone(); - - if let Err(e) = embed_image(&music_filename, &image_filename) { + if let Err(e) = embed_image(&opt.music_filename, &opt.image_filename) { eprintln!("{}", e); process::exit(1); } + if opt.verbose >= 1 { + println!("Embedded {:?} into {:?}", opt.image_filename, opt.music_filename); + } } diff --git a/src/bin/id3-image-extract.rs b/src/bin/id3-image-extract.rs index 1a14fbf..6169f0d 100644 --- a/src/bin/id3-image-extract.rs +++ b/src/bin/id3-image-extract.rs @@ -1,28 +1,41 @@ -use std::env; use std::process; use std::path::PathBuf; +use structopt::StructOpt; use id3_image::extract_image; +#[derive(StructOpt, Debug)] +#[structopt(name = "id3-image-embed")] +struct Opt { + /// Verbose mode (-v, -vv, -vvv, etc.) + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] + verbose: u8, + + /// Music file to extract image from + #[structopt(name = "music-file.mp3", required = true, parse(from_os_str))] + music_filename: PathBuf, + + /// (Optional) Output image: defaults to music filename with .jpg extension + #[structopt(name = "image-file.jpg", parse(from_os_str))] + image_filename: Option, +} + fn main() { - let args: Vec<_> = env::args().collect(); + let opt = Opt::from_args(); - if args.len() < 2 { - eprintln!("USAGE: id3-image-extract [image-file]"); - process::exit(1); - } + let image_filename = opt.image_filename.clone(). + unwrap_or_else(|| opt.music_filename.with_extension("jpg")); - let music_filename = args[1].clone(); - let image_filename = args.get(2).cloned().unwrap_or_else(|| replace_extension(&music_filename, "jpg")); - - if let Err(e) = extract_image(&music_filename, &image_filename) { + if let Err(e) = extract_image(&opt.music_filename, &image_filename) { eprintln!("{}", e); process::exit(1); } -} -fn replace_extension(path: &str, replacement: &str) -> String { - let mut path = PathBuf::from(&path); - path.set_extension(replacement); - path.to_string_lossy().to_string() + if opt.verbose == 1 { + // then just print the output filename for scripting purposes: + println!("{}", image_filename.display()); + } else if opt.verbose >= 2 { + // show a longer status message: + println!("Extracted cover art from {:?} to {:?}", opt.music_filename, image_filename); + } } diff --git a/src/lib.rs b/src/lib.rs index 059ffc1..3e37f4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,12 @@ +use std::path::Path; use std::error::Error; -pub fn embed_image(music_filename: &str, image_filename: &str) -> Result<(), Box> { +pub fn embed_image(music_filename: &Path, image_filename: &Path) -> Result<(), Box> { let mut tag = id3::Tag::read_from_path(&music_filename). - map_err(|e| format!("Error reading music file {}: {}", music_filename, e))?; + map_err(|e| format!("Error reading music file {:?}: {}", music_filename, e))?; let image = image::open(&image_filename). - map_err(|e| format!("Error reading image {}: {}", image_filename, e))?; + map_err(|e| format!("Error reading image {:?}: {}", image_filename, e))?; let mut encoded_image_bytes = Vec::new(); // Unwrap: Writing to a Vec should always succeed; @@ -19,14 +20,14 @@ pub fn embed_image(music_filename: &str, image_filename: &str) -> Result<(), Box }); tag.write_to_path(music_filename, id3::Version::Id3v23). - map_err(|e| format!("Error writing image to music file {}: {}", music_filename, e))?; + map_err(|e| format!("Error writing image to music file {:?}: {}", music_filename, e))?; Ok(()) } -pub fn extract_image(music_filename: &str, image_filename: &str) -> Result<(), Box> { +pub fn extract_image(music_filename: &Path, image_filename: &Path) -> Result<(), Box> { let tag = id3::Tag::read_from_path(&music_filename). - map_err(|e| format!("Error reading music file {}: {}", music_filename, e))?; + map_err(|e| format!("Error reading music file {:?}: {}", music_filename, e))?; let first_picture = tag.pictures().next(); @@ -34,8 +35,7 @@ pub fn extract_image(music_filename: &str, image_filename: &str) -> Result<(), B match image::load_from_memory(&p.data) { Ok(image) => { image.save(&image_filename). - map_err(|e| format!("Couldn't write image file {}: {}", image_filename, e))?; - println!("{}", image_filename); + map_err(|e| format!("Couldn't write image file {:?}: {}", image_filename, e))?; }, Err(e) => return Err(format!("Couldn't load image: {}", e).into()), }; @@ -44,14 +44,14 @@ pub fn extract_image(music_filename: &str, image_filename: &str) -> Result<(), B Ok(()) } -pub fn remove_images(music_filename: &str) -> Result<(), Box> { +pub fn remove_images(music_filename: &Path) -> Result<(), Box> { let mut tag = id3::Tag::read_from_path(&music_filename). - map_err(|e| format!("Error reading music file {}: {}", music_filename, e))?; + map_err(|e| format!("Error reading music file {:?}: {}", music_filename, e))?; tag.remove("APIC"); tag.write_to_path(music_filename, id3::Version::Id3v23). - map_err(|e| format!("Error updating music file {}: {}", music_filename, e))?; + map_err(|e| format!("Error updating music file {:?}: {}", music_filename, e))?; Ok(()) } diff --git a/tests/test_processing.rs b/tests/test_processing.rs index 1d8b19b..972e6c6 100644 --- a/tests/test_processing.rs +++ b/tests/test_processing.rs @@ -18,10 +18,10 @@ impl AsRef for Fixture { } impl Deref for Fixture { - type Target = str; + type Target = Path; fn deref(&self) -> &Self::Target { - self.path_buf.to_str().unwrap() + self.path_buf.deref() } } @@ -44,7 +44,7 @@ macro_rules! fixture { } } -fn read_tag(path: &str) -> id3::Tag { +fn read_tag(path: &Path) -> id3::Tag { id3::Tag::read_from_path(path).unwrap() } @@ -54,9 +54,9 @@ fn test_unsuccessful_image_embedding() { let image = fixture!("attempt_1.jpg"); // Nonexistent files - assert!(embed_image(&song, "nonexisting.jpg").is_err()); - assert!(embed_image("nonexisting.mp3", &image).is_err()); - assert!(embed_image("nonexisting.mp3", "nonexisting.jpg").is_err()); + assert!(embed_image(&song, &PathBuf::from("nonexistent.jpg")).is_err()); + assert!(embed_image(&PathBuf::from("nonexistent.mp3"), &image).is_err()); + assert!(embed_image(&PathBuf::from("nonexistent.mp3"), &PathBuf::from("nonexistent.jpg")).is_err()); // Wrong kinds of files assert!(embed_image(&image, &song).is_err());