From 7d3670e951c4214ed73cda79ccc0f0fe7391a352 Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Thu, 12 Mar 2026 23:06:40 +0100 Subject: [PATCH] =?UTF-8?q?fix(api/pages):=20fallback=20CBR=E2=86=92ZIP=20?= =?UTF-8?q?et=20CBZ=E2=86=92RAR=20pour=20archives=20mal=20extensionn=C3=A9?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Même correctif que dans le parsers/indexer : un .cbr qui est en réalité un ZIP (et vice-versa) retourne maintenant la bonne page au lieu d'un 500. Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/pages.rs | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/apps/api/src/pages.rs b/apps/api/src/pages.rs index 59f07c6..7b33ceb 100644 --- a/apps/api/src/pages.rs +++ b/apps/api/src/pages.rs @@ -352,8 +352,8 @@ fn render_page( filter: image::imageops::FilterType, ) -> Result, ApiError> { let page_bytes = match input_format { - "cbz" => extract_cbz_page(abs_path, page_number)?, - "cbr" => extract_cbr_page(abs_path, page_number)?, + "cbz" => extract_cbz_page(abs_path, page_number, true)?, + "cbr" => extract_cbr_page(abs_path, page_number, true)?, "pdf" => render_pdf_page(abs_path, page_number, width)?, _ => return Err(ApiError::bad_request("unsupported source format")), }; @@ -361,7 +361,7 @@ fn render_page( transcode_image(&page_bytes, out_format, quality, width, filter) } -fn extract_cbz_page(abs_path: &str, page_number: u32) -> Result, ApiError> { +fn extract_cbz_page(abs_path: &str, page_number: u32, allow_fallback: bool) -> Result, ApiError> { debug!("Opening CBZ archive: {}", abs_path); let file = std::fs::File::open(abs_path).map_err(|e| { if e.kind() == std::io::ErrorKind::NotFound { @@ -371,11 +371,18 @@ fn extract_cbz_page(abs_path: &str, page_number: u32) -> Result, ApiErro ApiError::internal(format!("cannot open cbz: {e}")) } })?; - - let mut archive = zip::ZipArchive::new(file).map_err(|e| { - error!("Invalid CBZ archive {}: {}", abs_path, e); - ApiError::internal(format!("invalid cbz: {e}")) - })?; + + let mut archive = match zip::ZipArchive::new(file) { + Ok(a) => a, + Err(zip_err) => { + if allow_fallback { + warn!("CBZ open failed for {}, trying RAR fallback: {}", abs_path, zip_err); + return extract_cbr_page(abs_path, page_number, false); + } + error!("Invalid CBZ archive {}: {}", abs_path, zip_err); + return Err(ApiError::internal(format!("invalid cbz: {zip_err}"))); + } + }; let mut image_names: Vec = Vec::new(); for i in 0..archive.len() { @@ -410,15 +417,22 @@ fn extract_cbz_page(abs_path: &str, page_number: u32) -> Result, ApiErro Ok(buf) } -fn extract_cbr_page(abs_path: &str, page_number: u32) -> Result, ApiError> { +fn extract_cbr_page(abs_path: &str, page_number: u32, allow_fallback: bool) -> Result, ApiError> { info!("Opening CBR archive: {}", abs_path); let index = page_number as usize - 1; // Pass 1: list all image names (in-process, no subprocess) let mut image_names: Vec = { - let archive = unrar::Archive::new(abs_path) - .open_for_listing() - .map_err(|e| ApiError::internal(format!("unrar listing failed: {}", e)))?; + let archive = match unrar::Archive::new(abs_path).open_for_listing() { + Ok(a) => a, + Err(e) => { + if allow_fallback { + warn!("CBR open failed for {}, trying ZIP fallback: {}", abs_path, e); + return extract_cbz_page(abs_path, page_number, false); + } + return Err(ApiError::internal(format!("unrar listing failed: {}", e))); + } + }; let mut names = Vec::new(); for entry in archive { let entry = entry.map_err(|e| ApiError::internal(format!("unrar entry error: {}", e)))?;