|
@@ -48,6 +48,7 @@ use util::version::version_string;
|
|
|
use player::Player;
|
|
|
use mercury::{MercuryRequest, MercuryMethod};
|
|
|
use librespot_protocol as protocol;
|
|
|
+use librespot_protocol::spirc::PlayStatus;
|
|
|
|
|
|
fn main() {
|
|
|
let mut args = std::env::args().skip(1);
|
|
@@ -83,6 +84,8 @@ fn main() {
|
|
|
can_play: true,
|
|
|
is_active: false,
|
|
|
became_active_at: 0,
|
|
|
+
|
|
|
+ state: PlayerState::new()
|
|
|
}.run();
|
|
|
|
|
|
/*
|
|
@@ -118,6 +121,88 @@ fn print_track(cache: &mut MetadataCache, track_id: SpotifyId) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+struct PlayerState {
|
|
|
+ status: PlayStatus,
|
|
|
+
|
|
|
+ context_uri: String,
|
|
|
+ index: u32,
|
|
|
+ queue: Vec<SpotifyId>,
|
|
|
+
|
|
|
+ repeat: bool,
|
|
|
+ shuffle: bool,
|
|
|
+
|
|
|
+ position_ms: u32,
|
|
|
+ position_measured_at: i64,
|
|
|
+
|
|
|
+ last_command_ident: String,
|
|
|
+ last_command_msgid: u32,
|
|
|
+}
|
|
|
+
|
|
|
+impl PlayerState {
|
|
|
+ fn new() -> PlayerState {
|
|
|
+ PlayerState {
|
|
|
+ status: PlayStatus::kPlayStatusPause,
|
|
|
+
|
|
|
+ context_uri: String::new(),
|
|
|
+ index: 0,
|
|
|
+ queue: Vec::new(),
|
|
|
+
|
|
|
+ repeat: false,
|
|
|
+ shuffle: false,
|
|
|
+
|
|
|
+ position_ms: 0,
|
|
|
+ position_measured_at: 0,
|
|
|
+
|
|
|
+ last_command_ident: String::new(),
|
|
|
+ last_command_msgid: 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn import(&mut self, state: &protocol::spirc::State) {
|
|
|
+ //println!("{:?}", state);
|
|
|
+ self.status = state.get_status();
|
|
|
+
|
|
|
+ self.context_uri = state.get_context_uri().to_string();
|
|
|
+ self.index = state.get_playing_track_index();
|
|
|
+ self.queue = state.get_track().iter().filter(|t| {
|
|
|
+ t.has_gid()
|
|
|
+ }).map(|t| {
|
|
|
+ SpotifyId::from_raw(t.get_gid())
|
|
|
+ }).collect();
|
|
|
+
|
|
|
+ self.repeat = state.get_repeat();
|
|
|
+ self.shuffle = state.get_shuffle();
|
|
|
+
|
|
|
+ self.position_ms = state.get_position_ms();
|
|
|
+ self.position_measured_at = SpircManager::now();
|
|
|
+ }
|
|
|
+
|
|
|
+ fn export(&self) -> protocol::spirc::State {
|
|
|
+ protobuf_init!(protocol::spirc::State::new(), {
|
|
|
+ status: self.status,
|
|
|
+
|
|
|
+ context_uri: self.context_uri.to_string(),
|
|
|
+ playing_track_index: self.index,
|
|
|
+ track: self.queue.iter().map(|t| {
|
|
|
+ protobuf_init!(protocol::spirc::TrackRef::new(), {
|
|
|
+ gid: t.to_raw().to_vec()
|
|
|
+ })
|
|
|
+ }).collect(),
|
|
|
+
|
|
|
+ shuffle: self.shuffle,
|
|
|
+ repeat: self.repeat,
|
|
|
+
|
|
|
+ position_ms: self.position_ms,
|
|
|
+ position_measured_at: self.position_measured_at as u64,
|
|
|
+
|
|
|
+ playing_from_fallback: true,
|
|
|
+
|
|
|
+ last_command_ident: self.last_command_ident.clone(),
|
|
|
+ last_command_msgid: self.last_command_msgid
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
struct SpircManager {
|
|
|
session: Session,
|
|
|
username: String,
|
|
@@ -132,6 +217,8 @@ struct SpircManager {
|
|
|
can_play: bool,
|
|
|
is_active: bool,
|
|
|
became_active_at: i64,
|
|
|
+
|
|
|
+ state: PlayerState
|
|
|
}
|
|
|
|
|
|
impl SpircManager {
|
|
@@ -140,7 +227,7 @@ impl SpircManager {
|
|
|
|
|
|
self.session.mercury.send(MercuryRequest{
|
|
|
method: MercuryMethod::SUB,
|
|
|
- uri: format!("hm://remote/user/{}/v23", self.username).to_string(),
|
|
|
+ uri: format!("hm://remote/user/{}/v23", self.username),
|
|
|
content_type: None,
|
|
|
callback: Some(tx),
|
|
|
payload: Vec::new()
|
|
@@ -148,23 +235,22 @@ impl SpircManager {
|
|
|
|
|
|
self.notify(None);
|
|
|
|
|
|
- for pkt in rx.iter() {
|
|
|
- let frame : protocol::spirc::Frame =
|
|
|
- protobuf::parse_from_bytes(pkt.payload.front().unwrap()).unwrap();
|
|
|
-
|
|
|
- println!("{:?} {} {} {}",
|
|
|
+ let rx = rx.into_iter().map(|pkt| {
|
|
|
+ protobuf::parse_from_bytes::<protocol::spirc::Frame>(pkt.payload.front().unwrap()).unwrap()
|
|
|
+ });
|
|
|
+
|
|
|
+ for frame in rx {
|
|
|
+ println!("{:?} {} {} {} {}",
|
|
|
frame.get_typ(),
|
|
|
frame.get_device_state().get_name(),
|
|
|
frame.get_ident(),
|
|
|
- frame.get_device_state().get_became_active_at());
|
|
|
-
|
|
|
- if frame.get_ident() == self.ident ||
|
|
|
- (frame.get_recipient().len() > 0 &&
|
|
|
- !frame.get_recipient().contains(&self.ident)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- self.handle(frame);
|
|
|
+ frame.get_seq_nr(),
|
|
|
+ frame.get_state_update_id());
|
|
|
+ if frame.get_ident() != self.ident &&
|
|
|
+ (frame.get_recipient().len() == 0 ||
|
|
|
+ frame.get_recipient().contains(&self.ident)) {
|
|
|
+ self.handle(frame);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -175,37 +261,79 @@ impl SpircManager {
|
|
|
}
|
|
|
protocol::spirc::MessageType::kMessageTypeLoad => {
|
|
|
self.is_active = true;
|
|
|
- self.became_active_at = {
|
|
|
- let ts = time::now_utc().to_timespec();
|
|
|
- ts.sec * 1000 + ts.nsec as i64 / 1000000
|
|
|
- };
|
|
|
- println!("{:?} {}", frame, self.became_active_at);
|
|
|
- self.notify(None)
|
|
|
+ self.became_active_at = SpircManager::now();
|
|
|
+
|
|
|
+ self.state.import(frame.get_state());
|
|
|
+
|
|
|
+ self.state.last_command_ident = frame.get_ident().to_string();
|
|
|
+ self.state.last_command_msgid = frame.get_seq_nr();
|
|
|
+
|
|
|
+ self.state_update_id = SpircManager::now();
|
|
|
+ self.notify(None);
|
|
|
+ }
|
|
|
+ protocol::spirc::MessageType::kMessageTypePlay => {
|
|
|
+ self.state.status = PlayStatus::kPlayStatusPlay;
|
|
|
+ self.state.position_measured_at = SpircManager::now();
|
|
|
+
|
|
|
+ self.state.last_command_ident = frame.get_ident().to_string();
|
|
|
+ self.state.last_command_msgid = frame.get_seq_nr();
|
|
|
+
|
|
|
+ self.state_update_id = SpircManager::now();
|
|
|
+ self.notify(None);
|
|
|
+ }
|
|
|
+ protocol::spirc::MessageType::kMessageTypePause => {
|
|
|
+ self.state.status = PlayStatus::kPlayStatusPause;
|
|
|
+ self.state.position_measured_at = SpircManager::now();
|
|
|
+
|
|
|
+ self.state.last_command_ident = frame.get_ident().to_string();
|
|
|
+ self.state.last_command_msgid = frame.get_seq_nr();
|
|
|
+
|
|
|
+ self.state_update_id = SpircManager::now();
|
|
|
+ self.notify(None);
|
|
|
+ }
|
|
|
+ protocol::spirc::MessageType::kMessageTypeSeek => {
|
|
|
+ self.state.position_ms = frame.get_position();
|
|
|
+ self.state.position_measured_at = SpircManager::now();
|
|
|
+
|
|
|
+ self.state.last_command_ident = frame.get_ident().to_string();
|
|
|
+ self.state.last_command_msgid = frame.get_seq_nr();
|
|
|
+
|
|
|
+ self.state_update_id = SpircManager::now();
|
|
|
+ self.notify(None);
|
|
|
+ }
|
|
|
+ protocol::spirc::MessageType::kMessageTypeNotify => {
|
|
|
+ if frame.get_device_state().get_is_active() {
|
|
|
+ //println!("{:?}", frame.get_state());
|
|
|
+ }
|
|
|
}
|
|
|
_ => ()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fn notify(&mut self, recipient: Option<&str>) {
|
|
|
- let device_state = self.device_state();
|
|
|
+ let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
|
|
|
+ version: 1,
|
|
|
+ ident: self.ident.clone(),
|
|
|
+ protocol_version: "2.0.0".to_string(),
|
|
|
+ seq_nr: { self.seq_nr += 1; self.seq_nr },
|
|
|
+ typ: protocol::spirc::MessageType::kMessageTypeNotify,
|
|
|
+ device_state: self.device_state(),
|
|
|
+ recipient: protobuf::RepeatedField::from_vec(
|
|
|
+ recipient.map(|r| vec![r.to_string()] ).unwrap_or(vec![])
|
|
|
+ ),
|
|
|
+ state_update_id: self.state_update_id
|
|
|
+ });
|
|
|
+
|
|
|
+ if self.is_active {
|
|
|
+ pkt.set_state(self.state.export());
|
|
|
+ }
|
|
|
+
|
|
|
self.session.mercury.send(MercuryRequest{
|
|
|
method: MercuryMethod::SEND,
|
|
|
- uri: format!("hm://remote/user/{}", self.username).to_string(),
|
|
|
+ uri: format!("hm://remote/user/{}", self.username),
|
|
|
content_type: None,
|
|
|
callback: None,
|
|
|
- payload: vec![
|
|
|
- protobuf_init!(protocol::spirc::Frame::new(), {
|
|
|
- version: 1,
|
|
|
- ident: self.ident.clone(),
|
|
|
- protocol_version: "2.0.0".to_string(),
|
|
|
- seq_nr: { self.seq_nr += 1; self.seq_nr },
|
|
|
- typ: protocol::spirc::MessageType::kMessageTypeNotify,
|
|
|
- device_state: device_state,
|
|
|
- recipient: protobuf::RepeatedField::from_vec(
|
|
|
- recipient.map(|r| vec![r.to_string()] ).unwrap_or(vec![])
|
|
|
- )
|
|
|
- }).write_to_bytes().unwrap()
|
|
|
- ]
|
|
|
+ payload: vec![ pkt.write_to_bytes().unwrap() ]
|
|
|
}).unwrap();
|
|
|
}
|
|
|
|
|
@@ -268,5 +396,10 @@ impl SpircManager {
|
|
|
],
|
|
|
})
|
|
|
}
|
|
|
+
|
|
|
+ fn now() -> i64 {
|
|
|
+ let ts = time::now_utc().to_timespec();
|
|
|
+ ts.sec * 1000 + ts.nsec as i64 / 1000000
|
|
|
+ }
|
|
|
}
|
|
|
|