mirror of
https://github.com/tsirysndr/music-player.git
synced 2026-01-10 21:58:02 -05:00
add play/pause from the terminal ui
This commit is contained in:
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ message GetCurrentlyPlayingSongRequest {}
|
||||
|
||||
message GetCurrentlyPlayingSongResponse {
|
||||
metadata.v1alpha1.Track track = 1;
|
||||
bool is_playing = 2;
|
||||
}
|
||||
|
||||
message GetPlaybackStateRequest {}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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(());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user