18 Commits
Author SHA1 Message Date
phoenix e2b6549c2d tsk-100: Updating workflow
Rust / build (pull_request) Failing after 8s
2026-07-10 18:44:26 -04:00
phoenix 34c055a30e tsk-100: Updating name 2026-07-10 18:44:13 -04:00
phoenix 489d13227b Update rust (#99)
Rust / build (pull_request) Failing after 4s
* bump: rust

* bump: rust ci

* Updated License

* bump: reqwest

* bump: tokio

* bump: uuid

* bump: icarus_models

* cargo update

* bump: icarus_dm
2026-05-05 21:05:40 -04:00
phoenix 0e6438cf0d Updated rust (#95)
* Updated rust version in workflow

* Using variable in workflow file

* Updating packages

* cargo update

* Version bump
2026-04-05 17:14:50 -04:00
KD 00615532ce icarus_models version bump (#93)
* icarus_models version bump

* Build fix

* Removed method

* Some refactoring

* More cleanup

* Version bump

* Using standard version
2025-10-30 15:33:09 -04:00
KD abd16eeba2 tsk-79: Simplifying functions (#92)
* tsk-79: Changed return type of function and cleaned up other functions

* tsk-79: Version bump

* tsk-79: Code formatting
2025-10-27 12:42:42 -04:00
KD 5c9fcf2ec5 tsk-83: Add token code prior to action execution (#91)
* tsk-83: Code cleanup

* tsk-83: Added code to get token before an action begins

* tsk-83: Small fix

* tsk-83: Version bump

* tsk-83: Cleanup
2025-10-26 16:39:16 -04:00
KD 6bedab0d88 tsk-82: Refactor validate_args() method (#90)
* tsk-82: Refactored validate_flags() method

* tsk-82: Warning fix

* tsk-82: Code formatting

* tsk-82: Version bump

* tsk-82: Forgot this

* tsk-82: Fix
2025-10-24 20:51:33 -04:00
KD 686e0d8f1f tsk-81: Simplify downloading song (#89)
* tsk-81: Using method in Song to save to filesystem

* tsk-81: icaurs_models version bump

* tsk-81: Code formatting

* tsk-81: icarus_models version bump

* tsk-81: Version bump
2025-10-24 13:34:55 -04:00
KD 05a9134aaa tsk-80: Removing unused serde references (#88)
* tsk-80: Removing unused serde references

* tsk-80: Version bump

* tsk-80: Formatting code
2025-10-23 21:10:08 -04:00
KD 3cadac4efd tsk-78: Updating returned error on converting ostring to string (#87)
* tsk-78: Making error to get string from o string more robust

* tsk-78: Version bump
2025-10-23 16:52:26 -04:00
KD 2188c2b746 tsk-77: Print version of the software (#86)
* tsk-77: Added code to print version

* tsk-77: Removed print statement

* tsk-77: Code formatting

* tsk-77: Version bump

* tsk-77: Updated help
2025-10-22 21:40:12 -04:00
KD 9328f3d3da tsk-76: Remove songs directory (#85)
* tsk-76: Removed songs directory

* tsk-76: Version bump
2025-10-22 20:59:20 -04:00
KD ca13f101d7 icarus_models version bump (#84)
* icarus_models version bump

* icarus_models related changes

* Removing method

* Code formatting

* Warning fix

* Removing comments

* Version bump

* cargo update
2025-10-19 23:52:43 -04:00
KD 1e4ffc798e Use official icarus_models version (#75)
* icarus_models v0.7.0

* Version bump
2025-10-13 20:58:30 -04:00
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
22 changed files with 1279 additions and 917 deletions
+5 -5
View File
@@ -2,9 +2,9 @@ name: Rust
on:
push:
branches: [ "master" ]
branches: [ "main" ]
pull_request:
branches: [ "master", "icarus_v2_support" ]
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
@@ -15,9 +15,9 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Rust version
run: rustup install 1.88.0 && rustup default 1.88.0
run: rustup install 1.95 && rustup default 1.95
- name: Debug secret
run: echo "${{ secrets.MYREPO_TOKEN }}" | head -c 10 ; echo "..."
@@ -27,7 +27,7 @@ jobs:
mkdir -p ~/.ssh
echo "$EXTREPO_KEY" > ~/.ssh/gitlab_deploy_key
chmod 600 ~/.ssh/gitlab_deploy_key
ssh-keyscan git.kundeng.us ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLl/OZiKVDxwnyvMxa+rjKvDpKqTxH1GWuGuDPLmENGQMbTVulajZWr9x8Q1cotoJiHZkt7DA5vczcjB/4lwgWA= >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.MYHOST }}= >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/gitlab_deploy_key
Generated
+1076 -613
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -1,17 +1,17 @@
[package]
name = "icarus-dm"
version = "0.7.0"
rust-version = "1.88"
name = "idm"
version = "0.10.0"
rust-version = "1.95"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = { version = "0.3.31" }
http = { version = "1.3.1" }
reqwest = { version = "0.12.20", features = ["json", "blocking", "multipart", "stream"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
tokio = { version = "1.45.1", features = ["full"] }
tokio-util = { version = "0.7.15", features = ["codec"] }
uuid = { version = "1.17.0", features = ["v4", "serde"] }
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.6" }
futures = { version = "0.3.32" }
http = { version = "1.4.0" }
reqwest = { version = "0.13.3", features = ["json", "blocking", "multipart", "stream"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
tokio = { version = "1.52", features = ["full"] }
tokio-util = { version = "0.7.18", features = ["codec"] }
uuid = { version = "1.23.1", features = ["v4", "serde"] }
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.11.1" }
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Kun Deng
Copyright (c) 2026 Kun Deng
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+9 -27
View File
@@ -1,72 +1,54 @@
# IcarusDownloadManager
# idm
IcarusDownloadManager is a CLI software client application that has the feature of uploading and downloading songs from the [Icarus](https://github.com/kdeng00/Icarus) Music Server.
idm (IcarusDownloadManager) is a CLI software client application that has the feature of uploading and downloading songs from the [icarus](https://git.kundeng.us/phoenix/icarus) Music Server.
## Built With
* Rust
* Cargo
* futures
* http
* reqwst
* serde
* serde_json
* tokio
* tokio-util
* icarus_models
### Getting Started
Clone the repo
```BASH
git clone git@github.com:kdeng00/IcarusDownloadManager.git
git clone git@git.kundeng.us:phoenix/idm.git
```
Build the project:
```BASH
cd IcarusDownloadManager
cd idm
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.
### Downloading Song
```BASH
icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -b e8407fc6-edd2-44c1-993f-08dd7324d91a
idm download -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -b e8407fc6-edd2-44c1-993f-08dd7324d91a
```
### Uploading Song with metadata
```BASH
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
idm 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 -ha https://auth.icarus.com -smca /path/where/songs/and/metadata/exists/
idm 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
```Bash
icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -rt songs
idm retrieve -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -rt songs
```
### Deleting Song
```BASH
icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -D e8407fc6-edd2-44c1-993f-08dd7324d91a
idm delete -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -D e8407fc6-edd2-44c1-993f-08dd7324d91a
```
View File
+4 -1
View File
@@ -30,7 +30,10 @@ pub fn print_help() {
-rt retrieve type (songs is only accepted)
Required for deleting a song
-D song id"#,
-D song id
Print version
-v"#,
);
println!("{msg}");
+9 -3
View File
@@ -4,6 +4,7 @@ mod models;
mod parsers;
mod syncers;
mod utilities;
mod version;
use std::env;
@@ -17,17 +18,22 @@ async fn main() {
utilities::checks::exit_program(-1);
}
println!("Argument count: {args_len}");
let mut act_mgr = managers::action_managers::ActionManager::default();
act_mgr.set_params(&args);
act_mgr.initialize();
let chosen_act = act_mgr.retrieve_icarus_action();
for act in &chosen_act.flags {
if act.flag == "-v" {
version::print_version();
utilities::checks::exit_program(-1);
}
}
chosen_act.print_action_and_flags();
let mut cmt_mgr = managers::commit_manager::CommitManager {
ica_action: chosen_act,
..Default::default()
};
cmt_mgr.commit_action().await;
@@ -63,7 +69,7 @@ mod tests {
match managers::commit_manager::retrieve_song(
&album, track, disc, &directory, &filename,
) {
Ok(song) => match song.to_data() {
Ok(song) => match icarus_models::song::io::to_data(&song) {
Ok(_) => {
print!("Success");
}
+20 -21
View File
@@ -1,8 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::{models, utilities};
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug)]
pub struct ActionManager {
pub action: String,
pub flags: Vec<models::flags::Flags>,
@@ -48,6 +46,7 @@ impl ActionManager {
String::from("-ca"),
String::from("-smca"),
String::from("-t"),
String::from("-v"),
]
}
@@ -63,31 +62,24 @@ impl ActionManager {
}
fn validate_flags(&mut self) {
println!("Validating flags");
let flag_vals = self.parsed_flags();
let mut i = 0;
println!("Flag count: {}", flag_vals.len());
while i < flag_vals.len() {
let flag = &flag_vals[i];
println!("Index: {i} | Value: {flag}");
let mut flg = models::flags::Flags::default();
// TODO: Refactor this
if self.is_valid_flag(flag) && self.does_flag_have_value(flag) {
println!("Flag has value");
flg.flag = String::from(flag);
flg.value = String::from(&flag_vals[i + 1]);
if self.is_valid_flag(flag) {
if self.does_flag_have_value(flag) {
flg.flag = String::from(flag);
flg.value = String::from(&flag_vals[i + 1]);
i += 1;
} else if self.is_valid_flag(flag) {
println!("Flag does not have a value");
flg.flag = String::from(flag);
i += 1;
} else {
flg.flag = String::from(flag);
}
} else {
println!("Flag {flag} is not valid");
utilities::checks::exit_program(-1);
}
@@ -155,9 +147,16 @@ impl ActionManager {
fn parsed_flags(&self) -> Vec<String> {
let mut parsed: Vec<String> = Vec::new();
for i in 2..self.params.len() {
let flag = String::from(&self.params[i]);
parsed.push(flag);
if self.param_count <= 2 {
let flag = String::from(&self.params[1]);
if flag == "-v" {
parsed.push(flag);
}
} else {
for i in 2..self.params.len() {
let flag = String::from(&self.params[i]);
parsed.push(flag);
}
}
parsed
+107 -152
View File
@@ -1,20 +1,20 @@
use std::collections::HashMap;
use std::default::Default;
use std::fs::read_dir;
use std::io::{Result, Write};
use std::io::Result;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::managers;
use crate::models::{self};
use crate::parsers;
use crate::syncers;
use crate::utilities;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Default)]
pub struct CommitManager {
pub ica_action: models::icarus_action::IcarusAction,
pub api: models::api::Api,
pub auth_api: models::api::Api,
}
#[derive(Clone, Debug)]
@@ -86,27 +86,36 @@ pub fn retrieve_song(
impl CommitManager {
pub async fn commit_action(&mut self) {
self.init_api().await;
let action = &self.ica_action.action;
println!("Committing {action} action");
let mapped_actions = &self.map_actions();
let mapped_action = self.find_mapped_action(mapped_actions, action);
let token = self.parse_token(&self.auth_api).await;
println!("{mapped_action:?}");
// TODO: Move code to get token here and then pass it to the respective functions
match mapped_action {
ActionValues::DeleteAct => self.delete_song().await,
ActionValues::DownloadAct => self.download_song().await,
ActionValues::RetrieveAct => self.retrieve_object().await,
ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata().await,
match self.find_mapped_action(mapped_actions, action) {
ActionValues::DeleteAct => self.delete_song(&token).await,
ActionValues::DownloadAct => self.download_song(&token).await,
ActionValues::RetrieveAct => self.retrieve_object(&token).await,
ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata(&token).await,
_ => {
println!("Nothing good here");
}
}
}
async fn init_api(&mut self) {
let mut prsr = parsers::api_parser::APIParser {
apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(),
};
prsr.parse_api(parsers::api_parser::APIType::Main);
prsr.parse_api(parsers::api_parser::APIType::Auth);
self.api = prsr.retrieve_api(parsers::api_parser::APIType::Main);
self.auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth);
}
fn find_mapped_action(
&self,
actions: &HashMap<String, ActionValues>,
@@ -122,7 +131,7 @@ impl CommitManager {
}
fn map_actions(&self) -> HashMap<String, ActionValues> {
let actions: HashMap<String, ActionValues> = HashMap::from([
HashMap::from([
("download".to_string(), ActionValues::DownloadAct),
("download".to_string(), ActionValues::DownloadAct),
(
@@ -131,24 +140,15 @@ impl CommitManager {
),
("retrieve".to_string(), ActionValues::RetrieveAct),
("delete".to_string(), ActionValues::DeleteAct),
]);
actions
])
}
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(),
};
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;
async fn delete_song(&self, token: &icarus_models::token::AccessToken) {
println!("Deleting song");
let mut del = syncers::delete::Delete {
api: self.api.clone(),
};
let mut song = icarus_models::song::Song::default();
for arg in &self.ica_action.flags {
@@ -160,12 +160,7 @@ impl CommitManager {
}
}
let api = prsr.retrieve_api(parsers::api_parser::APIType::Main);
let mut del = syncers::delete::Delete { api: api.clone() };
println!("Deleting song..");
match del.delete_song(&token, &song).await {
match del.delete_song(token, &song).await {
Ok((song, _coverart)) => {
println!("Song (Id {:?}) has been successfully deleted", song.id);
}
@@ -175,39 +170,42 @@ impl CommitManager {
}
}
async fn download_song(&self) {
println!("Deleting song");
async fn download_song(&self, token: &icarus_models::token::AccessToken) {
println!("Downloading song");
let dwn = self.ica_action.retrieve_flag_value(&String::from("-b"));
let song_id = uuid::Uuid::from_str(dwn.as_str()).unwrap();
let mut prsr = parsers::api_parser::APIParser {
apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(),
let mut dwn_loader = syncers::download::Download {
api: self.api.clone(),
};
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!("Message: {}", token.message);
let api = prsr.retrieve_api(parsers::api_parser::APIType::Main);
let mut dwn_loader = syncers::download::Download { api: api.clone() };
let song = icarus_models::song::Song {
id: song_id,
let mut song = icarus_models::song::Song {
id: uuid::Uuid::from_str(dwn.as_str()).unwrap(),
..Default::default()
};
match dwn_loader.download_song(&token, &song).await {
match dwn_loader.download_song(token, &song).await {
Ok(o) => {
println!("Success");
let filename = String::from("audio")
+ icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION;
let data = o.as_bytes();
let mut file = std::fs::File::create(filename).expect("Failed to save");
file.write_all(data)
.expect("Failed to save downloaded song");
println!("Song downloaded");
song.data = o.as_bytes().to_vec();
song.directory = String::from(".");
song.filename = match icarus_models::song::generate_filename(
icarus_models::types::MusicType::FlacExtension,
true,
) {
Ok(filename) => filename,
Err(err) => {
eprintln!("Error generating song filename: {err:?}");
utilities::checks::exit_program(-3);
return;
}
};
match song.save_to_filesystem() {
Ok(_) => {
println!("Song saved");
}
Err(err) => {
eprintln!("Error saving song: {err:?}");
}
}
}
Err(er) => {
println!("Error {er:?}");
@@ -223,7 +221,7 @@ impl CommitManager {
}
}
async fn retrieve_object(&self) {
async fn retrieve_object(&self, token: &icarus_models::token::AccessToken) {
println!("Retrieving song");
let rt = self.ica_action.retrieve_flag_value(&String::from("-rt"));
@@ -231,22 +229,11 @@ impl CommitManager {
panic!("Unsupported -rt: {rt}");
}
let mut prsr = parsers::api_parser::APIParser {
apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(),
let mut repo = syncers::retrieve_records::RetrieveRecords {
api: self.api.clone(),
};
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 mut repo = syncers::retrieve_records::RetrieveRecords { api: api.clone() };
match repo.get_all_songs(&token).await {
match repo.get_all_songs(token).await {
Ok(o) => {
println!("Songs");
println!("=====");
@@ -285,7 +272,7 @@ impl CommitManager {
token.unwrap()
}
async fn upload_song_with_metadata(&mut self) {
async fn upload_song_with_metadata(&mut self, token: &icarus_models::token::AccessToken) {
println!("Uplodaring song with metadara");
let songpath = self.ica_action.retrieve_flag_value(&String::from("-s"));
@@ -313,12 +300,12 @@ impl CommitManager {
println!("cover art path: {coverpath}");
let _ = self
.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath)
.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath, token)
.await;
} else if multitarget {
let _ = self.multi_target_upload(&uni).await;
let _ = self.multi_target_upload(&uni, token).await;
} else {
println!("Single or Multi target has not been chosen");
eprintln!("Single or Multi target has not been chosen");
}
}
@@ -329,19 +316,8 @@ impl CommitManager {
track_id: &str,
meta_path: &String,
cover_path: &str,
token: &icarus_models::token::AccessToken,
) -> Result<()> {
let mut prsr = parsers::api_parser::APIParser {
apis: vec![models::api::Api::default(), models::api::Api::default()],
ica_act: self.ica_action.clone(),
};
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.token);
let song_file = std::path::Path::new(&songpath);
if !song_file.exists() {
@@ -349,11 +325,11 @@ impl CommitManager {
panic!("Error");
}
let pa = std::path::Path::new(&cover_path);
let mut cover_art = icarus_models::coverart::CoverArt {
id: uuid::Uuid::nil(),
title: String::new(),
path: cover_path.to_owned(),
data: Vec::new(),
directory: String::from(pa.parent().unwrap().to_str().unwrap()),
filename: String::from(pa.file_name().unwrap().to_str().unwrap()),
..Default::default()
};
let file_name = std::ffi::OsString::from(&song_file.file_name().unwrap());
@@ -373,15 +349,15 @@ impl CommitManager {
println!("Directory: {:?}", s.directory);
println!("Filename: {:?}", s.filename);
println!("Path: {:?}", s.song_path());
s.data = s.to_data().unwrap();
cover_art.data = cover_art.to_data().unwrap();
s.data = icarus_models::song::io::to_data(&s).unwrap();
cover_art.data =
icarus_models::coverart::io::to_data(&cover_art).unwrap();
let members = UploadSongMembers {
song: s,
coverart: cover_art,
token: token,
album: album,
token: token.clone(),
album,
};
match self.upload_song_process(&members).await {
@@ -458,21 +434,14 @@ impl CommitManager {
println!("Queued coverart Id: {id:?}");
println!("Linked queued song to queued coverart");
println!("Queued status updated");
Ok(())
}
Err(err) => {
return Err(std::io::Error::other(err.to_string()));
}
Err(err) => Err(std::io::Error::other(err.to_string())),
},
Err(err) => {
return Err(std::io::Error::other(err.to_string()));
}
Err(err) => Err(std::io::Error::other(err.to_string())),
},
Err(err) => {
return Err(std::io::Error::other(err.to_string()));
}
Err(err) => Err(std::io::Error::other(err.to_string())),
}
Ok(())
}
fn get_songs(
@@ -496,7 +465,6 @@ impl CommitManager {
};
songs.push(icarus_models::song::Song {
id: uuid::Uuid::nil(),
title: track.title.clone(),
artist: track.artist.clone(),
disc: track.disc,
@@ -511,50 +479,46 @@ impl CommitManager {
audio_type: String::from("FLAC"),
directory: source_directory.to_owned(),
filename: song_filename,
user_id: uuid::Uuid::nil(),
data: Vec::new(),
date_created: String::new(),
..Default::default()
});
}
Ok(songs)
}
Err(_) => Ok(Vec::new()),
Err(_) => Err(std::io::Error::other("Songs not retrieved")),
}
}
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(),
};
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;
async fn multi_target_upload(
&mut self,
sourcepath: &String,
token: &icarus_models::token::AccessToken,
) -> std::io::Result<()> {
let directory_path = std::path::Path::new(&sourcepath);
if !directory_path.exists() {
panic!("Directory does not exist");
}
let coverart_path = self.get_cover_art_path(sourcepath).unwrap_or_default();
let mut cover_art =
icarus_models::coverart::init::init_coverart_only_path(coverart_path.clone());
let (coverart_directory, coverart_filename) =
self.get_coverart_dir_and_filename(sourcepath).unwrap();
let mut cover_art = icarus_models::coverart::init::init_coverart_dir_and_filename(
&coverart_directory,
&coverart_filename,
);
let metadatapath = self.get_metadata_path(sourcepath).unwrap_or_default();
let mut up = syncers::upload::Upload::default();
let host = self.ica_action.retrieve_flag_value(&String::from("-h"));
up.set_api(&host);
cover_art.data = cover_art.to_data().unwrap();
cover_art.data = icarus_models::coverart::io::to_data(&cover_art).unwrap();
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: song,
song,
coverart: cover_art.clone(),
token: token.clone(),
album: album.clone(),
@@ -565,7 +529,7 @@ impl CommitManager {
println!("Response: {o:?}");
}
Err(err) => {
println!("Error: {err:?}");
eprintln!("Error: {err:?}");
return Err(err);
}
}
@@ -574,19 +538,19 @@ impl CommitManager {
Ok(())
}
Err(err) => {
println!("Error: {err:?}");
eprintln!("Error: {err:?}");
Err(std::io::Error::other(err.to_string()))
}
},
Err(error) => {
println!("Error: {error:?}");
eprintln!("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))? {
fn get_coverart_dir_and_filename(&self, directory: &str) -> Result<(String, String)> {
for entry in read_dir(std::path::Path::new(directory))? {
let entry = entry?;
let file_type = entry.file_type();
@@ -596,14 +560,14 @@ impl CommitManager {
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);
return Ok((directory.to_string(), fname.unwrap()));
}
}
Ok(String::new())
Err(std::io::Error::other(
"CoverArt directory and filename not found",
))
}
fn find_file_extension(&self, file_name: &std::ffi::OsString) -> En {
@@ -671,15 +635,6 @@ impl CommitManager {
}
}
Ok(String::new())
}
fn _check_for_no_confirm(&self) -> bool {
for flag in self.ica_action.flags.iter() {
if flag.flag == "-nc" {
return true;
}
}
false
Err(std::io::Error::other("Metadata path not found"))
}
}
+5 -12
View File
@@ -23,7 +23,6 @@ impl Default for TokenManager {
user: icarus_models::user::User::default(),
api: models::api::Api::default(),
};
token.init();
token
@@ -35,7 +34,6 @@ impl TokenManager {
println!("Sending request for a token");
let url = self.retrieve_url();
println!("URL: {url}");
let mut token = icarus_models::token::AccessToken {
@@ -62,19 +60,18 @@ impl TokenManager {
token.token_type = login_result.token_type.clone();
token.expiration = login_result.expiration;
token.message = response.message;
Ok(token)
}
Err(_) => println!("Hm, the response didn't match the shape we expected."),
};
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
reqwest::StatusCode::UNAUTHORIZED => {
println!("Need to grab a new token");
Err(std::io::Error::other("Need to grab a new token"))
}
other => {
panic!("Uh oh! Something unexpected happened: {other:?}");
}
}
Ok(token)
}
pub fn init(&mut self) {
@@ -84,10 +81,6 @@ impl TokenManager {
}
pub fn retrieve_url(&self) -> String {
let api = &self.api;
let mut url = String::from(&api.url);
url += &String::from(&api.endpoint);
url
format!("{}{}", self.api.url, self.api.endpoint)
}
}
+1 -3
View File
@@ -1,10 +1,8 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
use crate::models::{self};
#[derive(Debug, Default, Deserialize, Serialize)]
#[derive(Debug, Default)]
pub struct UserManager {
pub user: icarus_models::user::User,
pub ica_action: models::icarus_action::IcarusAction,
+1 -3
View File
@@ -1,8 +1,6 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default)]
pub struct Api {
pub url: String,
pub endpoint: String,
+1 -3
View File
@@ -1,8 +1,6 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default)]
pub struct Flags {
pub flag: String,
pub value: String,
+1 -3
View File
@@ -1,10 +1,8 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
use crate::models;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default)]
pub struct IcarusAction {
pub action: String,
pub flags: Vec<models::flags::Flags>,
-1
View File
@@ -1,4 +1,3 @@
pub mod api;
pub mod flags;
pub mod icarus_action;
pub mod upload_form;
-9
View File
@@ -1,9 +0,0 @@
use std::default::Default;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct UploadForm {
pub url: Option<String>,
pub filepath: Option<String>,
}
+7 -11
View File
@@ -9,22 +9,18 @@ pub fn retrieve_url(api: &models::api::Api, with_id: bool, id: &uuid::Uuid) -> S
}
fn retrieve_url_reg(api: &models::api::Api) -> String {
let url = format!("{}api/{}/{}/", api.url, api.version, api.endpoint);
url
format!("{}api/{}/{}/", api.url, api.version, api.endpoint)
}
fn retrieve_url_with_id(api: &models::api::Api, id: &uuid::Uuid) -> String {
let url = format!("{}api/{}/{}/{}", api.url, api.version, api.endpoint, id);
url
format!("{}api/{}/{}/{}", api.url, api.version, api.endpoint, id)
}
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)
) -> Result<(http::HeaderName, http::HeaderValue), http::header::InvalidHeaderValue> {
match http::HeaderValue::from_str(&token.bearer_token()) {
Ok(auth_value) => Ok((reqwest::header::AUTHORIZATION, auth_value)),
Err(err) => Err(err),
}
}
+7 -7
View File
@@ -61,7 +61,7 @@ impl Upload {
println!("Url: {url:?}");
let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
let (auth, auth_val) = syncers::common::auth_header(token).await.unwrap();
headers.insert(auth, auth_val);
headers.insert("Accept", HeaderValue::from_str("*/*").unwrap());
headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap());
@@ -99,7 +99,7 @@ impl Upload {
let url = format!("{}/{endpoint}", self.api.url);
let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
let (auth, auth_val) = syncers::common::auth_header(token).await.unwrap();
headers.insert(auth, auth_val);
let payload = serde_json::json!({
@@ -132,7 +132,7 @@ impl Upload {
let url = format!("{}/{endpoint}", self.api.url);
let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
let (auth, auth_val) = syncers::common::auth_header(token).await.unwrap();
headers.insert(auth, auth_val);
let payload = serde_json::json!({
@@ -176,7 +176,7 @@ impl Upload {
token: &icarus_models::token::AccessToken,
coverart: &icarus_models::coverart::CoverArt,
) -> Result<uuid::Uuid, reqwest::Error> {
let coverartpath = coverart.path.clone();
let coverartpath = coverart.get_path().unwrap();
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);
@@ -200,7 +200,7 @@ impl Upload {
println!("Url: {url:?}");
let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
let (auth, auth_val) = syncers::common::auth_header(token).await.unwrap();
headers.insert(auth, auth_val);
headers.insert("Accept", HeaderValue::from_str("*/*").unwrap());
headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap());
@@ -239,7 +239,7 @@ impl Upload {
let url = format!("{}/{endpoint}", self.api.url);
let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
let (auth, auth_val) = syncers::common::auth_header(token).await.unwrap();
headers.insert(auth, auth_val);
let payload = serde_json::json!({
@@ -271,7 +271,7 @@ impl Upload {
let url = format!("{}/{endpoint}", self.api.url);
let mut headers = reqwest::header::HeaderMap::new();
let (auth, auth_val) = syncers::common::auth_header(token).await;
let (auth, auth_val) = syncers::common::auth_header(token).await.unwrap();
headers.insert(auth, auth_val);
let payload = serde_json::json!({
-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: &str) -> bool {
text.parse::<f64>().is_ok()
}
pub fn _index_of_item_in_container<F>(container: &str, 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;
}
index
}
}
pub fn exit_program(code: i32) {
std::process::exit(code);
}
+7 -1
View File
@@ -1,6 +1,12 @@
pub fn o_to_string(val: &std::ffi::OsString) -> Result<std::string::String, std::io::Error> {
match val.clone().into_string() {
Ok(value) => Ok(value),
Err(_) => Err(std::io::Error::other(String::from("Error"))),
Err(err) => match err.into_string() {
Ok(res) => Err(std::io::Error::other(res.to_string())),
Err(err) => match err.to_str() {
Some(err) => Err(std::io::Error::other(err)),
None => Err(std::io::Error::other("Undefined error")),
},
},
}
}
+6
View File
@@ -0,0 +1,6 @@
pub fn print_version() {
let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
println!("{name:?} {version:?}");
}