Add rust implementation

This commit is contained in:
2022-12-14 22:33:38 +01:00
parent e36487b5ad
commit a5827721cd
39 changed files with 2473 additions and 1410 deletions

View File

@@ -0,0 +1,202 @@
pub mod types;
pub mod utils;
pub mod zip;
use reqwest::Response;
use crate::config;
use self::types::{DownloadResult, Data, SpooledTempAsyncRead};
use self::utils::response_to_tempfile;
use self::zip::{unzip, zip};
use super::book_library::types::Book;
use super::covert::convert_file;
use super::{book_library::get_remote_book, filename_getter::get_filename_by_book};
use futures::stream::FuturesUnordered;
use futures::StreamExt;
pub async fn download<'a>(
book_id: &'a u32,
book_file_type: &'a str,
source_config: &'a config::SourceConfig,
) -> Option<(Response, bool)> {
let basic_url = &source_config.url;
let proxy = &source_config.proxy;
let url = if book_file_type == "fb2" || book_file_type == "epub" || book_file_type == "mobi" {
format!("{basic_url}/b/{book_id}/{book_file_type}")
} else {
format!("{basic_url}/b/{book_id}/download")
};
let client = match proxy {
Some(v) => {
let proxy_data = reqwest::Proxy::http(v);
reqwest::Client::builder()
.proxy(proxy_data.unwrap())
.build()
.unwrap()
}
None => reqwest::Client::new(),
};
let response = client.get(url).send().await;
let response = match response {
Ok(v) => v,
Err(_) => return None,
};
let response = match response.error_for_status() {
Ok(v) => v,
Err(_) => return None,
};
let headers = response.headers();
let content_type = match headers.get("Content-Type") {
Some(v) => v.to_str().unwrap(),
None => "",
};
if book_file_type.to_lowercase() == "html" && content_type.contains("text/html") {
return Some((response, false));
}
if content_type.contains("text/html")
{
return None;
}
let is_zip = content_type.contains("application/zip");
Some((response, is_zip))
}
pub async fn download_chain<'a>(
book: &'a Book,
file_type: &'a str,
source_config: &'a config::SourceConfig,
converting: bool
) -> Option<DownloadResult> {
let final_need_zip = file_type == "fb2zip";
let file_type_ = if converting {
&book.file_type
} else {
file_type
};
let (mut response, is_zip) = match download(&book.remote_id, file_type_, source_config).await {
Some(v) => v,
None => return None,
};
if is_zip && book.file_type.to_lowercase() == "html" {
let filename = get_filename_by_book(book, file_type, true);
return Some(DownloadResult::new(Data::Response(response), filename));
}
if !is_zip && !final_need_zip && !converting {
let filename = get_filename_by_book(book, &book.file_type, false);
return Some(DownloadResult::new(Data::Response(response), filename));
};
let unziped_temp_file = {
let temp_file_to_unzip_result = response_to_tempfile(&mut response).await;
let temp_file_to_unzip = match temp_file_to_unzip_result {
Some(v) => v,
None => return None,
};
match unzip(temp_file_to_unzip, "fb2") {
Some(v) => v,
None => return None,
}
};
let mut clean_file = if converting {
match convert_file(unziped_temp_file, file_type.to_string()).await {
Some(mut response) => {
match response_to_tempfile(&mut response).await {
Some(v) => v,
None => return None,
}
},
None => return None,
}
} else {
unziped_temp_file
};
if !final_need_zip {
let t = SpooledTempAsyncRead::new(clean_file);
let filename = get_filename_by_book(book, file_type, false);
return Some(DownloadResult::new(Data::SpooledTempAsyncRead(t), filename));
};
let t_file_type = if file_type == "fb2zip" { "fb2" } else { file_type };
let filename = get_filename_by_book(book, t_file_type, false);
match zip(&mut clean_file, filename.as_str()) {
Some(v) => {
let t = SpooledTempAsyncRead::new(v);
let filename = get_filename_by_book(book, file_type, true);
Some(DownloadResult::new(Data::SpooledTempAsyncRead(t), filename))
},
None => None,
}
}
pub async fn start_download_futures(
book: &Book,
file_type: &str,
) -> Option<DownloadResult> {
let mut futures = FuturesUnordered::new();
for source_config in &config::CONFIG.fl_sources {
futures.push(download_chain(
book,
file_type,
source_config,
false
));
if file_type == "epub" || file_type == "fb2" {
futures.push(download_chain(
book,
file_type.clone(),
source_config,
true
))
}
}
while let Some(result) = futures.next().await {
match result {
Some(v) => return Some(v),
None => (),
}
}
None
}
pub async fn book_download(
source_id: u32,
remote_id: u32,
file_type: &str,
) -> Result<Option<(DownloadResult, String)>, Box<dyn std::error::Error + Send + Sync>> {
let book = match get_remote_book(source_id, remote_id).await {
Ok(v) => v,
Err(err) => return Err(err),
};
let filename = get_filename_by_book(&book, file_type, false);
match start_download_futures(&book, file_type).await {
Some(v) => Ok(Some((v, filename))),
None => Ok(None),
}
}