using MoreLinq; using NLog; using Sony.Filtr.ApolloAPI; using Sony.Filtr.ApolloAPI.Models; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Definitions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.Core.EditorialPlaylists; using Sony.Filtr.ErrorLogging; using Sony.Filtr.Playlists; using Sony.Filtr.SpotifyWebAPI; using Sony.Filtr.SpotifyWebAPI.Model; using Sony.Filtr.Utility.Extensions; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Sony.Filtr.Tasks.Tasks.Spotify { public class ImportPlaylists : IScheduledTask { private readonly EditorialPlaylistManager _editorialPlaylistManager; private readonly EditorialPlaylistImportManager _importManager; private readonly IApolloWebApi _vendorApi; private readonly IApplicationInstanceManager _applicationInstanceManager; private readonly IgnoredPlaylistsManager _ignoredPlaylistsManager; private readonly Logger _logger; public ImportPlaylists(EditorialPlaylistManager editorialPlaylistManager, EditorialPlaylistImportManager importManager, IApolloWebApi vendorApi, IApplicationInstanceManager applicationInstanceManager, IgnoredPlaylistsManager ignoredPlaylistsManager) { _editorialPlaylistManager = editorialPlaylistManager; _importManager = importManager; _vendorApi = vendorApi; _applicationInstanceManager = applicationInstanceManager; _ignoredPlaylistsManager = ignoredPlaylistsManager; _logger = LogManager.GetLogger("ImportPlaylists"); } public async Task ExecuteAsync(Guid scheduledTaskLogId) { return await ImportSonyPlaylistsAsync(scheduledTaskLogId); } private async Task ImportSonyPlaylistsAsync(Guid scheduledTaskLogId) { var taskInfo = new ScheduledTaskLog(); var dataLog = new ScheduledTaskDataLog("SpotifyFiltrPlaylistImport", DateTimeOffset.UtcNow, scheduledTaskLogId); _logger.Debug("Begin importing playlist from Spotify-users"); var importUsers = _importManager.GetEditorialPlaylistImportUsers().ToList(); ConcurrentDictionary> existingPlaylists = new ConcurrentDictionary>(); foreach (var app in _applicationInstanceManager.GetApplications()) { var editorialPlaylists = await _editorialPlaylistManager.GetLightweightEditorialPlaylistsAsync(app); existingPlaylists.TryAdd(app.ID, editorialPlaylists); } var spotifyPlaylistsResponses = await GetSpotifyPlaylistsAsync(importUsers); var importedPlaylists = await SavePlaylistsAsync(importUsers, spotifyPlaylistsResponses, existingPlaylists); _logger.Debug("End importing playlist from Spotify-users"); dataLog.Rows = importedPlaylists; dataLog.Finished = DateTimeOffset.UtcNow; taskInfo.DataLogs.Add(dataLog); return taskInfo; } private async Task SavePlaylistsAsync(List importUsers, IDictionary spotifyPlaylistsResponses, ConcurrentDictionary> existingPlaylists) { int importedPlaylists = 0; var ignoredPlaylists = await _ignoredPlaylistsManager.GetIgnoredPlaylistsAsync(MusicService.Spotify); var ignoredPlaylistsUris = Enumerable.ToHashSet(ignoredPlaylists.Select(p => p.PlaylistId).Distinct()); await importUsers.ForEachAsync(5, async user => { try { var key = user.Username.Trim().ToLowerInvariant(); var username = user.Username; if (username.StartsWith("spotify:user:")) username = username.Replace("spotify:user:", string.Empty); if (!spotifyPlaylistsResponses.TryGetValue(key, out var playlists)) { _logger.Debug($"No playlist response for user: { username } "); return; } _logger.Debug($"Importing from user: { username }"); _logger.Debug($"User has { playlists.Count() } public playlists"); var ownPlaylists = playlists.Where(i => i.name != null && !i.name.Equals("starred", StringComparison.InvariantCultureIgnoreCase)).Where(p => p.owner.id.Equals(username, StringComparison.InvariantCultureIgnoreCase)).ToList(); _logger.Debug($"User has { ownPlaylists.Count } own playlists"); foreach (var playlistLink in ownPlaylists.Where(p => !ignoredPlaylistsUris.Contains(p.uri))) { var spotifyLink = new SpotifyLink($"spotify:playlist:{playlistLink.id}"); if (existingPlaylists.ContainsKey(user.ApplicationID) && !existingPlaylists[user.ApplicationID].Any(p => p.SpotifyLink == spotifyLink)) { EditorialPlaylist editorialPlaylist = new EditorialPlaylist { ApplicationID = user.ApplicationID, Name = playlistLink.name, SpotifyLink = spotifyLink, Active = false }; _logger.Debug($"Importing playlist: { spotifyLink.Uri }"); await _editorialPlaylistManager.SaveEditorialPlaylistAsync(editorialPlaylist); importedPlaylists++; } else { //_logger.Debug("--Skipping: " + playlistLink.uri + " already imported"); } } } catch (Exception ex) { _logger.Error(ex); ErrorLoggingManager.Instance.LogError(ex); } }); return importedPlaylists; } private async Task> GetSpotifyPlaylistsAsync(List importUsers) { var spotifyPlaylistsResponses = new ConcurrentDictionary(); var uniqueSpotifyUsers = importUsers.Select(u => u.Username.Trim().ToLowerInvariant()).Distinct(); await uniqueSpotifyUsers.ForEachAsync(5, async uniqueSpotifyUser => { _logger.Debug("Getting public playlists for {0}", uniqueSpotifyUser); try { const string fields = "items(id,name,owner.id),total"; var response = await _vendorApi.GetAllPublicPlaylistsAsync(uniqueSpotifyUser, fields); if (response != null) { spotifyPlaylistsResponses.TryAdd(uniqueSpotifyUser, response); } } catch (Exception ex) { _logger.Error(ex, "Could not get public playlists for {0}", uniqueSpotifyUser); } }); return spotifyPlaylistsResponses; } } }