From a779e13a7710a5ffbadbc485ae8674dc9399d446 Mon Sep 17 00:00:00 2001 From: phoenix Date: Tue, 15 Jul 2025 00:53:12 +0000 Subject: [PATCH] Create song (#33) Reviewed-on: https://git.kundeng.us/phoenix/songparser/pulls/33 Co-authored-by: phoenix Co-committed-by: phoenix --- src/api.rs | 131 +++++++++++++++++++++++++++++ src/main.rs | 209 ++++++++++++----------------------------------- src/responses.rs | 16 ++++ src/the_rest.rs | 43 ++++++++++ 4 files changed, 240 insertions(+), 159 deletions(-) create mode 100644 src/api.rs create mode 100644 src/responses.rs diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..f4d0933 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,131 @@ +pub async fn fetch_next_queue_item(base_url: &String) -> Result { + let client = reqwest::Client::new(); + let fetch_endpoint = String::from("api/v2/song/queue/next"); + let api_url = format!("{base_url}/{fetch_endpoint}"); + client.get(api_url).send().await +} + +pub mod parsing { + use futures::StreamExt; + + pub async fn parse_response_into_bytes( + response: reqwest::Response, + ) -> Result, reqwest::Error> { + // TODO: At some point, handle the flow if the size is small or + // large + let mut byte_stream = response.bytes_stream(); + let mut all_bytes = Vec::new(); + + while let Some(chunk) = byte_stream.next().await { + let chunk = chunk?; + all_bytes.extend_from_slice(&chunk); + } + + Ok(all_bytes) + } +} + +pub mod fetch_song_queue_data { + pub async fn get_data( + base_url: &String, + id: &uuid::Uuid, + ) -> Result { + let client = reqwest::Client::new(); + let endpoint = String::from("api/v2/song/queue"); + let api_url = format!("{base_url}/{endpoint}/{id}"); + client.get(api_url).send().await + } +} + +pub mod get_metadata_queue { + pub async fn get( + base_url: &String, + song_queue_id: &uuid::Uuid, + ) -> Result { + let client = reqwest::Client::new(); + let endpoint = String::from("api/v2/song/metadata/queue"); + let api_url = format!("{base_url}/{endpoint}"); + client + .get(api_url) + .query(&[("song_queue_id", song_queue_id)]) + .send() + .await + } + + pub mod response { + use serde::{Deserialize, Serialize}; + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Metadata { + pub song_queue_id: uuid::Uuid, + pub album: String, + pub album_artist: String, + pub artist: String, + pub disc: i32, + pub disc_count: i32, + pub duration: i64, + pub genre: String, + pub title: String, + pub track: i32, + pub track_count: i32, + pub year: i32, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct QueueItem { + pub id: uuid::Uuid, + pub metadata: Metadata, + #[serde(with = "time::serde::rfc3339")] + pub created_at: time::OffsetDateTime, + pub song_queue_id: uuid::Uuid, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } +} + +pub mod get_coverart_queue { + pub async fn get( + base_url: &String, + song_queue_id: &uuid::Uuid, + ) -> Result { + let client = reqwest::Client::new(); + let endpoint = String::from("api/v2/coverart/queue"); + let api_url = format!("{base_url}/{endpoint}"); + client + .get(api_url) + .query(&[("song_queue_id", song_queue_id)]) + .send() + .await + } + + pub async fn get_data( + base_url: &String, + coverart_queue_id: &uuid::Uuid, + ) -> Result { + let client = reqwest::Client::new(); + let endpoint = String::from("api/v2/coverart/queue/data"); + let api_url = format!("{base_url}/{endpoint}/{coverart_queue_id}"); + client.get(api_url).send().await + } + + pub mod response { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Deserialize, Serialize)] + pub struct CoverArtQueue { + pub id: uuid::Uuid, + pub song_queue_id: uuid::Uuid, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } +} diff --git a/src/main.rs b/src/main.rs index 8532b50..7bb1141 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +pub mod api; +pub mod responses; pub mod the_rest; pub mod update_queued_song; pub mod util; @@ -69,7 +71,7 @@ async fn some_work( song_queue_id: &uuid::Uuid, ) -> Result<(), std::io::Error> { match prep_song(app_base_url, song_queue_id).await { - Ok((song_queue_path, coverart_queue_path, metadata)) => { + Ok((song_queue_path, coverart_queue_path, metadata, coverart_queue_id)) => { match apply_metadata(&song_queue_path, &coverart_queue_path, &metadata).await { Ok(_applied) => { match update_queued_song::update_queued_song( @@ -86,7 +88,43 @@ async fn some_work( { Ok(_inner_response) => { println!("Response: {_inner_response:?}"); - Ok(()) + + // TODO: Do not hard code this. Check if one of the existing + // endpoints already have the user_id + let user_id = uuid::Uuid::new_v4(); + // TODO: Place this somewhere else + let song_type = String::from("flac"); + // Err(std::io::Error::other(err.to_string())) + match the_rest::create_song::create( + app_base_url, + &metadata, + &user_id, + &song_type, + ) + .await + { + Ok(response) => match response + .json::() + .await + { + Ok(resp) => { + println!("Response: {resp:?}"); + + let song = &resp.data[0]; + let url = format!("{app_base_url}/api/v2/coverart"); + let payload = serde_json::json!({ + "song_id": &song.id, + "coverart_queue_id": &coverart_queue_id, + }); + println!("Payload: {payload:?}"); + println!("Url: {url:?}"); + // println!("Response json: {:?}", response.text().await); + Ok(()) + } + Err(err) => Err(std::io::Error::other(err.to_string())), + }, + Err(err) => Err(std::io::Error::other(err.to_string())), + } } Err(err) => Err(std::io::Error::other(err.to_string())), } @@ -104,7 +142,15 @@ async fn some_work( async fn prep_song( api_url: &String, song_queue_id: &uuid::Uuid, -) -> Result<(String, String, api::get_metadata_queue::response::Metadata), reqwest::Error> { +) -> Result< + ( + String, + String, + api::get_metadata_queue::response::Metadata, + uuid::Uuid, + ), + reqwest::Error, +> { match api::fetch_song_queue_data::get_data(api_url, song_queue_id).await { Ok(response) => { // Process data here... @@ -148,7 +194,7 @@ async fn prep_song( let c_path = util::path_buf_to_string(&coverart_queue_path); let s_path = util::path_buf_to_string(&song_queue_path); - Ok((s_path, c_path, metadata.clone())) + Ok((s_path, c_path, metadata.clone(), *coverart_queue_id)) } Err(err) => { Err(err) @@ -375,158 +421,3 @@ pub async fn apply_metadata( Err(err) => Err(err), } } - -mod responses { - pub mod fetch_next_queue_item { - use serde::{Deserialize, Serialize}; - - #[derive(Debug, Deserialize, Serialize)] - pub struct QueueItem { - pub id: uuid::Uuid, - pub filename: String, - pub status: String, - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct SongQueueItem { - pub message: String, - pub data: Vec, - } - } -} - -mod api { - pub async fn fetch_next_queue_item( - base_url: &String, - ) -> Result { - let client = reqwest::Client::new(); - let fetch_endpoint = String::from("api/v2/song/queue/next"); - let api_url = format!("{base_url}/{fetch_endpoint}"); - client.get(api_url).send().await - } - - pub mod parsing { - use futures::StreamExt; - - pub async fn parse_response_into_bytes( - response: reqwest::Response, - ) -> Result, reqwest::Error> { - // TODO: At some point, handle the flow if the size is small or - // large - let mut byte_stream = response.bytes_stream(); - let mut all_bytes = Vec::new(); - - while let Some(chunk) = byte_stream.next().await { - let chunk = chunk?; - all_bytes.extend_from_slice(&chunk); - } - - Ok(all_bytes) - } - } - - pub mod fetch_song_queue_data { - pub async fn get_data( - base_url: &String, - id: &uuid::Uuid, - ) -> Result { - let client = reqwest::Client::new(); - let endpoint = String::from("api/v2/song/queue"); - let api_url = format!("{base_url}/{endpoint}/{id}"); - client.get(api_url).send().await - } - } - - pub mod get_metadata_queue { - pub async fn get( - base_url: &String, - song_queue_id: &uuid::Uuid, - ) -> Result { - let client = reqwest::Client::new(); - let endpoint = String::from("api/v2/song/metadata/queue"); - let api_url = format!("{base_url}/{endpoint}"); - client - .get(api_url) - .query(&[("song_queue_id", song_queue_id)]) - .send() - .await - } - - pub mod response { - use serde::{Deserialize, Serialize}; - - #[derive(Clone, Debug, Deserialize, Serialize)] - pub struct Metadata { - pub song_queue_id: uuid::Uuid, - pub album: String, - pub album_artist: String, - pub artist: String, - pub disc: i32, - pub disc_count: i32, - pub duration: i64, - pub genre: String, - pub title: String, - pub track: i32, - pub track_count: i32, - pub year: i32, - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct QueueItem { - pub id: uuid::Uuid, - pub metadata: Metadata, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub song_queue_id: uuid::Uuid, - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } - } - - pub mod get_coverart_queue { - pub async fn get( - base_url: &String, - song_queue_id: &uuid::Uuid, - ) -> Result { - let client = reqwest::Client::new(); - let endpoint = String::from("api/v2/coverart/queue"); - let api_url = format!("{base_url}/{endpoint}"); - client - .get(api_url) - .query(&[("song_queue_id", song_queue_id)]) - .send() - .await - } - - pub async fn get_data( - base_url: &String, - coverart_queue_id: &uuid::Uuid, - ) -> Result { - let client = reqwest::Client::new(); - let endpoint = String::from("api/v2/coverart/queue/data"); - let api_url = format!("{base_url}/{endpoint}/{coverart_queue_id}"); - client.get(api_url).send().await - } - - pub mod response { - use serde::{Deserialize, Serialize}; - - #[derive(Debug, Deserialize, Serialize)] - pub struct CoverArtQueue { - pub id: uuid::Uuid, - pub song_queue_id: uuid::Uuid, - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } - } -} diff --git a/src/responses.rs b/src/responses.rs new file mode 100644 index 0000000..c5a090e --- /dev/null +++ b/src/responses.rs @@ -0,0 +1,16 @@ +pub mod fetch_next_queue_item { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Deserialize, Serialize)] + pub struct QueueItem { + pub id: uuid::Uuid, + pub filename: String, + pub status: String, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct SongQueueItem { + pub message: String, + pub data: Vec, + } +} diff --git a/src/the_rest.rs b/src/the_rest.rs index ae021fd..8afffe7 100644 --- a/src/the_rest.rs +++ b/src/the_rest.rs @@ -1,6 +1,49 @@ // TODO: Refactor this file when this app is functional // TODO: Create song +pub mod create_song { + pub async fn create( + base_url: &String, + metadata_queue: &crate::api::get_metadata_queue::response::Metadata, + user_id: &uuid::Uuid, + song_type: &String, + ) -> Result { + let payload = serde_json::json!( + { + "album": &metadata_queue.album, + "album_artist": &metadata_queue.album_artist, + "artist": &metadata_queue.artist, + "disc": metadata_queue.disc, + "disc_count": metadata_queue.disc_count, + "duration": metadata_queue.duration, + "genre": &metadata_queue.genre, + "title": &metadata_queue.title, + "track": metadata_queue.track, + "track_count": metadata_queue.track_count, + "date": metadata_queue.year.to_string(), + "audio_type": &song_type, + "user_id": &user_id, + "song_queue_id": &metadata_queue.song_queue_id, + } + ); + + let client = reqwest::Client::builder().build()?; + + let url = format!("{base_url}/api/v2/song"); + + let request = client.post(url).json(&payload); + request.send().await + } + + pub mod response { + #[derive(Debug, serde::Deserialize, serde::Serialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } +} + // TODO: Create coverart // TODO: Wipe data from queued song // TODO: Wipe data from queued coverart