using Flurl; using Newtonsoft.Json; using Sony.Filtr.AppleMusic.Models; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Sony.Filtr.AppleMusic.Constants; using Sony.Filtr.Core.ResponseCollector; namespace Sony.Filtr.AppleMusic { public class AppleMusicApi { private readonly AppleMusicTokenGenerator _tokenGenerator; private readonly HttpClient _client; private readonly ResponseCollector _responseCollector; public AppleMusicApi(AppleMusicTokenGenerator tokenGenerator, ResponseCollector responseCollector) { _tokenGenerator = tokenGenerator; _responseCollector = responseCollector; _client = BuildApiHttpClient(); } public async Task GetAllStorefrontsAsync() { var url = $"/v1/storefronts"; var response = await _client.GetStringAsync(url); var storefronts = JsonConvert.DeserializeObject(response); return storefronts; } public async Task> GetPlaylistsAsync(string storeFront, IEnumerable playlistIds) { var url = $"/v1/catalog/{storeFront}/playlists?ids={string.Join(",", playlistIds)}"; var response = await _client.GetStringAsync(url); var playlistResponse = JsonConvert.DeserializeObject(response); return playlistResponse?.data; } public async Task> GetPlaylistsWithFullTracklistsAsync(string storefront, IEnumerable playlistIds) { var url = $"/v1/catalog/{storefront}/playlists?ids={string.Join(",", playlistIds)}"; var response = await _client.GetStringAsync(url); var playlistResponse = JsonConvert.DeserializeObject(response); //TODO figure out if we need pagination some other way as music videos do not seem to be included (at least on {playlist}/tracks/) 2018-11-27 if (playlistResponse?.data != null && playlistResponse.data.Any(x => x.relationships?.tracks?.data?.Count >= AppleMusicApiConstants.TrackDefaultPaginationLimit)) { foreach (var playlistData in playlistResponse.data.Where(x => x?.relationships?.tracks?.data.Count >= AppleMusicApiConstants.TrackDefaultPaginationLimit)) { playlistData.relationships.tracks.data = await GetPlaylistTracksAsync(storefront, playlistData.id); } } return playlistResponse?.data; } public async Task> GetPlaylistTracksAsync(string storefront, string playlistId) { var tracks = new List(); var url = $"/v1/catalog/{storefront}/playlists/{playlistId}/tracks?limit={AppleMusicApiConstants.TrackMaxPaginationLimit}"; do { var response = await _client.GetStringAsync(url); var tracksResponse = JsonConvert.DeserializeObject(response); tracks.AddRange(tracksResponse.Data); string nextUrl = null; if (tracksResponse.Next != null) nextUrl = new Flurl.Url(tracksResponse.Next).SetQueryParam("limit", AppleMusicApiConstants.TrackMaxPaginationLimit); url = nextUrl; } while (url != null); return tracks; } public async Task GetPlaylistAsync(string storeFront, string playlistId, string jobName = null) { var url = $"/v1/catalog/{storeFront}/playlists/{playlistId}"; var response = await _client.GetAsync(url); if (!response.IsSuccessStatusCode) { return null; } var responseString = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrEmpty(jobName)) { _responseCollector.PrepareFile(url, jobName, responseString); } var playlistResponse = JsonConvert.DeserializeObject(responseString); return playlistResponse?.data?.FirstOrDefault(); } public async Task GetPlaylistWithFullTracklistAsync(string storefront, string playlistId) { var url = $"/v1/catalog/{storefront}/playlists/{playlistId}"; var response = await _client.GetStringAsync(url); var playlistResponse = JsonConvert.DeserializeObject(response); var playlist = playlistResponse?.data?.FirstOrDefault(); //TODO figure out if we need pagination some other way as music videos do not seem to be included (at least on {playlist}/tracks/) 2018-11-27 if (playlist?.relationships?.tracks?.data?.Count >= AppleMusicApiConstants.TrackDefaultPaginationLimit) playlist.relationships.tracks.data = await GetPlaylistTracksAsync(storefront, playlistId); return playlist; } public async Task> GetCuratorsAsync(string storeFrontId, IEnumerable curatorIds) { var url = new Url($"/v1/catalog/{storeFrontId}/curators"); url.SetQueryParam("ids", string.Join(",", curatorIds)); var response = await _client.GetStringAsync(url); var curatorResponse = JsonConvert.DeserializeObject(response); return curatorResponse?.data; } private HttpClient BuildApiHttpClient() { var baseHandler = new HttpClientHandler() { UseCookies = false, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip }; var authHandler = new AppleMusicAuthClientCredentialsHttpMessageHandler(_tokenGenerator, baseHandler); return new HttpClient(authHandler) { BaseAddress = new Uri(AppleMusicApiConstants.ApiUrlBase) }; } public async Task> GetSongsAsync(string storefrontId, IEnumerable songIds) { var url = new Flurl.Url($"/v1/catalog/{storefrontId}/songs"); url.SetQueryParam("ids", string.Join(",", songIds)); var response = await _client.GetStringAsync(url); var songResponse = JsonConvert.DeserializeObject(response); return songResponse.data; } public async Task> GetAlbumsAsync(string storefrontId, IEnumerable albumIds) { var url = new Url($"/v1/catalog/{storefrontId}/albums"); url.SetQueryParam("ids", string.Join(",", albumIds)); var response = await _client.GetStringAsync(url); var albumResponse = JsonConvert.DeserializeObject(response); return albumResponse.data; } public async Task> GetArtistsAsync(string storefrontId, IEnumerable artistIds) { var url = new Flurl.Url($"/v1/catalog/{storefrontId}/artists"); url.SetQueryParam("ids", string.Join(",", artistIds)); var response = await _client.GetStringAsync(url); var songResponse = JsonConvert.DeserializeObject(response); return songResponse.data; } } }