From c1ce87dbbd31c21085dfb4544cc1a43b7e2b80b3 Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Thu, 2 Jul 2015 13:54:34 +0200 Subject: [PATCH] spirc: Keep track of player status --- src/main.rs | 205 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 169 insertions(+), 36 deletions(-) diff --git a/src/main.rs b/src/main.rs index 774a5597..aef53bab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, + + 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::(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 + } }