Handle cbd-tui results

This commit is contained in:
chmanie 2023-06-12 12:06:38 +02:00
parent 0c66165598
commit 1433ae9e7f
10 changed files with 76 additions and 39 deletions

1
Cargo.lock generated
View File

@ -384,6 +384,7 @@ dependencies = [
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tonic", "tonic",
"tracing",
] ]
[[package]] [[package]]

View File

@ -1,11 +1,11 @@
use flume::Sender; use flume::Sender;
use std::path::Path; use std::path::Path;
use std::sync::atomic::AtomicU64; use std::sync::atomic::AtomicU64;
use std::thread;
use std::time::Duration; use std::time::Duration;
use std::{fs::File, sync::atomic::Ordering}; use std::{fs::File, sync::atomic::Ordering};
use symphonia::core::probe::Hint; use symphonia::core::probe::Hint;
use tracing::{debug, warn}; use tracing::{warn};
use url::Url; use url::Url;
use crate::decoder::{MediaInfo, SymphoniaDecoder}; use crate::decoder::{MediaInfo, SymphoniaDecoder};

View File

@ -15,3 +15,4 @@ tokio-stream = "0.1"
tonic = "0.9" tonic = "0.9"
notify-rust = "4.8.0" notify-rust = "4.8.0"
serde = "1.0.164" serde = "1.0.164"
tracing = "0.1.37"

View File

@ -11,6 +11,7 @@ use ratatui::{
}, },
Frame, Frame,
}; };
use tracing::error;
use crabidy_core::proto::crabidy::LibraryNode; use crabidy_core::proto::crabidy::LibraryNode;
@ -57,15 +58,21 @@ impl Library {
} }
pub fn ascend(&mut self) { pub fn ascend(&mut self) {
if let Some(parent) = self.parent.as_ref() { if let Some(parent) = self.parent.as_ref() {
self.tx.send(MessageFromUi::GetLibraryNode(parent.clone())); if let Err(err) = self.tx.send(MessageFromUi::GetLibraryNode(parent.clone())) {
error!("Send error: {}", err);
}
} }
} }
pub fn dive(&mut self) { pub fn dive(&mut self) {
if let Some(idx) = self.list_state.selected() { if let Some(idx) = self.list_state.selected() {
let item = &self.list[idx]; let item = &self.list[idx];
if let UiItemKind::Node = item.kind { if let UiItemKind::Node = item.kind {
self.tx if let Err(err) = self
.send(MessageFromUi::GetLibraryNode(item.uuid.clone())); .tx
.send(MessageFromUi::GetLibraryNode(item.uuid.clone()))
{
error!("Send error: {}", err);
}
} }
} }
} }
@ -73,7 +80,7 @@ impl Library {
if let Some(items) = self.get_selected() { if let Some(items) = self.get_selected() {
match self.tx.send(MessageFromUi::AppendTracks(items)) { match self.tx.send(MessageFromUi::AppendTracks(items)) {
Ok(_) => self.remove_marks(), Ok(_) => self.remove_marks(),
Err(_) => { /* FIXME: warn */ } Err(err) => error!("Send error: {}", err),
} }
} }
} }
@ -81,7 +88,7 @@ impl Library {
if let Some(items) = self.get_selected() { if let Some(items) = self.get_selected() {
match self.tx.send(MessageFromUi::QueueTracks(items)) { match self.tx.send(MessageFromUi::QueueTracks(items)) {
Ok(_) => self.remove_marks(), Ok(_) => self.remove_marks(),
Err(_) => { /* FIXME: warn */ } Err(err) => error!("Send error: {}", err),
} }
} }
} }
@ -89,7 +96,7 @@ impl Library {
if let Some(items) = self.get_selected() { if let Some(items) = self.get_selected() {
match self.tx.send(MessageFromUi::ReplaceQueue(items)) { match self.tx.send(MessageFromUi::ReplaceQueue(items)) {
Ok(_) => self.remove_marks(), Ok(_) => self.remove_marks(),
Err(_) => { /* FIXME: warn */ } Err(err) => error!("Send error: {}", err),
} }
} }
} }
@ -97,7 +104,7 @@ impl Library {
if let Some(items) = self.get_selected() { if let Some(items) = self.get_selected() {
match self.tx.send(MessageFromUi::InsertTracks(items, pos)) { match self.tx.send(MessageFromUi::InsertTracks(items, pos)) {
Ok(_) => self.remove_marks(), Ok(_) => self.remove_marks(),
Err(_) => { /* FIXME: warn */ } Err(err) => error!("Send error: {}", err),
} }
} }
} }

