@@ -18,6 +18,8 @@ use crate::{
1818 connect:: PutStateRequest ,
1919 context:: Context ,
2020 extended_metadata:: BatchedEntityRequest ,
21+ extended_metadata:: { BatchedExtensionResponse , EntityRequest , ExtensionQuery } ,
22+ extension_kind:: ExtensionKind ,
2123 } ,
2224 token:: Token ,
2325 util,
@@ -32,7 +34,7 @@ use hyper::{
3234 header:: { ACCEPT , AUTHORIZATION , CONTENT_LENGTH , CONTENT_TYPE , HeaderName , RANGE } ,
3335} ;
3436use hyper_util:: client:: legacy:: ResponseFuture ;
35- use protobuf:: { Enum , Message , MessageFull } ;
37+ use protobuf:: { Enum , EnumOrUnknown , Message , MessageFull } ;
3638use rand:: RngCore ;
3739use sysinfo:: System ;
3840use thiserror:: Error ;
@@ -58,18 +60,14 @@ const NO_METRICS_AND_SALT: RequestOptions = RequestOptions {
5860 base_url : None ,
5961} ;
6062
61- const SPCLIENT_FALLBACK_ENDPOINT : RequestOptions = RequestOptions {
62- metrics : true ,
63- salt : true ,
64- base_url : Some ( "https://spclient.wg.spotify.com" ) ,
65- } ;
66-
6763#[ derive( Debug , Error ) ]
6864pub enum SpClientError {
6965 #[ error( "missing attribute {0}" ) ]
7066 Attribute ( String ) ,
7167 #[ error( "expected data but received none" ) ]
7268 NoData ,
69+ #[ error( "expected an entry to exist in {0}" ) ]
70+ ExpectedEntry ( & ' static str ) ,
7371}
7472
7573impl From < SpClientError > for Error {
@@ -572,39 +570,67 @@ impl SpClient {
572570 . await
573571 }
574572
575- pub async fn get_metadata ( & self , scope : & str , id : & SpotifyId ) -> SpClientResult {
576- let endpoint = format ! ( "/metadata/4/{}/{}" , scope, id. to_base16( ) ?) ;
577- // For unknown reasons, metadata requests must now be sent through spclient.wg.spotify.com.
578- // Otherwise, the API will respond with 500 Internal Server Error responses.
579- // Context: https://github.com/librespot-org/librespot/issues/1527
580- self . request_with_options (
581- & Method :: GET ,
582- & endpoint,
583- None ,
584- None ,
585- & SPCLIENT_FALLBACK_ENDPOINT ,
586- )
587- . await
573+ pub async fn get_extended_metadata (
574+ & self ,
575+ request : BatchedEntityRequest ,
576+ ) -> Result < BatchedExtensionResponse , Error > {
577+ let endpoint = "/extended-metadata/v0/extended-metadata" ;
578+ let res = self
579+ . request_with_protobuf ( & Method :: POST , endpoint, None , & request)
580+ . await ?;
581+ Ok ( BatchedExtensionResponse :: parse_from_bytes ( & res) ?)
582+ }
583+
584+ pub async fn get_metadata ( & self , kind : ExtensionKind , id : & SpotifyUri ) -> SpClientResult {
585+ let req = BatchedEntityRequest {
586+ entity_request : vec ! [ EntityRequest {
587+ entity_uri: id. to_uri( ) ?,
588+ query: vec![ ExtensionQuery {
589+ extension_kind: EnumOrUnknown :: new( kind) ,
590+ ..Default :: default ( )
591+ } ] ,
592+ ..Default :: default ( )
593+ } ] ,
594+ ..Default :: default ( )
595+ } ;
596+
597+ let mut res = self . get_extended_metadata ( req) . await ?;
598+ let mut extended_metadata = res
599+ . extended_metadata
600+ . pop ( )
601+ . ok_or ( SpClientError :: ExpectedEntry ( "extended_metadata" ) ) ?;
602+
603+ let mut data = extended_metadata
604+ . extension_data
605+ . pop ( )
606+ . ok_or ( SpClientError :: ExpectedEntry ( "extension_data" ) ) ?;
607+
608+ match data. extension_data . take ( ) {
609+ None => Err ( SpClientError :: ExpectedEntry ( "data" ) . into ( ) ) ,
610+ Some ( data) => Ok ( Bytes :: from ( data. value ) ) ,
611+ }
588612 }
589613
590- pub async fn get_track_metadata ( & self , track_id : & SpotifyId ) -> SpClientResult {
591- self . get_metadata ( "track" , track_id ) . await
614+ pub async fn get_track_metadata ( & self , track_uri : & SpotifyUri ) -> SpClientResult {
615+ self . get_metadata ( ExtensionKind :: TRACK_V4 , track_uri ) . await
592616 }
593617
594- pub async fn get_episode_metadata ( & self , episode_id : & SpotifyId ) -> SpClientResult {
595- self . get_metadata ( "episode" , episode_id) . await
618+ pub async fn get_episode_metadata ( & self , episode_uri : & SpotifyUri ) -> SpClientResult {
619+ self . get_metadata ( ExtensionKind :: EPISODE_V4 , episode_uri)
620+ . await
596621 }
597622
598- pub async fn get_album_metadata ( & self , album_id : & SpotifyId ) -> SpClientResult {
599- self . get_metadata ( "album" , album_id ) . await
623+ pub async fn get_album_metadata ( & self , album_uri : & SpotifyUri ) -> SpClientResult {
624+ self . get_metadata ( ExtensionKind :: ALBUM_V4 , album_uri ) . await
600625 }
601626
602- pub async fn get_artist_metadata ( & self , artist_id : & SpotifyId ) -> SpClientResult {
603- self . get_metadata ( "artist" , artist_id) . await
627+ pub async fn get_artist_metadata ( & self , artist_uri : & SpotifyUri ) -> SpClientResult {
628+ self . get_metadata ( ExtensionKind :: ARTIST_V4 , artist_uri)
629+ . await
604630 }
605631
606- pub async fn get_show_metadata ( & self , show_id : & SpotifyId ) -> SpClientResult {
607- self . get_metadata ( "show" , show_id ) . await
632+ pub async fn get_show_metadata ( & self , show_uri : & SpotifyUri ) -> SpClientResult {
633+ self . get_metadata ( ExtensionKind :: SHOW_V4 , show_uri ) . await
608634 }
609635
610636 pub async fn get_lyrics ( & self , track_id : & SpotifyId ) -> SpClientResult {
@@ -733,12 +759,6 @@ impl SpClient {
733759 // TODO: Seen-in-the-wild but unimplemented endpoints
734760 // - /presence-view/v1/buddylist
735761
736- pub async fn get_extended_metadata ( & self , request : BatchedEntityRequest ) -> SpClientResult {
737- let endpoint = "/extended-metadata/v0/extended-metadata" ;
738- self . request_with_protobuf ( & Method :: POST , endpoint, None , & request)
739- . await
740- }
741-
742762 pub async fn get_audio_storage ( & self , file_id : & FileId ) -> SpClientResult {
743763 let endpoint = format ! (
744764 "/storage-resolve/files/audio/interactive/{}" ,
0 commit comments