From ffc0d398f0cebfad22d67980c1c3c03b672a2114 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Sun, 2 Jun 2024 16:11:23 -0400 Subject: [PATCH] Added more multi-upload code --- Cargo.toml | 3 +- src/managers/commit_manager.rs | 125 ++++++++++++++++++++------------- src/models/song.rs | 56 +++++++++++++++ src/syncers/upload.rs | 116 ++++++++++++++++++++++++++++-- 4 files changed, 247 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 75ab991..79a044e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,10 @@ edition = "2021" [dependencies] futures = { version = "0.3.30" } -reqwest = { version = "0.12.4", features = ["json", "blocking"] } +reqwest = { version = "0.12.4", features = ["json", "blocking", "multipart", "stream"] } serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" tokio = { version = "1.37", features = ["full"] } +tokio-util = { version = "0.7.11", features = ["codec"] } # rodio = "0.17.3" # metadata = "0.1.8" diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index d3614de..a63b563 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use tokio::runtime::Runtime; use crate::models; +use crate::models::song::Album; use crate::parsers; use crate::syncers; use crate::utilities; @@ -20,30 +21,6 @@ pub struct CommitManager { pub ica_action: models::icarus_action::IcarusAction, } -#[derive(Debug, Deserialize, Serialize)] -pub struct Album { - pub title: String, - pub album_artist: String, - pub genre: String, - pub year: i32, - pub track_count: i32, - pub disc_count: i32, - pub songs: Vec, -} - -impl Default for Album { - fn default() -> Self { - Album { - title: String::new(), - album_artist: String::new(), - genre: String::new(), - year: 0, - track_count: 0, - disc_count: 0, - songs: Vec::new(), - } - } -} #[derive(Clone, Debug)] enum ActionValues { @@ -264,6 +241,7 @@ impl CommitManager { let mut cover_art = models::song::CoverArt::default(); let mut songs: Vec = Vec::new(); + let mut filenames: Vec = Vec::new(); let mut metadatapath: String = String::new(); // iterate files in metadatapath @@ -298,10 +276,16 @@ impl CommitManager { En::SongFile => { let mut song = models::song::Song::default(); let fname = self.o_to_string(&file_name); - song.filepath = Some(fname.unwrap()); - song.directory = Some(sourcepath.clone()); - // song.filepath = Some( - self.initialize_disc_and_track(&mut song); + + match fname { + Ok(s) => { + filenames.push(s.clone()); + song.filepath = Some(s.clone()); + song.directory = Some(sourcepath.clone()); + self.initialize_disc_and_track(&mut song); + }, + Err(er) => println!("Error: {:?}", er), + } songs.push(song) } @@ -309,22 +293,74 @@ impl CommitManager { } } - let album = self.retrieve_metadata(&metadatapath); - songs.clear(); - songs = album.songs.clone(); + let mut album = self.retrieve_metadata(&metadatapath); + + self.song_parsing(&mut album, &sourcepath, &filenames); + + // songs.clear(); + // songs = album.songs; let mut up = syncers::upload::Upload::default(); + let host = self.ica_action.retrieve_flag_value(&String::from("-h")); + up.set_api(&host); - for _ in songs { + for song in &album.songs { // Upload each song // TODO: Add functions to Upload struct that uploads song // with metadata and img + up.upload_song_with_metadata(&token, &song, &cover_art, &album); } Ok(()) } - // TODO: Implement + + // Makes sure the elements in album.songs is populated + fn song_parsing(&self, album: &mut models::song::Album, directory: &String, filenames: &Vec) { + // Apply directory + for song in &mut album.songs { + // let bor = song.as_mut(); + let mut dir = &song.directory; + match dir { + Some(s) => println!("{}", s), + None => { + song.directory = Some(directory.clone()); + }, + } + } + + // Apply filename + let mut index = 0; + for song in &mut album.songs { + let filename = filenames[index].clone(); + song.filepath = Some(filename); + index += 1; + } + + for song in &mut album.songs { + match &mut song.album { + Some(_) => {}, + None => { + song.album = Some(album.title.clone()); + }, + } + + match &mut song.genre { + Some(_) => {}, + None => { + song.genre = Some(album.genre.clone()); + }, + } + + match &mut song.year { + Some(_) => {}, + None => { + song.year = Some(album.year.clone()); + }, + } + } + } + fn find_file_extension(&self, file_name: &std::ffi::OsString) -> En { let file_name_str = Some(file_name.clone().into_string()); @@ -528,26 +564,19 @@ impl CommitManager { let content = self.retrieve_file_content(&path); // let alb = serde_json::from_str(&content.unwrap()); - - /* - let mut file: std::fs::File = std::fs::File::open(filepath).unwrap(); - let mut data = String::new(); - file.read_to_string(&mut data).unwrap(); - - return serde_json::from_str(&data).unwrap(); - */ + let val = content.unwrap(); // return alb.unwrap(); - return serde_json::from_str(&content.unwrap()).unwrap(); + let converted = serde_json::from_str(&val); + + match &converted { + Ok(res) => println!("Good!"), + Err(er) => println!("Error {:?}", er), + } + return converted.unwrap(); } fn retrieve_file_content(&self, path: &String) -> Result { - /* - let mut file = File::open(path)?; - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer)?; - */ - return std::fs::read_to_string(path); } } diff --git a/src/models/song.rs b/src/models/song.rs index 7060a27..bfdfe7a 100644 --- a/src/models/song.rs +++ b/src/models/song.rs @@ -1,4 +1,5 @@ use std::default::Default; +use std::io::Read; use serde::{Deserialize, Serialize}; @@ -20,6 +21,33 @@ pub struct Song { pub directory: Option, } +#[derive(Debug, Deserialize, Serialize)] +pub struct Album { + #[serde(alias = "album")] + pub title: String, + pub album_artist: String, + pub genre: String, + pub year: i32, + pub track_count: i32, + pub disc_count: i32, + #[serde(alias = "tracks")] + pub songs: Vec, +} + +impl Default for Album { + fn default() -> Self { + Album { + title: String::new(), + album_artist: String::new(), + genre: String::new(), + year: 0, + track_count: 0, + disc_count: 0, + songs: Vec::new(), + } + } +} + impl Default for Song { fn default() -> Self { Song { @@ -62,6 +90,15 @@ impl Song { return buffer; } + pub fn to_data(&self) -> Result, std::io::Error> { + let path = self.song_path(); + // let content = std::fs::read_to_string(path); + let mut file = std::fs::File::open(path)?; + let mut buffer = Vec::new(); + file.read(&mut buffer)?; + Ok(buffer) + // return content; + } // if 1 - wav, if 0 - mp3, anything else defaults to wav pub fn generate_filename_from_track(&mut self, i_type: i32) -> i32 { let mut filename: String = String::new(); @@ -103,3 +140,22 @@ impl Default for CoverArt { } } } + +impl CoverArt { + pub fn to_data(&self) -> Result, std::io::Error> { + let mut path: String = String::new(); + match &self.path { + Some(val) => { + path = String::from(val); + }, + None => { + (); + }, + } + + let mut file = std::fs::File::open(path)?; + let mut buffer = Vec::new(); + file.read(&mut buffer)?; + Ok(buffer) + } +} \ No newline at end of file diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index 6b5fd12..065e042 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -1,7 +1,13 @@ use std::default::Default; +use reqwest::blocking::multipart; +use serde::{Deserialize, Serialize}; use reqwest; +// use reqwest::blocking:: +use reqwest::{Body, Client}; +use reqwest::multipart::{Form, Part}; use reqwest::Response; +use tokio::fs::File; use crate::models; @@ -9,6 +15,22 @@ pub struct Upload { pub api: models::api::API, } + +#[derive(Debug, Deserialize, Serialize)] +struct Song { + title: String, + album: String, + artist: String, + album_artist: String, + year: i32, + genre: String, + duration: f64, + track: i32, + track_count: i32, + disc: i32, + disc_count: i32, +} + impl Default for Upload { fn default() -> Self { Upload { @@ -39,17 +61,103 @@ impl Upload { } } + // TODO: Implement + pub async fn upload_song_with_metadata(&mut self, token: &models::token::Token, song: &models::song::Song, + cover: &models::song::CoverArt, album: &models::song::Album) { + self.api.endpoint = String::from("song/data/upload/with/data"); + let url = self.retrieve_url(&song); + let client = reqwest::Client::new(); + let new_song = self.initialize_song(&song, &album); + let access_token = token.bearer_token(); + + let song_data = song.to_data(); + let cover_data = cover.to_data(); + + match song_data { + Ok(sd) => { + }, + Err(er) => { + println!("Error: {:?}", er); + std::process::exit(-1); + } + } + + match cover_data { + Ok(_) => {}, + Err(er) => { + println!("Error: {:?}", er); + std::process::exit(-1); + } + } + + if url.is_empty() { + println!("Url is empty"); + } + + /* + + let s_file = std::fs::File::open(song.song_path()); + let s_stream = tokio_util::codec::FramedRead::new(s_file, tokio_util::codec::BytesCodec::new()); + let s_file_body = Body::wrap_stream(s_stream); + let s_some_file = multipart::Part::stream(s_file_body) + .file_name("track.cdda.wav"); + + let s_data = Part::file(song.song_path()); + let c_data = multipart::Part::stream(&cover.path); + + let mut form = Form::new(); + let meta = serde_json::to_string_pretty(&new_song); + form = form.text("metadata", meta.unwrap()); + + form = form.part("file", s_some_file) + .part("cover", c_data); + + let response = client.post(url).multipart(form).send(); + let result = response.text; + + return Ok(result); + */ + } + + + + pub fn set_api(&mut self, host: &String) { + let mut api = models::api::API::default(); + api.url = host.clone(); + api.version = String::from("v1"); + self.api = api; + } fn retrieve_url(&self, song: &models::song::Song) -> String { - // let mut url: String = String::new(); let api = &self.api; - let mut url: String = String::from(&api.url); + let mut buffer = api.url.clone(); + let count = buffer.len(); + + if buffer.chars().nth(count - 1) != Some('/') { + buffer += "/"; + } + + let mut url: String = String::from(&buffer); url += &String::from("api/"); url += &String::from(&api.version); url += &String::from("/"); url += &String::from(&api.endpoint); - // url += &String::from("/"); - // url += &song.id.unwrap().to_string(); return url; } + + fn initialize_song(&self, song: &models::song::Song, album: &models::song::Album) -> Song { + return Song { + title: String::from(&song.title.clone().unwrap()), + album: album.title.clone(), + artist: String::from(&song.artist.clone().unwrap().clone()), + album_artist: album.album_artist.clone(), + year: album.year.clone(), + genre: album.genre.clone(), + duration: song.duration.clone().unwrap(), + track: (song.track.clone().unwrap()), + track_count: album.track_count.clone(), + disc: song.disc.clone().unwrap(), + disc_count: album.disc_count.clone(), + }; + } }