summary history branches tags files
commit:c6398cd719d6df5fefe5b9d8319f1afafae59cf1
author:Trevor Bentley
committer:Trevor Bentley
date:Mon Jan 22 14:11:41 2018 +0100
parents:59404a29037b7802ba4dae6654e3f11802e7d942
Handle Spotify outages: support empty device list and absurdly large timestamps
diff --git a/src/main.rs b/src/main.rs
line changes: +20/-14
index 139c50c..25d9e83
--- a/src/main.rs
+++ b/src/main.rs
@@ -467,8 +467,7 @@ fn fill_menu<T: TStatusBar>(app: &mut ConnectrApp,
     let device_list = spotify.device_list.read().unwrap();
     let player_state = spotify.player_state.read().unwrap();
     let presets = spotify.presets.read().unwrap();
-    if device_list.is_none() ||
-        player_state.is_none() {
+    if player_state.is_none() {
             status.add_label("No devices found.");
             status.add_separator();
             status.add_quit("Exit");
@@ -593,6 +592,10 @@ fn fill_menu<T: TStatusBar>(app: &mut ConnectrApp,
     }).collect();
     touchbar.device_data.fill(devices);
 
+    if device_list.len() == 0 {
+        status.add_label("unavailable");
+    }
+
     let selected_arr: Vec<bool> = device_list.into_iter().map(|d| {d.is_active}).collect();
     if let Ok(selected) = selected_arr.binary_search(&true) {
         touchbar.set_selected_device(selected as u32);
@@ -801,7 +804,7 @@ fn find_wine_path() -> Option<std::path::PathBuf> {
 
 fn scrobble(spotify: &mut connectr::SpotifyConnectr,
             state: Option<&connectr::PlayerState>,
-            played_ms: u32,
+            played_ms: u64,
             done: bool) {
     if let Some(state) = flatten_player_state(state) {
         let len = state.duration_ms;
@@ -834,8 +837,8 @@ struct FlatPlayState {
     album: String,
     device_type: String,
     uri: String,
-    progress_ms: u32,
-    duration_ms: u32,
+    progress_ms: u64,
+    duration_ms: u64,
     is_playing: bool,
 }
 
@@ -868,9 +871,9 @@ fn flatten_player_state(state: Option<&connectr::PlayerState>) -> Option<FlatPla
 
 #[derive(Debug)]
 enum StateChange {
-    Stopped(u32),
-    Changed(u32),
-    Played(u32),
+    Stopped(u64),
+    Changed(u64),
+    Played(u64),
     Unchanged,
 }
 
@@ -898,20 +901,20 @@ fn compare_playback_states(old: Option<&connectr::PlayerState>,
         // Assume that up to 31 seconds of the previous track played.
         let played = std::cmp::min(
             std::cmp::max(old_track.duration_ms as i64 - old_time as i64, 0),
-            31000) as u32;
+            31000) as u64;
         if old_track.is_playing && !new_track.is_playing {
             // End of context
             return StateChange::Stopped(played);
         }
         return StateChange::Changed(played);
     }
-    let played = std::cmp::max(new_time as i64 - old_time as i64, 0) as u32;
+    let played = std::cmp::max(new_time as i64 - old_time as i64, 0) as u64;
     if (new_time == 0 && !new_track.is_playing) && (old_time > 0 && old_track.is_playing) {
         // Playlist finished and reset.  Normally caught above, but this
         // is a special case for a 1-track playlist.
         let played = std::cmp::min(
             std::cmp::max(old_track.duration_ms as i64 - old_time as i64, 0),
-            31000) as u32;
+            31000) as u64;
         return StateChange::Stopped(played);
     }
     if played == 0 {
@@ -937,7 +940,7 @@ struct SpotifyThread {
 fn create_spotify_thread(rx_cmd: Receiver<String>) -> SpotifyThread {
     let (tx_in,rx_in) = channel::<String>();
     let (tx_out,rx_out) = channel::<SpotifyThreadCommand>();
-    let device_list = Arc::new(RwLock::new(None));
+    let device_list = Arc::new(RwLock::new(Some(Default::default())));
     let player_state = Arc::new(RwLock::new(None));
     let presets = Arc::new(RwLock::new(vec![]));
     let thread_device_list = device_list.clone();
@@ -948,7 +951,7 @@ fn create_spotify_thread(rx_cmd: Receiver<String>) -> SpotifyThread {
         let rx = rx_in;
         let rx_cmd = rx_cmd;
         let mut refresh_time_utc = 0;
-        let mut track_play_time_ms: u32 = 0;
+        let mut track_play_time_ms: u64 = 0;
 
         // Continuously try to create a connection to Spotify web API.
         // If it fails, assume that the settings file is corrupt and inform
@@ -1035,7 +1038,10 @@ fn create_spotify_thread(rx_cmd: Receiver<String>) -> SpotifyThread {
                 let dev_list = spotify.request_device_list();
                 {
                     let mut dev_writer = device_list.write().unwrap();
-                    *dev_writer = dev_list;
+                    *dev_writer = match dev_list {
+                        Some(_) => dev_list,
+                        None => Some(Default::default()),
+                    };
                 }
                 let play_state = spotify.request_player_state();
                 {

diff --git a/src/webapi/mod.rs b/src/webapi/mod.rs
line changes: +9/-3
index 58b8154..8da881e
--- a/src/webapi/mod.rs
+++ b/src/webapi/mod.rs
@@ -65,7 +65,7 @@ impl fmt::Display for ConnectDevice {
     }
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Default)]
 pub struct ConnectDeviceList {
     pub devices: Vec<ConnectDevice>,
 }
@@ -96,6 +96,12 @@ impl iter::IntoIterator for ConnectDeviceList {
     }
 }
 
+impl ConnectDeviceList {
+    pub fn len(&self) -> usize {
+        self.devices.len()
+    }
+}
+
 #[derive(Deserialize, Debug)]
 pub struct ConnectPlaybackArtist {
     pub name: String,
@@ -110,7 +116,7 @@ pub struct ConnectPlaybackAlbum {
 
 #[derive(Deserialize, Debug)]
 pub struct ConnectPlaybackItem {
-    pub duration_ms: u32,
+    pub duration_ms: u64,
     pub name: String,
     pub uri: String,
     pub album: ConnectPlaybackAlbum,
@@ -126,7 +132,7 @@ pub struct ConnectContext {
 pub struct PlayerState {
     pub timestamp: i64,
     pub device: ConnectDevice,
-    pub progress_ms: Option<u32>,
+    pub progress_ms: Option<u64>,
     pub is_playing: bool,
     pub item: Option<ConnectPlaybackItem>,
     pub shuffle_state: bool,