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)
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
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},
|
||||
};
|
||||
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<AppState, Box<dyn AppScreen>> = 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::<MainScreen>().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::<MainScreen>().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::<MainScreen>().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<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]
|
||||
|
@ -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<String>,
|
||||
soundcloud: Option<Vec<CloudPlaylist>>,
|
||||
pub progress: Option<(u32, u32)>,
|
||||
pub s_progress: Option<DownloadProgress>,
|
||||
pub tracks: Option<Vec<Track>>,
|
||||
sender: UnboundedSender<AppEvent>,
|
||||
}
|
||||
|
||||
@ -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<AppEvent>) -> 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::<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<'_>*/
|
||||
{
|
||||
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(),
|
||||
};
|
||||
|
10
src/sync.rs
10
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<Track>),
|
||||
SoundcloudGot(CloudPlaylists),
|
||||
DownloadPlaylist(CloudPlaylist),
|
||||
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();
|
||||
if !p.exists() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user