summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/context.rs19
-rw-r--r--src/main.rs13
-rw-r--r--src/picture.rs149
-rw-r--r--src/piece.rs22
4 files changed, 201 insertions, 2 deletions
diff --git a/src/context.rs b/src/context.rs
index 6bc4c05..55f3f97 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -14,6 +14,15 @@ pub struct Options {
pub config: Option<PathBuf>,
#[structopt(help = "Picture location")]
pub load: PathBuf,
+ #[structopt(short, long, default_value = "png", help = "Image extension to use for converted files")]
+ pub ext: String,
+ #[structopt(long, short, default_value = "build", help = "Where to build site")]
+ pub builddir: PathBuf,
+
+ #[structopt(long, default_value = "1080", help = "Scaled size for image")]
+ pub size_scaled: u32,
+ #[structopt(long, default_value = "720", help = "Thumbnail size for image")]
+ pub size_thumb: u32,
}
#[derive(Debug)]
@@ -36,16 +45,26 @@ pub struct Config {
pub struct Context {
pub options: Options,
pub config: Config,
+ pub imgdir: PathBuf,
}
impl Context {
pub fn new_with_args() -> Result<Context, ConfigError> {
let opts = Options::from_args();
let config = Config::load_with_options(&opts)?;
+ let imgdir = opts.builddir.join("imgs");
+
+ // Create img dir
+ if let Err(err) = fs::create_dir(&imgdir) {
+ if err.kind() != io::ErrorKind::AlreadyExists {
+ return Err(ConfigError::from(err));
+ }
+ }
Ok(Context {
options: opts,
config: config,
+ imgdir: imgdir,
})
}
diff --git a/src/main.rs b/src/main.rs
index 06346a2..28fe0c9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,14 +1,23 @@
mod context;
+mod picture;
+mod piece;
+
use context::Context;
+use picture::Picture;
+use piece::Piece;
fn main() {
println!("Hello, world!");
let ctx = Context::new_with_args().unwrap();
- println!("{:?}", ctx);
-
for file in ctx.get_image_files().unwrap() {
println!("{}", file.display());
+
+ let pic = Picture::new_from_file(&file).unwrap();
+ println!("{:?}", pic);
+
+ let piece = Piece::new(&ctx, pic).unwrap();
+ println!("{:?}", piece);
}
}
diff --git a/src/picture.rs b/src/picture.rs
new file mode 100644
index 0000000..f92876c
--- /dev/null
+++ b/src/picture.rs
@@ -0,0 +1,149 @@
+use std::path::{PathBuf, Path};
+use std::fs;
+use std::io;
+use std::io::{Read, Seek};
+use image::io::Reader as ImageReader;
+use image::error::ImageError;
+use image::imageops;
+
+use crate::context::Context;
+
+#[derive(Debug)]
+pub enum LoadError {
+ Io(io::Error),
+ ExifParser(exif::Error)
+}
+
+#[derive(Debug)]
+pub enum ConversionError {
+ Io(io::Error),
+ ImageError(ImageError),
+}
+
+#[derive(Debug)]
+pub struct Picture {
+ taken: Option<String>,
+ hash: md5::Digest,
+ path: PathBuf,
+}
+
+pub struct Converter<'a> {
+ imgdata: Option<image::DynamicImage>,
+ pic: &'a Picture,
+}
+
+fn hash_reader<R: Read>(reader: &mut R) -> Result<md5::Digest, io::Error> {
+ let mut hash = md5::Context::new();
+ let mut buff = [0; 1024];
+
+ loop {
+ let count = reader.read(&mut buff)?;
+ if count == 0 {
+ // Reached end, stopping
+ break
+ }
+
+ hash.consume(&buff[..count])
+ }
+
+ Ok(hash.compute())
+}
+
+impl Picture {
+ /// Hash file content and load exif data.
+ pub fn new_from_file(path: &Path) -> Result<Self, LoadError> {
+ let file = fs::File::open(path)?;
+ let mut reader = io::BufReader::new(&file);
+
+ let taken = match exif::Reader::new().read_from_container(&mut reader) {
+ Ok(exif) => exif.get_field(exif::Tag::DateTimeOriginal, exif::In::PRIMARY)
+ .map(|field| field.display_value().with_unit(&exif).to_string()),
+ Err(err) => {
+ println!("Could not load exif data for {}: {}", path.to_str().unwrap(), err);
+ None
+ }
+ };
+
+ // Move back to start of file for hashing
+ reader.seek(io::SeekFrom::Start(0))?;
+
+ Ok(Picture {
+ taken,
+ hash: hash_reader(&mut reader)?,
+ path: path.to_path_buf(),
+ })
+ }
+
+ pub fn convert(&self) -> Result<Converter, ConversionError> {
+ Ok(Converter {
+ imgdata: None,
+ pic: self,
+ })
+ }
+}
+
+impl Converter<'_> {
+ fn convert_image(&mut self, size: u32, dest: &Path) -> Result<(), ImageError> {
+ let scaled = self.get_imgdata()?.resize(
+ size,
+ std::u32::MAX,
+ imageops::FilterType::Lanczos3);
+
+ scaled.save(dest)
+ }
+
+ fn get_imgdata(&mut self) -> Result<&image::DynamicImage, ImageError> {
+ let picpath = &self.pic.path;
+
+ match self.imgdata {
+ None => self.imgdata = Some(
+ ImageReader::open(picpath)?.decode()?
+ ),
+ _ => ()
+ }
+
+ Ok(self.imgdata.as_ref().unwrap())
+ }
+
+ pub fn get_size(&mut self, ctx: &Context, size: u32) -> Result<PathBuf, ImageError> {
+ let hash = md5::compute(format!("{},{},{:?}", size, ctx.options.ext, self.pic.hash));
+ let name = format!("{:?}.{}", hash, ctx.options.ext);
+ let path = ctx.imgdir.join(name);
+
+ match path.exists() {
+ true => {
+ println!("Image of size {} already exists", size);
+ Ok(path)
+ },
+ false => {
+ println!("Scaling image to size {}", size);
+ self.convert_image(size, &path)?;
+ Ok(path)
+ }
+ }
+ }
+}
+
+impl From<io::Error> for LoadError {
+ fn from(error: io::Error) -> Self {
+ Self::Io(error)
+ }
+}
+
+impl From<exif::Error> for LoadError {
+ fn from(error: exif::Error) -> Self {
+ Self::ExifParser(error)
+ }
+}
+
+impl From<io::Error> for ConversionError {
+ fn from(error: io::Error) -> Self {
+ Self::Io(error)
+ }
+}
+
+impl From<ImageError> for ConversionError {
+ fn from(error: ImageError) -> Self {
+ Self::ImageError(error)
+ }
+}
diff --git a/src/piece.rs b/src/piece.rs
new file mode 100644
index 0000000..a0cc18e
--- /dev/null
+++ b/src/piece.rs
@@ -0,0 +1,22 @@
+use std::path::PathBuf;
+use crate::picture::{Picture, ConversionError};
+use crate::context::Context;
+
+#[derive(Debug)]
+pub struct Piece {
+ pic: Picture,
+ scaled_path: PathBuf,
+ thumb_path: PathBuf,
+}
+
+impl Piece {
+ pub fn new(ctx: &Context, pic: Picture) -> Result<Self, ConversionError> {
+ let mut conv = pic.convert()?;
+ let scaled_path = conv.get_size(ctx, ctx.options.size_scaled)?;
+ let thumb_path = conv.get_size(ctx, ctx.options.size_thumb)?;
+
+ Ok(Piece {
+ pic, scaled_path, thumb_path,
+ })
+ }
+}