cargo fmt

This commit is contained in:
blankie 2023-10-28 11:11:16 +11:00
parent 9ca4dda9d9
commit c2ea9b87b0
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
8 changed files with 97 additions and 73 deletions

View File

@ -2,18 +2,23 @@ use crate::structs;
extern crate serde_json; extern crate serde_json;
pub async fn get_sauce_info(client: reqwest::Client, sauce: i32) -> Result<structs::GalleryInfo, reqwest::Error> { pub async fn get_sauce_info(
client: reqwest::Client,
sauce: i32,
) -> Result<structs::GalleryInfo, reqwest::Error> {
let mut uri = String::from("https://nhentai.net/api/gallery/"); let mut uri = String::from("https://nhentai.net/api/gallery/");
uri.push_str(&sauce.to_string()); uri.push_str(&sauce.to_string());
let resp = client.get(&uri) let resp = client.get(&uri).send().await?;
.send()
.await?;
Ok(serde_json::from_str(&resp.text().await?).unwrap()) Ok(serde_json::from_str(&resp.text().await?).unwrap())
} }
pub async fn get_search_info(client: reqwest::Client, search_query: &str) -> Result<structs::SearchInfo, reqwest::Error> { pub async fn get_search_info(
client: reqwest::Client,
search_query: &str,
) -> Result<structs::SearchInfo, reqwest::Error> {
let uri = "https://nhentai.net/api/galleries/search"; let uri = "https://nhentai.net/api/galleries/search";
let resp = client.get(uri) let resp = client
.get(uri)
.query(&[("query", search_query)]) .query(&[("query", search_query)])
.send() .send()
.await?; .await?;

View File

@ -1,6 +1,6 @@
mod view;
mod search;
mod download; mod download;
mod search;
mod view;
use std::env; use std::env;
use std::path::Path; use std::path::Path;
@ -11,19 +11,22 @@ pub async fn run() {
let path = args.next().expect("Cannot get binary path"); let path = args.next().expect("Cannot get binary path");
let path = Path::new(&path).file_stem().unwrap().to_str().unwrap(); let path = Path::new(&path).file_stem().unwrap().to_str().unwrap();
let operation = match args.next() { let operation = match args.next() {
Some(operation) => operation, Some(operation) => operation,
None => { None => {
eprintln!("Missing operation, run `{} help`", path); eprintln!("Missing operation, run `{} help`", path);
exit(1); exit(1);
} }
}; };
match operation.as_str() { match operation.as_str() {
"search" => search::run(args).await, "search" => search::run(args).await,
"view" | "show" | "info" => view::run(args).await, "view" | "show" | "info" => view::run(args).await,
"download" | "dl" => download::run(args).await, "download" | "dl" => download::run(args).await,
"help" => println!(r#"Usage: {} search QUERY "help" => println!(
r#"Usage: {} search QUERY
or {} info/view/show SAUCE [SAUCE]... or {} info/view/show SAUCE [SAUCE]...
or {} download/dl SAUCE [SAUCE]..."#, path, path, path), or {} download/dl SAUCE [SAUCE]..."#,
path, path, path
),
_ => { _ => {
eprintln!("Unknown operation, run `{} help`", path); eprintln!("Unknown operation, run `{} help`", path);
exit(1) exit(1)

View File

@ -1,19 +1,19 @@
use crate::api; use crate::api;
use crate::utils;
use crate::structs; use crate::structs;
use crate::utils;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::fs::{create_dir, rename, write};
use std::io::Write; use std::io::Write;
use std::sync::Arc;
use std::path::Path; use std::path::Path;
use std::process::exit; use std::process::exit;
use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
use std::fs::{rename, create_dir, write};
extern crate tokio;
extern crate reqwest; extern crate reqwest;
extern crate tokio;
const DOWNLOAD_WORKERS: usize = 5; const DOWNLOAD_WORKERS: usize = 5;
const FAIL_DOWNLOAD_WAIT_TIME: u64 = 5000; const FAIL_DOWNLOAD_WAIT_TIME: u64 = 5000;
@ -27,14 +27,17 @@ pub async fn run(args: env::Args) {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let mut pages_vec: Vec<(String, String)> = Vec::new(); let mut pages_vec: Vec<(String, String)> = Vec::new();
{ {
let mut handles: Vec<JoinHandle<structs::GalleryInfoSuccess>> = Vec::with_capacity(sauces.len()); let mut handles: Vec<JoinHandle<structs::GalleryInfoSuccess>> =
Vec::with_capacity(sauces.len());
let mut sauce_info_vec: Vec<structs::GalleryInfoSuccess> = Vec::with_capacity(sauces.len()); let mut sauce_info_vec: Vec<structs::GalleryInfoSuccess> = Vec::with_capacity(sauces.len());
for sauce in sauces { for sauce in sauces {
let cloned_client = client.clone(); let cloned_client = client.clone();
handles.push(tokio::spawn(async move { handles.push(tokio::spawn(async move {
match api::get_sauce_info(cloned_client, sauce).await.unwrap() { match api::get_sauce_info(cloned_client, sauce).await.unwrap() {
structs::GalleryInfo::Info(sauce_info) => sauce_info, structs::GalleryInfo::Info(sauce_info) => sauce_info,
structs::GalleryInfo::Error(sauce_error) => panic!("{} returned: {}", sauce, sauce_error.error) structs::GalleryInfo::Error(sauce_error) => {
panic!("{} returned: {}", sauce, sauce_error.error)
}
} }
})); }));
} }
@ -48,8 +51,8 @@ pub async fn run(args: env::Args) {
Ok(()) => write(base_path.join("info.txt"), format!("{}\n", &sauce_info)).unwrap(), Ok(()) => write(base_path.join("info.txt"), format!("{}\n", &sauce_info)).unwrap(),
Err(err) => match err.kind() { Err(err) => match err.kind() {
std::io::ErrorKind::AlreadyExists => (), std::io::ErrorKind::AlreadyExists => (),
_ => panic!("Got a weird error while creating dir: {}", err) _ => panic!("Got a weird error while creating dir: {}", err),
} },
}; };
let mut page_num: i32 = 1; let mut page_num: i32 = 1;
for page in sauce_info.images.pages { for page in sauce_info.images.pages {
@ -57,7 +60,7 @@ pub async fn run(args: env::Args) {
"j" => ".jpg", "j" => ".jpg",
"p" => ".png", "p" => ".png",
"g" => ".gif", "g" => ".gif",
_ => panic!("Unknown extension type: {}", page.t) _ => panic!("Unknown extension type: {}", page.t),
}; };
let mut file_name = page_num.to_string(); let mut file_name = page_num.to_string();
file_name.push_str(file_ext); file_name.push_str(file_ext);
@ -65,9 +68,10 @@ pub async fn run(args: env::Args) {
if !file_path.exists() { if !file_path.exists() {
pages_vec.push(( pages_vec.push((
String::from(file_path.to_str().unwrap()), String::from(file_path.to_str().unwrap()),
format!("https://i.nhentai.net/galleries/{}/{}", format!(
sauce_info.media_id, "https://i.nhentai.net/galleries/{}/{}",
file_name) sauce_info.media_id, file_name
),
)); ));
} }
page_num += 1; page_num += 1;
@ -101,8 +105,11 @@ pub async fn run(args: env::Args) {
if success { if success {
break; break;
} }
}, }
Err(err) => eprintln!("[DW{}] Failed to download {} due to {}, sleeping for {}ms", worker_id, file_path, err, FAIL_DOWNLOAD_WAIT_TIME) Err(err) => eprintln!(
"[DW{}] Failed to download {} due to {}, sleeping for {}ms",
worker_id, file_path, err, FAIL_DOWNLOAD_WAIT_TIME
),
}; };
sleep(Duration::from_millis(FAIL_DOWNLOAD_WAIT_TIME)).await; sleep(Duration::from_millis(FAIL_DOWNLOAD_WAIT_TIME)).await;
} }
@ -116,18 +123,19 @@ pub async fn run(args: env::Args) {
} }
} }
async fn download_file(client: reqwest::Client, url: &str, file_name: &str) -> Result<bool, reqwest::Error> { async fn download_file(
let resp = client.get(url) client: reqwest::Client,
.send() url: &str,
.await?; file_name: &str,
) -> Result<bool, reqwest::Error> {
let resp = client.get(url).send().await?;
Ok(match resp.headers().get("Content-Type") { Ok(match resp.headers().get("Content-Type") {
Some(header) if header.to_str().unwrap_or_default().starts_with("image/") => { Some(header) if header.to_str().unwrap_or_default().starts_with("image/") => {
let bytes = resp.bytes().await?; let bytes = resp.bytes().await?;
let mut file = File::create(&file_name).unwrap(); let mut file = File::create(&file_name).unwrap();
file.write_all(&bytes).unwrap(); file.write_all(&bytes).unwrap();
true true
}, }
_ => false _ => false,
}) })
} }

View File

@ -14,7 +14,9 @@ pub async fn run(args: env::Args) {
eprintln!("Missing search query"); eprintln!("Missing search query");
exit(1); exit(1);
} }
let search_info = api::get_search_info(reqwest::Client::new(), &query).await.unwrap(); let search_info = api::get_search_info(reqwest::Client::new(), &query)
.await
.unwrap();
if search_info.num_pages < 1 { if search_info.num_pages < 1 {
eprintln!("No results found"); eprintln!("No results found");
exit(1); exit(1);

View File

@ -1,12 +1,12 @@
use crate::api; use crate::api;
use crate::utils;
use crate::structs; use crate::structs;
use crate::utils;
use std::env; use std::env;
use std::process::exit; use std::process::exit;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
extern crate tokio;
extern crate reqwest; extern crate reqwest;
extern crate tokio;
pub async fn run(args: env::Args) { pub async fn run(args: env::Args) {
let sauces = utils::get_arg_sauces(args).unwrap(); let sauces = utils::get_arg_sauces(args).unwrap();
@ -15,11 +15,15 @@ pub async fn run(args: env::Args) {
exit(1); exit(1);
} }
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let mut handles: Vec<JoinHandle<(structs::GalleryInfo, i32)>> = Vec::with_capacity(sauces.len()); let mut handles: Vec<JoinHandle<(structs::GalleryInfo, i32)>> =
Vec::with_capacity(sauces.len());
for sauce in sauces { for sauce in sauces {
let cloned_client = client.clone(); let cloned_client = client.clone();
handles.push(tokio::spawn(async move { handles.push(tokio::spawn(async move {
(api::get_sauce_info(cloned_client, sauce).await.unwrap(), sauce) (
api::get_sauce_info(cloned_client, sauce).await.unwrap(),
sauce,
)
})); }));
} }
let mut fail = false; let mut fail = false;
@ -32,7 +36,7 @@ pub async fn run(args: env::Args) {
println!(""); println!("");
} }
println!("{}", &sauce_info); println!("{}", &sauce_info);
}, }
structs::GalleryInfo::Error(sauce_error) => { structs::GalleryInfo::Error(sauce_error) => {
if one_done { if one_done {
eprintln!(""); eprintln!("");

View File

@ -1,7 +1,7 @@
mod api; mod api;
mod utils;
mod structs;
mod commands; mod commands;
mod structs;
mod utils;
extern crate tokio; extern crate tokio;

View File

@ -1,8 +1,8 @@
use std::fmt;
use std::marker::PhantomData;
use std::collections::BTreeMap;
use serde::de::{self, Visitor}; use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::collections::BTreeMap;
use std::fmt;
use std::marker::PhantomData;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct GalleryTitleInfo { pub struct GalleryTitleInfo {
@ -15,14 +15,14 @@ pub struct GalleryTitleInfo {
pub struct GalleryImageInfo { pub struct GalleryImageInfo {
pub t: String, pub t: String,
pub w: i32, pub w: i32,
pub h: i32 pub h: i32,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct GalleryImagesInfo { pub struct GalleryImagesInfo {
pub pages: Vec<GalleryImageInfo>, pub pages: Vec<GalleryImageInfo>,
pub cover: GalleryImageInfo, pub cover: GalleryImageInfo,
pub thumbnail: GalleryImageInfo pub thumbnail: GalleryImageInfo,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -31,7 +31,7 @@ pub struct GalleryTagInfo {
pub r#type: String, pub r#type: String,
pub name: String, pub name: String,
pub url: String, pub url: String,
pub count: i32 pub count: i32,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -45,26 +45,26 @@ pub struct GalleryInfoSuccess {
pub upload_date: i32, pub upload_date: i32,
pub tags: Vec<GalleryTagInfo>, pub tags: Vec<GalleryTagInfo>,
pub num_pages: i32, pub num_pages: i32,
pub num_favorites: i32 pub num_favorites: i32,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct GalleryInfoError { pub struct GalleryInfoError {
pub error: String pub error: String,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(untagged)] #[serde(untagged)]
pub enum GalleryInfo { pub enum GalleryInfo {
Info(GalleryInfoSuccess), Info(GalleryInfoSuccess),
Error(GalleryInfoError) Error(GalleryInfoError),
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct SearchInfo { pub struct SearchInfo {
pub result: Vec<GalleryInfoSuccess>, pub result: Vec<GalleryInfoSuccess>,
pub num_pages: i32, pub num_pages: i32,
pub per_page: i32 pub per_page: i32,
} }
impl fmt::Display for GalleryInfoSuccess { impl fmt::Display for GalleryInfoSuccess {
@ -96,23 +96,25 @@ impl fmt::Display for GalleryInfoSuccess {
"group" => "Groups", "group" => "Groups",
"language" => "Languages", "language" => "Languages",
"category" => "Categories", "category" => "Categories",
_ => tag_key _ => tag_key,
}; };
text.push_str(&format!("\n{}: {}", tag_key, tag_value.join(", "))); text.push_str(&format!("\n{}: {}", tag_key, tag_value.join(", ")));
} }
text.push_str(&format!("\nPages: {}\nFavorites: {}", self.num_pages, self.num_favorites)); text.push_str(&format!(
"\nPages: {}\nFavorites: {}",
self.num_pages, self.num_favorites
));
formatter.write_str(&text) formatter.write_str(&text)
} }
} }
fn convert_to_i32<'de, D>(deserializer: D) -> Result<i32, D::Error> fn convert_to_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
where where
D: Deserializer<'de> D: Deserializer<'de>,
{ {
struct ConvertToI32<T>(PhantomData<fn() -> T>); struct ConvertToI32<T>(PhantomData<fn() -> T>);
impl<'de> Visitor<'de> for ConvertToI32<i32> impl<'de> Visitor<'de> for ConvertToI32<i32> {
{
type Value = i32; type Value = i32;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
@ -121,28 +123,28 @@ where
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E> fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
Ok(i32::from(value)) Ok(i32::from(value))
} }
fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E> fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
Ok(i32::from(value)) Ok(i32::from(value))
} }
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E> fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
Ok(value) Ok(value)
} }
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
use std::i32; use std::i32;
if value >= i64::from(i32::MIN) && value <= i64::from(i32::MAX) { if value >= i64::from(i32::MIN) && value <= i64::from(i32::MAX) {
@ -154,21 +156,21 @@ where
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E> fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
Ok(i32::from(value)) Ok(i32::from(value))
} }
fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E> fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
Ok(i32::from(value)) Ok(i32::from(value))
} }
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E> fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
use std::{i32, u32}; use std::{i32, u32};
if value <= i32::MAX as u32 { if value <= i32::MAX as u32 {
@ -180,7 +182,7 @@ where
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
use std::{i32, u64}; use std::{i32, u64};
if value <= i32::MAX as u64 { if value <= i32::MAX as u64 {
@ -192,9 +194,9 @@ where
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where where
E: de::Error E: de::Error,
{ {
// https://brokenco.de/2020/08/03/serde-deserialize-with-string.html // https://brokenco.de/2020/08/03/serde-deserialize-with-string.html
value.parse::<i32>().map_err(serde::de::Error::custom) value.parse::<i32>().map_err(serde::de::Error::custom)
} }
} }

View File

@ -4,10 +4,10 @@ pub fn get_arg_sauces(args: env::Args) -> Result<Vec<i32>, String> {
let mut sauces: Vec<i32> = Vec::new(); let mut sauces: Vec<i32> = Vec::new();
for sauce in args { for sauce in args {
let sauce: i32 = match sauce.parse() { let sauce: i32 = match sauce.parse() {
Ok(sauce) => sauce, Ok(sauce) => sauce,
Err(_) => { Err(_) => {
return Err(format!("{} is not a number/sauce", sauce)); return Err(format!("{} is not a number/sauce", sauce));
} }
}; };
if !sauces.contains(&sauce) { if !sauces.contains(&sauce) {
sauces.push(sauce); sauces.push(sauce);