Files
manga-cli/src/main.rs

154 lines
5.0 KiB
Rust

use client::{MangaClient, MangaClientBuilder, SearchFilter};
use response_deserializer::{Chapter, Id, Language, Manga};
use select::Entry;
use std::future::Future;
use std::pin::Pin;
use util::SelectionType;
mod client;
mod error;
mod response_deserializer;
mod select;
mod test;
mod util;
const BASE: &str = "https://api.mangadex.dev";
#[tokio::main]
async fn main() {
let config = util::args();
let manga_client =
MangaClientBuilder::new()
.bonus(config.bonus.unwrap_or_else(util::ask_bonus))
.cover(config.cover.unwrap_or_else(util::ask_cover))
.result_limit(config.result_limit.unwrap_or(5))
.cover_size(util::CoverSize::W512)
.selection_type(
config
.selection_type
.unwrap_or_else(util::ask_selection_type),
)
.selection_range(
config
.selection_range
.unwrap_or_else(util::ask_selection_range),
)
.search(config.search.unwrap_or_else(|| {
util::ConfigSearch::Query(util::get_input("Enter search query: "))
}))
.search_filter(SearchFilter::default())
.language(Language::default())
.build();
let search_result = manga_client.get_manga().await;
let mut choice = 0;
if search_result.len() > 1 {
choice = select_manga_from_search(&manga_client, &search_result).await;
}
let manga = &search_result[choice as usize];
let mut chapters = match manga_client.get_chapters(&manga.id).await {
Ok(v) => v,
Err(e) => {
eprintln!("ERROR: {e:#?}");
std::process::exit(1);
}
};
println!("Downloading {} chapters", chapters.len());
chapters.sort_by(|a, b| {
a.attributes
.chapter
.unwrap_or(-1.)
.partial_cmp(&b.attributes.chapter.unwrap_or(-1.))
.unwrap()
});
let create_volumes = matches!(
manga_client.selection.selection_type,
SelectionType::Volume(_)
);
let selected_chapters = util::get_chapters_from_selection(manga_client.selection, &chapters);
let title = &manga.attributes.title.en;
util::download_the_stuff(
&manga_client.client,
&selected_chapters,
title,
create_volumes,
)
.await;
util::create_volumes_or_chapters(&selected_chapters, create_volumes, title);
}
async fn select_manga_from_search(client: &MangaClient, results: &[Manga]) -> u16 {
let cover_ex = match client.cover_size {
util::CoverSize::Full => "",
util::CoverSize::W256 => ".256.jpg",
util::CoverSize::W512 => ".512.jpg",
};
let mut entry_futures: Vec<Pin<Box<dyn Future<Output = Entry>>>> = Vec::new();
assert!(!results.is_empty());
for result in results.iter() {
let mut entry = Entry::new(result.attributes.title.en.clone());
if let Some(year) = result.attributes.year {
entry.add_info("year", year);
}
let id = result.id.to_string();
entry.add_info("id", &id);
entry.add_info("status", result.attributes.status.to_string());
entry.add_info(
"content rating",
result.attributes.content_rating.to_string(),
);
if let Some(chapters) = result.attributes.last_chapter {
entry.add_info("chapters", chapters);
}
if let Some(volumes) = result.attributes.last_volume {
entry.add_info("volumes", volumes);
}
if let Some(cover_data) = &result.relationships[2].attributes {
// The lib used for converting to sixel is abysmally slow for larger images, this
// should be in a future to allow for multithreaded work
// Cover data should only be present if used
assert!(client.cover);
let future = async move {
let image_url = format!(
"https://uploads.mangadex.org/covers/{id}/{}{cover_ex}",
&cover_data.file_name
);
let data = client
.client
.get(&image_url)
.send()
.await
.unwrap()
.bytes()
.await
.unwrap();
let result = util::convert_to_sixel(&data);
entry.set_image(result);
entry
};
entry_futures.push(Box::pin(future));
} else {
entry_futures.push(Box::pin(async move { entry }));
}
}
let entries: Vec<Entry> = futures::future::join_all(entry_futures).await;
assert!(!entries.is_empty());
if entries.len() == 1 {
return 0;
}
let choice = match select::select(&entries) {
Ok(v) => v,
Err(e) => {
eprintln!("ERROR: Failed to select: {e:?}");
std::process::exit(1);
}
};
choice
}