mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Add a SpircDelegate abstraction.
This commit is contained in:
parent
cdd939e88e
commit
1264394838
4 changed files with 117 additions and 49 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#![feature(plugin,scoped,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,arc_weak,append,future)]
|
#![feature(plugin,scoped,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,arc_weak,append,future)]
|
||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
#![allow(unused_imports,dead_code)]
|
//#![allow(unused_imports,dead_code)]
|
||||||
|
|
||||||
#![plugin(protobuf_macros)]
|
#![plugin(protobuf_macros)]
|
||||||
#[macro_use] extern crate lazy_static;
|
#[macro_use] extern crate lazy_static;
|
||||||
|
|
|
@ -44,7 +44,7 @@ fn main() {
|
||||||
|
|
||||||
let player = Player::new(&session);
|
let player = Player::new(&session);
|
||||||
|
|
||||||
let mut spirc_manager = SpircManager::new(&session, &player, username, name);
|
let mut spirc_manager = SpircManager::new(&session, player, username, name);
|
||||||
spirc_manager.run();
|
spirc_manager.run();
|
||||||
|
|
||||||
poll_thread.join();
|
poll_thread.join();
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
use portaudio;
|
use portaudio;
|
||||||
use vorbis;
|
use vorbis;
|
||||||
use std::sync::{mpsc, Mutex, Arc, Condvar};
|
use std::sync::{mpsc, Mutex, Arc, Condvar, MutexGuard};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use metadata::TrackRef;
|
use metadata::TrackRef;
|
||||||
use session::Session;
|
use session::Session;
|
||||||
use audio_file::AudioFile;
|
|
||||||
use audio_decrypt::AudioDecrypt;
|
use audio_decrypt::AudioDecrypt;
|
||||||
use util::{self, SpotifyId, Subfile};
|
use util::{self, SpotifyId, Subfile};
|
||||||
use librespot_protocol::spirc::PlayStatus;
|
use spirc::{SpircState, SpircDelegate, PlayStatus};
|
||||||
|
|
||||||
pub enum PlayerCommand {
|
pub struct Player<'s> {
|
||||||
Load(SpotifyId, bool, u32),
|
state: Arc<(Mutex<PlayerState>, Condvar)>,
|
||||||
Play,
|
|
||||||
Pause,
|
commands: mpsc::Sender<PlayerCommand>,
|
||||||
Stop,
|
|
||||||
Seek(u32)
|
#[allow(dead_code)]
|
||||||
|
thread: thread::JoinGuard<'s, ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlayerState {
|
pub struct PlayerState {
|
||||||
pub status: PlayStatus,
|
status: PlayStatus,
|
||||||
pub position_ms: u32,
|
position_ms: u32,
|
||||||
pub position_measured_at: i64,
|
position_measured_at: i64,
|
||||||
pub update_time: i64
|
update_time: i64
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PlayerInternal<'s> {
|
struct PlayerInternal<'s> {
|
||||||
|
@ -32,13 +32,12 @@ struct PlayerInternal<'s> {
|
||||||
commands: mpsc::Receiver<PlayerCommand>,
|
commands: mpsc::Receiver<PlayerCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Player<'s> {
|
enum PlayerCommand {
|
||||||
pub state: Arc<(Mutex<PlayerState>, Condvar)>,
|
Load(SpotifyId, bool, u32),
|
||||||
|
Play,
|
||||||
commands: mpsc::Sender<PlayerCommand>,
|
Pause,
|
||||||
|
Stop,
|
||||||
#[allow(dead_code)]
|
Seek(u32)
|
||||||
thread: thread::JoinGuard<'s, ()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'s> Player<'s> {
|
impl <'s> Player<'s> {
|
||||||
|
@ -67,7 +66,7 @@ impl <'s> Player<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command(&self, cmd: PlayerCommand) {
|
fn command(&self, cmd: PlayerCommand) {
|
||||||
self.commands.send(cmd).unwrap();
|
self.commands.send(cmd).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,4 +182,51 @@ impl <'s> PlayerInternal<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl <'s> SpircDelegate for Player<'s> {
|
||||||
|
type State = PlayerState;
|
||||||
|
|
||||||
|
fn load(&self, track: SpotifyId,
|
||||||
|
start_playing: bool, position_ms: u32) {
|
||||||
|
self.command(PlayerCommand::Load(track, start_playing, position_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn play(&self) {
|
||||||
|
self.command(PlayerCommand::Play)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pause(&self) {
|
||||||
|
self.command(PlayerCommand::Pause)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&self) {
|
||||||
|
self.command(PlayerCommand::Stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seek(&self, position_ms: u32) {
|
||||||
|
self.command(PlayerCommand::Seek(position_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> MutexGuard<Self::State> {
|
||||||
|
self.state.0.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_update<'a>(&'a self, guard: MutexGuard<'a, Self::State>)
|
||||||
|
-> MutexGuard<'a, Self::State> {
|
||||||
|
self.state.1.wait(guard).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpircState for PlayerState {
|
||||||
|
fn status(&self) -> PlayStatus {
|
||||||
|
return self.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn position(&self) -> (u32, i64) {
|
||||||
|
return (self.position_ms, self.position_measured_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_time(&self) -> i64 {
|
||||||
|
return self.update_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
74
src/spirc.rs
74
src/spirc.rs
|
@ -1,18 +1,17 @@
|
||||||
|
|
||||||
use protobuf::{self, Message};
|
use protobuf::{self, Message};
|
||||||
|
|
||||||
use util;
|
use util;
|
||||||
use session::{Config, Session};
|
use session::Session;
|
||||||
use util::SpotifyId;
|
use util::SpotifyId;
|
||||||
use util::version::version_string;
|
use util::version::version_string;
|
||||||
use player::{Player, PlayerCommand};
|
|
||||||
use mercury::{MercuryRequest, MercuryMethod};
|
use mercury::{MercuryRequest, MercuryMethod};
|
||||||
|
use std::sync::MutexGuard;
|
||||||
|
|
||||||
use librespot_protocol as protocol;
|
use librespot_protocol as protocol;
|
||||||
use librespot_protocol::spirc::PlayStatus;
|
pub use librespot_protocol::spirc::PlayStatus;
|
||||||
|
|
||||||
pub struct SpircManager<'s> {
|
pub struct SpircManager<'s, D: SpircDelegate> {
|
||||||
player: &'s Player<'s>,
|
delegate: D,
|
||||||
session: &'s Session,
|
session: &'s Session,
|
||||||
|
|
||||||
username: String,
|
username: String,
|
||||||
|
@ -37,11 +36,33 @@ pub struct SpircManager<'s> {
|
||||||
track: Option<SpotifyId>
|
track: Option<SpotifyId>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'s> SpircManager<'s> {
|
pub trait SpircDelegate {
|
||||||
pub fn new(session: &'s Session, player: &'s Player<'s>, username: String, name: String) -> SpircManager<'s> {
|
type State : SpircState;
|
||||||
|
|
||||||
|
fn load(&self, track: SpotifyId,
|
||||||
|
start_playing: bool, position_ms: u32);
|
||||||
|
fn play(&self);
|
||||||
|
fn pause(&self);
|
||||||
|
fn seek(&self, position_ms: u32);
|
||||||
|
fn stop(&self);
|
||||||
|
|
||||||
|
fn state(&self) -> MutexGuard<Self::State>;
|
||||||
|
fn wait_update<'a>(&'a self, guard: MutexGuard<'a, Self::State>)
|
||||||
|
-> MutexGuard<'a, Self::State>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SpircState {
|
||||||
|
fn status(&self) -> PlayStatus;
|
||||||
|
fn position(&self) -> (u32, i64);
|
||||||
|
fn update_time(&self) -> i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s, D: SpircDelegate> SpircManager<'s, D> {
|
||||||
|
pub fn new(session: &'s Session, delegate: D,
|
||||||
|
username: String, name: String) -> SpircManager<'s, D> {
|
||||||
SpircManager {
|
SpircManager {
|
||||||
|
delegate: delegate,
|
||||||
session: &session,
|
session: &session,
|
||||||
player: &player,
|
|
||||||
|
|
||||||
username: username.clone(),
|
username: username.clone(),
|
||||||
state_update_id: 0,
|
state_update_id: 0,
|
||||||
|
@ -88,10 +109,11 @@ impl <'s> SpircManager<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let h = self.player.state.0.lock().unwrap();
|
if {
|
||||||
if h.update_time > self.state_update_id {
|
let state = self.delegate.state();
|
||||||
|
state.update_time() > self.state_update_id
|
||||||
|
} {
|
||||||
self.state_update_id = util::now_ms();
|
self.state_update_id = util::now_ms();
|
||||||
drop(h);
|
|
||||||
self.notify(None);
|
self.notify(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,24 +136,23 @@ impl <'s> SpircManager<'s> {
|
||||||
|
|
||||||
let index = frame.get_state().get_playing_track_index() as usize;
|
let index = frame.get_state().get_playing_track_index() as usize;
|
||||||
let track = SpotifyId::from_raw(frame.get_state().get_track()[index].get_gid());
|
let track = SpotifyId::from_raw(frame.get_state().get_track()[index].get_gid());
|
||||||
|
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
||||||
self.track = Some(track);
|
self.track = Some(track);
|
||||||
self.player.command(PlayerCommand::Load(track,
|
self.delegate.load(track, play, frame.get_state().get_position_ms());
|
||||||
frame.get_state().get_status() == PlayStatus::kPlayStatusPlay,
|
|
||||||
frame.get_state().get_position_ms()));
|
|
||||||
}
|
}
|
||||||
protocol::spirc::MessageType::kMessageTypePlay => {
|
protocol::spirc::MessageType::kMessageTypePlay => {
|
||||||
self.player.command(PlayerCommand::Play);
|
self.delegate.play();
|
||||||
}
|
}
|
||||||
protocol::spirc::MessageType::kMessageTypePause => {
|
protocol::spirc::MessageType::kMessageTypePause => {
|
||||||
self.player.command(PlayerCommand::Pause);
|
self.delegate.pause();
|
||||||
}
|
}
|
||||||
protocol::spirc::MessageType::kMessageTypeSeek => {
|
protocol::spirc::MessageType::kMessageTypeSeek => {
|
||||||
self.player.command(PlayerCommand::Seek(frame.get_position()));
|
self.delegate.seek(frame.get_position());
|
||||||
}
|
}
|
||||||
protocol::spirc::MessageType::kMessageTypeNotify => {
|
protocol::spirc::MessageType::kMessageTypeNotify => {
|
||||||
if self.is_active && frame.get_device_state().get_is_active() {
|
if self.is_active && frame.get_device_state().get_is_active() {
|
||||||
self.is_active = false;
|
self.is_active = false;
|
||||||
self.player.command(PlayerCommand::Stop);
|
self.delegate.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -153,7 +174,7 @@ impl <'s> SpircManager<'s> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.is_active {
|
if self.is_active {
|
||||||
pkt.set_state(self.state());
|
pkt.set_state(self.spirc_state());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.session.mercury(MercuryRequest{
|
self.session.mercury(MercuryRequest{
|
||||||
|
@ -164,13 +185,14 @@ impl <'s> SpircManager<'s> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&mut self) -> protocol::spirc::State {
|
fn spirc_state(&self) -> protocol::spirc::State {
|
||||||
let state = self.player.state.0.lock().unwrap();
|
let state = self.delegate.state();
|
||||||
|
let (position_ms, position_measured_at) = state.position();
|
||||||
|
|
||||||
protobuf_init!(protocol::spirc::State::new(), {
|
protobuf_init!(protocol::spirc::State::new(), {
|
||||||
status: state.status,
|
status: state.status(),
|
||||||
position_ms: state.position_ms,
|
position_ms: position_ms,
|
||||||
position_measured_at: state.position_measured_at as u64,
|
position_measured_at: position_measured_at as u64,
|
||||||
|
|
||||||
playing_track_index: 0,
|
playing_track_index: 0,
|
||||||
track => [
|
track => [
|
||||||
|
@ -189,7 +211,7 @@ impl <'s> SpircManager<'s> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_state(&mut self) -> protocol::spirc::DeviceState {
|
fn device_state(&self) -> protocol::spirc::DeviceState {
|
||||||
protobuf_init!(protocol::spirc::DeviceState::new(), {
|
protobuf_init!(protocol::spirc::DeviceState::new(), {
|
||||||
sw_version: version_string(),
|
sw_version: version_string(),
|
||||||
is_active: self.is_active,
|
is_active: self.is_active,
|
||||||
|
|
Loading…
Reference in a new issue