static async Task Main(string[] args) { try { ConsoleLogWriter?consoleLogger = SetupLogging(); string version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0.0"; Logger.log.Info($"Starting BeatSyncConsole v{version}"); await CheckVersion().ConfigureAwait(false); ConfigManager = new ConfigManager(ConfigDirectory); bool validConfig = await ConfigManager.InitializeConfigAsync().ConfigureAwait(false); if (consoleLogger != null) { consoleLogger.LogLevel = ConfigManager.Config?.ConsoleLogLevel ?? BeatSyncLib.Logging.LogLevel.Info; } Config?config = ConfigManager.Config; if (validConfig && config != null && config.BeatSyncConfig != null) { SongFeedReaders.WebUtils.Initialize(new WebUtilities.HttpClientWrapper.HttpClientWrapper()); SongFeedReaders.WebUtils.WebClient.SetUserAgent("BeatSyncConsole/" + version); JobManager manager = new JobManager(config.BeatSyncConfig.MaxConcurrentDownloads); manager.Start(CancellationToken.None); IJobBuilder jobBuilder = await CreateJobBuilderAsync(config).ConfigureAwait(false); SongDownloader songDownloader = new SongDownloader(); Stopwatch sw = new Stopwatch(); sw.Start(); JobStats[] sourceStats = await songDownloader.RunAsync(config.BeatSyncConfig, jobBuilder, manager).ConfigureAwait(false); JobStats beatSyncStats = sourceStats.Aggregate((a, b) => a + b); await manager.CompleteAsync().ConfigureAwait(false); int recentPlaylistDays = config.BeatSyncConfig.RecentPlaylistDays; DateTime cutoff = DateTime.Now - new TimeSpan(recentPlaylistDays, 0, 0, 0); foreach (SongTarget?target in jobBuilder.SongTargets) { if (target is ITargetWithPlaylists targetWithPlaylists) { PlaylistManager?targetPlaylistManager = targetWithPlaylists.PlaylistManager; if (recentPlaylistDays > 0) { IPlaylist?recent = targetPlaylistManager?.GetOrAddPlaylist(BuiltInPlaylist.BeatSyncRecent); if (recent != null && recent.Count > 0) { int songsRemoved = recent.RemoveAll(s => s.DateAdded < cutoff); if (songsRemoved > 0) { recent.RaisePlaylistChanged(); } } } try { targetPlaylistManager?.StoreAllPlaylists(); } catch (AggregateException ex) { Logger.log.Error($"Error storing playlists: {ex.Message}"); foreach (var e in ex.InnerExceptions) { Logger.log.Debug(e); } } catch (Exception ex) { Logger.log.Error($"Error storing playlists: {ex.Message}"); Logger.log.Debug(ex); } } if (target is ITargetWithHistory targetWithHistory) { try { targetWithHistory.HistoryManager?.WriteToFile(); } catch (Exception ex) { Logger.log.Info($"Unable to save history at '{targetWithHistory.HistoryManager?.HistoryPath}': {ex.Message}"); } } } sw.Stop(); Logger.log.Info($"Finished after {sw.Elapsed.TotalSeconds}s: {beatSyncStats}"); config.BeatSyncConfig.LastRun = DateTime.Now; } else { Logger.log.Info("BeatSyncConsole cannot run without a valid config, exiting."); } LogManager.Stop(); LogManager.Wait(); Console.WriteLine("Press Enter to continue..."); Console.Read(); } catch (Exception ex) { string message = $"Fatal Error in BeatSyncConsole: {ex.Message}\n{ex.StackTrace}"; if (LogManager.IsAlive && LogManager.HasWriters && Logger.log != null) { Logger.log.Error(message); } else { ConsoleColor previousColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(message); Console.ForegroundColor = previousColor; } LogManager.Stop(); LogManager.Wait(); Console.WriteLine("Press Enter to continue..."); Console.Read(); } finally { LogManager.Abort(); } }