From 006fa3a2d833a9a0e3c5fe538760666783c667d4 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 12 Jun 2025 18:57:02 +0000 Subject: [PATCH] fetch song queue data (#21) Reviewed-on: https://git.kundeng.us/phoenix/songparser/pulls/21 Co-authored-by: kdeng00 Co-committed-by: kdeng00 --- Cargo.lock | 76 +++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +- src/main.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 178 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8be8125..2a0118a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -194,6 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -202,6 +218,34 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -220,10 +264,16 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -398,6 +448,14 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "icarus_envy" +version = "0.2.0" +source = "git+ssh://git@git.kundeng.us/phoenix/icarus_envy.git?tag=v0.2.0#3ee8c1e573ba9637769aa0538d2c11335e39ed9f" +dependencies = [ + "dotenvy", +] + [[package]] name = "icarus_models" version = "0.4.3" @@ -851,6 +909,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-core", + "futures-util", "h2", "http", "http-body", @@ -874,12 +933,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -1088,6 +1149,8 @@ name = "songparser" version = "0.1.0" dependencies = [ "dotenvy", + "futures", + "icarus_envy", "icarus_models", "reqwest", "serde", @@ -1488,6 +1551,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.77" diff --git a/Cargo.toml b/Cargo.toml index 6999867..ed00706 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,11 @@ rust-version = "1.86" [dependencies] tokio = { version = "1.44.1", features = ["full"] } -reqwest = { version = "0.12.19", features = ["json"] } +futures = { version = "0.3.31" } +reqwest = { version = "0.12.19", features = ["json", "stream"] } serde = { version = "1.0.218", features = ["derive"] } serde_json = { version = "1.0.139" } uuid = { version = "1.16.0", features = ["v4", "serde"] } dotenvy = { version = "0.15.7" } icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.4.3" } +icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.2.0" } diff --git a/src/main.rs b/src/main.rs index 8a0e0ab..efbb604 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +use std::io::Write; + +pub const SECONDS_TO_SLEEP: u64 = 5; + #[tokio::main] async fn main() -> Result<(), Box> { let app_base_url = get_icarus_url().await; @@ -15,19 +19,42 @@ async fn main() -> Result<(), Box> { if !song_queue_item.data.is_empty() { println!("Song queue item: {:?}", song_queue_item); - // Process data here... + println!("Fetching song queue data"); + match api::fetch_song_queue_data::get_data( + &app_base_url, + &song_queue_item.data[0].id, + ) + .await + { + Ok(response) => { + // Process data here... + let all_bytes = + api::fetch_song_queue_data::response::parse_response( + response, + ) + .await?; - // TODO: Parse the response body to a struct - // TODO: Get queued song data - // TODO: Get queued song's metadata - // TODO: Get queued coverart - // TODO: Get queued coverart's data - // TODO: Apply metadata to the queued song - // TODO: Update the queued song with the updated queued song - // TODO: Create song - // TODO: Create coverart - // TODO: Wipe data from queued song - // TODO: Wipe data from queued coverart + let (directory, filename) = + generate_song_queue_dir_and_filename().await; + let save_path = + save_song_to_fs(&directory, &filename, &all_bytes).await; + + println!("Saved at: {:?}", save_path); + + // TODO: Get queued song's metadata + // TODO: Get queued coverart + // TODO: Get queued coverart's data + // TODO: Apply metadata to the queued song + // TODO: Update the queued song with the updated queued song + // TODO: Create song + // TODO: Create coverart + // TODO: Wipe data from queued song + // TODO: Wipe data from queued coverart + } + Err(err) => { + eprintln!("Error fetching song queue data: {:?}", err); + } + } } else { println!("No data to fetch"); } @@ -41,10 +68,37 @@ async fn main() -> Result<(), Box> { } println!("Sleeping"); - tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(SECONDS_TO_SLEEP)).await; } } +// TODO: Consider having something like this in icarus_models +pub async fn generate_song_queue_dir_and_filename() -> (String, String) { + let mut song = icarus_models::song::Song::default(); + song.filename = song.generate_filename(icarus_models::types::MusicTypes::FlacExtension, true); + + song.directory = icarus_envy::environment::get_root_directory().await; + + (song.directory, song.filename) +} + +// TODO: Check to see if this is available in icarus_models +pub async fn save_song_to_fs( + directory: &String, + filename: &String, + data: &[u8], +) -> std::path::PathBuf { + // TODO: Add function to save bytes to a file in icarus_models + // repo + let dir = std::path::Path::new(directory); + let save_path = dir.join(filename); + + let mut file = std::fs::File::create(&save_path).unwrap(); + file.write_all(data).unwrap(); + + save_path +} + mod responses { pub mod fetch_next_queue_item { use serde::{Deserialize, Serialize}; @@ -73,6 +127,38 @@ mod api { let api_url = format!("{}/{}", base_url, fetch_endpoint); client.get(api_url).send().await } + + 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 response { + use futures::StreamExt; + + pub async fn parse_response( + 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) + } + } + } } async fn get_icarus_url() -> String {