1- using Newtonsoft . Json ;
1+ using Newtonsoft . Json ;
22using Newtonsoft . Json . Linq ;
33using SpotifyAPI . Web . Enums ;
44using SpotifyAPI . Web . Models ;
@@ -23,15 +23,14 @@ public SpotifyWebAPI(ProxyConfig proxyConfig)
2323 {
2424 _builder = new SpotifyWebBuilder ( ) ;
2525 UseAuth = true ;
26- WebClient = new SpotifyWebClient
26+ WebClient = new SpotifyWebClient ( proxyConfig )
2727 {
2828 JsonSettings =
2929 new JsonSerializerSettings
3030 {
3131 NullValueHandling = NullValueHandling . Ignore ,
3232 TypeNameHandling = TypeNameHandling . All
33- } ,
34- ProxyConfig = proxyConfig
33+ }
3534 } ;
3635 }
3736
@@ -79,6 +78,11 @@ public void Dispose()
7978 /// </summary>
8079 public int RetryTimes { get ; set ; } = 10 ;
8180
81+ /// <summary>
82+ /// Whether a failure of type "Too Many Requests" should use up one of the allocated retry attempts.
83+ /// </summary>
84+ public bool TooManyRequestsConsumesARetry { get ; set ; } = false ;
85+
8286 /// <summary>
8387 /// Error codes that will trigger auto-retry if <see cref="UseAutoRetry"/> is enabled.
8488 /// </summary>
@@ -2001,11 +2005,12 @@ public Task<ErrorResponse> TransferPlaybackAsync(List<string> deviceIds, bool pl
20012005 /// <param name="deviceId">The id of the device this command is targeting. If not supplied, the user's currently active device is the target.</param>
20022006 /// <param name="contextUri">Spotify URI of the context to play.</param>
20032007 /// <param name="uris">A JSON array of the Spotify track URIs to play.</param>
2004- /// <param name="offset">Indicates from where in the context playback should start.
2008+ /// <param name="offset">Indicates from where in the context playback should start.
2009+ /// <param name="positionMs">The starting time to seek the track to</param>
20052010 /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used.</param>
20062011 /// <returns></returns>
20072012 public ErrorResponse ResumePlayback ( string deviceId = "" , string contextUri = "" , List < string > uris = null ,
2008- int ? offset = null )
2013+ int ? offset = null , int positionMs = 0 )
20092014 {
20102015 JObject ob = new JObject ( ) ;
20112016 if ( ! string . IsNullOrEmpty ( contextUri ) )
@@ -2014,6 +2019,8 @@ public ErrorResponse ResumePlayback(string deviceId = "", string contextUri = ""
20142019 ob . Add ( "uris" , new JArray ( uris ) ) ;
20152020 if ( offset != null )
20162021 ob . Add ( "offset" , new JObject { { "position" , offset } } ) ;
2022+ if ( positionMs > 0 )
2023+ ob . Add ( "position_ms" , positionMs ) ;
20172024 return UploadData < ErrorResponse > ( _builder . ResumePlayback ( deviceId ) , ob . ToString ( Formatting . None ) , "PUT" ) ;
20182025 }
20192026
@@ -2023,11 +2030,12 @@ public ErrorResponse ResumePlayback(string deviceId = "", string contextUri = ""
20232030 /// <param name="deviceId">The id of the device this command is targeting. If not supplied, the user's currently active device is the target.</param>
20242031 /// <param name="contextUri">Spotify URI of the context to play.</param>
20252032 /// <param name="uris">A JSON array of the Spotify track URIs to play.</param>
2026- /// <param name="offset">Indicates from where in the context playback should start.
2033+ /// <param name="offset">Indicates from where in the context playback should start.
2034+ /// <param name="positionMs">The starting time to seek the track to</param>
20272035 /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used.</param>
20282036 /// <returns></returns>
20292037 public Task < ErrorResponse > ResumePlaybackAsync ( string deviceId = "" , string contextUri = "" , List < string > uris = null ,
2030- int ? offset = null )
2038+ int ? offset = null , int positionMs = 0 )
20312039 {
20322040 JObject ob = new JObject ( ) ;
20332041 if ( ! string . IsNullOrEmpty ( contextUri ) )
@@ -2036,6 +2044,8 @@ public Task<ErrorResponse> ResumePlaybackAsync(string deviceId = "", string cont
20362044 ob . Add ( "uris" , new JArray ( uris ) ) ;
20372045 if ( offset != null )
20382046 ob . Add ( "offset" , new JObject { { "position" , offset } } ) ;
2047+ if ( positionMs > 0 )
2048+ ob . Add ( "position_ms" , positionMs ) ;
20392049 return UploadDataAsync < ErrorResponse > ( _builder . ResumePlayback ( deviceId ) , ob . ToString ( Formatting . None ) , "PUT" ) ;
20402050 }
20412051
@@ -2045,11 +2055,12 @@ public Task<ErrorResponse> ResumePlaybackAsync(string deviceId = "", string cont
20452055 /// <param name="deviceId">The id of the device this command is targeting. If not supplied, the user's currently active device is the target.</param>
20462056 /// <param name="contextUri">Spotify URI of the context to play.</param>
20472057 /// <param name="uris">A JSON array of the Spotify track URIs to play.</param>
2048- /// <param name="offset">Indicates from where in the context playback should start.
2058+ /// <param name="offset">Indicates from where in the context playback should start.
2059+ /// <param name="positionMs">The starting time to seek the track to</param>
20492060 /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used.</param>
20502061 /// <returns></returns>
20512062 public ErrorResponse ResumePlayback ( string deviceId = "" , string contextUri = "" , List < string > uris = null ,
2052- string offset = "" )
2063+ string offset = "" , int positionMs = 0 )
20532064 {
20542065 JObject ob = new JObject ( ) ;
20552066 if ( ! string . IsNullOrEmpty ( contextUri ) )
@@ -2058,6 +2069,8 @@ public ErrorResponse ResumePlayback(string deviceId = "", string contextUri = ""
20582069 ob . Add ( "uris" , new JArray ( uris ) ) ;
20592070 if ( ! string . IsNullOrEmpty ( offset ) )
20602071 ob . Add ( "offset" , new JObject { { "uri" , offset } } ) ;
2072+ if ( positionMs > 0 )
2073+ ob . Add ( "position_ms" , positionMs ) ;
20612074 return UploadData < ErrorResponse > ( _builder . ResumePlayback ( deviceId ) , ob . ToString ( Formatting . None ) , "PUT" ) ;
20622075 }
20632076
@@ -2067,11 +2080,12 @@ public ErrorResponse ResumePlayback(string deviceId = "", string contextUri = ""
20672080 /// <param name="deviceId">The id of the device this command is targeting. If not supplied, the user's currently active device is the target.</param>
20682081 /// <param name="contextUri">Spotify URI of the context to play.</param>
20692082 /// <param name="uris">A JSON array of the Spotify track URIs to play.</param>
2070- /// <param name="offset">Indicates from where in the context playback should start.
2083+ /// <param name="offset">Indicates from where in the context playback should start.
2084+ /// <param name="positionMs">The starting time to seek the track to</param>
20712085 /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used.</param>
20722086 /// <returns></returns>
20732087 public Task < ErrorResponse > ResumePlaybackAsync ( string deviceId = "" , string contextUri = "" , List < string > uris = null ,
2074- string offset = "" )
2088+ string offset = "" , int positionMs = 0 )
20752089 {
20762090 JObject ob = new JObject ( ) ;
20772091 if ( ! string . IsNullOrEmpty ( contextUri ) )
@@ -2080,6 +2094,8 @@ public Task<ErrorResponse> ResumePlaybackAsync(string deviceId = "", string cont
20802094 ob . Add ( "uris" , new JArray ( uris ) ) ;
20812095 if ( ! string . IsNullOrEmpty ( offset ) )
20822096 ob . Add ( "offset" , new JObject { { "uri" , offset } } ) ;
2097+ if ( positionMs > 0 )
2098+ ob . Add ( "position_ms" , positionMs ) ;
20832099 return UploadDataAsync < ErrorResponse > ( _builder . ResumePlayback ( deviceId ) , ob . ToString ( Formatting . None ) , "PUT" ) ;
20842100 }
20852101
@@ -2434,6 +2450,26 @@ public T DownloadData<T>(string url) where T : BasicModel
24342450 return response . Item2 ;
24352451 }
24362452
2453+ /// <summary>
2454+ /// Retrieves whether request had a "TooManyRequests" error, and get the amount Spotify recommends waiting before another request.
2455+ /// </summary>
2456+ /// <param name="info">Info object to analyze.</param>
2457+ /// <returns>Seconds to wait before making another request. -1 if no error.</returns>
2458+ /// <remarks>AUTH NEEDED</remarks>
2459+ private int GetTooManyRequests ( ResponseInfo info )
2460+ {
2461+ // 429 is "TooManyRequests" value specified in Spotify API
2462+ if ( 429 != ( int ) info . StatusCode )
2463+ {
2464+ return - 1 ;
2465+ }
2466+ if ( ! int . TryParse ( info . Headers . Get ( "Retry-After" ) , out var secondsToWait ) )
2467+ {
2468+ return - 1 ;
2469+ }
2470+ return secondsToWait ;
2471+ }
2472+
24372473 public async Task < T > DownloadDataAsync < T > ( string url ) where T : BasicModel
24382474 {
24392475 int triesLeft = RetryTimes + 1 ;
@@ -2442,15 +2478,30 @@ public async Task<T> DownloadDataAsync<T>(string url) where T : BasicModel
24422478 Tuple < ResponseInfo , T > response = null ;
24432479 do
24442480 {
2445- if ( response != null ) { await Task . Delay ( RetryAfter ) . ConfigureAwait ( false ) ; }
2481+ if ( response != null )
2482+ {
2483+ int msToWait = RetryAfter ;
2484+ var secondsToWait = GetTooManyRequests ( response . Item1 ) ;
2485+ if ( secondsToWait > 0 )
2486+ {
2487+ msToWait = secondsToWait * 1000 ;
2488+ }
2489+ await Task . Delay ( msToWait ) . ConfigureAwait ( false ) ;
2490+ }
24462491 response = await DownloadDataAltAsync < T > ( url ) . ConfigureAwait ( false ) ;
24472492
24482493 response . Item2 . AddResponseInfo ( response . Item1 ) ;
24492494 lastError = response . Item2 . Error ;
24502495
2451- triesLeft -= 1 ;
2496+ if ( TooManyRequestsConsumesARetry || GetTooManyRequests ( response . Item1 ) == - 1 )
2497+ {
2498+ triesLeft -= 1 ;
2499+ }
24522500
2453- } while ( UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes . Contains ( lastError . Status ) ) ;
2501+ } while ( UseAutoRetry
2502+ && triesLeft > 0
2503+ && ( GetTooManyRequests ( response . Item1 ) != - 1
2504+ || ( lastError != null && RetryErrorCodes . Contains ( lastError . Status ) ) ) ) ;
24542505
24552506
24562507 return response . Item2 ;
0 commit comments