Browse Source

spirc: Keep track of player status

Paul Lietar 9 years ago
parent
commit
c1ce87dbbd
1 changed files with 169 additions and 36 deletions
  1. 169 36
      src/main.rs

+ 169 - 36
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<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
+    }
 }