Language Migration #21

Merged
kdeng00 merged 47 commits from rust-lg into master 2024-06-15 12:22:18 -04:00
19 changed files with 93 additions and 150 deletions
Showing only changes of commit 188a81beca - Show all commits
+1 -1
View File
@@ -10,8 +10,8 @@ edition = "2021"
[dependencies]
futures = { version = "0.3.30" }
reqwest = { version = "0.12.4", features = ["json", "blocking", "multipart", "stream"] }
http = { version = "1.1.0" }
reqwest = { version = "0.12.4", features = ["json", "blocking", "multipart", "stream"] }
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115"
tokio = { version = "1.37", features = ["full"] }
+19 -44
View File
@@ -1,86 +1,67 @@
# IcarusDownloadManager
IcarusDownloadManager is a Linux CLI software client application that has the feature of uploading and downloading songs from the [Icarus](https://github.com/kdeng00/Icarus) Music Server.
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.
## Built With
* C++ with C++20 features
* CMake
* GCC >= 10 or Visual Studio >= 17 [2022]
* [vcpkg](https://github.com/microsoft/vcpkg)
* [json](https://github.com/nlohmann/json)
* [openssl](https://github.com/openssl/openssl)
* [curl](https://github.com/curl/curl)
* [cpr](https://github.com/libcpr/cpr)
* Rust
* Cargo
* futures
* http
* reqwst
* serde
* serde_json
* tokio
* tokio-util
### Getting Started
Clone the repo
```
```BASH
git clone --recursive https://github.com/kdeng00/IcarusDownloadManager
```
Install packages
```
vcpkg install nlohman-json
vcpkg install openssl
vcpkg install curl
vcpkg install cpr
```
Build the project:
```
```BASH
cd IcarusDownloadManager
mkdir build
cd build
cmake ..
cmake --build . --config release -j
cargo build
```
The program has been built and can be executed by the binary file *icd*. For information on how to use icd, 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
```BASH
icd download -u spacecadet -p stellar40 -h https://icarus.com -b 15
```
### Uploading Song
```BASH
icd upload -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.mp3
icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -b 15
```
### Uploading Song with metadata
```BASH
icd upload-meta -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.mp3 -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 -s /path/of/song.mp3 -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png
```
### Uploading Song with metadata from directory
```BASH
icd 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 -smca /path/where/songs/and/metadata/exists/
```
### Retrieving Song in json
```Bash
icd retrieve -u spacecadet -p stellar40 -h https://icarus.com -rt songs
icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -rt songs
```
### Deleting Song
```BASH
icd delete -u spacecadet -p stellar40 -h https://icarus.com -D 15
icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -D 15
```
@@ -97,12 +78,6 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduc
[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)
## Authors
* **Kun Deng** - [kdeng00](https://github.com/kdeng00)
See also the list of [contributors](https://github.com/kdeng00/Icarus/graphs/contributors) who participated in this project.
## License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
+1 -1
View File
@@ -1,3 +1,3 @@
pub const WAV_FILE_EXTENSION: &str = ".wav";
pub const MP3_FILE_EXTENSION: &str = ".mp3";
pub const _MP3_FILE_EXTENSION: &str = ".mp3";
pub const JPG_FILE_EXTENSION: &str = ".jpg";
-7
View File
@@ -18,7 +18,6 @@ fn print_help() {
Actions
download
upload (Search NOTE)
upload-meta
retrieve
delete
@@ -29,12 +28,6 @@ fn print_help() {
-p password
-h host
Required for upload
-s path of song
-sd directory where to search for songs to upload (Optional)
-sr directory where to recursively search for songs to upload (Optional)
-nc will not prompt the user when uploading from a directory
Required for upload with metadata
-s path of song
-t track number
+2 -2
View File
@@ -124,7 +124,7 @@ impl ActionManager {
}
}
fn print_action(&self) {
fn _print_action(&self) {
if self.action.len() == 0 {
println!("Action is empty");
} else {
@@ -132,7 +132,7 @@ impl ActionManager {
}
}
fn print_flags(&self) {
fn _print_flags(&self) {
println!("Printing flags...");
for flag in &self.flags {
println!("Flag {}", flag.flag);
+25 -20
View File
@@ -1,21 +1,18 @@
use std::collections::HashMap;
use std::default::Default;
use std::fmt::Display;
use std::fs::{read_dir, DirEntry};
use std::io::{Error, Read, Result, Write};
use std::path::Path;
use std::fs::read_dir;
use std::io::{Result, Write};
use std::str::FromStr;
use futures::{FutureExt, TryFutureExt};
use serde::{Deserialize, Serialize};
use tokio::runtime::Runtime;
use crate::managers;
use crate::models::song::Album;
use crate::models::{self, song};
use crate::models::{self};
use crate::syncers;
use crate::utilities;
use crate::{constants, parsers};
use crate::{exit_program, managers};
#[derive(Debug, Deserialize, Serialize)]
pub struct CommitManager {
@@ -32,7 +29,7 @@ enum ActionValues {
None,
}
enum RetrieveTypes {
enum _RetrieveTypes {
Songs,
}
@@ -45,7 +42,7 @@ enum En {
}
impl Album {
pub fn print_info(&self) {
pub fn _print_info(&self) {
println!("Album: {}", self.title);
println!("Album Artist: {}", self.album_artist);
println!("Genre: {}", self.genre);
@@ -126,7 +123,6 @@ impl CommitManager {
return actions;
}
// TODO: Implement
fn delete_song(&self) {
let mut prsr = parsers::api_parser::APIParser {
ica_act: self.ica_action.clone(),
@@ -150,11 +146,20 @@ impl CommitManager {
}
}
let del = syncers::delete::Delete { api: api };
let mut del = syncers::delete::Delete { api: api.clone() };
println!("Deleting song..");
del.delete_song(&token, &song);
let res_fut = del.delete_song(&token, &song);
let result = Runtime::new().unwrap().block_on(res_fut);
match result {
Ok(o) => {
println!("Song (Id {:?}) has been successfully deleted", o.id);
}
Err(er) => {
println!("Error {:?}", er);
}
}
}
fn download_song(&self) {
@@ -213,8 +218,8 @@ impl CommitManager {
let result = Runtime::new().unwrap().block_on(result_fut);
match result {
Ok(o) => {
for song in o {
println!("{:?} - {:?}", song.artist, song.title);
for son in o {
son.print_info();
}
}
Err(er) => {
@@ -280,9 +285,9 @@ impl CommitManager {
println!("metadata path: {}", metadata_path);
println!("cover art path: {}", coverpath);
self.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath);
let _ = self.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath);
} else if multitarget {
self.multi_target_upload(&uni);
let _ = self.multi_target_upload(&uni);
} else {
println!("Single or Multi target has not been chosen");
}
@@ -681,8 +686,8 @@ impl CommitManager {
song.track = Some(track);
}
fn parse_disc_and_track(&self, song: &mut models::song::Song, track_id: &String) {
let sep = |a: &char, b: &char| -> bool {
fn _parse_disc_and_track(&self, song: &mut models::song::Song, track_id: &String) {
let sep = |_a: &char, _b: &char| -> bool {
return false;
};
@@ -690,7 +695,7 @@ impl CommitManager {
if index == -1 {
let mut d_str: String = String::new();
let mut t_str = String::new();
let t_str = String::new();
for c in track_id.chars().skip(0).take(index as usize) {
d_str.push(c);
@@ -712,7 +717,7 @@ impl CommitManager {
}
}
fn check_for_no_confirm(&self) -> bool {
fn _check_for_no_confirm(&self) -> bool {
for flag in self.ica_action.flags.iter() {
if flag.flag == "-nc" {
return true;
+3 -8
View File
@@ -24,9 +24,7 @@ impl TokenManager {
pub async fn request_token(&self) -> Result<models::token::Token, std::io::Error> {
println!("Sending request for a token");
// let endpoint = self.construct_endpoint();
let mut url = self.retrieve_url();
// url += &endpoint;
let url = self.retrieve_url();
println!("URL: {}", url);
@@ -59,7 +57,7 @@ impl TokenManager {
}
pub fn init(&mut self) {
let mut api = &mut self.api;
let api = &mut self.api;
api.version = String::from("v1");
api.endpoint = String::from(format!("api/{}/login", api.version));
}
@@ -67,9 +65,6 @@ impl TokenManager {
pub fn retrieve_url(&self) -> String {
let api = &self.api;
let mut url = String::from(&api.url);
// url += &String::from("api/");
// url += &String::from(&api.version);
// url += &String::from("/");
url += &String::from(&api.endpoint);
url += &String::from("/");
@@ -77,7 +72,7 @@ impl TokenManager {
}
// NOTE: This can get deleted. Redundant
fn construct_endpoint(&self) -> String {
fn _construct_endpoint(&self) -> String {
let mut endpoint: String = String::from("api/");
endpoint += &self.api.version;
endpoint += "/login";
+1 -1
View File
@@ -2,7 +2,7 @@ use std::default::Default;
use serde::{Deserialize, Serialize};
use crate::models::{self, user::User};
use crate::models::{self};
#[derive(Debug, Deserialize, Serialize)]
pub struct UserManager {
+4 -3
View File
@@ -9,6 +9,7 @@ use crate::constants;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Song {
#[serde(alias = "song_id")]
pub id: Option<i32>,
pub title: Option<String>,
pub artist: Option<String>,
@@ -111,7 +112,7 @@ impl Song {
}
// if 1 - wav, if 0 - mp3, anything else defaults to wav
pub fn generate_filename_from_track(&mut self, i_type: i32) -> i32 {
pub fn _generate_filename_from_track(&mut self, i_type: i32) -> i32 {
let mut filename: String = String::new();
if self.track.unwrap() < 10 {
filename += "0";
@@ -120,7 +121,7 @@ impl Song {
filename += &self.track.unwrap().to_string();
if i_type == 0 {
filename += constants::file_extensions::MP3_FILE_EXTENSION;
filename += constants::file_extensions::_MP3_FILE_EXTENSION;
} else {
filename += constants::file_extensions::WAV_FILE_EXTENSION;
}
@@ -130,7 +131,7 @@ impl Song {
return 0;
}
pub fn to_metadata_json(&self) -> Result<String, serde_json::Error> {
pub fn _to_metadata_json(&self) -> Result<String, serde_json::Error> {
return serde_json::to_string_pretty(&self);
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ impl Default for User {
}
impl User {
pub fn to_json(&self) -> Result<String, serde_json::Error> {
pub fn _to_json(&self) -> Result<String, serde_json::Error> {
return serde_json::to_string_pretty(&self);
}
}
+1 -5
View File
@@ -15,22 +15,18 @@ impl APIParser {
let flags = self.ica_act.flags.clone();
println!("Parsing api");
let mut i = 0;
// for (i, elem) in flags {
for elem in flags {
let arg = elem.flag;
let value = elem.value;
if arg == "-h" {
if value.chars().nth((value.len() - 1)) == Some('/') {
if value.chars().nth(value.len() - 1) == Some('/') {
self.api.url = value;
} else {
self.api.url = value + "/";
}
break;
}
i += 1;
}
self.api.version = "v1".to_string();
+20 -9
View File
@@ -1,16 +1,9 @@
use std::default::Default;
use std::io::Error;
use reqwest;
use reqwest::Response;
use serde;
// use serde::Deserialize;
// use serde::Serialize;
use crate::models;
use super::syncer_base;
#[derive(Clone, Debug)]
pub struct Delete {
pub api: models::api::API,
@@ -25,9 +18,14 @@ impl Default for Delete {
}
impl Delete {
pub async fn delete_song(&self, token: &models::token::Token, song: &models::song::Song) {
pub async fn delete_song(
&mut self,
token: &models::token::Token,
song: &models::song::Song,
) -> Result<models::song::Song, std::io::Error> {
self.api.endpoint = "song/data/delete".to_owned();
let url = self.retrieve_url(&song);
let client = reqwest::Client::new();
let client = reqwest::Client::builder().build().unwrap();
let access_token = token.bearer_token();
let response = client
.delete(&url)
@@ -35,15 +33,28 @@ impl Delete {
.send()
.await
.unwrap();
let mut sng = models::song::Song::default();
match response.status() {
reqwest::StatusCode::OK => {
println!("Success!");
let s = response.json::<models::song::Song>().await;
match s {
//
Ok(parsed) => {
sng = parsed;
}
Err(er) => {
println!("Error {:?}", er);
}
};
}
other => {
panic!("Issue occurred: {:?}", other);
}
}
return Ok(sng);
}
fn retrieve_url(&self, song: &models::song::Song) -> String {
+1 -1
View File
@@ -16,7 +16,7 @@ impl Default for Download {
#[derive(Debug)]
pub enum MyError {
Request(reqwest::Error),
_Request(reqwest::Error),
Other(String),
}
-1
View File
@@ -1,5 +1,4 @@
pub mod delete;
pub mod download;
pub mod retrieve_records;
pub mod syncer_base;
pub mod upload;
-2
View File
@@ -3,8 +3,6 @@ use std::io::Error;
use crate::models;
// use super::syncer_base::Result;
pub struct RetrieveRecords {
pub api: models::api::API,
}
-28
View File
@@ -1,28 +0,0 @@
// use crate::models;
pub struct Syncer {
// pub api: models::api::API,
ok: i32,
unauthorized: i32,
not_found: i32,
}
impl Syncer {
pub fn ok() -> i32 {
return 200;
}
pub fn unauthorized() -> i32 {
return 401;
}
pub fn not_found() -> i32 {
return 404;
}
}
pub enum Result {
OK,
UNAUTHORIZED,
NOTFOUND,
}
+3 -4
View File
@@ -5,7 +5,6 @@ use http::HeaderValue;
use reqwest;
use reqwest::multipart::Form;
use serde::{Deserialize, Serialize};
use tokio::fs::File;
use crate::constants;
use crate::models;
@@ -112,7 +111,7 @@ impl Upload {
return Ok(response_text);
}
fn initialize_form(
fn _initialize_form(
&self,
song_raw_data: Vec<u8>,
cover_raw_data: Vec<u8>,
@@ -124,14 +123,14 @@ impl Upload {
http::HeaderValue::from_static("application/octet-stream"),
);
let mut file = reqwest::multipart::Part::bytes(song_raw_data).headers(headers);
let file = reqwest::multipart::Part::bytes(song_raw_data).headers(headers);
let mut headers_i = HeaderMap::new();
headers_i.insert(
http::header::CONTENT_TYPE,
http::HeaderValue::from_static("image/jpeg"),
);
let mut cover = reqwest::multipart::Part::bytes(cover_raw_data).headers(headers_i);
let cover = reqwest::multipart::Part::bytes(cover_raw_data).headers(headers_i);
let mut song_filename = String::from("audio");
song_filename += constants::file_extensions::WAV_FILE_EXTENSION;
+8 -9
View File
@@ -9,24 +9,23 @@ impl Checks {
}
// TODO: Implement
pub fn item_in_container(
container: &Vec<String>,
item: &String,
func: fn(a: &String, b: &String) -> String,
pub fn _item_in_container(
_container: &Vec<String>,
_item: &String,
_func: fn(a: &String, b: &String) -> String,
) -> String {
return String::from("");
}
// TODO: Implement
pub fn item_iter_in_container(
container: &Vec<String>,
item: &String,
func: fn(a: &String, b: &String) -> String,
pub fn _item_iter_in_container(
_container: &Vec<String>,
_item: &String,
_func: fn(a: &String, b: &String) -> String,
) -> String {
return String::from("");
}
// TODO: Implement
pub fn index_of_item_in_container<F>(container: &String, item: &char, func: F) -> i32
where
F: Fn(&char, &char) -> bool,
+3 -3
View File
@@ -4,15 +4,15 @@ use serde::{Deserialize, Serialize};
pub struct Conversions {}
impl Conversions {
pub fn to_lower_char(val: &mut char) {
pub fn _to_lower_char(val: &mut char) {
if val.is_alphabetic() {
*val = val.to_lowercase().next().unwrap();
}
}
pub fn initialize_values(&self) {}
pub fn _initialize_values(&self) {}
pub fn print_value<T: std::fmt::Debug>(&self, val: T) {
pub fn _print_value<T: std::fmt::Debug>(&self, val: T) {
println!("Going to print value");
println!("{:?}", val);
}