Implement track queueing, play current queue track
This commit is contained in:
parent
fc0b48941d
commit
ea5597aab2
|
|
@ -7,7 +7,10 @@ use crabidy_core::proto::crabidy::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
|
event::{
|
||||||
|
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, KeyModifiers,
|
||||||
|
ModifierKeyCode,
|
||||||
|
},
|
||||||
execute,
|
execute,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
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 {
|
struct UiItem {
|
||||||
uuid: String,
|
uuid: String,
|
||||||
title: String,
|
title: String,
|
||||||
|
kind: UiItemKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QueueView {
|
struct QueueView {
|
||||||
|
|
@ -103,12 +119,6 @@ impl ListView for QueueView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum UiFocus {
|
|
||||||
Library,
|
|
||||||
Queue,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QueueView {
|
impl QueueView {
|
||||||
fn check_focus(&mut self, focus: UiFocus) {
|
fn check_focus(&mut self, focus: UiFocus) {
|
||||||
if !self.is_selected() && matches!(focus, UiFocus::Queue) {
|
if !self.is_selected() && matches!(focus, UiFocus::Queue) {
|
||||||
|
|
@ -117,6 +127,18 @@ impl QueueView {
|
||||||
self.select(None);
|
self.select(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn skip(&self, tx: &Sender<MessageFromUi>) {
|
||||||
|
if let Some(pos) = self.selected() {
|
||||||
|
if pos < self.get_size() - 1 {
|
||||||
|
tx.send(MessageFromUi::SetCurrentTrack(pos + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn play_selected(&self, tx: &Sender<MessageFromUi>) {
|
||||||
|
if let Some(pos) = self.selected() {
|
||||||
|
tx.send(MessageFromUi::SetCurrentTrack(pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
fn update(&mut self, queue: Queue) {
|
fn update(&mut self, queue: Queue) {
|
||||||
self.list = queue
|
self.list = queue
|
||||||
.tracks
|
.tracks
|
||||||
|
|
@ -124,6 +146,7 @@ impl QueueView {
|
||||||
.map(|t| UiItem {
|
.map(|t| UiItem {
|
||||||
uuid: t.uuid.clone(),
|
uuid: t.uuid.clone(),
|
||||||
title: t.title.clone(),
|
title: t.title.clone(),
|
||||||
|
kind: UiItemKind::Track,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
@ -183,12 +206,14 @@ impl LibraryView {
|
||||||
}
|
}
|
||||||
fn dive(&mut self, tx: &Sender<MessageFromUi>) {
|
fn dive(&mut self, tx: &Sender<MessageFromUi>) {
|
||||||
if let Some(item) = self.get_selected() {
|
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<MessageFromUi>) {
|
fn queue_replace_with_item(&mut self, tx: &Sender<MessageFromUi>) {
|
||||||
if let Some(item) = self.get_selected() {
|
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) {
|
fn update(&mut self, node: LibraryNode) {
|
||||||
|
|
@ -209,6 +234,7 @@ impl LibraryView {
|
||||||
.map(|t| UiItem {
|
.map(|t| UiItem {
|
||||||
uuid: t.uuid.clone(),
|
uuid: t.uuid.clone(),
|
||||||
title: t.title.clone(),
|
title: t.title.clone(),
|
||||||
|
kind: UiItemKind::Track,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -219,6 +245,7 @@ impl LibraryView {
|
||||||
.map(|c| UiItem {
|
.map(|c| UiItem {
|
||||||
uuid: c.uuid.clone(),
|
uuid: c.uuid.clone(),
|
||||||
title: c.title.clone(),
|
title: c.title.clone(),
|
||||||
|
kind: UiItemKind::Node,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
@ -295,14 +322,15 @@ enum MessageToUi {
|
||||||
enum MessageFromUi {
|
enum MessageFromUi {
|
||||||
Quit,
|
Quit,
|
||||||
GetLibraryNode(String),
|
GetLibraryNode(String),
|
||||||
ReplaceWithNode(String),
|
ReplaceWithItem(String, UiItemKind),
|
||||||
|
SetCurrentTrack(usize),
|
||||||
TogglePlay,
|
TogglePlay,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn orchestrate<'a>(
|
async fn orchestrate<'a>(
|
||||||
(tx, rx): (Sender<MessageToUi>, Receiver<MessageFromUi>),
|
(tx, rx): (Sender<MessageToUi>, Receiver<MessageFromUi>),
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
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? {
|
if let Some(root_node) = rpc_client.get_library_node("/").await? {
|
||||||
tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone()));
|
tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone()));
|
||||||
|
|
@ -324,12 +352,23 @@ async fn orchestrate<'a>(
|
||||||
tx.send(MessageToUi::ReplaceLibraryNode(node.clone()));
|
tx.send(MessageToUi::ReplaceLibraryNode(node.clone()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MessageFromUi::ReplaceWithNode(uuid) => {
|
MessageFromUi::ReplaceWithItem(uuid, kind) => {
|
||||||
rpc_client.replace_queue_with(&uuid).await?
|
match kind {
|
||||||
|
UiItemKind::Node => {
|
||||||
|
rpc_client.replace_queue_with_node(&uuid).await?
|
||||||
|
}
|
||||||
|
UiItemKind::Track => {
|
||||||
|
rpc_client.replace_queue_with_track(&uuid).await?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MessageFromUi::TogglePlay => {
|
MessageFromUi::TogglePlay => {
|
||||||
rpc_client.toggle_play().await?
|
rpc_client.toggle_play().await?
|
||||||
}
|
}
|
||||||
|
MessageFromUi::SetCurrentTrack(pos) => {
|
||||||
|
rpc_client.set_current_track(pos).await?
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Ok(resp)) = queue_update_stream.next() => {
|
Some(Ok(resp)) = queue_update_stream.next() => {
|
||||||
|
|
@ -404,36 +443,42 @@ fn run_ui(tx: Sender<MessageFromUi>, rx: Receiver<MessageToUi>) {
|
||||||
if crossterm::event::poll(timeout).unwrap() {
|
if crossterm::event::poll(timeout).unwrap() {
|
||||||
if let Event::Key(key) = event::read().unwrap() {
|
if let Event::Key(key) = event::read().unwrap() {
|
||||||
if key.kind == KeyEventKind::Press {
|
if key.kind == KeyEventKind::Press {
|
||||||
match (app.focus, key.code) {
|
match (app.focus, key.modifiers, key.code) {
|
||||||
(_, KeyCode::Char('q')) => {
|
(_, KeyModifiers::NONE, KeyCode::Char('q')) => {
|
||||||
tx.send(MessageFromUi::Quit);
|
tx.send(MessageFromUi::Quit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(_, KeyCode::Tab) => app.cycle_active(),
|
(_, KeyModifiers::NONE, KeyCode::Tab) => app.cycle_active(),
|
||||||
(_, KeyCode::Char(' ')) => {
|
(_, KeyModifiers::NONE, KeyCode::Char(' ')) => {
|
||||||
tx.send(MessageFromUi::TogglePlay);
|
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();
|
app.library.next();
|
||||||
}
|
}
|
||||||
(UiFocus::Library, KeyCode::Char('k')) => {
|
(UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('k')) => {
|
||||||
app.library.prev();
|
app.library.prev();
|
||||||
}
|
}
|
||||||
(UiFocus::Library, KeyCode::Char('h')) => {
|
(UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('h')) => {
|
||||||
app.library.ascend(&tx);
|
app.library.ascend(&tx);
|
||||||
}
|
}
|
||||||
(UiFocus::Library, KeyCode::Char('l')) => {
|
(UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('l')) => {
|
||||||
app.library.dive(&tx);
|
app.library.dive(&tx);
|
||||||
}
|
}
|
||||||
(UiFocus::Library, KeyCode::Enter) => {
|
(UiFocus::Library, KeyModifiers::NONE, KeyCode::Enter) => {
|
||||||
app.library.queue_replace_with_selected(&tx);
|
app.library.queue_replace_with_item(&tx);
|
||||||
}
|
}
|
||||||
(UiFocus::Queue, KeyCode::Char('j')) => {
|
(UiFocus::Queue, KeyModifiers::NONE, KeyCode::Char('j')) => {
|
||||||
app.queue.next();
|
app.queue.next();
|
||||||
}
|
}
|
||||||
(UiFocus::Queue, KeyCode::Char('k')) => {
|
(UiFocus::Queue, KeyModifiers::NONE, KeyCode::Char('k')) => {
|
||||||
app.queue.prev();
|
app.queue.prev();
|
||||||
}
|
}
|
||||||
|
(UiFocus::Queue, KeyModifiers::NONE, KeyCode::Enter) => {
|
||||||
|
app.queue.play_selected(&tx);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use crabidy_core::proto::crabidy::{
|
||||||
crabidy_service_client::CrabidyServiceClient, get_queue_updates_response::QueueUpdateResult,
|
crabidy_service_client::CrabidyServiceClient, get_queue_updates_response::QueueUpdateResult,
|
||||||
GetLibraryNodeRequest, GetQueueUpdatesRequest, GetQueueUpdatesResponse, GetTrackUpdatesRequest,
|
GetLibraryNodeRequest, GetQueueUpdatesRequest, GetQueueUpdatesResponse, GetTrackUpdatesRequest,
|
||||||
GetTrackUpdatesResponse, LibraryNode, LibraryNodeState, ReplaceWithNodeRequest,
|
GetTrackUpdatesResponse, LibraryNode, LibraryNodeState, ReplaceWithNodeRequest,
|
||||||
ReplaceWithNodeResponse, TogglePlayRequest,
|
ReplaceWithNodeResponse, ReplaceWithTrackRequest, ReplaceWithTrackResponse,
|
||||||
|
SetCurrentTrackRequest, SetCurrentTrackResponse, TogglePlayRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -105,7 +106,7 @@ impl RpcClient {
|
||||||
Ok(stream)
|
Ok(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn replace_queue_with(&mut self, uuid: &str) -> Result<(), Box<dyn Error>> {
|
pub async fn replace_queue_with_node(&mut self, uuid: &str) -> Result<(), Box<dyn Error>> {
|
||||||
let replace_with_node_request = Request::new(ReplaceWithNodeRequest {
|
let replace_with_node_request = Request::new(ReplaceWithNodeRequest {
|
||||||
uuid: uuid.to_string(),
|
uuid: uuid.to_string(),
|
||||||
});
|
});
|
||||||
|
|
@ -118,12 +119,35 @@ impl RpcClient {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn toggle_play(&mut self) -> Result<(), Box<dyn Error>> {
|
pub async fn replace_queue_with_track(&mut self, uuid: &str) -> Result<(), Box<dyn Error>> {
|
||||||
let toggle_play_request = Request::new(TogglePlayRequest {});
|
let replace_with_track_request = Request::new(ReplaceWithTrackRequest {
|
||||||
|
uuid: uuid.to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
.toggle_play(toggle_play_request)
|
.replace_with_track(replace_with_track_request)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn toggle_play(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
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<dyn Error>> {
|
||||||
|
let set_current_track_request = Request::new(SetCurrentTrackRequest {
|
||||||
|
position: pos as u32,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.set_current_track(set_current_track_request)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue