From b96405af8262fc5b0e0567440f969d110492a836 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Wed, 9 Oct 2019 19:49:13 +0200 Subject: [PATCH] Make `SpotifyId` understand more URI formats --- connect/src/spirc.rs | 44 +++++++++++++++++++++++++++--------------- core/src/spotify_id.rs | 13 ++++++++++--- metadata/src/lib.rs | 4 ++++ 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 92e26ce0..5e00ebb0 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -13,14 +13,14 @@ use context::StationContext; use librespot_core::config::ConnectConfig; use librespot_core::mercury::MercuryError; use librespot_core::session::Session; -use librespot_core::spotify_id::SpotifyId; +use librespot_core::spotify_id::{SpotifyId, SpotifyIdError}; use librespot_core::util::SeqGenerator; use librespot_core::version; use librespot_core::volume::Volume; use playback::mixer::Mixer; use playback::player::Player; use protocol; -use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State}; +use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef}; pub struct SpircTask { player: Player, @@ -797,7 +797,7 @@ impl SpircTask { } fn update_tracks(&mut self, frame: &protocol::spirc::Frame) { - // debug!("State: {:?}", frame.get_state()); + debug!("State: {:?}", frame.get_state()); let index = frame.get_state().get_playing_track_index(); let context_uri = frame.get_state().get_context_uri().to_owned(); let tracks = frame.get_state().get_track(); @@ -813,31 +813,43 @@ impl SpircTask { self.state.set_shuffle(frame.get_state().get_shuffle()); } + // should this be a method of SpotifyId directly? + fn get_spotify_id_for_track(&self, track_ref: &TrackRef) -> Result { + SpotifyId::from_raw(track_ref.get_gid()).or_else(|_| { + let uri = track_ref.get_uri(); + debug!("Malformed or no gid, attempting to parse URI <{}>", uri); + SpotifyId::from_uri(uri) + }) + } + fn load_track(&mut self, play: bool) { let context_uri = self.state.get_context_uri().to_owned(); - let index = self.state.get_playing_track_index(); - info!("context: {}", context_uri); - // Redundant check here - let track = if context_uri.contains(":show:") || context_uri.contains(":episode:") { - let uri = self.state.get_track()[index as usize].get_uri(); - SpotifyId::from_uri(uri).expect("Unable to parse uri") - } else { - let mut index = self.state.get_playing_track_index(); - // Check for malformed gid - let tracks_len = self.state.get_track().len() as u32; + let mut index = self.state.get_playing_track_index(); + let tracks_len = self.state.get_track().len() as u32; + debug!( + "Loading context: {} index: [{}] of {}", + context_uri, index, tracks_len + ); + // Tracks either have a gid or uri. + // Context based frames sometimes use spotify:meta:page: that needs to be ignored. + let track = { let mut track_ref = &self.state.get_track()[index as usize]; - while track_ref.get_gid().len() != 16 { + let mut track_id = self.get_spotify_id_for_track(track_ref); + while track_id.is_err() { warn!( "Skipping track {:?} at position [{}] of {}", track_ref.get_uri(), index, tracks_len ); + // This will keep looping over, instead we should cylce tracks only once index = if index + 1 < tracks_len { index + 1 } else { 0 }; track_ref = &self.state.get_track()[index as usize]; + track_id = self.get_spotify_id_for_track(track_ref); } - SpotifyId::from_raw(track_ref.get_gid()).unwrap() - }; + track_id + } + .unwrap(); let position = self.state.get_position_ms(); let end_of_track = self.player.load(track, play, position); diff --git a/core/src/spotify_id.rs b/core/src/spotify_id.rs index c8f01674..e6f0cdd8 100644 --- a/core/src/spotify_id.rs +++ b/core/src/spotify_id.rs @@ -5,6 +5,7 @@ use std::fmt; pub enum SpotifyAudioType { Track, Podcast, + NonPlayable, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -71,12 +72,18 @@ impl SpotifyId { pub fn from_uri(uri: &str) -> Result { let parts = uri.split(":").collect::>(); - if uri.contains(":show:") || uri.contains(":episode:") { - let mut spotify_id = SpotifyId::from_base62(parts[2]).unwrap(); + let gid = parts.last().unwrap(); + if uri.contains(":episode:") { + let mut spotify_id = SpotifyId::from_base62(gid).unwrap(); let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::Podcast); Ok(spotify_id) + } else if uri.contains(":track:") { + SpotifyId::from_base62(gid) } else { - SpotifyId::from_base62(parts[2]) + // show/playlist/artist/album/?? + let mut spotify_id = SpotifyId::from_base62(gid).unwrap(); + let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::NonPlayable); + Ok(spotify_id) } } diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 75a42e2f..344a3a54 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -8,6 +8,7 @@ extern crate librespot_protocol as protocol; pub mod cover; +use futures::future; use futures::Future; use linear_map::LinearMap; @@ -71,6 +72,9 @@ impl AudioItem { match id.audio_type { SpotifyAudioType::Track => Track::get_audio_item(session, id), SpotifyAudioType::Podcast => Episode::get_audio_item(session, id), + SpotifyAudioType::NonPlayable => { + Box::new(future::err::(MercuryError)) + } } } }