Update la web app pour tirer partie du nouveau systeme de cache
This commit is contained in:
@@ -15,6 +15,7 @@ use axum::{
|
||||
Json,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "openapi")]
|
||||
@@ -39,6 +40,21 @@ pub struct DownloadStatus {
|
||||
pub finished: bool,
|
||||
/// Erreur éventuelle
|
||||
pub error: Option<String>,
|
||||
/// Informations sur la conversion
|
||||
pub conversion: Option<ConversionStatus>,
|
||||
}
|
||||
|
||||
/// Informations sur la conversion en cours ou réalisée
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||
pub struct ConversionStatus {
|
||||
/// Mode de conversion (ex: "passthrough", "transcode")
|
||||
#[cfg_attr(feature = "openapi", schema(example = "passthrough"))]
|
||||
pub mode: String,
|
||||
/// Codec source détecté (si disponible)
|
||||
pub input_codec: Option<String>,
|
||||
/// Informations complémentaires lisibles (optionnel)
|
||||
pub details: Option<String>,
|
||||
}
|
||||
|
||||
/// Requête pour ajouter un item au cache
|
||||
@@ -134,30 +150,74 @@ pub async fn get_download_status<C: CacheConfig>(
|
||||
State(cache): State<Arc<Cache<C>>>,
|
||||
Path(pk): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
// Vérifier que l'item existe dans la DB
|
||||
if cache.db.get(&pk, false).is_err() {
|
||||
return (
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(ErrorResponse {
|
||||
error: "NOT_FOUND".to_string(),
|
||||
message: format!("Item with pk '{}' not found in cache", pk),
|
||||
}),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
let entry = match cache.db.get(&pk, false) {
|
||||
Ok(entry) => entry,
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(ErrorResponse {
|
||||
error: "NOT_FOUND".to_string(),
|
||||
message: format!("Item with pk '{}' not found in cache", pk),
|
||||
}),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let in_progress = cache.get_download(&pk).await.is_some();
|
||||
let current_size = cache.current_size(&pk).await;
|
||||
let transformed_size = cache.transformed_size(&pk).await;
|
||||
let expected_size = cache.expected_size(&pk).await;
|
||||
let finished = cache.is_finished(&pk).await;
|
||||
let download = cache.get_download(&pk).await;
|
||||
let file_path = cache.get_file_path(&pk);
|
||||
let file_size = if file_path.exists() {
|
||||
std::fs::metadata(&file_path).ok().map(|m| m.len())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let error = if let Some(download) = cache.get_download(&pk).await {
|
||||
let in_progress = download.is_some();
|
||||
let current_size = if let Some(download) = download.as_ref() {
|
||||
Some(download.current_size().await)
|
||||
} else {
|
||||
file_size
|
||||
};
|
||||
|
||||
let transformed_size = if let Some(download) = download.as_ref() {
|
||||
Some(download.transformed_size().await)
|
||||
} else {
|
||||
file_size
|
||||
};
|
||||
|
||||
let expected_size = if let Some(download) = download.as_ref() {
|
||||
download.expected_size().await
|
||||
} else {
|
||||
file_size
|
||||
};
|
||||
|
||||
let finished = if let Some(download) = download.as_ref() {
|
||||
download.finished().await
|
||||
} else {
|
||||
file_path.exists()
|
||||
};
|
||||
|
||||
let error = if let Some(download) = download.as_ref() {
|
||||
download.error().await
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut conversion = if let Some(download) = download.as_ref() {
|
||||
download
|
||||
.transform_metadata()
|
||||
.await
|
||||
.map(ConversionStatus::from)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if conversion.is_none() {
|
||||
if let Some(meta) = entry.metadata.as_ref() {
|
||||
conversion = conversion_from_json(meta);
|
||||
}
|
||||
}
|
||||
|
||||
let status = DownloadStatus {
|
||||
pk,
|
||||
in_progress,
|
||||
@@ -166,11 +226,28 @@ pub async fn get_download_status<C: CacheConfig>(
|
||||
expected_size,
|
||||
finished,
|
||||
error,
|
||||
conversion,
|
||||
};
|
||||
|
||||
(StatusCode::OK, Json(status)).into_response()
|
||||
}
|
||||
|
||||
impl From<crate::download::TransformMetadata> for ConversionStatus {
|
||||
fn from(value: crate::download::TransformMetadata) -> Self {
|
||||
Self {
|
||||
mode: value.mode.unwrap_or_else(|| "unknown".to_string()),
|
||||
input_codec: value.input_codec,
|
||||
details: value.details,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn conversion_from_json(value: &Value) -> Option<ConversionStatus> {
|
||||
value
|
||||
.get("conversion")
|
||||
.and_then(|conv| serde_json::from_value(conv.clone()).ok())
|
||||
}
|
||||
|
||||
/// Ajoute un item au cache depuis une URL
|
||||
///
|
||||
/// Télécharge l'item depuis l'URL fournie et l'ajoute au cache.
|
||||
|
||||
@@ -94,9 +94,10 @@ impl<C: CacheConfig> Cache<C> {
|
||||
///
|
||||
/// let transformer_factory = Arc::new(|| {
|
||||
/// // Créer un transformer qui convertit les données
|
||||
/// Box::new(|input, file, progress| {
|
||||
/// Box::new(|input, file, ctx| {
|
||||
/// Box::pin(async move {
|
||||
/// // Transformation personnalisée
|
||||
/// ctx.report_progress(0);
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// }) as StreamTransformer
|
||||
@@ -610,6 +611,15 @@ impl<C: CacheConfig> Cache<C> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Retourne les métadonnées de transformation (si disponibles)
|
||||
pub async fn transform_metadata(&self, pk: &str) -> Option<crate::download::TransformMetadata> {
|
||||
if let Some(download) = self.get_download(pk).await {
|
||||
download.transform_metadata().await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Indique si le téléchargement est terminé
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -15,18 +15,20 @@ use tokio_util::io::ReaderStream;
|
||||
/// La fonction reçoit :
|
||||
/// - Un `CacheInput` abstrait (HTTP ou lecteur en streaming)
|
||||
/// - Un writer pour écrire les données transformées
|
||||
/// - Un callback pour mettre à jour la progression
|
||||
/// - Un contexte fournissant des utilitaires (progression, métadonnées)
|
||||
///
|
||||
/// Elle retourne un `Future` qui se résout en `Result`.
|
||||
pub type StreamTransformer = Box<
|
||||
dyn FnOnce(
|
||||
CacheInput,
|
||||
tokio::fs::File,
|
||||
Arc<dyn Fn(u64) + Send + Sync>,
|
||||
TransformContextHandle,
|
||||
) -> Pin<Box<dyn Future<Output = Result<(), String>> + Send>>
|
||||
+ Send,
|
||||
>;
|
||||
|
||||
pub type TransformContextHandle = Arc<TransformContext>;
|
||||
|
||||
type ByteStream = Pin<Box<dyn Stream<Item = Result<Bytes, String>> + Send>>;
|
||||
|
||||
/// Source générique (HTTP ou lecteur) exposée aux transformers.
|
||||
@@ -180,6 +182,7 @@ struct DownloadState {
|
||||
finished: bool,
|
||||
read_position: u64,
|
||||
error: Option<String>,
|
||||
transform_metadata: Option<TransformMetadata>,
|
||||
}
|
||||
|
||||
/// Objet représentant un téléchargement en cours
|
||||
@@ -200,6 +203,7 @@ impl Download {
|
||||
finished: false,
|
||||
read_position: 0,
|
||||
error: None,
|
||||
transform_metadata: None,
|
||||
})),
|
||||
})
|
||||
}
|
||||
@@ -274,6 +278,45 @@ impl Download {
|
||||
let state = self.state.read().await;
|
||||
state.error.clone()
|
||||
}
|
||||
|
||||
pub async fn transform_metadata(&self) -> Option<TransformMetadata> {
|
||||
let state = self.state.read().await;
|
||||
state.transform_metadata.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TransformMetadata {
|
||||
pub mode: Option<String>,
|
||||
pub input_codec: Option<String>,
|
||||
pub details: Option<String>,
|
||||
}
|
||||
|
||||
pub struct TransformContext {
|
||||
state: Arc<RwLock<DownloadState>>,
|
||||
progress_cb: Arc<dyn Fn(u64) + Send + Sync>,
|
||||
}
|
||||
|
||||
impl TransformContext {
|
||||
fn new(state: Arc<RwLock<DownloadState>>, progress_cb: Arc<dyn Fn(u64) + Send + Sync>) -> Self {
|
||||
Self { state, progress_cb }
|
||||
}
|
||||
|
||||
/// Reports progress (in bytes) to the download state.
|
||||
pub fn report_progress(&self, bytes: u64) {
|
||||
(self.progress_cb)(bytes);
|
||||
}
|
||||
|
||||
/// Returns the underlying progress callback (useful for piping into other APIs).
|
||||
pub fn progress_callback(&self) -> Arc<dyn Fn(u64) + Send + Sync> {
|
||||
Arc::clone(&self.progress_cb)
|
||||
}
|
||||
|
||||
/// Stores metadata describing the transformation that occurred.
|
||||
pub async fn set_metadata(&self, metadata: TransformMetadata) {
|
||||
let mut state = self.state.write().await;
|
||||
state.transform_metadata = Some(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lance le téléchargement d'une URL dans un fichier.
|
||||
@@ -402,7 +445,9 @@ async fn process_input(
|
||||
});
|
||||
});
|
||||
|
||||
match transformer(input, file, Arc::clone(&progress_callback)).await {
|
||||
let context = Arc::new(TransformContext::new(Arc::clone(&state), progress_callback));
|
||||
|
||||
match transformer(input, file, Arc::clone(&context)).await {
|
||||
Ok(_) => {
|
||||
let mut s = state.write().await;
|
||||
if s.current_size == 0 {
|
||||
|
||||
@@ -134,7 +134,7 @@ pub use cache_trait::{pk_from_content_header, FileCache};
|
||||
pub use db::{CacheEntry, DB};
|
||||
pub use download::{
|
||||
download, download_with_transformer, ingest_with_transformer, peek_header, peek_reader_header,
|
||||
Download, StreamTransformer,
|
||||
Download, StreamTransformer, TransformContextHandle, TransformMetadata,
|
||||
};
|
||||
|
||||
#[cfg(feature = "pmoserver")]
|
||||
|
||||
Reference in New Issue
Block a user