BooruImageTagCuratingHelper/src/main.rs

155 lines
4.3 KiB
Rust

use gelbooru_api::{posts, Client};
use serde::Deserialize;
use std::collections::HashSet;
use std::{
io::{BufRead, Write},
process::Command,
};
use toml;
const CONFIG: &str = "./config.toml";
#[derive(Deserialize)]
struct Config {
keybinds: Keybinds,
search: Search,
storage: Storage,
}
#[derive(Deserialize)]
struct Keybinds {
quit: char,
delete: char,
accept: char,
}
#[derive(Deserialize)]
struct Search {
tags: Vec<String>,
}
#[derive(Deserialize)]
struct Storage {
pending: String,
rejected: String,
verified: String,
}
#[tokio::main]
async fn main() {
let config: Config = parse_config(CONFIG);
let mut arg = std::env::args().skip(1);
match &arg
.next()
.expect("Not enough arguments, please use -f or -v")[..]
{
"-f" => fetch_mode(&config.storage, &config.search).await,
"-v" => verify_mode(&config.keybinds, &config.storage),
_ => {}
}
}
/// Parses the given filename to a config struct
fn parse_config(filename: &str) -> Config {
let toml_file = std::fs::read_to_string(filename)
.expect("No config file, consider geting the original one and moodifing it");
toml::from_str(&toml_file).expect("Malformed config file, check the original one for reference")
}
async fn fetch_mode(storage: &Storage, search: &Search) {
let client = Client::public();
let posts = posts().random(true).tags(&search.tags).send(&client);
let pending_set = std::fs::read_to_string(&storage.pending).expect("No pending file");
let rejected_set = std::fs::read_to_string(&storage.rejected).expect("No rejected file");
let verified_set = std::fs::read_to_string(&storage.verified).expect("No verified file");
let image_set = pending_set
.lines()
.chain(rejected_set.lines())
.chain(verified_set.lines())
.collect::<HashSet<_>>();
let posts = posts.await.unwrap();
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(true) // This is needed to append to file
.open(&storage.pending)
.unwrap();
for post in posts.posts {
let imgurl = post.image_url();
if !image_set.contains(imgurl) {
write!(file, "{}\n", imgurl).unwrap();
}
}
}
fn verify_mode(keybinds: &Keybinds, storage: &Storage) {
let file: String = std::fs::read(&storage.pending)
.expect("File not found")
.iter()
.map(|c| *c as char)
.collect();
let mut file = file.lines();
while let Some(image_uri) = file.next() {
//Open image
Command::new("xdg-open")
.arg(image_uri)
.spawn()
.expect("open command failed to start");
//Print Legend
println!(
"{} to quit; {} to delete; {} to accept",
&keybinds.quit, &keybinds.delete, &keybinds.accept,
);
// Wait for input
if input_parse(storage, keybinds, image_uri) {
std::fs::write(
&storage.pending,
format!(
"{}\n{}",
image_uri,
file.map(|s| s.to_owned() + "\n").collect::<String>()
),
)
.unwrap();
return;
}
}
std::fs::write(&storage.pending, "").unwrap();
}
fn input_parse(storage: &Storage, keybinds: &Keybinds, image_uri: &str) -> bool {
loop {
let key = std::io::stdin()
.lock()
.lines()
.next()
.unwrap()
.unwrap()
.chars()
.next()
.unwrap_or('_');
if &key == &keybinds.quit {
return true;
} else if &key == &keybinds.accept {
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(true) // This is needed to append to file
.open(&storage.verified)
.unwrap();
write!(file, "{}\n", image_uri).unwrap();
return false;
} else if &key == &keybinds.delete {
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(true) // This is needed to append to file
.open(&storage.rejected)
.unwrap();
write!(file, "{}\n", image_uri).unwrap();
return false;
}
}
}