From ea5597aab20613eff25c820f68826f83be77b981 Mon Sep 17 00:00:00 2001 From: chmanie Date: Sat, 27 May 2023 02:13:16 +0200 Subject: [PATCH] Implement track queueing, play current queue track --- cbd-tui/src/main.rs | 97 +++++++++++++++++++++++++++++++++------------ cbd-tui/src/rpc.rs | 34 +++++++++++++--- 2 files changed, 100 insertions(+), 31 deletions(-) diff --git a/cbd-tui/src/main.rs b/cbd-tui/src/main.rs index bd8d966..ad0f658 100644 --- a/cbd-tui/src/main.rs +++ b/cbd-tui/src/main.rs @@ -7,7 +7,10 @@ use crabidy_core::proto::crabidy::{ }; use crossterm::{ - event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, + event::{ + self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, KeyModifiers, + ModifierKeyCode, + }, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; @@ -71,9 +74,22 @@ trait ListView { } } +#[derive(Clone, Copy)] +enum UiFocus { + Library, + Queue, +} + +#[derive(Clone, Copy)] +enum UiItemKind { + Node, + Track, +} + struct UiItem { uuid: String, title: String, + kind: UiItemKind, } struct QueueView { @@ -103,12 +119,6 @@ impl ListView for QueueView { } } -#[derive(Clone, Copy)] -enum UiFocus { - Library, - Queue, -} - impl QueueView { fn check_focus(&mut self, focus: UiFocus) { if !self.is_selected() && matches!(focus, UiFocus::Queue) { @@ -117,6 +127,18 @@ impl QueueView { self.select(None); } } + fn skip(&self, tx: &Sender) { + if let Some(pos) = self.selected() { + if pos < self.get_size() - 1 { + tx.send(MessageFromUi::SetCurrentTrack(pos + 1)); + } + } + } + fn play_selected(&self, tx: &Sender) { + if let Some(pos) = self.selected() { + tx.send(MessageFromUi::SetCurrentTrack(pos)); + } + } fn update(&mut self, queue: Queue) { self.list = queue .tracks @@ -124,6 +146,7 @@ impl QueueView { .map(|t| UiItem { uuid: t.uuid.clone(), title: t.title.clone(), + kind: UiItemKind::Track, }) .collect(); } @@ -183,12 +206,14 @@ impl LibraryView { } fn dive(&mut self, tx: &Sender) { if let Some(item) = self.get_selected() { - tx.send(MessageFromUi::GetLibraryNode(item.uuid.clone())); + if let UiItemKind::Node = item.kind { + tx.send(MessageFromUi::GetLibraryNode(item.uuid.clone())); + } } } - fn queue_replace_with_selected(&mut self, tx: &Sender) { + fn queue_replace_with_item(&mut self, tx: &Sender) { if let Some(item) = self.get_selected() { - tx.send(MessageFromUi::ReplaceWithNode(item.uuid.clone())); + tx.send(MessageFromUi::ReplaceWithItem(item.uuid.clone(), item.kind)); } } fn update(&mut self, node: LibraryNode) { @@ -209,6 +234,7 @@ impl LibraryView { .map(|t| UiItem { uuid: t.uuid.clone(), title: t.title.clone(), + kind: UiItemKind::Track, }) .collect(); } else { @@ -219,6 +245,7 @@ impl LibraryView { .map(|c| UiItem { uuid: c.uuid.clone(), title: c.title.clone(), + kind: UiItemKind::Node, }) .collect(); } @@ -295,14 +322,15 @@ enum MessageToUi { enum MessageFromUi { Quit, GetLibraryNode(String), - ReplaceWithNode(String), + ReplaceWithItem(String, UiItemKind), + SetCurrentTrack(usize), TogglePlay, } async fn orchestrate<'a>( (tx, rx): (Sender, Receiver), ) -> Result<(), Box> { - let mut rpc_client = rpc::RpcClient::connect("http://[::1]:50051").await?; + let mut rpc_client = rpc::RpcClient::connect("http://192.168.178.28:50051").await?; if let Some(root_node) = rpc_client.get_library_node("/").await? { tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone())); @@ -324,12 +352,23 @@ async fn orchestrate<'a>( tx.send(MessageToUi::ReplaceLibraryNode(node.clone())); } }, - MessageFromUi::ReplaceWithNode(uuid) => { - rpc_client.replace_queue_with(&uuid).await? + MessageFromUi::ReplaceWithItem(uuid, kind) => { + match kind { + UiItemKind::Node => { + rpc_client.replace_queue_with_node(&uuid).await? + } + UiItemKind::Track => { + rpc_client.replace_queue_with_track(&uuid).await? + } + } } MessageFromUi::TogglePlay => { rpc_client.toggle_play().await? } + MessageFromUi::SetCurrentTrack(pos) => { + rpc_client.set_current_track(pos).await? + } + } } Some(Ok(resp)) = queue_update_stream.next() => { @@ -404,36 +443,42 @@ fn run_ui(tx: Sender, rx: Receiver) { if crossterm::event::poll(timeout).unwrap() { if let Event::Key(key) = event::read().unwrap() { if key.kind == KeyEventKind::Press { - match (app.focus, key.code) { - (_, KeyCode::Char('q')) => { + match (app.focus, key.modifiers, key.code) { + (_, KeyModifiers::NONE, KeyCode::Char('q')) => { tx.send(MessageFromUi::Quit); break; } - (_, KeyCode::Tab) => app.cycle_active(), - (_, KeyCode::Char(' ')) => { + (_, KeyModifiers::NONE, KeyCode::Tab) => app.cycle_active(), + (_, KeyModifiers::NONE, KeyCode::Char(' ')) => { tx.send(MessageFromUi::TogglePlay); } - (UiFocus::Library, KeyCode::Char('j')) => { + (_, KeyModifiers::CONTROL, KeyCode::Char('n')) => { + app.queue.skip(&tx); + } + (UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('j')) => { app.library.next(); } - (UiFocus::Library, KeyCode::Char('k')) => { + (UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('k')) => { app.library.prev(); } - (UiFocus::Library, KeyCode::Char('h')) => { + (UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('h')) => { app.library.ascend(&tx); } - (UiFocus::Library, KeyCode::Char('l')) => { + (UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('l')) => { app.library.dive(&tx); } - (UiFocus::Library, KeyCode::Enter) => { - app.library.queue_replace_with_selected(&tx); + (UiFocus::Library, KeyModifiers::NONE, KeyCode::Enter) => { + app.library.queue_replace_with_item(&tx); } - (UiFocus::Queue, KeyCode::Char('j')) => { + (UiFocus::Queue, KeyModifiers::NONE, KeyCode::Char('j')) => { app.queue.next(); } - (UiFocus::Queue, KeyCode::Char('k')) => { + (UiFocus::Queue, KeyModifiers::NONE, KeyCode::Char('k')) => { app.queue.prev(); } + (UiFocus::Queue, KeyModifiers::NONE, KeyCode::Enter) => { + app.queue.play_selected(&tx); + } _ => {} } } diff --git a/cbd-tui/src/rpc.rs b/cbd-tui/src/rpc.rs index b55bb6d..a9ee190 100644 --- a/cbd-tui/src/rpc.rs +++ b/cbd-tui/src/rpc.rs @@ -2,7 +2,8 @@ use crabidy_core::proto::crabidy::{ crabidy_service_client::CrabidyServiceClient, get_queue_updates_response::QueueUpdateResult, GetLibraryNodeRequest, GetQueueUpdatesRequest, GetQueueUpdatesResponse, GetTrackUpdatesRequest, GetTrackUpdatesResponse, LibraryNode, LibraryNodeState, ReplaceWithNodeRequest, - ReplaceWithNodeResponse, TogglePlayRequest, + ReplaceWithNodeResponse, ReplaceWithTrackRequest, ReplaceWithTrackResponse, + SetCurrentTrackRequest, SetCurrentTrackResponse, TogglePlayRequest, }; use std::{ @@ -105,7 +106,7 @@ impl RpcClient { Ok(stream) } - pub async fn replace_queue_with(&mut self, uuid: &str) -> Result<(), Box> { + pub async fn replace_queue_with_node(&mut self, uuid: &str) -> Result<(), Box> { let replace_with_node_request = Request::new(ReplaceWithNodeRequest { uuid: uuid.to_string(), }); @@ -118,12 +119,35 @@ impl RpcClient { Ok(()) } - pub async fn toggle_play(&mut self) -> Result<(), Box> { - let toggle_play_request = Request::new(TogglePlayRequest {}); + pub async fn replace_queue_with_track(&mut self, uuid: &str) -> Result<(), Box> { + let replace_with_track_request = Request::new(ReplaceWithTrackRequest { + uuid: uuid.to_string(), + }); let response = self .client - .toggle_play(toggle_play_request) + .replace_with_track(replace_with_track_request) + .await?; + + Ok(()) + } + + pub async fn toggle_play(&mut self) -> Result<(), Box> { + let toggle_play_request = Request::new(TogglePlayRequest {}); + + let response = self.client.toggle_play(toggle_play_request).await?; + + Ok(()) + } + + pub async fn set_current_track(&mut self, pos: usize) -> Result<(), Box> { + let set_current_track_request = Request::new(SetCurrentTrackRequest { + position: pos as u32, + }); + + let response = self + .client + .set_current_track(set_current_track_request) .await?; Ok(())