Browse Source

Support Dailymixes and refactor dynamic playlists

ashthespy 6 years ago
parent
commit
c0416972b6
3 changed files with 133 additions and 55 deletions
  1. 86 0
      connect/src/context.rs
  2. 1 0
      connect/src/lib.rs
  3. 46 55
      connect/src/spirc.rs

+ 86 - 0
connect/src/context.rs

@@ -0,0 +1,86 @@
+use core::spotify_id::SpotifyId;
+use protocol::spirc::TrackRef;
+
+use serde;
+
+#[derive(Deserialize, Debug)]
+pub struct StationContext {
+    pub uri: Option<String>,
+    pub next_page_url: String,
+    #[serde(deserialize_with = "deserialize_protobuf_TrackRef")]
+    pub tracks: Vec<TrackRef>,
+    // Not required for core functionality
+    // pub seeds: Vec<String>,
+    // #[serde(rename = "imageUri")]
+    // pub image_uri: String,
+    // pub subtitle: Option<String>,
+    // pub subtitles: Vec<String>,
+    // #[serde(rename = "subtitleUri")]
+    // pub subtitle_uri: Option<String>,
+    // pub title: String,
+    // #[serde(rename = "titleUri")]
+    // pub title_uri: String,
+    // pub related_artists: Vec<ArtistContext>,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct PageContext {
+    pub uri: String,
+    pub next_page_url: String,
+    #[serde(deserialize_with = "deserialize_protobuf_TrackRef")]
+    pub tracks: Vec<TrackRef>,
+    // Not required for core functionality
+    // pub url: String,
+    // // pub restrictions:
+}
+
+#[derive(Deserialize, Debug)]
+pub struct TrackContext {
+    #[serde(rename = "original_gid")]
+    pub gid: String,
+    pub uri: String,
+    pub uid: String,
+    // Not required for core functionality
+    // pub album_uri: String,
+    // pub artist_uri: String,
+    // pub metadata: MetadataContext,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct ArtistContext {
+    artist_name: String,
+    artist_uri: String,
+    image_uri: String,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct MetadataContext {
+    album_title: String,
+    artist_name: String,
+    artist_uri: String,
+    image_url: String,
+    title: String,
+    uid: String,
+}
+
+#[allow(non_snake_case)]
+fn deserialize_protobuf_TrackRef<D>(de: D) -> Result<Vec<TrackRef>, D::Error>
+where
+    D: serde::Deserializer,
+{
+    let v: Vec<TrackContext> = try!(serde::Deserialize::deserialize(de));
+    let track_vec = v
+        .iter()
+        .map(|v| {
+            let mut t = TrackRef::new();
+            //  This has got to be the most round about way of doing this.
+            t.set_gid(SpotifyId::from_base62(&v.gid).unwrap().to_raw().to_vec());
+            t.set_uri(v.uri.to_owned());
+
+            t
+        })
+        .collect::<Vec<TrackRef>>();
+
+    Ok(track_vec)
+}

+ 1 - 0
connect/src/lib.rs

@@ -26,5 +26,6 @@ extern crate librespot_core as core;
 extern crate librespot_playback as playback;
 extern crate librespot_protocol as protocol;
 
+pub mod context;
 pub mod discovery;
 pub mod spirc;

+ 46 - 55
connect/src/spirc.rs

@@ -12,60 +12,18 @@ use core::version;
 use core::volume::Volume;
 
 use protocol;
-use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef};
+use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State};
 
 use playback::mixer::Mixer;
 use playback::player::Player;
-use serde;
 use serde_json;
 
+use context::StationContext;
 use rand;
 use rand::seq::SliceRandom;
 use std;
 use std::time::{SystemTime, UNIX_EPOCH};
 
