21 Commits

Author SHA1 Message Date
KD ecd025346c tsk-70: Rust update (#74)
* Updated rust in workflow

* Updated rust in cargo

* icarus_models version bump

* Version bump

* Workflow changes
2025-10-12 17:06:57 -04:00
KD 74184fa022 Dependency update (#73)
* Cargo update

* Updated crates

* Removed unused file

* Code cleanup

* Version bump

* Removed commented code
2025-10-12 16:57:49 -04:00
KD c2a2ca049d icarus_models version bump (#71)
* icarus_models version bump

* icarus_models related changes

* Warning fixes

* Version bump

* Test fix
2025-10-11 20:05:34 -04:00
KD 157887bfac Merge pull request #52 from kdeng00/icarus_v2_support
Support for icarus v2 API
2025-08-30 13:23:15 -04:00
kdeng00 2b77bb978c Version bump 2025-08-30 13:18:12 -04:00
KD 206ebf03be Updated License (#69) 2025-08-30 13:17:29 -04:00
KD 0d7d57bf5f Minor refatoring (#68)
* Removed old upload method

* Removed upload commit action

* Version bump
2025-08-30 13:13:53 -04:00
KD 11b8cc21d3 Delete song (#67)
* Changed code to comply with async

* Able to delete song

* Updated readme

* Code formatting and cleanup

* Version bump
2025-08-30 13:06:04 -04:00
KD e109789b18 Retrieve songs (#66)
* Able to retrieve songs

* Updated readme

* Removed unused import

* Code formatting

* Version bump
2025-08-30 12:10:03 -04:00
KD ec0cbd7da0 Download song (#65)
* Got download functionality working

* Updated readme

* Code formatting

* Updated help

* Version bump
2025-08-30 11:52:04 -04:00
KD 809f27cf01 Upload song (#58)
* Queue song (#57)

* Added code to queue song

* Making functions async

* Able to queue song

* Cleanup

* Link user to queued song (#59)

* Renaming queue_song function

* Queued song linked to user

* Cleanup and code formatting

* Queue metadata (#60)

* Added code to queue metadata

* Code formatting

* Queue coverart (#61)

* Added queued coverart code

* Code formatting and cleanup

* Link queued coverart to queued song (#62)

* Added code to link queued coverart to queued song

* Code formatting

* 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

* Multi upload (#64)

* Added comments

* Added TODO

* Got it working

* Updated documentation

* Version bump

* Code formatting and cleanup
2025-08-29 21:59:54 -04:00
KD 8a07718ce8 Token fetch (#56)
* Added print statement and TODO

* Refactoring token fetching code

Adding two APIs. One for auth and the other for the main app

* Fixed token code

* Code formatting

* Cleanup
2025-08-27 16:33:18 -04:00
KD 77cb5c8599 Icarus models bump (#55)
* icarus_models bump

* Fix after depdenency update
2025-08-27 15:07:44 -04:00
KD dbcfcfa109 Warning fixes (#54)
* More warning fixes

* Should be the last of the warning fixes

* Code cleanup

* Code formatting
2025-07-02 19:25:08 -04:00
KD 8b2b2f82e9 Update dependencies (#53)
* Removing new lines

* Updated reqwest, tokio, tokio-utils, and uuid

* Including Cargo.lock in source control

* Not sure how this got here

* Updated icarus_models

* Workflow change

* Removing unused workflow

* Github workflow fix

* Warning fixes

* Fixed what caused failed test

* Code cleanup and formatting
2025-07-02 12:26:38 -04:00
KD 8e6ddbc9df Rust std change (#51)
* Targeting rust 1.88

* Workflow changes
2025-06-30 20:49:15 -04:00
KD c8ba6606be Icarus models update (#50)
* Updated dependencies

* Saving changes

* More changes

* More changes

* Updated dependencies

* Updated readme

* Saving changes

* Cleanup

* Updated icarus_models

* Updated icarus_models
2025-04-10 21:04:58 -04:00
KD b7d55ebcf4 Targeting rust 1.86 (#49)
* Targeting rust 1.86

* Workflow changes
2025-04-05 17:06:49 -04:00
KD d5f95ddf9b Updated dependencies (#48)
* Updated dependencies

* Saving changes

* More changes

* More changes

* Updated dependencies

* Updated readme

* Saving changes

* Cleanup
2025-04-03 23:16:37 -04:00
KD 885f1db3af Removed Versioning section (#46) 2025-04-03 21:46:18 -04:00
KD d6f45c8279 Updated git cloning (#47) 2025-04-03 21:46:04 -04:00
27 changed files with 2738 additions and 580 deletions
-22
View File
@@ -1,22 +0,0 @@
name: Rust
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
+6 -6
View File
@@ -2,9 +2,9 @@ name: Rust
on: on:
push: push:
branches: [ "master" ] branches: [ "main" ]
pull_request: pull_request:
branches: [ "master" ] branches: [ "main" ]
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@@ -12,12 +12,12 @@ env:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Install Rust version - name: Install Rust version
run: rustup install 1.85.0 && rustup default 1.85.0 run: rustup install 1.90.0 && rustup default 1.90.0
- name: Debug secret - name: Debug secret
run: echo "${{ secrets.MYREPO_TOKEN }}" | head -c 10 ; echo "..." run: echo "${{ secrets.MYREPO_TOKEN }}" | head -c 10 ; echo "..."
@@ -36,4 +36,4 @@ jobs:
EXTREPO_KEY: ${{ secrets.MYREPO_TOKEN }} EXTREPO_KEY: ${{ secrets.MYREPO_TOKEN }}
- name: Run tests - name: Run tests
run: cargo test --verbose run: cargo test --verbose
-1
View File
@@ -7,4 +7,3 @@
/target /target
.vscode/ .vscode/
Cargo.lock
View File
Generated
+1994
View File
File diff suppressed because it is too large Load Diff
+9 -11
View File
@@ -1,19 +1,17 @@
[package] [package]
name = "icarus-dm" name = "icarus-dm"
version = "0.6.0" version = "0.8.0"
rust-version = "1.90"
edition = "2024" edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
futures = { version = "0.3.31" } futures = { version = "0.3.31" }
http = { version = "1.3.1" } http = { version = "1.3.1" }
reqwest = { version = "0.12.15", features = ["json", "blocking", "multipart", "stream"] } reqwest = { version = "0.12.23", features = ["json", "blocking", "multipart", "stream"] }
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.140" serde_json = "1.0.145"
tokio = { version = "1.44.1", features = ["full"] } tokio = { version = "1.47.1", features = ["full"] }
tokio-util = { version = "0.7.14", features = ["codec"] } tokio-util = { version = "0.7.16", features = ["codec"] }
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.2.0" } uuid = { version = "1.18.1", features = ["v4", "serde"] }
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.7.0-70-f0a716b243-111" }
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019 Kun Deng Copyright (c) 2025 Kun Deng
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+11 -14
View File
@@ -14,6 +14,7 @@ IcarusDownloadManager is a CLI software client application that has the feature
* serde_json * serde_json
* tokio * tokio
* tokio-util * tokio-util
* icarus_models
### Getting Started ### Getting Started
@@ -21,7 +22,7 @@ IcarusDownloadManager is a CLI software client application that has the feature
Clone the repo Clone the repo
```BASH ```BASH
git clone --recursive https://github.com/kdeng00/IcarusDownloadManager git clone git@github.com:kdeng00/IcarusDownloadManager.git
``` ```
@@ -32,36 +33,40 @@ cd IcarusDownloadManager
cargo build cargo build
``` ```
Even though this project is open source, there are some libraries that are closed source (may be opened later).
In order to successfully build it, your ssh public key would be needed to add to the closed libraries. If you
have interest, something could be worked out to provide access.
The program has been built and can be executed by the binary file *icarus-dm*. For information on how to use icarua-dm, merely execute the program without any command line arguments. The program has been built and can be executed by the binary file *icarus-dm*. For information on how to use icarua-dm, merely execute the program without any command line arguments.
### Downloading Song ### Downloading Song
```BASH ```BASH
icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -b 15 icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -b e8407fc6-edd2-44c1-993f-08dd7324d91a
``` ```
### Uploading Song with metadata ### Uploading Song with metadata
```BASH ```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 ### Uploading Song with metadata from directory
```BASH ```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 ### Retrieving Song in json
```Bash ```Bash
icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -rt songs icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -rt songs
``` ```
### Deleting Song ### Deleting Song
```BASH ```BASH
icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -D 15 icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -D e8407fc6-edd2-44c1-993f-08dd7324d91a
``` ```
@@ -69,15 +74,7 @@ icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -D 15
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduct, and the process for submitting pull requests to the project. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduct, and the process for submitting pull requests to the project.
## Versioning
[v0.4.0](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.4.0)
[v0.3.2](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.3.2)
[v0.3.0](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.3.0)
[v0.2.0](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.2.0)
[v0.1.2](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.1.2)
[v0.1.1](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.1.1)
[v0.1.0](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/0.1.0)
## License ## License
+3 -2
View File
@@ -12,7 +12,8 @@ pub fn print_help() {
Required for all actions Required for all actions
-u username -u username
-p password -p password
-h host -h api host
-ha auth api host
Required for upload with metadata Required for upload with metadata
-s path of song -s path of song
@@ -32,5 +33,5 @@ pub fn print_help() {
-D song id"#, -D song id"#,
); );
println!("{}", msg); println!("{msg}");
} }
+6 -5
View File
@@ -7,7 +7,8 @@ mod utilities;
use std::env; use std::env;
fn main() { #[tokio::main]
async fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let args_len = args.len() as i32; let args_len = args.len() as i32;
@@ -16,7 +17,7 @@ fn main() {
utilities::checks::exit_program(-1); utilities::checks::exit_program(-1);
} }
println!("Argument count: {}", args_len); println!("Argument count: {args_len}");
let mut act_mgr = managers::action_managers::ActionManager::default(); let mut act_mgr = managers::action_managers::ActionManager::default();
act_mgr.set_params(&args); act_mgr.set_params(&args);
@@ -29,7 +30,7 @@ fn main() {
ica_action: chosen_act, ica_action: chosen_act,
}; };
cmt_mgr.commit_action(); cmt_mgr.commit_action().await;
} }
#[cfg(test)] #[cfg(test)]
@@ -41,7 +42,7 @@ mod tests {
let meta_path = String::from("tests/sample2_tracks/album.json"); let meta_path = String::from("tests/sample2_tracks/album.json");
if !std::path::Path::new(&meta_path).exists() { if !std::path::Path::new(&meta_path).exists() {
assert!(false, "File does not exists: {:?}", meta_path); assert!(false, "File does not exists: {meta_path:?}");
} }
match icarus_models::album::collection::parse_album(&meta_path) { match icarus_models::album::collection::parse_album(&meta_path) {
@@ -62,7 +63,7 @@ mod tests {
match managers::commit_manager::retrieve_song( match managers::commit_manager::retrieve_song(
&album, track, disc, &directory, &filename, &album, track, disc, &directory, &filename,
) { ) {
Ok(song) => match song.to_data() { Ok(song) => match icarus_models::song::io::to_data(&song) {
Ok(_) => { Ok(_) => {
print!("Success"); print!("Success");
} }
+19 -22
View File
@@ -23,18 +23,19 @@ impl Default for ActionManager {
impl ActionManager { impl ActionManager {
pub fn retrieve_icarus_action(&self) -> models::icarus_action::IcarusAction { pub fn retrieve_icarus_action(&self) -> models::icarus_action::IcarusAction {
return models::icarus_action::IcarusAction { models::icarus_action::IcarusAction {
flags: self.flags.clone(), flags: self.flags.clone(),
action: String::from(&self.action), action: String::from(&self.action),
}; }
} }
fn supported_flags(&self) -> Vec<String> { fn supported_flags(&self) -> Vec<String> {
return vec![ vec![
String::from("-u"), String::from("-u"),
String::from("-p"), String::from("-p"),
String::from("-t"), String::from("-t"),
String::from("-h"), String::from("-h"),
String::from("-ha"),
String::from("-s"), String::from("-s"),
String::from("-sd"), String::from("-sd"),
String::from("-sr"), String::from("-sr"),
@@ -47,7 +48,7 @@ impl ActionManager {
String::from("-ca"), String::from("-ca"),
String::from("-smca"), String::from("-smca"),
String::from("-t"), String::from("-t"),
]; ]
} }
pub fn initialize(&mut self) { pub fn initialize(&mut self) {
@@ -56,8 +57,8 @@ impl ActionManager {
self.action = self.action.to_lowercase(); self.action = self.action.to_lowercase();
} }
pub fn set_params(&mut self, args: &Vec<String>) { pub fn set_params(&mut self, args: &[String]) {
self.params = args.clone(); self.params = args.to_owned();
self.param_count = self.params.len() as i32; self.param_count = self.params.len() as i32;
} }
@@ -71,26 +72,26 @@ impl ActionManager {
while i < flag_vals.len() { while i < flag_vals.len() {
let flag = &flag_vals[i]; let flag = &flag_vals[i];
println!("Index: {} | Value: {}", i, flag); println!("Index: {i} | Value: {flag}");
let mut flg = models::flags::Flags::default(); let mut flg = models::flags::Flags::default();
// TODO: Refactor this
if self.is_valid_flag(flag) && self.does_flag_have_value(flag) { if self.is_valid_flag(flag) && self.does_flag_have_value(flag) {
println!("Flag has value"); println!("Flag has value");
flg.flag = String::from(flag); flg.flag = String::from(flag);
flg.value = String::from(&flag_vals[i + 1]); flg.value = String::from(&flag_vals[i + 1]);
i = i + 1; i += 1;
} else if self.is_valid_flag(flag) { } else if self.is_valid_flag(flag) {
println!("Flag does not have a value"); println!("Flag does not have a value");
flg.flag = String::from(flag); flg.flag = String::from(flag);
} else { } else {
println!("Flag {} is not valid", flag); println!("Flag {flag} is not valid");
utilities::checks::exit_program(-1); utilities::checks::exit_program(-1);
} }
self.flags.push(flg); self.flags.push(flg);
println!("");
i += 1; i += 1;
} }
} }
@@ -113,34 +114,30 @@ impl ActionManager {
} }
} }
return found; found
} }
fn does_flag_have_value(&self, flag: &String) -> bool { fn does_flag_have_value(&self, flag: &String) -> bool {
let flags_tmp = self.parsed_flags(); let flags_tmp = self.parsed_flags();
let mut i_found: i32 = -1; let mut i_found: i32 = -1;
for i in 0..flags_tmp.len() { for (i, item) in flags_tmp.iter().enumerate() {
let flg = &flags_tmp[i]; let flg = &item;
if flg == flag { if *flg == flag {
i_found = i as i32; i_found = i as i32;
break; break;
} }
} }
if i_found >= 0 { if i_found >= 0 {
if (i_found + 1) < flags_tmp.len().try_into().unwrap() { (i_found + 1) < flags_tmp.len().try_into().unwrap()
return true;
} else {
return false;
}
} else { } else {
return false; false
} }
} }
fn _print_action(&self) { fn _print_action(&self) {
if self.action.len() == 0 { if self.action.is_empty() {
println!("Action is empty"); println!("Action is empty");
} else { } else {
println!("Action is {}", self.action); println!("Action is {}", self.action);
@@ -163,6 +160,6 @@ impl ActionManager {
parsed.push(flag); parsed.push(flag);
} }
return parsed; parsed
} }
} }
+275 -212
View File
@@ -5,7 +5,6 @@ use std::io::{Result, Write};
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::runtime::Runtime;
use crate::managers; use crate::managers;
use crate::models::{self}; use crate::models::{self};
@@ -23,7 +22,6 @@ enum ActionValues {
DeleteAct, DeleteAct,
DownloadAct, DownloadAct,
RetrieveAct, RetrieveAct,
UploadAct,
UploadSongWithMetadata, UploadSongWithMetadata,
None, None,
} }
@@ -36,12 +34,20 @@ enum En {
Other, Other,
} }
#[derive(Debug)]
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( pub fn retrieve_song(
album: &icarus_models::album::collection::Album, album: &icarus_models::album::collection::Album,
track: i32, track: i32,
disc: i32, disc: i32,
directory: &String, directory: &str,
filename: &String, filename: &str,
) -> Result<icarus_models::song::Song> { ) -> Result<icarus_models::song::Song> {
let mut found = false; let mut found = false;
let mut song = icarus_models::song::Song::default(); let mut song = icarus_models::song::Song::default();
@@ -55,16 +61,16 @@ pub fn retrieve_song(
song.audio_type = String::from( song.audio_type = String::from(
icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION, icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION,
); );
song.disc = track.disc.clone(); song.disc = track.disc;
song.disc_count = album.disc_count.clone(); song.disc_count = album.disc_count;
song.duration = track.duration as i32; song.duration = track.duration as i32;
song.genre = album.genre.clone(); song.genre = album.genre.clone();
song.title = track.title.clone(); song.title = track.title.clone();
song.year = album.year.clone(); song.year = album.year;
song.track = track.track.clone(); song.track = track.track;
song.track_count = album.track_count.clone(); song.track_count = album.track_count;
song.directory = directory.clone(); song.directory = directory.to_owned();
song.filename = filename.clone(); song.filename = filename.to_owned();
found = true; found = true;
break; break;
@@ -72,31 +78,29 @@ pub fn retrieve_song(
} }
if found { if found {
return Ok(song); Ok(song)
} else {
Err(std::io::Error::other("Song not found"))
} }
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Song not found",
));
} }
impl CommitManager { impl CommitManager {
pub fn commit_action(&mut self) { pub async fn commit_action(&mut self) {
let action = &self.ica_action.action; let action = &self.ica_action.action;
println!("Committing {} action", action); println!("Committing {action} action");
let mapped_actions = &self.map_actions(); let mapped_actions = &self.map_actions();
let mapped_action = self.find_mapped_action(&mapped_actions, action); let mapped_action = self.find_mapped_action(mapped_actions, action);
println!("{:?}", mapped_action); println!("{mapped_action:?}");
// TODO: Move code to get token here and then pass it to the respective functions
match mapped_action { match mapped_action {
ActionValues::DeleteAct => self.delete_song(), ActionValues::DeleteAct => self.delete_song().await,
ActionValues::DownloadAct => self.download_song(), ActionValues::DownloadAct => self.download_song().await,
ActionValues::RetrieveAct => self.retrieve_object(), ActionValues::RetrieveAct => self.retrieve_object().await,
ActionValues::UploadAct => self.upload_song(), ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata().await,
ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata(),
_ => { _ => {
println!("Nothing good here"); println!("Nothing good here");
} }
@@ -114,14 +118,13 @@ impl CommitManager {
} }
} }
return ActionValues::None; ActionValues::None
} }
fn map_actions(&self) -> HashMap<String, ActionValues> { fn map_actions(&self) -> HashMap<String, ActionValues> {
let actions: HashMap<String, ActionValues> = HashMap::from([ let actions: HashMap<String, ActionValues> = HashMap::from([
("download".to_string(), ActionValues::DownloadAct), ("download".to_string(), ActionValues::DownloadAct),
("download".to_string(), ActionValues::DownloadAct), ("download".to_string(), ActionValues::DownloadAct),
("upload".to_string(), ActionValues::UploadAct),
( (
"upload-meta".to_string(), "upload-meta".to_string(),
ActionValues::UploadSongWithMetadata, ActionValues::UploadSongWithMetadata,
@@ -130,18 +133,19 @@ impl CommitManager {
("delete".to_string(), ActionValues::DeleteAct), ("delete".to_string(), ActionValues::DeleteAct),
]); ]);
return actions; actions
} }
fn delete_song(&self) { async fn delete_song(&self) {
let mut prsr = parsers::api_parser::APIParser { let mut prsr = parsers::api_parser::APIParser {
apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(), ica_act: self.ica_action.clone(),
api: models::api::API::default(),
}; };
prsr.parse_api(); prsr.parse_api(parsers::api_parser::APIType::Main);
let api = prsr.retrieve_api(); prsr.parse_api(parsers::api_parser::APIType::Auth);
let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth);
let token = self.parse_token(&api); let token = self.parse_token(&auth_api).await;
println!("Deleting song"); println!("Deleting song");
@@ -152,90 +156,100 @@ impl CommitManager {
let value = &arg.value; let value = &arg.value;
if flag == "-D" { if flag == "-D" {
song.id = value.parse::<i32>().unwrap(); song.id = uuid::Uuid::from_str(value.as_str()).unwrap();
} }
} }
let api = prsr.retrieve_api(parsers::api_parser::APIType::Main);
let mut del = syncers::delete::Delete { api: api.clone() }; let mut del = syncers::delete::Delete { api: api.clone() };
println!("Deleting song.."); println!("Deleting song..");
let res_fut = del.delete_song(&token, &song); match del.delete_song(&token, &song).await {
let result = Runtime::new().unwrap().block_on(res_fut); Ok((song, _coverart)) => {
match result { println!("Song (Id {:?}) has been successfully deleted", song.id);
Ok(o) => {
println!("Song (Id {:?}) has been successfully deleted", o.id);
} }
Err(er) => { Err(er) => {
println!("Error {:?}", er); println!("Error {er:?}");
} }
} }
} }
fn download_song(&self) { async fn download_song(&self) {
println!("Deleting song"); println!("Deleting song");
let dwn = self.ica_action.retrieve_flag_value(&String::from("-b")); let dwn = self.ica_action.retrieve_flag_value(&String::from("-b"));
let id: i32 = dwn.parse::<i32>().unwrap(); let song_id = uuid::Uuid::from_str(dwn.as_str()).unwrap();
let mut prsr = parsers::api_parser::APIParser { let mut prsr = parsers::api_parser::APIParser {
api: models::api::API::default(), apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(), ica_act: self.ica_action.clone(),
}; };
prsr.parse_api(); prsr.parse_api(parsers::api_parser::APIType::Main);
prsr.parse_api(parsers::api_parser::APIType::Auth);
let api = prsr.retrieve_api(); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth);
let token = self.parse_token(&api); let token = self.parse_token(&auth_api).await;
println!("Message: {}", token.message); println!("Message: {}", token.message);
let api = prsr.retrieve_api(parsers::api_parser::APIType::Main);
let mut dwn_loader = syncers::download::Download { api: api.clone() }; let mut dwn_loader = syncers::download::Download { api: api.clone() };
let mut song = icarus_models::song::Song::default(); let song = icarus_models::song::Song {
song.id = id; id: song_id,
let result_fut = dwn_loader.download_song(&token, &song); ..Default::default()
match Runtime::new().unwrap().block_on(result_fut) { };
match dwn_loader.download_song(&token, &song).await {
Ok(o) => { Ok(o) => {
println!("Success"); println!("Success");
let filename = String::from("audio") let filename = String::from("audio")
+ icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION; + icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION;
let data = o.as_bytes(); let data = o.as_bytes();
let mut file = std::fs::File::create(filename).expect("Failed to save"); let mut file = std::fs::File::create(filename).expect("Failed to save");
file.write_all(&data) file.write_all(data)
.expect("Failed to save downloaded song"); .expect("Failed to save downloaded song");
} }
Err(er) => { Err(er) => {
println!("Error {:?}", er); println!("Error {er:?}");
match er { match er {
syncers::download::MyError::Request(error) => { syncers::download::MyError::Request(error) => {
println!("Error: {:?}", error); println!("Error: {error:?}");
} }
syncers::download::MyError::Other(ss) => { syncers::download::MyError::Other(ss) => {
println!("Error: {:?}", ss); println!("Error: {ss:?}");
} }
} }
} }
} }
} }
fn retrieve_object(&self) { async fn retrieve_object(&self) {
println!("Retrieving song"); println!("Retrieving song");
let rt = self.ica_action.retrieve_flag_value(&String::from("-rt")); let rt = self.ica_action.retrieve_flag_value(&String::from("-rt"));
if rt != "songs" { if rt != "songs" {
panic!("Unsupported -rt: {}", rt); panic!("Unsupported -rt: {rt}");
} }
let mut prsr = parsers::api_parser::APIParser { let mut prsr = parsers::api_parser::APIParser {
api: models::api::API::default(), apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(), ica_act: self.ica_action.clone(),
}; };
prsr.parse_api(); 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).await;
println!("Token {token:?}");
let api = prsr.retrieve_api(parsers::api_parser::APIType::Main);
let api = prsr.retrieve_api();
let token = self.parse_token(&api);
let mut repo = syncers::retrieve_records::RetrieveRecords { api: api.clone() }; let mut repo = syncers::retrieve_records::RetrieveRecords { api: api.clone() };
let result_fut = repo.get_all_songs(&token);
match Runtime::new().unwrap().block_on(result_fut) { match repo.get_all_songs(&token).await {
Ok(o) => { Ok(o) => {
println!("Songs");
println!("=====");
for song in o { for song in o {
println!("Title: {:?}", song.title); println!("Title: {:?}", song.title);
println!("Artist: {:?}", song.artist); println!("Artist: {:?}", song.artist);
@@ -244,17 +258,12 @@ impl CommitManager {
} }
} }
Err(er) => { Err(er) => {
println!("Error: {:?}", er); println!("Error: {er:?}");
} }
} }
} }
fn upload_song(&self) { async fn parse_token(&self, api: &models::api::Api) -> icarus_models::token::AccessToken {
println!("Deleting song");
panic!("Not supported");
}
fn parse_token(&self, api: &models::api::API) -> icarus_models::token::AccessToken {
println!("Fetching token"); println!("Fetching token");
let mut usr_mgr: managers::user_manager::UserManager = let mut usr_mgr: managers::user_manager::UserManager =
@@ -271,12 +280,12 @@ impl CommitManager {
}; };
tok_mgr.init(); tok_mgr.init();
let token = Runtime::new().unwrap().block_on(tok_mgr.request_token()); let token = tok_mgr.request_token().await;
return token.unwrap(); token.unwrap()
} }
fn upload_song_with_metadata(&mut self) { async fn upload_song_with_metadata(&mut self) {
println!("Uplodaring song with metadara"); println!("Uplodaring song with metadara");
let songpath = self.ica_action.retrieve_flag_value(&String::from("-s")); let songpath = self.ica_action.retrieve_flag_value(&String::from("-s"));
@@ -284,13 +293,13 @@ impl CommitManager {
let coverpath = self.ica_action.retrieve_flag_value(&String::from("-ca")); let coverpath = self.ica_action.retrieve_flag_value(&String::from("-ca"));
let track_id = self.ica_action.retrieve_flag_value(&String::from("-t")); let track_id = self.ica_action.retrieve_flag_value(&String::from("-t"));
let single_target = songpath.len() > 0 let single_target = !songpath.is_empty()
&& metadata_path.len() > 0 && !metadata_path.is_empty()
&& coverpath.len() > 0 && !coverpath.is_empty()
&& track_id.len() > 0; && !track_id.is_empty();
let uni = self.ica_action.retrieve_flag_value(&String::from("-smca")); let uni = self.ica_action.retrieve_flag_value(&String::from("-smca"));
let multitarget = uni.len() > 0; let multitarget = !uni.is_empty();
if single_target && multitarget { if single_target && multitarget {
println!("Cannot upload from source and directory"); println!("Cannot upload from source and directory");
@@ -298,34 +307,38 @@ impl CommitManager {
} }
if single_target { if single_target {
println!("Song path: {}", songpath); println!("Song path: {songpath}");
println!("Track ID: {}", track_id); println!("Track ID: {track_id}");
println!("metadata path: {}", metadata_path); println!("metadata path: {metadata_path}");
println!("cover art path: {}", coverpath); 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 { } else if multitarget {
let _ = self.multi_target_upload(&uni); let _ = self.multi_target_upload(&uni).await;
} else { } else {
println!("Single or Multi target has not been chosen"); println!("Single or Multi target has not been chosen");
} }
} }
fn sing_target_upload( /// Only uploading one song
async fn sing_target_upload(
&mut self, &mut self,
songpath: &String, songpath: &String,
track_id: &String, track_id: &str,
meta_path: &String, meta_path: &String,
cover_path: &String, cover_path: &str,
) -> Result<()> { ) -> Result<()> {
let mut prsr = parsers::api_parser::APIParser { let mut prsr = parsers::api_parser::APIParser {
api: models::api::API::default(), apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(), ica_act: self.ica_action.clone(),
}; };
prsr.parse_api(); prsr.parse_api(parsers::api_parser::APIType::Main);
prsr.parse_api(parsers::api_parser::APIType::Auth);
let api = prsr.retrieve_api(); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth);
let token = self.parse_token(&api); let token = self.parse_token(&auth_api).await;
println!("Token: {:?}", token.token); println!("Token: {:?}", token.token);
@@ -337,19 +350,20 @@ impl CommitManager {
} }
let mut cover_art = icarus_models::coverart::CoverArt { let mut cover_art = icarus_models::coverart::CoverArt {
id: 0, id: uuid::Uuid::nil(),
title: String::new(), title: String::new(),
path: cover_path.clone(), path: cover_path.to_owned(),
data: Vec::new(), data: Vec::new(),
..Default::default()
}; };
let file_name = std::ffi::OsString::from(&song_file.file_name().unwrap()); let file_name = std::ffi::OsString::from(&song_file.file_name().unwrap());
match self.find_file_extension(&file_name) { match self.find_file_extension(&file_name) {
En::SongFile => match utilities::string::o_to_string(&file_name) { En::SongFile => match utilities::string::o_to_string(&file_name) {
Ok(s) => { Ok(s) => {
println!("file name: {:?}", file_name); println!("file name: {file_name:?}");
match icarus_models::album::collection::parse_album(&meta_path) { match icarus_models::album::collection::parse_album(meta_path) {
Ok(album) => { Ok(album) => {
let filename = s.clone(); let filename = s.clone();
let directory = song_file.parent().unwrap().display().to_string(); let directory = song_file.parent().unwrap().display().to_string();
@@ -359,67 +373,120 @@ impl CommitManager {
println!("Directory: {:?}", s.directory); println!("Directory: {:?}", s.directory);
println!("Filename: {:?}", s.filename); println!("Filename: {:?}", s.filename);
println!("Path: {:?}", s.song_path()); println!("Path: {:?}", s.song_path());
s.data = s.to_data().unwrap(); s.data = icarus_models::song::io::to_data(&s).unwrap();
cover_art.data = cover_art.to_data().unwrap(); cover_art.data =
icarus_models::coverart::io::to_data(&cover_art).unwrap();
let mut up = syncers::upload::Upload::default(); let members = UploadSongMembers {
let host = self.ica_action.retrieve_flag_value(&String::from("-h")); song: s,
up.set_api(&host); coverart: cover_art,
token,
album,
};
let res = up.upload_song_with_metadata(&token, &s, &cover_art); match self.upload_song_process(&members).await {
Ok(_) => Ok(()),
match Runtime::new().unwrap().block_on(res) { Err(err) => Err(err),
Ok(o) => {
println!("Successfully sent {:?}", o);
Ok(())
}
Err(er) => {
println!("Some error {:?}", er);
Err(std::io::Error::new(
std::io::ErrorKind::Other,
er.to_string(),
))
}
} }
} }
Err(err) => { Err(err) => {
println!("Error: {:?}", err); println!("Error: {err:?}");
Err(std::io::Error::new( Err(std::io::Error::other(err.to_string()))
std::io::ErrorKind::Other,
err.to_string(),
))
} }
} }
} }
Err(er) => { Err(er) => {
println!("Error: {:?}", er); println!("Error: {er:?}");
return Err(std::io::Error::new( Err(std::io::Error::other(er.to_string()))
std::io::ErrorKind::Other,
er.to_string(),
));
} }
}, },
_ => { _ => Err(std::io::Error::other("No sutitable file found".to_owned())),
return Err(std::io::Error::new( }
std::io::ErrorKind::Other, }
"No sutitable file found".to_owned(),
)); /// 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"));
up.set_api(&host);
let token = &data.token;
let song = &data.song;
let album = &data.album;
let coverart = &data.coverart;
println!("Queueing song");
let queued_song_id = match up.queue_song(token, song).await {
Ok(id) => id,
Err(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()));
} }
} }
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:?}");
match up.queue_coverart(token, coverart).await {
Ok(id) => match up
.link_queued_song_to_queued_coverart(token, &queued_song_id, &id)
.await
{
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()));
}
},
Err(err) => {
return Err(std::io::Error::other(err.to_string()));
}
}
Ok(())
} }
fn get_songs( fn get_songs(
&self, &self,
metadata_path: &String, metadata_path: &String,
source_directory: &String, source_directory: &str,
) -> Result<Vec<icarus_models::song::Song>> { ) -> Result<Vec<icarus_models::song::Song>> {
match icarus_models::album::collection::parse_album(metadata_path) { match icarus_models::album::collection::parse_album(metadata_path) {
Ok(albums) => { Ok(albums) => {
let mut songs: Vec<icarus_models::song::Song> = Vec::new(); let mut songs: Vec<icarus_models::song::Song> = Vec::new();
for track in &albums.tracks { for track in &albums.tracks {
let filename = if track.track < 10 { let song_filename = if track.track < 10 {
"track0".to_owned() "track0".to_owned()
+ &track.track.to_string() + &track.track.to_string()
+ icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION + icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION
@@ -430,22 +497,22 @@ impl CommitManager {
}; };
songs.push(icarus_models::song::Song { songs.push(icarus_models::song::Song {
id: -1, id: uuid::Uuid::nil(),
title: track.title.clone(), title: track.title.clone(),
artist: track.artist.clone(), artist: track.artist.clone(),
disc: track.disc.clone(), disc: track.disc,
track: track.track.clone(), track: track.track,
duration: track.duration.clone() as i32, duration: track.duration as i32,
year: albums.year.clone(), year: albums.year,
album_artist: albums.artist.clone(), album_artist: albums.artist.clone(),
genre: albums.genre.clone(), genre: albums.genre.clone(),
disc_count: albums.disc_count.clone(), disc_count: albums.disc_count,
track_count: albums.track_count.clone(), track_count: albums.track_count,
album: albums.title.clone(), album: albums.title.clone(),
audio_type: String::from("FLAC"), audio_type: String::from("FLAC"),
directory: source_directory.clone(), directory: source_directory.to_owned(),
filename: filename, filename: song_filename,
user_id: -1, user_id: uuid::Uuid::nil(),
data: Vec::new(), data: Vec::new(),
date_created: String::new(), date_created: String::new(),
}); });
@@ -456,14 +523,15 @@ 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 { let mut prsr = parsers::api_parser::APIParser {
api: models::api::API::default(), apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(), ica_act: self.ica_action.clone(),
}; };
prsr.parse_api(); prsr.parse_api(parsers::api_parser::APIType::Main);
let api = prsr.retrieve_api(); prsr.parse_api(parsers::api_parser::APIType::Auth);
let token = self.parse_token(&api); let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth);
let token = self.parse_token(&auth_api).await;
let directory_path = std::path::Path::new(&sourcepath); let directory_path = std::path::Path::new(&sourcepath);
@@ -471,50 +539,72 @@ impl CommitManager {
panic!("Directory does not exist"); panic!("Directory does not exist");
} }
let mut cover_art = icarus_models::coverart::CoverArt { let coverart_path = self.get_cover_art_path(sourcepath).unwrap_or_default();
id: 0, let mut cover_art =
title: String::new(), icarus_models::coverart::init::init_coverart_only_path(coverart_path.clone());
path: match self.get_cover_art_path(&sourcepath) { let metadatapath = self.get_metadata_path(sourcepath).unwrap_or_default();
Ok(o) => o,
Err(_) => String::new(),
},
data: Vec::new(),
};
let metadatapath = match self.get_metadata_path(&sourcepath) {
Ok(o) => o,
Err(_) => String::new(),
};
let mut up = syncers::upload::Upload::default(); let mut up = syncers::upload::Upload::default();
let host = self.ica_action.retrieve_flag_value(&String::from("-h")); let host = self.ica_action.retrieve_flag_value(&String::from("-h"));
up.set_api(&host); up.set_api(&host);
cover_art.data = cover_art.to_data().unwrap(); cover_art.data = icarus_models::coverart::io::to_data(&cover_art).unwrap();
println!(""); match self.get_songs(&metadatapath, sourcepath) {
Ok(sngs) => match icarus_models::album::collection::parse_album(&metadatapath) {
Ok(album) => {
for song in sngs {
let members = UploadSongMembers {
song,
coverart: cover_art.clone(),
token: token.clone(),
album: album.clone(),
};
match self.get_songs(&metadatapath, &sourcepath) { match self.upload_song_process(&members).await {
Ok(sngs) => { Ok(o) => {
for song in sngs { println!("Response: {o:?}");
match Runtime::new() }
.unwrap() Err(err) => {
.block_on(up.upload_song_with_metadata(&token, &song, &cover_art)) println!("Error: {err:?}");
{ return Err(err);
Ok(o) => { }
println!("Response: {:?}", o);
} }
Err(err) => { }
println!("Error: {:?}", err);
} Ok(())
};
} }
} Err(err) => {
println!("Error: {err:?}");
Err(std::io::Error::other(err.to_string()))
}
},
Err(error) => { Err(error) => {
println!("Error: {:?}", error); println!("Error: {error:?}");
Err(std::io::Error::other(error.to_string()))
}
}
}
fn get_cover_art_path(&self, directory_path: &String) -> Result<String> {
for entry in read_dir(std::path::Path::new(directory_path))? {
let entry = entry?;
let file_type = entry.file_type();
let file_name = entry.file_name();
println!("file type: {file_type:?}");
println!("file name: {file_name:?}");
if let En::ImageFile = self.find_file_extension(&file_name) {
let directory_part = directory_path.clone();
let fname = utilities::string::o_to_string(&file_name);
let fullpath = format!("{}/{}", directory_part, &fname.unwrap());
return Ok(fullpath);
} }
} }
Ok(()) Ok(String::new())
} }
fn find_file_extension(&self, file_name: &std::ffi::OsString) -> En { fn find_file_extension(&self, file_name: &std::ffi::OsString) -> En {
@@ -562,31 +652,7 @@ impl CommitManager {
} }
} }
return En::Other; En::Other
}
fn get_cover_art_path(&self, directory_path: &String) -> Result<String> {
for entry in read_dir(std::path::Path::new(directory_path))? {
let entry = entry?;
let file_type = entry.file_type();
let file_name = entry.file_name();
println!("file type: {:?}", file_type);
println!("file name: {:?}", file_name);
match self.find_file_extension(&file_name) {
En::ImageFile => {
let directory_part = directory_path.clone();
let fname = utilities::string::o_to_string(&file_name);
let fullpath = directory_part + "/" + &fname.unwrap();
return Ok(fullpath);
}
_ => {}
}
}
Ok(String::new())
} }
fn get_metadata_path(&self, directory_path: &String) -> Result<String> { fn get_metadata_path(&self, directory_path: &String) -> Result<String> {
@@ -596,16 +662,13 @@ impl CommitManager {
let file_type = entry.file_type(); let file_type = entry.file_type();
let file_name = entry.file_name(); let file_name = entry.file_name();
println!("file type: {:?}", file_type); println!("file type: {file_type:?}");
println!("file name: {:?}", file_name); println!("file name: {file_name:?}");
match self.find_file_extension(&file_name) { if let En::MetadataFile = self.find_file_extension(&file_name) {
En::MetadataFile => { let directory_part = directory_path.clone();
let directory_part = directory_path.clone(); let fname = utilities::string::o_to_string(&file_name);
let fname = utilities::string::o_to_string(&file_name); return Ok(format!("{}/{}", directory_part, &fname.unwrap()));
return Ok(directory_part + "/" + &fname.unwrap());
}
_ => {}
} }
} }
@@ -618,6 +681,6 @@ impl CommitManager {
return true; return true;
} }
} }
return false; false
} }
} }
+29 -15
View File
@@ -2,21 +2,31 @@ use std::default::Default;
use crate::models; use crate::models;
mod response {
pub mod token {
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Response {
pub message: String,
pub data: Vec<icarus_models::login_result::LoginResult>,
}
}
}
pub struct TokenManager { pub struct TokenManager {
pub user: icarus_models::user::User, pub user: icarus_models::user::User,
pub api: models::api::API, pub api: models::api::Api,
} }
impl Default for TokenManager { impl Default for TokenManager {
fn default() -> Self { fn default() -> Self {
let mut token = TokenManager { let mut token = TokenManager {
user: icarus_models::user::User::default(), user: icarus_models::user::User::default(),
api: models::api::API::default(), api: models::api::Api::default(),
}; };
token.init(); token.init();
return token; token
} }
} }
@@ -26,10 +36,10 @@ impl TokenManager {
let url = self.retrieve_url(); let url = self.retrieve_url();
println!("URL: {}", url); println!("URL: {url}");
let mut token = icarus_models::token::AccessToken { let mut token = icarus_models::token::AccessToken {
user_id: -1, user_id: uuid::Uuid::nil(),
username: String::new(), username: String::new(),
token: String::new(), token: String::new(),
token_type: String::new(), token_type: String::new(),
@@ -43,11 +53,15 @@ impl TokenManager {
match response.status() { match response.status() {
reqwest::StatusCode::OK => { reqwest::StatusCode::OK => {
// on success, parse our JSON to an APIResponse // on success, parse our JSON to an APIResponse
let s = response.json::<icarus_models::token::AccessToken>().await; match response.json::<response::token::Response>().await {
match s { Ok(response) => {
// let login_result = &response.data[0];
Ok(parsed) => { token.user_id = login_result.id;
token = parsed; token.username = login_result.username.clone();
token.token = login_result.token.clone();
token.token_type = login_result.token_type.clone();
token.expiration = login_result.expiration;
token.message = response.message;
} }
Err(_) => println!("Hm, the response didn't match the shape we expected."), Err(_) => println!("Hm, the response didn't match the shape we expected."),
}; };
@@ -56,17 +70,17 @@ impl TokenManager {
println!("Need to grab a new token"); println!("Need to grab a new token");
} }
other => { other => {
panic!("Uh oh! Something unexpected happened: {:?}", other); panic!("Uh oh! Something unexpected happened: {other:?}");
} }
} }
return Ok(token); Ok(token)
} }
pub fn init(&mut self) { pub fn init(&mut self) {
let api = &mut self.api; let api = &mut self.api;
api.version = String::from("v1"); api.version = String::from(crate::parsers::api_parser::API_VERSION);
api.endpoint = String::from(format!("api/{}/login", api.version)); api.endpoint = format!("api/{}/login", api.version);
} }
pub fn retrieve_url(&self) -> String { pub fn retrieve_url(&self) -> String {
@@ -74,6 +88,6 @@ impl TokenManager {
let mut url = String::from(&api.url); let mut url = String::from(&api.url);
url += &String::from(&api.endpoint); url += &String::from(&api.endpoint);
return url; url
} }
} }
+2 -11
View File
@@ -4,24 +4,15 @@ use serde::{Deserialize, Serialize};
use crate::models::{self}; use crate::models::{self};
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
pub struct UserManager { pub struct UserManager {
pub user: icarus_models::user::User, pub user: icarus_models::user::User,
pub ica_action: models::icarus_action::IcarusAction, pub ica_action: models::icarus_action::IcarusAction,
} }
impl Default for UserManager {
fn default() -> Self {
UserManager {
user: icarus_models::user::User::default(),
ica_action: models::icarus_action::IcarusAction::default(),
}
}
}
impl UserManager { impl UserManager {
pub fn retrieve_user(&self) -> icarus_models::user::User { pub fn retrieve_user(&self) -> icarus_models::user::User {
return self.user.clone(); self.user.clone()
} }
pub fn parse_user_from_actions(&mut self) { pub fn parse_user_from_actions(&mut self) {
+2 -12
View File
@@ -2,19 +2,9 @@ use std::default::Default;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct API { pub struct Api {
pub url: String, pub url: String,
pub endpoint: String, pub endpoint: String,
pub version: String, pub version: String,
} }
impl Default for API {
fn default() -> Self {
API {
url: String::new(),
endpoint: String::new(),
version: String::new(),
}
}
}
+1 -10
View File
@@ -2,17 +2,8 @@ use std::default::Default;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Flags { pub struct Flags {
pub flag: String, pub flag: String,
pub value: String, pub value: String,
} }
impl Default for Flags {
fn default() -> Self {
Flags {
flag: String::new(),
value: String::new(),
}
}
}
+2 -11
View File
@@ -4,21 +4,12 @@ use serde::{Deserialize, Serialize};
use crate::models; use crate::models;
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct IcarusAction { pub struct IcarusAction {
pub action: String, pub action: String,
pub flags: Vec<models::flags::Flags>, pub flags: Vec<models::flags::Flags>,
} }
impl Default for IcarusAction {
fn default() -> Self {
IcarusAction {
action: String::new(),
flags: Vec::new(),
}
}
}
impl IcarusAction { impl IcarusAction {
pub fn retrieve_flag_value(&self, flag: &String) -> String { pub fn retrieve_flag_value(&self, flag: &String) -> String {
let mut val: String = String::new(); let mut val: String = String::new();
@@ -30,7 +21,7 @@ impl IcarusAction {
} }
} }
return val; val
} }
pub fn print_action_and_flags(&self) { pub fn print_action_and_flags(&self) {
-1
View File
@@ -1,4 +1,3 @@
pub mod api; pub mod api;
pub mod flags; pub mod flags;
pub mod icarus_action; pub mod icarus_action;
pub mod upload_form;
-18
View File
@@ -1,18 +0,0 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct UploadForm {
pub url: Option<String>,
pub filepath: Option<String>,
}
impl Default for UploadForm {
fn default() -> Self {
UploadForm {
url: None,
filepath: None,
}
}
}
+37 -11
View File
@@ -2,16 +2,26 @@ use crate::models;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct APIParser { pub struct APIParser {
pub api: models::api::API, pub apis: Vec<models::api::Api>,
pub ica_act: models::icarus_action::IcarusAction, pub ica_act: models::icarus_action::IcarusAction,
} }
pub const API_VERSION: &str = "v2";
pub enum APIType {
Main,
Auth,
}
impl APIParser { impl APIParser {
pub fn retrieve_api(&self) -> models::api::API { pub fn retrieve_api(&self, api_type: APIType) -> models::api::Api {
return self.api.clone(); match api_type {
APIType::Main => self.apis[0].clone(),
APIType::Auth => self.apis[1].clone(),
}
} }
pub fn parse_api(&mut self) { pub fn parse_api(&mut self, api_type: APIType) {
let flags = self.ica_act.flags.clone(); let flags = self.ica_act.flags.clone();
println!("Parsing api"); println!("Parsing api");
@@ -19,16 +29,32 @@ impl APIParser {
let arg = elem.flag; let arg = elem.flag;
let value = elem.value; let value = elem.value;
if arg == "-h" { match api_type {
if value.chars().nth(value.len() - 1) == Some('/') { APIType::Main => {
self.api.url = value; if arg == "-h" {
} else { if value.chars().nth(value.len() - 1) == Some('/') {
self.api.url = value + "/"; self.apis[0].url = value;
} else {
self.apis[0].url = format!("{value}/");
}
break;
}
}
APIType::Auth => {
if arg == "-ha" {
if value.chars().nth(value.len() - 1) == Some('/') {
self.apis[1].url = value;
} else {
self.apis[1].url = format!("{value}/");
}
break;
}
} }
break;
} }
} }
self.api.version = "v1".to_string(); for api in &mut self.apis {
api.version = String::from(API_VERSION);
}
} }
} }
+18 -20
View File
@@ -1,32 +1,30 @@
use crate::models; use crate::models;
pub fn retrieve_url(api: &models::api::API, with_id: bool, id: i32) -> String { pub fn retrieve_url(api: &models::api::Api, with_id: bool, id: &uuid::Uuid) -> String {
if with_id { if with_id {
retrieve_url_with_id(&api, id) retrieve_url_with_id(api, id)
} else { } else {
retrieve_url_reg(&api) retrieve_url_reg(api)
} }
} }
fn retrieve_url_reg(api: &models::api::API) -> String { fn retrieve_url_reg(api: &models::api::Api) -> String {
let mut url: String = String::from(&api.url); let url = format!("{}api/{}/{}/", api.url, api.version, api.endpoint);
url += &String::from("api/");
url += &String::from(&api.version);
url += &String::from("/");
url += &String::from(&api.endpoint);
url += &String::from("/");
return url; url
} }
fn retrieve_url_with_id(api: &models::api::API, id: i32) -> String { fn retrieve_url_with_id(api: &models::api::Api, id: &uuid::Uuid) -> String {
let mut url: String = String::from(&api.url); let url = format!("{}api/{}/{}/{}", api.url, api.version, api.endpoint, id);
url += &String::from("api/");
url += &String::from(&api.version);
url += &String::from("/");
url += &String::from(&api.endpoint);
url += &String::from("/");
url += &id.to_string();
return url; 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)
} }
+28 -20
View File
@@ -3,17 +3,24 @@ use std::default::Default;
use reqwest; use reqwest;
use crate::models; use crate::models;
use crate::syncers;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct Delete { pub struct Delete {
pub api: models::api::API, pub api: models::api::Api,
} }
impl Default for Delete { mod response {
fn default() -> Self { pub mod delete_song {
Delete { #[derive(Debug, serde::Deserialize)]
api: models::api::API::default(), pub struct SongAndCoverArt {
pub song: icarus_models::song::Song,
pub coverart: icarus_models::coverart::CoverArt,
}
#[derive(Debug, serde::Deserialize)]
pub struct Response {
pub message: String,
pub data: Vec<SongAndCoverArt>,
} }
} }
} }
@@ -23,9 +30,12 @@ impl Delete {
&mut self, &mut self,
token: &icarus_models::token::AccessToken, token: &icarus_models::token::AccessToken,
song: &icarus_models::song::Song, song: &icarus_models::song::Song,
) -> Result<icarus_models::song::Song, std::io::Error> { ) -> Result<(icarus_models::song::Song, icarus_models::coverart::CoverArt), std::io::Error>
self.api.endpoint = "song/data/delete".to_owned(); {
let url = syncers::common::retrieve_url(&self.api, true, song.id); self.api.endpoint = "api/v2/song".to_owned();
let url = format!("{}{}/{}", self.api.url, self.api.endpoint, song.id);
println!("Url: {url:?}");
let client = reqwest::Client::builder().build().unwrap(); let client = reqwest::Client::builder().build().unwrap();
let access_token = token.bearer_token(); let access_token = token.bearer_token();
let response = client let response = client
@@ -39,18 +49,16 @@ impl Delete {
reqwest::StatusCode::OK => { reqwest::StatusCode::OK => {
println!("Success!"); println!("Success!");
match response.json::<icarus_models::song::Song>().await { match response.json::<response::delete_song::Response>().await {
Ok(sng) => Ok(sng), Ok(resp) => {
Err(er) => Err(std::io::Error::new( println!("Response message: {:?}", resp.message);
std::io::ErrorKind::Other, let data = &resp.data[0];
er.to_string(), Ok((data.song.clone(), data.coverart.clone()))
)), }
Err(er) => Err(std::io::Error::other(er.to_string())),
} }
} }
other => Err(std::io::Error::new( other => Err(std::io::Error::other(other.to_string())),
std::io::ErrorKind::Other,
other.to_string(),
)),
} }
} }
} }
+13 -29
View File
@@ -3,16 +3,9 @@ use std::default::Default;
use crate::models; use crate::models;
use crate::syncers; use crate::syncers;
#[derive(Default)]
pub struct Download { pub struct Download {
pub api: models::api::API, pub api: models::api::Api,
}
impl Default for Download {
fn default() -> Self {
Download {
api: models::api::API::default(),
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -27,10 +20,12 @@ impl Download {
token: &icarus_models::token::AccessToken, token: &icarus_models::token::AccessToken,
song: &icarus_models::song::Song, song: &icarus_models::song::Song,
) -> Result<String, MyError> { ) -> Result<String, MyError> {
self.api.endpoint = String::from("song/data/download"); self.api.endpoint = String::from("song/download");
let url = syncers::common::retrieve_url(&self.api, true, song.id); let url = syncers::common::retrieve_url(&self.api, true, &song.id);
let access_token = token.bearer_token(); let access_token = token.bearer_token();
println!("Url: {url:?}");
let client = reqwest::Client::builder().build().unwrap(); let client = reqwest::Client::builder().build().unwrap();
match client match client
@@ -40,27 +35,16 @@ impl Download {
.await .await
{ {
Ok(rep) => match rep.status() { Ok(rep) => match rep.status() {
reqwest::StatusCode::OK => { reqwest::StatusCode::OK => match rep.text().await {
let data = rep.text(); Ok(e) => Ok(e),
match data.await { Err(er) => Err(MyError::Other(er.to_string())),
Ok(e) => { },
return Ok(e);
}
Err(er) => {
return Err(MyError::Other(er.to_string()));
}
}
}
reqwest::StatusCode::UNAUTHORIZED => { reqwest::StatusCode::UNAUTHORIZED => {
return Err(MyError::Other(String::from("Need to grab a new token"))); Err(MyError::Other(String::from("Need to grab a new token")))
}
other => {
return Err(MyError::Other(other.to_string()));
} }
other => Err(MyError::Other(other.to_string())),
}, },
Err(er) => { Err(er) => Err(MyError::Request(er)),
return Err(MyError::Request(er));
}
} }
} }
} }
+20 -26
View File
@@ -2,16 +2,18 @@ use std::default::Default;
use std::io::Error; use std::io::Error;
use crate::models; use crate::models;
use crate::syncers;
#[derive(Default)]
pub struct RetrieveRecords { pub struct RetrieveRecords {
pub api: models::api::API, pub api: models::api::Api,
} }
impl Default for RetrieveRecords { mod response {
fn default() -> Self { pub mod get_all_songs {
RetrieveRecords { #[derive(Debug, serde::Deserialize)]
api: models::api::API::default(), pub struct Response {
pub message: String,
pub data: Vec<icarus_models::song::Song>,
} }
} }
} }
@@ -21,10 +23,12 @@ impl RetrieveRecords {
&mut self, &mut self,
token: &icarus_models::token::AccessToken, token: &icarus_models::token::AccessToken,
) -> Result<Vec<icarus_models::song::Song>, Error> { ) -> Result<Vec<icarus_models::song::Song>, Error> {
self.api.endpoint = String::from("song"); self.api.endpoint = String::from("api/v2/song/all");
let url = syncers::common::retrieve_url(&self.api, false, 0); let url = format!("{}{}", self.api.url, self.api.endpoint);
let access_token = token.bearer_token(); let access_token = token.bearer_token();
println!("url: {url:?}");
let client = reqwest::Client::builder().build().unwrap(); let client = reqwest::Client::builder().build().unwrap();
let response = client let response = client
.get(&url) .get(&url)
@@ -35,29 +39,19 @@ impl RetrieveRecords {
match response.status() { match response.status() {
reqwest::StatusCode::OK => { reqwest::StatusCode::OK => {
// on success, parse our JSON to an APIResponse // on success, parse our JSON to an API Response
match response.json::<Vec<icarus_models::song::Song>>().await { match response.json::<response::get_all_songs::Response>().await {
Ok(parsed) => Ok(parsed), Ok(parsed) => {
Err(err) => { println!("Response message: {:?}", parsed.message);
return Err(std::io::Error::new( Ok(parsed.data)
std::io::ErrorKind::Other,
err.to_string(),
));
} }
Err(err) => Err(std::io::Error::other(err.to_string())),
} }
} }
reqwest::StatusCode::UNAUTHORIZED => { reqwest::StatusCode::UNAUTHORIZED => {
return Err(std::io::Error::new( Err(std::io::Error::other("Need to grab a new token"))
std::io::ErrorKind::Other,
"Need to grab a new token",
));
}
other => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
other.to_string(),
));
} }
other => Err(std::io::Error::other(other.to_string())),
} }
} }
} }
+261 -67
View File
@@ -6,46 +6,72 @@ use reqwest;
use crate::models; use crate::models;
use crate::syncers; use crate::syncers;
#[derive(Default)]
pub struct Upload { pub struct Upload {
pub api: models::api::API, pub api: models::api::Api,
} }
impl Default for Upload { mod response {
fn default() -> Self { pub mod queue_song {
Upload { #[derive(Debug, serde::Deserialize)]
api: models::api::API::default(), pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod queue_metadata {
#[derive(Debug, serde::Deserialize)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod queue_coverart {
#[derive(Debug, serde::Deserialize)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
} }
} }
} }
impl Upload { impl Upload {
pub async fn upload_song_with_metadata( pub async fn queue_song(
&mut self, &self,
token: &icarus_models::token::AccessToken, token: &icarus_models::token::AccessToken,
song: &icarus_models::song::Song, song: &icarus_models::song::Song,
cover: &icarus_models::coverart::CoverArt, ) -> Result<uuid::Uuid, reqwest::Error> {
) -> Result<reqwest::Response, reqwest::Error> { let songpath = song.song_path().unwrap();
self.api.endpoint = String::from("song/data/upload/with/data"); let file = tokio::fs::File::open(&songpath).await.unwrap();
let url = syncers::common::retrieve_url(&self.api, false, 0); let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new());
let access_token = token.bearer_token(); let file_body = reqwest::Body::wrap_stream(stream);
if url.is_empty() { let form = reqwest::multipart::Form::new().part(
println!("Url is empty"); "file",
} reqwest::multipart::Part::stream(file_body)
.file_name(song.filename.clone())
.mime_str("application/octet-stream")
.unwrap(),
);
println!("Url: {}", url); let endpoint = String::from("api/v2/song/queue");
println!("Token: {}", access_token); let url = format!("{}/{endpoint}", self.api.url);
println!("Path: {:?}", song.song_path()); println!("Url: {url:?}");
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert( let (auth, auth_val) = syncers::common::auth_header(token).await;
reqwest::header::AUTHORIZATION, headers.insert(auth, auth_val);
HeaderValue::from_str(&access_token.clone()).unwrap(), headers.insert("Accept", HeaderValue::from_str("*/*").unwrap());
); headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap());
headers.insert(reqwest::header::ACCEPT, HeaderValue::from_static("*/*")); headers.insert("Cache-Control", HeaderValue::from_str("no-cache").unwrap());
let form = self.init_form(&song, &cover); let client = reqwest::Client::builder()
let client = reqwest::Client::builder().build().unwrap(); .timeout(std::time::Duration::from_secs(300))
.pool_idle_timeout(std::time::Duration::from_secs(30))
.build()
.unwrap();
match client match client
.post(url) .post(url)
.headers(headers) .headers(headers)
@@ -53,58 +79,226 @@ impl Upload {
.send() .send()
.await .await
{ {
Ok(r) => { Ok(response) => match response.json::<response::queue_song::Response>().await {
return Ok(r); Ok(resp) => {
} println!("Message: {:?}", resp.message);
Err(err) => { Ok(resp.data[0])
return Err(err); }
} Err(err) => Err(err),
},
Err(err) => Err(err),
} }
} }
fn init_form( pub async fn link_user_to_queued_song(
&self, &self,
song: &icarus_models::song::Song, token: &icarus_models::token::AccessToken,
cover: &icarus_models::coverart::CoverArt, queued_song_id: &uuid::Uuid,
) -> reqwest::multipart::Form { ) -> Result<(), reqwest::Error> {
let songpath = match song.song_path() { let endpoint = String::from("api/v2/song/queue/link");
Ok(s) => s, let url = format!("{}/{endpoint}", self.api.url);
Err(_) => String::new(),
};
let coverpath = cover.path.clone();
println!("Cover path: {:?}", coverpath);
let song_detail = match song.to_metadata_json(true) {
Ok(s) => s,
Err(_) => String::new(),
};
println!("\n{}\n", song_detail); let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
headers.insert(auth, auth_val);
let mut song_filename = String::from("audio"); let payload = serde_json::json!({
song_filename += icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION; "song_queue_id": queued_song_id,
let mut cover_filename = String::from("cover"); "user_id": token.user_id
cover_filename += icarus_models::constants::file_extensions::image::JPGEXTENSION; });
let form = reqwest::multipart::Form::new() let client = reqwest::Client::builder().build().unwrap();
.part(
"file",
reqwest::multipart::Part::bytes(std::fs::read(songpath).unwrap())
.file_name(song_filename),
)
.part(
"cover",
reqwest::multipart::Part::bytes(std::fs::read(coverpath).unwrap())
.file_name(cover_filename),
)
.text("metadata", song_detail);
return form; match client
.patch(url)
.headers(headers)
.json(&payload)
.send()
.await
{
Ok(_) => Ok(()),
Err(err) => Err(err),
}
} }
pub fn set_api(&mut self, host: &String) { pub async fn queue_metadata(
let mut api = models::api::API::default(); &self,
api.url = host.clone(); token: &icarus_models::token::AccessToken,
api.version = String::from("v1"); album: &icarus_models::album::collection::Album,
song: &icarus_models::song::Song,
queued_song_id: &uuid::Uuid,
) -> Result<uuid::Uuid, reqwest::Error> {
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::<response::queue_metadata::Response>().await {
Ok(resp) => {
println!("Message: {:?}", resp.message);
Ok(resp.data[0])
}
Err(err) => Err(err),
},
Err(err) => Err(err),
}
}
pub async fn queue_coverart(
&self,
token: &icarus_models::token::AccessToken,
coverart: &icarus_models::coverart::CoverArt,
) -> Result<uuid::Uuid, reqwest::Error> {
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::<response::queue_coverart::Response>().await {
Ok(resp) => {
println!("Message: {:?}", resp.message);
Ok(resp.data[0])
}
Err(err) => Err(err),
},
Err(err) => Err(err),
}
}
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),
}
}
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),
}
}
pub fn set_api(&mut self, host: &str) {
let api = models::api::Api {
url: host.to_owned(),
version: String::from(crate::parsers::api_parser::API_VERSION),
endpoint: String::new(),
};
self.api = api; self.api = api;
} }
} }
-29
View File
@@ -1,32 +1,3 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct Checks {}
impl Checks {
pub fn _is_numeric(text: &String) -> bool {
text.parse::<f64>().is_ok()
}
pub fn _index_of_item_in_container<F>(container: &String, item: &char, func: F) -> i32
where
F: Fn(&char, &char) -> bool,
{
let mut index = -1;
for c in container.chars() {
if func(&c, item) {
index += 1;
break;
}
index += 1;
}
return index;
}
}
pub fn exit_program(code: i32) { pub fn exit_program(code: i32) {
std::process::exit(code); std::process::exit(code);
} }
+1 -4
View File
@@ -1,9 +1,6 @@
pub fn o_to_string(val: &std::ffi::OsString) -> Result<std::string::String, std::io::Error> { pub fn o_to_string(val: &std::ffi::OsString) -> Result<std::string::String, std::io::Error> {
match val.clone().into_string() { match val.clone().into_string() {
Ok(value) => Ok(value), Ok(value) => Ok(value),
Err(_) => Err(std::io::Error::new( Err(_) => Err(std::io::Error::other(String::from("Error"))),
std::io::ErrorKind::Other,
String::from("Error"),
)),
} }
} }