View File

@ -117,7 +117,7 @@ impl App {
let main = Layout::default() let main = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(f.size()); .split(full_screen);
self.library.render(f, main[0], library_focused); self.library.render(f, main[0], library_focused);

View File

@ -47,10 +47,14 @@ impl NowPlaying {
} }
pub fn update_track(&mut self, active: Option<Track>) { pub fn update_track(&mut self, active: Option<Track>) {
if let Some(track) = &active { if let Some(track) = &active {
let body = if let Some(ref album) = track.album {
format!("{} by {} ({})", track.title, track.artist, album.title)
} else {
format!("{} by {}", track.title, track.artist)
};
Notification::new() Notification::new()
.summary("Crabidy playing") .summary("Playing")
// FIXME: album .body(&body)
.body(&format!("{} by {}", track.title, track.artist))
.show() .show()
.unwrap(); .unwrap();
} }
@ -118,7 +122,7 @@ impl NowPlaying {
f.render_widget(media_info_p, now_playing_layout[0]); f.render_widget(media_info_p, now_playing_layout[0]);
if let (Some(position), Some(duration), Some(track)) = if let (Some(position), Some(duration), Some(_track)) =
(self.position, self.duration, &self.track) (self.position, self.duration, &self.track)
{ {
let pos = position.as_secs(); let pos = position.as_secs();

View File

@ -9,6 +9,7 @@ use ratatui::{
}, },
Frame, Frame,
}; };
use tracing::error;
use crabidy_core::proto::crabidy::Queue as QueueData; use crabidy_core::proto::crabidy::Queue as QueueData;
@ -33,20 +34,28 @@ impl Queue {
} }
} }
pub fn play_next(&self) { pub fn play_next(&self) {
self.tx.send(MessageFromUi::NextTrack); if let Err(err) = self.tx.send(MessageFromUi::NextTrack) {
error!("Send error: {}", err);
}
} }
pub fn play_prev(&self) { pub fn play_prev(&self) {
self.tx.send(MessageFromUi::PrevTrack); if let Err(err) = self.tx.send(MessageFromUi::PrevTrack) {
error!("Send error: {}", err);
}
} }
pub fn play_selected(&self) { pub fn play_selected(&self) {
if let Some(pos) = self.selected() { if let Some(pos) = self.selected() {
self.tx.send(MessageFromUi::SetCurrentTrack(pos)); if let Err(err) = self.tx.send(MessageFromUi::SetCurrentTrack(pos)) {
error!("Send error: {}", err);
}
} }
} }
pub fn remove_track(&mut self) { pub fn remove_track(&mut self) {
if let Some(pos) = self.selected() { if let Some(pos) = self.selected() {
// FIXME: mark multiple tracks on queue and remove them // FIXME: mark multiple tracks on queue and remove them
self.tx.send(MessageFromUi::RemoveTracks(vec![pos])); if let Err(err) = self.tx.send(MessageFromUi::RemoveTracks(vec![pos])) {
error!("Send error: {}", err);
}
} }
} }
pub fn update_position(&mut self, pos: usize) { pub fn update_position(&mut self, pos: usize) {
@ -57,10 +66,9 @@ impl Queue {
self.list = queue self.list = queue
.tracks .tracks
.iter() .iter()
.enumerate() .map(|track| UiItem {
.map(|(i, t)| UiItem { uuid: track.uuid.clone(),
uuid: t.uuid.clone(), title: format!("{} - {}", track.artist, track.title),
title: format!("{} - {}", t.artist, t.title),
kind: UiItemKind::Track, kind: UiItemKind::Track,
marked: false, marked: false,
is_queable: false, is_queable: false,

View File

@ -36,6 +36,7 @@ use ratatui::{backend::CrosstermBackend, Terminal};
use tokio::{fs, select, signal, task}; use tokio::{fs, select, signal, task};
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
use tonic::{transport::Channel, Request, Status, Streaming}; use tonic::{transport::Channel, Request, Status, Streaming};
use tracing::error;
use app::{App, MessageFromUi, MessageToUi, StatefulList, UiFocus}; use app::{App, MessageFromUi, MessageToUi, StatefulList, UiFocus};
use config::Config; use config::Config;
@ -44,6 +45,7 @@ use rpc::RpcClient;
static CONFIG: OnceLock<Config> = OnceLock::new(); static CONFIG: OnceLock<Config> = OnceLock::new();
#[tokio::main] #[tokio::main]
// FIXME: use anyhow, thiserror
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = CONFIG.get_or_init(|| crabidy_core::init_config("cbd-tui.toml")); let config = CONFIG.get_or_init(|| crabidy_core::init_config("cbd-tui.toml"));
@ -51,12 +53,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (tx, ui_rx): (Sender<MessageToUi>, Receiver<MessageToUi>) = flume::unbounded(); let (tx, ui_rx): (Sender<MessageToUi>, Receiver<MessageToUi>) = flume::unbounded();
// FIXME: unwrap // FIXME: unwrap
tokio::spawn(async move { orchestrate(config, (tx, rx)).await.unwrap() }); tokio::spawn(async move { orchestrate(config, (tx, rx)).await.unwrap() }).await?;
tokio::task::spawn_blocking(|| { tokio::task::spawn_blocking(|| {
run_ui(ui_tx, ui_rx); run_ui(ui_tx, ui_rx);
}) })
.await; .await?;
Ok(()) Ok(())
} }
@ -68,14 +70,16 @@ async fn orchestrate<'a>(
let mut rpc_client = rpc::RpcClient::connect(&config.server.address).await?; let mut rpc_client = rpc::RpcClient::connect(&config.server.address).await?;
if let Some(root_node) = rpc_client.get_library_node("node:/").await? { if let Some(root_node) = rpc_client.get_library_node("node:/").await? {
tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone())); tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone()))?;
} }
let init_data = rpc_client.init().await?; let init_data = rpc_client.init().await?;
tx.send_async(MessageToUi::Init(init_data)).await?; tx.send_async(MessageToUi::Init(init_data)).await?;
loop { loop {
poll(&mut rpc_client, &rx, &tx).await.ok(); if let Err(err) = poll(&mut rpc_client, &rx, &tx).await {
error!("Poll error: {}", err);
}
} }
} }
@ -89,7 +93,7 @@ async fn poll(
match msg { match msg {
MessageFromUi::GetLibraryNode(uuid) => { MessageFromUi::GetLibraryNode(uuid) => {
if let Some(node) = rpc_client.get_library_node(&uuid).await? { if let Some(node) = rpc_client.get_library_node(&uuid).await? {
tx.send(MessageToUi::ReplaceLibraryNode(node.clone())); tx.send(MessageToUi::ReplaceLibraryNode(node.clone()))?;
} }
}, },
MessageFromUi::AppendTracks(uuids) => { MessageFromUi::AppendTracks(uuids) => {
@ -211,7 +215,7 @@ fn run_ui(tx: Sender<MessageFromUi>, rx: Receiver<MessageToUi>) {
} }
} }
terminal.draw(|f| app.render(f)); terminal.draw(|f| app.render(f)).unwrap();
let timeout = tick_rate let timeout = tick_rate
.checked_sub(last_tick.elapsed()) .checked_sub(last_tick.elapsed())
@ -226,25 +230,39 @@ fn run_ui(tx: Sender<MessageFromUi>, rx: Receiver<MessageToUi>) {
} }
(_, KeyModifiers::NONE, KeyCode::Tab) => app.cycle_active(), (_, KeyModifiers::NONE, KeyCode::Tab) => app.cycle_active(),
(_, KeyModifiers::NONE, KeyCode::Char(' ')) => { (_, KeyModifiers::NONE, KeyCode::Char(' ')) => {
tx.send(MessageFromUi::TogglePlay); if let Err(err) = tx.send(MessageFromUi::TogglePlay) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::NONE, KeyCode::Char('r')) => { (_, KeyModifiers::NONE, KeyCode::Char('r')) => {
tx.send(MessageFromUi::RestartTrack); if let Err(err) = tx.send(MessageFromUi::RestartTrack) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::SHIFT, KeyCode::Char('J')) => { (_, KeyModifiers::SHIFT, KeyCode::Char('J')) => {
tx.send(MessageFromUi::ChangeVolume(-0.1)); if let Err(err) = tx.send(MessageFromUi::ChangeVolume(-0.1)) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::SHIFT, KeyCode::Char('K')) => { (_, KeyModifiers::SHIFT, KeyCode::Char('K')) => {
tx.send(MessageFromUi::ChangeVolume(0.1)); if let Err(err) = tx.send(MessageFromUi::ChangeVolume(0.1)) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::NONE, KeyCode::Char('m')) => { (_, KeyModifiers::NONE, KeyCode::Char('m')) => {
tx.send(MessageFromUi::ToggleMute); if let Err(err) = tx.send(MessageFromUi::ToggleMute) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::NONE, KeyCode::Char('z')) => { (_, KeyModifiers::NONE, KeyCode::Char('z')) => {
tx.send(MessageFromUi::ToggleShuffle); if let Err(err) = tx.send(MessageFromUi::ToggleShuffle) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::NONE, KeyCode::Char('x')) => { (_, KeyModifiers::NONE, KeyCode::Char('x')) => {
tx.send(MessageFromUi::ToggleRepeat); if let Err(err) = tx.send(MessageFromUi::ToggleRepeat) {
error!("Send error: {}", err);
}
} }
(_, KeyModifiers::CONTROL, KeyCode::Char('n')) => { (_, KeyModifiers::CONTROL, KeyCode::Char('n')) => {
app.queue.play_next(); app.queue.play_next();

View File

@ -72,9 +72,7 @@ impl RpcClient {
} }
pub async fn reconnect_update_stream(&mut self) { pub async fn reconnect_update_stream(&mut self) {
let update_stream = Self::get_update_stream(&mut self.client).await; self.update_stream = Self::get_update_stream(&mut self.client).await;
// FIXME: apparently mem::replace doesn't do anything here
mem::replace(&mut self.update_stream, update_stream);
} }
pub async fn init(&mut self) -> Result<InitResponse, Box<dyn Error>> { pub async fn init(&mut self) -> Result<InitResponse, Box<dyn Error>> {

View File

@ -73,7 +73,7 @@ where
if let Some(config_dir) = dirs::config_dir() { if let Some(config_dir) = dirs::config_dir() {
let dir = Path::new(&config_dir).join("crabidy"); let dir = Path::new(&config_dir).join("crabidy");
if !dir.is_dir() { if !dir.is_dir() {
create_dir_all(&dir); create_dir_all(&dir).expect("Could not create crabidy directory in config dir");
} }
let config_file_path = dir.join(config_file_name); let config_file_path = dir.join(config_file_name);
if !config_file_path.is_file() { if !config_file_path.is_file() {