From 304a588e115af2a2fb2fc113db64a03fddd6e54e Mon Sep 17 00:00:00 2001 From: KD Date: Wed, 27 Aug 2025 18:48:58 -0400 Subject: [PATCH 1/7] Queue song (#57) * Added code to queue song * Making functions async * Able to queue song * Cleanup --- src/main.rs | 5 +- src/managers/commit_manager.rs | 87 ++++++++++++++++++++-------------- src/syncers/upload.rs | 64 +++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 37 deletions(-) diff --git a/src/main.rs b/src/main.rs index ae68c95..98eb30d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,8 @@ mod utilities; use std::env; -fn main() { +#[tokio::main] +async fn main() { let args: Vec = env::args().collect(); let args_len = args.len() as i32; @@ -29,7 +30,7 @@ fn main() { ica_action: chosen_act, }; - cmt_mgr.commit_action(); + cmt_mgr.commit_action().await; } #[cfg(test)] diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index 3eb3709..1971d41 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -79,7 +79,7 @@ pub fn retrieve_song( } impl CommitManager { - pub fn commit_action(&mut self) { + pub async fn commit_action(&mut self) { let action = &self.ica_action.action; println!("Committing {action} action"); @@ -91,11 +91,11 @@ impl CommitManager { // TODO: Move code to get token here and then pass it to the respective functions match mapped_action { - ActionValues::DeleteAct => self.delete_song(), - ActionValues::DownloadAct => self.download_song(), - ActionValues::RetrieveAct => self.retrieve_object(), + ActionValues::DeleteAct => self.delete_song().await, + ActionValues::DownloadAct => self.download_song().await, + ActionValues::RetrieveAct => self.retrieve_object().await, ActionValues::UploadAct => self.upload_song(), - ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata(), + ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata().await, _ => { println!("Nothing good here"); } @@ -132,7 +132,7 @@ impl CommitManager { actions } - fn delete_song(&self) { + async fn delete_song(&self) { let mut prsr = parsers::api_parser::APIParser { apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), @@ -141,7 +141,7 @@ impl CommitManager { prsr.parse_api(parsers::api_parser::APIType::Auth); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); - let token = self.parse_token(&auth_api); + let token = self.parse_token(&auth_api).await; println!("Deleting song"); @@ -173,7 +173,7 @@ impl CommitManager { } } - fn download_song(&self) { + async fn download_song(&self) { println!("Deleting song"); let dwn = self.ica_action.retrieve_flag_value(&String::from("-b")); let song_id = uuid::Uuid::from_str(dwn.as_str()).unwrap(); @@ -186,7 +186,7 @@ impl CommitManager { prsr.parse_api(parsers::api_parser::APIType::Auth); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); - let token = self.parse_token(&auth_api); + let token = self.parse_token(&auth_api).await; println!("Message: {}", token.message); let api = prsr.retrieve_api(parsers::api_parser::APIType::Main); @@ -220,7 +220,7 @@ impl CommitManager { } } - fn retrieve_object(&self) { + async fn retrieve_object(&self) { println!("Retrieving song"); let rt = self.ica_action.retrieve_flag_value(&String::from("-rt")); @@ -236,7 +236,7 @@ impl CommitManager { prsr.parse_api(parsers::api_parser::APIType::Auth); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); - let token = self.parse_token(&auth_api); + let token = self.parse_token(&auth_api).await; println!("Token {token:?}"); let api = prsr.retrieve_api(parsers::api_parser::APIType::Main); @@ -265,7 +265,7 @@ impl CommitManager { panic!("Not supported"); } - fn parse_token(&self, api: &models::api::Api) -> icarus_models::token::AccessToken { + async fn parse_token(&self, api: &models::api::Api) -> icarus_models::token::AccessToken { println!("Fetching token"); let mut usr_mgr: managers::user_manager::UserManager = @@ -282,12 +282,12 @@ impl CommitManager { }; tok_mgr.init(); - let token = Runtime::new().unwrap().block_on(tok_mgr.request_token()); + let token = tok_mgr.request_token().await; token.unwrap() } - fn upload_song_with_metadata(&mut self) { + async fn upload_song_with_metadata(&mut self) { println!("Uplodaring song with metadara"); let songpath = self.ica_action.retrieve_flag_value(&String::from("-s")); @@ -314,15 +314,17 @@ impl CommitManager { println!("metadata path: {metadata_path}"); println!("cover art path: {coverpath}"); - let _ = self.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath); + let _ = self + .sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath) + .await; } else if multitarget { - let _ = self.multi_target_upload(&uni); + let _ = self.multi_target_upload(&uni).await; } else { println!("Single or Multi target has not been chosen"); } } - fn sing_target_upload( + async fn sing_target_upload( &mut self, songpath: &String, track_id: &str, @@ -337,7 +339,7 @@ impl CommitManager { prsr.parse_api(parsers::api_parser::APIType::Auth); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); - let token = self.parse_token(&auth_api); + let token = self.parse_token(&auth_api).await; println!("Token: {:?}", token.token); @@ -376,21 +378,9 @@ impl CommitManager { cover_art.data = cover_art.to_data().unwrap(); - let mut up = syncers::upload::Upload::default(); - let host = self.ica_action.retrieve_flag_value(&String::from("-h")); - up.set_api(&host); - - let res = up.upload_song_with_metadata(&token, &s, &cover_art); - - match Runtime::new().unwrap().block_on(res) { - Ok(o) => { - println!("Successfully sent {o:?}"); - Ok(()) - } - Err(er) => { - println!("Some error {er:?}"); - Err(std::io::Error::other(er.to_string())) - } + match self.queue_song(&token, &s).await { + Ok(_) => Ok(()), + Err(err) => Err(err), } } Err(err) => { @@ -408,6 +398,33 @@ impl CommitManager { } } + async fn queue_song( + &self, + token: &icarus_models::token::AccessToken, + song: &icarus_models::song::Song, + ) -> Result<()> { + let mut up = syncers::upload::Upload::default(); + let host = self.ica_action.retrieve_flag_value(&String::from("-h")); + up.set_api(&host); + + let mut queued_song_id = uuid::Uuid::nil(); + + println!("Queueing song"); + + match up.queue_song(token, song).await { + Ok(id) => { + queued_song_id = id; + } + Err(err) => { + eprintln!("Error: {err:?}"); + } + } + + println!("Queued song Id: {queued_song_id:?}"); + + Ok(()) + } + fn get_songs( &self, metadata_path: &String, @@ -455,7 +472,7 @@ impl CommitManager { } } - fn multi_target_upload(&mut self, sourcepath: &String) -> std::io::Result<()> { + async fn multi_target_upload(&mut self, sourcepath: &String) -> std::io::Result<()> { let mut prsr = parsers::api_parser::APIParser { apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), @@ -463,7 +480,7 @@ impl CommitManager { prsr.parse_api(parsers::api_parser::APIType::Main); prsr.parse_api(parsers::api_parser::APIType::Auth); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); - let token = self.parse_token(&auth_api); + let token = self.parse_token(&auth_api).await; let directory_path = std::path::Path::new(&sourcepath); diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index 473dd75..eedc762 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -11,6 +11,16 @@ pub struct Upload { pub api: models::api::Api, } +mod response { + pub mod queue_song { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } +} + impl Upload { pub async fn upload_song_with_metadata( &mut self, @@ -51,6 +61,60 @@ impl Upload { } } + pub async fn queue_song( + &self, + token: &icarus_models::token::AccessToken, + song: &icarus_models::song::Song, + ) -> Result { + let songpath = song.song_path().unwrap(); + let file = tokio::fs::File::open(&songpath).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let file_body = reqwest::Body::wrap_stream(stream); + + let form = reqwest::multipart::Form::new().part( + "file", + reqwest::multipart::Part::stream(file_body) + .file_name(song.filename.clone()) + .mime_str("application/octet-stream") + .unwrap(), + ); + + let endpoint = String::from("api/v2/song/queue"); + let url = format!("{}/{endpoint}", self.api.url); + println!("Url: {url:?}"); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert( + reqwest::header::AUTHORIZATION, + HeaderValue::from_str(&token.bearer_token()).unwrap(), + ); + headers.insert("Accept", HeaderValue::from_str("*/*").unwrap()); + headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap()); + headers.insert("Cache-Control", HeaderValue::from_str("no-cache").unwrap()); + + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(300)) + .pool_idle_timeout(std::time::Duration::from_secs(30)) + .build() + .unwrap(); + match client + .post(url) + .headers(headers) + .multipart(form) + .send() + .await + { + Ok(response) => match response.json::().await { + Ok(resp) => { + println!("Message: {:?}", resp.message); + Ok(resp.data[0]) + } + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } + fn init_form( &self, song: &icarus_models::song::Song, -- 2.47.3 From a36914cc0da91311a1c4562e6ff0f9d3a09c8c07 Mon Sep 17 00:00:00 2001 From: KD Date: Thu, 28 Aug 2025 13:05:39 -0400 Subject: [PATCH 2/7] Link user to queued song (#59) * Renaming queue_song function * Queued song linked to user * Cleanup and code formatting --- src/managers/commit_manager.rs | 43 ++++++++++++++++++++++++---------- src/syncers/common.rs | 9 +++++++ src/syncers/upload.rs | 37 +++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index 1971d41..2cfd3f2 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -36,6 +36,13 @@ enum En { Other, } +#[derive(Debug)] +struct UploadSongMembers { + pub song: icarus_models::song::Song, + pub coverart: icarus_models::coverart::CoverArt, + pub token: icarus_models::token::AccessToken, +} + pub fn retrieve_song( album: &icarus_models::album::collection::Album, track: i32, @@ -378,7 +385,13 @@ impl CommitManager { cover_art.data = cover_art.to_data().unwrap(); - match self.queue_song(&token, &s).await { + let members = UploadSongMembers { + song: s, + coverart: cover_art, + token: token, + }; + + match self.upload_song_process(&members).await { Ok(_) => Ok(()), Err(err) => Err(err), } @@ -398,30 +411,34 @@ impl CommitManager { } } - async fn queue_song( - &self, - token: &icarus_models::token::AccessToken, - song: &icarus_models::song::Song, - ) -> Result<()> { + async fn upload_song_process(&self, data: &UploadSongMembers) -> Result<()> { let mut up = syncers::upload::Upload::default(); let host = self.ica_action.retrieve_flag_value(&String::from("-h")); up.set_api(&host); - let mut queued_song_id = uuid::Uuid::nil(); + let token = &data.token; + let song = &data.song; println!("Queueing song"); - match up.queue_song(token, song).await { - Ok(id) => { - queued_song_id = id; - } + let queued_song_id = match up.queue_song(token, song).await { + Ok(id) => id, Err(err) => { - eprintln!("Error: {err:?}"); + return Err(std::io::Error::other(err.to_string())); } - } + }; println!("Queued song Id: {queued_song_id:?}"); + match up.link_user_to_queued_song(token, &queued_song_id).await { + Ok(_) => { + println!("Queued song linked to user"); + } + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + } + Ok(()) } diff --git a/src/syncers/common.rs b/src/syncers/common.rs index a2c345a..5d7eeb6 100644 --- a/src/syncers/common.rs +++ b/src/syncers/common.rs @@ -19,3 +19,12 @@ fn retrieve_url_with_id(api: &models::api::Api, id: &uuid::Uuid) -> String { url } + +pub async fn auth_header( + token: &icarus_models::token::AccessToken, +) -> (http::HeaderName, http::HeaderValue) { + let auth = reqwest::header::AUTHORIZATION; + let auth_value = http::HeaderValue::from_str(&token.bearer_token()).unwrap(); + + (auth, auth_value) +} diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index eedc762..ac2f20f 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -84,10 +84,8 @@ impl Upload { println!("Url: {url:?}"); let mut headers = reqwest::header::HeaderMap::new(); - headers.insert( - reqwest::header::AUTHORIZATION, - HeaderValue::from_str(&token.bearer_token()).unwrap(), - ); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); headers.insert("Accept", HeaderValue::from_str("*/*").unwrap()); headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap()); headers.insert("Cache-Control", HeaderValue::from_str("no-cache").unwrap()); @@ -115,6 +113,37 @@ impl Upload { } } + pub async fn link_user_to_queued_song( + &self, + token: &icarus_models::token::AccessToken, + queued_song_id: &uuid::Uuid, + ) -> Result<(), reqwest::Error> { + let endpoint = String::from("api/v2/song/queue/link"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let client = reqwest::Client::builder().build().unwrap(); + + let payload = serde_json::json!({ + "song_queue_id": queued_song_id, + "user_id": token.user_id + }); + + match client + .patch(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + fn init_form( &self, song: &icarus_models::song::Song, -- 2.47.3 From acf27cd98270326224e880f7af430b045df66c01 Mon Sep 17 00:00:00 2001 From: KD Date: Thu, 28 Aug 2025 15:57:52 -0400 Subject: [PATCH 3/7] Queue metadata (#60) * Added code to queue metadata * Code formatting --- src/managers/commit_manager.rs | 13 +++++++ src/syncers/upload.rs | 62 ++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index 2cfd3f2..a3281d4 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -41,6 +41,7 @@ struct UploadSongMembers { pub song: icarus_models::song::Song, pub coverart: icarus_models::coverart::CoverArt, pub token: icarus_models::token::AccessToken, + pub album: icarus_models::album::collection::Album, } pub fn retrieve_song( @@ -389,6 +390,7 @@ impl CommitManager { song: s, coverart: cover_art, token: token, + album: album, }; match self.upload_song_process(&members).await { @@ -418,6 +420,7 @@ impl CommitManager { let token = &data.token; let song = &data.song; + let album = &data.album; println!("Queueing song"); @@ -439,6 +442,16 @@ impl CommitManager { } } + let queued_metadata_id = match up.queue_metadata(token, album, song, &queued_song_id).await + { + Ok(id) => id, + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }; + + println!("Queued metadata Id: {queued_metadata_id:?}"); + Ok(()) } diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index ac2f20f..f85568f 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -19,6 +19,14 @@ mod response { pub data: Vec, } } + + pub mod queue_metadata { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } } impl Upload { @@ -125,13 +133,13 @@ impl Upload { let (auth, auth_val) = syncers::common::auth_header(token).await; headers.insert(auth, auth_val); - let client = reqwest::Client::builder().build().unwrap(); - let payload = serde_json::json!({ "song_queue_id": queued_song_id, "user_id": token.user_id }); + let client = reqwest::Client::builder().build().unwrap(); + match client .patch(url) .headers(headers) @@ -144,6 +152,56 @@ impl Upload { } } + pub async fn queue_metadata( + &self, + token: &icarus_models::token::AccessToken, + album: &icarus_models::album::collection::Album, + song: &icarus_models::song::Song, + queued_song_id: &uuid::Uuid, + ) -> Result { + let endpoint = String::from("api/v2/song/metadata/queue"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let payload = serde_json::json!({ + "song_queue_id": queued_song_id, + "title": song.title, + "artist": song.artist, + "album_artist": album.artist, + "album": album.title, + "genre": song.genre, + "track": song.track, + "track_count": album.track_count, + "disc": song.disc, + "disc_count": album.disc_count, + "year": album.year, + "duration": song.duration, + }); + + let client = reqwest::Client::builder().build().unwrap(); + + match client + .post(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(response) => match response.json::().await { + Ok(resp) => { + println!("Message: {:?}", resp.message); + + Ok(resp.data[0]) + } + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } + fn init_form( &self, song: &icarus_models::song::Song, -- 2.47.3 From e3a1dfdf86dc5315ff8c595044057c074abbfc02 Mon Sep 17 00:00:00 2001 From: KD Date: Thu, 28 Aug 2025 16:30:38 -0400 Subject: [PATCH 4/7] Queue coverart (#61) * Added queued coverart code * Code formatting and cleanup --- src/managers/commit_manager.rs | 10 +++++ src/syncers/upload.rs | 68 +++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index a3281d4..00cd7d4 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -421,6 +421,7 @@ impl CommitManager { let token = &data.token; let song = &data.song; let album = &data.album; + let coverart = &data.coverart; println!("Queueing song"); @@ -452,6 +453,15 @@ impl CommitManager { println!("Queued metadata Id: {queued_metadata_id:?}"); + match up.queue_coverart(token, coverart).await { + Ok(id) => { + println!("Queued coverart Id: {id:?}"); + } + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + } + Ok(()) } diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index f85568f..f51ae60 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -27,6 +27,14 @@ mod response { pub data: Vec, } } + + pub mod queue_coverart { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } } impl Upload { @@ -202,6 +210,64 @@ impl Upload { } } + pub async fn queue_coverart( + &self, + token: &icarus_models::token::AccessToken, + coverart: &icarus_models::coverart::CoverArt, + ) -> Result { + let coverartpath = coverart.path.clone(); + let file = tokio::fs::File::open(&coverartpath).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let file_body = reqwest::Body::wrap_stream(stream); + + let file_name = std::path::Path::new(&coverartpath) + .file_name() + .and_then(|name| name.to_str()) + .map(String::from) + .unwrap_or("file".to_string()); + + let form = reqwest::multipart::Form::new().part( + "file", + reqwest::multipart::Part::stream(file_body) + .file_name(file_name) + .mime_str("application/octet-stream") + .unwrap(), + ); + + let endpoint = String::from("api/v2/coverart/queue"); + let url = format!("{}/{endpoint}", self.api.url); + println!("Url: {url:?}"); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + headers.insert("Accept", HeaderValue::from_str("*/*").unwrap()); + headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap()); + headers.insert("Cache-Control", HeaderValue::from_str("no-cache").unwrap()); + + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(300)) + .pool_idle_timeout(std::time::Duration::from_secs(30)) + .build() + .unwrap(); + match client + .post(url) + .headers(headers) + .multipart(form) + .send() + .await + { + Ok(response) => match response.json::().await { + Ok(resp) => { + println!("Message: {:?}", resp.message); + Ok(resp.data[0]) + } + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } + fn init_form( &self, song: &icarus_models::song::Song, @@ -236,7 +302,7 @@ impl Upload { pub fn set_api(&mut self, host: &str) { let api = models::api::Api { url: host.to_owned(), - version: String::from("v1"), + version: String::from(crate::parsers::api_parser::API_VERSION), endpoint: String::new(), }; self.api = api; -- 2.47.3 From d67e3202789bab91cc8479c294b1240a5221bdaa Mon Sep 17 00:00:00 2001 From: KD Date: Thu, 28 Aug 2025 16:44:55 -0400 Subject: [PATCH 5/7] Link queued coverart to queued song (#62) * Added code to link queued coverart to queued song * Code formatting --- src/managers/commit_manager.rs | 15 ++++++++++++--- src/syncers/upload.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index 00cd7d4..aebb9c6 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -454,9 +454,18 @@ impl CommitManager { println!("Queued metadata Id: {queued_metadata_id:?}"); match up.queue_coverart(token, coverart).await { - Ok(id) => { - println!("Queued coverart Id: {id:?}"); - } + Ok(id) => match up + .link_queued_song_to_queued_coverart(token, &queued_song_id, &id) + .await + { + Ok(_) => { + println!("Queued coverart Id: {id:?}"); + println!("Linked queued song to queued coverart"); + } + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }, Err(err) => { return Err(std::io::Error::other(err.to_string())); } diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index f51ae60..fe2f088 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -268,6 +268,38 @@ impl Upload { } } + pub async fn link_queued_song_to_queued_coverart( + &self, + token: &icarus_models::token::AccessToken, + queued_song_id: &uuid::Uuid, + queued_coverart_id: &uuid::Uuid, + ) -> Result<(), reqwest::Error> { + let endpoint = String::from("api/v2/coverart/queue/link"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let payload = serde_json::json!({ + "song_queue_id": queued_song_id, + "coverart_id": queued_coverart_id + }); + + let client = reqwest::Client::builder().build().unwrap(); + + match client + .patch(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + fn init_form( &self, song: &icarus_models::song::Song, -- 2.47.3 From 07402d1b5e55c8ac7dd84977654b693f8f05ad04 Mon Sep 17 00:00:00 2001 From: KD Date: Thu, 28 Aug 2025 18:41:46 -0400 Subject: [PATCH 6/7] Update queued song status (#63) * Added function call to update queued song status * Added skeleton method * Added more code to the method * Got it working * Code formatting --- src/managers/commit_manager.rs | 17 +++++++++++++---- src/syncers/upload.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index aebb9c6..2740d1b 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -458,10 +458,19 @@ impl CommitManager { .link_queued_song_to_queued_coverart(token, &queued_song_id, &id) .await { - Ok(_) => { - println!("Queued coverart Id: {id:?}"); - println!("Linked queued song to queued coverart"); - } + Ok(_) => match up + .update_queued_song_status(token, &queued_song_id, "ready") + .await + { + Ok(_) => { + println!("Queued coverart Id: {id:?}"); + println!("Linked queued song to queued coverart"); + println!("Queued status updated"); + } + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }, Err(err) => { return Err(std::io::Error::other(err.to_string())); } diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index fe2f088..a0b6ea8 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -300,6 +300,38 @@ impl Upload { } } + pub async fn update_queued_song_status( + &self, + token: &icarus_models::token::AccessToken, + queued_song_id: &uuid::Uuid, + status: &str, + ) -> Result<(), reqwest::Error> { + let endpoint = String::from("api/v2/song/queue"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let payload = serde_json::json!({ + "id": queued_song_id, + "status": status + }); + + let client = reqwest::Client::builder().build().unwrap(); + + match client + .patch(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + fn init_form( &self, song: &icarus_models::song::Song, -- 2.47.3 From 3ad8ac2fbad463a16caaf7e8849f7a5a63d1c3fc Mon Sep 17 00:00:00 2001 From: KD Date: Fri, 29 Aug 2025 21:57:41 -0400 Subject: [PATCH 7/7] Multi upload (#64) * Added comments * Added TODO * Got it working * Updated documentation * Version bump * Code formatting and cleanup --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 4 ++-- src/managers/commit_manager.rs | 44 ++++++++++++++++++++++------------ 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a34de73..0667dfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -479,7 +479,7 @@ dependencies = [ [[package]] name = "icarus-dm" -version = "0.6.1" +version = "0.6.2" dependencies = [ "futures", "http", diff --git a/Cargo.toml b/Cargo.toml index 99038c5..c83c7d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icarus-dm" -version = "0.6.1" +version = "0.6.2" rust-version = "1.88" edition = "2024" diff --git a/README.md b/README.md index dce72b0..375a3e8 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,13 @@ icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -b e8407fc6- ### Uploading Song with metadata ```BASH -icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.flac -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png +icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -s /path/of/song.flac -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png ``` ### Uploading Song with metadata from directory ```BASH -icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -smca /path/where/songs/and/metadata/exists/ +icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -smca /path/where/songs/and/metadata/exists/ ``` ### Retrieving Song in json diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index 2740d1b..713616f 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -332,6 +332,7 @@ impl CommitManager { } } + /// Only uploading one song async fn sing_target_upload( &mut self, songpath: &String, @@ -413,6 +414,7 @@ impl CommitManager { } } + /// Upload song to the queue to get processed async fn upload_song_process(&self, data: &UploadSongMembers) -> Result<()> { let mut up = syncers::upload::Upload::default(); let host = self.ica_action.retrieve_flag_value(&String::from("-h")); @@ -558,27 +560,39 @@ impl CommitManager { cover_art.data = cover_art.to_data().unwrap(); match self.get_songs(&metadatapath, sourcepath) { - Ok(sngs) => { - for song in sngs { - match Runtime::new() - .unwrap() - .block_on(up.upload_song_with_metadata(&token, &song, &cover_art)) - { - Ok(o) => { - println!("Response: {o:?}"); + Ok(sngs) => match icarus_models::album::collection::parse_album(&metadatapath) { + Ok(album) => { + for song in sngs { + let members = UploadSongMembers { + song: song, + coverart: cover_art.clone(), + token: token.clone(), + album: album.clone(), + }; + + match self.upload_song_process(&members).await { + Ok(o) => { + println!("Response: {o:?}"); + } + Err(err) => { + println!("Error: {err:?}"); + return Err(err); + } } - Err(err) => { - println!("Error: {err:?}"); - } - }; + } + + Ok(()) } - } + Err(err) => { + println!("Error: {err:?}"); + Err(std::io::Error::other(err.to_string())) + } + }, Err(error) => { println!("Error: {error:?}"); + Err(std::io::Error::other(error.to_string())) } } - - Ok(()) } fn get_cover_art_path(&self, directory_path: &String) -> Result { -- 2.47.3