@@ -60,22 +60,55 @@ impl Client {
6060 }
6161
6262 /// Initialize the application's playback upon creating a new session or during startup
63- pub async fn initialize_playback ( & self , state : & SharedState ) -> Result < ( ) > {
64- self . retrieve_current_playback ( state, false ) . await ?;
65-
66- if state. player . read ( ) . playback . is_none ( ) {
67- tracing:: info!( "No playback found, trying to connect to an available device..." ) ;
68- // // handle `connect_device` task separately as we don't want to block here
69- tokio:: task:: spawn ( {
70- let client = self . clone ( ) ;
71- let state = state. clone ( ) ;
72- async move {
73- client. connect_device ( & state) . await ;
74- }
75- } ) ;
76- }
63+ pub fn initialize_playback ( & self , state : & SharedState ) {
64+ tokio:: task:: spawn ( {
65+ let client = self . clone ( ) ;
66+ let state = state. clone ( ) ;
67+ async move {
68+ // The main playback initialization logic is simple:
69+ // if there is no playback, connect to an available device
70+ //
71+ // However, because it takes time for Spotify server to show up new changes,
72+ // a retry logic is implemented to ensure the application's state is properly initialized
73+ let delay = std:: time:: Duration :: from_secs ( 1 ) ;
74+
75+ for _ in 0 ..5 {
76+ tokio:: time:: sleep ( delay) . await ;
77+
78+ if let Err ( err) = client. retrieve_current_playback ( & state, false ) . await {
79+ tracing:: error!( "Failed to retrieve current playback: {err:#}" ) ;
80+ return ;
81+ }
7782
78- Ok ( ( ) )
83+ // if playback exists, don't connect to a new device
84+ if state. player . read ( ) . playback . is_some ( ) {
85+ continue ;
86+ }
87+
88+ let id = match client. find_available_device ( ) . await {
89+ Ok ( Some ( id) ) => Some ( Cow :: Owned ( id) ) ,
90+ Ok ( None ) => None ,
91+ Err ( err) => {
92+ tracing:: error!( "Failed to find an available device: {err:#}" ) ;
93+ None
94+ }
95+ } ;
96+
97+ if let Some ( id) = id {
98+ tracing:: info!( "Trying to connect to device (id={id})" ) ;
99+ if let Err ( err) = client. transfer_playback ( & id, Some ( false ) ) . await {
100+ tracing:: warn!( "Connection failed (device_id={id}): {err:#}" ) ;
101+ } else {
102+ tracing:: info!( "Connection succeeded (device_id={id})!" ) ;
103+ // upon new connection, reset the buffered playback
104+ state. player . write ( ) . buffered_playback = None ;
105+ client. update_playback ( & state) ;
106+ break ;
107+ }
108+ }
109+ }
110+ }
111+ } ) ;
79112 }
80113
81114 /// Create a new client session
@@ -114,10 +147,7 @@ impl Client {
114147 if let Some ( state) = state {
115148 // reset the application's caches
116149 state. data . write ( ) . caches = MemoryCaches :: new ( ) ;
117-
118- self . initialize_playback ( state)
119- . await
120- . context ( "initialize playback" ) ?;
150+ self . initialize_playback ( state) ;
121151 }
122152
123153 Ok ( ( ) )
@@ -554,40 +584,6 @@ impl Client {
554584 Ok ( ( ) )
555585 }
556586
557- /// Connect to a Spotify device
558- async fn connect_device ( & self , state : & SharedState ) {
559- // Device connection can fail when the specified device hasn't shown up
560- // in the Spotify's server, resulting in a failed `TransferPlayback` API request.
561- // This is why a retry mechanism is needed to ensure a successful connection.
562- let delay = std:: time:: Duration :: from_secs ( 1 ) ;
563-
564- for _ in 0 ..10 {
565- tokio:: time:: sleep ( delay) . await ;
566-
567- let id = match self . find_available_device ( ) . await {
568- Ok ( Some ( id) ) => Some ( Cow :: Owned ( id) ) ,
569- Ok ( None ) => None ,
570- Err ( err) => {
571- tracing:: error!( "Failed to find an available device: {err:#}" ) ;
572- None
573- }
574- } ;
575-
576- if let Some ( id) = id {
577- tracing:: info!( "Trying to connect to device (id={id})" ) ;
578- if let Err ( err) = self . transfer_playback ( & id, Some ( false ) ) . await {
579- tracing:: warn!( "Connection failed (device_id={id}): {err:#}" ) ;
580- } else {
581- tracing:: info!( "Connection succeeded (device_id={id})!" ) ;
582- // upon new connection, reset the buffered playback
583- state. player . write ( ) . buffered_playback = None ;
584- self . update_playback ( state) ;
585- break ;
586- }
587- }
588- }
589- }
590-
591587 pub fn update_playback ( & self , state : & SharedState ) {
592588 // After handling a request changing the player's playback,
593589 // update the playback state by making multiple get-playback requests.
0 commit comments