test
This commit is contained in:
parent
3a77aff9f1
commit
b29ef4901e
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -403,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -975,8 +975,8 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itunesdb"
|
name = "itunesdb"
|
||||||
version = "0.1.6"
|
version = "0.1.11"
|
||||||
source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#eac020520b7efa2173065772105ab0b2c4ba6da6"
|
source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#4204fdcda886438d815da7b70f736506e06a22e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@ -1081,7 +1081,6 @@ dependencies = [
|
|||||||
"md-5",
|
"md-5",
|
||||||
"puremp3",
|
"puremp3",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"redb",
|
|
||||||
"regex",
|
"regex",
|
||||||
"rusb",
|
"rusb",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1395,15 +1394,6 @@ dependencies = [
|
|||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redb"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ea0a72cd7140de9fc3e318823b883abf819c20d478ec89ce880466dc2ef263c6"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
@ -1538,7 +1528,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1881,7 +1871,7 @@ dependencies = [
|
|||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2339,7 +2329,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -21,7 +21,6 @@ futures = "0.3"
|
|||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tokio-util = { version = "0.7.12", features = ["codec"] }
|
tokio-util = { version = "0.7.12", features = ["codec"] }
|
||||||
soundcloud = { version = "0.1.8", git = "https://gitea.awain.net/alterwain/soundcloud_api.git" }
|
soundcloud = { version = "0.1.8", git = "https://gitea.awain.net/alterwain/soundcloud_api.git" }
|
||||||
itunesdb = { version = "0.1.6", git = "https://gitea.awain.net/alterwain/ITunesDB.git" }
|
itunesdb = { version = "0.1.11", git = "https://gitea.awain.net/alterwain/ITunesDB.git" }
|
||||||
redb = "2.4.0"
|
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
puremp3 = "0.1.0"
|
puremp3 = "0.1.0"
|
||||||
|
235
src/db.rs
235
src/db.rs
@ -1,235 +0,0 @@
|
|||||||
use std::fs::File;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use itunesdb::xobjects::{XArgument, XPlaylist, XTrackItem};
|
|
||||||
use md5::{Digest, Md5};
|
|
||||||
use redb::{Database, Error, ReadableTable, TableDefinition};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use soundcloud::sobjects::CloudTrack;
|
|
||||||
|
|
||||||
use crate::config::{get_db, get_temp_dl_dir};
|
|
||||||
|
|
||||||
const TRACKS: TableDefinition<u32, Vec<u8>> = TableDefinition::new("tracks");
|
|
||||||
const PLAYLISTS: TableDefinition<u64, Vec<u8>> = TableDefinition::new("playlists");
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Track {
|
|
||||||
pub unique_id: u32,
|
|
||||||
filetype: u32,
|
|
||||||
stars: u8,
|
|
||||||
last_modified_time: u32,
|
|
||||||
size: u32,
|
|
||||||
pub length: u32,
|
|
||||||
year: u32,
|
|
||||||
pub bitrate: u32,
|
|
||||||
sample_rate: u32,
|
|
||||||
play_count: u32,
|
|
||||||
pub dbid: u64,
|
|
||||||
bpm: u16,
|
|
||||||
skip_count: u32,
|
|
||||||
has_artwork: u8,
|
|
||||||
media_type: u32,
|
|
||||||
pub title: String,
|
|
||||||
pub location: String,
|
|
||||||
album: String,
|
|
||||||
pub artist: String,
|
|
||||||
pub genre: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct DBPlaylist {
|
|
||||||
pub persistent_playlist_id: u64,
|
|
||||||
pub title: String,
|
|
||||||
pub timestamp: u32,
|
|
||||||
pub is_master: bool,
|
|
||||||
pub tracks: Vec<Track>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
pub struct Playlist {
|
|
||||||
pub persistent_playlist_id: u64,
|
|
||||||
pub title: String,
|
|
||||||
pub timestamp: u32,
|
|
||||||
pub is_master: bool,
|
|
||||||
pub tracks: Vec<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CloudTrack> for Track {
|
|
||||||
fn from(value: CloudTrack) -> Self {
|
|
||||||
let mut track_path = get_temp_dl_dir();
|
|
||||||
track_path.push(value.id.to_string());
|
|
||||||
track_path.set_extension("mp3");
|
|
||||||
let f = File::open(&track_path).unwrap();
|
|
||||||
let data = &std::fs::read(&track_path).unwrap()[..];
|
|
||||||
let (header, _samples) = puremp3::read_mp3(data).unwrap();
|
|
||||||
Track {
|
|
||||||
unique_id: 0,
|
|
||||||
filetype: 0,
|
|
||||||
stars: 0,
|
|
||||||
last_modified_time: 0,
|
|
||||||
size: f.metadata().unwrap().len() as u32,
|
|
||||||
length: 0,
|
|
||||||
year: 0,
|
|
||||||
bitrate: header.bitrate.bps() / 1000,
|
|
||||||
sample_rate: header.sample_rate.hz(),
|
|
||||||
play_count: 0,
|
|
||||||
dbid: hash(data),
|
|
||||||
bpm: 0,
|
|
||||||
skip_count: 0,
|
|
||||||
has_artwork: 0,
|
|
||||||
media_type: 0,
|
|
||||||
title: value.title.unwrap(),
|
|
||||||
location: String::new(),
|
|
||||||
album: String::new(),
|
|
||||||
artist: value
|
|
||||||
.user
|
|
||||||
.map_or(String::new(), |a| a.username.unwrap_or(a.permalink)),
|
|
||||||
genre: value.genre.unwrap_or_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_str_arg(value: &XTrackItem, arg_type: u32) -> Option<&XArgument> {
|
|
||||||
value.args.iter().find(|arg| arg.arg_type == arg_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<XTrackItem> 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()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_db() -> Database {
|
|
||||||
Database::create(get_db()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_playlist(db: &Database, playlist: Playlist) -> Result<(), Error> {
|
|
||||||
let write_txn = db.begin_write()?;
|
|
||||||
{
|
|
||||||
let mut table = write_txn.open_table(PLAYLISTS)?;
|
|
||||||
let uid = playlist.persistent_playlist_id;
|
|
||||||
let data = bincode::serialize(&playlist).unwrap();
|
|
||||||
table.insert(uid, data)?;
|
|
||||||
}
|
|
||||||
write_txn.commit()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_playlist(db: &Database, id: u64) -> Result<DBPlaylist, Error> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
let table = read_txn.open_table(PLAYLISTS)?;
|
|
||||||
let b = table.get(id)?.unwrap().value();
|
|
||||||
let value: Playlist = bincode::deserialize(&b).unwrap();
|
|
||||||
let playlist = DBPlaylist {
|
|
||||||
persistent_playlist_id: value.persistent_playlist_id,
|
|
||||||
timestamp: value.timestamp,
|
|
||||||
title: value.title,
|
|
||||||
is_master: value.is_master,
|
|
||||||
tracks: value.tracks.iter().map(|id| get_track(db, *id)).filter(|t| t.is_ok()).map(|t| t.unwrap()).collect(),
|
|
||||||
};
|
|
||||||
Ok(playlist.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_all_playlists(db: &Database) -> Result<Vec<DBPlaylist>, Error> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
let table = read_txn.open_table(PLAYLISTS)?;
|
|
||||||
Ok(table
|
|
||||||
.iter()
|
|
||||||
.unwrap()
|
|
||||||
.flatten()
|
|
||||||
.map(|d| bincode::deserialize(&d.1.value()).unwrap())
|
|
||||||
.collect::<Vec<Playlist>>()
|
|
||||||
.iter()
|
|
||||||
.map(|p| DBPlaylist{
|
|
||||||
persistent_playlist_id: p.persistent_playlist_id,
|
|
||||||
timestamp: p.timestamp,
|
|
||||||
title: p.title.clone(),
|
|
||||||
is_master: p.is_master,
|
|
||||||
tracks: p.tracks.iter().map(|id| get_track(db, *id)).filter(|t| t.is_ok()).map(|t| t.unwrap()).collect()
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_track(db: &Database, track: Track) -> Result<(), Error> {
|
|
||||||
let write_txn = db.begin_write()?;
|
|
||||||
{
|
|
||||||
let mut table = write_txn.open_table(TRACKS)?;
|
|
||||||
let uid = track.unique_id;
|
|
||||||
let data = bincode::serialize(&track).unwrap();
|
|
||||||
table.insert(uid, data)?;
|
|
||||||
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
let table = read_txn.open_table(PLAYLISTS)?;
|
|
||||||
|
|
||||||
let pls = table
|
|
||||||
.iter()
|
|
||||||
.unwrap()
|
|
||||||
.flatten()
|
|
||||||
.map(|d| bincode::deserialize(&d.1.value()).unwrap())
|
|
||||||
.collect::<Vec<Playlist>>();
|
|
||||||
|
|
||||||
for pl in pls {
|
|
||||||
if !pl.is_master { continue }
|
|
||||||
let mut master = pl.clone();
|
|
||||||
master.tracks.push(uid);
|
|
||||||
insert_playlist(db, master);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write_txn.commit()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_track(db: &Database, id: u32) -> Result<Track, Error> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
let table = read_txn.open_table(TRACKS)?;
|
|
||||||
let b = table.get(id)?.unwrap().value();
|
|
||||||
let track: Track = bincode::deserialize(&b).unwrap();
|
|
||||||
Ok(track)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_all_tracks(db: &Database) -> Result<Vec<Track>, Error> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
let table = read_txn.open_table(TRACKS)?;
|
|
||||||
Ok(table
|
|
||||||
.iter()
|
|
||||||
.unwrap()
|
|
||||||
.flatten()
|
|
||||||
.map(|d| bincode::deserialize(&d.1.value()).unwrap())
|
|
||||||
.collect::<Vec<Track>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_last_track_id(db: &Database) -> Result<u32, Error> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
let table = read_txn.open_table(TRACKS)?;
|
|
||||||
let l = table.last()?.map_or(80u32, |v| v.0.value());
|
|
||||||
Ok(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: this hash function is used to make unique ids for each track. It doesn't aim to generate secure ones.
|
|
||||||
fn hash(buf: &[u8]) -> u64 {
|
|
||||||
let mut hasher = Md5::new();
|
|
||||||
hasher.update(buf);
|
|
||||||
let arr = hasher.finalize()[..8].try_into().unwrap();
|
|
||||||
u64::from_be_bytes(arr)
|
|
||||||
}
|
|
@ -25,7 +25,6 @@ use tokio_util::sync::CancellationToken;
|
|||||||
use wait_screen::WaitScreen;
|
use wait_screen::WaitScreen;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod db;
|
|
||||||
mod dlp;
|
mod dlp;
|
||||||
mod file_system;
|
mod file_system;
|
||||||
mod loading_screen;
|
mod loading_screen;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
|
use itunesdb::xobjects::XPlaylist;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Modifier, Style, Stylize},
|
style::{Color, Modifier, Style, Stylize},
|
||||||
@ -10,8 +11,8 @@ use ratatui::{
|
|||||||
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::{db::Track, screen::AppScreen, sync::AppEvent, theme::Theme};
|
use crate::{screen::AppScreen, sync::AppEvent, theme::Theme};
|
||||||
use crate::db::DBPlaylist;
|
use crate::sync::DBPlaylist;
|
||||||
|
|
||||||
pub struct MainScreen {
|
pub struct MainScreen {
|
||||||
mode: bool,
|
mode: bool,
|
||||||
@ -246,11 +247,11 @@ impl MainScreen {
|
|||||||
);
|
);
|
||||||
if let Some(s) = &self.playlists {
|
if let Some(s) = &self.playlists {
|
||||||
for (i, playlist) in s.iter().enumerate() {
|
for (i, playlist) in s.iter().enumerate() {
|
||||||
let date = Utc.timestamp_millis_opt(playlist.timestamp as i64).unwrap();
|
let date = Utc.timestamp_millis_opt(playlist.data.timestamp as i64).unwrap();
|
||||||
let mut row = Row::new(vec![
|
let mut row = Row::new(vec![
|
||||||
playlist.persistent_playlist_id.to_string(),
|
playlist.data.persistent_playlist_id.to_string(),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
playlist.tracks.len().to_string(),
|
playlist.elems.len().to_string(),
|
||||||
format!("{}", date.format("%Y-%m-%d %H:%M")),
|
format!("{}", date.format("%Y-%m-%d %H:%M")),
|
||||||
"YES".to_string(),
|
"YES".to_string(),
|
||||||
]);
|
]);
|
||||||
@ -328,7 +329,7 @@ impl MainScreen {
|
|||||||
.style(Style::default().fg(Color::Gray)),
|
.style(Style::default().fg(Color::Gray)),
|
||||||
);
|
);
|
||||||
if let Some(pls) = &self.playlists {
|
if let Some(pls) = &self.playlists {
|
||||||
let s = &pls.get(self.selected_playlist as usize).unwrap().tracks;
|
let s = &pls.get(self.selected_playlist as usize).unwrap().elems;
|
||||||
for (i, track) in s.iter().enumerate() {
|
for (i, track) in s.iter().enumerate() {
|
||||||
let mut row = Row::new(vec![
|
let mut row = Row::new(vec![
|
||||||
track.unique_id.to_string(),
|
track.unique_id.to_string(),
|
||||||
|
71
src/sync.rs
71
src/sync.rs
@ -1,7 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use itunesdb::xobjects::XSomeList;
|
use itunesdb::xobjects::{XDatabase, XTrackItem};
|
||||||
use redb::Database;
|
|
||||||
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs::File,
|
fs::File,
|
||||||
@ -12,8 +11,7 @@ use tokio_util::sync::CancellationToken;
|
|||||||
|
|
||||||
use crate::{config::{
|
use crate::{config::{
|
||||||
get_config_path, get_configs_dir, get_temp_dl_dir, get_temp_itunesdb, LyricaConfiguration,
|
get_config_path, get_configs_dir, get_temp_dl_dir, get_temp_itunesdb, LyricaConfiguration,
|
||||||
}, db::{self, Track}, dlp::{self, DownloadProgress}, util, AppState};
|
}, dlp::{self, DownloadProgress}, util, AppState};
|
||||||
use crate::db::{DBPlaylist, Playlist};
|
|
||||||
|
|
||||||
pub enum AppEvent {
|
pub enum AppEvent {
|
||||||
SearchIPod,
|
SearchIPod,
|
||||||
@ -26,6 +24,13 @@ pub enum AppEvent {
|
|||||||
SwitchScreen(AppState),
|
SwitchScreen(AppState),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DBPlaylist {
|
||||||
|
pub id: u64,
|
||||||
|
pub title: String,
|
||||||
|
pub timestamp: u32,
|
||||||
|
pub tracks: Vec<XTrackItem>
|
||||||
|
}
|
||||||
|
|
||||||
pub fn initialize_async_service(
|
pub fn initialize_async_service(
|
||||||
sender: Sender<AppEvent>,
|
sender: Sender<AppEvent>,
|
||||||
receiver: UnboundedReceiver<AppEvent>,
|
receiver: UnboundedReceiver<AppEvent>,
|
||||||
@ -36,7 +41,7 @@ pub fn initialize_async_service(
|
|||||||
|
|
||||||
let mut ipod_db = None;
|
let mut ipod_db = None;
|
||||||
|
|
||||||
let database = db::init_db();
|
let mut database = None;
|
||||||
|
|
||||||
let mut receiver = receiver;
|
let mut receiver = receiver;
|
||||||
|
|
||||||
@ -49,13 +54,13 @@ pub fn initialize_async_service(
|
|||||||
AppEvent::SearchIPod => {
|
AppEvent::SearchIPod => {
|
||||||
if let Some(p) = util::search_ipod() {
|
if let Some(p) = util::search_ipod() {
|
||||||
ipod_db = Some(p.clone());
|
ipod_db = Some(p.clone());
|
||||||
parse_itunes(&database, &sender, p).await;
|
database = Some(parse_itunes(&sender, p).await);
|
||||||
let _ = sender.send(AppEvent::SwitchScreen(AppState::MainScreen)).await;
|
let _ = sender.send(AppEvent::SwitchScreen(AppState::MainScreen)).await;
|
||||||
} else {
|
} else {
|
||||||
let _ = sender.send(AppEvent::IPodNotFound).await;
|
let _ = sender.send(AppEvent::IPodNotFound).await;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
AppEvent::DownloadPlaylist(playlist) => download_playlist(playlist, &database, &sender, ipod_db.as_ref().unwrap().clone()).await,
|
AppEvent::DownloadPlaylist(playlist) => download_playlist(playlist, &mut database.as_mut().unwrap(), &sender, ipod_db.clone().unwrap()).await,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +72,7 @@ pub fn initialize_async_service(
|
|||||||
|
|
||||||
async fn download_playlist(
|
async fn download_playlist(
|
||||||
playlist: CloudPlaylist,
|
playlist: CloudPlaylist,
|
||||||
database: &Database,
|
database: &mut XDatabase,
|
||||||
sender: &Sender<AppEvent>,
|
sender: &Sender<AppEvent>,
|
||||||
ipod_path: String
|
ipod_path: String
|
||||||
) {
|
) {
|
||||||
@ -82,15 +87,15 @@ async fn download_playlist(
|
|||||||
if track.title.is_none() {
|
if track.title.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut t: Track = track.clone().into();
|
let mut t: XTrackItem = track.into();
|
||||||
t.unique_id = db::get_last_track_id(database).unwrap_or(80) + 1;
|
t.data.unique_id = database.get_unique_id();
|
||||||
let mut tp = PathBuf::new();
|
let mut tp = PathBuf::new();
|
||||||
tp.push("iPod_Control");
|
tp.push("iPod_Control");
|
||||||
tp.push("Music");
|
tp.push("Music");
|
||||||
tp.push(["F", &format!("{:02}", &(t.unique_id % 100))].concat());
|
tp.push(["F", &format!("{:02}", &(t.data.unique_id % 100))].concat());
|
||||||
tp.push(format!("{:X}", t.unique_id));
|
tp.push(format!("{:X}", t.data.unique_id));
|
||||||
tp.set_extension("mp3");
|
tp.set_extension("mp3");
|
||||||
t.location = tp.to_str().unwrap().to_string().replace(r"/", ":").to_string();
|
t.set_location(tp.to_str().unwrap().to_string().replace("/", ":").to_string());
|
||||||
let mut dest = p.clone();
|
let mut dest = p.clone();
|
||||||
dest.push(tp);
|
dest.push(tp);
|
||||||
|
|
||||||
@ -100,7 +105,7 @@ async fn download_playlist(
|
|||||||
|
|
||||||
let _ = std::fs::copy(track_path, dest);
|
let _ = std::fs::copy(track_path, dest);
|
||||||
|
|
||||||
let _ = db::insert_track(database, t);
|
let _ = database.add_track(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = sender
|
let _ = sender
|
||||||
@ -108,7 +113,17 @@ async fn download_playlist(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_itunes(database: &Database, sender: &Sender<AppEvent>, path: String) {
|
fn get_playlists(db: &mut XDatabase) -> Vec<DBPlaylist> {
|
||||||
|
let pls = db.get_playlists(); // string arg type 1 - playlist title.
|
||||||
|
pls.iter()
|
||||||
|
.map(|t| DBPlaylist {
|
||||||
|
id: t.data.persistent_playlist_id,
|
||||||
|
title: t.get_title(),
|
||||||
|
timestamp: t.data.timestamp,
|
||||||
|
tracks: t.elems.iter().map(|(i, _a)| db.get_track(i.track_id)).filter(|t| t.is_some()).map(|t| t.unwrap().clone()).collect()}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_itunes(sender: &Sender<AppEvent>, path: String) -> XDatabase {
|
||||||
let cd = get_temp_itunesdb();
|
let cd = get_temp_itunesdb();
|
||||||
let mut p: PathBuf = Path::new(&path).into();
|
let mut p: PathBuf = Path::new(&path).into();
|
||||||
p.push("iPod_Control");
|
p.push("iPod_Control");
|
||||||
@ -119,31 +134,11 @@ async fn parse_itunes(database: &Database, sender: &Sender<AppEvent>, path: Stri
|
|||||||
let mut file = File::open(cd).await.unwrap();
|
let mut file = File::open(cd).await.unwrap();
|
||||||
let mut contents = vec![];
|
let mut contents = vec![];
|
||||||
file.read_to_end(&mut contents).await.unwrap();
|
file.read_to_end(&mut contents).await.unwrap();
|
||||||
let mut xdb = itunesdb::deserializer::parse_bytes(&contents);
|
let mut database = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let XSomeList::Playlists(playlists) = &xdb.find_dataset(3).child {
|
|
||||||
for playlist in playlists {
|
|
||||||
let pl = Playlist {
|
|
||||||
persistent_playlist_id: playlist.data.persistent_playlist_id,
|
|
||||||
timestamp: playlist.data.timestamp,
|
|
||||||
title: String::new() ,
|
|
||||||
is_master: playlist.data.is_master_playlist_flag != 0,
|
|
||||||
tracks: playlist.elems.iter().map(|e| e.0.track_id).collect()
|
|
||||||
};
|
|
||||||
let _ = db::insert_playlist(database, pl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = sender
|
let _ = sender
|
||||||
.send(AppEvent::ITunesParsed(
|
.send(AppEvent::ITunesParsed(
|
||||||
db::get_all_playlists(database).unwrap(),
|
get_playlists(&mut database),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -188,4 +183,6 @@ async fn parse_itunes(database: &Database, sender: &Sender<AppEvent>, path: Stri
|
|||||||
collection: playlists,
|
collection: playlists,
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
database
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user