fix(parsers): corriger récursion infinie CBZ↔CBR causant un stack overflow
analyze_cbz et analyze_cbr se rappelaient mutuellement sans garde quand un fichier échouait les deux formats → stack overflow à l'analyse. Ajout d'un paramètre allow_fallback=false pour briser la boucle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -152,20 +152,21 @@ pub fn parse_metadata(
|
|||||||
/// `pdf_render_scale`: max dimension used for PDF rasterization; 0 means use default (400).
|
/// `pdf_render_scale`: max dimension used for PDF rasterization; 0 means use default (400).
|
||||||
pub fn analyze_book(path: &Path, format: BookFormat, pdf_render_scale: u32) -> Result<(i32, Vec<u8>)> {
|
pub fn analyze_book(path: &Path, format: BookFormat, pdf_render_scale: u32) -> Result<(i32, Vec<u8>)> {
|
||||||
match format {
|
match format {
|
||||||
BookFormat::Cbz => analyze_cbz(path),
|
BookFormat::Cbz => analyze_cbz(path, true),
|
||||||
BookFormat::Cbr => analyze_cbr(path),
|
BookFormat::Cbr => analyze_cbr(path, true),
|
||||||
BookFormat::Pdf => analyze_pdf(path, pdf_render_scale),
|
BookFormat::Pdf => analyze_pdf(path, pdf_render_scale),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_cbz(path: &Path) -> Result<(i32, Vec<u8>)> {
|
fn analyze_cbz(path: &Path, allow_fallback: bool) -> Result<(i32, Vec<u8>)> {
|
||||||
let file = std::fs::File::open(path)
|
let file = std::fs::File::open(path)
|
||||||
.with_context(|| format!("cannot open cbz: {}", path.display()))?;
|
.with_context(|| format!("cannot open cbz: {}", path.display()))?;
|
||||||
let mut archive = match zip::ZipArchive::new(file) {
|
let mut archive = match zip::ZipArchive::new(file) {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
if allow_fallback {
|
||||||
// Some .cbz files are actually RAR archives with the wrong extension — fallback to CBR parser
|
// Some .cbz files are actually RAR archives with the wrong extension — fallback to CBR parser
|
||||||
return analyze_cbr(path).map_err(|rar_err| {
|
return analyze_cbr(path, false).map_err(|rar_err| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"invalid cbz archive and RAR fallback also failed for {}: ZIP={}, RAR={}",
|
"invalid cbz archive and RAR fallback also failed for {}: ZIP={}, RAR={}",
|
||||||
path.display(),
|
path.display(),
|
||||||
@@ -174,6 +175,8 @@ fn analyze_cbz(path: &Path) -> Result<(i32, Vec<u8>)> {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return Err(anyhow::anyhow!("invalid cbz archive for {}: {}", path.display(), e));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut image_names: Vec<String> = Vec::new();
|
let mut image_names: Vec<String> = Vec::new();
|
||||||
@@ -198,7 +201,7 @@ fn analyze_cbz(path: &Path) -> Result<(i32, Vec<u8>)> {
|
|||||||
Ok((count, buf))
|
Ok((count, buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_cbr(path: &Path) -> Result<(i32, Vec<u8>)> {
|
fn analyze_cbr(path: &Path, allow_fallback: bool) -> Result<(i32, Vec<u8>)> {
|
||||||
// Pass 1: list all image names via unrar (in-process, no subprocess)
|
// Pass 1: list all image names via unrar (in-process, no subprocess)
|
||||||
let mut image_names: Vec<String> = {
|
let mut image_names: Vec<String> = {
|
||||||
let archive = unrar::Archive::new(path)
|
let archive = unrar::Archive::new(path)
|
||||||
@@ -209,8 +212,8 @@ fn analyze_cbr(path: &Path) -> Result<(i32, Vec<u8>)> {
|
|||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let e_str = e.to_string();
|
let e_str = e.to_string();
|
||||||
if e_str.contains("Not a RAR archive") || e_str.contains("bad archive") {
|
if allow_fallback && (e_str.contains("Not a RAR archive") || e_str.contains("bad archive")) {
|
||||||
return analyze_cbz(path).map_err(|zip_err| {
|
return analyze_cbz(path, false).map_err(|zip_err| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"not a RAR archive and ZIP fallback also failed for {}: RAR={}, ZIP={}",
|
"not a RAR archive and ZIP fallback also failed for {}: RAR={}, ZIP={}",
|
||||||
path.display(),
|
path.display(),
|
||||||
@@ -371,7 +374,7 @@ fn is_image_name(name: &str) -> bool {
|
|||||||
pub fn extract_first_page(path: &Path, format: BookFormat) -> Result<Vec<u8>> {
|
pub fn extract_first_page(path: &Path, format: BookFormat) -> Result<Vec<u8>> {
|
||||||
match format {
|
match format {
|
||||||
BookFormat::Cbz => extract_cbz_first_page(path),
|
BookFormat::Cbz => extract_cbz_first_page(path),
|
||||||
BookFormat::Cbr => analyze_cbr(path).map(|(_, bytes)| bytes),
|
BookFormat::Cbr => analyze_cbr(path, true).map(|(_, bytes)| bytes),
|
||||||
BookFormat::Pdf => analyze_pdf(path, 0).map(|(_, bytes)| bytes),
|
BookFormat::Pdf => analyze_pdf(path, 0).map(|(_, bytes)| bytes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user