mirror of
https://github.com/flibusta-apps/books_downloader.git
synced 2025-12-06 15:05:37 +01:00
Add x-filename-b64
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -185,6 +185,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-prometheus",
|
"axum-prometheus",
|
||||||
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ zip = "0.6.3"
|
|||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
bytes = "1.3.0"
|
bytes = "1.3.0"
|
||||||
axum-prometheus = "0.2.0"
|
axum-prometheus = "0.2.0"
|
||||||
|
base64 = "0.13.0"
|
||||||
|
|||||||
@@ -94,13 +94,27 @@ pub async fn download_chain<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_zip && book.file_type.to_lowercase() == "html" {
|
if is_zip && book.file_type.to_lowercase() == "html" {
|
||||||
let filename = get_filename_by_book(book, file_type, true);
|
let filename = get_filename_by_book(book, file_type, true, false);
|
||||||
return Some(DownloadResult::new(Data::Response(response), filename));
|
let filename_ascii = get_filename_by_book(book, file_type, true, true);
|
||||||
|
return Some(
|
||||||
|
DownloadResult::new(
|
||||||
|
Data::Response(response),
|
||||||
|
filename,
|
||||||
|
filename_ascii
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_zip && !final_need_zip && !converting {
|
if !is_zip && !final_need_zip && !converting {
|
||||||
let filename = get_filename_by_book(book, &book.file_type, false);
|
let filename = get_filename_by_book(book, &book.file_type, false, false);
|
||||||
return Some(DownloadResult::new(Data::Response(response), filename));
|
let filename_ascii = get_filename_by_book(book, file_type, false, true);
|
||||||
|
return Some(
|
||||||
|
DownloadResult::new(
|
||||||
|
Data::Response(response),
|
||||||
|
filename,
|
||||||
|
filename_ascii,
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let unziped_temp_file = {
|
let unziped_temp_file = {
|
||||||
@@ -133,17 +147,31 @@ pub async fn download_chain<'a>(
|
|||||||
|
|
||||||
if !final_need_zip {
|
if !final_need_zip {
|
||||||
let t = SpooledTempAsyncRead::new(clean_file);
|
let t = SpooledTempAsyncRead::new(clean_file);
|
||||||
let filename = get_filename_by_book(book, file_type, false);
|
let filename = get_filename_by_book(book, file_type, false, false);
|
||||||
return Some(DownloadResult::new(Data::SpooledTempAsyncRead(t), filename));
|
let filename_ascii = get_filename_by_book(book, file_type, false, true);
|
||||||
|
return Some(
|
||||||
|
DownloadResult::new(
|
||||||
|
Data::SpooledTempAsyncRead(t),
|
||||||
|
filename,
|
||||||
|
filename_ascii
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let t_file_type = if file_type == "fb2zip" { "fb2" } else { file_type };
|
let t_file_type = if file_type == "fb2zip" { "fb2" } else { file_type };
|
||||||
let filename = get_filename_by_book(book, t_file_type, false);
|
let filename = get_filename_by_book(book, t_file_type, false, false);
|
||||||
match zip(&mut clean_file, filename.as_str()) {
|
match zip(&mut clean_file, filename.as_str()) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let t = SpooledTempAsyncRead::new(v);
|
let t = SpooledTempAsyncRead::new(v);
|
||||||
let filename = get_filename_by_book(book, file_type, true);
|
let filename = get_filename_by_book(book, file_type, true, false);
|
||||||
Some(DownloadResult::new(Data::SpooledTempAsyncRead(t), filename))
|
let filename_ascii = get_filename_by_book(book, file_type, true, true);
|
||||||
|
Some(
|
||||||
|
DownloadResult::new(
|
||||||
|
Data::SpooledTempAsyncRead(t),
|
||||||
|
filename,
|
||||||
|
filename_ascii
|
||||||
|
)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
@@ -187,16 +215,14 @@ pub async fn book_download(
|
|||||||
source_id: u32,
|
source_id: u32,
|
||||||
remote_id: u32,
|
remote_id: u32,
|
||||||
file_type: &str,
|
file_type: &str,
|
||||||
) -> Result<Option<(DownloadResult, String)>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Option<DownloadResult>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let book = match get_remote_book(source_id, remote_id).await {
|
let book = match get_remote_book(source_id, remote_id).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
let filename = get_filename_by_book(&book, file_type, false);
|
|
||||||
|
|
||||||
match start_download_futures(&book, file_type).await {
|
match start_download_futures(&book, file_type).await {
|
||||||
Some(v) => Ok(Some((v, filename))),
|
Some(v) => Ok(Some(v)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub enum Data {
|
|||||||
pub struct DownloadResult {
|
pub struct DownloadResult {
|
||||||
pub data: Data,
|
pub data: Data,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
|
pub filename_ascii: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_response_async_read(it: Response) -> impl AsyncRead {
|
pub fn get_response_async_read(it: Response) -> impl AsyncRead {
|
||||||
@@ -24,8 +25,8 @@ pub fn get_response_async_read(it: Response) -> impl AsyncRead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadResult {
|
impl DownloadResult {
|
||||||
pub fn new(data: Data, filename: String) -> Self {
|
pub fn new(data: Data, filename: String, filename_ascii: String) -> Self {
|
||||||
Self { data, filename }
|
Self { data, filename, filename_ascii }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_async_read(self) -> Pin<Box<dyn AsyncRead + Send>> {
|
pub fn get_async_read(self) -> Pin<Box<dyn AsyncRead + Send>> {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub fn get_author_short_name(author: BookAuthor) -> String {
|
|||||||
parts.join(" ")
|
parts.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_filename_by_book(book: &BookWithRemote, file_type: &str, force_zip: bool) -> String {
|
pub fn get_filename_by_book(book: &BookWithRemote, file_type: &str, force_zip: bool, only_ascii: bool) -> String {
|
||||||
let book_id = book.remote_id;
|
let book_id = book.remote_id;
|
||||||
let mut filename_parts: Vec<String> = vec![];
|
let mut filename_parts: Vec<String> = vec![];
|
||||||
|
|
||||||
@@ -73,12 +73,14 @@ pub fn get_filename_by_book(book: &BookWithRemote, file_type: &str, force_zip: b
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let replace_transliterator = Transliterator::new(replace_char_map);
|
let replace_transliterator = Transliterator::new(replace_char_map);
|
||||||
let normal_filename = replace_transliterator.convert(&filename_without_type, false);
|
let mut normal_filename = replace_transliterator.convert(&filename_without_type, false);
|
||||||
|
|
||||||
let normal_filename = normal_filename.replace(|c: char| !c.is_ascii(), "");
|
if only_ascii {
|
||||||
|
normal_filename = normal_filename.replace(|c: char| !c.is_ascii(), "");
|
||||||
|
}
|
||||||
|
|
||||||
let right_part = format!(".{book_id}.{file_type_}");
|
let right_part = format!(".{book_id}.{file_type_}");
|
||||||
let normal_filename_slice = std::cmp::min(64 - right_part.len() - 1, normal_filename.len());
|
let normal_filename_slice = std::cmp::min(64 - right_part.len() - 1, normal_filename.len() -1);
|
||||||
let left_part = normal_filename.get(..normal_filename_slice).expect(
|
let left_part = normal_filename.get(..normal_filename_slice).expect(
|
||||||
&format!("Can't slice left part: {:?} {:?}", normal_filename, normal_filename_slice)
|
&format!("Can't slice left part: {:?} {:?}", normal_filename, normal_filename_slice)
|
||||||
);
|
);
|
||||||
|
|||||||
10
src/views.rs
10
src/views.rs
@@ -30,17 +30,21 @@ pub async fn download(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let (data, filename) = match download_result {
|
let data = match download_result {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err((StatusCode::NO_CONTENT, "Can't download!".to_string())),
|
None => return Err((StatusCode::NO_CONTENT, "Can't download!".to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let filename = data.filename.clone();
|
||||||
|
let filename_ascii = data.filename_ascii.clone();
|
||||||
|
|
||||||
let reader = data.get_async_read();
|
let reader = data.get_async_read();
|
||||||
let stream = ReaderStream::new(reader);
|
let stream = ReaderStream::new(reader);
|
||||||
let body = StreamBody::new(stream);
|
let body = StreamBody::new(stream);
|
||||||
|
|
||||||
let headers = AppendHeaders([
|
let headers = AppendHeaders([
|
||||||
(header::CONTENT_DISPOSITION, format!("attachment; filename={filename}"))
|
(header::CONTENT_DISPOSITION, format!("attachment; filename={filename_ascii}")),
|
||||||
|
(header::HeaderName::from_static("x-filename-b64"), base64::encode(filename))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Ok((headers, body))
|
Ok((headers, body))
|
||||||
@@ -62,7 +66,7 @@ pub async fn get_filename(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let filename = match get_book(book_id).await {
|
let filename = match get_book(book_id).await {
|
||||||
Ok(book) => get_filename_by_book(&book, file_type.as_str(), false),
|
Ok(book) => get_filename_by_book(&book, file_type.as_str(), false, false),
|
||||||
Err(_) => return (StatusCode::BAD_REQUEST, "Book not found!".to_string()),
|
Err(_) => return (StatusCode::BAD_REQUEST, "Book not found!".to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user