@@ -11,12 +11,14 @@ use rspotify::spotify::{
1111 page:: { CursorBasedPage , Page } ,
1212 playing:: PlayHistory ,
1313 playlist:: { PlaylistTrack , SimplifiedPlaylist } ,
14+ recommend:: Recommendations ,
1415 search:: { SearchAlbums , SearchArtists , SearchPlaylists , SearchTracks } ,
1516 track:: { FullTrack , SavedTrack , SimplifiedTrack } ,
1617 user:: PrivateUser ,
1718 } ,
1819 senum:: { Country , RepeatState } ,
1920} ;
21+ use serde_json:: { map:: Map , Value } ;
2022use std:: {
2123 cmp:: { max, min} ,
2224 collections:: HashSet ,
@@ -143,6 +145,7 @@ pub enum RouteId {
143145 MadeForYou ,
144146 Artists ,
145147 Podcasts ,
148+ Recommendations ,
146149}
147150
148151pub struct Route {
@@ -158,6 +161,7 @@ pub enum TrackTableContext {
158161 AlbumSearch ,
159162 PlaylistSearch ,
160163 SavedTracks ,
164+ RecommendedTracks ,
161165}
162166
163167#[ derive( Clone , PartialEq , Debug ) ]
@@ -166,6 +170,12 @@ pub enum AlbumTableContext {
166170 Full ,
167171}
168172
173+ #[ derive( Clone , PartialEq , Debug ) ]
174+ pub enum RecommendationsContext {
175+ Artist ,
176+ Song ,
177+ }
178+
169179pub struct SearchResult {
170180 pub albums : Option < SearchAlbums > ,
171181 pub artists : Option < SearchArtists > ,
@@ -242,6 +252,9 @@ pub struct App {
242252 pub playlist_tracks : Option < Page < PlaylistTrack > > ,
243253 pub playlists : Option < Page < SimplifiedPlaylist > > ,
244254 pub recently_played : SpotifyResultAndSelectedIndex < Option < CursorBasedPage < PlayHistory > > > ,
255+ pub recommended_tracks : Vec < FullTrack > ,
256+ pub recommendations_seed : String ,
257+ pub recommendations_context : Option < RecommendationsContext > ,
245258 pub search_results : SearchResult ,
246259 pub selected_album : Option < SelectedAlbum > ,
247260 pub selected_album_full : Option < SelectedFullAlbum > ,
@@ -293,6 +306,9 @@ impl App {
293306 playlist_offset : 0 ,
294307 playlist_tracks : None ,
295308 playlists : None ,
309+ recommended_tracks : vec ! [ ] ,
310+ recommendations_context : None ,
311+ recommendations_seed : "" . to_string ( ) ,
296312 search_results : SearchResult {
297313 hovered_block : SearchResultBlock :: SongSearch ,
298314 selected_block : SearchResultBlock :: Empty ,
@@ -479,6 +495,59 @@ impl App {
479495 }
480496 }
481497
498+ pub fn get_recommendations_for_seed (
499+ & mut self ,
500+ seed_artists : Option < Vec < String > > ,
501+ seed_tracks : Option < Vec < String > > ,
502+ first_track : Option < & FullTrack > ,
503+ ) {
504+ if let ( Some ( spotify) , Some ( user) ) = ( & self . spotify , & self . user . to_owned ( ) ) {
505+ let user_country =
506+ Country :: from_str ( & user. country . to_owned ( ) . unwrap_or_else ( || "" . to_string ( ) ) ) ;
507+ let empty_payload: Map < String , Value > = Map :: new ( ) ;
508+
509+ match spotify. recommendations (
510+ seed_artists, // artists
511+ None , // genres
512+ seed_tracks, // tracks
513+ self . large_search_limit , // adjust playlist to screen size
514+ user_country, // country
515+ & empty_payload, // payload
516+ ) {
517+ Ok ( result) => {
518+ if let Some ( mut recommended_tracks) = self . extract_recommended_tracks ( & result) {
519+ //custom first track
520+ if let Some ( track) = first_track {
521+ recommended_tracks. insert ( 0 , track. clone ( ) ) ;
522+ }
523+ self . recommended_tracks = recommended_tracks. clone ( ) ;
524+ self . set_tracks_to_table ( recommended_tracks) ;
525+ self . track_table . context = Some ( TrackTableContext :: RecommendedTracks ) ;
526+
527+ if self . get_current_route ( ) . id != RouteId :: Recommendations {
528+ self . push_navigation_stack (
529+ RouteId :: Recommendations ,
530+ ActiveBlock :: TrackTable ,
531+ ) ;
532+ } ;
533+ }
534+ self . start_recommendations_playback ( Some ( 0 ) ) ;
535+ }
536+ Err ( e) => println ! ( "error: {:?}" , e) ,
537+ }
538+ }
539+ }
540+
541+ pub fn get_recommendations_for_trackid ( & mut self , id : & str ) {
542+ if let Some ( track) = self . get_fulltrack_from_id ( id) {
543+ let track_id_list: Option < Vec < String > > = match & track. id {
544+ Some ( id) => Some ( vec ! [ id. to_string( ) ] ) ,
545+ None => None ,
546+ } ;
547+ self . get_recommendations_for_seed ( None , track_id_list, Some ( & track) ) ;
548+ }
549+ }
550+
482551 fn change_volume ( & mut self , volume_percent : u8 ) {
483552 if let ( Some ( spotify) , Some ( device_id) , Some ( context) ) = (
484553 & self . spotify ,
@@ -564,6 +633,19 @@ impl App {
564633 }
565634 }
566635
636+ pub fn start_recommendations_playback ( & mut self , offset : Option < usize > ) {
637+ self . start_playback (
638+ None ,
639+ Some (
640+ self . recommended_tracks
641+ . iter ( )
642+ . map ( |x| x. uri . clone ( ) )
643+ . collect :: < Vec < String > > ( ) ,
644+ ) ,
645+ offset,
646+ ) ;
647+ }
648+
567649 pub fn start_playback (
568650 & mut self ,
569651 context_uri : Option < String > ,
@@ -720,6 +802,42 @@ impl App {
720802 ) ;
721803 }
722804
805+ fn extract_recommended_tracks (
806+ & self ,
807+ recommendations : & Recommendations ,
808+ ) -> Option < Vec < FullTrack > > {
809+ if let Some ( spotify) = & self . spotify {
810+ let tracks = recommendations
811+ . clone ( )
812+ . tracks
813+ . into_iter ( )
814+ . map ( |item| item. uri )
815+ . collect :: < Vec < String > > ( ) ;
816+ if let Ok ( result) =
817+ spotify. tracks ( tracks. iter ( ) . map ( |x| & x[ ..] ) . collect :: < Vec < & str > > ( ) , None )
818+ {
819+ return Some ( result. tracks ) ;
820+ }
821+ }
822+
823+ None
824+ }
825+
826+ fn get_fulltrack_from_id ( & self , id : & str ) -> Option < FullTrack > {
827+ if let Some ( spotify) = & self . spotify {
828+ match spotify. track ( id) {
829+ Ok ( track) => {
830+ return Some ( track) ;
831+ }
832+ Err ( _e) => {
833+ return None ;
834+ }
835+ } ;
836+ }
837+
838+ None
839+ }
840+
723841 pub fn set_tracks_to_table ( & mut self , tracks : Vec < FullTrack > ) {
724842 self . track_table . tracks = tracks. clone ( ) ;
725843
0 commit comments