commit: | e3f44d6f2d99ae07ce074b28ae9739ed2773d803 |
author: | Trevor Bentley |
committer: | Trevor Bentley |
date: | Tue Jan 16 22:20:56 2018 +0100 |
parents: | adbb8d766b0ea558275c3114a3bbf4a43391bb21 |
diff --git a/Cargo.toml b/Cargo.toml line changes: +5/-3 index cae8991..cbb575d --- a/Cargo.toml +++ b/Cargo.toml
@@ -58,19 +58,21 @@ systray = {path = "deps/systray-rs", version="0.1.1-connectr"} [target."cfg(windows)".dependencies.rubrail] default-features=false -version = "0.5" +version = "0.6" [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies.rubrail] default-features = false -version = "0.5" +version = "0.6" [target."cfg(target_os = \"macos\")".dependencies] cocoa = "0.9" objc-foundation = "0.1.1" objc_id = "0.1" -rubrail = "0.5" + +[target."cfg(target_os = \"macos\")".dependencies.rubrail] +version = "0.6" [target."cfg(target_os = \"macos\")".dependencies.objc] version = "0.2.2"
diff --git a/src/http/mod.rs b/src/http/mod.rs line changes: +4/-1 index 4229684..27b472a --- a/src/http/mod.rs +++ b/src/http/mod.rs
@@ -21,7 +21,7 @@ use self::url::percent_encoding; use super::settings; -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] pub enum HttpMethod { GET, POST,
@@ -63,6 +63,7 @@ impl fmt::Display for HttpResponse { } } +#[derive(Debug)] pub enum AccessToken<'a> { Bearer(&'a str), Basic(&'a str),
@@ -72,6 +73,8 @@ pub enum AccessToken<'a> { pub fn http(url: &str, query: Option<&str>, body: Option<&str>, method: HttpMethod, access_token: AccessToken) -> HttpResponse { let mut headers = List::new(); + #[cfg(feature = "verbose_http")] + info!("HTTP URL: {:?} {}\nQuery: {:?}\nBody: {:?}\nToken: {:?}", method, url, query, body, access_token); let data = match method { HttpMethod::POST => { match query {
diff --git a/src/main.rs b/src/main.rs line changes: +15/-3 index ceebd1a..9e9013a --- a/src/main.rs +++ b/src/main.rs
@@ -126,13 +126,19 @@ impl TScrubberData for TouchbarScrubberData { self.entries.borrow().len() as u32 } fn text(&self, _item: rubrail::ItemId, idx: u32) -> String { - self.entries.borrow()[idx as usize].0.to_string() + match self.entries.borrow().get(idx as usize) { + Some(e) => e.0.to_string(), + None => String::new(), + } } fn width(&self, _item: rubrail::ItemId, idx: u32) -> u32 { // 10px per character + some padding seems to work nicely for the default // font. no idea what it's like on other machines. does the touchbar // font change? ¯\_(ツ)_/¯ - let len = self.entries.borrow()[idx as usize].0.len() as u32; + let len = match self.entries.borrow().get(idx as usize) { + Some(e) => e.0.len() as u32, + None => 1, + }; let width = len * 8 + 20; width }
@@ -463,7 +469,9 @@ fn fill_menu<T: TStatusBar>(app: &mut ConnectrApp, let presets = spotify.presets.read().unwrap(); if device_list.is_none() || player_state.is_none() { - // TODO: handle empty groups + status.add_label("No devices found."); + status.add_separator(); + status.add_quit("Exit"); return; } let device_list = device_list.as_ref().unwrap();
@@ -1050,4 +1058,8 @@ fn play_uri(spotify: &mut connectr::SpotifyConnectr, device: Option<&str>, uri: require(spotify.play(None)); } }; + + // Always set it back to None, so commands go to the currently + // playing device. + spotify.set_target_device(None); }
diff --git a/src/osx/rustnsobject.rs b/src/osx/rustnsobject.rs line changes: +2/-2 index 4f39bd3..f975896 --- a/src/osx/rustnsobject.rs +++ b/src/osx/rustnsobject.rs
@@ -132,7 +132,7 @@ impl INSObject for ObjcSubclass { let rustdata: &mut RustWrapperClass = &mut *(ptr as *mut RustWrapperClass); if let Some(ref cb) = rustdata.cb_fn { // Ownership? Fuck ownership! - let mut rustdata: &mut RustWrapperClass = &mut *(ptr as *mut RustWrapperClass); + let rustdata: &mut RustWrapperClass = &mut *(ptr as *mut RustWrapperClass); cb(rustdata, sender); } }
@@ -143,7 +143,7 @@ impl INSObject for ObjcSubclass { extern fn objc_get_rust_data(this: &Object, _cmd: Sel) -> u64 { unsafe {*this.get_ivar("_rustdata")} } - + unsafe { let f: extern fn(&mut Object, Sel, u64) = objc_cb; decl.add_method(sel!(cb:), f);
diff --git a/src/webapi/mod.rs b/src/webapi/mod.rs line changes: +33/-20 index 80dcc81..79e2c19 --- a/src/webapi/mod.rs +++ b/src/webapi/mod.rs
@@ -38,10 +38,11 @@ pub fn parse_spotify_token(json: &str) -> (String, String, u64) { let expires_in = json_data["expires_in"].as_u64().unwrap_or(0 as u64); return (String::from(access_token),String::from(refresh_token), expires_in); } - (String::new(), String::new(), 0) + // Default to blank, expiring in 5s to trigger a retry. + (String::new(), String::new(), 5000) } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] pub struct ConnectDevice { pub id: Option<String>, pub is_active: bool,
@@ -119,7 +120,7 @@ pub struct ConnectContext { pub uri: String, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] pub struct PlayerState { pub timestamp: i64, pub device: ConnectDevice,
@@ -587,11 +588,13 @@ impl<'a> SpotifyConnectr<'a> { } } pub fn alarm_reschedule(&mut self, id: AlarmId) -> Result<(), ()> { - let mut alarm = { self.alarm_with_id(id)? }; + let alarm = { self.alarm_with_id(id)? }; let alarm_time = Self::next_alarm_datetime(&alarm.entry).unwrap(); let (tx, rx) = channel::<>(); - let closure = move || { tx.send(()).unwrap(); }; + let closure = move || { + tx.send(()).unwrap_or_else(|_| { warn!("Alarm clock skipped."); }); + }; let guard = alarm.timer.schedule_with_date(alarm_time, closure); let duration = alarm_time.signed_duration_since(Local::now()); info!("Alarm {} set for {} hours from now ({} mins)", alarm.entry.time,
@@ -645,7 +648,9 @@ impl<'a> SpotifyConnectr<'a> { }; let expire_offset = chrono::Duration::seconds(expire_offset); info!("Refreshing Spotify credentials in {} sec", expire_offset.num_seconds()); - let closure = move || { tx.send(()).unwrap(); }; + let closure = move || { + tx.send(()).unwrap_or_else(|_| { warn!("Token refresh lost."); }); + }; self.refresh_timer_guard = Some(self.refresh_timer.schedule_with_delay(expire_offset, closure)); Ok(()) }
@@ -700,6 +705,7 @@ impl<'a> SpotifyConnectr<'a> { old_alarm.entry.device); self.set_target_device(Some(old_alarm.entry.device.clone())); self.play(Some(&old_alarm.entry.context)); + self.set_target_device(None); let id = old_alarm.id; self.alarms.push(old_alarm); let _ = self.alarm_reschedule(id);
@@ -728,16 +734,22 @@ impl<'a> SpotifyConnectr<'a> { let _ = self.schedule_token_refresh(); } pub fn request_oauth_tokens(&self, auth_code: &str, settings: &settings::Settings) -> (String, String, u64) { - let query = QueryString::new() - .add("grant_type", "authorization_code") - .add("code", auth_code) - .add("redirect_uri", format!("http://127.0.0.1:{}", settings.port)) - .add("client_id", settings.client_id.clone()) - .add("client_secret", settings.secret.clone()) - .build(); + let query = QueryString::new() + .add("grant_type", "authorization_code") + .add("code", auth_code) + .add("redirect_uri", format!("http://127.0.0.1:{}", settings.port)) + .add("client_id", settings.client_id.clone()) + .add("client_secret", settings.secret.clone()) + .build(); let json_response = http::http(self.api.token, Some(&query), None, http::HttpMethod::POST, - http::AccessToken::None).unwrap(); - parse_spotify_token(&json_response) + http::AccessToken::None); + match json_response.code { + Some(200) => { + parse_spotify_token(&json_response.unwrap()) + }, + // Default to blank, expiring in 5s to trigger a retry. + _ => { (String::new(), String::new(), 5000) } + } } pub fn connect(&mut self) { if self.access_token.is_some() {
@@ -797,12 +809,16 @@ impl<'a> SpotifyConnectr<'a> { Ok(json) => json, Err(err) => { info!("json error: {}", err); None }, }, + Some(202) => { + warn!("Spotify returned no state."); + Default::default() + } Some(401) => { warn!("Access token invalid. Attempting to reauthenticate."); self.refresh_access_token(); - None + Default::default() } - _ => None + _ => Default::default() } } pub fn set_target_device(&mut self, device: Option<DeviceId>) {
@@ -857,14 +873,11 @@ impl<'a> SpotifyConnectr<'a> { http::http(self.api.repeat, Some(&query), None, http::HttpMethod::PUT, self.bearer_token()) } pub fn transfer_multi(&mut self, devices: Vec<String>, play: bool) -> SpotifyResponse { - let device = devices[0].clone(); let body = serde_json::to_string(&DeviceIdList {device_ids: devices, play: play}).unwrap(); - self.set_target_device(Some(device)); http::http(self.api.player, None, Some(&body), http::HttpMethod::PUT, self.bearer_token()) } pub fn transfer(&mut self, device: String, play: bool) -> SpotifyResponse { let body = serde_json::to_string(&DeviceIdList {device_ids: vec![device.clone()], play: play}).unwrap(); - self.set_target_device(Some(device)); http::http(self.api.player, None, Some(&body), http::HttpMethod::PUT, self.bearer_token()) } pub fn save_track(&mut self, track: String, playlist: String) -> SpotifyResponse {