11use crate :: { Error , SpotifyId } ;
2- use std:: { borrow:: Cow , fmt} ;
2+ use std:: { borrow:: Cow , fmt, str :: FromStr , time :: Duration } ;
33use thiserror:: Error ;
44
55use librespot_protocol as protocol;
@@ -65,7 +65,10 @@ pub enum SpotifyUri {
6565impl SpotifyUri {
6666 /// Returns whether this `SpotifyUri` is for a playable audio item, if known.
6767 pub fn is_playable ( & self ) -> bool {
68- matches ! ( self , SpotifyUri :: Episode { .. } | SpotifyUri :: Track { .. } )
68+ matches ! (
69+ self ,
70+ SpotifyUri :: Episode { .. } | SpotifyUri :: Track { .. } | SpotifyUri :: Local { .. }
71+ )
6972 }
7073
7174 /// Gets the item type of this URI as a static string
@@ -147,6 +150,7 @@ impl SpotifyUri {
147150 } ;
148151
149152 let name = parts. next ( ) . ok_or ( SpotifyUriError :: InvalidFormat ) ?;
153+
150154 match item_type {
151155 SPOTIFY_ITEM_TYPE_ALBUM => Ok ( Self :: Album {
152156 id : SpotifyId :: from_base62 ( name) ?,
@@ -167,12 +171,23 @@ impl SpotifyUri {
167171 SPOTIFY_ITEM_TYPE_TRACK => Ok ( Self :: Track {
168172 id : SpotifyId :: from_base62 ( name) ?,
169173 } ) ,
170- SPOTIFY_ITEM_TYPE_LOCAL => Ok ( Self :: Local {
171- artist : "unimplemented" . to_owned ( ) ,
172- album_title : "unimplemented" . to_owned ( ) ,
173- track_title : "unimplemented" . to_owned ( ) ,
174- duration : Default :: default ( ) ,
175- } ) ,
174+ SPOTIFY_ITEM_TYPE_LOCAL => {
175+ let artist = name;
176+ let album_title = parts. next ( ) . unwrap_or_default ( ) ;
177+ // enforce track_title exists; spotify:local:::<duration> is a silly URI
178+ let track_title = parts. next ( ) . ok_or ( SpotifyUriError :: InvalidFormat ) ?;
179+ let duration_secs = parts
180+ . next ( )
181+ . and_then ( |f| u64:: from_str ( f) . ok ( ) )
182+ . ok_or ( SpotifyUriError :: InvalidFormat ) ?;
183+
184+ Ok ( Self :: Local {
185+ artist : artist. to_owned ( ) ,
186+ album_title : album_title. to_owned ( ) ,
187+ track_title : track_title. to_owned ( ) ,
188+ duration : Duration :: from_secs ( duration_secs) ,
189+ } )
190+ }
176191 _ => Ok ( Self :: Unknown {
177192 kind : item_type. to_owned ( ) . into ( ) ,
178193 id : name. to_owned ( ) ,
@@ -533,15 +548,33 @@ mod tests {
533548
534549 #[ test]
535550 fn from_local_uri ( ) {
536- let actual = SpotifyUri :: from_uri ( "spotify:local:xyz:123" ) . unwrap ( ) ;
551+ let actual = SpotifyUri :: from_uri (
552+ "spotify:local:David+Wise:Donkey+Kong+Country%3A+Tropical+Freeze:Snomads+Island:127" ,
553+ )
554+ . unwrap ( ) ;
555+
556+ assert_eq ! (
557+ actual,
558+ SpotifyUri :: Local {
559+ artist: "David+Wise" . to_owned( ) ,
560+ album_title: "Donkey+Kong+Country%3A+Tropical+Freeze" . to_owned( ) ,
561+ track_title: "Snomads+Island" . to_owned( ) ,
562+ duration: Duration :: from_secs( 127 ) ,
563+ }
564+ ) ;
565+ }
566+
567+ #[ test]
568+ fn from_local_uri_missing_fields ( ) {
569+ let actual = SpotifyUri :: from_uri ( "spotify:local:::Snomads+Island:127" ) . unwrap ( ) ;
537570
538571 assert_eq ! (
539572 actual,
540573 SpotifyUri :: Local {
541- artist: "unimplemented " . to_owned( ) ,
542- album_title: "unimplemented " . to_owned( ) ,
543- track_title: "unimplemented " . to_owned( ) ,
544- duration: Default :: default ( ) ,
574+ artist: "" . to_owned( ) ,
575+ album_title: "" . to_owned( ) ,
576+ track_title: "Snomads+Island " . to_owned( ) ,
577+ duration: Duration :: from_secs ( 127 ) ,
545578 }
546579 ) ;
547580 }
0 commit comments