perf(api,indexer): optimiser pages, thumbnails, watcher et robustesse fd
- Pages: mode Original (zero-transcoding), ETag/304, cache index CBZ, préfetch next 2 pages, filtre Triangle par défaut - Thumbnails: DCT scaling JPEG via jpeg-decoder (decode 7x plus rapide), img.thumbnail() pour resize, support format Original, fix JPEG RGBA8 - API fallback thumbnail: OutputFormat::Original + DCT scaling au lieu de WebP full-decode, retour (bytes, content_type) dynamique - Watcher: remplacement notify par poll léger sans inotify/fd, skip poll quand job actif, snapshots en mémoire - Jobs: mutex exclusif corrigé (tous statuts actifs, tous types exclusifs) - Robustesse: suppression fs::canonicalize (problèmes fd Docker), list_folders avec erreurs explicites, has_children default true - Backoffice: FormRow items-start pour alignement inputs avec helper text, labels settings clarifiés Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
use anyhow::{Context, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BookFormat {
|
||||
@@ -527,6 +528,40 @@ pub fn extract_page(path: &Path, format: BookFormat, page_number: u32, pdf_rende
|
||||
}
|
||||
}
|
||||
|
||||
/// Cache of sorted image names per archive path. Avoids re-listing and sorting on every page request.
|
||||
static CBZ_INDEX_CACHE: OnceLock<Mutex<HashMap<PathBuf, Vec<String>>>> = OnceLock::new();
|
||||
|
||||
fn cbz_index_cache() -> &'static Mutex<HashMap<PathBuf, Vec<String>>> {
|
||||
CBZ_INDEX_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
|
||||
}
|
||||
|
||||
/// Get sorted image names from cache, or list + sort + cache them.
|
||||
fn get_cbz_image_index(path: &Path, archive: &mut zip::ZipArchive<std::fs::File>) -> Vec<String> {
|
||||
{
|
||||
let cache = cbz_index_cache().lock().unwrap();
|
||||
if let Some(names) = cache.get(path) {
|
||||
return names.clone();
|
||||
}
|
||||
}
|
||||
let mut image_names: Vec<String> = Vec::new();
|
||||
for i in 0..archive.len() {
|
||||
let entry = match archive.by_index(i) {
|
||||
Ok(e) => e,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let name = entry.name().to_ascii_lowercase();
|
||||
if is_image_name(&name) {
|
||||
image_names.push(entry.name().to_string());
|
||||
}
|
||||
}
|
||||
image_names.sort_by(|a, b| natord::compare(a, b));
|
||||
{
|
||||
let mut cache = cbz_index_cache().lock().unwrap();
|
||||
cache.insert(path.to_path_buf(), image_names.clone());
|
||||
}
|
||||
image_names
|
||||
}
|
||||
|
||||
fn extract_cbz_page(path: &Path, page_number: u32, allow_fallback: bool) -> Result<Vec<u8>> {
|
||||
let file = std::fs::File::open(path)
|
||||
.with_context(|| format!("cannot open cbz: {}", path.display()))?;
|
||||
@@ -534,18 +569,7 @@ fn extract_cbz_page(path: &Path, page_number: u32, allow_fallback: bool) -> Resu
|
||||
|
||||
match zip::ZipArchive::new(file) {
|
||||
Ok(mut archive) => {
|
||||
let mut image_names: Vec<String> = Vec::new();
|
||||
for i in 0..archive.len() {
|
||||
let entry = match archive.by_index(i) {
|
||||
Ok(e) => e,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let name = entry.name().to_ascii_lowercase();
|
||||
if is_image_name(&name) {
|
||||
image_names.push(entry.name().to_string());
|
||||
}
|
||||
}
|
||||
image_names.sort_by(|a, b| natord::compare(a, b));
|
||||
let image_names = get_cbz_image_index(path, &mut archive);
|
||||
|
||||
let selected = image_names
|
||||
.get(index)
|
||||
|
||||
Reference in New Issue
Block a user