FS start
This commit is contained in:
parent
4d89b9e187
commit
a64f77c2a4
142
Cargo.lock
generated
142
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
@ -4,7 +4,7 @@
|
||||
|
||||
Lightweight iPod manager, batteries included.
|
||||
|
||||
#
|
||||
#
|
||||
|
||||
<div align="center">
|
||||
<img src=""/>
|
||||
@ -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)
|
||||
- Implement addition to fresh ipod (without any data)
|
||||
- Implement youtube api
|
||||
- Implement albums generation
|
10
src/dlp.rs
10
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<AppEvent>,
|
||||
) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
) -> 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<AppEvent>,
|
||||
) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
) -> 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();
|
||||
|
@ -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<DirEntry>,
|
||||
table: SmartTable,
|
||||
sender: UnboundedSender<AppEvent>,
|
||||
}
|
||||
|
||||
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![
|
||||
"<F4> SWITCH TO NORMAL".bold(),
|
||||
" | ".dark_gray(),
|
||||
"<F5> SAVE AS PLAYLIST".bold(),
|
||||
"<F5> SAVE AS IS".bold(),
|
||||
" | ".dark_gray(),
|
||||
"<F6> SAVE AS IS".bold(),
|
||||
"<F6> SAVE AS PLAYLIST".bold(),
|
||||
" | ".dark_gray(),
|
||||
"<F8> 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::<Vec<DirEntry>>();
|
||||
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<Utc> = entry.metadata().unwrap().modified().unwrap().into();
|
||||
@ -116,11 +137,22 @@ impl FileSystem {
|
||||
})
|
||||
.collect::<Vec<Vec<String>>>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
230
src/sync.rs
230
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<XTrackItem>,
|
||||
}
|
||||
|
||||
fn track_from_soundcloud(value: &CloudTrack) -> XTrackItem {
|
||||
async fn track_from_soundcloud(value: &CloudTrack) -> Option<XTrackItem> {
|
||||
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<AppEvent>,
|
||||
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<AppEvent>, 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<AudioFileTrack>,
|
||||
}
|
||||
|
||||
#[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<AudioInfo> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user