diff --git a/src/db.rs b/src/db.rs index e3ed651..26cd010 100644 --- a/src/db.rs +++ b/src/db.rs @@ -124,6 +124,17 @@ pub fn get_track(db: &Database, id: u32) -> Result { Ok(track) } +pub fn get_all_tracks(db: &Database) -> Result, 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::>()) +} + pub fn get_last_track_id(db: &Database) -> Result { let read_txn = db.begin_read()?; let table = read_txn.open_table(TRACKS)?; diff --git a/src/file_system.rs b/src/file_system.rs new file mode 100644 index 0000000..980e3d6 --- /dev/null +++ b/src/file_system.rs @@ -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!() + } +} diff --git a/src/loading_screen.rs b/src/loading_screen.rs new file mode 100644 index 0000000..f49b227 --- /dev/null +++ b/src/loading_screen.rs @@ -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, +} + +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![" 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::().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]); + } +} diff --git a/src/main.rs b/src/main.rs index 83b42c4..c509f99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use futures::StreamExt; +use loading_screen::LoadingScreen; use main_screen::MainScreen; use ratatui::{ prelude::{Backend, CrosstermBackend}, @@ -25,6 +26,8 @@ use wait_screen::WaitScreen; mod config; mod db; mod dlp; +mod file_system; +mod loading_screen; mod main_screen; mod screen; mod sync; @@ -35,6 +38,8 @@ mod wait_screen; enum AppState { IPodWait, MainScreen, + LoadingScreen, + FileSystem, } pub struct App { @@ -58,6 +63,7 @@ impl Default for App { let mut screens: HashMap> = HashMap::new(); 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())); Self { receiver: rx, @@ -102,25 +108,23 @@ impl App { AppEvent::IPodNotFound => { 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) => { - let a = self.screens.get_mut(&AppState::MainScreen).unwrap(); - let screen: &mut MainScreen = a.as_any().downcast_mut::().unwrap(); + let screen: &mut MainScreen = self.get_screen(&AppState::MainScreen); screen.set_soundcloud_playlists(playlists); }, AppEvent::OverallProgress((c, max)) => { - let a = self.screens.get_mut(&AppState::MainScreen).unwrap(); - let screen: &mut MainScreen = a.as_any().downcast_mut::().unwrap(); + self.state = AppState::LoadingScreen; + let screen: &mut LoadingScreen = self.get_screen(&AppState::LoadingScreen); screen.progress = Some((c, max)); - screen.download_screen(); }, AppEvent::CurrentProgress(progress) => { - let a = self.screens.get_mut(&AppState::MainScreen).unwrap(); - let screen: &mut MainScreen = a.as_any().downcast_mut::().unwrap(); + self.state = AppState::LoadingScreen; + let screen: &mut LoadingScreen = self.get_screen(&AppState::LoadingScreen); screen.s_progress = Some(progress); - screen.download_screen(); }, _ => {} } @@ -141,6 +145,14 @@ impl App { fn exit(&mut self) { self.token.cancel(); } + + fn get_screen(&mut self, state: &AppState) -> &mut T + where + T: 'static + AppScreen, + { + let a = self.screens.get_mut(state).unwrap(); + a.as_any().downcast_mut::().unwrap() + } } #[tokio::main] diff --git a/src/main_screen.rs b/src/main_screen.rs index e3d66e3..d0804c2 100644 --- a/src/main_screen.rs +++ b/src/main_screen.rs @@ -5,22 +5,14 @@ use ratatui::{ layout::{Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style, Stylize}, text::{Line, Span}, - widgets::{Block, Borders, Gauge, Paragraph, Row, Table, Tabs}, + widgets::{Block, Borders, Paragraph, Row, Table, Tabs}, Frame, }; use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists}; use strum::IntoEnumIterator; use tokio::sync::mpsc::UnboundedSender; -use crate::{dlp::DownloadProgress, screen::AppScreen, sync::AppEvent}; - -/*struct Playlist { - id: u64, - title: String, - link: String, - created_at: String, - track_count: u32, -}*/ +use crate::{db::Track, screen::AppScreen, sync::AppEvent}; pub struct MainScreen { selected_tab: i8, @@ -28,8 +20,7 @@ pub struct MainScreen { max_rows: i32, tab_titles: Vec, soundcloud: Option>, - pub progress: Option<(u32, u32)>, - pub s_progress: Option, + pub tracks: Option>, sender: UnboundedSender, } @@ -72,10 +63,7 @@ impl AppScreen for MainScreen { frame.render_widget(tabs, chunks[0]); - match self.selected_tab { - -1 => self.render_progress(frame, chunks[1]), - _ => self.render_tab(frame, chunks[1]), - } + self.render_tab(frame, chunks[1]); // Render Status Bar let status_bar = Paragraph::new(Line::from(vec![ @@ -101,11 +89,10 @@ impl AppScreen for MainScreen { impl MainScreen { pub fn new(sender: UnboundedSender) -> Self { MainScreen { - selected_row: -1, + selected_row: 0, max_rows: 0, soundcloud: None, - progress: None, - s_progress: None, + tracks: None, selected_tab: 0, tab_titles: vec![ "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) { self.max_rows = match self.selected_tab { 1 => self.soundcloud.as_deref().unwrap_or(&[]).len(), @@ -175,66 +158,6 @@ impl MainScreen { 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::().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<'_>*/ { let rows = match self.selected_tab { @@ -265,7 +188,7 @@ impl MainScreen { } 2 => { // local - /* let mut v = Vec::new(); + let mut v = Vec::new(); v.push( Row::new(vec!["Id", "Title", "Artist", "Bitrate", "Hash"]) .style(Style::default().fg(Color::Gray)), @@ -286,8 +209,7 @@ impl MainScreen { v.push(row); } } - v*/ - Vec::new() + v } _ => Vec::new(), }; diff --git a/src/sync.rs b/src/sync.rs index 5fdb0fa..6460902 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use itunesdb::xobjects::{XDatabase, XSomeList}; +use itunesdb::xobjects::XSomeList; use redb::Database; use soundcloud::sobjects::{CloudPlaylist, CloudPlaylists}; use tokio::{ @@ -23,7 +23,7 @@ pub enum AppEvent { IPodFound(String), IPodNotFound, ParseItunes(String), - ITunesParsed(XDatabase), + ITunesParsed(Vec), SoundcloudGot(CloudPlaylists), DownloadPlaylist(CloudPlaylist), CurrentProgress(DownloadProgress), @@ -108,7 +108,11 @@ async fn parse_itunes(database: &Database, sender: &Sender, 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(); if !p.exists() {