✨ Add pmocovers integration for external cover URL proxying
- Introduce optional `pmocover` dependency in pmocontrol - Add cover URL transformation logic for both REST and SSE endpoints using `pmocovers::proxy_cover_url`/sync - Refactor `/covers/proxy?...=` handler to accept any external URL and return local cached route - Implement helper functions `transform_cover_url` (async) & sync variant for consistent cover URL normalization - Update Cargo.lock to include `pmocovers`
This commit is contained in:
@@ -109,9 +109,9 @@ pub struct CoverProxyResponse {
|
||||
|
||||
/// GET /covers/proxy?url=<encoded_url>
|
||||
/// Proxy transparent qui :
|
||||
/// 1. Détecte si l'URL est une URL LAN externe (pas déjà locale)
|
||||
/// 2. Ajoute à cache via add_from_url (déduplication automatique)
|
||||
/// 3. Retourne l'URL locale du cache
|
||||
/// 1. Ajoute l'URL au cache (add_from_url gère déduplication)
|
||||
/// 2. Retourne l'URL locale du cache
|
||||
/// Note: Si l'URL est déjà une cover locale de notre instance, on retourne directement l'URL
|
||||
#[cfg(feature = "pmoserver")]
|
||||
pub async fn cover_proxy_handler(
|
||||
Query(params): Query<CoverProxyParams>,
|
||||
@@ -120,31 +120,19 @@ pub async fn cover_proxy_handler(
|
||||
) -> impl IntoResponse {
|
||||
let external_url = ¶ms.url;
|
||||
|
||||
// Ignorer si déjà une URL de NOTRE instance pmomusic (ne pas se cacher soi-même)
|
||||
// Si c'est déjà une URL locale de NOTRE instance pmomusic, la retourner directement
|
||||
if is_local_cover_url(external_url, &base_url) {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(ErrorResponse {
|
||||
error: "INVALID_REQUEST".to_string(),
|
||||
message: "URL is already a local cover from this instance".to_string(),
|
||||
StatusCode::OK,
|
||||
Json(CoverProxyResponse {
|
||||
cached_url: external_url.clone(),
|
||||
pk: String::new(),
|
||||
}),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
// Vérifier si c'est une URL LAN à proxyfier
|
||||
if !should_proxy_url(external_url) {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(ErrorResponse {
|
||||
error: "INVALID_REQUEST".to_string(),
|
||||
message: "URL is not a LAN URL requiring proxy".to_string(),
|
||||
}),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
// Ajouter au cache (add_from_url gère la déduplication)
|
||||
// Ajouter au cache (add_from_url gère la déduplication et le download)
|
||||
match cache.add_from_url(external_url, Some("external-covers")).await {
|
||||
Ok(pk) => {
|
||||
// Retourner l'URL locale
|
||||
|
||||
@@ -135,6 +135,77 @@ pub fn get_cover_cache() -> Option<Arc<Cache>> {
|
||||
COVER_CACHE.get().cloned()
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helper pour proxyfier les URLs de covers externes
|
||||
// ============================================================================
|
||||
|
||||
/// Transforme une URL de cover externe en URL locale du cache.
|
||||
///
|
||||
/// Si l'URL est déjà une route locale de notre cache, la retourne directement.
|
||||
/// Sinon, ajoute l'URL au cache (download si nécessaire) et retourne l'URL locale.
|
||||
///
|
||||
/// Usage :
|
||||
/// ```rust
|
||||
/// let local_url = pmocovers::proxy_cover_url("https://example.com/cover.jpg").await?;
|
||||
/// ```
|
||||
#[cfg(feature = "pmoserver")]
|
||||
pub async fn proxy_cover_url(url: &str, base_url: &pmoserver::BaseUrl) -> anyhow::Result<String> {
|
||||
proxy_cover_url_sync_impl(url, base_url).await
|
||||
}
|
||||
|
||||
/// Version synchrone de proxy_cover_url.
|
||||
/// Utilise un runtime tokio temporaire pour exécuter add_from_url.
|
||||
///
|
||||
/// Usage :
|
||||
/// ```rust
|
||||
/// let local_url = pmocovers::proxy_cover_url_sync("https://example.com/cover.jpg", base_url);
|
||||
/// ```
|
||||
#[cfg(feature = "pmoserver")]
|
||||
pub fn proxy_cover_url_sync(url: &str, base_url: &pmoserver::BaseUrl) -> anyhow::Result<String> {
|
||||
// Si c'est déjà une route locale de notre cache, retourner directement
|
||||
if url.starts_with("/covers/") {
|
||||
return Ok(url.to_string());
|
||||
}
|
||||
|
||||
// Si c'est déjà une URL de notre instance, la retourner directement
|
||||
if url.starts_with(&base_url.0) {
|
||||
return Ok(url.to_string());
|
||||
}
|
||||
|
||||
// Ajouter au cache en utilisant un runtime temporaire
|
||||
let cache = get_cover_cache()
|
||||
.ok_or_else(|| anyhow::anyhow!("Cover cache not initialized"))?;
|
||||
|
||||
let runtime = tokio::runtime::Runtime::new()?;
|
||||
let pk = runtime.block_on(async move {
|
||||
cache.add_from_url(url, Some("external-covers")).await
|
||||
})?;
|
||||
|
||||
let route = pmocache::covers_route_for(&pk, None);
|
||||
Ok(base_url.url_for(&route))
|
||||
}
|
||||
|
||||
#[cfg(feature = "pmoserver")]
|
||||
async fn proxy_cover_url_sync_impl(url: &str, base_url: &pmoserver::BaseUrl) -> anyhow::Result<String> {
|
||||
// Si c'est déjà une route locale de notre cache, retourner directement
|
||||
if url.starts_with("/covers/") {
|
||||
return Ok(url.to_string());
|
||||
}
|
||||
|
||||
// Si c'est déjà une URL de notre instance, la retourner directement
|
||||
if url.starts_with(&base_url.0) {
|
||||
return Ok(url.to_string());
|
||||
}
|
||||
|
||||
// Ajouter au cache (add_from_url gère déduplication et download)
|
||||
let cache = get_cover_cache()
|
||||
.ok_or_else(|| anyhow::anyhow!("Cover cache not initialized"))?;
|
||||
|
||||
let pk = cache.add_from_url(url, Some("external-covers")).await?;
|
||||
let route = pmocache::covers_route_for(&pk, None);
|
||||
Ok(base_url.url_for(&route))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Extension pmoserver
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user