diff options
| author | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2020-05-25 14:26:00 -0400 |
|---|---|---|
| committer | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2020-05-25 18:04:31 -0400 |
| commit | c69ed1e0bf7567883445e917414a32f6815addb6 (patch) | |
| tree | 369c7c24e5f456b2d3d57156d350c48b742c701a /src | |
| parent | d59a92b04a398b274fbeda0de6d8e64c718837dd (diff) | |
Implement initial thumbnail creation.
Diffstat (limited to 'src')
| -rw-r--r-- | src/birka-cli.rs | 119 | ||||
| -rw-r--r-- | src/main.rs | 133 |
2 files changed, 166 insertions, 86 deletions
diff --git a/src/birka-cli.rs b/src/birka-cli.rs new file mode 100644 index 0000000..d2caba3 --- /dev/null +++ b/src/birka-cli.rs @@ -0,0 +1,119 @@ +// Copyright © 2020 Jakob L. Kreuze <zerodaysfordays@sdf.org> +// +// This file is part of бирка-тян. +// +// бирка-тян is free software; you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// бирка-тян is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public +// License along with бирка-тян. If not, see <http://www.gnu.org/licenses/>. + +#[macro_use] +extern crate anyhow; +extern crate dirs; + +mod database; + +use anyhow::Result; +use blake2::{Blake2b, Digest}; +use database::TagDatabase; +use std::env; +use std::io; +use std::path::Path; + +/// Return a base64-encoded BLAKE2b hash identifying the file at `path`. +fn hash_file(path: &Path) -> Result<String> { + let mut file = std::fs::File::open(path)?; + let mut hasher = Blake2b::new(); + io::copy(&mut file, &mut hasher)?; + Ok(base64::encode(&hasher.result())) +} + +fn main() { + let args: Vec<String> = env::args().collect(); + if args.len() < 2 { + eprintln!("usage: {} ACTION [ARGS ...]", args[0]); + std::process::exit(1); + } + + let tb = if let Ok(val) = env::var("TB_PATH") { + TagDatabase::new(&val) + } else if let Some(dir) = dirs::config_dir() { + TagDatabase::new(dir.join("birka.db").to_str().unwrap()) + } else { + TagDatabase::new(Path::new("/tmp").join("pometka.db").to_str().unwrap()) + } + .unwrap(); + + let action = &args[1]; + match action.to_lowercase().as_str() { + "add" => { + if args.len() < 3 { + eprintln!("usage: {} add PATH [TAGS ...]", args[0]); + std::process::exit(1); + } + let path = Path::new(&args[2]).canonicalize().unwrap(); + let hash = hash_file(&path).unwrap(); + let id = tb.import_image(&path, &hash).unwrap(); + for arg in args[3..].iter() { + tb.add_tag(arg).unwrap(); + tb.tag_image(id, arg).unwrap(); + } + } + "id_for" => { + if args.len() < 3 { + eprintln!("usage: {} id_for PATH ", args[0]); + std::process::exit(1); + } + let path = Path::new(&args[2]).canonicalize().unwrap(); + println!("{}", tb.image_id(&path).unwrap()); + } + "add_tags" => { + if args.len() < 3 { + eprintln!("usage: {} add_tags ID [TAGS ...]", args[0]); + std::process::exit(1); + } + let id = args[2].parse::<i64>().unwrap(); + for arg in args[3..].iter() { + tb.add_tag(arg).unwrap(); + tb.tag_image(id, arg).unwrap(); + } + } + "remove_tags" => { + if args.len() < 3 { + eprintln!("usage: {} remove_tags ID [TAGS ...]", args[0]); + std::process::exit(1); + } + let id = args[2].parse::<i64>().unwrap(); + for arg in args[3..].iter() { + tb.untag_image(id, arg).unwrap(); + } + } + "query" => { + let tags = args[2..].iter().map(|s| s.as_str()).collect::<Vec<&str>>(); + for tag in tags.iter() { + if tb.tag_id(tag).is_err() { + eprintln!("unknown tag: {}", tag); + std::process::exit(1); + } + } + for image in tb.images_by_tags(&tags[..]).unwrap() { + println!( + "{},{},{}{}", + image.id, image.blake2, image.orig_dir, image.filename + ); + } + } + _ => { + eprintln!("Unknown action '{}'", action); + std::process::exit(1); + } + } +} diff --git a/src/main.rs b/src/main.rs index d2caba3..b5665b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,105 +15,66 @@ // You should have received a copy of the GNU Affero General Public // License along with бирка-тян. If not, see <http://www.gnu.org/licenses/>. +#![feature(proc_macro_hygiene, decl_macro)] + #[macro_use] extern crate anyhow; -extern crate dirs; +extern crate image; +#[macro_use] +extern crate rocket; +#[macro_use] +extern crate rocket_contrib; mod database; -use anyhow::Result; -use blake2::{Blake2b, Digest}; use database::TagDatabase; +use image::{GenericImageView, ImageFormat}; +use rocket_contrib::json::{Json, JsonValue}; use std::env; -use std::io; +use std::fs; +use std::fs::File; +use std::os::unix::fs::symlink; use std::path::Path; -/// Return a base64-encoded BLAKE2b hash identifying the file at `path`. -fn hash_file(path: &Path) -> Result<String> { - let mut file = std::fs::File::open(path)?; - let mut hasher = Blake2b::new(); - io::copy(&mut file, &mut hasher)?; - Ok(base64::encode(&hasher.result())) +#[get("/")] +fn index() -> &'static str { + "Hello, world!" } -fn main() { - let args: Vec<String> = env::args().collect(); - if args.len() < 2 { - eprintln!("usage: {} ACTION [ARGS ...]", args[0]); - std::process::exit(1); - } +#[get("/posts?<tags>")] +fn posts(tags: String) -> Json<Vec<String>> { + let tags: Vec<String> = tags.split(",").map(|s| s.into()).collect(); + Json(tags) +} - let tb = if let Ok(val) = env::var("TB_PATH") { - TagDatabase::new(&val) - } else if let Some(dir) = dirs::config_dir() { - TagDatabase::new(dir.join("birka.db").to_str().unwrap()) - } else { - TagDatabase::new(Path::new("/tmp").join("pometka.db").to_str().unwrap()) - } - .unwrap(); +fn create_image_directory() { + let bin = env::current_exe().unwrap(); + let image_dir = bin.parent().unwrap().join("image/"); + fs::create_dir(&image_dir).ok(); - let action = &args[1]; - match action.to_lowercase().as_str() { - "add" => { - if args.len() < 3 { - eprintln!("usage: {} add PATH [TAGS ...]", args[0]); - std::process::exit(1); - } - let path = Path::new(&args[2]).canonicalize().unwrap(); - let hash = hash_file(&path).unwrap(); - let id = tb.import_image(&path, &hash).unwrap(); - for arg in args[3..].iter() { - tb.add_tag(arg).unwrap(); - tb.tag_image(id, arg).unwrap(); - } - } - "id_for" => { - if args.len() < 3 { - eprintln!("usage: {} id_for PATH ", args[0]); - std::process::exit(1); - } - let path = Path::new(&args[2]).canonicalize().unwrap(); - println!("{}", tb.image_id(&path).unwrap()); - } - "add_tags" => { - if args.len() < 3 { - eprintln!("usage: {} add_tags ID [TAGS ...]", args[0]); - std::process::exit(1); - } - let id = args[2].parse::<i64>().unwrap(); - for arg in args[3..].iter() { - tb.add_tag(arg).unwrap(); - tb.tag_image(id, arg).unwrap(); - } - } - "remove_tags" => { - if args.len() < 3 { - eprintln!("usage: {} remove_tags ID [TAGS ...]", args[0]); - std::process::exit(1); - } - let id = args[2].parse::<i64>().unwrap(); - for arg in args[3..].iter() { - tb.untag_image(id, arg).unwrap(); - } - } - "query" => { - let tags = args[2..].iter().map(|s| s.as_str()).collect::<Vec<&str>>(); - for tag in tags.iter() { - if tb.tag_id(tag).is_err() { - eprintln!("unknown tag: {}", tag); - std::process::exit(1); - } - } - for image in tb.images_by_tags(&tags[..]).unwrap() { - println!( - "{},{},{}{}", - image.id, image.blake2, image.orig_dir, image.filename - ); - } - } - _ => { - eprintln!("Unknown action '{}'", action); - std::process::exit(1); + let tb_path = bin.parent().unwrap().join("birka.db"); + let tb = TagDatabase::new(tb_path.to_str().unwrap()).unwrap(); + + for image in tb.images_by_tags(&vec![][..]).unwrap() { + let src = format!("{}/{}", image.orig_dir, image.filename); + let dst = format!("{}/{}", image_dir.to_str().unwrap(), image.filename); + symlink(&src, &dst).ok(); + + let stem = Path::new(&dst).file_stem().unwrap().to_str().unwrap(); + let thumb_path = format!("{}_thumb.png", stem); + let thumb_path = Path::new(&thumb_path); + + if !thumb_path.exists() { + let im = image::open(&Path::new(&src)).unwrap(); + let fout = &mut File::create(&thumb_path).unwrap(); + im.thumbnail(100, 100) + .write_to(fout, ImageFormat::Png) + .unwrap(); } } } + +fn main() { + create_image_directory(); + rocket::ignite().mount("/", routes![index, posts]).launch(); +} |