@@ -306,7 +306,7 @@ struct AudioFileDownloadStatus {
306306}
307307
308308struct AudioFileShared {
309- cdn_url : CdnUrl ,
309+ cdn_url : String ,
310310 file_size : usize ,
311311 bytes_per_second : usize ,
312312 cond : Condvar ,
@@ -426,25 +426,46 @@ impl AudioFileStreaming {
426426 ) -> Result < AudioFileStreaming , Error > {
427427 let cdn_url = CdnUrl :: new ( file_id) . resolve_audio ( & session) . await ?;
428428
429- if let Ok ( url) = cdn_url. try_get_url ( ) {
430- trace ! ( "Streaming from {}" , url) ;
431- }
432-
433429 let minimum_download_size = AudioFetchParams :: get ( ) . minimum_download_size ;
434430
435- // When the audio file is really small, this `download_size` may turn out to be
436- // larger than the audio file we're going to stream later on. This is OK; requesting
437- // `Content-Range` > `Content-Length` will return the complete file with status code
438- // 206 Partial Content.
439- let mut streamer =
440- session
441- . spclient ( )
442- . stream_from_cdn ( & cdn_url, 0 , minimum_download_size) ?;
431+ let mut response_streamer_url = None ;
432+ let urls = cdn_url. try_get_urls ( ) ?;
433+ for url in & urls {
434+ // When the audio file is really small, this `download_size` may turn out to be
435+ // larger than the audio file we're going to stream later on. This is OK; requesting
436+ // `Content-Range` > `Content-Length` will return the complete file with status code
437+ // 206 Partial Content.
438+ let mut streamer =
439+ session
440+ . spclient ( )
441+ . stream_from_cdn ( * url, 0 , minimum_download_size) ?;
442+
443+ // Get the first chunk with the headers to get the file size.
444+ // The remainder of that chunk with possibly also a response body is then
445+ // further processed in `audio_file_fetch`.
446+ let streamer_result = tokio:: time:: timeout ( Duration :: from_secs ( 10 ) , streamer. next ( ) )
447+ . await
448+ . map_err ( |_| AudioFileError :: WaitTimeout . into ( ) )
449+ . and_then ( |x| x. ok_or_else ( || AudioFileError :: NoData . into ( ) ) )
450+ . and_then ( |x| x. map_err ( Error :: from) ) ;
451+
452+ match streamer_result {
453+ Ok ( r) => {
454+ response_streamer_url = Some ( ( r, streamer, url) ) ;
455+ break ;
456+ }
457+ Err ( e) => warn ! ( "Fetching {url} failed with error {e:?}, trying next" ) ,
458+ }
459+ }
460+
461+ let Some ( ( response, streamer, url) ) = response_streamer_url else {
462+ return Err ( Error :: unavailable ( format ! (
463+ "{} URLs failed, none left to try" ,
464+ urls. len( )
465+ ) ) ) ;
466+ } ;
443467
444- // Get the first chunk with the headers to get the file size.
445- // The remainder of that chunk with possibly also a response body is then
446- // further processed in `audio_file_fetch`.
447- let response = streamer. next ( ) . await . ok_or ( AudioFileError :: NoData ) ??;
468+ trace ! ( "Streaming from {}" , url) ;
448469
449470 let code = response. status ( ) ;
450471 if code != StatusCode :: PARTIAL_CONTENT {
@@ -473,7 +494,7 @@ impl AudioFileStreaming {
473494 } ;
474495
475496 let shared = Arc :: new ( AudioFileShared {
476- cdn_url,
497+ cdn_url : url . to_string ( ) ,
477498 file_size,
478499 bytes_per_second,
479500 cond : Condvar :: new ( ) ,
0 commit comments