using Nancy; using Sony.Filtr.AdminAPI.Utility; using Sony.Filtr.Applications; using Sony.Filtr.Buzz; using Sony.Filtr.Contracts.Definitions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.Contracts.Entities.Buzz; using Sony.Filtr.Core.SonyMusic; using Sony.Filtr.DistributedCaching; using Sony.Filtr.Playlists.Spotify; using Sony.Filtr.Playlists.Spotify.Model; using Sony.Filtr.SpotifyWebAPI; using Sony.Filtr.Utility; using Sony.Filtr.Utility.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Sony.Filtr.Playlists.Spotify.Model.History; using TaskExtensions = Sony.Filtr.Utility.Extensions.TaskExtensions; namespace Sony.Filtr.AdminAPI.Modules.SpotifySpecific { public class SpotifyPlaylistTracklistModule : BaseModule { private readonly SpotifyWebApi _spotifyWebApi; private readonly SpotifyPlaylistHistoricTrackListManager _historicTrackListManager; private readonly BuzzAccountManager _buzzAccountManager; private readonly SonyMusicManager _sonyMusicManager; private readonly DistributedCacheHandler _distributedCacheHandler; private readonly SpotifyPlaylistManager _spotifyPlaylistManager; public SpotifyPlaylistTracklistModule(SpotifyPlaylistManager spotifyPlaylistManager, ApplicationInstanceManager applicationInstanceManager, SpotifyWebApi spotifyWebApi, SpotifyPlaylistHistoricTrackListManager historicTrackListManager, BuzzAccountManager buzzAccountManager, SonyMusicManager sonyMusicManager, DistributedCacheHandler distributedCacheHandler) : base(applicationInstanceManager) { _spotifyWebApi = spotifyWebApi; _historicTrackListManager = historicTrackListManager; _buzzAccountManager = buzzAccountManager; _sonyMusicManager = sonyMusicManager; _distributedCacheHandler = distributedCacheHandler; _spotifyPlaylistManager = spotifyPlaylistManager; Get["GetSpotifyTracklistDates", "/spotifyPlaylist/tracklisthistory/dates", true] = async (parameters, cx) => { string playlistId = Request.GetRequiredPlaylistId(); var availableDates = await _historicTrackListManager.GetAvailableHistoricTrackListDatesAsync(playlistId); var datesViewModel = availableDates.ToDictionary(d => d.Key.Year + "-" + d.Key.Month.ToString("D2") + "-" + d.Key.Day.ToString("D2"), d => d.Value); return Response.AsJson(datesViewModel); }; Get["GetSpotifyTracklistHistory", "/spotifyPlaylist/tracklisthistory/{date}/", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); string region = Request.Query.region; var limit = Maybe.ToIntOrFallback((string)Request.Query.limit, 1000); var offset = Maybe.ToIntOrFallback((string)Request.Query.offset, 0); var date = (string)parameters.date; var trackList = await _historicTrackListManager.GetHistoricTrackListAsync(playlistId, date); var viewModel = await BuildViewModelAsync(trackList, region); var paginatedContent = viewModel.Paginate(limit, offset); return Response.AsJson(paginatedContent); }; Get["GetSpotifyPlaylistPreviousTracks", "/spotifyPlaylist/tracklisthistory/previousTracks/", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); var limit = Maybe.ToIntOrFallback((string)Request.Query.limit, 1000); var offset = Maybe.ToIntOrFallback((string)Request.Query.offset, 0); var region = (string)Request.Query.region ?? (string)Request.Query.market; var previousTracks = await _historicTrackListManager.GetPreviousTracksAsync(playlistId, limit, offset); previousTracks = await SetIsSonyAsync(previousTracks, region); return Response.AsJson(previousTracks); }; Get["GetSpotifyPlaylistsWithHistoryForTrack", "/spotifyPlaylist/tracklisthistory/track/", true] = async (parameters, cx) => { var limit = Maybe.ToIntOrFallback((string)Request.Query.limit, 1000); var offset = Maybe.ToIntOrFallback((string)Request.Query.offset, 0); var trackUri = (string)Request.Query.trackUri; string[] isrcs = GetIsrcFromRequestAsync(Request); if (!string.IsNullOrWhiteSpace(trackUri) && ((isrcs == null) || isrcs.Length == 0)) { var spotifyLink = new SpotifyLink(trackUri); var trackResponse = (await spotifyWebApi.GetAllTracksAsync(new List() { spotifyLink.ExtractTrackID() })).ToList(); trackResponse = trackResponse.FilterNull().ToList(); if (trackResponse.Any()) { isrcs = new string[] { trackResponse.First().external_ids.isrc }; } } if ((isrcs == null) || (isrcs.Length == 0)) { return HttpStatusCode.BadRequest; } var excludeCurrentPlaylists = Maybe.ToBoolOrFallback((string)Request.Query.excludeCurrentPlaylists, false); var cacheKey = "/spotifyPlaylist/tracklisthistory/track/" + String.Join(",", isrcs) + ":" + excludeCurrentPlaylists; var historicPlaylistsForTrackViewModels = _distributedCacheHandler.GetList(cacheKey); if (historicPlaylistsForTrackViewModels == null) { var historicPlaylist = await _historicTrackListManager.GetHistoricPlaylistsWithTrackSummaryAsync(isrcs, excludeCurrentPlaylists); historicPlaylistsForTrackViewModels = historicPlaylist.Select(p => new HistoricTrackInPlaylistViewModel() { Playlist = new SpotifyPlaylistViewModel() { Name = p.SpotifyPlaylist.Name, SpotifyLink = p.SpotifyPlaylist.PlaylistUri, CountryCode = p.SpotifyPlaylist.CountryCode, Description = p.SpotifyPlaylist.Description, BuzzCategoryId = p.SpotifyPlaylist.BuzzCategoryId, Subscribers = p.SpotifyPlaylist.Followers, ImageFileName = p.SpotifyPlaylist.Image, AccountId = p.AccountId, AccountName = p.AccountName }, EarliestTrackDate = p.EarliestTrackDate, LatestTrackDate = p.LatestTrackDate, ISRC = p.ISRC }).ToList(); _distributedCacheHandler.PutList(cacheKey, historicPlaylistsForTrackViewModels, DateTime.Now.AddMinutes(5)); } var paginatedViewModels = historicPlaylistsForTrackViewModels.Paginate(limit, offset); return Response.AsJson(paginatedViewModels); }; Get["GetSpotifyTracklist", "/spotifyPlaylist/tracklist/", true] = async (parameters, cx) => { string playlistId = Request.GetRequiredPlaylistId(); string region = Request.Query.region; var limit = Maybe.ToIntOrFallback((string)Request.Query.limit, 1000); var offset = Maybe.ToIntOrFallback((string)Request.Query.offset, 0); IEnumerable trackList = await spotifyPlaylistManager.GetTrackListWithDataByIdAsync(playlistId); var viewModel = await BuildViewModelAsync(trackList, region); var paginatedContent = viewModel.Paginate(limit, offset); return Response.AsJson(paginatedContent); }; Get["GetPlaylistedTracklistSummary", "/spotifyPlaylist/tracklistSummary", true] = async (parameters, cx) => { var trackIdsParameter = (string)Request.Query.trackId; var trackIds = trackIdsParameter?.Split(',').ToList(); if (trackIds == null || !trackIds.Any()) return HttpStatusCode.BadRequest; IEnumerable trackIdSummary = Enumerable.Empty(); Dictionary isrcsMapping = new Dictionary(); IEnumerable trackIdSummaryIsrc = Enumerable.Empty(); IEnumerable buzzCategories = Enumerable.Empty(); var tasks = new List { Task.Run(async () => { trackIdSummary = await historicTrackListManager.GetPlaylistSummaryByTrackIdAsync(trackIds); }), Task.Run(async () => { isrcsMapping = await spotifyPlaylistManager.GetIsrcFromTrackIdAsync(trackIds); }) }; await TaskExtensions.EnsureAllSuccessAsync(tasks, TaskScheduler.FromCurrentSynchronizationContext()); IEnumerable isrcs = isrcsMapping.Select(p => p.Value).ToArray(); if (!isrcs.Any()) { var emptyViewModels = trackIds.Select(trackId => new TrackPlaylistSummaryViewModel() { TrackId = trackId }).ToList(); return Response.AsJson(emptyViewModels); } tasks.Clear(); tasks.Add(Task.Run(async () => { trackIdSummaryIsrc = await historicTrackListManager.GetPlaylistSummaryByISRCAsync(isrcs); })); tasks.Add(Task.Run(() => { buzzCategories = _buzzAccountManager.GetBuzzCategories(); })); await TaskExtensions.EnsureAllSuccessAsync(tasks, TaskScheduler.FromCurrentSynchronizationContext()); IEnumerable trackPlaylistSummaries = BuildTrackPlaylistSummaryViewModels(trackIds, isrcsMapping, trackIdSummaryIsrc, trackIdSummary, buzzCategories); return Response.AsJson(trackPlaylistSummaries); }; Get["GetPlaylistedTracklistHistorySummary", "/spotifyPlaylist/tracklistHistorySummary", true] = async (parameters, cx) => { var trackIdsParameter = (string)Request.Query.trackId; var trackIds = trackIdsParameter?.Split(',').ToList(); if (trackIds == null || !trackIds.Any()) return HttpStatusCode.BadRequest; var date = Maybe.ToDateTimeOrDefault((string)Request.Query.date); IEnumerable trackIdSummary = Enumerable.Empty(); Dictionary isrcsMapping = new Dictionary(); IEnumerable trackIdSummaryIsrc = Enumerable.Empty(); IEnumerable buzzCategories = Enumerable.Empty(); var tasks = new List { Task.Run(async () => { trackIdSummary = await historicTrackListManager.GetPlaylistHistorySummaryByTrackIdAsync(trackIds, date); }), Task.Run(async () => { isrcsMapping = await spotifyPlaylistManager.GetIsrcFromTrackIdAsync(trackIds); }) }; await TaskExtensions.EnsureAllSuccessAsync(tasks, TaskScheduler.FromCurrentSynchronizationContext()); IEnumerable isrcs = isrcsMapping.Select(p => p.Value).ToArray(); if (!isrcs.Any()) { var emptyViewModels = trackIds.Select(trackId => new TrackPlaylistSummaryViewModel() { TrackId = trackId }).ToList(); return Response.AsJson(emptyViewModels); } IEnumerable trackPlaylistSummaries = BuildTrackPlaylistSummaryViewModels(trackIds, isrcsMapping, trackIdSummaryIsrc, trackIdSummary, buzzCategories); return Response.AsJson(trackPlaylistSummaries); }; Get["GetPlaylistedTracklistHistoryOnlySummary", "/spotifyPlaylist/tracklistHistoryOnlySummary", true] = async (parameters, cx) => { var trackIdsParameter = (string)Request.Query.trackId; var trackIds = trackIdsParameter?.Split(',').ToList(); if (trackIds == null || !trackIds.Any()) return HttpStatusCode.BadRequest; DateTime? date = Maybe.ToDateTimeOrDefault((string)Request.Query.date); IEnumerable trackIdSummary = Enumerable.Empty(); Dictionary isrcsMapping = new Dictionary(); IEnumerable trackIdSummaryIsrc = Enumerable.Empty(); IEnumerable buzzCategories = Enumerable.Empty(); var tasks = new List { Task.Run(async () => { trackIdSummary = await historicTrackListManager.GetPlaylistHistoricOnlySummaryByTrackIdAsync(trackIds, date); }), Task.Run(async () => { isrcsMapping = await spotifyPlaylistManager.GetIsrcFromTrackIdAsync(trackIds); }) }; await TaskExtensions.EnsureAllSuccessAsync(tasks, TaskScheduler.FromCurrentSynchronizationContext()); var isrcs = isrcsMapping.Select(p => p.Value).ToArray(); if (!isrcs.Any()) { var emptyViewModels = trackIds.Select(trackId => new TrackPlaylistSummaryViewModel() { TrackId = trackId }).ToArray(); return Response.AsJson(emptyViewModels); } tasks.Clear(); tasks.Add(Task.Run(async () => { trackIdSummaryIsrc = await historicTrackListManager.GetPlaylistHistoricOnlySummaryByIsrcAsync(isrcs, date); })); tasks.Add(Task.Run(() => { buzzCategories = _buzzAccountManager.GetBuzzCategories(); })); await TaskExtensions.EnsureAllSuccessAsync(tasks, TaskScheduler.FromCurrentSynchronizationContext()); IEnumerable trackPlaylistSummaries = BuildTrackPlaylistSummaryViewModels(trackIds, isrcsMapping, trackIdSummaryIsrc, trackIdSummary, buzzCategories); return Response.AsJson(trackPlaylistSummaries); }; Get["GetPlaylistedTracklistHistoryChange", "/spotifyPlaylist/tracklistHistoryChange", true] = async (parameters, cx) => { var trackIdsParameter = (string)Request.Query.trackId; var trackIds = trackIdsParameter?.Split(',').ToList(); if (trackIds == null || !trackIds.Any()) return HttpStatusCode.BadRequest; var startDate = Maybe.ToDateTimeOrDefault((string)Request.Query.startDate); if (!startDate.HasValue) return HttpStatusCode.BadRequest; Dictionary isrcsMapping = await spotifyPlaylistManager.GetIsrcFromTrackIdAsync(trackIds); IEnumerable isrcs = isrcsMapping.Select(p => p.Value).ToArray(); if (!isrcs.Any()) { var emptyViewModels = trackIds.Select(trackId => new TrackPlaylistSummaryViewModel() { TrackId = trackId }).ToArray(); return Response.AsJson(emptyViewModels); } IEnumerable historicPlaylistTrackChange = Enumerable.Empty(); Dictionary totalPlaylistTrackChange = new Dictionary(); IEnumerable buzzCategories = Enumerable.Empty(); var tasks = new List { Task.Run(async () => { historicPlaylistTrackChange = await historicTrackListManager.GetHistoricPlaylistTrackChangeAsync(isrcs, startDate.Value); }), Task.Run(async () => { totalPlaylistTrackChange = await historicTrackListManager.GetTotalPlaylistTrackChangeAsync(isrcs, startDate.Value); }), Task.Run(() => { buzzCategories = _buzzAccountManager.GetBuzzCategories(); }) }; await TaskExtensions.EnsureAllSuccessAsync(tasks, TaskScheduler.FromCurrentSynchronizationContext()); IEnumerable viewModels = BuildTrackPlaylistChangeViewModels(trackIds, isrcsMapping, startDate.Value, historicPlaylistTrackChange, buzzCategories, totalPlaylistTrackChange); return Response.AsJson(viewModels); }; Get["PlaylistTrackPositionChange", "/spotifyPlaylist/trackPositionChange", true] = async (parameters, cx) => { var playlistIds = Request.GetRequiredPlaylistIds(); string trackUri = (string)Request.Query.trackUri; string isrc = (string)Request.Query.isrc; DateTime startDate = Maybe.ToDateTimeOrFallback((string)Request.Query.startDate, DateTime.Today.AddDays(-30)); DateTime endDate = Maybe.ToDateTimeOrFallback((string)Request.Query.endDate, DateTime.Today); if (trackUri != null) { var trackId = new SpotifyLink(trackUri).ExtractTrackID(); var isrcResponse = await spotifyPlaylistManager.GetIsrcFromTrackIdAsync(new List { trackId }); isrc = isrcResponse.GetValueOrDefault(trackId); } if (string.IsNullOrWhiteSpace(isrc)) return HttpStatusCode.BadRequest; var positions = await historicTrackListManager.GetHistoricTrackPositionChangeAsync(isrc, playlistIds, startDate, endDate); foreach(var trackPositionChange in positions) { //Position is stored 0-based, switch to 1-based. trackPositionChange.EndPosition += 1; trackPositionChange.StartPosition += 1; } return Response.AsJson(positions); }; Get["GetPlaylistsTracklistDistribution", "/spotifyPlaylist/tracklistDistribution", true] = async (parameters, cx) => { var playlistIds = Request.GetRequiredPlaylistIds(); var region = (string)Request.Query.region; var tracklistKpis = await spotifyPlaylistManager.GetTrackListKpiAsync(playlistIds, region); return Response.AsJson(tracklistKpis); }; } private string[] GetIsrcFromRequestAsync(Request request) { var ISRCs = (string)request.Query.isrc; if (!string.IsNullOrWhiteSpace(ISRCs)) { return ISRCs.Split(','); } return null; } private async Task> SetIsSonyAsync(PaginatedContent previousTracks, string region) { if (previousTracks.Items.Any()) { var tracks = previousTracks.Items.Select(t => t.Track).ToList(); var albumUpcs = tracks.Select(a => a.AlbumUpc).Where(u => !string.IsNullOrWhiteSpace(u)).Distinct().ToList(); var sonyUpcs = await _sonyMusicManager.GetSonyAlbumAsync(albumUpcs, MusicService.Spotify, region); var isrcs = tracks.Select(t => t.ISRC).FilterNull().Distinct().ToList(); var overrideIsSonyIsrcs = await _sonyMusicManager.GetIsSonyOverrideByIsrcAsync(isrcs, region); foreach (var track in previousTracks.Items) { track.Track.IsSony = DetermineIsSony(track.Track, sonyUpcs, overrideIsSonyIsrcs); } } return previousTracks; } private bool DetermineIsSony(TracklistItem track, Dictionary sonyUpcs, Dictionary overrideIsSonyIsrcs) { if (!string.IsNullOrWhiteSpace(track.ISRC) && overrideIsSonyIsrcs.ContainsKey(track.ISRC)) return overrideIsSonyIsrcs[track.ISRC]; return track.AlbumUpc != null && sonyUpcs.GetValueOrNull(track.AlbumUpc) == SpotifyAnalyticsAccount.Sony; } private IEnumerable BuildTrackPlaylistSummaryViewModels( IEnumerable trackIds, Dictionary isrcsMapping, IEnumerable trackIdSummaryIsrc, IEnumerable trackIdSummary, IEnumerable buzzCategories) { List trackPlaylistSummaries = new List(); foreach(var trackId in trackIds) { string isrc = isrcsMapping.GetValueOrDefault(trackId); IEnumerable currentIsrcSummary = trackIdSummaryIsrc.Where(p => p.Identifier == isrc).ToArray(); IEnumerable currentTrackIdSummary = trackIdSummary.Where(p => p.Identifier == trackId).ToArray(); trackPlaylistSummaries.Add(new TrackPlaylistSummaryViewModel() { TrackId = trackId, Isrc = isrc, TotalPlaylistCountByIsrc = currentIsrcSummary.Sum(p => p.PlaylistCount), TotalPlaylistCountByTrackId = currentTrackIdSummary.Sum(p => p.PlaylistCount), PlaylistCountByIsrc = BuildPlaylistCountViewModelAsync(currentIsrcSummary, buzzCategories), PlaylistCountByTrackId = BuildPlaylistCountViewModelAsync(currentTrackIdSummary, buzzCategories), TotalPlaylistSubscribersByIsrc = currentIsrcSummary.Sum(p => p.Followers), TotalPlaylistSubscribersByTrackId= currentTrackIdSummary.Sum(p => p.Followers), PlaylistSubscribersByIsrc = BuildPlaylistSubscriberViewModelAsync(currentIsrcSummary, buzzCategories), PlaylistSubscribersByTrackId = BuildPlaylistSubscriberViewModelAsync(currentTrackIdSummary, buzzCategories), }); } return trackPlaylistSummaries; } private IEnumerable BuildTrackPlaylistChangeViewModels( IEnumerable trackIds, Dictionary isrcsMapping, DateTime startDate, IEnumerable historicTrackChanges, IEnumerable buzzCategories, Dictionary totalPlaylistTrackChange) { List trackPlaylistSummaries = new List(); var buzzCategoriesDic = buzzCategories.ToDictionary(k => k.ID, v => v.Name); foreach (var trackId in trackIds) { var isrc = isrcsMapping.GetValueOrDefault(trackId); var currentIsrcSummary = historicTrackChanges.Where(p => p.ISRC == isrc).ToArray(); var total = totalPlaylistTrackChange.GetValueOrDefault(isrc); trackPlaylistSummaries.Add(new TrackPlaylistChangeViewModel() { TrackId = trackId, Isrc = isrc, TotalAdded = total, TotalRemoved = currentIsrcSummary.Sum(p => p.Removed), PlaylistChangesByIsrc = currentIsrcSummary.Select(p=> new TrackPlaylistOwnerAddedRemovedViewModel() { Added = p.Added, Removed = p.Removed, Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = GetBuzzCategoryName(buzzCategoriesDic, p)} }).ToArray(), StartDate = new TrackPlaylistChangeDateViewModel() { Date = startDate, PlaylistCountByIsrc = currentIsrcSummary.Select(p=> new TrackPlaylistOwnerCountViewModel() { Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = GetBuzzCategoryName(buzzCategoriesDic, p) }, PlaylistCount = p.PlaylistCountStart }), PlaylistSubscribersByIsrc = currentIsrcSummary.Select(p => new TrackPlaylistOwnerSubscribersViewModel() { Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = GetBuzzCategoryName(buzzCategoriesDic, p) }, Subscribers = p.FollowersStart }), TotalPlaylistCountByIsrc = currentIsrcSummary.Sum(p=> p.PlaylistCountStart), TotalPlaylistSubscribersByIsrc = currentIsrcSummary.Sum(p=> p.FollowersStart) }, EndDate = new TrackPlaylistChangeDateViewModel() { PlaylistCountByIsrc = currentIsrcSummary.Select(p => new TrackPlaylistOwnerCountViewModel() { Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = GetBuzzCategoryName(buzzCategoriesDic, p) }, PlaylistCount = p.PlaylistCountEnd }), PlaylistSubscribersByIsrc = currentIsrcSummary.Select(p => new TrackPlaylistOwnerSubscribersViewModel() { Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = GetBuzzCategoryName(buzzCategoriesDic, p) }, Subscribers = p.FollowersEnd }), TotalPlaylistCountByIsrc = currentIsrcSummary.Sum(p => p.PlaylistCountEnd), TotalPlaylistSubscribersByIsrc = currentIsrcSummary.Sum(p => p.FollowersEnd) } }); } return trackPlaylistSummaries; } private static string GetBuzzCategoryName(Dictionary buzzCategoriesDic, HistoricTrackChange p) { if (p.BuzzCategoryId.HasValue) return buzzCategoriesDic.GetValueOrDefault(p.BuzzCategoryId.Value); return null; } private IEnumerable BuildPlaylistCountViewModelAsync(IEnumerable currentIsrcSummary, IEnumerable buzzCategories) { return currentIsrcSummary.Select(p => new TrackPlaylistOwnerCountViewModel() { PlaylistCount = p.PlaylistCount, Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = buzzCategories.FirstOrDefault(c => c.ID == p.BuzzCategoryId)?.Name } }).ToArray(); } private IEnumerable BuildPlaylistSubscriberViewModelAsync(IEnumerable currentIsrcSummary, IEnumerable buzzCategories) { return currentIsrcSummary.Select(p => new TrackPlaylistOwnerSubscribersViewModel() { Subscribers = p.Followers, Owner = new BuzzCategoryViewModel() { CategoryId = p.BuzzCategoryId, CategoryName = buzzCategories.FirstOrDefault(c => c.ID == p.BuzzCategoryId)?.Name } }).ToArray(); } public class TrackPlaylistSummaryViewModel { public TrackPlaylistSummaryViewModel() { PlaylistCountByIsrc = new List(); PlaylistCountByTrackId = new List(); PlaylistSubscribersByIsrc = new List(); PlaylistSubscribersByTrackId = new List(); } public string TrackId { get; set; } public string Isrc { get; set; } public IEnumerable PlaylistCountByIsrc { get; set; } public IEnumerable PlaylistCountByTrackId { get; set; } public IEnumerable PlaylistSubscribersByIsrc { get; set; } public IEnumerable PlaylistSubscribersByTrackId { get; set; } public int TotalPlaylistSubscribersByIsrc { get; set; } public int TotalPlaylistSubscribersByTrackId{ get; set; } public int TotalPlaylistCountByTrackId { get; set; } public int TotalPlaylistCountByIsrc { get; set; } } public class TrackPlaylistChangeViewModel { public string TrackId { get; set; } public string Isrc { get; set; } public TrackPlaylistChangeDateViewModel StartDate { get; set; } public TrackPlaylistChangeDateViewModel EndDate { get; set; } public IEnumerable PlaylistChangesByIsrc { get; set; } public int TotalAdded { get; set; } public int TotalRemoved { get; set; } } public class TrackPlaylistChangeDateViewModel { public DateTime? Date { get; set; } public IEnumerable PlaylistCountByIsrc { get; set; } public IEnumerable PlaylistSubscribersByIsrc { get; set; } public int TotalPlaylistSubscribersByIsrc { get; set; } public int TotalPlaylistCountByIsrc { get; set; } } public class TrackPlaylistOwnerCountViewModel { public BuzzCategoryViewModel Owner { get; set; } public int PlaylistCount { get; set; } } public class TrackPlaylistOwnerSubscribersViewModel { public BuzzCategoryViewModel Owner { get; set; } public int Subscribers { get; set; } } public class TrackPlaylistOwnerAddedRemovedViewModel { public BuzzCategoryViewModel Owner { get; set; } public int Added { get; set; } public int Removed { get; set; } } public class BuzzCategoryViewModel { public int? CategoryId { get; set; } public string CategoryName { get; set; } } private async Task> BuildViewModelAsync(IEnumerable trackList, string region) { var simpleTrackList = trackList.Select(t => new TracklistViewModel() { Artists = t.Artists, Position = t.Position, TrackUri = SpotifyLink.FromTrackId(t.TrackId).Uri, TrackName = t.TrackName, ArtistName = string.Join(", ", t.Artists.Select(a=> a.Name)), ISRC = t.ISRC, AlbumUri = SpotifyLink.FromAlbumId(t.AlbumId).Uri, AlbumName = t.AlbumName, AlbumUpc = t.AlbumUpc, IsSony = t.IsSony, Popularity = t.Popularity, Added = t.Added, EarliestAdded = t.EarliestAdded, Danceability = t.Danceability, Energy = t.Energy, AudioKey = t.AudioKey, Loudness = t.Loudness, AudioMode = t.AudioMode, Speechiness = t.Speechiness, Acousticness = t.Acousticness, Instrumentalness = t.Instrumentalness, Liveness = t.Liveness, Valence = t.Valence, Tempo = t.Tempo, }).ToList(); if (simpleTrackList.Any()) { var trackIds = trackList.Select(t => t.TrackId).FilterNull().Distinct().ToList(); var albumsIds = trackList.Select(t => t.AlbumId).FilterNull().Distinct().ToList(); var albumsUpcs = trackList.Select(t => t.AlbumUpc).FilterNull().Distinct().ToList(); var sonyAlbumUpc = await _sonyMusicManager.GetSonyAlbumAsync(albumsUpcs, MusicService.Spotify, region); var tracks = await _spotifyWebApi.GetAllTracksAsync(trackIds); var albums = await _spotifyWebApi.GetAllAlbumInfoAsync(albumsIds, fields: ""); var trackDic = tracks.ToDictionary(k => k.id, v => v); var albumDic = albums.ToDictionary(k => k.id, v => v); foreach (var tracklistViewModel in simpleTrackList) { if (tracklistViewModel.AlbumUri != null) { var albumId = new SpotifyLink(tracklistViewModel.AlbumUri).ExtractAlbumID(); tracklistViewModel.AlbumReleaseDate = albumDic.GetValueOrDefault(albumId)?.release_date; tracklistViewModel.AlbumImageUrl = albumDic.GetValueOrDefault(albumId)?.images?.OrderBy(i => i.height).FirstOrDefault()?.url; } if (tracklistViewModel.TrackUri != null) { var trackId = new SpotifyLink(tracklistViewModel.TrackUri).ExtractTrackID(); tracklistViewModel.PreviewUrl = trackDic.GetValueOrDefault(trackId)?.preview_url; } var sonyRelease = sonyAlbumUpc.GetValueOrNull(tracklistViewModel.AlbumUpc); tracklistViewModel.IsSony = sonyRelease == SpotifyAnalyticsAccount.Sony; tracklistViewModel.SonyRelease = sonyRelease; } } return simpleTrackList; } public class TracklistViewModel { public int Position { get; set; } public string TrackUri { get; set; } public string TrackName { get; set; } public string ArtistName { get; set; } public string ISRC { get; set; } public string AlbumName { get; set; } public bool IsSony { get; set; } public int? Popularity { get; set; } public DateTime? Added { get; set; } public DateTime? EarliestAdded { get; set; } public string AlbumUri { get; set; } public string AlbumUpc { get; set; } public string AlbumImageUrl { get; set; } public string AlbumReleaseDate { get; set; } public string PreviewUrl { get; set; } public float? Danceability { get; set; } public float? Energy { get; set; } public int? AudioKey { get; set; } public float? Loudness { get; set; } public int? AudioMode { get; set; } public float? Speechiness { get; set; } public float? Acousticness { get; set; } public float? Instrumentalness { get; set; } public float? Liveness { get; set; } public float? Valence { get; set; } public float? Tempo { get; set; } public List Artists { get; set; } public SpotifyAnalyticsAccount? SonyRelease { get; set; } } [Serializable] public class HistoricTrackInPlaylistViewModel { //public string PlaylistUri { get; set; } public DateTime EarliestTrackDate { get; set; } public DateTime LatestTrackDate { get; set; } public string ISRC { get; set; } //public List Dates { get; set; } public SpotifyPlaylistViewModel Playlist { get; set; } //public TracklistItem TrackListItem { get; set; } } [Serializable] public class SpotifyPlaylistViewModel { public string Name { get; set; } public string Description { get; set; } public string ImageFileName { get; set; } //public string User { get; set; } public string CountryCode { get; set; } //public int Duration { get; set; } //public int TrackCount { get; set; } public long? Subscribers { get; set; } public int? BuzzCategoryId { get; set; } public string SpotifyLink { get; set; } //public bool Removed { get; set; } public string AccountId { get; set; } public string AccountName { get; set; } } } }