update la webapp

This commit is contained in:
2025-11-25 08:24:08 +01:00
parent 3957e17f64
commit aeddcc9c64
17 changed files with 1550 additions and 78 deletions

101
pmoaudiocache/src/api.rs Normal file
View File

@@ -0,0 +1,101 @@
//! API REST handlers spécifiques au cache audio
use crate::metadata_ext::AudioTrackMetadataExt;
use crate::Cache;
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use pmometadata::TrackMetadata;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use utoipa::ToSchema;
/// Réponse contenant l'URL de la cover avec fallback
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct CoverUrlResponse {
/// PK de la piste
#[schema(example = "1a2b3c4d5e6f7a8b")]
pub pk: String,
/// URL de la cover (cover_pk, cover_url, ou data URL par défaut)
#[schema(example = "https://example.com/cover.jpg")]
pub cover_url: String,
/// Source de l'URL: "cover_pk", "cover_url", ou "default"
#[schema(example = "cover_pk")]
pub source: String,
}
/// Récupère l'URL de la cover d'une piste avec logique de fallback
///
/// Cette route retourne l'URL de la cover en appliquant la logique de priorité suivante :
/// 1. Si `cover_pk` est défini dans les métadonnées, retourne la clé du cache de covers
/// 2. Sinon, si `cover_url` est défini, retourne l'URL externe
/// 3. Sinon, retourne une image SVG par défaut (data URL)
///
/// # Arguments
///
/// * `pk` - Clé primaire de la piste audio
///
/// # Responses
///
/// * `200 OK` - Retourne l'URL de la cover avec la source
/// * `404 NOT_FOUND` - Piste non trouvée
/// * `500 INTERNAL_SERVER_ERROR` - Erreur lors de la lecture des métadonnées
#[utoipa::path(
get,
path = "/{pk}/cover-url",
tag = "audio",
params(
("pk" = String, Path, description = "Clé primaire de la piste")
),
responses(
(status = 200, description = "URL de la cover récupérée avec succès", body = CoverUrlResponse),
(status = 404, description = "Piste non trouvée", body = pmocache::api::ErrorResponse),
(status = 500, description = "Erreur interne", body = pmocache::api::ErrorResponse),
)
)]
pub async fn get_cover_url(
State(cache): State<Arc<Cache>>,
Path(pk): Path<String>,
) -> impl IntoResponse {
// Vérifier que la piste existe
if cache.db.get(&pk, false).is_err() {
return (
StatusCode::NOT_FOUND,
Json(pmocache::api::ErrorResponse {
error: "NOT_FOUND".to_string(),
message: format!("Track with pk '{}' not found in cache", pk),
}),
)
.into_response();
}
// Récupérer les métadonnées
let metadata = cache.track_metadata(&pk);
let metadata_guard = metadata.read().await;
// Déterminer la source et l'URL
let (cover_url, source) = match metadata_guard.get_cover_pk().await {
Ok(Some(cover_pk)) if !cover_pk.is_empty() => (cover_pk, "cover_pk".to_string()),
_ => match metadata_guard.get_cover_url().await {
Ok(Some(url)) if !url.is_empty() => (url, "cover_url".to_string()),
_ => (
pmometadata::get_default_cover_url(),
"default".to_string(),
),
},
};
(
StatusCode::OK,
Json(CoverUrlResponse {
pk,
cover_url,
source,
}),
)
.into_response()
}

View File

@@ -81,6 +81,9 @@ pub mod metadata_ext;
pub mod streaming;
pub mod track_metadata;
#[cfg(feature = "pmoserver")]
pub mod api;
#[cfg(feature = "pmoserver")]
pub mod openapi;
@@ -218,7 +221,19 @@ impl AudioCacheExt for pmoserver::Server {
// API REST générique (pmocache)
// Routes: GET/POST/DELETE /api/audio, etc.
let api_router = create_api_router(cache.clone());
let mut api_router = create_api_router(cache.clone());
// Ajouter les endpoints audio spécifiques
// Route: GET /api/audio/{pk}/cover-url
api_router = api_router.merge(
axum::Router::new()
.route(
"/{pk}/cover-url",
axum::routing::get(crate::api::get_cover_url),
)
.with_state(cache.clone())
);
let openapi = crate::ApiDoc::openapi();
self.add_openapi(api_router, openapi, "audio").await;

View File

@@ -7,6 +7,9 @@ use utoipa::OpenApi;
/// L'API réutilise les handlers génériques de pmocache.
#[derive(OpenApi)]
#[openapi(
paths(
crate::api::get_cover_url,
),
components(
schemas(
pmocache::CacheEntry,
@@ -15,6 +18,7 @@ use utoipa::OpenApi;
pmocache::api::DeleteItemResponse,
pmocache::api::ErrorResponse,
pmocache::api::DownloadStatus,
crate::api::CoverUrlResponse,
)
),
tags(
@@ -56,6 +60,9 @@ Supprime une piste
### GET /api/audio/{pk}/status
Récupère le statut du téléchargement et de la conversion
### GET /api/audio/{pk}/cover-url
Récupère l'URL de la cover avec fallback automatique (cover_pk → cover_url → image par défaut)
### DELETE /api/audio
Purge complètement le cache
@@ -84,6 +91,8 @@ Les métadonnées suivantes sont extraites et stockées :
- Numéro de piste/disque, total de pistes/disques
- Durée, taux d'échantillonnage, bitrate
- Nombre de canaux
- Cover : `cover_pk` (clé dans le cache de covers) et `cover_url` (URL externe)
- Fallback automatique vers une image SVG par défaut si aucune cover n'est disponible
## Collections