add play/pause from the terminal ui

This commit is contained in:
Tsiry Sandratraina
2022-10-02 21:27:09 +03:00
parent 880c0f1f70
commit fa5a4e372b
10 changed files with 130 additions and 36 deletions

View File

@@ -63,9 +63,10 @@ impl PlaybackClient {
Ok(())
}
pub async fn current(&mut self) -> Result<Option<Track>, Box<dyn std::error::Error>> {
pub async fn current(&mut self) -> Result<(Option<Track>, bool), Box<dyn std::error::Error>> {
let request = tonic::Request::new(GetCurrentlyPlayingSongRequest {});
let response = self.client.get_currently_playing_song(request).await?;
Ok(response.into_inner().track)
let response = response.into_inner();
Ok((response.track, response.is_playing))
}
}

View File

@@ -50,8 +50,10 @@ pub trait PlayerEngine: Send + Sync {
async fn wait_for_tracklist(
mut event: UnboundedReceiver<PlayerEvent>,
) -> (Vec<Track>, Vec<Track>);
async fn get_current_track(&self) -> Option<Track>;
async fn wait_for_current_track(mut channel: UnboundedReceiver<PlayerEvent>) -> Option<Track>;
async fn get_current_track(&self) -> Option<(Option<Track>, bool)>;
async fn wait_for_current_track(
mut channel: UnboundedReceiver<PlayerEvent>,
) -> Option<(Option<Track>, bool)>;
}
#[derive(Clone)]
@@ -182,7 +184,7 @@ impl PlayerEngine for Player {
handle.join().unwrap()
}
async fn get_current_track(&self) -> Option<Track> {
async fn get_current_track(&self) -> Option<(Option<Track>, bool)> {
let channel = self.get_player_event_channel();
let handle = thread::spawn(move || {
Runtime::new()
@@ -204,10 +206,12 @@ impl PlayerEngine for Player {
(vec![], vec![])
}
async fn wait_for_current_track(mut channel: UnboundedReceiver<PlayerEvent>) -> Option<Track> {
async fn wait_for_current_track(
mut channel: UnboundedReceiver<PlayerEvent>,
) -> Option<(Option<Track>, bool)> {
while let Some(event) = channel.recv().await {
if matches!(event, PlayerEvent::CurrentTrack { .. }) {
return event.get_current_track().unwrap();
return event.get_current_track();
}
}
None
@@ -504,7 +508,8 @@ impl PlayerInternal {
fn handle_get_current_track(&mut self) {
let track = self.tracklist.current_track();
self.send_event(PlayerEvent::CurrentTrack { track });
let is_playing = self.state.is_playing();
self.send_event(PlayerEvent::CurrentTrack { track, is_playing });
}
}
@@ -655,11 +660,23 @@ pub enum PlayerEvent {
Playing,
Paused,
TimeToPreloadNextTrack,
EndOfTrack { is_last_track: bool },
VolumeSet { volume: u16 },
Error { track_id: String, error: String },
TracklistUpdated { tracks: (Vec<Track>, Vec<Track>) },
CurrentTrack { track: Option<Track> },
EndOfTrack {
is_last_track: bool,
},
VolumeSet {
volume: u16,
},
Error {
track_id: String,
error: String,
},
TracklistUpdated {
tracks: (Vec<Track>, Vec<Track>),
},
CurrentTrack {
track: Option<Track>,
is_playing: bool,
},
}
impl PlayerEvent {
@@ -679,10 +696,10 @@ impl PlayerEvent {
}
}
pub fn get_current_track(&self) -> Option<Option<Track>> {
pub fn get_current_track(&self) -> Option<(Option<Track>, bool)> {
use PlayerEvent::*;
match self {
CurrentTrack { track, .. } => Some(track.clone()),
CurrentTrack { track, is_playing } => Some((track.clone(), is_playing.clone())),
_ => None,
}
}

View File

@@ -27,7 +27,7 @@ pub async fn scan_directory(
if mime == "audio/mpeg"
|| mime == "audio/mp4"
|| mime == "audio/ogg"
// || mime == "audio/ogg"
|| mime == "audio/m4a"
|| mime == "audio/aac"
{

View File

@@ -8,6 +8,7 @@ message GetCurrentlyPlayingSongRequest {}
message GetCurrentlyPlayingSongResponse {
metadata.v1alpha1.Track track = 1;
bool is_playing = 2;
}
message GetPlaybackStateRequest {}

View File

@@ -32,10 +32,24 @@ impl PlaybackService for Playback {
) -> Result<tonic::Response<GetCurrentlyPlayingSongResponse>, tonic::Status> {
let player = self.player.lock().await;
let track = player.get_current_track().await;
if track.is_none() {
let response = GetCurrentlyPlayingSongResponse { track: None };
let response = GetCurrentlyPlayingSongResponse {
track: None,
is_playing: false,
};
return Ok(tonic::Response::new(response));
}
let (track, is_playing) = track.unwrap();
if track.is_none() {
let response = GetCurrentlyPlayingSongResponse {
track: None,
is_playing: false,
};
return Ok(tonic::Response::new(response));
}
let track = track.unwrap();
let response = GetCurrentlyPlayingSongResponse {
track: Some(Track {
@@ -55,6 +69,7 @@ impl PlaybackService for Playback {
}),
..Default::default()
}),
is_playing,
};
Ok(tonic::Response::new(response))
}

View File

@@ -142,7 +142,9 @@ impl App {
pub fn increase_volume(&mut self) {}
pub fn toggle_playback(&mut self) {}
pub fn toggle_playback(&mut self) {
self.dispatch(IoEvent::TogglePlayback);
}
pub fn seek_forwards(&mut self) {}

View File

@@ -232,7 +232,7 @@ pub async fn parse_args(matches: ArgMatches) -> Result<(), Box<dyn std::error::E
if let Some(_) = matches.subcommand_matches("current") {
let mut client = PlaybackClient::new().await?;
let result = client.current().await?;
let (result, _) = client.current().await?;
if result.is_none() {
println!("No song is currently playing");
return Ok(());

View File

@@ -5,6 +5,24 @@ use super::common_key_events;
pub fn handler(key: Key, app: &mut App) {
match key {
k if common_key_events::left_event(k) => common_key_events::handle_left_event(app),
k if common_key_events::down_event(k) => {
let next_index = common_key_events::on_down_press_handler(
&app.track_table.tracks,
Some(app.track_table.selected_index),
);
app.track_table.selected_index = next_index;
}
k if common_key_events::up_event(k) => {
let next_index = common_key_events::on_up_press_handler(
&app.track_table.tracks,
Some(app.track_table.selected_index),
);
app.track_table.selected_index = next_index;
}
k if common_key_events::high_event(k) => {
let next_index = common_key_events::on_high_press_handler();
app.track_table.selected_index = next_index;
}
_ => (),
}
}

View File

@@ -22,8 +22,7 @@ pub enum IoEvent {
Shuffle(bool),
Repeat(bool),
GetCurrentPlayback,
Play,
Pause,
TogglePlayback,
}
pub struct Network<'a> {
@@ -66,8 +65,7 @@ impl<'a> Network<'a> {
IoEvent::Shuffle(enable) => self.shuffle(enable).await,
IoEvent::Repeat(enable) => self.repeat(enable).await,
IoEvent::GetCurrentPlayback => self.get_current_playback().await,
IoEvent::Play => self.play().await,
IoEvent::Pause => self.pause().await,
IoEvent::TogglePlayback => self.toggle_playback().await,
}
}
@@ -174,11 +172,11 @@ impl<'a> Network<'a> {
todo!()
}
async fn play(&mut self) -> Result<(), Box<dyn std::error::Error>> {
todo!()
}
async fn pause(&mut self) -> Result<(), Box<dyn std::error::Error>> {
todo!()
async fn toggle_playback(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let (_, is_playing) = self.playback.current().await?;
if is_playing {
return self.playback.pause().await;
}
self.playback.play().await
}
}

View File

@@ -558,22 +558,64 @@ pub fn draw_play_queue<B>(f: &mut Frame<B>, app: &App, layout_chunk: Rect)
where
B: Backend,
{
let track_items: Vec<String> = Vec::new();
let header = TableHeader {
id: TableId::Song,
items: vec![
TableHeaderItem {
id: ColumnId::Title,
text: "Title",
width: get_percentage_width(layout_chunk.width, 0.3),
},
TableHeaderItem {
text: "Artist",
width: get_percentage_width(layout_chunk.width, 0.3),
..Default::default()
},
TableHeaderItem {
text: "Album",
width: get_percentage_width(layout_chunk.width, 0.3),
..Default::default()
},
TableHeaderItem {
text: "Duration",
width: get_percentage_width(layout_chunk.width, 0.1),
..Default::default()
},
],
};
let items = app
.track_table
.tracks
.iter()
.map(|item| TableItem {
id: item.id.clone(),
format: vec![
item.title.clone(),
item.artists
.iter()
.map(|a| a.name.to_owned())
.collect::<Vec<String>>()
.join(", "),
item.album.clone().unwrap_or_default().title,
millis_to_minutes((item.duration * 1000.0) as u128),
],
})
.collect::<Vec<TableItem>>();
let current_route = app.get_current_route();
let highlight_state = (
current_route.active_block == ActiveBlock::PlayQueue,
current_route.hovered_block == ActiveBlock::PlayQueue,
);
draw_selectable_list(
draw_table(
f,
app,
layout_chunk,
"Play Queue",
&track_items,
("Play Queue", &header),
&items,
app.track_table.selected_index,
highlight_state,
None,
);
)
}