diff --git a/SpotifyAPI/Web/SpotifyWebAPI.cs b/SpotifyAPI/Web/SpotifyWebAPI.cs
index d8795ab2..1d7f1eaa 100644
--- a/SpotifyAPI/Web/SpotifyWebAPI.cs
+++ b/SpotifyAPI/Web/SpotifyWebAPI.cs
@@ -44,6 +44,28 @@ namespace SpotifyAPI.Web
GC.SuppressFinalize(this);
}
+ #region Configuration
+ ///
+ /// Specifies after how many miliseconds should a failed request be retried.
+ ///
+ public int RetryAfter { get; set; } = 50;
+
+ ///
+ /// Should a failed request (specified by be automatically retried or not.
+ ///
+ public bool UseAutoRetry { get; set; } = false;
+
+ ///
+ /// Maximum number of tries for one failed request.
+ ///
+ public int RetryTimes { get; set; } = 10;
+
+ ///
+ /// Error codes that will trigger auto-retry if is enabled.
+ ///
+ public IEnumerable RetryErrorCodes { get; private set; } = new int[] { 500, 502, 503 };
+ #endregion Configuration
+
#region Search
///
@@ -697,24 +719,13 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for IsFollowing");
- Tuple res = DownloadDataAlt(_builder.IsFollowing(followType, ids));
- ListResponse ret;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+
+ var url = _builder.IsFollowing(followType, ids);
+ return DownloadList(url);
}
+
+
///
/// Check to see if the current user is following one or more artists or other Spotify users asynchronously.
///
@@ -726,22 +737,9 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for IsFollowing");
- Tuple res = await DownloadDataAltAsync(_builder.IsFollowing(followType, ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+
+ var url = _builder.IsFollowing(followType, ids);
+ return await DownloadListAsync(url);
}
///
@@ -850,22 +848,9 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for IsFollowingPlaylist");
- Tuple res = DownloadDataAlt(_builder.IsFollowingPlaylist(ownerId, playlistId, ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+
+ var url = _builder.IsFollowingPlaylist(ownerId, playlistId, ids);
+ return DownloadList(url);
}
///
@@ -880,22 +865,9 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for IsFollowingPlaylist");
- Tuple res = await DownloadDataAltAsync(_builder.IsFollowingPlaylist(ownerId, playlistId, ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+
+ var url = _builder.IsFollowingPlaylist(ownerId, playlistId, ids);
+ return await DownloadListAsync(url);
}
///
@@ -1038,22 +1010,9 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for CheckSavedTracks");
- Tuple res = DownloadDataAlt(_builder.CheckSavedTracks(ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+
+ var url = _builder.CheckSavedTracks(ids);
+ return DownloadList(url);
}
///
@@ -1066,22 +1025,8 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for CheckSavedTracks");
- Tuple res = await DownloadDataAltAsync(_builder.CheckSavedTracks(ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+ var url = _builder.CheckSavedTracks(ids);
+ return await DownloadListAsync(url);
}
///
@@ -1194,22 +1139,9 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for CheckSavedTracks");
- Tuple res = DownloadDataAlt(_builder.CheckSavedAlbums(ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+
+ var url = _builder.CheckSavedAlbums(ids);
+ return DownloadList(url);
}
///
@@ -1222,22 +1154,8 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for CheckSavedAlbumsAsync");
- Tuple res = await DownloadDataAltAsync(_builder.CheckSavedAlbums(ids));
- ListResponse ret = null;
- if (res.Item2 is JArray)
- ret = new ListResponse
- {
- List = res.Item2.ToObject>(),
- Error = null
- };
- else
- ret = new ListResponse
- {
- List = null,
- Error = res.Item2["error"].ToObject()
- };
- ret.AddResponseInfo(res.Item1);
- return ret;
+ var url = _builder.CheckSavedAlbums(ids);
+ return await DownloadListAsync(url);
}
#endregion Library
@@ -1911,15 +1829,94 @@ namespace SpotifyAPI.Web
return await GetPreviousPageAsync, T>(paging);
}
+ private ListResponse DownloadList(string url)
+ {
+ int triesLeft = RetryTimes + 1;
+ Error lastError = null;
+
+ ListResponse data = null;
+ do
+ {
+ if (data != null) { System.Threading.Thread.Sleep(RetryAfter); }
+ Tuple res = DownloadDataAlt(url);
+ data = ExtractDataToListResponse(res);
+
+ lastError = data.Error;
+
+ triesLeft -= 1;
+
+ } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
+
+ return data;
+ }
+
+ private async Task> DownloadListAsync(string url)
+ {
+ int triesLeft = RetryTimes + 1;
+ Error lastError = null;
+
+ ListResponse data = null;
+ do
+ {
+ if (data != null) { await Task.Delay(RetryAfter); }
+ Tuple res = await DownloadDataAltAsync(url);
+ data = ExtractDataToListResponse(res);
+
+ lastError = data.Error;
+
+ triesLeft -= 1;
+
+ } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
+
+ return data;
+ }
+
+ private static ListResponse ExtractDataToListResponse(Tuple res)
+ {
+ ListResponse ret = null;
+ if (res.Item2 is JArray)
+ {
+ ret = new ListResponse
+ {
+ List = res.Item2.ToObject>(),
+ Error = null
+ };
+ }
+ else
+ {
+ ret = new ListResponse
+ {
+ List = null,
+ Error = res.Item2["error"].ToObject()
+ };
+ }
+ ret.AddResponseInfo(res.Item1);
+ return ret;
+ }
+
public T UploadData(string url, string uploadData, string method = "POST") where T : BasicModel
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for all Upload-Actions");
- WebClient.SetHeader("Authorization", TokenType + " " + AccessToken);
- WebClient.SetHeader("Content-Type", "application/json");
+ int triesLeft = RetryTimes + 1;
+ Error lastError = null;
+
+ Tuple response = null;
+ do
+ {
+ WebClient.SetHeader("Authorization", TokenType + " " + AccessToken);
+ WebClient.SetHeader("Content-Type", "application/json");
+
+ if (response != null) { System.Threading.Thread.Sleep(RetryAfter); }
+ response = WebClient.UploadJson(url, uploadData, method);
+
+ response.Item2.AddResponseInfo(response.Item1);
+ lastError = response.Item2.Error;
+
+ triesLeft -= 1;
+
+ } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
- Tuple response = WebClient.UploadJson(url, uploadData, method);
- response.Item2.AddResponseInfo(response.Item1);
return response.Item2;
}
@@ -1927,35 +1924,70 @@ namespace SpotifyAPI.Web
{
if (!UseAuth)
throw new InvalidOperationException("Auth is required for all Upload-Actions");
- WebClient.SetHeader("Authorization", TokenType + " " + AccessToken);
- WebClient.SetHeader("Content-Type", "application/json");
- Tuple response = await WebClient.UploadJsonAsync(url, uploadData, method);
- response.Item2.AddResponseInfo(response.Item1);
+ int triesLeft = RetryTimes + 1;
+ Error lastError = null;
+
+ Tuple response = null;
+ do
+ {
+ WebClient.SetHeader("Authorization", TokenType + " " + AccessToken);
+ WebClient.SetHeader("Content-Type", "application/json");
+
+ if (response != null) { await Task.Delay(RetryAfter); }
+ response = await WebClient.UploadJsonAsync(url, uploadData, method);
+
+ response.Item2.AddResponseInfo(response.Item1);
+ lastError = response.Item2.Error;
+
+ triesLeft -= 1;
+
+ } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
+
return response.Item2;
}
public T DownloadData(string url) where T : BasicModel
{
- if (UseAuth)
- WebClient.SetHeader("Authorization", TokenType + " " + AccessToken);
- else
- WebClient.RemoveHeader("Authorization");
+ int triesLeft = RetryTimes + 1;
+ Error lastError = null;
+
+ Tuple response = null;
+ do
+ {
+ if(response != null) { System.Threading.Thread.Sleep(RetryAfter); }
+ response = DownloadDataAlt(url);
+
+ response.Item2.AddResponseInfo(response.Item1);
+ lastError = response.Item2.Error;
+
+ triesLeft -= 1;
+
+ } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
+
- Tuple response = WebClient.DownloadJson(url);
- response.Item2.AddResponseInfo(response.Item1);
return response.Item2;
}
public async Task DownloadDataAsync(string url) where T : BasicModel
{
- if (UseAuth)
- WebClient.SetHeader("Authorization", TokenType + " " + AccessToken);
- else
- WebClient.RemoveHeader("Authorization");
+ int triesLeft = RetryTimes + 1;
+ Error lastError = null;
+
+ Tuple response = null;
+ do
+ {
+ if (response != null) { await Task.Delay(RetryAfter); }
+ response = await DownloadDataAltAsync(url);
+
+ response.Item2.AddResponseInfo(response.Item1);
+ lastError = response.Item2.Error;
+
+ triesLeft -= 1;
+
+ } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
+
- Tuple response = await WebClient.DownloadJsonAsync(url);
- response.Item2.AddResponseInfo(response.Item1);
return response.Item2;
}