-// Keep this here for now
-
-#[derive(Deserialize, Debug)]
-struct TrackContext {
-    album_uri: String,
-    artist_uri: String,
-    // metadata: String,
-    #[serde(rename = "original_gid")]
-    gid: String,
-    uid: String,
-    uri: String,
-}
-#[derive(Deserialize, Debug)]
-struct StationContext {
-    uri: String,
-    next_page_url: String,
-    seeds: Vec<String>,
-    #[serde(deserialize_with = "deserialize_protobuf_TrackRef")]
-    tracks: Vec<TrackRef>,
-}
-
-#[allow(non_snake_case)]
-fn deserialize_protobuf_TrackRef<D>(de: D) -> Result<Vec<TrackRef>, D::Error>
-where
-    D: serde::Deserializer,
-{
-    let v: Vec<TrackContext> = try!(serde::Deserialize::deserialize(de));
-    let track_vec = v
-        .iter()
-        .map(|v| {
-            let mut t = TrackRef::new();
-            //  This has got to be the most round about way of doing this.
-            t.set_gid(SpotifyId::from_base62(&v.gid).unwrap().to_raw().to_vec());
-            t.set_uri(v.uri.to_owned());
-
-            t
-        })
-        .collect::<Vec<TrackRef>>();
-
-    Ok(track_vec)
-}
-
 pub struct SpircTask {
     player: Player,
     mixer: Box<Mixer>,
@@ -396,12 +354,26 @@ impl Future for SpircTask {
 
                 match self.context_fut.poll() {
                     Ok(Async::Ready(value)) => {
-                        let r_context = serde_json::from_value::<StationContext>(value).ok();
-                        debug!("Radio Context: {:#?}", r_context);
-                        if let Some(ref context) = r_context {
-                            warn!("Got {:?} tracks from <{}>", context.tracks.len(), context.uri);
-                        }
-                        self.context = r_context;
+                        let r_context = serde_json::from_value::<StationContext>(value.clone());
+                        self.context = match r_context {
+                            Ok(context) => {
+                                info!(
+                                    "Resolved {:?} tracks from <{:?}>",
+                                    context.tracks.len(),
+                                    self.state.get_context_uri(),
+                                );
+                                Some(context)
+                            }
+                            Err(e) => {
+                                error!("Unable to parse JSONContext {:?}\n{:?}", e, value);
+                                None
+                            }
+                        };
+                        // It needn't be so verbose - can be as simple as
+                        // if let Some(ref context) = r_context {
+                        //     info!("Got {:?} tracks from <{}>", context.tracks.len(), context.uri);
+                        // }
+                        // self.context = r_context;
 
                         progress = true;
                         self.context_fut = Box::new(future::empty());
@@ -409,7 +381,7 @@ impl Future for SpircTask {
                     Ok(Async::NotReady) => (),
                     Err(err) => {
                         self.context_fut = Box::new(future::empty());
-                        error!("Error: {:?}", err)
+                        error!("ContextError: {:?}", err)
                     }
                 }
             }
@@ -686,7 +658,9 @@ impl SpircTask {
             self.state.get_track().len() as u32 - new_index < 5
         );
         let context_uri = self.state.get_context_uri().to_owned();
-        if context_uri.contains("station") && ((self.state.get_track().len() as u32) - new_index) < 5 {
+        if (context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:"))
+            && ((self.state.get_track().len() as u32) - new_index) < 5
+        {
             self.context_fut = self.resolve_station(&context_uri);
             self.update_tracks_from_context();
         }
@@ -808,7 +782,7 @@ impl SpircTask {
         let context_uri = frame.get_state().get_context_uri().to_owned();
         let tracks = frame.get_state().get_track();
         debug!("Frame has {:?} tracks", tracks.len());
-        if context_uri.contains("station") {
+        if context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:") {
             self.context_fut = self.resolve_station(&context_uri);
         }
 
@@ -820,9 +794,26 @@ impl SpircTask {
     }
 
     fn load_track(&mut self, play: bool) {
-        let index = self.state.get_playing_track_index();
+        let mut index = self.state.get_playing_track_index();
         let track = {
-            let gid = self.state.get_track()[index as usize].get_gid();
+            // let gid = self.state.get_track()[index as usize].get_gid();
+            let track_ref = self.state.get_track()[index as usize].to_owned();
+            let mut gid = track_ref.get_gid();
+            if gid.len() != 16 {
+                let track_len = self.state.get_track().len() as u32;
+                if index < track_len - 1 {
+                    index = 0;
+                } else {
+                    index = index + 1;
+                }
+                warn!(
+                    "Skipping track {:?} at position [{}] of {}",
+                    track_ref.get_uri(),
+                    index,
+                    track_len
+                );
+                gid = self.state.get_track()[index as usize].get_gid();
+            }
             SpotifyId::from_raw(gid).unwrap()
         };
         let position = self.state.get_position_ms();