using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using MoreLinq; using NLog; using Sony.Filtr.Applications; using Sony.Filtr.API.ViewModels; using Sony.Filtr.API.ViewModels.v3; using Sony.Filtr.Buzz; using Sony.Filtr.ContentManagement; using Sony.Filtr.Contracts.Definitions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.Contracts.Entities.Buzz; using Sony.Filtr.Contracts.Entities.Genre; using Sony.Filtr.Core; using Sony.Filtr.Core.Buzz; using Sony.Filtr.Core.EditorialPlaylists; using Sony.Filtr.Core.Genres; using Sony.Filtr.Core.ImageCarousel; using Sony.Filtr.Core.LastModified; using Sony.Filtr.Core.PromotedTracks; using Sony.Filtr.Core.Releases; using Sony.Filtr.Core.Seasons; using Sony.Filtr.Core.Settings; using Sony.Filtr.Core.SpotifyRegion; using Sony.Filtr.Core.SpotifyUserCountries; using Sony.Filtr.Core.TopLists; using Sony.Filtr.DistributedCaching; using Sony.Filtr.Globalization; using Sony.Filtr.Globalization.v3; using Sony.Filtr.PlaylistGeneration; using Sony.Filtr.PlaylistGeneration.Autocomplete; using Sony.Filtr.Search; using Sony.Filtr.Utility; using Sony.Filtr.Utility.Extensions; using PlaylistViewModel = Sony.Filtr.API.ViewModels.v3.PlaylistViewModel; using ReleaseViewModel = Sony.Filtr.API.ViewModels.v3.ReleaseViewModel; namespace Sony.Filtr.API.Service { public class SpotifyService3 : BaseService3, IService3 { private readonly SeasonManager _seasonManager; private readonly ApplicationInstanceManager _applicationInstanceManager; private readonly EditorialPlaylistManager _editorialPlaylistManager; private readonly PlaylistCategoryManager _playlistCategoryManager; private readonly PlaylistGenerationManager _playlistGenerationManager; private readonly ImageCarouselManager _imageCarouselManager; private readonly ReleasesManager _releasesManager; private readonly DistributedCacheHandler _distributedCacheHandler; private readonly SearchManager _searchManager; private readonly PromotedTracksManager _promotedTracksManager; private readonly GenreManager _genreManager; private readonly SpotifyUserCountryManager _spotifyUserCountryManager; private readonly BuzzAccountManager _buzzAccountManager; private readonly SpotifyRegionManager _spotifyRegionManager; private readonly GlobalizationHandler _globalization; private readonly Logger _logger; private GlobalizationHandler2 GlobalizationHandler2 { get; set; } private TrackSuggestionManager2 TrackSuggestionManager2 { get; set; } private EditorialPlaylistStatsManager EditorialPlaylistStatsManager { get; set; } private LastModifiedManager LastModifiedManager { get; set; } public SpotifyService3(GlobalizationHandler2 globalizationHandler, TrackSuggestionManager2 trackSuggestionManager, EditorialPlaylistStatsManager editorialPlaylistStatsManager, LastModifiedManager lastModifiedManager, SeasonManager seasonManager, ApplicationInstanceManager applicationInstanceManager, EditorialPlaylistManager editorialPlaylistManager, PlaylistCategoryManager playlistCategoryManager, PlaylistGenerationManager playlistGenerationManager, MarketGlobalizationHandler marketGlobalizationHandler, ImageCarouselManager imageCarouselManager, AutocompleteManager autocompleteManager, ReleasesManager releasesManager, DistributedCacheHandler distributedCacheHandler, TopListManager topListManager, ContentManager contentManager, SearchManager searchManager, PromotedTracksManager promotedTracksManager, GenreManager genreManager, SpotifyUserCountryManager spotifyUserCountryManager, BuzzAccountManager buzzAccountManager, SpotifyRegionManager spotifyRegionManager, GlobalizationHandler globalization, SettingsManager settingsManager) : base(applicationInstanceManager, marketGlobalizationHandler, lastModifiedManager, playlistGenerationManager, trackSuggestionManager, autocompleteManager, distributedCacheHandler, topListManager, contentManager, searchManager, genreManager, settingsManager) { _seasonManager = seasonManager; _applicationInstanceManager = applicationInstanceManager; _editorialPlaylistManager = editorialPlaylistManager; _playlistCategoryManager = playlistCategoryManager; _playlistGenerationManager = playlistGenerationManager; _imageCarouselManager = imageCarouselManager; _releasesManager = releasesManager; _distributedCacheHandler = distributedCacheHandler; _searchManager = searchManager; _promotedTracksManager = promotedTracksManager; _genreManager = genreManager; _spotifyUserCountryManager = spotifyUserCountryManager; _buzzAccountManager = buzzAccountManager; _spotifyRegionManager = spotifyRegionManager; _globalization = globalization; GlobalizationHandler2 = globalizationHandler; TrackSuggestionManager2 = trackSuggestionManager; EditorialPlaylistStatsManager = editorialPlaylistStatsManager; LastModifiedManager = lastModifiedManager; _logger = LogManager.GetLogger("TrackSuggestionManager2"); } public SeasonViewModel GetSeason(Application app) { var season = _seasonManager.GetActiveSeason(app); if (season == null) return null; var themeViewModel = new ThemeViewModel() { Active = season.ThemeActive, //HeaderBackgroundColor = season.HeaderBackgroundColor, BackgroundColor = season.BackgroundColor, TextColor = season.TextColor, AccentColor = season.AccentColor, AccentColorPlaylist = season.AccentColorPlaylist, AccentColorPlaylists = season.AccentColorPlaylists, AccentColorReleases = season.AccentColorReleases, //HeaderBackgroundImage = season.HeaderBackgroundImage, HeaderPlaylistBackgroundImage = BuildUrl(ApiConfig.EditorialImageBaseUrl, season.HeaderPlaylistBackgroundImage), HeaderPlaylistsBackgroundImage = BuildUrl(ApiConfig.EditorialImageBaseUrl, season.HeaderPlaylistsBackgroundImage), HeaderReleasesBackgroundImage = BuildUrl(ApiConfig.EditorialImageBaseUrl, season.HeaderReleasesBackgroundImage), HeaderStartBackgroundImage = BuildUrl(ApiConfig.EditorialImageBaseUrl, season.HeaderStartBackgroundImage), }; return new SeasonViewModel() { SpotifyUri = season.SpotifyUri, Label = season.Label, Description = season.Description, Theme = themeViewModel, }; } private string BuildUrl(string baseUrl, string filename) { if (string.IsNullOrWhiteSpace(filename)) return null; return baseUrl + "/" + filename; } public async Task> GetNewReleasesAsync(Application app, string countryCode) { var viewModels = new ConcurrentBag(); var allReleases = _releasesManager.GetReleases(app, countryCode).ToList(); List releases = new List(); if (allReleases.Count() > 100) { releases.AddRange(allReleases.OrderBy(i=> i.Index).Where(t=> t.ReleaseType == ReleaseType.Single).Take(50)); releases.AddRange(allReleases.OrderBy(i => i.Index).Where(t => t.ReleaseType == ReleaseType.Album).Take(50)); } else { releases = allReleases; } var genreCategories = _genreManager.GetGenreCategories().ToDictionary(g => g.ID, g => g.Name); var artists = releases.Select(r => r.ArtistName).Distinct().Select(a=> new Artist(a)).ToList(); var allArtistTags = await _playlistGenerationManager.GetArtistTagsAsync(app, artists); await releases.ForEachAsync(5, async release => { IEnumerable> artistTags; if (!allArtistTags.TryGetValue(release.ArtistName, out artistTags)) { artistTags = new List>(); } var tagViewModels = artistTags.Select(t => new TagViewModel(t, genreCategories)).ToList(); var playlistViewModel = ReleaseViewModel.Build(release, release.Index, tagViewModels, ApiConfig.EditorialImageBaseUrl); viewModels.Add(playlistViewModel); }); return viewModels.OrderBy(r=> r.ReleaseType).ThenBy(r => r.Index).ToList(); } public async Task GetNewReleasesLastModifiedDateAsync(Application application) { return await LastModifiedManager.GetLastModifyDateAsync(application, EntityType.SpotifyReleases); } public async Task> GetPlaylistsAsync(Application app, DateTime? lastModified = null) { if (!lastModified.HasValue) lastModified = await GetPlaylistLastModifiedDateAsync(app); var appKeyPart = (app != null) ? app.ID.ToString() : "null"; var cacheKey = "PlaylistViewModels-Cache-2.1:" + typeof(PlaylistsCacheResponse).Name + ":" + PlaylistsCacheResponse.Version + ":" + PlaylistViewModel.Version + ":" + appKeyPart; var playlistCacheResponse = _distributedCacheHandler.Get(cacheKey) as PlaylistsCacheResponse; if (playlistCacheResponse != null) { if (playlistCacheResponse.GenerateDate >= lastModified) { //The data we have was generated after data changed, just return it return playlistCacheResponse.ViewModels; } else if (playlistCacheResponse.GenerateDate > DateTime.UtcNow.AddDays(-2)) { //The data we have was generated before data changed but is not to old. Return data and trigger new update in background ThreadPool.QueueUserWorkItem(async pl => { //TODO: Might be a good idea to lock/synchronize so we don't start multiple updates. try { _logger.Debug("Updating playlist viewmodels in background for market {0}. LastModified: {1}", appKeyPart, lastModified); var viewModelsBackground = await GetPlaylistsInternalAsync(app); var generateDate = DateTime.UtcNow; _distributedCacheHandler.Put(cacheKey, new PlaylistsCacheResponse() { ViewModels = viewModelsBackground, GenerateDate = generateDate, LastModified = lastModified.Value, }); //_logger.Debug("LastGenerationDate was {0}", GetPlaylistViewModelGenerationLastModified(app)); SetPlaylistViewModelGenerationLastModified(app, generateDate); _logger.Debug("Done updating playlist viewmodels in background. LastModified: {0}. GenerationDate: {1}", lastModified, generateDate); //_logger.Debug("LastGenerationDate is {0}", GetPlaylistViewModelGenerationLastModified(app)); //_logger.Debug("LastModifyDate is {0}", await GetPlaylistLastModifiedDateAsync(app)); } catch (Exception ex) { ErrorLogging.ErrorLoggingManager.Instance.LogError(ex); } }); return playlistCacheResponse.ViewModels; } //Cached data is to old. Generate new directly in response. } _logger.Debug("Updating playlist viewmodels in foreground for market {0}. LastModified: {1}", appKeyPart, lastModified); var viewModels = await GetPlaylistsInternalAsync(app); _distributedCacheHandler.Put(cacheKey, new PlaylistsCacheResponse() { ViewModels = viewModels, GenerateDate = DateTime.UtcNow, LastModified = lastModified.Value, }); return viewModels; } private async Task> GetPlaylistsInternalAsync(Application app) { var globalMarket = _applicationInstanceManager.GetGlobalPushApplication(); var fallbackMarket = _applicationInstanceManager.GetFallbackApplication(); var allPlaylists = await GetPlaylistsToIncludeAsync(app, globalMarket, fallbackMarket); var playlistAttributes = await _editorialPlaylistManager.GetPlaylistAttributesAsync(allPlaylists); var viewModels = await BuildPlaylistViewModelsAsync(app, playlistAttributes); return viewModels; } private async Task> GetPlaylistsToIncludeAsync(Application app, Application globalMarket, Application fallbackMarket) { var globalPlaylists = await _editorialPlaylistManager.GetLightweightEditorialPlaylistsAsync(globalMarket, onlyActive: true); var playlists = await _editorialPlaylistManager.GetLightweightEditorialPlaylistsAsync(app, onlyActive: true); var allPlaylists = playlists.Union(globalPlaylists); if (app.IncludeOtherPlaylists) { var fallbackPlaylists = await _editorialPlaylistManager.GetLightweightEditorialPlaylistsAsync(fallbackMarket, onlyActive: true); allPlaylists = allPlaylists.Union(fallbackPlaylists); } return allPlaylists.Where(p => p.SpotifyLink != null && p.PriorityLevel != 0).DistinctBy(playlist => playlist.SpotifyLink).ToList(); } private string CalculateHash(List viewModels) { using (HashAlgorithm crypto = MD5.Create()) { using (MemoryStream memStream = new MemoryStream()) { DataContractSerializer x = new DataContractSerializer(typeof(List)); x.WriteObject(memStream, viewModels); memStream.Position = 0; return Convert.ToBase64String(crypto.ComputeHash(memStream)); } } } private async Task> BuildPlaylistViewModelsAsync(Application app, List playlists) { var editorialPlaylistsLocal = playlists.Select(p=> p.EditorialPlaylist).ToList(); var playlistStats = await EditorialPlaylistStatsManager.GetPlaylistStatsAsync(editorialPlaylistsLocal); var genreCategories = _genreManager.GetGenreCategories().ToDictionary(g => g.ID, g => g.Name); var currentGenres = GetGlobalAndLocalGenres(app).ToDictionary(k => k.Name, v => v); var allPlaylistTags = await _editorialPlaylistManager.GetTagsAsync(editorialPlaylistsLocal); var allPlaylistArtists = await _editorialPlaylistManager.GetArtistsAsync(editorialPlaylistsLocal); var allPlaylistCategories = await _playlistCategoryManager.GetCategoriesAsync(editorialPlaylistsLocal); ConcurrentBag playlistViewModels = new ConcurrentBag(); var popularity = CalculatePopularity(playlists); foreach (var playlist in playlists) { var id = playlist.EditorialPlaylist.ID; var uri = playlist.EditorialPlaylist.SpotifyLinkUri; List playlistTags = new List(); if (allPlaylistTags.ContainsKey(id)) { playlistTags = allPlaylistTags[id]; playlistTags = playlistTags.Where(t => t.Score > 1).OrderByDescending(p => p.Score).Take(5).ToList(); } List playlistArtists = new List(); if (allPlaylistArtists.ContainsKey(uri)) { playlistArtists = allPlaylistArtists[uri]; } var rankedRealTags = playlistTags.Join(currentGenres, match => match.Genre.ToLower(), pair => pair.Key.ToLower(), (match, pair) => new RankedItem(pair.Value, match.Score)); //var currentRealGenres = playlist.Genres.Where(g => currentGenres.ContainsKey(g.Genre)).Select(g => currentGenres[g.Genre]); var tags = rankedRealTags.Select(t => new TagViewModel(t, genreCategories)).OrderByDescending(t => t.Match).ThenBy(t => t.Label).ToList(); List playlistCategories; if (!allPlaylistCategories.TryGetValue(id, out playlistCategories)) playlistCategories = new List(); var categoryNames = playlistCategories.Select(c => c.ID).ToList();// string.Format("category-{0}", TextUtility.GenerateSlug(c.Name))).ToList(); int? popularityInt = null; if (popularity.ContainsKey(id)) popularityInt = popularity[id]; var playlistStat = playlistStats.FirstOrDefault(pi => pi.Playlist.ID == id); if (playlistStat != null) { playlistViewModels.Add(PlaylistViewModel.BuildViewModel(playlistStat, tags, playlistArtists, categoryNames, ApiConfig.EditorialImageBaseUrl, playlist.Attributes, popularityInt)); } } return playlistViewModels.OrderBy(p => p.Match).ThenBy(p=> p.Id).ToList(); } private Dictionary CalculatePopularity(IEnumerable editorialPlaylists) { var playlistStats = editorialPlaylists.Where(p=> p.Attributes != null).Select(p => new { Playlist = p.EditorialPlaylist, Attribute = p.Attributes, }).Select(p => new { Playlist = p.Playlist, Followers = p.Attribute.Followers, FollowerChange7 = p.Attribute.FollowerChange7, FollowerChange30 = p.Attribute.FollowerChange30, FollowerChangePercent = 1.0*p.Attribute.FollowerChange30/p.Attribute.Followers, }).ToList(); var popularity = playlistStats.Select(p => new { Playlist = p.Playlist, FollowerRank = playlistStats.OrderByDescending(pi => pi.Followers) .ItemIndex() .FirstOrDefault(pii => pii.Item.Playlist.ID == p.Playlist.ID) .Index, Follower7Rank = playlistStats.OrderByDescending(pi => pi.FollowerChange7) .ItemIndex() .FirstOrDefault(pii => pii.Item.Playlist.ID == p.Playlist.ID) .Index, FollowerChangeRank = playlistStats.OrderByDescending(pi => pi.FollowerChangePercent) .ItemIndex() .FirstOrDefault(pii => pii.Item.Playlist.ID == p.Playlist.ID) .Index, }).Select(p => new { Playlist = p.Playlist, Popularity = (p.FollowerRank*2.2) + (p.Follower7Rank*0.4) + (p.FollowerChangeRank*0.7), }).ToList(); return popularity.ToDictionary(k => k.Playlist.ID, v => (int)(100-Scale(v.Popularity, popularity.Min(p => p.Popularity), popularity.Max(p => p.Popularity), 0.0, 100.0))); } private double Scale(double valueIn, double baseMin, double baseMax, double limitMin, double limitMax) { return ((limitMax - limitMin)*(valueIn - baseMin)/(baseMax - baseMin)) + limitMin; } public List GetCategories(Application app) { var categories = _playlistCategoryManager.GetCategories(app); var icons = _playlistCategoryManager.GetIcons().ToDictionary(c=> c.ID); return categories.Where(c=> c.Active).Select(c => new PlaylistCategoryViewModel(c, icons)).ToList(); } public async Task> GetPlaylistTracksAsync(string playlistId) { int playlistIntId; if (int.TryParse(playlistId, out playlistIntId)) { var playlist = await _editorialPlaylistManager.GetEditorialPlaylistAsync(playlistIntId); if (playlist == null) return null; return playlist.Tracks.Select(t => new PlaylistTrackViewModel() { Title = t.Name, ArtistName = t.ArtistName, TrackDuration = t.Duration, Uri = t.SpotifyLink.Uri, TrackId = t.SpotifyLink.ExtractTrackID(), }).ToList(); } return new List(); } public async Task> GetPromotedTracksAsync(Application app) { var promotedTracks = await _promotedTracksManager.GetPromotedTracksAsync(app); promotedTracks = promotedTracks.Where(p=> p.ServiceType == ServiceType.Spotify).ToList(); promotedTracks = promotedTracks.Where(HasMatchData).ToList(); //Filter in case something is missing. var genreCategories = _genreManager.GetGenreCategories().ToDictionary(g => g.ID, g => g.Name); var trackViewModels = new ConcurrentBag(); Parallel.ForEach(promotedTracks.ItemIndex(), promotedTrack => { var localTags = _genreManager.MatchTags(app, promotedTrack.Item.Tags); var tags = localTags.Select(t => new TagViewModel(t, genreCategories)); var trackSuggestion = new ExtendedTrackSuggestion() { ExtendedTrack = promotedTrack.Item.ExtendedTrack, Score = promotedTrack.Index }; var viewModel = TrackViewModel.BuildTrackViewModel(trackSuggestion, tags, null); trackViewModels.Add(viewModel); }); return trackViewModels.OrderBy(t => t.Match).ToList(); } private bool HasMatchData(PromotedTrack promotedTrack) { return (promotedTrack.Tags != null); } public MyJsonDictionary> GetGlobalization(Application app) { var translations = _globalization.GetTranslations(app); return GroupTranslations(translations); } private static MyJsonDictionary> GroupTranslations(Dictionary translations) { var groupedTranslations = new MyJsonDictionary>(); foreach (var translation in translations) { //TODO: would be nice to group in several layers. var keyGroup = translation.Key.Split('.').First(); var key = translation.Key.Split('.').Last(); if (groupedTranslations.ContainsKey(keyGroup)) { groupedTranslations[keyGroup].Add(key, translation.Value); } else { var inner = new MyJsonDictionary(); inner[key] = translation.Value; groupedTranslations[keyGroup] = inner; } } return groupedTranslations; } public Dictionary GetGlobalizationForLanguage(string language) { if (string.IsNullOrEmpty(language)) return null; var cultureInfo = CultureInfo.GetCultureInfo(language); var translations = GlobalizationHandler2.GetTranslations(cultureInfo); var fallbackTranslations = GlobalizationHandler2.GetTranslations(FallbackLanguage); var keys = GlobalizationHandler2.GetKeys(); return keys.ToDictionary(k => k.Key, v => GetGlobalizationOrFallback(v.Id, translations, fallbackTranslations)); } private string GetGlobalizationOrFallback(int keyId, List translations, List fallbackTranslations) { var trans = translations.FirstOrDefault(t => t.GlobalizationKey.Id == keyId); if (trans != null) return trans.Value; trans = fallbackTranslations.FirstOrDefault(t => t.GlobalizationKey.Id == keyId); if (trans != null) return trans.Value; return string.Empty; } public ServiceType ServiceType { get { return ServiceType.Spotify; } } public BootstrapViewModel GetBootstrapNew(string countryCode) { var applicationsInCountry = _applicationInstanceManager.GetRegularApplicationByCountrySpecialFallback(countryCode); //TODO: Only include applications which are active for service? //TODO: Which languages to use? //var languageViewModels = GetOrderedLanguageViewModels(app); var bootstrap = new BootstrapViewModel { //Languages = languageViewModels, Markets = new List() { applicationsInCountry.ID }, BaseUrls = new BaseUrlsViewModel() { CDNBaseUrl = ApiConfig.CDNBaseUrl, EditorialImageBaseUrl = ApiConfig.EditorialImageBaseUrl, ProxyBaseUrl = ApiConfig.ProxyBaseUrl, }, DefaultFilteringValues = GetDefaultFilteringValues(), }; return bootstrap; } public BootstrapViewModel GetWorkoutBootstrap(Application application) { var bootstrap = new BootstrapViewModel { Markets = new List() { application.ID}, BaseUrls = new BaseUrlsViewModel() { CDNBaseUrl = ApiConfig.CDNBaseUrl, EditorialImageBaseUrl = ApiConfig.EditorialImageBaseUrl, ProxyBaseUrl = ApiConfig.ProxyBaseUrl, }, DefaultFilteringValues = GetDefaultFilteringValues(), }; return bootstrap; } private List GetOrderedLanguageViewModels(Application app) { IEnumerable languages = GlobalizationHandler2.GetTranslatedLanguages(); if (languages.Contains(app.CultureInfo)) languages = languages.OrderByDescending(l => l.Equals(app.CultureInfo)).ThenBy(l => l.NativeName); else languages = languages.OrderByDescending(l => l.Equals(FallbackLanguage)).ThenBy(l => l.NativeName); var languageViewModels = languages.Select(l => new LanguageViewModel(l)).ToList(); return languageViewModels; } public async Task> GetTracks(Application app, string countryCode, string tag, string artist) { return await base.GetTracks(app, countryCode, tag, artist, ServiceType.Spotify); } public async Task> SearchTracksAsync(Application app, string countryCode, SearchTracksParameters searchTracksParameters) { if (searchTracksParameters.Artists != null && searchTracksParameters.Artists.Any()) { var allArtistTracks = new List(); foreach (var artist in searchTracksParameters.Artists) { var artistArtist = await _playlistGenerationManager.GetArtistAsync(artist); if (artistArtist == null) continue; List tracks = await TrackSuggestionManager2.GetSuggestedTracksWithLocalFilteringAsync(app, countryCode, artistArtist.Name, searchTracksParameters); var artists = tracks.Select(t => t.Artist).DistinctBy(a => a.Name, StringComparer.InvariantCultureIgnoreCase).ToList(); var artistsTags = await _playlistGenerationManager.GetArtistTagsAsync(app, artists); var genreCategories = _genreManager.GetGenreCategories().ToDictionary(g => g.ID, g => g.Name); var artistTagViewModel = artistsTags.ToDictionary(k => k.Key, v => v.Value.Select(t => new TagViewModel(t, genreCategories)).ToList()); //var artistMoods = artists.AsParallel().ToDictionary(k => k.Name, v => PlaylistGenerationManager.Instance.GetMood(v).Result); List viewModels = new List(); foreach (var track in tracks) { List artistTags; if (!artistTagViewModel.TryGetValue(track.Artist.Name, out artistTags)) { artistTags = new List(); } var viewModel = TrackViewModel.BuildTrackViewModel(track, artistTags, relatedTo: artist); viewModels.Add(viewModel); } allArtistTracks.AddRange(viewModels); } return allArtistTracks.OrderByDescending(t=> t.Match).ThenByDescending(t=> t.ArtistName).ThenByDescending(t=> t.Title).ToList(); } else if (searchTracksParameters.Tag != null) { var tag = searchTracksParameters.Tag; var genre = GetGlobalAndLocalGenre(app, tag); if (genre == null) return null; var allTagCategories = _genreManager.GetGenreCategories(); var tagCategories = allTagCategories.ToDictionary(g => g.ID, g => g.Name); var tracks = await TrackSuggestionManager2.GetSuggestedTracksForGenreNewAsync(genre, countryCode, searchTracksParameters); //var tracks = await TrackSuggestionManager2.GetSuggestedTracksForGenreAsync(genre, countryCode, searchTracksParameters); var artists = tracks.Select(t => t.Artist).DistinctBy(a => a.Name).ToList(); var artistsTags = await _playlistGenerationManager.GetArtistTagsAsync(app, artists); var artistTagViewModel = artistsTags.ToDictionary(k => k.Key, v => v.Value.Select(t => new TagViewModel(t, tagCategories)).ToList()); //var artistMoods = tracks.Select(t => t.Artist).DistinctBy(a => a.Name).ToDictionary(k => k.Name, v => PlaylistGenerationManager.Instance.GetMood(v).Result); List viewModels = new List(); foreach (var track in tracks) { List artistTags; if (!artistTagViewModel.TryGetValue(track.Artist.Name, out artistTags)) { artistTags = new List(); } var viewModel = TrackViewModel.BuildTrackViewModel(track, artistTags); viewModels.Add(viewModel); } return viewModels; } return null; } public async Task GetEditorialPlaylistAsync(int id) { return await _editorialPlaylistManager.GetEditorialPlaylistAsync(id); } public List GetCarouselImages(Application application) { var images = _imageCarouselManager.GetImages(application); var imagesToUse = images.Where(i => i.Active && i.ServiceType == ServiceType.Spotify).ToList(); return imagesToUse.Select(CarouselImageViewModel.BuildViewModel).ToList(); } public async Task GetPlaylistAsync(string playlistId) { int id; if (!int.TryParse(playlistId, out id)) return null; var playlist = await _editorialPlaylistManager.GetEditorialPlaylistAsync(id); if (playlist == null) return null; var allPlaylistsInMarket = await GetPlaylistsAsync(new Application() { ID = playlist.ApplicationID }); return allPlaylistsInMarket.FirstOrDefault(p => p.Id == id); } public async Task SearchAsync(Application application, string searchString) { var result = await _searchManager.SearchStuffAsync(application, ServiceType.Spotify, searchString); return await BuildSearchResultViewModelAsync(application, result); } public async Task GetFiltrUsersLastModifiedAsync() { return await LastModifiedManager.GetLastModifyDateAsync(null, EntityType.FiltrUsers); } public async Task> GetFiltrUsersAsync() { var sonyBuzzUsers = (await _buzzAccountManager.GetBuzzUsersAsync((int)StaticBuzzCategory.SonyMusic, (int)MusicService.Spotify)).DistinctBy(u=> u.Username).Select(u=> new FiltrUserCountryViewModel() { Username = u.Username, CountryCode = u.CountryCode }); var mappedUsers = _spotifyUserCountryManager.GetSpotifyUsersCountry() .Where(k => !sonyBuzzUsers.Any(s => s.Username.Equals(k.SpotifyUserName, StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrWhiteSpace(s.CountryCode))) .Select(u => new FiltrUserCountryViewModel() {Username = u.SpotifyUserName, CountryCode = u.CountryCode}) .ToList(); var joinedResult = sonyBuzzUsers.Union(mappedUsers).GroupBy(k => k.Username, StringComparer.InvariantCultureIgnoreCase) .Select(models => new FiltrUserCountryViewModel() { Username = models.Key, CountryCode = models.Select(c => c.CountryCode).FirstOrDefault(c => !string.IsNullOrWhiteSpace(c)) }).ToList(); return joinedResult; } private async Task BuildSearchResultViewModelAsync(Application application, SearchResult searchResult) { var playlistViewModels = await GetPlaylistsAsync(application, null); var releaseViewModels = await GetNewReleasesAsync(application, null); var searchResultViewModel = new SearchResultViewModel(); if (searchResult != null) { searchResultViewModel.Artists = searchResult.Artists; searchResultViewModel.Tags = searchResult.Tags; searchResultViewModel.Releases = searchResult.Releases.Select( r => releaseViewModels.FirstOrDefault( ri => new SpotifyLink(r.TrackLink).ExtractTrackID() == ri.TrackId)) .Where(r => r != null).DistinctBy(r => r.Id) .ToList(); searchResultViewModel.Playlists = searchResult.Playlists.Select(p => playlistViewModels.FirstOrDefault(pi => pi.Id == p.Id)) .Where(r => r != null).DistinctBy(p=> p.Id) .ToList(); } return searchResultViewModel; } public async Task GetAvailableRegionsLastModifiedAsync() { return await LastModifiedManager.GetLastModifyDateAsync(null, EntityType.SpotifyRegions); } public async Task> GetAvailableRegionsAsync() { return await _spotifyRegionManager.GetAvailableRegionsAsync(); } public async Task> GetPopularTagsAsync(Application market) { var playlists = await GetPlaylistsAsync(market); return playlists.SelectMany(p => p.Tags) .GroupBy(t => t.Label) .OrderByDescending(t => t.Sum(ti => ti.Match)) .Select(t => t.Key) .Distinct(StringComparer.InvariantCultureIgnoreCase) .Take(25) .ToList(); } } }