@@ -3,6 +3,8 @@ use crate::config::{DBusType, MprisConfig};
3
3
#[ cfg( feature = "dbus_mpris" ) ]
4
4
use crate :: dbus_mpris:: DbusServer ;
5
5
use crate :: process:: spawn_program_on_event;
6
+ use crate :: utils:: Backoff ;
7
+ use color_eyre:: eyre:: { self , Context } ;
6
8
use futures:: future:: Either ;
7
9
#[ cfg( not( feature = "dbus_mpris" ) ) ]
8
10
use futures:: future:: Pending ;
@@ -24,7 +26,7 @@ use librespot_playback::{
24
26
mixer:: Mixer ,
25
27
player:: Player ,
26
28
} ;
27
- use log:: error;
29
+ use log:: { error, info } ;
28
30
use std:: pin:: Pin ;
29
31
use std:: sync:: Arc ;
30
32
@@ -102,43 +104,63 @@ impl MainLoop {
102
104
async fn get_connection ( & mut self ) -> Result < ConnectionInfo < impl Future < Output = ( ) > > , Error > {
103
105
let creds = self . credentials_provider . get_credentials ( ) . await ;
104
106
105
- let session = Session :: new ( self . session_config . clone ( ) , self . cache . clone ( ) ) ;
106
- let player = {
107
- let audio_device = self . audio_device . clone ( ) ;
108
- let audio_format = self . audio_format ;
109
- let backend = self . backend ;
110
- Player :: new (
111
- self . player_config . clone ( ) ,
107
+ let mut connection_backoff = Backoff :: default ( ) ;
108
+ loop {
109
+ let session = Session :: new ( self . session_config . clone ( ) , self . cache . clone ( ) ) ;
110
+ let player = {
111
+ let audio_device = self . audio_device . clone ( ) ;
112
+ let audio_format = self . audio_format ;
113
+ let backend = self . backend ;
114
+ Player :: new (
115
+ self . player_config . clone ( ) ,
116
+ session. clone ( ) ,
117
+ self . mixer . get_soft_volume ( ) ,
118
+ move || backend ( audio_device, audio_format) ,
119
+ )
120
+ } ;
121
+
122
+ // TODO: expose is_group
123
+ match Spirc :: new (
124
+ ConnectConfig {
125
+ name : self . device_name . clone ( ) ,
126
+ device_type : self . device_type ,
127
+ is_group : false ,
128
+ initial_volume : self . initial_volume ,
129
+ has_volume_ctrl : self . has_volume_ctrl ,
130
+ } ,
112
131
session. clone ( ) ,
113
- self . mixer . get_soft_volume ( ) ,
114
- move || backend ( audio_device, audio_format) ,
132
+ creds. clone ( ) ,
133
+ player. clone ( ) ,
134
+ self . mixer . clone ( ) ,
115
135
)
116
- } ;
117
-
118
- // TODO: expose is_group
119
- Spirc :: new (
120
- ConnectConfig {
121
- name : self . device_name . clone ( ) ,
122
- device_type : self . device_type ,
123
- is_group : false ,
124
- initial_volume : self . initial_volume ,
125
- has_volume_ctrl : self . has_volume_ctrl ,
126
- } ,
127
- session. clone ( ) ,
128
- creds,
129
- player. clone ( ) ,
130
- self . mixer . clone ( ) ,
131
- )
132
- . await
133
- . map ( |( spirc, spirc_task) | ConnectionInfo {
134
- spirc,
135
- session,
136
- player,
137
- spirc_task,
138
- } )
136
+ . await
137
+ {
138
+ Ok ( ( spirc, spirc_task) ) => {
139
+ break Ok ( ConnectionInfo {
140
+ spirc,
141
+ session,
142
+ player,
143
+ spirc_task,
144
+ } )
145
+ }
146
+ Err ( err) => {
147
+ let Ok ( backoff) = connection_backoff. next_backoff ( ) else {
148
+ break Err ( err) ;
149
+ } ;
150
+ error ! ( "connection to spotify failed: {err}" ) ;
151
+ info ! (
152
+ "retrying connection in {} seconds (retry {}/{})" ,
153
+ backoff. as_secs( ) ,
154
+ connection_backoff. retries( ) ,
155
+ connection_backoff. max_retries( )
156
+ ) ;
157
+ tokio:: time:: sleep ( backoff) . await ;
158
+ }
159
+ }
160
+ }
139
161
}
140
162
141
- pub ( crate ) async fn run ( mut self ) {
163
+ pub ( crate ) async fn run ( mut self ) -> eyre :: Result < ( ) > {
142
164
tokio:: pin! {
143
165
let ctrl_c = tokio:: signal:: ctrl_c( ) ;
144
166
// we don't necessarily have a dbus server
@@ -157,18 +179,15 @@ impl MainLoop {
157
179
None
158
180
} ;
159
181
160
- ' mainloop: loop {
182
+ let mainloop_result : eyre :: Result < ( ) > = ' mainloop: loop {
161
183
let connection = tokio:: select!(
162
184
_ = & mut ctrl_c => {
163
- break ' mainloop;
185
+ break ' mainloop Ok ( ( ) ) ;
164
186
}
165
187
connection = self . get_connection( ) => {
166
188
match connection {
167
189
Ok ( connection) => connection,
168
- Err ( err) => {
169
- error!( "failed to connect to spotify: {}" , err) ;
170
- break ' mainloop;
171
- }
190
+ Err ( err) => break ' mainloop Err ( err) . wrap_err( "failed to connect to spotify" ) ,
172
191
}
173
192
}
174
193
) ;
@@ -184,9 +203,8 @@ impl MainLoop {
184
203
. as_mut ( )
185
204
. set_session ( shared_spirc. clone ( ) , connection. session )
186
205
{
187
- error ! ( "failed to configure dbus server: {err}" ) ;
188
206
let _ = shared_spirc. shutdown ( ) ;
189
- break ' mainloop;
207
+ break ' mainloop Err ( err ) . wrap_err ( "failed to configure dbus server" ) ;
190
208
}
191
209
}
192
210
@@ -204,7 +222,7 @@ impl MainLoop {
204
222
// the program should shut down
205
223
_ = & mut ctrl_c => {
206
224
let _ = shared_spirc. shutdown( ) ;
207
- break ' mainloop;
225
+ break ' mainloop Ok ( ( ) ) ;
208
226
}
209
227
// spirc was shut down by some external factor
210
228
_ = & mut spirc_task => {
@@ -214,12 +232,9 @@ impl MainLoop {
214
232
result = & mut dbus_server => {
215
233
#[ cfg( feature = "dbus_mpris" ) ]
216
234
{
217
- if let Err ( err) = result {
218
- error!( "DBus terminated unexpectedly: {err}" ) ;
219
- }
220
235
let _ = shared_spirc. shutdown( ) ;
221
236
* dbus_server. as_mut( ) = Either :: Right ( future:: pending( ) ) ;
222
- break ' mainloop;
237
+ break ' mainloop result . wrap_err ( "DBus terminated unexpectedly" ) ;
223
238
}
224
239
#[ cfg( not( feature = "dbus_mpris" ) ) ]
225
240
result // unused variable
@@ -252,21 +267,27 @@ impl MainLoop {
252
267
#[ cfg( feature = "dbus_mpris" ) ]
253
268
if let Either :: Left ( dbus_server) = Either :: as_pin_mut ( dbus_server. as_mut ( ) ) {
254
269
if let Err ( err) = dbus_server. drop_session ( ) {
255
- error ! ( "failed to reconfigure dbus server: {err}" ) ;
256
- break ' mainloop;
270
+ break ' mainloop Err ( err) . wrap_err ( "failed to reconfigure DBus server" ) ;
257
271
}
258
272
}
259
- }
273
+ } ;
274
+
260
275
if let CredentialsProvider :: Discovery { stream, .. } = self . credentials_provider {
261
276
let _ = stream. into_inner ( ) . shutdown ( ) . await ;
262
277
}
263
278
#[ cfg( feature = "dbus_mpris" ) ]
264
279
if let Either :: Left ( dbus_server) = Either :: as_pin_mut ( dbus_server. as_mut ( ) ) {
265
280
if dbus_server. shutdown ( ) {
266
281
if let Err ( err) = dbus_server. await {
267
- error ! ( "failed to shutdown the dbus server: {err}" ) ;
282
+ let err = Err ( err) . wrap_err ( "failed to shutdown DBus server" ) ;
283
+ if mainloop_result. is_ok ( ) {
284
+ return err;
285
+ } else {
286
+ error ! ( "additional error while shutting down: {err:?}" ) ;
287
+ }
268
288
}
269
289
}
270
290
}
291
+ mainloop_result
271
292
}
272
293
}
0 commit comments