add global play/pause/stop buttons + necessary state logic

This commit is contained in:
Henrik Friedrichsen
2019-03-02 18:37:56 +01:00
parent 70d68f8854
commit 3d1673a7a4
3 changed files with 84 additions and 15 deletions

View File

@@ -1,8 +1,11 @@
use crossbeam_channel::{unbounded, Receiver, Sender, TryIter};
use cursive::{CbFunc, Cursive};
use spotify::PlayerState;
pub enum Event {
QueueUpdate,
PlayState(PlayerState),
}
pub type EventSender = Sender<Event>;

View File

@@ -87,12 +87,28 @@ fn main() {
let queue = Arc::new(Mutex::new(queue::Queue::new(event_manager.clone())));
let spotify = Arc::new(spotify::Spotify::new(
event_manager.clone(),
cfg.username,
cfg.password,
cfg.client_id,
queue.clone(),
));
// global player keybindings (play, pause, stop)
{
let spotify = spotify.clone();
cursive.add_global_callback('P', move |_s| {
spotify.toggleplayback();
});
}
{
let spotify = spotify.clone();
cursive.add_global_callback('S', move |_s| {
spotify.stop();
});
}
let track = TextView::new("Track Title");
let pos = TextView::new("[0:00/0:00]");
let status = LinearLayout::horizontal().child(track).child(pos);
@@ -133,6 +149,7 @@ fn main() {
trace!("event received");
match event {
Event::QueueUpdate => queue.redraw(&mut cursive),
Event::PlayState(state) => spotify.updatestate(state),
}
}
}

View File

@@ -25,8 +25,10 @@ use tokio_core::reactor::Core;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::RwLock;
use std::thread;
use events::{Event, EventManager};
use queue::Queue;
enum WorkerCommand {
@@ -36,12 +38,21 @@ enum WorkerCommand {
Stop,
}
pub enum PlayerState {
Playing,
Paused,
Stopped,
}
pub struct Spotify {
pub state: RwLock<PlayerState>,
pub api: SpotifyAPI,
channel: mpsc::UnboundedSender<WorkerCommand>,
events: EventManager,
}
struct Worker {
events: EventManager,
commands: mpsc::UnboundedReceiver<WorkerCommand>,
player: Player,
play_task: Box<futures::Future<Item = (), Error = oneshot::Canceled>>,
@@ -50,11 +61,13 @@ struct Worker {
impl Worker {
fn new(
events: EventManager,
commands: mpsc::UnboundedReceiver<WorkerCommand>,
player: Player,
queue: Arc<Mutex<Queue>>,
) -> Worker {
Worker {
events: events,
commands: commands,
player: player,
play_task: Box::new(futures::empty()),
@@ -80,9 +93,18 @@ impl futures::Future for Worker {
self.play_task = Box::new(self.player.load(track, false, 0));
info!("player loading track..");
}
WorkerCommand::Play => self.player.play(),
WorkerCommand::Pause => self.player.pause(),
WorkerCommand::Stop => self.player.stop(),
WorkerCommand::Play => {
self.player.play();
self.events.send(Event::PlayState(PlayerState::Playing));
},
WorkerCommand::Pause => {
self.player.pause();
self.events.send(Event::PlayState(PlayerState::Paused));
}
WorkerCommand::Stop => {
self.player.stop();
self.events.send(Event::PlayState(PlayerState::Stopped));
}
}
}
match self.play_task.poll() {
@@ -97,6 +119,11 @@ impl futures::Future for Worker {
SpotifyId::from_base62(&track.id).expect("could not load track");
self.play_task = Box::new(self.player.load(trackid, false, 0));
self.player.play();
self.events.send(Event::PlayState(PlayerState::Playing));
}
else {
self.events.send(Event::PlayState(PlayerState::Stopped));
}
}
Ok(Async::NotReady) => (),
@@ -117,6 +144,7 @@ impl futures::Future for Worker {
impl Spotify {
pub fn new(
events: EventManager,
user: String,
password: String,
client_id: String,
@@ -132,29 +160,36 @@ impl Spotify {
let (tx, rx) = mpsc::unbounded();
let (p, c) = oneshot::channel();
thread::spawn(move || {
Spotify::worker(
rx,
p,
session_config,
player_config,
credentials,
client_id,
queue,
)
});
{
let events = events.clone();
thread::spawn(move || {
Spotify::worker(
events,
rx,
p,
session_config,
player_config,
credentials,
client_id,
queue,
)
});
}
let token = c.wait().unwrap();
debug!("token received: {:?}", token);
let api = SpotifyAPI::default().access_token(&token.access_token);
Spotify {
state: RwLock::new(PlayerState::Stopped),
api: api,
channel: tx,
events: events,
}
}
fn worker(
events: EventManager,
commands: mpsc::UnboundedReceiver<WorkerCommand>,
token_channel: oneshot::Sender<Token>,
session_config: SessionConfig,
@@ -179,7 +214,7 @@ impl Spotify {
let (player, _eventchannel) =
Player::new(player_config, session, None, move || (backend)(None));
let worker = Worker::new(commands, player, queue);
let worker = Worker::new(events, commands, player, queue);
debug!("worker thread ready.");
core.run(worker).unwrap();
debug!("worker thread finished.");
@@ -196,11 +231,25 @@ impl Spotify {
.unwrap();
}
pub fn updatestate(&self, newstate: PlayerState) {
let mut state = self.state.write().expect("could not acquire write lock on player state");
*state = newstate;
}
pub fn play(&self) {
info!("play()");
self.channel.unbounded_send(WorkerCommand::Play).unwrap();
}
pub fn toggleplayback(&self) {
let state = self.state.read().expect("could not acquire read lock on player state");
match *state {
PlayerState::Playing => self.pause(),
PlayerState::Paused => self.play(),
_ => (),
}
}
pub fn pause(&self) {
info!("pause()");
self.channel.unbounded_send(WorkerCommand::Pause).unwrap();