From c7230d3b3298b21bc4653a4cef4f961e62566db3 Mon Sep 17 00:00:00 2001 From: KD Date: Mon, 28 Jul 2025 17:05:42 -0400 Subject: [PATCH] Download coverart endpoint (#166) * Added download coverart endpoint * Endpoint is now available * Added test * Code formatting * Version bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/callers/coverart.rs | 36 +++++++++++++++++++++++++ src/callers/mod.rs | 1 + src/main.rs | 60 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f02946..b76cb8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "icarus" -version = "0.1.97" +version = "0.1.98" dependencies = [ "axum", "common-multipart-rfc7578", diff --git a/Cargo.toml b/Cargo.toml index 2773535..1c63dbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icarus" -version = "0.1.97" +version = "0.1.98" edition = "2024" rust-version = "1.88" diff --git a/src/callers/coverart.rs b/src/callers/coverart.rs index 7a3398a..3818f57 100644 --- a/src/callers/coverart.rs +++ b/src/callers/coverart.rs @@ -646,4 +646,40 @@ pub mod endpoint { } } } + + pub async fn download_coverart( + axum::Extension(pool): axum::Extension, + axum::extract::Path(id): axum::extract::Path, + ) -> (axum::http::StatusCode, axum::response::Response) { + match super::cov_db::get_coverart(&pool, &id).await { + Ok(coverart) => match coverart.to_data() { + Ok(data) => { + let bytes = axum::body::Bytes::from(data); + let mut response = bytes.into_response(); + let headers = response.headers_mut(); + // TODO: Address hard coding + headers.insert( + axum::http::header::CONTENT_TYPE, + "audio/jpg".parse().unwrap(), + ); + headers.insert( + axum::http::header::CONTENT_DISPOSITION, + format!("attachment; filename=\"{id}.jpg\"") + .parse() + .unwrap(), + ); + + (axum::http::StatusCode::OK, response) + } + Err(_err) => ( + axum::http::StatusCode::NOT_FOUND, + axum::response::Response::default(), + ), + }, + Err(_err) => ( + axum::http::StatusCode::NOT_FOUND, + axum::response::Response::default(), + ), + } + } } diff --git a/src/callers/mod.rs b/src/callers/mod.rs index e0bfeda..3c6b223 100644 --- a/src/callers/mod.rs +++ b/src/callers/mod.rs @@ -21,6 +21,7 @@ pub mod endpoints { pub const DOWNLOADSONG: &str = "/api/v2/song/download/{id}"; pub const CREATECOVERART: &str = "/api/v2/coverart"; pub const GETCOVERART: &str = "/api/v2/coverart"; + pub const DOWNLOADCOVERART: &str = "/api/v2/coverart/download/{id}"; } pub mod response { diff --git a/src/main.rs b/src/main.rs index 6a4a58d..e1c8186 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,6 +123,10 @@ pub mod init { crate::callers::endpoints::GETCOVERART, get(crate::callers::coverart::endpoint::get_coverart), ) + .route( + crate::callers::endpoints::DOWNLOADCOVERART, + get(crate::callers::coverart::endpoint::download_coverart), + ) .route( crate::callers::endpoints::STREAMSONG, get(crate::callers::song::endpoint::stream_song), @@ -2061,5 +2065,61 @@ mod tests { let _ = super::db_mgr::drop_database(&tm_pool, &db_name).await; } + + #[tokio::test] + async fn test_download_coverart() { + let tm_pool = super::db_mgr::get_pool().await.unwrap(); + let db_name = super::db_mgr::generate_db_name().await; + + match super::db_mgr::create_database(&tm_pool, &db_name).await { + Ok(_) => { + println!("Success"); + } + Err(err) => { + assert!(false, "Error: {:?}", err); + } + } + + let pool = super::db_mgr::connect_to_db(&db_name).await.unwrap(); + super::db_mgr::migrations(&pool).await; + + let app = super::init::app(pool).await; + + let id = test_data::coverart_id().await.unwrap(); + + let uri = + super::format_url_with_value(crate::callers::endpoints::DOWNLOADCOVERART, &id) + .await; + + match app + .clone() + .oneshot( + axum::http::Request::builder() + .method(axum::http::Method::GET) + .uri(&uri) + .body(axum::body::Body::empty()) + .unwrap(), + ) + .await + { + Ok(response) => { + let e = response.into_body(); + let mut data = e.into_data_stream(); + while let Some(chunk) = data.next().await { + match chunk { + Ok(_data) => {} + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + } + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + let _ = super::db_mgr::drop_database(&tm_pool, &db_name).await; + } } }