feat: add webp and use yaml
This commit is contained in:
parent
e34b6f93b0
commit
20e8e7d3fe
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "screenshot_organiser"
|
||||
version = "0.1.0"
|
||||
authors = ["Kyle Clemens <git@kyleclemens.com>"]
|
||||
authors = ["Anna Clemens <git@annaclemens.io>"]
|
||||
|
||||
edition = "2018"
|
||||
|
||||
|
@ -9,14 +9,15 @@ edition = "2018"
|
|||
chrono = "0.4"
|
||||
regex = "1"
|
||||
notify = "4"
|
||||
image = "0.20"
|
||||
image = "0.23"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
serde_regex = "0.3"
|
||||
failure = "0.1"
|
||||
serde_yaml = "0.8"
|
||||
serde_regex = "1"
|
||||
anyhow = "1"
|
||||
tempdir = "0.3"
|
||||
num_cpus = "1"
|
||||
rayon = "1"
|
||||
ctrlc = "3"
|
||||
crossbeam-channel = "0.3"
|
||||
crossbeam-channel = "0.5"
|
||||
libwebp = { version = "0.1", features = ["static"] }
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# You should set FFXIV/ReShade to output PNG files.
|
||||
|
||||
options:
|
||||
# This is the directory that your screenshots are stored in.
|
||||
screenshots_dir: './screenshots'
|
||||
# File names must match a regular expression in this list in order
|
||||
# to be processed.
|
||||
#
|
||||
# The two defaults are FFXIV's format and ReShade's format.
|
||||
#
|
||||
# Each regular expression must contain groups for month, day, year,
|
||||
# hour, minute, and second.
|
||||
match:
|
||||
- '(?P<year>\d{4})-(?P<month>\d{2})-(?<day>\d{2})_(?P<hour>\d{2})-(?P<minute>\d{2})-(?P<second>\d{2})_.*.png'
|
||||
- 'ffxiv_(?P<month>\d{2})(?P<day>\d{2})(?P<year>\d{4})_(?P<hour>\d{2})(?P<minute>\d{2})(?P<second>\d{2})(?:_\d{3})?.(?:jpg|png)'
|
||||
- 'ffxiv_dx11 (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}) (?P<hour>\d{2})-(?P<minute>\d{2})-(?P<second>\d{2}).(?:png|bmp)'
|
||||
# How long to delay events before giving them to the program. This
|
||||
# usually doesn't need to be changed.
|
||||
event_delay: 1000
|
||||
|
||||
# A list of jobs to run on the files.
|
||||
#
|
||||
# The two types are convert and move.
|
||||
pipeline:
|
||||
# Convert screenshots to lossless webp.
|
||||
- job: convert
|
||||
options:
|
||||
to:
|
||||
# The format to convert the screenshot to.
|
||||
#
|
||||
# Supported options: png, jpg*, gif, bmp, ico, webp*
|
||||
# Options marked with * require a quality to be specified.
|
||||
format: webp
|
||||
# The quality of the output. This is only necessary for jpg and webp.
|
||||
#
|
||||
# For jpg, the number can be between 0 and 100. Smaller
|
||||
# numbers are lower quality but lower file size.
|
||||
#
|
||||
# For webp, the number can be -1 to indicate lossless
|
||||
# encoding. It can also be between 0 and 100, with lower
|
||||
# numbers having lower quality but lower file size.
|
||||
quality: -1
|
||||
# Whether to keep the original file. If this is false, the
|
||||
# original will be deleted.
|
||||
keep_original: false
|
||||
- job: move
|
||||
options:
|
||||
# The path to move the file to without its extension. You can
|
||||
# specify directories to sort files into directories.
|
||||
to: '%Y/%m/%d/ffxiv.%Y-%m-%d.%H-%M-%S'
|
||||
# Whether to use your local time zone or UTC.
|
||||
local: true
|
|
@ -2,11 +2,14 @@ use crate::{Result, config::Config, state::State};
|
|||
|
||||
use chrono::Local;
|
||||
|
||||
use failure::bail;
|
||||
use anyhow::bail;
|
||||
|
||||
use image::{ImageFormat, ImageOutputFormat};
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::Write,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
|
@ -51,7 +54,21 @@ impl Job {
|
|||
.create(true)
|
||||
.open(&f)?;
|
||||
|
||||
i.write_to(&mut dest, to.as_image_output_format())?;
|
||||
match to {
|
||||
FileType::WebP { quality } => {
|
||||
let rgba = i.into_rgba();
|
||||
let flat = rgba.into_flat_samples();
|
||||
let stride = flat.layout.width_stride as u32 * flat.layout.width;
|
||||
let data = if quality < 0 {
|
||||
libwebp::WebPEncodeLosslessRGBA(&flat.samples, flat.layout.width, flat.layout.height, stride)?
|
||||
} else {
|
||||
let quality = std::cmp::min(100, quality);
|
||||
libwebp::WebPEncodeRGBA(&flat.samples, flat.layout.width, flat.layout.height, stride, f32::from(quality))?
|
||||
};
|
||||
dest.write_all(&data)?;
|
||||
},
|
||||
_ => i.write_to(&mut dest, to.as_image_output_format())?,
|
||||
}
|
||||
|
||||
if keep {
|
||||
add = Some(old_f);
|
||||
|
@ -83,13 +100,7 @@ impl Job {
|
|||
if let Some(p) = file_path.parent() {
|
||||
std::fs::create_dir_all(p)?;
|
||||
}
|
||||
match std::fs::rename(&f, &file_path) {
|
||||
Err(ref e) if cfg!(windows) && e.raw_os_error() == Some(17) => {
|
||||
std::fs::copy(&f, &file_path)?;
|
||||
std::fs::remove_file(&f)?;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
std::fs::copy(&f, &file_path)?;
|
||||
*f = file_path;
|
||||
}
|
||||
|
||||
|
@ -108,6 +119,10 @@ pub enum FileType {
|
|||
Gif,
|
||||
Bmp,
|
||||
Ico,
|
||||
WebP {
|
||||
// less than 0 for lossless
|
||||
quality: i8,
|
||||
},
|
||||
}
|
||||
|
||||
impl FileType {
|
||||
|
@ -118,22 +133,24 @@ impl FileType {
|
|||
FileType::Gif => "gif",
|
||||
FileType::Bmp => "bmp",
|
||||
FileType::Ico => "ico",
|
||||
FileType::WebP { .. } => "webp",
|
||||
}
|
||||
}
|
||||
|
||||
fn as_image_format(self) -> ImageFormat {
|
||||
match self {
|
||||
FileType::Png => ImageFormat::PNG,
|
||||
FileType::Jpg { .. } => ImageFormat::JPEG,
|
||||
FileType::Gif => ImageFormat::GIF,
|
||||
FileType::Bmp => ImageFormat::BMP,
|
||||
FileType::Ico => ImageFormat::ICO,
|
||||
FileType::Png => ImageFormat::Png,
|
||||
FileType::Jpg { .. } => ImageFormat::Jpeg,
|
||||
FileType::Gif => ImageFormat::Gif,
|
||||
FileType::Bmp => ImageFormat::Bmp,
|
||||
FileType::Ico => ImageFormat::Ico,
|
||||
FileType::WebP { .. } => ImageFormat::WebP,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_image_output_format(self) -> ImageOutputFormat {
|
||||
match self {
|
||||
FileType::Jpg { quality } => ImageOutputFormat::JPEG(quality),
|
||||
FileType::Jpg { quality } => ImageOutputFormat::Jpeg(quality),
|
||||
_ => self.as_image_format().into(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crossbeam_channel::Receiver;
|
|||
|
||||
use chrono::{DateTime, Duration, Local, TimeZone, Utc};
|
||||
|
||||
use failure::Error;
|
||||
use anyhow::Error;
|
||||
|
||||
use notify::{DebouncedEvent, RecursiveMode, Watcher};
|
||||
|
||||
|
@ -39,12 +39,12 @@ fn main() -> Result<()> {
|
|||
|
||||
let config_path = match std::env::args().nth(1) {
|
||||
Some(x) => x,
|
||||
None => "config.json".into(),
|
||||
None => "config.yaml".into(),
|
||||
};
|
||||
println!("Attempting to read config from `{}`.", config_path);
|
||||
|
||||
let f = fs::File::open(config_path)?;
|
||||
let config: Config = serde_json::from_reader(f)?;
|
||||
let config: Config = serde_yaml::from_reader(f)?;
|
||||
|
||||
println!("Config successfully read.");
|
||||
|
||||
|
|
Loading…
Reference in New Issue