diff --git a/Cargo.lock b/Cargo.lock index d193a05..4ff420d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -433,7 +433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1021,8 +1021,8 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "itunesdb" -version = "0.1.1" -source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#5a6ca7a9f5eca42959e3498a6eb2700f502b5509" +version = "0.1.2" +source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#2db99df934c29f03b842a43245f1e93d7d4ade27" dependencies = [ "bincode", "env_logger", @@ -1662,7 +1662,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2029,7 +2029,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2532,7 +2532,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index abde97d..13802b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ tokio = { version = "1", features = ["full"] } tokio-util = { version = "0.7.12", features = ["codec"] } strum = { version = "0.27", features = ["derive"] } soundcloud = { version = "0.1.4", git = "https://gitea.awain.net/alterwain/soundcloud_api.git" } -itunesdb = { version = "0.1.1", git = "https://gitea.awain.net/alterwain/ITunesDB.git" } +itunesdb = { version = "0.1.2", git = "https://gitea.awain.net/alterwain/ITunesDB.git" } ureq = "3.0.5" color-thief = "0.2" redb = "2.4.0" diff --git a/src/db.rs b/src/db.rs index 4013485..e3ed651 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,5 +1,6 @@ use std::fs::File; +use itunesdb::xobjects::{XArgument, XTrackItem}; use md5::{Digest, Md5}; use redb::{Database, Error, ReadableTable, TableDefinition}; use serde::{Deserialize, Serialize}; @@ -66,6 +67,37 @@ impl From for Track { } } +fn find_str_arg(value: &XTrackItem, arg_type: u32) -> Option<&XArgument> { + value.args.iter().find(|arg| arg.arg_type == arg_type) +} + +impl From for Track { + fn from(value: XTrackItem) -> Self { + Track { + unique_id: value.data.unique_id, + filetype: value.data.filetype, + stars: value.data.stars, + last_modified_time: value.data.last_modified_time, + size: value.data.size, + length: value.data.length, + year: value.data.year, + bitrate: value.data.bitrate, + sample_rate: value.data.sample_rate, + play_count: value.data.play_count, + dbid: value.data.dbid, + bpm: value.data.bpm, + skip_count: value.data.skip_count, + has_artwork: value.data.has_artwork, + media_type: value.data.media_type, + title: find_str_arg(&value, 1).map_or(String::new(), |a| a.val.clone()), + location: find_str_arg(&value, 2).map_or(String::new(), |a| a.val.clone()), + album: find_str_arg(&value, 3).map_or(String::new(), |a| a.val.clone()), + artist: find_str_arg(&value, 4).map_or(String::new(), |a| a.val.clone()), + genre: find_str_arg(&value, 5).map_or(String::new(), |a| a.val.clone()), + } + } +} + // TODO: implement From (or Into) for Track, convert from Soundcloud Audio or iTunes pub fn init_db() -> Database { diff --git a/src/main_screen.rs b/src/main_screen.rs index d7116a8..e3d66e3 100644 --- a/src/main_screen.rs +++ b/src/main_screen.rs @@ -263,6 +263,32 @@ impl MainScreen { } v } + 2 => { + // local + /* let mut v = Vec::new(); + v.push( + Row::new(vec!["Id", "Title", "Artist", "Bitrate", "Hash"]) + .style(Style::default().fg(Color::Gray)), + ); + if let Some(s) = &self.soundcloud { + for (i, playlist) in s.iter().enumerate() { + let date: DateTime = playlist.created_at.parse().unwrap(); + let mut row = Row::new(vec![ + playlist.id.to_string(), + playlist.title.clone(), + [playlist.track_count.to_string(), " songs".to_string()].concat(), + format!("{}", date.format("%Y-%m-%d %H:%M")), + "NO".to_string(), + ]); + if self.selected_row == i as i32 { + row = row.style(Style::default().bg(Color::Yellow)); + } + v.push(row); + } + } + v*/ + Vec::new() + } _ => Vec::new(), }; diff --git a/src/sync.rs b/src/sync.rs index 78d47df..5fdb0fa 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; -use itunesdb::xobjects::XDatabase; +use itunesdb::xobjects::{XDatabase, XSomeList}; +use redb::Database; use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists}; use tokio::{ fs::File, @@ -55,49 +56,8 @@ pub fn initialize_async_service( }*/ let _ = sender.send(AppEvent::IPodFound("/Users/michael/Documents/ipod/iTunes/iTunesDB".to_string())).await; }, - AppEvent::ParseItunes(path) => { - // todo: parse itunes - let cd = get_temp_itunesdb(); - let p: PathBuf = Path::new(&path).into(); - // p.push("iPod_Control"); - // p.push("iTunes"); - // p.set_file_name("iTunesDB"); - let _ = std::fs::copy(p, &cd); - let mut file = File::open(cd).await.unwrap(); - let mut contents = vec![]; - file.read_to_end(&mut contents).await.unwrap(); - let xdb = itunesdb::deserializer::parse_bytes(&contents); - let _ = sender.send(AppEvent::ITunesParsed(xdb)).await; - - let p = get_config_path(); - if !p.exists() { - let config = LyricaConfiguration::default(); - let cfg_str = toml::to_string_pretty(&config).unwrap(); - let mut file = File::create(&p).await.unwrap(); - file.write(cfg_str.as_bytes()).await; - } - let mut file = File::open(p).await.unwrap(); - let mut content = String::new(); - file.read_to_string(&mut content).await.unwrap(); - let config: LyricaConfiguration = toml::from_str(&content).unwrap(); - - let app_version = soundcloud::get_app().await.unwrap().unwrap(); - let client_id = soundcloud::get_client_id().await.unwrap().unwrap(); - let playlists = soundcloud::get_playlists(config.get_soundcloud().user_id, client_id, app_version).await.unwrap(); - - let _ = sender.send(AppEvent::SoundcloudGot(playlists)).await; - }, - AppEvent::DownloadPlaylist(playlist) => { - if let Ok(()) = dlp::download_from_soundcloud(&playlist.permalink_url, &get_temp_dl_dir(), sender.clone()).await { - let tracks = playlist.tracks; - for track in tracks { - if track.title.is_none() { continue; } - let mut t: Track = track.into(); - t.unique_id = db::get_last_track_id(&database).unwrap_or(80) + 1; - let _ = db::insert_track(&database, t); - } - } - }, + AppEvent::ParseItunes(path) => parse_itunes(&database, &sender, path).await, + AppEvent::DownloadPlaylist(playlist) => download_playlist(playlist, &database, &sender).await, _ => {} } } @@ -106,3 +66,68 @@ pub fn initialize_async_service( } }); } + +async fn download_playlist( + playlist: CloudPlaylist, + database: &Database, + sender: &Sender, +) { + if let Ok(()) = + dlp::download_from_soundcloud(&playlist.permalink_url, &get_temp_dl_dir(), sender.clone()) + .await + { + let tracks = playlist.tracks; + for track in tracks { + if track.title.is_none() { + continue; + } + let mut t: Track = track.into(); + t.unique_id = db::get_last_track_id(database).unwrap_or(80) + 1; + let _ = db::insert_track(database, t); + } + } +} + +async fn parse_itunes(database: &Database, sender: &Sender, path: String) { + // todo: parse itunes + let cd = get_temp_itunesdb(); + let p: PathBuf = Path::new(&path).into(); + // p.push("iPod_Control"); + // p.push("iTunes"); + // p.set_file_name("iTunesDB"); + let _ = std::fs::copy(p, &cd); + let mut file = File::open(cd).await.unwrap(); + let mut contents = vec![]; + file.read_to_end(&mut contents).await.unwrap(); + let mut xdb = itunesdb::deserializer::parse_bytes(&contents); + + if let XSomeList::TrackList(tracks) = &xdb.find_dataset(1).child { + for track in tracks { + let t: Track = track.clone().into(); + let _ = db::insert_track(database, t); + } + } + + let _ = sender.send(AppEvent::ITunesParsed(xdb)).await; + + let p = get_config_path(); + if !p.exists() { + let config = LyricaConfiguration::default(); + let cfg_str = toml::to_string_pretty(&config).unwrap(); + let mut file = File::create(&p).await.unwrap(); + file.write(cfg_str.as_bytes()).await; + } + let mut file = File::open(p).await.unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).await.unwrap(); + let config: LyricaConfiguration = toml::from_str(&content).unwrap(); + + let app_version = soundcloud::get_app().await.unwrap().unwrap(); + let client_id = soundcloud::get_client_id().await.unwrap().unwrap(); + let playlists = + soundcloud::get_playlists(config.get_soundcloud().user_id, client_id, app_version) + .await + .unwrap(); + + let _ = sender.send(AppEvent::SoundcloudGot(playlists)).await; +}