Fixed itunesdb bug. Added ability to download single song form playlist. Artwork addition development started, fs mode development started.
This commit is contained in:
parent
3bd8f1c75d
commit
4d89b9e187
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -405,7 +405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -982,8 +982,8 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "itunesdb"
|
||||
version = "0.1.51"
|
||||
source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#ed639f96d59b6777114551e8c3054f2a0ac9c7da"
|
||||
version = "0.1.57"
|
||||
source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#ea14aaf6c3284b2cd83f4628b21f652099a79815"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"env_logger",
|
||||
@ -1536,7 +1536,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1879,7 +1879,7 @@ dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2367,7 +2367,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -20,7 +20,7 @@ futures = "0.3"
|
||||
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.51", git = "https://gitea.awain.net/alterwain/ITunesDB.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"
|
||||
|
49
src/dlp.rs
49
src/dlp.rs
@ -18,6 +18,55 @@ pub struct DownloadProgress {
|
||||
pub eta: String,
|
||||
}
|
||||
|
||||
pub async fn download_track_from_soundcloud(
|
||||
track_url: &str,
|
||||
download_dir: &PathBuf,
|
||||
sender: Sender<AppEvent>,
|
||||
) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
let _ = sender
|
||||
.send(AppEvent::SwitchScreen(crate::AppState::LoadingScreen))
|
||||
.await;
|
||||
|
||||
if download_dir.exists() {
|
||||
let _ = std::fs::remove_dir_all(download_dir);
|
||||
}
|
||||
let _ = std::fs::create_dir_all(download_dir);
|
||||
|
||||
let args = &[
|
||||
"-f",
|
||||
"mp3",
|
||||
"--ignore-errors",
|
||||
"--newline",
|
||||
"--progress-template",
|
||||
"{\"progress_percentage\":\"%(progress._percent_str)s\",\"progress_total\":\"%(progress._total_bytes_str)s\",\"speed\":\"%(progress._speed_str)s\",\"eta\":\"%(progress._eta_str)s\"}",
|
||||
"-o",
|
||||
"%(id)i.%(ext)s",
|
||||
"--write-thumbnail",
|
||||
track_url
|
||||
];
|
||||
|
||||
let mut command = Command::new("yt-dlp");
|
||||
command.args(args);
|
||||
command.stdout(Stdio::piped());
|
||||
command.stderr(Stdio::null());
|
||||
command.current_dir(download_dir);
|
||||
|
||||
let mut child = command.spawn()?;
|
||||
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let mut reader = BufReader::new(stdout).lines();
|
||||
|
||||
while let 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;
|
||||
let _ = sender.send(AppEvent::CurrentProgress(progress)).await;
|
||||
}
|
||||
}
|
||||
let _ = sender.send(AppEvent::OverallProgress((1, 1))).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn download_from_soundcloud(
|
||||
playlist_url: &str,
|
||||
download_dir: &PathBuf,
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::component::table::SmartTable;
|
||||
use crate::{screen::AppScreen, theme::Theme};
|
||||
use crate::sync::AppEvent;
|
||||
use crate::{screen::AppScreen, theme::Theme, AppState};
|
||||
use chrono::{DateTime, Utc};
|
||||
use crossterm::event::KeyCode;
|
||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
||||
@ -10,30 +11,11 @@ use std::cmp::Ordering;
|
||||
use std::fs::DirEntry;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::PathBuf;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
pub struct FileSystem {
|
||||
table: SmartTable,
|
||||
}
|
||||
|
||||
impl Default for FileSystem {
|
||||
fn default() -> Self {
|
||||
let table = SmartTable::new(
|
||||
["Name", "Type", "Size", "Modified"]
|
||||
.iter_mut()
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
vec![
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Length(5),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(30),
|
||||
],
|
||||
);
|
||||
|
||||
let mut a = Self { table };
|
||||
a.get_path(dirs::document_dir().unwrap());
|
||||
a
|
||||
}
|
||||
sender: UnboundedSender<AppEvent>,
|
||||
}
|
||||
|
||||
impl AppScreen for FileSystem {
|
||||
@ -41,6 +23,11 @@ impl AppScreen for FileSystem {
|
||||
match key_event.code {
|
||||
KeyCode::Up => self.table.previous_row(),
|
||||
KeyCode::Down => self.table.next_row(),
|
||||
KeyCode::F(4) => {
|
||||
let _ = self
|
||||
.sender
|
||||
.send(AppEvent::SwitchScreen(AppState::MainScreen));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -58,6 +45,8 @@ impl AppScreen for FileSystem {
|
||||
|
||||
// Render Status Bar
|
||||
let status_bar = Paragraph::new(Line::from(vec![
|
||||
"<F4> SWITCH TO NORMAL".bold(),
|
||||
" | ".dark_gray(),
|
||||
"<F5> SAVE AS PLAYLIST".bold(),
|
||||
" | ".dark_gray(),
|
||||
"<F6> SAVE AS IS".bold(),
|
||||
@ -78,6 +67,25 @@ impl AppScreen for FileSystem {
|
||||
}
|
||||
|
||||
impl FileSystem {
|
||||
pub fn new(sender: UnboundedSender<AppEvent>) -> Self {
|
||||
let table = SmartTable::new(
|
||||
["Name", "Type", "Size", "Modified"]
|
||||
.iter_mut()
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
vec![
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Length(5),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(30),
|
||||
],
|
||||
);
|
||||
|
||||
let mut a = Self { table, sender };
|
||||
a.get_path(dirs::document_dir().unwrap());
|
||||
a
|
||||
}
|
||||
|
||||
fn get_path(&mut self, p: PathBuf) {
|
||||
let paths = std::fs::read_dir(&p).unwrap();
|
||||
let mut dir = paths
|
||||
|
@ -68,7 +68,7 @@ impl Default for App {
|
||||
screens.insert(AppState::IPodWait, Box::new(WaitScreen::default()));
|
||||
screens.insert(AppState::MainScreen, Box::new(MainScreen::new(jx.clone())));
|
||||
screens.insert(AppState::LoadingScreen, Box::new(LoadingScreen::default()));
|
||||
screens.insert(AppState::FileSystem, Box::new(FileSystem::default()));
|
||||
screens.insert(AppState::FileSystem, Box::new(FileSystem::new(jx.clone())));
|
||||
|
||||
Self {
|
||||
receiver: rx,
|
||||
|
@ -160,14 +160,31 @@ impl MainScreen {
|
||||
fn download_row(&mut self) {
|
||||
if self.selected_tab == 1 {
|
||||
// SC
|
||||
let playlist = self
|
||||
.soundcloud
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(self.pl_table.selected_row())
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = self.sender.send(AppEvent::DownloadPlaylist(playlist));
|
||||
match self.mode {
|
||||
false => {
|
||||
let playlist = self
|
||||
.soundcloud
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(self.pl_table.selected_row())
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = self.sender.send(AppEvent::DownloadPlaylist(playlist));
|
||||
}
|
||||
true => {
|
||||
let track = self
|
||||
.soundcloud
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(self.pl_table.selected_row())
|
||||
.unwrap()
|
||||
.tracks
|
||||
.get(self.song_table.selected_row())
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = self.sender.send(AppEvent::DownloadTrack(track));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
60
src/sync.rs
60
src/sync.rs
@ -25,6 +25,7 @@ pub enum AppEvent {
|
||||
ITunesParsed(Vec<DBPlaylist>),
|
||||
SoundcloudGot(CloudPlaylists),
|
||||
DownloadPlaylist(CloudPlaylist),
|
||||
DownloadTrack(CloudTrack),
|
||||
CurrentProgress(DownloadProgress),
|
||||
OverallProgress((u32, u32)),
|
||||
SwitchScreen(AppState),
|
||||
@ -114,6 +115,7 @@ pub fn initialize_async_service(
|
||||
}
|
||||
},
|
||||
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;},
|
||||
_ => {}
|
||||
}
|
||||
@ -124,6 +126,64 @@ pub fn initialize_async_service(
|
||||
});
|
||||
}
|
||||
|
||||
async fn download_track(
|
||||
track: CloudTrack,
|
||||
database: &mut XDatabase,
|
||||
sender: &Sender<AppEvent>,
|
||||
ipod_path: String,
|
||||
) {
|
||||
if let Ok(()) = dlp::download_track_from_soundcloud(
|
||||
&track.permalink_url.clone().unwrap(),
|
||||
&get_temp_dl_dir(),
|
||||
sender.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
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");
|
||||
|
||||
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());
|
||||
|
||||
database.add_track(t);
|
||||
}
|
||||
|
||||
let _ = sender
|
||||
.send(AppEvent::SwitchScreen(AppState::MainScreen))
|
||||
.await;
|
||||
|
||||
let _ = sender
|
||||
.send(AppEvent::ITunesParsed(get_playlists(database)))
|
||||
.await;
|
||||
|
||||
overwrite_database(database, &ipod_path);
|
||||
}
|
||||
|
||||
async fn download_playlist(
|
||||
playlist: CloudPlaylist,
|
||||
database: &mut XDatabase,
|
||||
|
Loading…
x
Reference in New Issue
Block a user