modified: src/db.rs
new file: src/file_system.rs new file: src/loading_screen.rs modified: src/main.rs modified: src/main_screen.rs modified: src/sync.rs
This commit is contained in:
parent
b34032d228
commit
1657e328fb
11
src/db.rs
11
src/db.rs
@ -124,6 +124,17 @@ pub fn get_track(db: &Database, id: u32) -> Result<Track, Error> {
|
|||||||
Ok(track)
|
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> {
|
pub fn get_last_track_id(db: &Database) -> Result<u32, Error> {
|
||||||
let read_txn = db.begin_read()?;
|
let read_txn = db.begin_read()?;
|
||||||
let table = read_txn.open_table(TRACKS)?;
|
let table = read_txn.open_table(TRACKS)?;
|
||||||
|
15
src/file_system.rs
Normal file
15
src/file_system.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use crate::screen::AppScreen;
|
||||||
|
|
||||||
|
pub struct FileSystem {}
|
||||||
|
|
||||||
|
impl AppScreen for FileSystem {
|
||||||
|
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {}
|
||||||
|
|
||||||
|
fn render(&self, frame: &mut ratatui::Frame) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
101
src/loading_screen.rs
Normal file
101
src/loading_screen.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use ratatui::{
|
||||||
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
|
style::{Color, Style, Stylize},
|
||||||
|
text::Line,
|
||||||
|
widgets::{Block, Borders, Gauge, Paragraph},
|
||||||
|
Frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{dlp::DownloadProgress, screen::AppScreen};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct LoadingScreen {
|
||||||
|
pub progress: Option<(u32, u32)>,
|
||||||
|
pub s_progress: Option<DownloadProgress>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppScreen for LoadingScreen {
|
||||||
|
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {}
|
||||||
|
|
||||||
|
fn render(&self, frame: &mut ratatui::Frame) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Min(0), // Main content area
|
||||||
|
Constraint::Length(1), // Status bar
|
||||||
|
])
|
||||||
|
.split(frame.area());
|
||||||
|
|
||||||
|
self.render_progress(frame, chunks[0]);
|
||||||
|
|
||||||
|
// Render Status Bar
|
||||||
|
let status_bar = Paragraph::new(Line::from(vec!["<Q> QUIT".bold()])).centered();
|
||||||
|
frame.render_widget(status_bar, chunks[1]); // Render into third chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadingScreen {
|
||||||
|
fn render_progress(&self, frame: &mut Frame, area: Rect) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Min(0), // Main content
|
||||||
|
Constraint::Length(6), // Progress bar
|
||||||
|
Constraint::Length(6), // Progress bar
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
let main_content = Paragraph::new("Please wait").block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title("Downloading has started!"),
|
||||||
|
);
|
||||||
|
|
||||||
|
frame.render_widget(main_content, chunks[0]);
|
||||||
|
|
||||||
|
let gauge = Gauge::default()
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title(" Downloading Playlist "),
|
||||||
|
)
|
||||||
|
.gauge_style(Style::default().fg(Color::Green))
|
||||||
|
.ratio(self.progress.unwrap().0 as f64 / self.progress.unwrap().1 as f64)
|
||||||
|
.label(format!(
|
||||||
|
"{:}/{:}",
|
||||||
|
self.progress.unwrap().0,
|
||||||
|
self.progress.unwrap().1
|
||||||
|
));
|
||||||
|
|
||||||
|
frame.render_widget(gauge, chunks[1]);
|
||||||
|
|
||||||
|
if self.s_progress.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let s: String = self
|
||||||
|
.s_progress
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.progress_percentage
|
||||||
|
.chars()
|
||||||
|
.filter(|c| c.is_ascii_digit() || *c == '.')
|
||||||
|
.collect();
|
||||||
|
let ratio: f64 = s.parse::<f64>().unwrap_or(0.0);
|
||||||
|
|
||||||
|
let gauge = Gauge::default()
|
||||||
|
.block(Block::default().borders(Borders::ALL).title(format!(
|
||||||
|
" Downloading Item (ETA: {}) ",
|
||||||
|
self.s_progress.as_ref().unwrap().eta
|
||||||
|
)))
|
||||||
|
.gauge_style(Style::default().fg(Color::Green))
|
||||||
|
.ratio(ratio / 100.0)
|
||||||
|
.label(self.s_progress.as_ref().unwrap().progress_total.to_string());
|
||||||
|
|
||||||
|
frame.render_widget(gauge, chunks[2]);
|
||||||
|
}
|
||||||
|
}
|
32
src/main.rs
32
src/main.rs
@ -10,6 +10,7 @@ use crossterm::{
|
|||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use loading_screen::LoadingScreen;
|
||||||
use main_screen::MainScreen;
|
use main_screen::MainScreen;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::{Backend, CrosstermBackend},
|
prelude::{Backend, CrosstermBackend},
|
||||||
@ -25,6 +26,8 @@ use wait_screen::WaitScreen;
|
|||||||
mod config;
|
mod config;
|
||||||
mod db;
|
mod db;
|
||||||
mod dlp;
|
mod dlp;
|
||||||
|
mod file_system;
|
||||||
|
mod loading_screen;
|
||||||
mod main_screen;
|
mod main_screen;
|
||||||
mod screen;
|
mod screen;
|
||||||
mod sync;
|
mod sync;
|
||||||
@ -35,6 +38,8 @@ mod wait_screen;
|
|||||||
enum AppState {
|
enum AppState {
|
||||||
IPodWait,
|
IPodWait,
|
||||||
MainScreen,
|
MainScreen,
|
||||||
|
LoadingScreen,
|
||||||
|
FileSystem,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@ -58,6 +63,7 @@ impl Default for App {
|
|||||||
let mut screens: HashMap<AppState, Box<dyn AppScreen>> = HashMap::new();
|
let mut screens: HashMap<AppState, Box<dyn AppScreen>> = HashMap::new();
|
||||||
screens.insert(AppState::IPodWait, Box::new(WaitScreen::default()));
|
screens.insert(AppState::IPodWait, Box::new(WaitScreen::default()));
|
||||||
screens.insert(AppState::MainScreen, Box::new(MainScreen::new(jx.clone())));
|
screens.insert(AppState::MainScreen, Box::new(MainScreen::new(jx.clone())));
|
||||||
|
screens.insert(AppState::LoadingScreen, Box::new(LoadingScreen::default()));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
receiver: rx,
|
receiver: rx,
|
||||||
@ -102,25 +108,23 @@ impl App {
|
|||||||
AppEvent::IPodNotFound => {
|
AppEvent::IPodNotFound => {
|
||||||
let _ = self.sender.send(AppEvent::SearchIPod);
|
let _ = self.sender.send(AppEvent::SearchIPod);
|
||||||
},
|
},
|
||||||
AppEvent::ITunesParsed(xdb) => {
|
AppEvent::ITunesParsed(tracks) => {
|
||||||
|
let screen: &mut MainScreen = self.get_screen(&AppState::MainScreen);
|
||||||
|
screen.tracks = Some(tracks);
|
||||||
},
|
},
|
||||||
AppEvent::SoundcloudGot(playlists) => {
|
AppEvent::SoundcloudGot(playlists) => {
|
||||||
let a = self.screens.get_mut(&AppState::MainScreen).unwrap();
|
let screen: &mut MainScreen = self.get_screen(&AppState::MainScreen);
|
||||||
let screen: &mut MainScreen = a.as_any().downcast_mut::<MainScreen>().unwrap();
|
|
||||||
screen.set_soundcloud_playlists(playlists);
|
screen.set_soundcloud_playlists(playlists);
|
||||||
},
|
},
|
||||||
AppEvent::OverallProgress((c, max)) => {
|
AppEvent::OverallProgress((c, max)) => {
|
||||||
let a = self.screens.get_mut(&AppState::MainScreen).unwrap();
|
self.state = AppState::LoadingScreen;
|
||||||
let screen: &mut MainScreen = a.as_any().downcast_mut::<MainScreen>().unwrap();
|
let screen: &mut LoadingScreen = self.get_screen(&AppState::LoadingScreen);
|
||||||
screen.progress = Some((c, max));
|
screen.progress = Some((c, max));
|
||||||
screen.download_screen();
|
|
||||||
},
|
},
|
||||||
AppEvent::CurrentProgress(progress) => {
|
AppEvent::CurrentProgress(progress) => {
|
||||||
let a = self.screens.get_mut(&AppState::MainScreen).unwrap();
|
self.state = AppState::LoadingScreen;
|
||||||
let screen: &mut MainScreen = a.as_any().downcast_mut::<MainScreen>().unwrap();
|
let screen: &mut LoadingScreen = self.get_screen(&AppState::LoadingScreen);
|
||||||
screen.s_progress = Some(progress);
|
screen.s_progress = Some(progress);
|
||||||
screen.download_screen();
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -141,6 +145,14 @@ impl App {
|
|||||||
fn exit(&mut self) {
|
fn exit(&mut self) {
|
||||||
self.token.cancel();
|
self.token.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_screen<T>(&mut self, state: &AppState) -> &mut T
|
||||||
|
where
|
||||||
|
T: 'static + AppScreen,
|
||||||
|
{
|
||||||
|
let a = self.screens.get_mut(state).unwrap();
|
||||||
|
a.as_any().downcast_mut::<T>().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -5,22 +5,14 @@ use ratatui::{
|
|||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Modifier, Style, Stylize},
|
style::{Color, Modifier, Style, Stylize},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, Gauge, Paragraph, Row, Table, Tabs},
|
widgets::{Block, Borders, Paragraph, Row, Table, Tabs},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::{dlp::DownloadProgress, screen::AppScreen, sync::AppEvent};
|
use crate::{db::Track, screen::AppScreen, sync::AppEvent};
|
||||||
|
|
||||||
/*struct Playlist {
|
|
||||||
id: u64,
|
|
||||||
title: String,
|
|
||||||
link: String,
|
|
||||||
created_at: String,
|
|
||||||
track_count: u32,
|
|
||||||
}*/
|
|
||||||
|
|
||||||
pub struct MainScreen {
|
pub struct MainScreen {
|
||||||
selected_tab: i8,
|
selected_tab: i8,
|
||||||
@ -28,8 +20,7 @@ pub struct MainScreen {
|
|||||||
max_rows: i32,
|
max_rows: i32,
|
||||||
tab_titles: Vec<String>,
|
tab_titles: Vec<String>,
|
||||||
soundcloud: Option<Vec<CloudPlaylist>>,
|
soundcloud: Option<Vec<CloudPlaylist>>,
|
||||||
pub progress: Option<(u32, u32)>,
|
pub tracks: Option<Vec<Track>>,
|
||||||
pub s_progress: Option<DownloadProgress>,
|
|
||||||
sender: UnboundedSender<AppEvent>,
|
sender: UnboundedSender<AppEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,10 +63,7 @@ impl AppScreen for MainScreen {
|
|||||||
|
|
||||||
frame.render_widget(tabs, chunks[0]);
|
frame.render_widget(tabs, chunks[0]);
|
||||||
|
|
||||||
match self.selected_tab {
|
self.render_tab(frame, chunks[1]);
|
||||||
-1 => self.render_progress(frame, chunks[1]),
|
|
||||||
_ => self.render_tab(frame, chunks[1]),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render Status Bar
|
// Render Status Bar
|
||||||
let status_bar = Paragraph::new(Line::from(vec![
|
let status_bar = Paragraph::new(Line::from(vec![
|
||||||
@ -101,11 +89,10 @@ impl AppScreen for MainScreen {
|
|||||||
impl MainScreen {
|
impl MainScreen {
|
||||||
pub fn new(sender: UnboundedSender<AppEvent>) -> Self {
|
pub fn new(sender: UnboundedSender<AppEvent>) -> Self {
|
||||||
MainScreen {
|
MainScreen {
|
||||||
selected_row: -1,
|
selected_row: 0,
|
||||||
max_rows: 0,
|
max_rows: 0,
|
||||||
soundcloud: None,
|
soundcloud: None,
|
||||||
progress: None,
|
tracks: None,
|
||||||
s_progress: None,
|
|
||||||
selected_tab: 0,
|
selected_tab: 0,
|
||||||
tab_titles: vec![
|
tab_titles: vec![
|
||||||
"YouTube".to_string(),
|
"YouTube".to_string(),
|
||||||
@ -117,10 +104,6 @@ impl MainScreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn download_screen(&mut self) {
|
|
||||||
self.selected_tab = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_max_rows(&mut self) {
|
fn update_max_rows(&mut self) {
|
||||||
self.max_rows = match self.selected_tab {
|
self.max_rows = match self.selected_tab {
|
||||||
1 => self.soundcloud.as_deref().unwrap_or(&[]).len(),
|
1 => self.soundcloud.as_deref().unwrap_or(&[]).len(),
|
||||||
@ -175,66 +158,6 @@ impl MainScreen {
|
|||||||
self.soundcloud = Some(pl.collection);
|
self.soundcloud = Some(pl.collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_progress(&self, frame: &mut Frame, area: Rect) {
|
|
||||||
let chunks = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints([
|
|
||||||
Constraint::Min(0), // Main content
|
|
||||||
Constraint::Length(6), // Progress bar
|
|
||||||
Constraint::Length(6), // Progress bar
|
|
||||||
])
|
|
||||||
.split(area);
|
|
||||||
|
|
||||||
let main_content = Paragraph::new("Please wait").block(
|
|
||||||
Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.title("Downloading has started!"),
|
|
||||||
);
|
|
||||||
|
|
||||||
frame.render_widget(main_content, chunks[0]);
|
|
||||||
|
|
||||||
let gauge = Gauge::default()
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.title(" Downloading Playlist "),
|
|
||||||
)
|
|
||||||
.gauge_style(Style::default().fg(Color::Green))
|
|
||||||
.ratio(self.progress.unwrap().0 as f64 / self.progress.unwrap().1 as f64)
|
|
||||||
.label(format!(
|
|
||||||
"{:}/{:}",
|
|
||||||
self.progress.unwrap().0,
|
|
||||||
self.progress.unwrap().1
|
|
||||||
));
|
|
||||||
|
|
||||||
frame.render_widget(gauge, chunks[1]);
|
|
||||||
|
|
||||||
if self.s_progress.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let s: String = self
|
|
||||||
.s_progress
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.progress_percentage
|
|
||||||
.chars()
|
|
||||||
.filter(|c| c.is_ascii_digit() || *c == '.')
|
|
||||||
.collect();
|
|
||||||
let ratio: f64 = s.parse::<f64>().unwrap_or(0.0);
|
|
||||||
|
|
||||||
let gauge = Gauge::default()
|
|
||||||
.block(Block::default().borders(Borders::ALL).title(format!(
|
|
||||||
" Downloading Item (ETA: {}) ",
|
|
||||||
self.s_progress.as_ref().unwrap().eta
|
|
||||||
)))
|
|
||||||
.gauge_style(Style::default().fg(Color::Green))
|
|
||||||
.ratio(ratio / 100.0)
|
|
||||||
.label(self.s_progress.as_ref().unwrap().progress_total.to_string());
|
|
||||||
|
|
||||||
frame.render_widget(gauge, chunks[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_tab(&self, frame: &mut Frame, area: Rect) /*-> Table<'_>*/
|
fn render_tab(&self, frame: &mut Frame, area: Rect) /*-> Table<'_>*/
|
||||||
{
|
{
|
||||||
let rows = match self.selected_tab {
|
let rows = match self.selected_tab {
|
||||||
@ -265,7 +188,7 @@ impl MainScreen {
|
|||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
// local
|
// local
|
||||||
/* let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
v.push(
|
v.push(
|
||||||
Row::new(vec!["Id", "Title", "Artist", "Bitrate", "Hash"])
|
Row::new(vec!["Id", "Title", "Artist", "Bitrate", "Hash"])
|
||||||
.style(Style::default().fg(Color::Gray)),
|
.style(Style::default().fg(Color::Gray)),
|
||||||
@ -286,8 +209,7 @@ impl MainScreen {
|
|||||||
v.push(row);
|
v.push(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v*/
|
v
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
};
|
};
|
||||||
|
10
src/sync.rs
10
src/sync.rs
@ -1,6 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use itunesdb::xobjects::{XDatabase, XSomeList};
|
use itunesdb::xobjects::XSomeList;
|
||||||
use redb::Database;
|
use redb::Database;
|
||||||
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@ -23,7 +23,7 @@ pub enum AppEvent {
|
|||||||
IPodFound(String),
|
IPodFound(String),
|
||||||
IPodNotFound,
|
IPodNotFound,
|
||||||
ParseItunes(String),
|
ParseItunes(String),
|
||||||
ITunesParsed(XDatabase),
|
ITunesParsed(Vec<Track>),
|
||||||
SoundcloudGot(CloudPlaylists),
|
SoundcloudGot(CloudPlaylists),
|
||||||
DownloadPlaylist(CloudPlaylist),
|
DownloadPlaylist(CloudPlaylist),
|
||||||
CurrentProgress(DownloadProgress),
|
CurrentProgress(DownloadProgress),
|
||||||
@ -108,7 +108,11 @@ async fn parse_itunes(database: &Database, sender: &Sender<AppEvent>, path: Stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = sender.send(AppEvent::ITunesParsed(xdb)).await;
|
let _ = sender
|
||||||
|
.send(AppEvent::ITunesParsed(
|
||||||
|
db::get_all_tracks(database).unwrap(),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
|
||||||
let p = get_config_path();
|
let p = get_config_path();
|
||||||
if !p.exists() {
|
if !p.exists() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user