Compare commits

...

3 Commits

5 changed files with 74 additions and 29 deletions

View File

@ -1,7 +1,9 @@
[package]
name = "raw_extractor"
version = "0.1.0"
authors = ["Jean-Claude Graf <mail@jeanclaudegraf.ch>"]
edition = "2021"
description = "Extract embedded thumbnail from raw image file"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -9,3 +11,7 @@ edition = "2021"
anyhow = "1.0.77"
clap = { version = "4.4.12", features = ["derive"] }
rexiv2 = "0.10.0"
[[bin]]
name = "rex"
path = "src/main.rs"

13
README.md Normal file
View File

@ -0,0 +1,13 @@
# RawExtractor
RawExtractor, or in short *rex*, is a small utility designed for extracting embedded thumbnails from various raw image formats. It provides an effortless way to extract thumbnails. Unlike existing tools like exiv2 or exiftools, it doesn't require complex input arguments nor some bash hacking.
## Goals / Features
- Extract embedded thumbnail with largest available resolution
- Preserve metadata on extracted thumbnail
- Ease of use
- (TODO) Offer flexibility in choice of destination and file naming
## Dependencies
- [Exiv2](https://exiv2.org/): C++ Library used to interact with the metadata of the image
- [gexiv2](https://wiki.gnome.org/Projects/gexiv2): GObject wrapper around Exiv2

View File

@ -4,24 +4,40 @@ use std::path::{self, PathBuf};
#[derive(Parser)]
#[command(author, version, about, long_about=None)]
pub struct Cli {
#[arg(required=true, num_args=1.., value_parser = validate_path)]
pub input: Vec<PathBuf>,
#[arg(required=true, num_args=1.., value_parser = validate_file)]
pub input_images: Vec<PathBuf>,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(short, long, value_parser=validate_dir)]
pub output_dir: Option<PathBuf>,
}
/// Ensures that a provided path is an existing file
fn validate_path(image: &str) -> Result<PathBuf, String> {
let image: PathBuf = path::absolute(image).expect("Failed to get absolute path");
if !image.exists() {
Err(format!("File {} does not exist.", image.to_str().unwrap()))
} else if !image.is_file() {
fn validate_file(file: &str) -> Result<PathBuf, String> {
let file: PathBuf = path::absolute(file).expect("Failed to get absolute path");
if !file.exists() {
Err(format!("File {} does not exist.", file.to_str().unwrap()))
} else if !file.is_file() {
Err(format!(
"{} does not appear to be a file.",
image.to_str().unwrap()
file.to_str().unwrap()
))
} else {
Ok(image)
Ok(file)
}
}
/// Ensures that a provided path is an existing directory
fn validate_dir(dir: &str) -> Result<PathBuf, String> {
let dir: PathBuf = path::absolute(dir).expect("Failed to get absolute path");
if !dir.exists() {
Err(format!("File {} does not exist.", dir.to_str().unwrap()))
} else if !dir.is_dir() {
Err(format!(
"{} does not appear to be a directory.",
dir.to_str().unwrap()
))
} else {
println!("path {:?} filename {:?}", dir, dir.file_name());
Ok(dir)
}
}

View File

@ -1,23 +1,17 @@
#![feature(absolute_path)]
pub mod commands;
pub mod utils;
use anyhow::Result;
use clap::Parser;
use rexiv2::Metadata;
use utils::append_to_path;
use commands::Cli;
fn main() -> Result<()> {
let cli = Cli::parse();
for src in cli.input.iter() {
for src in cli.input_images.iter() {
assert!(src.is_absolute());
let dst = match cli.output {
Some(ref e) => e.clone(),
None => append_to_path(src.clone(), ".jpg"),
};
println!("Processing image {:?}", src);
let metadata = Metadata::new_from_path(&src)?;
@ -28,16 +22,39 @@ fn main() -> Result<()> {
.iter()
.max_by_key(|e| e.get_size())
.expect("previews is not supposed to be empty");
let mut dst = match cli.output_dir {
Some(ref e) => e.to_owned(),
None => src
.parent()
.expect("image is supposed to have a parent")
.to_owned(),
};
assert!(dst.is_dir());
// Set filename to name of source, without extension as `save_to_file` will add the appropriate extension
dst.push(
src.file_stem()
.expect("image is supposed to have a filename"),
);
preview
.save_to_file(&dst)
.expect("failed to save preview to file");
println!("Stored preview to {:?}", dst);
let ext = preview.get_extension().expect("failed to get extension");
dst.set_extension(
preview
.get_extension()
.expect("failed to get preview extension")
.strip_prefix(".")
.expect("extension is supposed to start with ."),
);
assert!(dst.is_file());
metadata
.save_to_file(append_to_path(dst.clone(), &ext))
.save_to_file(&dst)
.expect("failed to save metadata to file");
println!("Stored metadata to {:?}", dst);
println!("Stored preview to {:?}", dst);
} else {
println!("Image {:?} contains no embedded JPG", src);
}

View File

@ -1,7 +0,0 @@
use std::path::PathBuf;
pub fn append_to_path(p: PathBuf, s: &str) -> PathBuf {
let mut p = p.into_os_string();
p.push(s);
p.into()
}