CLI options, and better path/string handling
This commit is contained in:
parent
711314fe57
commit
432ee8f8b6
@ -1,21 +1,33 @@
|
|||||||
use std::env;
|
|
||||||
use std::process;
|
use std::process;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
use id3_image::embed_image;
|
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() {
|
fn main() {
|
||||||
let args: Vec<_> = env::args().collect();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
if args.len() < 3 {
|
if let Err(e) = embed_image(&opt.music_filename, &opt.image_filename) {
|
||||||
eprintln!("USAGE: id3-image-embed <mp3-file> <image-file>");
|
|
||||||
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) {
|
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
if opt.verbose >= 1 {
|
||||||
|
println!("Embedded {:?} into {:?}", opt.image_filename, opt.music_filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,41 @@
|
|||||||
use std::env;
|
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
use id3_image::extract_image;
|
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<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<_> = env::args().collect();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
if args.len() < 2 {
|
let image_filename = opt.image_filename.clone().
|
||||||
eprintln!("USAGE: id3-image-extract <mp3-file> [image-file]");
|
unwrap_or_else(|| opt.music_filename.with_extension("jpg"));
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let music_filename = args[1].clone();
|
if let Err(e) = extract_image(&opt.music_filename, &image_filename) {
|
||||||
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) {
|
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_extension(path: &str, replacement: &str) -> String {
|
if opt.verbose == 1 {
|
||||||
let mut path = PathBuf::from(&path);
|
// then just print the output filename for scripting purposes:
|
||||||
path.set_extension(replacement);
|
println!("{}", image_filename.display());
|
||||||
path.to_string_lossy().to_string()
|
} else if opt.verbose >= 2 {
|
||||||
|
// show a longer status message:
|
||||||
|
println!("Extracted cover art from {:?} to {:?}", opt.music_filename, image_filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
22
src/lib.rs
22
src/lib.rs
@ -1,11 +1,12 @@
|
|||||||
|
use std::path::Path;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
pub fn embed_image(music_filename: &str, image_filename: &str) -> Result<(), Box<Error>> {
|
pub fn embed_image(music_filename: &Path, image_filename: &Path) -> Result<(), Box<Error>> {
|
||||||
let mut tag = id3::Tag::read_from_path(&music_filename).
|
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).
|
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();
|
let mut encoded_image_bytes = Vec::new();
|
||||||
// Unwrap: Writing to a Vec should always succeed;
|
// 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).
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_image(music_filename: &str, image_filename: &str) -> Result<(), Box<Error>> {
|
pub fn extract_image(music_filename: &Path, image_filename: &Path) -> Result<(), Box<Error>> {
|
||||||
let tag = id3::Tag::read_from_path(&music_filename).
|
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();
|
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) {
|
match image::load_from_memory(&p.data) {
|
||||||
Ok(image) => {
|
Ok(image) => {
|
||||||
image.save(&image_filename).
|
image.save(&image_filename).
|
||||||
map_err(|e| format!("Couldn't write image file {}: {}", image_filename, e))?;
|
map_err(|e| format!("Couldn't write image file {:?}: {}", image_filename, e))?;
|
||||||
println!("{}", image_filename);
|
|
||||||
},
|
},
|
||||||
Err(e) => return Err(format!("Couldn't load image: {}", e).into()),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_images(music_filename: &str) -> Result<(), Box<Error>> {
|
pub fn remove_images(music_filename: &Path) -> Result<(), Box<Error>> {
|
||||||
let mut tag = id3::Tag::read_from_path(&music_filename).
|
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.remove("APIC");
|
||||||
|
|
||||||
tag.write_to_path(music_filename, id3::Version::Id3v23).
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ impl AsRef<Path> for Fixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Fixture {
|
impl Deref for Fixture {
|
||||||
type Target = str;
|
type Target = Path;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
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()
|
id3::Tag::read_from_path(path).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,9 +54,9 @@ fn test_unsuccessful_image_embedding() {
|
|||||||
let image = fixture!("attempt_1.jpg");
|
let image = fixture!("attempt_1.jpg");
|
||||||
|
|
||||||
// Nonexistent files
|
// Nonexistent files
|
||||||
assert!(embed_image(&song, "nonexisting.jpg").is_err());
|
assert!(embed_image(&song, &PathBuf::from("nonexistent.jpg")).is_err());
|
||||||
assert!(embed_image("nonexisting.mp3", &image).is_err());
|
assert!(embed_image(&PathBuf::from("nonexistent.mp3"), &image).is_err());
|
||||||
assert!(embed_image("nonexisting.mp3", "nonexisting.jpg").is_err());
|
assert!(embed_image(&PathBuf::from("nonexistent.mp3"), &PathBuf::from("nonexistent.jpg")).is_err());
|
||||||
|
|
||||||
// Wrong kinds of files
|
// Wrong kinds of files
|
||||||
assert!(embed_image(&image, &song).is_err());
|
assert!(embed_image(&image, &song).is_err());
|
||||||
|
Loading…
Reference in New Issue
Block a user