From f705c7fbb67c1564ea36c1e0afe9841611e9a43f Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 28 Jul 2025 15:02:32 -0400 Subject: [PATCH 1/6] Added initial code for downloading song endpoint --- src/callers/song.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/callers/song.rs b/src/callers/song.rs index ab175c3..f0ab49a 100644 --- a/src/callers/song.rs +++ b/src/callers/song.rs @@ -774,6 +774,11 @@ pub mod endpoint { } } + pub async fn download_song() + -> (axum::http::StatusCode, axum::response::Response) { + (axum::http::StatusCode::OK, axum::response::Response::default()) + } + pub async fn update_song_queue_status( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, -- 2.47.3 From 76531cbba7cc02b761b6e14bca70455a63c8a866 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 28 Jul 2025 16:20:45 -0400 Subject: [PATCH 2/6] Finished download song endpoint --- src/callers/song.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/callers/song.rs b/src/callers/song.rs index f0ab49a..2fd7526 100644 --- a/src/callers/song.rs +++ b/src/callers/song.rs @@ -774,11 +774,6 @@ pub mod endpoint { } } - pub async fn download_song() - -> (axum::http::StatusCode, axum::response::Response) { - (axum::http::StatusCode::OK, axum::response::Response::default()) - } - pub async fn update_song_queue_status( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, @@ -1064,4 +1059,29 @@ pub mod endpoint { )), } } + + pub async fn download_song(axum::Extension(pool): axum::Extension, + axum::extract::Path(id): axum::extract::Path) + -> (axum::http::StatusCode, axum::response::Response) { + match super::song_db::get_song(&pool, &id).await { + Ok(song) => match song.to_data() { + Ok(data) => { + let bytes = axum::body::Bytes::from(data); + let mut response = bytes.into_response(); + let headers = response.headers_mut(); + headers.insert(axum::http::header::CONTENT_TYPE, "audio/flac".parse().unwrap()); + headers.insert(axum::http::header::CONTENT_DISPOSITION, format!("attachment; filename=\"{id}.flac\"").parse().unwrap()); + + (axum::http::StatusCode::OK, response) + } + Err(_err) => { + (axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::response::Response::default()) + } + } + Err(_err) => { + (axum::http::StatusCode::NOT_FOUND, axum::response::Response::default()) + } + } + } + } -- 2.47.3 From a949c26933a2ecccc6358a6c7c0029daf9cb9a66 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 28 Jul 2025 16:22:26 -0400 Subject: [PATCH 3/6] Making download endpoint available --- src/callers/mod.rs | 1 + src/main.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/callers/mod.rs b/src/callers/mod.rs index b04f7a2..e0bfeda 100644 --- a/src/callers/mod.rs +++ b/src/callers/mod.rs @@ -18,6 +18,7 @@ pub mod endpoints { pub const CREATESONG: &str = "/api/v2/song"; pub const GETSONGS: &str = "/api/v2/song"; pub const STREAMSONG: &str = "/api/v2/song/stream/{id}"; + pub const DOWNLOADSONG: &str = "/api/v2/song/download/{id}"; pub const CREATECOVERART: &str = "/api/v2/coverart"; pub const GETCOVERART: &str = "/api/v2/coverart"; } diff --git a/src/main.rs b/src/main.rs index d1d99f7..55c0ac4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -127,6 +127,7 @@ pub mod init { crate::callers::endpoints::STREAMSONG, get(crate::callers::song::endpoint::stream_song), ) + .route(crate::callers::endpoints::DOWNLOADSONG, get(crate::callers::song::endpoint::download_song)) } pub async fn app() -> axum::Router { -- 2.47.3 From e11524020c63f69180c37f43b1b99976bff58389 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 28 Jul 2025 16:31:03 -0400 Subject: [PATCH 4/6] Added test --- src/main.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/main.rs b/src/main.rs index 55c0ac4..7c5c9b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -655,6 +655,11 @@ mod tests { serde_json::from_slice(&body).unwrap() } + pub async fn format_url_with_value(endpoint: &str, value: &uuid::Uuid) -> String { + let last = endpoint.len() - 5; + format!("{}/{value}", &endpoint[0..last]) + } + // TODO: Change the name of the function to be more expressive and put into it's own module pub mod payload_data { pub async fn queue_metadata_payload_data(song_queue_id: &uuid::Uuid) -> serde_json::Value { @@ -1998,5 +2003,59 @@ mod tests { let _ = super::db_mgr::drop_database(&tm_pool, &db_name).await; } + + #[tokio::test] + async fn test_download_song() { + 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::song_id().await.unwrap(); + + let uri = super::format_url_with_value(crate::callers::endpoints::DOWNLOADSONG, &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; + } } } -- 2.47.3 From 0a4d1807e119cac5f79d4072aafe32ff2130304c Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 28 Jul 2025 16:34:25 -0400 Subject: [PATCH 5/6] Code formatting --- src/callers/song.rs | 52 +++++++++++++++++++++++++++------------------ src/main.rs | 12 +++++++---- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/callers/song.rs b/src/callers/song.rs index 2fd7526..6d8d6e9 100644 --- a/src/callers/song.rs +++ b/src/callers/song.rs @@ -1060,28 +1060,38 @@ pub mod endpoint { } } - pub async fn download_song(axum::Extension(pool): axum::Extension, - axum::extract::Path(id): axum::extract::Path) - -> (axum::http::StatusCode, axum::response::Response) { - match super::song_db::get_song(&pool, &id).await { - Ok(song) => match song.to_data() { - Ok(data) => { - let bytes = axum::body::Bytes::from(data); - let mut response = bytes.into_response(); - let headers = response.headers_mut(); - headers.insert(axum::http::header::CONTENT_TYPE, "audio/flac".parse().unwrap()); - headers.insert(axum::http::header::CONTENT_DISPOSITION, format!("attachment; filename=\"{id}.flac\"").parse().unwrap()); + pub async fn download_song( + axum::Extension(pool): axum::Extension, + axum::extract::Path(id): axum::extract::Path, + ) -> (axum::http::StatusCode, axum::response::Response) { + match super::song_db::get_song(&pool, &id).await { + Ok(song) => match song.to_data() { + Ok(data) => { + let bytes = axum::body::Bytes::from(data); + let mut response = bytes.into_response(); + let headers = response.headers_mut(); + headers.insert( + axum::http::header::CONTENT_TYPE, + "audio/flac".parse().unwrap(), + ); + headers.insert( + axum::http::header::CONTENT_DISPOSITION, + format!("attachment; filename=\"{id}.flac\"") + .parse() + .unwrap(), + ); - (axum::http::StatusCode::OK, response) - } - Err(_err) => { - (axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::response::Response::default()) - } + (axum::http::StatusCode::OK, response) } - Err(_err) => { - (axum::http::StatusCode::NOT_FOUND, axum::response::Response::default()) - } - } + Err(_err) => ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::response::Response::default(), + ), + }, + Err(_err) => ( + axum::http::StatusCode::NOT_FOUND, + axum::response::Response::default(), + ), + } } - } diff --git a/src/main.rs b/src/main.rs index 7c5c9b1..6a4a58d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -127,7 +127,10 @@ pub mod init { crate::callers::endpoints::STREAMSONG, get(crate::callers::song::endpoint::stream_song), ) - .route(crate::callers::endpoints::DOWNLOADSONG, get(crate::callers::song::endpoint::download_song)) + .route( + crate::callers::endpoints::DOWNLOADSONG, + get(crate::callers::song::endpoint::download_song), + ) } pub async fn app() -> axum::Router { @@ -656,8 +659,8 @@ mod tests { } pub async fn format_url_with_value(endpoint: &str, value: &uuid::Uuid) -> String { - let last = endpoint.len() - 5; - format!("{}/{value}", &endpoint[0..last]) + let last = endpoint.len() - 5; + format!("{}/{value}", &endpoint[0..last]) } // TODO: Change the name of the function to be more expressive and put into it's own module @@ -2025,7 +2028,8 @@ mod tests { let id = test_data::song_id().await.unwrap(); - let uri = super::format_url_with_value(crate::callers::endpoints::DOWNLOADSONG, &id).await; + let uri = + super::format_url_with_value(crate::callers::endpoints::DOWNLOADSONG, &id).await; match app .clone() -- 2.47.3 From 441c122a014ab9675d7ec4d006e52a335fa70297 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 28 Jul 2025 16:36:30 -0400 Subject: [PATCH 6/6] Version bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eeb8c42..1f02946 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "icarus" -version = "0.1.96" +version = "0.1.97" dependencies = [ "axum", "common-multipart-rfc7578", diff --git a/Cargo.toml b/Cargo.toml index 456f330..2773535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icarus" -version = "0.1.96" +version = "0.1.97" edition = "2024" rust-version = "1.88" -- 2.47.3