Implement multi track queue, replace, append, insert
This commit is contained in:
parent
06c02ac75c
commit
773cb511e0
|
|
@ -31,7 +31,7 @@ use std::{
|
|||
collections::HashMap,
|
||||
error::Error,
|
||||
fmt, io,
|
||||
ops::Div,
|
||||
ops::{Div, IndexMut},
|
||||
println, thread,
|
||||
time::{Duration, Instant},
|
||||
vec,
|
||||
|
|
@ -46,7 +46,9 @@ const COLOR_PRIMARY: Color = Color::Rgb(129, 161, 193);
|
|||
// const COLOR_PRIMARY_DARK: Color = Color::Rgb(94, 129, 172);
|
||||
const COLOR_PRIMARY_DARK: Color = Color::Rgb(59, 66, 82);
|
||||
const COLOR_SECONDARY: Color = Color::Rgb(180, 142, 173);
|
||||
// const COLOR_RED: Color = Color::Rgb(191, 97, 106);
|
||||
const COLOR_RED: Color = Color::Rgb(191, 97, 106);
|
||||
const COLOR_GREEN: Color = Color::Rgb(163, 190, 140);
|
||||
// const COLOR_ORANGE: Color = Color::Rgb(208, 135, 112);
|
||||
// const COLOR_BRIGHT: Color = Color::Rgb(216, 222, 233);
|
||||
|
||||
trait ListView {
|
||||
|
|
@ -162,6 +164,8 @@ struct UiItem {
|
|||
uuid: String,
|
||||
title: String,
|
||||
kind: UiItemKind,
|
||||
marked: bool,
|
||||
is_queable: bool,
|
||||
}
|
||||
|
||||
struct QueueView {
|
||||
|
|
@ -197,12 +201,10 @@ impl QueueView {
|
|||
self.tx.send(MessageFromUi::SetCurrentTrack(pos));
|
||||
}
|
||||
}
|
||||
fn insert_track(&mut self) {
|
||||
todo!();
|
||||
}
|
||||
fn remove_track(&mut self) {
|
||||
if let Some(pos) = self.selected() {
|
||||
self.tx.send(MessageFromUi::RemoveTrack(pos));
|
||||
// FIXME: mark multiple tracks on queue and remove them
|
||||
self.tx.send(MessageFromUi::RemoveTracks(vec![pos]));
|
||||
}
|
||||
}
|
||||
fn update_position(&mut self, pos: usize) {
|
||||
|
|
@ -218,6 +220,8 @@ impl QueueView {
|
|||
uuid: t.uuid.clone(),
|
||||
title: format!("{} - {}", t.artist, t.title),
|
||||
kind: UiItemKind::Track,
|
||||
marked: false,
|
||||
is_queable: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -256,9 +260,18 @@ impl ListView for LibraryView {
|
|||
}
|
||||
|
||||
impl LibraryView {
|
||||
fn get_selected(&self) -> Option<&UiItem> {
|
||||
fn get_selected(&self) -> Option<Vec<String>> {
|
||||
if self.list.iter().any(|i| i.marked) {
|
||||
return Some(
|
||||
self.list
|
||||
.iter()
|
||||
.filter(|i| i.marked)
|
||||
.map(|i| i.uuid.to_string())
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
if let Some(idx) = self.list_state.selected() {
|
||||
return Some(&self.list[idx]);
|
||||
return Some(vec![self.list[idx].uuid.to_string()]);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -268,7 +281,8 @@ impl LibraryView {
|
|||
}
|
||||
}
|
||||
fn dive(&mut self) {
|
||||
if let Some(item) = self.get_selected() {
|
||||
if let Some(idx) = self.list_state.selected() {
|
||||
let item = &self.list[idx];
|
||||
if let UiItemKind::Node = item.kind {
|
||||
self.tx
|
||||
.send(MessageFromUi::GetLibraryNode(item.uuid.clone()));
|
||||
|
|
@ -276,23 +290,57 @@ impl LibraryView {
|
|||
}
|
||||
}
|
||||
fn queue_append(&mut self) {
|
||||
if let Some(item) = self.get_selected() {
|
||||
self.tx.send(MessageFromUi::AppendTrack(item.uuid.clone()));
|
||||
if let Some(items) = self.get_selected() {
|
||||
match self.tx.send(MessageFromUi::AppendTracks(items)) {
|
||||
Ok(_) => self.remove_marks(),
|
||||
Err(_) => { /* FIXME: warn */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
fn queue_queue(&mut self) {
|
||||
if let Some(item) = self.get_selected() {
|
||||
self.tx.send(MessageFromUi::QueueTrack(item.uuid.clone()));
|
||||
if let Some(items) = self.get_selected() {
|
||||
match self.tx.send(MessageFromUi::QueueTracks(items)) {
|
||||
Ok(_) => self.remove_marks(),
|
||||
Err(_) => { /* FIXME: warn */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
fn queue_replace(&mut self) {
|
||||
if let Some(item) = self.get_selected() {
|
||||
self.tx.send(MessageFromUi::ReplaceQueue(item.uuid.clone()));
|
||||
if let Some(items) = self.get_selected() {
|
||||
match self.tx.send(MessageFromUi::ReplaceQueue(items)) {
|
||||
Ok(_) => self.remove_marks(),
|
||||
Err(_) => { /* FIXME: warn */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
fn queue_insert(&mut self, pos: usize) {
|
||||
if let Some(items) = self.get_selected() {
|
||||
match self.tx.send(MessageFromUi::InsertTracks(items, pos)) {
|
||||
Ok(_) => self.remove_marks(),
|
||||
Err(_) => { /* FIXME: warn */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
fn prev_selected(&self) -> usize {
|
||||
*self.positions.get(&self.uuid).unwrap_or(&0)
|
||||
}
|
||||
fn toggle_mark(&mut self) {
|
||||
if let Some(idx) = self.list_state.selected() {
|
||||
let mut item = &mut self.list[idx];
|
||||
if !item.is_queable {
|
||||
return;
|
||||
}
|
||||
item.marked = !item.marked;
|
||||
}
|
||||
}
|
||||
fn remove_marks(&mut self) {
|
||||
if self.list.iter().any(|i| i.marked) {
|
||||
self.list
|
||||
.iter_mut()
|
||||
.filter(|i| i.marked)
|
||||
.for_each(|i| i.marked = false);
|
||||
}
|
||||
}
|
||||
fn update(&mut self, node: LibraryNode) {
|
||||
if node.tracks.is_empty() && node.children.is_empty() {
|
||||
return;
|
||||
|
|
@ -312,6 +360,8 @@ impl LibraryView {
|
|||
uuid: t.uuid.clone(),
|
||||
title: format!("{} - {}", t.artist, t.title),
|
||||
kind: UiItemKind::Track,
|
||||
marked: false,
|
||||
is_queable: true,
|
||||
})
|
||||
.collect();
|
||||
} else {
|
||||
|
|
@ -323,6 +373,8 @@ impl LibraryView {
|
|||
uuid: c.uuid.clone(),
|
||||
title: c.title.clone(),
|
||||
kind: UiItemKind::Node,
|
||||
marked: false,
|
||||
is_queable: c.is_queable,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
|
@ -416,11 +468,11 @@ enum MessageToUi {
|
|||
// FIXME: Rename this
|
||||
enum MessageFromUi {
|
||||
GetLibraryNode(String),
|
||||
AppendTrack(String),
|
||||
QueueTrack(String),
|
||||
InsertTrack(String, usize),
|
||||
RemoveTrack(usize),
|
||||
ReplaceQueue(String),
|
||||
AppendTracks(Vec<String>),
|
||||
QueueTracks(Vec<String>),
|
||||
InsertTracks(Vec<String>, usize),
|
||||
RemoveTracks(Vec<usize>),
|
||||
ReplaceQueue(Vec<String>),
|
||||
NextTrack,
|
||||
PrevTrack,
|
||||
RestartTrack,
|
||||
|
|
@ -443,20 +495,20 @@ async fn poll(
|
|||
tx.send(MessageToUi::ReplaceLibraryNode(node.clone()));
|
||||
}
|
||||
},
|
||||
MessageFromUi::AppendTrack(uuid) => {
|
||||
rpc_client.append_track(&uuid).await?
|
||||
MessageFromUi::AppendTracks(uuids) => {
|
||||
rpc_client.append_tracks(uuids).await?
|
||||
}
|
||||
MessageFromUi::QueueTrack(uuid) => {
|
||||
rpc_client.queue_track(&uuid).await?
|
||||
MessageFromUi::QueueTracks(uuids) => {
|
||||
rpc_client.queue_tracks(uuids).await?
|
||||
}
|
||||
MessageFromUi::InsertTrack(uuid, pos) => {
|
||||
rpc_client.insert_track(&uuid, pos).await?
|
||||
MessageFromUi::InsertTracks(uuids, pos) => {
|
||||
rpc_client.insert_tracks(uuids, pos).await?
|
||||
}
|
||||
MessageFromUi::RemoveTrack(pos) => {
|
||||
rpc_client.remove_track(pos).await?
|
||||
MessageFromUi::RemoveTracks(positions) => {
|
||||
rpc_client.remove_tracks(positions).await?
|
||||
}
|
||||
MessageFromUi::ReplaceQueue(uuid) => {
|
||||
rpc_client.replace_queue(&uuid).await?
|
||||
MessageFromUi::ReplaceQueue(uuids) => {
|
||||
rpc_client.replace_queue(uuids).await?
|
||||
}
|
||||
MessageFromUi::NextTrack => {
|
||||
rpc_client.next_track().await?
|
||||
|
|
@ -502,7 +554,7 @@ async fn poll(
|
|||
async fn orchestrate<'a>(
|
||||
(tx, rx): (Sender<MessageToUi>, Receiver<MessageFromUi>),
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut rpc_client = rpc::RpcClient::connect("http://192.168.178.32:50051").await?;
|
||||
let mut rpc_client = rpc::RpcClient::connect("http://192.168.1.149:50051").await?;
|
||||
|
||||
if let Some(root_node) = rpc_client.get_library_node("node:/").await? {
|
||||
tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone()));
|
||||
|
|
@ -599,7 +651,7 @@ fn run_ui(tx: Sender<MessageFromUi>, rx: Receiver<MessageToUi>) {
|
|||
(_, KeyModifiers::NONE, KeyCode::Char(' ')) => {
|
||||
tx.send(MessageFromUi::TogglePlay);
|
||||
}
|
||||
(_, KeyModifiers::NONE, KeyCode::Char('p')) => {
|
||||
(_, KeyModifiers::NONE, KeyCode::Char('r')) => {
|
||||
tx.send(MessageFromUi::RestartTrack);
|
||||
}
|
||||
(_, KeyModifiers::SHIFT, KeyCode::Char('J')) => {
|
||||
|
|
@ -650,6 +702,14 @@ fn run_ui(tx: Sender<MessageFromUi>, rx: Receiver<MessageToUi>) {
|
|||
(UiFocus::Library, KeyModifiers::NONE, KeyCode::Enter) => {
|
||||
app.library.queue_replace();
|
||||
}
|
||||
(UiFocus::Library, KeyModifiers::NONE, KeyCode::Char('s')) => {
|
||||
app.library.toggle_mark();
|
||||
}
|
||||
(UiFocus::Queue, KeyModifiers::NONE, KeyCode::Char('p')) => {
|
||||
if let Some(selected) = app.queue.selected() {
|
||||
app.library.queue_insert(selected);
|
||||
}
|
||||
}
|
||||
(UiFocus::Queue, KeyModifiers::NONE, KeyCode::Char('g')) => {
|
||||
app.queue.first();
|
||||
}
|
||||
|
|
@ -711,7 +771,21 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
|||
.library
|
||||
.list
|
||||
.iter()
|
||||
.map(|i| ListItem::new(Span::from(i.title.to_string())))
|
||||
.map(|i| {
|
||||
let text = if i.marked {
|
||||
format!("* {}", i.title)
|
||||
} else {
|
||||
i.title.to_string()
|
||||
};
|
||||
let style = if i.marked {
|
||||
Style::default()
|
||||
.fg(COLOR_GREEN)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default()
|
||||
};
|
||||
return ListItem::new(Span::from(text)).style(style);
|
||||
})
|
||||
.collect();
|
||||
|
||||
let library_list = List::new(library_items)
|
||||
|
|
@ -750,13 +824,14 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
|||
.enumerate()
|
||||
.map(|(idx, item)| {
|
||||
let active = idx == app.queue.current_position;
|
||||
|
||||
let title = if active {
|
||||
format!("> {}", item.title)
|
||||
} else {
|
||||
item.title.to_string()
|
||||
};
|
||||
let style = if active {
|
||||
Style::default().add_modifier(Modifier::BOLD)
|
||||
Style::default().fg(COLOR_RED).add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -104,43 +104,41 @@ impl RpcClient {
|
|||
Err(Box::new(RpcClientError::NotFound))
|
||||
}
|
||||
|
||||
pub async fn append_track(&mut self, uuid: &str) -> Result<(), Box<dyn Error>> {
|
||||
let append_request = Request::new(AppendRequest {
|
||||
uuid: vec![uuid.to_string()],
|
||||
});
|
||||
pub async fn append_tracks(&mut self, uuids: Vec<String>) -> Result<(), Box<dyn Error>> {
|
||||
let append_request = Request::new(AppendRequest { uuids });
|
||||
self.client.append(append_request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn queue_track(&mut self, uuid: &str) -> Result<(), Box<dyn Error>> {
|
||||
let queue_request = Request::new(QueueRequest {
|
||||
uuid: vec![uuid.to_string()],
|
||||
});
|
||||
pub async fn queue_tracks(&mut self, uuids: Vec<String>) -> Result<(), Box<dyn Error>> {
|
||||
let queue_request = Request::new(QueueRequest { uuids });
|
||||
self.client.queue(queue_request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_track(&mut self, uuid: &str, pos: usize) -> Result<(), Box<dyn Error>> {
|
||||
pub async fn insert_tracks(
|
||||
&mut self,
|
||||
uuids: Vec<String>,
|
||||
pos: usize,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let insert_request = Request::new(InsertRequest {
|
||||
uuid: vec![uuid.to_string()],
|
||||
uuids,
|
||||
position: pos as u32,
|
||||
});
|
||||
self.client.insert(insert_request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_track(&mut self, pos: usize) -> Result<(), Box<dyn Error>> {
|
||||
pub async fn remove_tracks(&mut self, positions: Vec<usize>) -> Result<(), Box<dyn Error>> {
|
||||
let remove_request = Request::new(RemoveRequest {
|
||||
positions: vec![pos as u32],
|
||||
positions: positions.iter().map(|p| *p as u32).collect(),
|
||||
});
|
||||
self.client.remove(remove_request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn replace_queue(&mut self, uuid: &str) -> Result<(), Box<dyn Error>> {
|
||||
let replace_request = Request::new(ReplaceRequest {
|
||||
uuid: vec![uuid.to_string()],
|
||||
});
|
||||
pub async fn replace_queue(&mut self, uuids: Vec<String>) -> Result<(), Box<dyn Error>> {
|
||||
let replace_request = Request::new(ReplaceRequest { uuids });
|
||||
self.client.replace(replace_request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue