amélioration de la webapp

This commit is contained in:
2025-10-19 13:42:29 +02:00
parent 756ae7f82c
commit 208fe8be76
179 changed files with 2426 additions and 1685 deletions

View File

@@ -43,12 +43,14 @@ fn create_webp_transformer() -> StreamTransformer {
// Convertir en WebP
let img = image::load_from_memory(&bytes)
.map_err(|e| format!("Image decode error: {}", e))?;
let webp_data = crate::webp::encode_webp(&img)
.map_err(|e| format!("WebP encode error: {}", e))?;
let webp_data =
crate::webp::encode_webp(&img).map_err(|e| format!("WebP encode error: {}", e))?;
// Écrire et mettre à jour la progression
use tokio::io::AsyncWriteExt;
file.write_all(&webp_data).await.map_err(|e| e.to_string())?;
file.write_all(&webp_data)
.await
.map_err(|e| e.to_string())?;
file.flush().await.map_err(|e| e.to_string())?;
progress(webp_data.len() as u64);

View File

@@ -42,15 +42,15 @@ pub mod webp;
#[cfg(feature = "pmoserver")]
pub mod openapi;
pub use cache::{Cache, CoversConfig, new_cache};
pub use cache::{new_cache, Cache, CoversConfig};
#[cfg(feature = "pmoserver")]
pub use openapi::ApiDoc;
#[cfg(feature = "pmoserver")]
use utoipa::OpenApi;
#[cfg(feature = "pmoserver")]
use std::sync::Arc;
#[cfg(feature = "pmoserver")]
use utoipa::OpenApi;
/// Générateur de variantes d'images
///
@@ -64,7 +64,13 @@ fn create_variant_generator() -> pmocache::pmoserver_ext::ParamGenerator<CoversC
match webp::generate_variant(&cache, &pk, size).await {
Ok(data) => return Some(data),
Err(e) => {
tracing::warn!("Cannot generate variant {}x{} for {}: {}", size, size, pk, e);
tracing::warn!(
"Cannot generate variant {}x{} for {}: {}",
size,
size,
pk,
e
);
return None;
}
}
@@ -98,21 +104,26 @@ pub trait CoverCacheExt {
/// - `DELETE /api/covers/{pk}` - Supprimer une image (API REST)
/// - `GET /api/covers/{pk}/status` - Statut du téléchargement
/// - `GET /swagger-ui/covers` - Documentation interactive
async fn init_cover_cache(&mut self, cache_dir: &str, limit: usize)
-> anyhow::Result<Arc<Cache>>;
async fn init_cover_cache(
&mut self,
cache_dir: &str,
limit: usize,
) -> anyhow::Result<Arc<Cache>>;
/// Initialise le cache d'images avec la configuration par défaut
///
/// Utilise automatiquement les paramètres de `pmoconfig::Config`
async fn init_cover_cache_configured(&mut self)
-> anyhow::Result<Arc<Cache>>;
async fn init_cover_cache_configured(&mut self) -> anyhow::Result<Arc<Cache>>;
}
#[cfg(feature = "pmoserver")]
impl CoverCacheExt for pmoserver::Server {
async fn init_cover_cache(&mut self, cache_dir: &str, limit: usize)
-> anyhow::Result<Arc<Cache>> {
use pmocache::pmoserver_ext::{create_file_router_with_generator, create_api_router};
async fn init_cover_cache(
&mut self,
cache_dir: &str,
limit: usize,
) -> anyhow::Result<Arc<Cache>> {
use pmocache::pmoserver_ext::{create_api_router, create_file_router_with_generator};
let cache = Arc::new(cache::new_cache(cache_dir, limit)?);
@@ -121,7 +132,7 @@ impl CoverCacheExt for pmoserver::Server {
let file_router = create_file_router_with_generator(
cache.clone(),
"image/webp",
Some(create_variant_generator())
Some(create_variant_generator()),
);
self.add_router("/", file_router).await;
@@ -134,8 +145,7 @@ impl CoverCacheExt for pmoserver::Server {
Ok(cache)
}
async fn init_cover_cache_configured(&mut self)
-> anyhow::Result<Arc<Cache>> {
async fn init_cover_cache_configured(&mut self) -> anyhow::Result<Arc<Cache>> {
let config = pmoconfig::get_config();
let cache_dir = config.get_cover_cache_dir()?;
let limit = config.get_cover_cache_size()?;

View File

@@ -1,5 +1,5 @@
use anyhow::Result;
use image::{DynamicImage, imageops::FilterType};
use image::{imageops::FilterType, DynamicImage};
use webp::{Encoder, WebPMemory};
pub fn encode_webp(img: &DynamicImage) -> Result<Vec<u8>> {
@@ -11,34 +11,38 @@ pub fn encode_webp(img: &DynamicImage) -> Result<Vec<u8>> {
pub fn ensure_square(img: &DynamicImage, size: u32) -> DynamicImage {
let (width, height) = (img.width(), img.height());
// Calculer le ratio de mise à l'échelle
let scale = if width > height {
size as f32 / width as f32
} else {
size as f32 / height as f32
};
let new_width = (width as f32 * scale) as u32;
let new_height = (height as f32 * scale) as u32;
// Redimensionner l'image
let resized = img.resize(new_width, new_height, FilterType::Lanczos3);
// Créer une image carrée avec fond transparent
let mut square = DynamicImage::new_rgba8(size, size);
// Calculer la position pour centrer l'image redimensionnée
let x = (size - new_width) / 2;
let y = (size - new_height) / 2;
// Copier l'image redimensionnée au centre du carré
image::imageops::overlay(&mut square, &resized, x.into(), y.into());
square
}
pub async fn generate_variant(cache: &super::cache::Cache, pk: &str, size: usize) -> Result<Vec<u8>> {
pub async fn generate_variant(
cache: &super::cache::Cache,
pk: &str,
size: usize,
) -> Result<Vec<u8>> {
// Utiliser file_path_with_qualifier pour obtenir le chemin
let variant_path = cache.file_path_with_qualifier(pk, &size.to_string());
@@ -49,10 +53,7 @@ pub async fn generate_variant(cache: &super::cache::Cache, pk: &str, size: usize
let orig_path = cache.file_path_with_qualifier(pk, "orig");
// Charger l'image de manière synchrone (image::open n'est pas async)
let img = tokio::task::spawn_blocking(move || {
image::open(orig_path)
})
.await??;
let img = tokio::task::spawn_blocking(move || image::open(orig_path)).await??;
let square = ensure_square(&img, size as u32);
let webp_data = encode_webp(&square)?;