diff --git a/Cargo.lock b/Cargo.lock
index a05ceb5..f22ea80 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -64,6 +70,26 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "audiotags"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44e797ce0164cf599c71f2c3849b56301d96a3dc033544588e875686b050ed39"
+dependencies = [
+ "audiotags-macro",
+ "id3",
+ "metaflac",
+ "mp4ameta",
+ "readme-rustdocifier",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "audiotags-macro"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaa9b2312fc01f7291f3b7b0f52ed08b1c0177c96a2e696ab55695cc4d06889"
+
[[package]]
name = "autocfg"
version = "1.4.0"
@@ -80,7 +106,7 @@ dependencies = [
"cc",
"cfg-if",
"libc",
- "miniz_oxide",
+ "miniz_oxide 0.7.4",
"object",
"rustc-demangle",
]
@@ -106,12 +132,6 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
-[[package]]
-name = "bitstream-io"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614aa3f2bac03707e62a84d18a48dd3d9ea6171313fd5e6a53b5054d8ae74601"
-
[[package]]
name = "bumpalo"
version = "3.17.0"
@@ -231,6 +251,15 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "crossterm"
version = "0.28.1"
@@ -424,6 +453,16 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+[[package]]
+name = "flate2"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide 0.8.4",
+]
+
[[package]]
name = "fnv"
version = "1.0.7"
@@ -629,6 +668,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "http"
version = "1.2.0"
@@ -888,6 +933,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "id3"
+version = "1.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "472295f55960dd48e38c89442fa5d5423f5cf0ed2c665485be78e129231a39e9"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "flate2",
+]
+
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -1078,19 +1134,19 @@ dependencies = [
name = "lyrica"
version = "0.1.0"
dependencies = [
+ "audiotags",
"chrono",
"color-eyre",
"crossterm",
"dirs",
"futures",
"itunesdb",
- "mp3-duration",
- "puremp3",
"rand",
"ratatui",
"regex",
"rusb",
"serde",
+ "serde-xml-rs",
"serde_json",
"soundcloud",
"throbber-widgets-tui",
@@ -1106,6 +1162,16 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+[[package]]
+name = "metaflac"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdf25a3451319c52a4a56d956475fbbb763bfb8420e2187d802485cb0fd8d965"
+dependencies = [
+ "byteorder",
+ "hex",
+]
+
[[package]]
name = "mime"
version = "0.3.17"
@@ -1121,6 +1187,15 @@ dependencies = [
"adler",
]
+[[package]]
+name = "miniz_oxide"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
+dependencies = [
+ "adler2",
+]
+
[[package]]
name = "mio"
version = "1.0.3"
@@ -1134,14 +1209,21 @@ dependencies = [
]
[[package]]
-name = "mp3-duration"
-version = "0.1.10"
+name = "mp4ameta"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "348bdc7300502f0801e5b57c448815713cd843b744ef9bda252a2698fdf90a0f"
+checksum = "eb23d62e8eb5299a3f79657c70ea9269eac8f6239a76952689bcd06a74057e81"
dependencies = [
- "thiserror 1.0.69",
+ "lazy_static",
+ "mp4ameta_proc",
]
+[[package]]
+name = "mp4ameta_proc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07dcca13d1740c0a665f77104803360da0bdb3323ecce2e93fa2c959a6d52806"
+
[[package]]
name = "native-tls"
version = "0.2.13"
@@ -1331,16 +1413,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "puremp3"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2b7efbb39e373af70c139e0611375fa6cad751fb93d528a610b55302710d883"
-dependencies = [
- "bitstream-io",
- "byteorder",
-]
-
[[package]]
name = "quote"
version = "1.0.38"
@@ -1402,6 +1474,12 @@ dependencies = [
"unicode-width 0.2.0",
]
+[[package]]
+name = "readme-rustdocifier"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08ad765b21a08b1a8e5cdce052719188a23772bcbefb3c439f0baaf62c56ceac"
+
[[package]]
name = "redox_syscall"
version = "0.5.8"
@@ -1637,6 +1715,18 @@ dependencies = [
"serde_derive",
]
+[[package]]
+name = "serde-xml-rs"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782"
+dependencies = [
+ "log",
+ "serde",
+ "thiserror 1.0.69",
+ "xml-rs",
+]
+
[[package]]
name = "serde_derive"
version = "1.0.217"
@@ -2527,6 +2617,12 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+[[package]]
+name = "xml-rs"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
+
[[package]]
name = "yoke"
version = "0.7.5"
diff --git a/Cargo.toml b/Cargo.toml
index 4bc8082..32df9fc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ dirs = "6.0.0"
toml = "0.8.20"
serde = "1.0.217"
serde_json = "1.0"
+serde-xml-rs = "0.6.0"
regex = "1.11.1"
ratatui = { version = "0.29.0", features = ["all-widgets"] }
color-eyre = "0.6.3"
@@ -21,8 +22,7 @@ tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7.12", features = ["codec"] }
soundcloud = { version = "0.1.8", git = "https://gitea.awain.net/alterwain/soundcloud_api.git" }
itunesdb = { version = "0.1.57", git = "https://gitea.awain.net/alterwain/ITunesDB.git" }
-puremp3 = "0.1.0"
-mp3-duration = "0.1.10"
rand = "0.8.5"
tui-big-text = "0.7.1"
throbber-widgets-tui = "0.8.0"
+audiotags = "0.5.0"
\ No newline at end of file
diff --git a/README.md b/README.md
index 61712d6..d7033b6 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Lightweight iPod manager, batteries included.
-#
+#
![]()
@@ -39,8 +39,8 @@ Lightweight iPod manager, batteries included.
## Todos
-- Implement scrollbox
-- Implement single song download
- Implement import from file system
- Implement artwork integration
-- Implement addition to fresh ipod (without any data)
\ No newline at end of file
+- Implement addition to fresh ipod (without any data)
+- Implement youtube api
+- Implement albums generation
\ No newline at end of file
diff --git a/src/dlp.rs b/src/dlp.rs
index 6716c63..1243148 100644
--- a/src/dlp.rs
+++ b/src/dlp.rs
@@ -1,4 +1,4 @@
-use std::{path::PathBuf, process::Stdio};
+use std::{io, path::PathBuf, process::Stdio};
use regex::Regex;
use serde::Deserialize;
@@ -22,7 +22,7 @@ pub async fn download_track_from_soundcloud(
track_url: &str,
download_dir: &PathBuf,
sender: Sender
,
-) -> std::result::Result<(), Box> {
+) -> io::Result<()> {
let _ = sender
.send(AppEvent::SwitchScreen(crate::AppState::LoadingScreen))
.await;
@@ -56,7 +56,7 @@ pub async fn download_track_from_soundcloud(
let stdout = child.stdout.take().unwrap();
let mut reader = BufReader::new(stdout).lines();
- while let Some(line) = reader.next_line().await? {
+ while let Ok(Some(line)) = reader.next_line().await {
if line.starts_with("{") {
let progress: DownloadProgress = serde_json::from_str(&line).unwrap();
let _ = sender.send(AppEvent::OverallProgress((0, 1))).await;
@@ -71,7 +71,7 @@ pub async fn download_from_soundcloud(
playlist_url: &str,
download_dir: &PathBuf,
sender: Sender,
-) -> std::result::Result<(), Box> {
+) -> io::Result<()> {
let _ = sender
.send(AppEvent::SwitchScreen(crate::AppState::LoadingScreen))
.await;
@@ -106,7 +106,7 @@ pub async fn download_from_soundcloud(
let stdout = child.stdout.take().unwrap();
let mut reader = BufReader::new(stdout).lines();
- while let Some(line) = reader.next_line().await? {
+ while let Ok(Some(line)) = reader.next_line().await {
match dl_rx.find(&line) {
Some(m) => {
let mut s = m.as_str();
diff --git a/src/file_system.rs b/src/file_system.rs
index 79c8d55..7a63791 100644
--- a/src/file_system.rs
+++ b/src/file_system.rs
@@ -8,16 +8,25 @@ use ratatui::prelude::{Line, Stylize};
use ratatui::widgets::Paragraph;
use ratatui::Frame;
use std::cmp::Ordering;
+use std::ffi::OsStr;
use std::fs::DirEntry;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use tokio::sync::mpsc::UnboundedSender;
pub struct FileSystem {
+ files: Vec,
table: SmartTable,
sender: UnboundedSender,
}
+fn check_extension_comptability(ext: &OsStr) -> bool {
+ matches!(
+ ext.to_str().unwrap().to_lowercase().as_str(),
+ "mp3" | "m4a" | "wav" | "aiff" | "aif" | "aac"
+ )
+}
+
impl AppScreen for FileSystem {
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
match key_event.code {
@@ -28,6 +37,9 @@ impl AppScreen for FileSystem {
.sender
.send(AppEvent::SwitchScreen(AppState::MainScreen));
}
+ KeyCode::F(5) => {
+ self.download_as_is();
+ }
_ => {}
}
}
@@ -47,9 +59,9 @@ impl AppScreen for FileSystem {
let status_bar = Paragraph::new(Line::from(vec![
" SWITCH TO NORMAL".bold(),
" | ".dark_gray(),
- " SAVE AS PLAYLIST".bold(),
+ " SAVE AS IS".bold(),
" | ".dark_gray(),
- " SAVE AS IS".bold(),
+ " SAVE AS PLAYLIST".bold(),
" | ".dark_gray(),
" SELECT".bold(),
" | ".dark_gray(),
@@ -81,7 +93,11 @@ impl FileSystem {
],
);
- let mut a = Self { table, sender };
+ let mut a = Self {
+ table,
+ sender,
+ files: Vec::new(),
+ };
a.get_path(dirs::document_dir().unwrap());
a
}
@@ -90,7 +106,12 @@ impl FileSystem {
let paths = std::fs::read_dir(&p).unwrap();
let mut dir = paths
.filter_map(|res| res.ok())
- .filter(|p| p.path().extension().map_or(false, |ext| ext == "mp3") || p.path().is_dir())
+ .filter(|p| {
+ p.path()
+ .extension()
+ .map_or(false, check_extension_comptability)
+ || p.path().is_dir()
+ })
.collect::>();
dir.sort_by(|a, _b| {
if a.file_type().unwrap().is_dir() {
@@ -100,7 +121,7 @@ impl FileSystem {
}
});
- let dir = dir
+ let data = dir
.iter()
.map(|entry| {
let datetime: DateTime = entry.metadata().unwrap().modified().unwrap().into();
@@ -116,11 +137,22 @@ impl FileSystem {
})
.collect::>>();
- self.table.set_data(dir);
+ self.files = dir;
+
+ self.table.set_data(data);
self.table
.set_title(p.iter().last().unwrap().to_str().unwrap().to_string());
}
+ fn download_as_is(&self) {
+ let entry = self.files.get(self.table.selected_row()).unwrap();
+ if entry.path().is_dir() {
+ todo!("Implement that later");
+ } else {
+ let _ = self.sender.send(AppEvent::LoadFromFS(entry.path()));
+ }
+ }
+
fn render_main(&self, frame: &mut Frame, area: Rect) {
self.table.render(frame, area);
}
diff --git a/src/sync.rs b/src/sync.rs
index a806c19..afa0a6a 100644
--- a/src/sync.rs
+++ b/src/sync.rs
@@ -1,7 +1,9 @@
+use audiotags::Tag;
use itunesdb::objects::{ListSortOrder, PlaylistItem};
use itunesdb::serializer;
use itunesdb::xobjects::{XDatabase, XPlArgument, XPlaylist, XTrackItem};
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists, CloudTrack};
+use std::error::Error;
use std::io::Write;
use std::path::{Path, PathBuf};
use tokio::{
@@ -29,6 +31,7 @@ pub enum AppEvent {
CurrentProgress(DownloadProgress),
OverallProgress((u32, u32)),
SwitchScreen(AppState),
+ LoadFromFS(PathBuf),
}
pub struct DBPlaylist {
@@ -38,23 +41,24 @@ pub struct DBPlaylist {
pub tracks: Vec,
}
-fn track_from_soundcloud(value: &CloudTrack) -> XTrackItem {
+async fn track_from_soundcloud(value: &CloudTrack) -> Option {
let mut track_path = get_temp_dl_dir();
track_path.push(value.id.to_string());
track_path.set_extension("mp3");
let f = std::fs::File::open(&track_path).unwrap();
let mut data = &std::fs::read(&track_path).unwrap()[..];
- let (header, _samples) = puremp3::read_mp3(data).unwrap();
-
- let duration = mp3_duration::from_read(&mut data).unwrap();
+ let audio_info = audio_file_info::from_path(track_path.to_str().unwrap())
+ .await
+ .unwrap();
+ let audio_info = audio_info.audio_file.tracks.get(0).unwrap();
let mut track = XTrackItem::new(
value.id as u32,
f.metadata().unwrap().len() as u32,
- duration.as_millis() as u32,
+ (audio_info.duration * 1000.0) as u32,
0,
- header.bitrate.bps() / 1000,
- header.sample_rate.hz(),
+ audio_info.bit_rate as u32,
+ audio_info.sample_rate as u32,
hash(),
0,
);
@@ -67,7 +71,7 @@ fn track_from_soundcloud(value: &CloudTrack) -> XTrackItem {
);
track.set_genre(value.genre.clone().unwrap());
track.update_arg(6, String::from("MPEG audio file"));
- track
+ Some(track)
}
// note: this hash function is used to make unique ids for each track. It doesn't aim to generate secure ones.
@@ -108,15 +112,16 @@ pub fn initialize_async_service(
AppEvent::SearchIPod => {
if let Some(p) = util::search_ipod() {
ipod_db = Some(p.clone());
- database = Some(parse_itunes(&sender, p).await);
let _ = sender.send(AppEvent::SwitchScreen(AppState::MainScreen)).await;
+ database = Some(parse_itunes(&sender, p).await);
} else {
let _ = sender.send(AppEvent::IPodNotFound).await;
}
},
- AppEvent::DownloadPlaylist(playlist) => download_playlist(playlist, database.as_mut().unwrap(), &sender, ipod_db.clone().unwrap()).await,
- AppEvent::DownloadTrack(track) => download_track(track, database.as_mut().unwrap(), &sender, ipod_db.clone().unwrap()).await,
+ AppEvent::DownloadPlaylist(playlist) => { download_playlist(playlist, database.as_mut().unwrap(), &sender, ipod_db.clone().unwrap()).await; },
+ AppEvent::DownloadTrack(track) => { download_track(track, database.as_mut().unwrap(), &sender, ipod_db.clone().unwrap()).await; },
AppEvent::SwitchScreen(state) => { let _ = sender.send(AppEvent::SwitchScreen(state)).await;},
+ AppEvent::LoadFromFS(path) => load_from_fs(path, database.as_mut().unwrap(), &sender, ipod_db.clone().unwrap()),
_ => {}
}
}
@@ -126,6 +131,51 @@ pub fn initialize_async_service(
});
}
+fn load_from_fs(
+ path: PathBuf,
+ database: &mut XDatabase,
+ sender: &Sender,
+ ipod_path: String,
+) {
+ let mut tag = Tag::new().read_from_path(path).unwrap();
+
+ let id = database.get_unique_id();
+
+ let mut track = XTrackItem::new(
+ id,
+ 0,
+ (tag.duration().unwrap() / 1000.0) as u32,
+ tag.year().unwrap() as u32,
+ 0,
+ 0,
+ hash(),
+ 0,
+ );
+ // TODO: implement check for every property
+ track.set_title(tag.title().unwrap().to_string());
+
+ let mut tp = PathBuf::new();
+ tp.push(":iPod_Control");
+ tp.push("Music");
+ tp.push(["F", &format!("{:02}", &(track.data.unique_id % 100))].concat());
+ tp.push(format!("{:X}", track.data.unique_id));
+ tp.set_extension("mp3");
+ track.set_location(
+ tp.to_str()
+ .unwrap()
+ .to_string()
+ .replace("/", ":")
+ .to_string(),
+ );
+ //track.update_arg(6);
+
+ track.set_genre(tag.genre().unwrap().to_string());
+ track.set_artist(tag.artist().unwrap().to_string());
+ track.set_album(tag.album().unwrap().title.to_string());
+
+ let cover = tag.album().unwrap().cover.unwrap();
+}
+
async fn download_track(
track: CloudTrack,
database: &mut XDatabase,
@@ -141,36 +191,37 @@ async fn download_track(
{
let p: PathBuf = Path::new(&ipod_path).into();
- let mut t: XTrackItem = track_from_soundcloud(&track);
- t.data.unique_id = database.get_unique_id();
- let mut tp = PathBuf::new();
- tp.push(":iPod_Control");
- tp.push("Music");
- tp.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
- tp.push(format!("{:X}", t.data.unique_id));
- tp.set_extension("mp3");
- t.set_location(
- tp.to_str()
- .unwrap()
- .to_string()
- .replace("/", ":")
- .to_string(),
- );
- let mut dest = p.clone();
- dest.push("iPod_Control");
- dest.push("Music");
- dest.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
- let _ = std::fs::create_dir_all(dest.to_str().unwrap());
- dest.push(format!("{:X}", t.data.unique_id));
- dest.set_extension("mp3");
+ if let Some(mut t) = track_from_soundcloud(&track).await {
+ t.data.unique_id = database.get_unique_id();
+ let mut tp = PathBuf::new();
+ tp.push(":iPod_Control");
+ tp.push("Music");
+ tp.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
+ tp.push(format!("{:X}", t.data.unique_id));
+ tp.set_extension("mp3");
+ t.set_location(
+ tp.to_str()
+ .unwrap()
+ .to_string()
+ .replace("/", ":")
+ .to_string(),
+ );
+ let mut dest = p.clone();
+ dest.push("iPod_Control");
+ dest.push("Music");
+ dest.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
+ let _ = std::fs::create_dir_all(dest.to_str().unwrap());
+ dest.push(format!("{:X}", t.data.unique_id));
+ dest.set_extension("mp3");
- let mut track_path = get_temp_dl_dir();
- track_path.push(track.id.to_string());
- track_path.set_extension("mp3");
+ let mut track_path = get_temp_dl_dir();
+ track_path.push(track.id.to_string());
+ track_path.set_extension("mp3");
- let _ = std::fs::copy(track_path.to_str().unwrap(), dest.to_str().unwrap());
+ let _ = std::fs::copy(track_path.to_str().unwrap(), dest.to_str().unwrap());
- database.add_track(t);
+ database.add_track(t);
+ }
}
let _ = sender
@@ -206,37 +257,38 @@ async fn download_playlist(
if track.title.is_none() {
continue;
}
- let mut t: XTrackItem = track_from_soundcloud(&track);
- t.data.unique_id = database.get_unique_id();
- new_playlist.add_elem(t.data.unique_id);
- let mut tp = PathBuf::new();
- tp.push(":iPod_Control");
- tp.push("Music");
- tp.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
- tp.push(format!("{:X}", t.data.unique_id));
- tp.set_extension("mp3");
- t.set_location(
- tp.to_str()
- .unwrap()
- .to_string()
- .replace("/", ":")
- .to_string(),
- );
- let mut dest = p.clone();
- dest.push("iPod_Control");
- dest.push("Music");
- dest.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
- let _ = std::fs::create_dir_all(dest.to_str().unwrap());
- dest.push(format!("{:X}", t.data.unique_id));
- dest.set_extension("mp3");
+ if let Some(mut t) = track_from_soundcloud(&track).await {
+ t.data.unique_id = database.get_unique_id();
+ new_playlist.add_elem(t.data.unique_id);
+ let mut tp = PathBuf::new();
+ tp.push(":iPod_Control");
+ tp.push("Music");
+ tp.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
+ tp.push(format!("{:X}", t.data.unique_id));
+ tp.set_extension("mp3");
+ t.set_location(
+ tp.to_str()
+ .unwrap()
+ .to_string()
+ .replace("/", ":")
+ .to_string(),
+ );
+ let mut dest = p.clone();
+ dest.push("iPod_Control");
+ dest.push("Music");
+ dest.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
+ let _ = std::fs::create_dir_all(dest.to_str().unwrap());
+ dest.push(format!("{:X}", t.data.unique_id));
+ dest.set_extension("mp3");
- let mut track_path = get_temp_dl_dir();
- track_path.push(track.id.to_string());
- track_path.set_extension("mp3");
+ let mut track_path = get_temp_dl_dir();
+ track_path.push(track.id.to_string());
+ track_path.set_extension("mp3");
- let _ = std::fs::copy(track_path.to_str().unwrap(), dest.to_str().unwrap());
+ let _ = std::fs::copy(track_path.to_str().unwrap(), dest.to_str().unwrap());
- database.add_track(t);
+ database.add_track(t);
+ }
}
database.add_playlist(new_playlist);
@@ -335,3 +387,49 @@ async fn parse_itunes(sender: &Sender, path: String) -> XDatabase {
database
}
+
+mod audio_file_info {
+ use serde::{Deserialize, Serialize};
+ use std::process::Stdio;
+ use tokio::io::{AsyncReadExt, BufReader};
+ use tokio::process::Command;
+
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ pub struct AudioInfo {
+ pub audio_file: AudioFileInfo,
+ }
+
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ pub struct AudioFileInfo {
+ pub file_name: String,
+ pub file_type: String,
+ pub tracks: Vec,
+ }
+
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ pub struct AudioFileTrack {
+ pub num_channels: u32,
+ pub sample_rate: u64,
+ pub format_type: String,
+ pub audio_bytes: u64,
+ pub duration: f64,
+ pub bit_rate: u64,
+ }
+
+ pub async fn from_path(p: &str) -> Option {
+ let mut command = Command::new("afinfo");
+ command.args(vec!["-x", p]);
+ command.stdout(Stdio::piped());
+ command.stderr(Stdio::null());
+
+ let mut child = command.spawn().unwrap();
+
+ let mut str = String::new();
+ let stdout = child.stdout.take().unwrap();
+ let size = BufReader::new(stdout)
+ .read_to_string(&mut str)
+ .await
+ .unwrap();
+ Some(serde_xml_rs::from_str(&str).unwrap())
+ }
+}