using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Nancy; using Sony.Filtr.AdminAPI.Config; using Sony.Filtr.AdminAPI.ViewModels; using Sony.Filtr.AdminAPI.ViewModels.PlaylistCuration; using Sony.Filtr.Buzz; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.Core.EditorialPlaylists; using Sony.Filtr.Core.Moodagent; using Sony.Filtr.Core.SonyMusic; using Sony.Filtr.Core.SpotifyBrowse; using Sony.Filtr.Core.SpotifyBrowse.Data; using Sony.Filtr.Playlists.Spotify; using Sony.Filtr.SpotifyWebAPI; using Sony.Filtr.SpotifyWebAPI.Model; using Sony.Filtr.Utility.Extensions; using Track = Sony.Filtr.SpotifyWebAPI.Model.Track; namespace Sony.Filtr.AdminAPI.Modules { public class PlaylistCurationModule : BaseModule { private readonly SpotifyWebApi _spotifyWebApi; private readonly EditorialPlaylistManager _editorialPlaylistManager; private readonly MoodagentApi _moodagentApi; private readonly EditorialPlaylistViewModelHelper _editorialPlaylistViewModelHelper; private readonly SonyMusicManager _sonyMusicManager; private readonly BuzzAccountManager _buzzAccountManager; private readonly SpotifyBrowseManager _spotifyBrowseManager; private readonly SpotifyPlaylistHistoricTrackListManager _spotifyPlaylistHistoricTrackListManager; public PlaylistCurationModule(IApplicationInstanceManager applicationInstanceManager, SpotifyWebApi spotifyWebApi, EditorialPlaylistManager editorialPlaylistManager, MoodagentApi moodagentApi, EditorialPlaylistViewModelHelper editorialPlaylistViewModelHelper, SonyMusicManager sonyMusicManager, BuzzAccountManager buzzAccountManager, SpotifyBrowseManager spotifyBrowseManager, SpotifyPlaylistHistoricTrackListManager spotifyPlaylistHistoricTrackListManager) : base(applicationInstanceManager) { _spotifyWebApi = spotifyWebApi; _editorialPlaylistManager = editorialPlaylistManager; _moodagentApi = moodagentApi; _editorialPlaylistViewModelHelper = editorialPlaylistViewModelHelper; _sonyMusicManager = sonyMusicManager; _buzzAccountManager = buzzAccountManager; _spotifyBrowseManager = spotifyBrowseManager; _spotifyPlaylistHistoricTrackListManager = spotifyPlaylistHistoricTrackListManager; Get["GetPlaylistCurationMoodagentPlaylistSuggestion", "/playlistCuration/Moodagent/PlaylistSuggestion", true] = async (parameters, cancellationToken) => { var trackUri = (string) Request.Query.trackUri; if (string.IsNullOrWhiteSpace(trackUri)) { return HttpStatusCode.BadRequest; } var limit = (int?) Request.Query.limit ?? 100; var offset = (int?) Request.Query.offset ?? 0; var region = (string) Request.Query.region; var streamsRegion = (string)Request.Query.streamsRegion; var trackLink = new SpotifyLink(trackUri); var viewModel = new MoodagentPlaylistRecommendationViewModel { Items = new List(), Pagination = new Pagination() { Limit = limit, Offset = offset, Total = 0 }, Track = await GetTrackViewModelAsync(trackLink, region), }; var playlistResponse = await _moodagentApi.GetPlaylistsRecommendationsFromTrackAsync(trackLink, limit, offset); if (playlistResponse?.Results != null && playlistResponse.Results.Any()) { viewModel.Items = await BuildPlaylistRecommendationViewModelAsync(playlistResponse, viewModel.Track.Isrc, streamsRegion ?? region); viewModel.Pagination.Total = playlistResponse.NumResults; } return Response.AsJson(viewModel); }; Get["GetPlaylistCurationMoodagentTrackSuggestion", "/playlistCuration/Moodagent/TrackSuggestion", true] = async (parameters, cancellationToken) => { var playlistUri = (string)Request.Query.playlistUri; if (string.IsNullOrWhiteSpace(playlistUri)) { return HttpStatusCode.BadRequest; } var playlistLink = new SpotifyLink(playlistUri); var limit = (int?)Request.Query.limit ?? 100; var offset = (int?)Request.Query.offset ?? 0; var region = (string) Request.Query.region; var playlistViewModelTask = GetPlaylistCurationPlaylistItemViewModelAsync(playlistLink); var viewModel = await GetTrackRecommendationViewModelAsync(playlistLink, limit, offset, region); viewModel.Playlist = await playlistViewModelTask; return Response.AsJson(viewModel); }; } private async Task> BuildPlaylistRecommendationViewModelAsync(PlaylistRecommendationResponse playlistResponse, string isrc, string region) { var playlistUris = playlistResponse.Results.Select(p => p.SpotifyURI).ToList(); var editorialPlaylistsTask = _editorialPlaylistManager.GetEditorialPlaylistsAsync(playlistUris); var playlistAttributesTask = _editorialPlaylistManager.GetPlaylistAttributesAsync(playlistUris); var browsePlaylistsTask = _spotifyBrowseManager.GetSpotifyBrowsePlaylistsSummaryAsync(DateTime.Today, playlistUris); var previousPlaylistUrisTask = _spotifyPlaylistHistoricTrackListManager.GetAllPreviousPlaylistUrisAsync(isrc, playlistUris); var allPlaylistDic = (await editorialPlaylistsTask).GroupBy(k => k.SpotifyLink).ToDictionary(k => k.Key, v => v.ToList()); var playlistAttributes = await playlistAttributesTask; var browsePlaylists = await browsePlaylistsTask; var browseLookup = browsePlaylists.GroupBy(k => k.PlaylistUri).ToDictionary(k => k.Key, v => v.ToList()); var joinedPlaylists = playlistResponse.Results.Join(allPlaylistDic, item => item.SpotifyURI, pair => pair.Key.Uri, (item, pair) => new { Playlist = pair, Moodagent = item, }).ToList(); var previousPlaylistUris = await previousPlaylistUrisTask; var buzzCategories = _buzzAccountManager.GetBuzzCategories().ToDictionary(k => k.ID, v => v.Name); var viewModels = joinedPlaylists.Select(p => new PlaylistCurationMoodagentAnalysis() { Playlist = BuildPlaylistViewModel(p.Playlist.Value, playlistAttributes.GetValueOrDefault(p.Playlist.Key.Uri), buzzCategories, previousPlaylistUris.Contains(p.Playlist.Key.Uri)), Browse = BuildBrowsePlaylistViewModel(browseLookup.GetValueOrDefault(p.Playlist.Key.Uri)), Moodagent = p.Moodagent }).ToList(); await _editorialPlaylistViewModelHelper.FillStreamsAsync(viewModels.Select(p=> p.Playlist).Cast().ToList(), region); return viewModels; } public class SpotifyBrowseViewModel { public string Region { get; set; } public string CategoryName { get; set; } public string CategoryId { get; set; } public int Position { get; set; } public int PeakPosition { get; set; } } private List BuildBrowsePlaylistViewModel(List browseCategoryStatItems) { return browseCategoryStatItems?.Select(b => new SpotifyBrowseViewModel() { CategoryId = b.CategoryId, CategoryName = b.CategoryName, Region = b.Market, Position = b.Position, PeakPosition = b.PeakPosition }).ToList(); } private async Task GetTrackViewModelAsync(SpotifyLink trackLink, string region) { var trackIds = new List() { trackLink.ExtractTrackID() }; var track = (await _spotifyWebApi.GetAllTracksAsync(trackIds)).FirstOrDefault(); if (track == null) { throw new NancyStatusException(HttpStatusCode.BadRequest, "Could not find track"); } var fullAlbum = (await _spotifyWebApi.GetAllAlbumInfoAsync(track.album.id.AsEnumerable())).FirstOrDefault(); //We need upc from full album var trackViewModel = BuildTrackItemViewModel(track, region, fullAlbum); return trackViewModel; } private async Task GetTrackRecommendationViewModelAsync(SpotifyLink playlistLink, int limit, int offset, string region) { var trackRecommendationResponse = await _moodagentApi.GetTrackRecommendationsFromPlaylistAsync(playlistLink, limit, offset); if (trackRecommendationResponse?.Results == null || !trackRecommendationResponse.Results.Any()) { return new MoodagentTrackRecommendationViewModel() { Items = new List(), Pagination = new Pagination() { Limit = limit, Offset = offset, Total = 0 }, }; } var trackIds = trackRecommendationResponse.Results.Select(t => t.TrackID).Distinct().ToList(); var allTracks = (await _spotifyWebApi.GetAllTracksAsync(trackIds, region)).ToList(); allTracks = allTracks.FilterNull().ToList(); var albumsIds = allTracks.Select(t => t.album.id).Distinct(); var albums = await _spotifyWebApi.GetAllAlbumInfoAsync(albumsIds, market: region); var albumDic = albums.ToDictionary(k => k.id, v => v); var albumUpcsMapping = albums.Select(a => new { upc = a.external_ids.upc, albumId = a.id}); var albumUpcs = albumUpcsMapping.Select(u=> u.upc).FilterNull().Distinct().ToList(); var sonyUpcs = await _sonyMusicManager.IsSonyAlbumAsync(albumUpcs, null, region); var viewModels = trackRecommendationResponse.Results.Join(allTracks, item => item.TrackID, track => track.linked_from?.id ?? track.id, (item, track) => new PlaylistCurationTrackMoodagentAnalysis { Moodagent = item, Track = BuildTrackItemViewModel(track, region, albumDic?.GetValueOrDefault(track?.album.id)) }).ToList(); foreach(var viewModel in viewModels) { if (viewModel.Track.AlbumUpc != null) { viewModel.Track.IsSony = sonyUpcs.Contains(viewModel.Track.AlbumUpc); } } var paginatedContent = new MoodagentTrackRecommendationViewModel() { Items = viewModels.ToList(), Pagination = new Pagination() { Limit = limit, Offset = offset, Total = trackRecommendationResponse.NumResults }, }; return paginatedContent; } private static PlaylistCurationTrackItemViewModel BuildTrackItemViewModel(Track track, string region, FullAlbumItem fullAlbum) { return new PlaylistCurationTrackItemViewModel() { Name = track.name, Artists = track.artists.Select(ta => ta.name).ToList(), AlbumImageUrl = track.album?.images?.OrderByDescending(i => i?.width).FirstOrDefault()?.url, Popularity = track.popularity, SpotifyUri = track.uri, Isrc = track.external_ids?.isrc, Regions = GetAvailableRegions(region, track), PreviewUrl = track.preview_url, Duration = track.duration_ms / 1000, Explicit = track.@explicit, Copyright = fullAlbum?.copyrights, AlbumName = track.album?.name, AlbumUri = track.album?.uri, AlbumUpc = fullAlbum?.external_ids?.upc, }; } private static List GetAvailableRegions(string region, Track track) { if (string.IsNullOrWhiteSpace(region)) return track.available_markets; if (track.is_playable ?? false) return new List() { region.ToUpperInvariant() }; return new List(); } private async Task GetPlaylistCurationPlaylistItemViewModelAsync(SpotifyLink playlistLink) { var playlistUris = new List {playlistLink.Uri}; var requestPlaylist = (await _editorialPlaylistManager.GetEditorialPlaylistsAsync(playlistUris)).FirstOrDefault(); var requestPlaylistAttributes = await _editorialPlaylistManager.GetPlaylistAttributesAsync(playlistUris); //var browseItem = await _spotifyBrowseManager.GetSpotifyBrowsePlaylistsAsync(DateTime.Today, playlistUris); var buzzCategories = _buzzAccountManager.GetBuzzCategories(); var buzzCategoriesDic = buzzCategories.ToDictionary(k => k.ID, v => v.Name); var singlePlaylistList = new List { requestPlaylist }; var playlistViewModel = BuildPlaylistViewModel(singlePlaylistList, requestPlaylistAttributes.Values.FirstOrDefault(), buzzCategoriesDic); return playlistViewModel; } private PlaylistCurationPlaylistItemViewModel BuildPlaylistViewModel(List playlists, EditorialPlaylistAttributes attributes, Dictionary buzzCategories, bool? trackPreviouslyAdded = null) { var playlist = playlists.First(); var buzzCategoryName = (playlist.BuzzCategoryId.HasValue && buzzCategories.ContainsKey(playlist.BuzzCategoryId.Value)) ? buzzCategories[playlist.BuzzCategoryId.Value] : null; return new PlaylistCurationPlaylistItemViewModel() { Id = playlist.ID, Name = playlist.Name, CountryCode = playlist.CountryCode, Markets = playlists.Select(p=> p.ApplicationID).Distinct(), BuzzCategoryId = playlist.BuzzCategoryId, BuzzCategoryName = buzzCategoryName, Followers = attributes?.Followers, SpotifyImageFileName = playlist.SpotifyImageFileName, SpotifyLink = playlist.SpotifyLinkUri, //TrackLatestAdded = historicPlaylistsTrackSummary?.LatestTrackDate TrackPreviouslyAdded = trackPreviouslyAdded }; } } }