using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using MoreLinq; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.SpotifyWebAPI.Model; using Sony.Filtr.Utility.HttpHandlers; using Url = Flurl.Url; namespace Sony.Filtr.SpotifyWebAPI { public class AuthenticatedSpotifyWebApi : SpotifyWebApi { public readonly string ClientId; internal AuthenticatedSpotifyWebApi(ServiceAccount serviceAccount, string clientId, string clientSecret, IServiceAccountManager serviceAccountManager) : this(BuildLoggedInDefaultClient(serviceAccount, serviceAccountManager, clientId, clientSecret)) { this.ClientId = clientId; } public AuthenticatedSpotifyWebApi(HttpClient httpClient) : base(httpClient) { } private static HttpClient BuildLoggedInDefaultClient(ServiceAccount serviceAccount, IServiceAccountManager serviceAccountManager, string clientId, string clientSecret) { var baseHandler = new HttpClientHandler() { UseCookies = false, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip }; var rateLimitHandler = new ThreadRateLimitHttpHandler(1) { InnerHandler = baseHandler }; HttpClient client = new HttpClient(new SpotifyAuthorizationCodeHttpMessageHandler(serviceAccount, serviceAccountManager, clientId, clientSecret, rateLimitHandler)); client.BaseAddress = new Uri(BaseUrl); return client; } public async Task> AddAllTrackAsync(string userId, string playlistId, IEnumerable trackUrisToAdd) { List trackChangeResponses = new List(); foreach (var trackBatch in trackUrisToAdd.Batch(100)) { var trackChangeResponse = await AddTrackAsync(userId, playlistId, trackBatch); if (trackChangeResponse != null) trackChangeResponses.Add(trackChangeResponse); } return trackChangeResponses; } public async Task AddTrackAsync(string userId, string playlistId, IEnumerable trackUrisToAdd) { var url = new Url($"https://api.spotify.com/v1/users/{userId}/playlists/{playlistId}/tracks"); var addTrackRequest = new AddTracksRequest() { uris = trackUrisToAdd.ToList(), }; var response = await GetDefaultClient().PostAsJsonAsync(url, addTrackRequest); if (!response.IsSuccessStatusCode) { throw SpotifyWebAPIException.FromHttpResponseMessage(response, $"Could not add tracks. UserId: '{userId}'. PlaylistId: '{playlistId}'"); } return await response.Content.ReadAsAsync(); } private HttpContent GetJsonContent(object obj) { var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings() {ContractResolver = new CamelCasePropertyNamesContractResolver()}); return new StringContent(json, Encoding.UTF8, "application/json"); } public async Task DeleteMultiplePlaylistTracksAsync(string userId, string playlistId, IEnumerable trackUris, string snapshot = null) { var url = new Url($"https://api.spotify.com/v1/users/{userId}/playlists/{playlistId}/tracks"); List trackChangeResponses = new List(); string lastSnapshotId = null; foreach(var trackBatch in trackUris.Batch(100)) { var deleteRequest = new DeleteTrackRequest() { tracks = trackBatch.Select(t => new TrackId() { uri = t }).ToList(), snapshot_id = lastSnapshotId ?? snapshot }; var requestMessage = new HttpRequestMessage(HttpMethod.Delete, url) { Content = GetJsonContent(deleteRequest) }; var response = await GetDefaultClient().SendAsync(requestMessage); if (!response.IsSuccessStatusCode) { throw SpotifyWebAPIException.FromHttpResponseMessage(response, $"Could not delete tracks. UserId: '{userId}'. PlaylistId: '{playlistId}'"); } var trackChange = await response.Content.ReadAsAsync(); trackChangeResponses.Add(trackChange); lastSnapshotId = trackChange.snapshot_id; } return lastSnapshotId; } public async Task OrderPlaylistTracksAsync(string userId, string playlistId, int rangeStart, int rangeLength, int insertBefore, string snapshotId) { var url = new Url($"https://api.spotify.com/v1/users/{userId}/playlists/{playlistId}/tracks"); var reorderRequest = new ReorderRequest() { insert_before = insertBefore, range_length = rangeLength, range_start = rangeStart, snapshot_id = snapshotId, }; var response = await GetDefaultClient().PutAsJsonAsync(url, reorderRequest); if (!response.IsSuccessStatusCode) { throw SpotifyWebAPIException.FromHttpResponseMessage(response, $"Could not reorder tracks. UserId: '{userId}'. PlaylistId: '{playlistId}'"); } return await response.Content.ReadAsAsync(); } public async Task DeleteAllPlaylistTracksInPositionAsync(string userId, string playlistId, List trackDuplicatesToRemove, string snapshot) { var url = new Url($"https://api.spotify.com/v1/users/{userId}/playlists/{playlistId}/tracks"); string lastSnapshotId = null; List trackChangeResponses = new List(); foreach (var trackBatch in trackDuplicatesToRemove.Batch(100)) { var requestMessage = new HttpRequestMessage(HttpMethod.Delete, url); var jsonRequest = JsonConvert.SerializeObject(new RemoveTrackOccurrencesRequest() { positions = trackBatch.ToList(), snapshot_id = snapshot }, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }); requestMessage.Content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"); var response = await GetDefaultClient().SendAsync(requestMessage); if (!response.IsSuccessStatusCode) { throw SpotifyWebAPIException.FromHttpResponseMessage(response, $"Could not delete tracks in position. Playlist: '{playlistId}'. UserId: '{userId}'"); } var trackChange = await response.Content.ReadAsAsync(); trackChangeResponses.Add(trackChange); lastSnapshotId = trackChange.snapshot_id; } return lastSnapshotId; } public async Task SetPlaylistDetails(string user, string playlistId, string name, bool isPublic) { string requestUrl = string.Format("v1/users/{0}/playlists/{1}", user, playlistId); var detailsRequest = new PlaylistDetailsRequest() { name = name, @public = isPublic, }; var response = await GetDefaultClient().PutAsJsonAsync(requestUrl, detailsRequest); if (!response.IsSuccessStatusCode) { throw SpotifyWebAPIException.FromHttpResponseMessage(response, $"Could not set playlist details. User: '{user}'. PlaylistId: '{playlistId}'"); } } } }