From d7bae3e260b9eee9e08351d2da734d07b853eee0 Mon Sep 17 00:00:00 2001 From: whimsical-c4lic0 Date: Thu, 5 Feb 2026 02:21:05 -0600 Subject: [PATCH] Move config and logging into services --- OF DL/Program.cs | 865 ++++-------------------------- OF DL/Services/AuthService.cs | 72 +++ OF DL/Services/ConfigService.cs | 385 +++++++++++++ OF DL/Services/IAuthService.cs | 12 + OF DL/Services/IConfigService.cs | 13 + OF DL/Services/ILoggingService.cs | 12 + OF DL/Services/LoggingService.cs | 42 ++ 7 files changed, 649 insertions(+), 752 deletions(-) create mode 100644 OF DL/Services/AuthService.cs create mode 100644 OF DL/Services/ConfigService.cs create mode 100644 OF DL/Services/IAuthService.cs create mode 100644 OF DL/Services/IConfigService.cs create mode 100644 OF DL/Services/ILoggingService.cs create mode 100644 OF DL/Services/LoggingService.cs diff --git a/OF DL/Program.cs b/OF DL/Program.cs index 07f3c6b..d79378b 100644 --- a/OF DL/Program.cs +++ b/OF DL/Program.cs @@ -9,144 +9,125 @@ using OF_DL.Entities.Streams; using OF_DL.Enumerations; using OF_DL.Enumurations; using OF_DL.Helpers; +using OF_DL.Services; using Serilog; -using Serilog.Core; -using Serilog.Events; using Spectre.Console; using System.Reflection; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using static OF_DL.Entities.Messages.Messages; -using Akka.Configuration; -using System.Text; using Microsoft.Extensions.DependencyInjection; using OF_DL.Helpers.Interfaces; namespace OF_DL; -public class Program +public class Program(IServiceProvider serviceProvider) { public int MAX_AGE = 0; public static List paid_post_ids = new(); private static bool clientIdBlobMissing = false; private static bool devicePrivateKeyMissing = false; - private readonly Entities.Config config; - private readonly IAuthHelper authHelper; - private readonly IServiceProvider serviceProvider; - private Auth? auth = null; - private static LoggingLevelSwitch levelSwitch = new LoggingLevelSwitch(); - public Program(Entities.Config config, IAuthHelper authHelper, IServiceProvider serviceProvider) - { - this.config = config; - this.authHelper = authHelper; - this.serviceProvider = serviceProvider; - } - - private async Task LoadAuthFromBrowser() + private async Task LoadAuthFromBrowser() { + var authService = serviceProvider.GetRequiredService(); bool runningInDocker = Environment.GetEnvironmentVariable("OFDL_DOCKER") != null; - try + // Show initial message + AnsiConsole.MarkupLine($"[yellow]Downloading dependencies. Please wait ...[/]"); + + // Show instructions based on the environment + await Task.Delay(5000); + if (runningInDocker) { - Task setupBrowserTask = authHelper.SetupBrowser(runningInDocker); - - Task.Delay(1000).Wait(); - if (!setupBrowserTask.IsCompleted) - { - AnsiConsole.MarkupLine($"[yellow]Downloading dependencies. Please wait ...[/]"); - } - setupBrowserTask.Wait(); - - Task getAuthTask = authHelper.GetAuthFromBrowser(); - Task.Delay(5000).Wait(); - if (!getAuthTask.IsCompleted) - { - if (runningInDocker) - { - AnsiConsole.MarkupLine( - "[yellow]In your web browser, navigate to the port forwarded from your docker container.[/]"); - AnsiConsole.MarkupLine( - "[yellow]For instance, if your docker run command included \"-p 8080:8080\", open your web browser to \"http://localhost:8080\".[/]"); - AnsiConsole.MarkupLine("[yellow]Once on that webpage, please use it to log in to your OF account. Do not navigate away from the page.[/]"); - } - else - { - AnsiConsole.MarkupLine($"[yellow]In the new window that has opened, please log in to your OF account. Do not close the window or tab. Do not navigate away from the page.[/]\n"); - AnsiConsole.MarkupLine($"[yellow]Note: Some users have reported that \"Sign in with Google\" has not been working with the new authentication method.[/]"); - AnsiConsole.MarkupLine($"[yellow]If you use this method or encounter other issues while logging in, use one of the legacy authentication methods documented here:[/]"); - AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); - } - } - auth = await getAuthTask; + AnsiConsole.MarkupLine( + "[yellow]In your web browser, navigate to the port forwarded from your docker container.[/]"); + AnsiConsole.MarkupLine( + "[yellow]For instance, if your docker run command included \"-p 8080:8080\", open your web browser to \"http://localhost:8080\".[/]"); + AnsiConsole.MarkupLine("[yellow]Once on that webpage, please use it to log in to your OF account. Do not navigate away from the page.[/]"); } - catch (Exception e) + else + { + AnsiConsole.MarkupLine($"[yellow]In the new window that has opened, please log in to your OF account. Do not close the window or tab. Do not navigate away from the page.[/]\n"); + AnsiConsole.MarkupLine($"[yellow]Note: Some users have reported that \"Sign in with Google\" has not been working with the new authentication method.[/]"); + AnsiConsole.MarkupLine($"[yellow]If you use this method or encounter other issues while logging in, use one of the legacy authentication methods documented here:[/]"); + AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); + } + + // Load auth from browser using the service + bool success = await authService.LoadFromBrowserAsync(); + + if (!success || authService.CurrentAuth == null) { AnsiConsole.MarkupLine($"\n[red]Authentication failed. Be sure to log into to OF using the new window that opened automatically.[/]"); AnsiConsole.MarkupLine($"[red]The window will close automatically when the authentication process is finished.[/]"); AnsiConsole.MarkupLine($"[red]If the problem persists, you may want to try using a legacy authentication method documented here:[/]\n"); - AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); - AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); - Log.Error(e, "auth invalid after attempt to get auth from browser"); - - Environment.Exit(2); - } - - if (auth == null) - { - AnsiConsole.MarkupLine($"\n[red]Authentication failed. Be sure to log into to OF using the new window that opened automatically.[/]"); - AnsiConsole.MarkupLine($"[red]The window will close automatically when the authentication process is finished.[/]"); - AnsiConsole.MarkupLine($"[red]If the problem persists, you may want to try using a legacy authentication method documented here:[/]\n"); - AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); + AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); Log.Error("auth invalid after attempt to get auth from browser"); Environment.Exit(2); } - else - { - await File.WriteAllTextAsync("auth.json", JsonConvert.SerializeObject(auth, Formatting.Indented)); - } + + await authService.SaveToFileAsync(); } public static async Task Main(string[] args) { - levelSwitch.MinimumLevel = LogEventLevel.Error; //set initial level (until we've read from config) - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.ControlledBy(levelSwitch) - .WriteTo.File("logs/OFDL.txt", rollingInterval: RollingInterval.Day) - .CreateLogger(); - AnsiConsole.Write(new FigletText("Welcome to OF-DL").Color(Color.Red)); AnsiConsole.Markup("Documentation: [link]https://docs.ofdl.tools/[/]\n"); AnsiConsole.Markup("Discord server: [link]https://discord.com/invite/6bUW8EJ53j[/]\n\n"); - // Load configuration - Entities.Config? config = LoadConfiguration(args, out bool cliNonInteractive); - if (config == null) + // Set up dependency injection with LoggingService and ConfigService + ServiceCollection services = new(); + services.AddSingleton(); + services.AddSingleton(); + ServiceProvider tempServiceProvider = services.BuildServiceProvider(); + + // Load configuration using ConfigService + IConfigService configService = tempServiceProvider.GetRequiredService(); + + if (!await configService.LoadConfigurationAsync(args)) { + AnsiConsole.MarkupLine($"\n[red]config.conf is not valid, check your syntax![/]\n"); + AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); + if (!configService.IsCliNonInteractive) + { + Console.ReadKey(); + } Environment.Exit(3); return; } - // Set up dependency injection - var services = new ServiceCollection(); - ConfigureServices(services, config); + if (configService.CurrentConfig == null) + { + AnsiConsole.MarkupLine($"\n[red]Failed to load configuration.[/]\n"); + Environment.Exit(3); + return; + } + + AnsiConsole.Markup("[green]config.conf located successfully!\n[/]"); + + // Set up full dependency injection with loaded config + services = new ServiceCollection(); + ConfigureServices(services, configService.CurrentConfig, tempServiceProvider.GetRequiredService()); var serviceProvider = services.BuildServiceProvider(); // Get the Program instance and run var program = serviceProvider.GetRequiredService(); - await program.RunAsync(args, cliNonInteractive); + await program.RunAsync(args, configService.IsCliNonInteractive); } - private static void ConfigureServices(IServiceCollection services, Entities.Config config) + private static void ConfigureServices(IServiceCollection services, Entities.Config config, ILoggingService loggingService) { services.AddSingleton(config); services.AddSingleton(config); services.AddSingleton(config); + services.AddSingleton(loggingService); + services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -156,409 +137,11 @@ public class Program services.AddSingleton(); } - private static Entities.Config? LoadConfiguration(string[] args, out bool cliNonInteractive) - { - cliNonInteractive = false; - Entities.Config? config = null; - - try - { - //Remove config.json and convert to config.conf - if (File.Exists("config.json")) - { - AnsiConsole.Markup("[green]config.json located successfully!\n[/]"); - try - { - string jsonText = File.ReadAllText("config.json"); - var jsonConfig = JsonConvert.DeserializeObject(jsonText); - - if (jsonConfig != null) - { - var hoconConfig = new StringBuilder(); - hoconConfig.AppendLine("# Auth"); - hoconConfig.AppendLine("Auth {"); - hoconConfig.AppendLine($" DisableBrowserAuth = {jsonConfig.DisableBrowserAuth.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - hoconConfig.AppendLine("# External Tools"); - hoconConfig.AppendLine("External {"); - hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Download Settings"); - hoconConfig.AppendLine("Download {"); - hoconConfig.AppendLine(" Media {"); - hoconConfig.AppendLine($" DownloadAvatarHeaderPhoto = {jsonConfig.DownloadAvatarHeaderPhoto.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidPosts = {jsonConfig.DownloadPaidPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPosts = {jsonConfig.DownloadPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadArchived = {jsonConfig.DownloadArchived.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStreams = {jsonConfig.DownloadStreams.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStories = {jsonConfig.DownloadStories.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadHighlights = {jsonConfig.DownloadHighlights.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadMessages = {jsonConfig.DownloadMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidMessages = {jsonConfig.DownloadPaidMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadImages = {jsonConfig.DownloadImages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadVideos = {jsonConfig.DownloadVideos.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadAudios = {jsonConfig.DownloadAudios.ToString().ToLower()}"); - hoconConfig.AppendLine(" }"); - hoconConfig.AppendLine($" IgnoreOwnMessages = {jsonConfig.IgnoreOwnMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPostsIncrementally = {jsonConfig.DownloadPostsIncrementally.ToString().ToLower()}"); - hoconConfig.AppendLine($" BypassContentForCreatorsWhoNoLongerExist = {jsonConfig.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDuplicatedMedia = {jsonConfig.DownloadDuplicatedMedia.ToString().ToLower()}"); - hoconConfig.AppendLine($" SkipAds = {jsonConfig.SkipAds.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPath = \"{jsonConfig.DownloadPath}\""); - hoconConfig.AppendLine($" DownloadOnlySpecificDates = {jsonConfig.DownloadOnlySpecificDates.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDateSelection = \"{jsonConfig.DownloadDateSelection.ToString().ToLower()}\""); - hoconConfig.AppendLine($" CustomDate = \"{jsonConfig.CustomDate?.ToString("yyyy-MM-dd")}\""); - hoconConfig.AppendLine($" ShowScrapeSize = {jsonConfig.ShowScrapeSize.ToString().ToLower()}"); - hoconConfig.AppendLine($" DisableTextSanitization = false"); - hoconConfig.AppendLine($" DownloadVideoResolution = \"{(jsonConfig.DownloadVideoResolution == VideoResolution.source ? "source" : jsonConfig.DownloadVideoResolution.ToString().TrimStart('_'))}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# File Settings"); - hoconConfig.AppendLine("File {"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{jsonConfig.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{jsonConfig.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{jsonConfig.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{jsonConfig.MessageFileNameFormat}\""); - hoconConfig.AppendLine($" RenameExistingFilesWhenCustomFormatIsSelected = {jsonConfig.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Creator-Specific Configurations"); - hoconConfig.AppendLine("CreatorConfigs {"); - foreach (var creatorConfig in jsonConfig.CreatorConfigs) - { - hoconConfig.AppendLine($" \"{creatorConfig.Key}\" {{"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{creatorConfig.Value.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{creatorConfig.Value.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{creatorConfig.Value.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{creatorConfig.Value.MessageFileNameFormat}\""); - hoconConfig.AppendLine(" }"); - } - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Folder Settings"); - hoconConfig.AppendLine("Folder {"); - hoconConfig.AppendLine($" FolderPerPaidPost = {jsonConfig.FolderPerPaidPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPost = {jsonConfig.FolderPerPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPaidMessage = {jsonConfig.FolderPerPaidMessage.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerMessage = {jsonConfig.FolderPerMessage.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Subscription Settings"); - hoconConfig.AppendLine("Subscriptions {"); - hoconConfig.AppendLine($" IncludeExpiredSubscriptions = {jsonConfig.IncludeExpiredSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IncludeRestrictedSubscriptions = {jsonConfig.IncludeRestrictedSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IgnoredUsersListName = \"{jsonConfig.IgnoredUsersListName}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Interaction Settings"); - hoconConfig.AppendLine("Interaction {"); - hoconConfig.AppendLine($" NonInteractiveMode = {jsonConfig.NonInteractiveMode.ToString().ToLower()}"); - hoconConfig.AppendLine($" NonInteractiveModeListName = \"{jsonConfig.NonInteractiveModeListName}\""); - hoconConfig.AppendLine($" NonInteractiveModePurchasedTab = {jsonConfig.NonInteractiveModePurchasedTab.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Performance Settings"); - hoconConfig.AppendLine("Performance {"); - hoconConfig.AppendLine($" Timeout = {(jsonConfig.Timeout.HasValue ? jsonConfig.Timeout.Value : -1)}"); - hoconConfig.AppendLine($" LimitDownloadRate = {jsonConfig.LimitDownloadRate.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadLimitInMbPerSec = {jsonConfig.DownloadLimitInMbPerSec}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Logging/Debug Settings"); - hoconConfig.AppendLine("Logging {"); - hoconConfig.AppendLine($" LoggingLevel = \"{jsonConfig.LoggingLevel.ToString().ToLower()}\""); - hoconConfig.AppendLine("}"); - - File.WriteAllText("config.conf", hoconConfig.ToString()); - File.Delete("config.json"); - AnsiConsole.Markup("[green]config.conf created successfully from config.json!\n[/]"); - } - } - catch (Exception e) - { - Console.WriteLine(e); - AnsiConsole.MarkupLine($"\n[red]config.conf is not valid, check your syntax![/]\n"); - AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); - Log.Error("config.conf processing failed.", e.Message); - - if (!cliNonInteractive) - { - Console.ReadKey(); - } - Environment.Exit(3); - } - } - - //I dont like it... but I needed to move config here, otherwise the logging level gets changed too late after we missed a whole bunch of important info - if (File.Exists("config.conf")) - { - AnsiConsole.Markup("[green]config.conf located successfully!\n[/]"); - try - { - string hoconText = File.ReadAllText("config.conf"); - - var hoconConfig = ConfigurationFactory.ParseString(hoconText); - - config = new Entities.Config - { - //Auth - DisableBrowserAuth = hoconConfig.GetBoolean("Auth.DisableBrowserAuth"), - - // FFmpeg Settings - FFmpegPath = hoconConfig.GetString("External.FFmpegPath"), - - // Download Settings - DownloadAvatarHeaderPhoto = hoconConfig.GetBoolean("Download.Media.DownloadAvatarHeaderPhoto"), - DownloadPaidPosts = hoconConfig.GetBoolean("Download.Media.DownloadPaidPosts"), - DownloadPosts = hoconConfig.GetBoolean("Download.Media.DownloadPosts"), - DownloadArchived = hoconConfig.GetBoolean("Download.Media.DownloadArchived"), - DownloadStreams = hoconConfig.GetBoolean("Download.Media.DownloadStreams"), - DownloadStories = hoconConfig.GetBoolean("Download.Media.DownloadStories"), - DownloadHighlights = hoconConfig.GetBoolean("Download.Media.DownloadHighlights"), - DownloadMessages = hoconConfig.GetBoolean("Download.Media.DownloadMessages"), - DownloadPaidMessages = hoconConfig.GetBoolean("Download.Media.DownloadPaidMessages"), - DownloadImages = hoconConfig.GetBoolean("Download.Media.DownloadImages"), - DownloadVideos = hoconConfig.GetBoolean("Download.Media.DownloadVideos"), - DownloadAudios = hoconConfig.GetBoolean("Download.Media.DownloadAudios"), - IgnoreOwnMessages = hoconConfig.GetBoolean("Download.IgnoreOwnMessages"), - DownloadPostsIncrementally = hoconConfig.GetBoolean("Download.DownloadPostsIncrementally"), - BypassContentForCreatorsWhoNoLongerExist = hoconConfig.GetBoolean("Download.BypassContentForCreatorsWhoNoLongerExist"), - DownloadDuplicatedMedia = hoconConfig.GetBoolean("Download.DownloadDuplicatedMedia"), - SkipAds = hoconConfig.GetBoolean("Download.SkipAds"), - DownloadPath = hoconConfig.GetString("Download.DownloadPath"), - DownloadOnlySpecificDates = hoconConfig.GetBoolean("Download.DownloadOnlySpecificDates"), - DownloadDateSelection = Enum.Parse(hoconConfig.GetString("Download.DownloadDateSelection"), true), - CustomDate = !string.IsNullOrWhiteSpace(hoconConfig.GetString("Download.CustomDate")) ? DateTime.Parse(hoconConfig.GetString("Download.CustomDate")) : null, - ShowScrapeSize = hoconConfig.GetBoolean("Download.ShowScrapeSize"), - // Optional flag; default to false when missing - DisableTextSanitization = bool.TryParse(hoconConfig.GetString("Download.DisableTextSanitization", "false"), out var dts) ? dts : false, - DownloadVideoResolution = ParseVideoResolution(hoconConfig.GetString("Download.DownloadVideoResolution", "source")), - - // File Settings - PaidPostFileNameFormat = hoconConfig.GetString("File.PaidPostFileNameFormat"), - PostFileNameFormat = hoconConfig.GetString("File.PostFileNameFormat"), - PaidMessageFileNameFormat = hoconConfig.GetString("File.PaidMessageFileNameFormat"), - MessageFileNameFormat = hoconConfig.GetString("File.MessageFileNameFormat"), - RenameExistingFilesWhenCustomFormatIsSelected = hoconConfig.GetBoolean("File.RenameExistingFilesWhenCustomFormatIsSelected"), - - // Folder Settings - FolderPerPaidPost = hoconConfig.GetBoolean("Folder.FolderPerPaidPost"), - FolderPerPost = hoconConfig.GetBoolean("Folder.FolderPerPost"), - FolderPerPaidMessage = hoconConfig.GetBoolean("Folder.FolderPerPaidMessage"), - FolderPerMessage = hoconConfig.GetBoolean("Folder.FolderPerMessage"), - - // Subscription Settings - IncludeExpiredSubscriptions = hoconConfig.GetBoolean("Subscriptions.IncludeExpiredSubscriptions"), - IncludeRestrictedSubscriptions = hoconConfig.GetBoolean("Subscriptions.IncludeRestrictedSubscriptions"), - IgnoredUsersListName = hoconConfig.GetString("Subscriptions.IgnoredUsersListName"), - - // Interaction Settings - NonInteractiveMode = hoconConfig.GetBoolean("Interaction.NonInteractiveMode"), - NonInteractiveModeListName = hoconConfig.GetString("Interaction.NonInteractiveModeListName"), - NonInteractiveModePurchasedTab = hoconConfig.GetBoolean("Interaction.NonInteractiveModePurchasedTab"), - - // Performance Settings - Timeout = string.IsNullOrWhiteSpace(hoconConfig.GetString("Performance.Timeout")) ? -1 : hoconConfig.GetInt("Performance.Timeout"), - LimitDownloadRate = hoconConfig.GetBoolean("Performance.LimitDownloadRate"), - DownloadLimitInMbPerSec = hoconConfig.GetInt("Performance.DownloadLimitInMbPerSec"), - - // Logging/Debug Settings - LoggingLevel = Enum.Parse(hoconConfig.GetString("Logging.LoggingLevel"), true) - }; - - ValidateFileNameFormat(config.PaidPostFileNameFormat, "PaidPostFileNameFormat"); - ValidateFileNameFormat(config.PostFileNameFormat, "PostFileNameFormat"); - ValidateFileNameFormat(config.PaidMessageFileNameFormat, "PaidMessageFileNameFormat"); - ValidateFileNameFormat(config.MessageFileNameFormat, "MessageFileNameFormat"); - - var creatorConfigsSection = hoconConfig.GetConfig("CreatorConfigs"); - if (creatorConfigsSection != null) - { - foreach (var key in creatorConfigsSection.AsEnumerable()) - { - var creatorKey = key.Key; - var creatorHocon = creatorConfigsSection.GetConfig(creatorKey); - if (!config.CreatorConfigs.ContainsKey(creatorKey) && creatorHocon != null) - { - config.CreatorConfigs.Add(key.Key, new CreatorConfig - { - PaidPostFileNameFormat = creatorHocon.GetString("PaidPostFileNameFormat"), - PostFileNameFormat = creatorHocon.GetString("PostFileNameFormat"), - PaidMessageFileNameFormat = creatorHocon.GetString("PaidMessageFileNameFormat"), - MessageFileNameFormat = creatorHocon.GetString("MessageFileNameFormat") - }); - - ValidateFileNameFormat(config.CreatorConfigs[key.Key].PaidPostFileNameFormat, $"{key.Key}.PaidPostFileNameFormat"); - ValidateFileNameFormat(config.CreatorConfigs[key.Key].PostFileNameFormat, $"{key.Key}.PostFileNameFormat"); - ValidateFileNameFormat(config.CreatorConfigs[key.Key].PaidMessageFileNameFormat, $"{key.Key}.PaidMessageFileNameFormat"); - ValidateFileNameFormat(config.CreatorConfigs[key.Key].MessageFileNameFormat, $"{key.Key}.MessageFileNameFormat"); - } - } - } - - levelSwitch.MinimumLevel = (LogEventLevel)config.LoggingLevel; //set the logging level based on config - // Apply text sanitization preference globally - OF_DL.Utils.XmlUtils.Passthrough = config.DisableTextSanitization; - Log.Debug("Configuration:"); - string configString = JsonConvert.SerializeObject(config, Formatting.Indented); - Log.Debug(configString); - } - catch (Exception e) - { - Console.WriteLine(e); - AnsiConsole.MarkupLine($"\n[red]config.conf is not valid, check your syntax![/]\n"); - AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); - Log.Error("config.conf processing failed.", e.Message); - - if (!cliNonInteractive) - { - Console.ReadKey(); - } - Environment.Exit(3); - } - } - else - { - Entities.Config jsonConfig = new Entities.Config(); - var hoconConfig = new StringBuilder(); - hoconConfig.AppendLine("# Auth"); - hoconConfig.AppendLine("Auth {"); - hoconConfig.AppendLine($" DisableBrowserAuth = {jsonConfig.DisableBrowserAuth.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - hoconConfig.AppendLine("# External Tools"); - hoconConfig.AppendLine("External {"); - hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Download Settings"); - hoconConfig.AppendLine("Download {"); - hoconConfig.AppendLine(" Media {"); - hoconConfig.AppendLine($" DownloadAvatarHeaderPhoto = {jsonConfig.DownloadAvatarHeaderPhoto.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidPosts = {jsonConfig.DownloadPaidPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPosts = {jsonConfig.DownloadPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadArchived = {jsonConfig.DownloadArchived.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStreams = {jsonConfig.DownloadStreams.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStories = {jsonConfig.DownloadStories.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadHighlights = {jsonConfig.DownloadHighlights.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadMessages = {jsonConfig.DownloadMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidMessages = {jsonConfig.DownloadPaidMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadImages = {jsonConfig.DownloadImages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadVideos = {jsonConfig.DownloadVideos.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadAudios = {jsonConfig.DownloadAudios.ToString().ToLower()}"); - hoconConfig.AppendLine(" }"); - hoconConfig.AppendLine($" IgnoreOwnMessages = {jsonConfig.IgnoreOwnMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPostsIncrementally = {jsonConfig.DownloadPostsIncrementally.ToString().ToLower()}"); - hoconConfig.AppendLine($" BypassContentForCreatorsWhoNoLongerExist = {jsonConfig.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDuplicatedMedia = {jsonConfig.DownloadDuplicatedMedia.ToString().ToLower()}"); - hoconConfig.AppendLine($" SkipAds = {jsonConfig.SkipAds.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPath = \"{jsonConfig.DownloadPath}\""); - hoconConfig.AppendLine($" DownloadOnlySpecificDates = {jsonConfig.DownloadOnlySpecificDates.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDateSelection = \"{jsonConfig.DownloadDateSelection.ToString().ToLower()}\""); - hoconConfig.AppendLine($" CustomDate = \"{jsonConfig.CustomDate?.ToString("yyyy-MM-dd")}\""); - hoconConfig.AppendLine($" ShowScrapeSize = {jsonConfig.ShowScrapeSize.ToString().ToLower()}"); - // New option defaults to false when converting legacy json - hoconConfig.AppendLine($" DisableTextSanitization = false"); - hoconConfig.AppendLine($" DownloadVideoResolution = \"{(jsonConfig.DownloadVideoResolution == VideoResolution.source ? "source" : jsonConfig.DownloadVideoResolution.ToString().TrimStart('_'))}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# File Settings"); - hoconConfig.AppendLine("File {"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{jsonConfig.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{jsonConfig.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{jsonConfig.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{jsonConfig.MessageFileNameFormat}\""); - hoconConfig.AppendLine($" RenameExistingFilesWhenCustomFormatIsSelected = {jsonConfig.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Creator-Specific Configurations"); - hoconConfig.AppendLine("CreatorConfigs {"); - foreach (var creatorConfig in jsonConfig.CreatorConfigs) - { - hoconConfig.AppendLine($" \"{creatorConfig.Key}\" {{"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{creatorConfig.Value.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{creatorConfig.Value.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{creatorConfig.Value.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{creatorConfig.Value.MessageFileNameFormat}\""); - hoconConfig.AppendLine(" }"); - } - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Folder Settings"); - hoconConfig.AppendLine("Folder {"); - hoconConfig.AppendLine($" FolderPerPaidPost = {jsonConfig.FolderPerPaidPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPost = {jsonConfig.FolderPerPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPaidMessage = {jsonConfig.FolderPerPaidMessage.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerMessage = {jsonConfig.FolderPerMessage.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Subscription Settings"); - hoconConfig.AppendLine("Subscriptions {"); - hoconConfig.AppendLine($" IncludeExpiredSubscriptions = {jsonConfig.IncludeExpiredSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IncludeRestrictedSubscriptions = {jsonConfig.IncludeRestrictedSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IgnoredUsersListName = \"{jsonConfig.IgnoredUsersListName}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Interaction Settings"); - hoconConfig.AppendLine("Interaction {"); - hoconConfig.AppendLine($" NonInteractiveMode = {jsonConfig.NonInteractiveMode.ToString().ToLower()}"); - hoconConfig.AppendLine($" NonInteractiveModeListName = \"{jsonConfig.NonInteractiveModeListName}\""); - hoconConfig.AppendLine($" NonInteractiveModePurchasedTab = {jsonConfig.NonInteractiveModePurchasedTab.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Performance Settings"); - hoconConfig.AppendLine("Performance {"); - hoconConfig.AppendLine($" Timeout = {(jsonConfig.Timeout.HasValue ? jsonConfig.Timeout.Value : -1)}"); - hoconConfig.AppendLine($" LimitDownloadRate = {jsonConfig.LimitDownloadRate.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadLimitInMbPerSec = {jsonConfig.DownloadLimitInMbPerSec}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Logging/Debug Settings"); - hoconConfig.AppendLine("Logging {"); - hoconConfig.AppendLine($" LoggingLevel = \"{jsonConfig.LoggingLevel.ToString().ToLower()}\""); - hoconConfig.AppendLine("}"); - - File.WriteAllText("config.conf", hoconConfig.ToString()); - AnsiConsole.Markup("[red]config.conf does not exist, a default file has been created in the folder you are running the program from[/]"); - Log.Error("config.conf does not exist"); - - if (!cliNonInteractive) - { - Console.ReadKey(); - } - Environment.Exit(3); - } - - - if (args is not null && args.Length > 0) - { - const string NON_INTERACTIVE_ARG = "--non-interactive"; - - if (args.Any(a => NON_INTERACTIVE_ARG.Equals(NON_INTERACTIVE_ARG, StringComparison.OrdinalIgnoreCase))) - { - cliNonInteractive = true; - Log.Debug("NonInteractiveMode set via command line"); - } - - Log.Debug("Additional arguments:"); - foreach (string argument in args) - { - Log.Debug(argument); - } - } - } - catch (Exception e) - { - Console.WriteLine(e); - Log.Error("Configuration loading failed.", e.Message); - return null; - } - - return config; - } - private async Task RunAsync(string[] args, bool cliNonInteractive) { + var config = serviceProvider.GetRequiredService(); + var authService = serviceProvider.GetRequiredService(); + try { var os = Environment.OSVersion; @@ -650,51 +233,46 @@ public class Program } - if (File.Exists("auth.json")) + if (await authService.LoadFromFileAsync()) { AnsiConsole.Markup("[green]auth.json located successfully!\n[/]"); - Log.Debug("Auth file found"); - try + } + else if (File.Exists("auth.json")) + { + // File exists but failed to load + Log.Information("Auth file found but could not be deserialized"); + if (!config!.DisableBrowserAuth) { - auth = JsonConvert.DeserializeObject(await File.ReadAllTextAsync("auth.json")); - Log.Debug("Auth file found and deserialized"); + Log.Debug("Deleting auth.json"); + File.Delete("auth.json"); } - catch (Exception _) - { - Log.Information("Auth file found but could not be deserialized"); - if (!config!.DisableBrowserAuth) - { - Log.Debug("Deleting auth.json"); - File.Delete("auth.json"); - } - if (cliNonInteractive) - { - AnsiConsole.MarkupLine($"\n[red]auth.json has invalid JSON syntax. The file can be generated automatically when OF-DL is run in the standard, interactive mode.[/]\n"); - AnsiConsole.MarkupLine($"[red]You may also want to try using the browser extension which is documented here:[/]\n"); - AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); - AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); + if (cliNonInteractive) + { + AnsiConsole.MarkupLine($"\n[red]auth.json has invalid JSON syntax. The file can be generated automatically when OF-DL is run in the standard, interactive mode.[/]\n"); + AnsiConsole.MarkupLine($"[red]You may also want to try using the browser extension which is documented here:[/]\n"); + AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); + AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); - Console.ReadKey(); - Environment.Exit(2); - } + Console.ReadKey(); + Environment.Exit(2); + } - if (!config!.DisableBrowserAuth) - { - await LoadAuthFromBrowser(); - } - else - { - AnsiConsole.MarkupLine($"\n[red]auth.json is missing. The file can be generated automatically when OF-DL is run in the standard, interactive mode.[/]\n"); - AnsiConsole.MarkupLine($"[red]You may also want to try using the browser extension which is documented here:[/]\n"); - AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); - AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); + if (!config!.DisableBrowserAuth) + { + await LoadAuthFromBrowser(); + } + else + { + AnsiConsole.MarkupLine($"\n[red]auth.json is missing. The file can be generated automatically when OF-DL is run in the standard, interactive mode.[/]\n"); + AnsiConsole.MarkupLine($"[red]You may also want to try using the browser extension which is documented here:[/]\n"); + AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]"); + AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]"); - Console.ReadKey(); - Environment.Exit(2); - } - } + Console.ReadKey(); + Environment.Exit(2); + } } else { @@ -726,7 +304,7 @@ public class Program } //Added to stop cookie being filled with un-needed headers - ValidateCookieString(auth!); + ValidateCookieString(authService.CurrentAuth!); if (File.Exists("rules.json")) { @@ -775,11 +353,11 @@ public class Program Log.Debug($"FFMPEG found: {config.FFmpegPath}"); Log.Debug("FFMPEG path set in config.conf"); } - else if (!string.IsNullOrEmpty(auth!.FFMPEG_PATH) && ValidateFilePath(auth.FFMPEG_PATH)) + else if (!string.IsNullOrEmpty(authService.CurrentAuth!.FFMPEG_PATH) && ValidateFilePath(authService.CurrentAuth.FFMPEG_PATH)) { // FFmpeg path is set in auth.json and is valid (config.conf takes precedence and auth.json is only available for backward compatibility) ffmpegFound = true; - config.FFmpegPath = auth.FFMPEG_PATH; + config.FFmpegPath = authService.CurrentAuth.FFMPEG_PATH; Log.Debug($"FFMPEG found: {config.FFmpegPath}"); Log.Debug("FFMPEG path set in auth.json"); } @@ -923,14 +501,14 @@ public class Program } //Check if auth is valid - var apiHelper = ActivatorUtilities.CreateInstance(serviceProvider, auth); + var apiHelper = ActivatorUtilities.CreateInstance(serviceProvider, authService.CurrentAuth); Entities.User? validate = await apiHelper.GetUserInfo($"/users/me"); if (validate == null || (validate?.name == null && validate?.username == null)) { Log.Error("Auth failed"); - auth = null; + authService.CurrentAuth = null; if (!config!.DisableBrowserAuth) { if (File.Exists("auth.json")) @@ -944,7 +522,7 @@ public class Program await LoadAuthFromBrowser(); } - if (auth == null) + if (authService.CurrentAuth == null) { AnsiConsole.MarkupLine($"\n[red]Auth failed. Please try again or use other authentication methods detailed here:[/]\n"); AnsiConsole.MarkupLine($"[link]https://docs.ofdl.tools/config/auth[/]\n"); @@ -954,7 +532,7 @@ public class Program } AnsiConsole.Markup($"[green]Logged In successfully as {validate.name} {validate.username}\n[/]"); - await DownloadAllData(apiHelper, auth, config); + await DownloadAllData(apiHelper, authService.CurrentAuth, config); } catch (Exception ex) { @@ -978,6 +556,7 @@ public class Program private async Task DownloadAllData(IAPIHelper m_ApiHelper, Auth Auth, Entities.Config Config) { IDBHelper dBHelper = serviceProvider.GetRequiredService(); + IConfigService configService = serviceProvider.GetRequiredService(); Log.Debug("Calling DownloadAllData"); @@ -1048,7 +627,8 @@ public class Program } else { - var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists); + var loggingService = serviceProvider.GetRequiredService(); + var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists, configService, loggingService); Config = userSelectionResult.updatedConfig; hasSelectedUsersKVP = new KeyValuePair>(userSelectionResult.IsExit, userSelectionResult.selectedUsers); @@ -2991,7 +2571,7 @@ public class Program } } - public static async Task<(bool IsExit, Dictionary? selectedUsers, Entities.Config? updatedConfig)> HandleUserSelection(IAPIHelper apiHelper, Entities.Config currentConfig, Dictionary users, Dictionary lists) + public static async Task<(bool IsExit, Dictionary? selectedUsers, Entities.Config? updatedConfig)> HandleUserSelection(IAPIHelper apiHelper, Entities.Config currentConfig, Dictionary users, Dictionary lists, IConfigService configService, ILoggingService loggingService) { bool hasSelectedUsers = false; Dictionary selectedUsers = new Dictionary(); @@ -3145,108 +2725,8 @@ public class Program } } - var hoconConfig = new StringBuilder(); - hoconConfig.AppendLine("# Auth"); - hoconConfig.AppendLine("Auth {"); - hoconConfig.AppendLine($" DisableBrowserAuth = {newConfig.DisableBrowserAuth.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - hoconConfig.AppendLine("# External Tools"); - hoconConfig.AppendLine("External {"); - hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Download Settings"); - hoconConfig.AppendLine("Download {"); - hoconConfig.AppendLine(" Media {"); - hoconConfig.AppendLine($" DownloadAvatarHeaderPhoto = {newConfig.DownloadAvatarHeaderPhoto.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidPosts = {newConfig.DownloadPaidPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPosts = {newConfig.DownloadPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadArchived = {newConfig.DownloadArchived.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStreams = {newConfig.DownloadStreams.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStories = {newConfig.DownloadStories.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadHighlights = {newConfig.DownloadHighlights.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadMessages = {newConfig.DownloadMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidMessages = {newConfig.DownloadPaidMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadImages = {newConfig.DownloadImages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadVideos = {newConfig.DownloadVideos.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadAudios = {newConfig.DownloadAudios.ToString().ToLower()}"); - hoconConfig.AppendLine(" }"); - hoconConfig.AppendLine($" IgnoreOwnMessages = {newConfig.IgnoreOwnMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPostsIncrementally = {newConfig.DownloadPostsIncrementally.ToString().ToLower()}"); - hoconConfig.AppendLine($" BypassContentForCreatorsWhoNoLongerExist = {newConfig.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDuplicatedMedia = {newConfig.DownloadDuplicatedMedia.ToString().ToLower()}"); - hoconConfig.AppendLine($" SkipAds = {newConfig.SkipAds.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPath = \"{newConfig.DownloadPath}\""); - hoconConfig.AppendLine($" DownloadOnlySpecificDates = {newConfig.DownloadOnlySpecificDates.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDateSelection = \"{newConfig.DownloadDateSelection.ToString().ToLower()}\""); - hoconConfig.AppendLine($" CustomDate = \"{newConfig.CustomDate?.ToString("yyyy-MM-dd")}\""); - hoconConfig.AppendLine($" ShowScrapeSize = {newConfig.ShowScrapeSize.ToString().ToLower()}"); - hoconConfig.AppendLine($" DisableTextSanitization = {newConfig.DisableTextSanitization.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadVideoResolution = \"{(newConfig.DownloadVideoResolution == VideoResolution.source ? "source" : newConfig.DownloadVideoResolution.ToString().TrimStart('_'))}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# File Settings"); - hoconConfig.AppendLine("File {"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{newConfig.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{newConfig.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{newConfig.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{newConfig.MessageFileNameFormat}\""); - hoconConfig.AppendLine($" RenameExistingFilesWhenCustomFormatIsSelected = {newConfig.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Creator-Specific Configurations"); - hoconConfig.AppendLine("CreatorConfigs {"); - foreach (var creatorConfig in newConfig.CreatorConfigs) - { - hoconConfig.AppendLine($" \"{creatorConfig.Key}\" {{"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{creatorConfig.Value.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{creatorConfig.Value.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{creatorConfig.Value.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{creatorConfig.Value.MessageFileNameFormat}\""); - hoconConfig.AppendLine(" }"); - } - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Folder Settings"); - hoconConfig.AppendLine("Folder {"); - hoconConfig.AppendLine($" FolderPerPaidPost = {newConfig.FolderPerPaidPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPost = {newConfig.FolderPerPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPaidMessage = {newConfig.FolderPerPaidMessage.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerMessage = {newConfig.FolderPerMessage.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Subscription Settings"); - hoconConfig.AppendLine("Subscriptions {"); - hoconConfig.AppendLine($" IncludeExpiredSubscriptions = {newConfig.IncludeExpiredSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IncludeRestrictedSubscriptions = {newConfig.IncludeRestrictedSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IgnoredUsersListName = \"{newConfig.IgnoredUsersListName}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Interaction Settings"); - hoconConfig.AppendLine("Interaction {"); - hoconConfig.AppendLine($" NonInteractiveMode = {newConfig.NonInteractiveMode.ToString().ToLower()}"); - hoconConfig.AppendLine($" NonInteractiveModeListName = \"{newConfig.NonInteractiveModeListName}\""); - hoconConfig.AppendLine($" NonInteractiveModePurchasedTab = {newConfig.NonInteractiveModePurchasedTab.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Performance Settings"); - hoconConfig.AppendLine("Performance {"); - hoconConfig.AppendLine($" Timeout = {(newConfig.Timeout.HasValue ? newConfig.Timeout.Value : -1)}"); - hoconConfig.AppendLine($" LimitDownloadRate = {newConfig.LimitDownloadRate.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadLimitInMbPerSec = {newConfig.DownloadLimitInMbPerSec}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Logging/Debug Settings"); - hoconConfig.AppendLine("Logging {"); - hoconConfig.AppendLine($" LoggingLevel = \"{newConfig.LoggingLevel.ToString().ToLower()}\""); - hoconConfig.AppendLine("}"); - - File.WriteAllText("config.conf", hoconConfig.ToString()); - - string newConfigString = JsonConvert.SerializeObject(newConfig, Formatting.Indented); - - Log.Debug($"Config changed:"); - Log.Debug(newConfigString); + configService.UpdateConfig(newConfig); + await configService.SaveConfigurationAsync(); currentConfig = newConfig; if (configChanged) @@ -3267,7 +2747,7 @@ public class Program foreach (string name in typeof(LoggingLevel).GetEnumNames()) { string itemLabel = $"[red]{name}[/]"; - choices.Add(new(itemLabel, name == levelSwitch.MinimumLevel.ToString())); + choices.Add(new(itemLabel, name == loggingService.GetCurrentLoggingLevel().ToString())); } SelectionPrompt selectionPrompt = new SelectionPrompt() @@ -3288,7 +2768,6 @@ public class Program levelOption = levelOption.Replace("[red]", "").Replace("[/]", ""); LoggingLevel newLogLevel = (LoggingLevel)Enum.Parse(typeof(LoggingLevel), levelOption, true); - levelSwitch.MinimumLevel = (LogEventLevel)newLogLevel; Log.Debug($"Logging level changed to: {levelOption}"); @@ -3302,108 +2781,8 @@ public class Program currentConfig = newConfig; - // Dump new config in the log file - Log.Debug("Configuration:"); - string configString = JsonConvert.SerializeObject(currentConfig, Formatting.Indented); - Log.Debug(configString); - - var hoconConfig = new StringBuilder(); - hoconConfig.AppendLine("# Auth"); - hoconConfig.AppendLine("Auth {"); - hoconConfig.AppendLine($" DisableBrowserAuth = {newConfig.DisableBrowserAuth.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - hoconConfig.AppendLine("# External Tools"); - hoconConfig.AppendLine("External {"); - hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Download Settings"); - hoconConfig.AppendLine("Download {"); - hoconConfig.AppendLine(" Media {"); - hoconConfig.AppendLine($" DownloadAvatarHeaderPhoto = {newConfig.DownloadAvatarHeaderPhoto.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidPosts = {newConfig.DownloadPaidPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPosts = {newConfig.DownloadPosts.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadArchived = {newConfig.DownloadArchived.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStreams = {newConfig.DownloadStreams.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadStories = {newConfig.DownloadStories.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadHighlights = {newConfig.DownloadHighlights.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadMessages = {newConfig.DownloadMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPaidMessages = {newConfig.DownloadPaidMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadImages = {newConfig.DownloadImages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadVideos = {newConfig.DownloadVideos.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadAudios = {newConfig.DownloadAudios.ToString().ToLower()}"); - hoconConfig.AppendLine(" }"); - hoconConfig.AppendLine($" IgnoreOwnMessages = {newConfig.IgnoreOwnMessages.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPostsIncrementally = {newConfig.DownloadPostsIncrementally.ToString().ToLower()}"); - hoconConfig.AppendLine($" BypassContentForCreatorsWhoNoLongerExist = {newConfig.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDuplicatedMedia = {newConfig.DownloadDuplicatedMedia.ToString().ToLower()}"); - hoconConfig.AppendLine($" SkipAds = {newConfig.SkipAds.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadPath = \"{newConfig.DownloadPath}\""); - hoconConfig.AppendLine($" DownloadOnlySpecificDates = {newConfig.DownloadOnlySpecificDates.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadDateSelection = \"{newConfig.DownloadDateSelection.ToString().ToLower()}\""); - hoconConfig.AppendLine($" CustomDate = \"{newConfig.CustomDate?.ToString("yyyy-MM-dd")}\""); - hoconConfig.AppendLine($" ShowScrapeSize = {newConfig.ShowScrapeSize.ToString().ToLower()}"); - hoconConfig.AppendLine($" DisableTextSanitization = {newConfig.DisableTextSanitization.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadVideoResolution = \"{(newConfig.DownloadVideoResolution == VideoResolution.source ? "source" : newConfig.DownloadVideoResolution.ToString().TrimStart('_'))}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# File Settings"); - hoconConfig.AppendLine("File {"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{newConfig.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{newConfig.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{newConfig.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{newConfig.MessageFileNameFormat}\""); - hoconConfig.AppendLine($" RenameExistingFilesWhenCustomFormatIsSelected = {newConfig.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Creator-Specific Configurations"); - hoconConfig.AppendLine("CreatorConfigs {"); - foreach (var creatorConfig in newConfig.CreatorConfigs) - { - hoconConfig.AppendLine($" \"{creatorConfig.Key}\" {{"); - hoconConfig.AppendLine($" PaidPostFileNameFormat = \"{creatorConfig.Value.PaidPostFileNameFormat}\""); - hoconConfig.AppendLine($" PostFileNameFormat = \"{creatorConfig.Value.PostFileNameFormat}\""); - hoconConfig.AppendLine($" PaidMessageFileNameFormat = \"{creatorConfig.Value.PaidMessageFileNameFormat}\""); - hoconConfig.AppendLine($" MessageFileNameFormat = \"{creatorConfig.Value.MessageFileNameFormat}\""); - hoconConfig.AppendLine(" }"); - } - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Folder Settings"); - hoconConfig.AppendLine("Folder {"); - hoconConfig.AppendLine($" FolderPerPaidPost = {newConfig.FolderPerPaidPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPost = {newConfig.FolderPerPost.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerPaidMessage = {newConfig.FolderPerPaidMessage.ToString().ToLower()}"); - hoconConfig.AppendLine($" FolderPerMessage = {newConfig.FolderPerMessage.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Subscription Settings"); - hoconConfig.AppendLine("Subscriptions {"); - hoconConfig.AppendLine($" IncludeExpiredSubscriptions = {newConfig.IncludeExpiredSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IncludeRestrictedSubscriptions = {newConfig.IncludeRestrictedSubscriptions.ToString().ToLower()}"); - hoconConfig.AppendLine($" IgnoredUsersListName = \"{newConfig.IgnoredUsersListName}\""); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Interaction Settings"); - hoconConfig.AppendLine("Interaction {"); - hoconConfig.AppendLine($" NonInteractiveMode = {newConfig.NonInteractiveMode.ToString().ToLower()}"); - hoconConfig.AppendLine($" NonInteractiveModeListName = \"{newConfig.NonInteractiveModeListName}\""); - hoconConfig.AppendLine($" NonInteractiveModePurchasedTab = {newConfig.NonInteractiveModePurchasedTab.ToString().ToLower()}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Performance Settings"); - hoconConfig.AppendLine("Performance {"); - hoconConfig.AppendLine($" Timeout = {(newConfig.Timeout.HasValue ? newConfig.Timeout.Value : -1)}"); - hoconConfig.AppendLine($" LimitDownloadRate = {newConfig.LimitDownloadRate.ToString().ToLower()}"); - hoconConfig.AppendLine($" DownloadLimitInMbPerSec = {newConfig.DownloadLimitInMbPerSec}"); - hoconConfig.AppendLine("}"); - - hoconConfig.AppendLine("# Logging/Debug Settings"); - hoconConfig.AppendLine("Logging {"); - hoconConfig.AppendLine($" LoggingLevel = \"{newConfig.LoggingLevel.ToString().ToLower()}\""); - hoconConfig.AppendLine("}"); - - File.WriteAllText("config.conf", hoconConfig.ToString()); + configService.UpdateConfig(newConfig); + await configService.SaveConfigurationAsync(); if (configChanged) { @@ -3553,22 +2932,4 @@ public class Program } } - public static void ValidateFileNameFormat(string? format, string settingName) - { - if(!string.IsNullOrEmpty(format) && !format.Contains("{mediaId}", StringComparison.OrdinalIgnoreCase) && !format.Contains("{filename}", StringComparison.OrdinalIgnoreCase)) - { - AnsiConsole.Markup($"[red]{settingName} is not unique enough, please make sure you include either '{{mediaId}}' or '{{filename}}' to ensure that files are not overwritten with the same filename.[/]\n"); - AnsiConsole.Markup("[red]Press any key to continue.[/]\n"); - Console.ReadKey(); - Environment.Exit(2); - } - } - - public static VideoResolution ParseVideoResolution(string value) - { - if (value.Equals("source", StringComparison.OrdinalIgnoreCase)) - return VideoResolution.source; - - return Enum.Parse("_" + value, ignoreCase: true); - } } diff --git a/OF DL/Services/AuthService.cs b/OF DL/Services/AuthService.cs new file mode 100644 index 0000000..bf93ddd --- /dev/null +++ b/OF DL/Services/AuthService.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; +using OF_DL.Entities; +using OF_DL.Helpers.Interfaces; +using Serilog; + +namespace OF_DL.Services +{ + public class AuthService(IAuthHelper authHelper) : IAuthService + { + public Auth? CurrentAuth { get; set; } + + public async Task LoadFromFileAsync(string filePath = "auth.json") + { + try + { + if (!File.Exists(filePath)) + { + Log.Debug("Auth file not found: {FilePath}", filePath); + return false; + } + + var json = await File.ReadAllTextAsync(filePath); + CurrentAuth = JsonConvert.DeserializeObject(json); + Log.Debug("Auth file loaded and deserialized successfully"); + return CurrentAuth != null; + } + catch (Exception ex) + { + Log.Error(ex, "Failed to load auth from file"); + return false; + } + } + + public async Task LoadFromBrowserAsync() + { + try + { + bool runningInDocker = Environment.GetEnvironmentVariable("OFDL_DOCKER") != null; + + await authHelper.SetupBrowser(runningInDocker); + CurrentAuth = await authHelper.GetAuthFromBrowser(); + + return CurrentAuth != null; + } + catch (Exception ex) + { + Log.Error(ex, "Failed to load auth from browser"); + return false; + } + } + + public async Task SaveToFileAsync(string filePath = "auth.json") + { + if (CurrentAuth == null) + { + Log.Warning("Attempted to save null auth to file"); + return; + } + + try + { + var json = JsonConvert.SerializeObject(CurrentAuth, Formatting.Indented); + await File.WriteAllTextAsync(filePath, json); + Log.Debug($"Auth saved to file: {filePath}"); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to save auth to file"); + } + } + } +} diff --git a/OF DL/Services/ConfigService.cs b/OF DL/Services/ConfigService.cs new file mode 100644 index 0000000..67a418a --- /dev/null +++ b/OF DL/Services/ConfigService.cs @@ -0,0 +1,385 @@ +using Akka.Configuration; +using Newtonsoft.Json; +using OF_DL.Entities; +using Serilog; +using System.Text; +using OF_DL.Enumerations; +using Config = OF_DL.Entities.Config; + +namespace OF_DL.Services +{ + public class ConfigService(ILoggingService loggingService) : IConfigService + { + public Config? CurrentConfig { get; private set; } + public bool IsCliNonInteractive { get; private set; } + + public async Task LoadConfigurationAsync(string[] args) + { + try + { + IsCliNonInteractive = false; + + // Migrate from config.json to config.conf if needed + await MigrateFromJsonToConfAsync(); + + // Load config.conf or create default + if (File.Exists("config.conf")) + { + Log.Debug("config.conf located successfully"); + if (!await LoadConfigFromFileAsync("config.conf")) + { + return false; + } + } + else + { + Log.Debug("config.conf not found, creating default"); + await CreateDefaultConfigFileAsync(); + if (!await LoadConfigFromFileAsync("config.conf")) + { + return false; + } + } + + // Check for command-line arguments + if (args != null && args.Length > 0) + { + const string NON_INTERACTIVE_ARG = "--non-interactive"; + if (args.Any(a => a.Equals(NON_INTERACTIVE_ARG, StringComparison.OrdinalIgnoreCase))) + { + IsCliNonInteractive = true; + Log.Debug("NonInteractiveMode set via command line"); + } + + Log.Debug("Additional arguments:"); + foreach (string argument in args) + { + Log.Debug(argument); + } + } + + return true; + } + catch (Exception ex) + { + Log.Error(ex, "Configuration loading failed"); + return false; + } + } + + public async Task SaveConfigurationAsync(string filePath = "config.conf") + { + if (CurrentConfig == null) + { + Log.Warning("Attempted to save null config to file"); + return; + } + + try + { + var hoconConfig = BuildHoconFromConfig(CurrentConfig); + await File.WriteAllTextAsync(filePath, hoconConfig); + Log.Debug($"Config saved to file: {filePath}"); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to save config to file"); + } + } + + public void UpdateConfig(Config newConfig) + { + CurrentConfig = newConfig; + + // Update logging level + loggingService.UpdateLoggingLevel(newConfig.LoggingLevel); + + // Apply text sanitization preference globally + OF_DL.Utils.XmlUtils.Passthrough = newConfig.DisableTextSanitization; + + Log.Debug("Configuration updated"); + string configString = JsonConvert.SerializeObject(newConfig, Formatting.Indented); + Log.Debug(configString); + } + + private async Task MigrateFromJsonToConfAsync() + { + if (!File.Exists("config.json")) + return; + + try + { + Log.Debug("config.json found, migrating to config.conf"); + string jsonText = await File.ReadAllTextAsync("config.json"); + var jsonConfig = JsonConvert.DeserializeObject(jsonText); + + if (jsonConfig != null) + { + var hoconConfig = BuildHoconFromConfig(jsonConfig); + await File.WriteAllTextAsync("config.conf", hoconConfig); + File.Delete("config.json"); + Log.Information("config.conf created successfully from config.json"); + } + } + catch (Exception ex) + { + Log.Error(ex, "Failed to migrate config.json to config.conf"); + throw; + } + } + + private async Task LoadConfigFromFileAsync(string filePath) + { + try + { + string hoconText = await File.ReadAllTextAsync(filePath); + var hoconConfig = ConfigurationFactory.ParseString(hoconText); + + CurrentConfig = new Config + { + // Auth + DisableBrowserAuth = hoconConfig.GetBoolean("Auth.DisableBrowserAuth"), + + // FFmpeg Settings + FFmpegPath = hoconConfig.GetString("External.FFmpegPath"), + + // Download Settings + DownloadAvatarHeaderPhoto = hoconConfig.GetBoolean("Download.Media.DownloadAvatarHeaderPhoto"), + DownloadPaidPosts = hoconConfig.GetBoolean("Download.Media.DownloadPaidPosts"), + DownloadPosts = hoconConfig.GetBoolean("Download.Media.DownloadPosts"), + DownloadArchived = hoconConfig.GetBoolean("Download.Media.DownloadArchived"), + DownloadStreams = hoconConfig.GetBoolean("Download.Media.DownloadStreams"), + DownloadStories = hoconConfig.GetBoolean("Download.Media.DownloadStories"), + DownloadHighlights = hoconConfig.GetBoolean("Download.Media.DownloadHighlights"), + DownloadMessages = hoconConfig.GetBoolean("Download.Media.DownloadMessages"), + DownloadPaidMessages = hoconConfig.GetBoolean("Download.Media.DownloadPaidMessages"), + DownloadImages = hoconConfig.GetBoolean("Download.Media.DownloadImages"), + DownloadVideos = hoconConfig.GetBoolean("Download.Media.DownloadVideos"), + DownloadAudios = hoconConfig.GetBoolean("Download.Media.DownloadAudios"), + IgnoreOwnMessages = hoconConfig.GetBoolean("Download.IgnoreOwnMessages"), + DownloadPostsIncrementally = hoconConfig.GetBoolean("Download.DownloadPostsIncrementally"), + BypassContentForCreatorsWhoNoLongerExist = hoconConfig.GetBoolean("Download.BypassContentForCreatorsWhoNoLongerExist"), + DownloadDuplicatedMedia = hoconConfig.GetBoolean("Download.DownloadDuplicatedMedia"), + SkipAds = hoconConfig.GetBoolean("Download.SkipAds"), + DownloadPath = hoconConfig.GetString("Download.DownloadPath"), + DownloadOnlySpecificDates = hoconConfig.GetBoolean("Download.DownloadOnlySpecificDates"), + DownloadDateSelection = Enum.Parse(hoconConfig.GetString("Download.DownloadDateSelection"), true), + CustomDate = !string.IsNullOrWhiteSpace(hoconConfig.GetString("Download.CustomDate")) ? DateTime.Parse(hoconConfig.GetString("Download.CustomDate")) : null, + ShowScrapeSize = hoconConfig.GetBoolean("Download.ShowScrapeSize"), + DisableTextSanitization = bool.TryParse(hoconConfig.GetString("Download.DisableTextSanitization", "false"), out var dts) ? dts : false, + DownloadVideoResolution = ParseVideoResolution(hoconConfig.GetString("Download.DownloadVideoResolution", "source")), + + // File Settings + PaidPostFileNameFormat = hoconConfig.GetString("File.PaidPostFileNameFormat"), + PostFileNameFormat = hoconConfig.GetString("File.PostFileNameFormat"), + PaidMessageFileNameFormat = hoconConfig.GetString("File.PaidMessageFileNameFormat"), + MessageFileNameFormat = hoconConfig.GetString("File.MessageFileNameFormat"), + RenameExistingFilesWhenCustomFormatIsSelected = hoconConfig.GetBoolean("File.RenameExistingFilesWhenCustomFormatIsSelected"), + + // Folder Settings + FolderPerPaidPost = hoconConfig.GetBoolean("Folder.FolderPerPaidPost"), + FolderPerPost = hoconConfig.GetBoolean("Folder.FolderPerPost"), + FolderPerPaidMessage = hoconConfig.GetBoolean("Folder.FolderPerPaidMessage"), + FolderPerMessage = hoconConfig.GetBoolean("Folder.FolderPerMessage"), + + // Subscription Settings + IncludeExpiredSubscriptions = hoconConfig.GetBoolean("Subscriptions.IncludeExpiredSubscriptions"), + IncludeRestrictedSubscriptions = hoconConfig.GetBoolean("Subscriptions.IncludeRestrictedSubscriptions"), + IgnoredUsersListName = hoconConfig.GetString("Subscriptions.IgnoredUsersListName"), + + // Interaction Settings + NonInteractiveMode = hoconConfig.GetBoolean("Interaction.NonInteractiveMode"), + NonInteractiveModeListName = hoconConfig.GetString("Interaction.NonInteractiveModeListName"), + NonInteractiveModePurchasedTab = hoconConfig.GetBoolean("Interaction.NonInteractiveModePurchasedTab"), + + // Performance Settings + Timeout = string.IsNullOrWhiteSpace(hoconConfig.GetString("Performance.Timeout")) ? -1 : hoconConfig.GetInt("Performance.Timeout"), + LimitDownloadRate = hoconConfig.GetBoolean("Performance.LimitDownloadRate"), + DownloadLimitInMbPerSec = hoconConfig.GetInt("Performance.DownloadLimitInMbPerSec"), + + // Logging/Debug Settings + LoggingLevel = Enum.Parse(hoconConfig.GetString("Logging.LoggingLevel"), true) + }; + + // Validate file name formats + ValidateFileNameFormat(CurrentConfig.PaidPostFileNameFormat, "PaidPostFileNameFormat"); + ValidateFileNameFormat(CurrentConfig.PostFileNameFormat, "PostFileNameFormat"); + ValidateFileNameFormat(CurrentConfig.PaidMessageFileNameFormat, "PaidMessageFileNameFormat"); + ValidateFileNameFormat(CurrentConfig.MessageFileNameFormat, "MessageFileNameFormat"); + + // Load creator-specific configs + var creatorConfigsSection = hoconConfig.GetConfig("CreatorConfigs"); + if (creatorConfigsSection != null) + { + foreach (var key in creatorConfigsSection.AsEnumerable()) + { + var creatorKey = key.Key; + var creatorHocon = creatorConfigsSection.GetConfig(creatorKey); + if (!CurrentConfig.CreatorConfigs.ContainsKey(creatorKey) && creatorHocon != null) + { + CurrentConfig.CreatorConfigs.Add(key.Key, new CreatorConfig + { + PaidPostFileNameFormat = creatorHocon.GetString("PaidPostFileNameFormat"), + PostFileNameFormat = creatorHocon.GetString("PostFileNameFormat"), + PaidMessageFileNameFormat = creatorHocon.GetString("PaidMessageFileNameFormat"), + MessageFileNameFormat = creatorHocon.GetString("MessageFileNameFormat") + }); + + ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PaidPostFileNameFormat, $"{key.Key}.PaidPostFileNameFormat"); + ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PostFileNameFormat, $"{key.Key}.PostFileNameFormat"); + ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PaidMessageFileNameFormat, $"{key.Key}.PaidMessageFileNameFormat"); + ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].MessageFileNameFormat, $"{key.Key}.MessageFileNameFormat"); + } + } + } + + // Update logging level + loggingService.UpdateLoggingLevel(CurrentConfig.LoggingLevel); + + // Apply text sanitization preference globally + OF_DL.Utils.XmlUtils.Passthrough = CurrentConfig.DisableTextSanitization; + + Log.Debug("Configuration loaded successfully"); + string configString = JsonConvert.SerializeObject(CurrentConfig, Formatting.Indented); + Log.Debug(configString); + + return true; + } + catch (Exception ex) + { + Log.Error(ex, "Failed to parse config file"); + return false; + } + } + + private async Task CreateDefaultConfigFileAsync() + { + Config defaultConfig = new Config(); + var hoconConfig = BuildHoconFromConfig(defaultConfig); + await File.WriteAllTextAsync("config.conf", hoconConfig); + Log.Information("Created default config.conf file"); + } + + private string BuildHoconFromConfig(Config config) + { + var hocon = new StringBuilder(); + + hocon.AppendLine("# Auth"); + hocon.AppendLine("Auth {"); + hocon.AppendLine($" DisableBrowserAuth = {config.DisableBrowserAuth.ToString().ToLower()}"); + hocon.AppendLine("}"); + + hocon.AppendLine("# External Tools"); + hocon.AppendLine("External {"); + hocon.AppendLine($" FFmpegPath = \"{config.FFmpegPath}\""); + hocon.AppendLine("}"); + + hocon.AppendLine("# Download Settings"); + hocon.AppendLine("Download {"); + hocon.AppendLine(" Media {"); + hocon.AppendLine($" DownloadAvatarHeaderPhoto = {config.DownloadAvatarHeaderPhoto.ToString().ToLower()}"); + hocon.AppendLine($" DownloadPaidPosts = {config.DownloadPaidPosts.ToString().ToLower()}"); + hocon.AppendLine($" DownloadPosts = {config.DownloadPosts.ToString().ToLower()}"); + hocon.AppendLine($" DownloadArchived = {config.DownloadArchived.ToString().ToLower()}"); + hocon.AppendLine($" DownloadStreams = {config.DownloadStreams.ToString().ToLower()}"); + hocon.AppendLine($" DownloadStories = {config.DownloadStories.ToString().ToLower()}"); + hocon.AppendLine($" DownloadHighlights = {config.DownloadHighlights.ToString().ToLower()}"); + hocon.AppendLine($" DownloadMessages = {config.DownloadMessages.ToString().ToLower()}"); + hocon.AppendLine($" DownloadPaidMessages = {config.DownloadPaidMessages.ToString().ToLower()}"); + hocon.AppendLine($" DownloadImages = {config.DownloadImages.ToString().ToLower()}"); + hocon.AppendLine($" DownloadVideos = {config.DownloadVideos.ToString().ToLower()}"); + hocon.AppendLine($" DownloadAudios = {config.DownloadAudios.ToString().ToLower()}"); + hocon.AppendLine(" }"); + hocon.AppendLine($" IgnoreOwnMessages = {config.IgnoreOwnMessages.ToString().ToLower()}"); + hocon.AppendLine($" DownloadPostsIncrementally = {config.DownloadPostsIncrementally.ToString().ToLower()}"); + hocon.AppendLine($" BypassContentForCreatorsWhoNoLongerExist = {config.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}"); + hocon.AppendLine($" DownloadDuplicatedMedia = {config.DownloadDuplicatedMedia.ToString().ToLower()}"); + hocon.AppendLine($" SkipAds = {config.SkipAds.ToString().ToLower()}"); + hocon.AppendLine($" DownloadPath = \"{config.DownloadPath}\""); + hocon.AppendLine($" DownloadOnlySpecificDates = {config.DownloadOnlySpecificDates.ToString().ToLower()}"); + hocon.AppendLine($" DownloadDateSelection = \"{config.DownloadDateSelection.ToString().ToLower()}\""); + hocon.AppendLine($" CustomDate = \"{config.CustomDate?.ToString("yyyy-MM-dd")}\""); + hocon.AppendLine($" ShowScrapeSize = {config.ShowScrapeSize.ToString().ToLower()}"); + hocon.AppendLine($" DisableTextSanitization = {config.DisableTextSanitization.ToString().ToLower()}"); + hocon.AppendLine($" DownloadVideoResolution = \"{(config.DownloadVideoResolution == VideoResolution.source ? "source" : config.DownloadVideoResolution.ToString().TrimStart('_'))}\""); + hocon.AppendLine("}"); + + hocon.AppendLine("# File Settings"); + hocon.AppendLine("File {"); + hocon.AppendLine($" PaidPostFileNameFormat = \"{config.PaidPostFileNameFormat}\""); + hocon.AppendLine($" PostFileNameFormat = \"{config.PostFileNameFormat}\""); + hocon.AppendLine($" PaidMessageFileNameFormat = \"{config.PaidMessageFileNameFormat}\""); + hocon.AppendLine($" MessageFileNameFormat = \"{config.MessageFileNameFormat}\""); + hocon.AppendLine($" RenameExistingFilesWhenCustomFormatIsSelected = {config.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}"); + hocon.AppendLine("}"); + + hocon.AppendLine("# Creator-Specific Configurations"); + hocon.AppendLine("CreatorConfigs {"); + foreach (var creatorConfig in config.CreatorConfigs) + { + hocon.AppendLine($" \"{creatorConfig.Key}\" {{"); + hocon.AppendLine($" PaidPostFileNameFormat = \"{creatorConfig.Value.PaidPostFileNameFormat}\""); + hocon.AppendLine($" PostFileNameFormat = \"{creatorConfig.Value.PostFileNameFormat}\""); + hocon.AppendLine($" PaidMessageFileNameFormat = \"{creatorConfig.Value.PaidMessageFileNameFormat}\""); + hocon.AppendLine($" MessageFileNameFormat = \"{creatorConfig.Value.MessageFileNameFormat}\""); + hocon.AppendLine(" }"); + } + hocon.AppendLine("}"); + + hocon.AppendLine("# Folder Settings"); + hocon.AppendLine("Folder {"); + hocon.AppendLine($" FolderPerPaidPost = {config.FolderPerPaidPost.ToString().ToLower()}"); + hocon.AppendLine($" FolderPerPost = {config.FolderPerPost.ToString().ToLower()}"); + hocon.AppendLine($" FolderPerPaidMessage = {config.FolderPerPaidMessage.ToString().ToLower()}"); + hocon.AppendLine($" FolderPerMessage = {config.FolderPerMessage.ToString().ToLower()}"); + hocon.AppendLine("}"); + + hocon.AppendLine("# Subscription Settings"); + hocon.AppendLine("Subscriptions {"); + hocon.AppendLine($" IncludeExpiredSubscriptions = {config.IncludeExpiredSubscriptions.ToString().ToLower()}"); + hocon.AppendLine($" IncludeRestrictedSubscriptions = {config.IncludeRestrictedSubscriptions.ToString().ToLower()}"); + hocon.AppendLine($" IgnoredUsersListName = \"{config.IgnoredUsersListName}\""); + hocon.AppendLine("}"); + + hocon.AppendLine("# Interaction Settings"); + hocon.AppendLine("Interaction {"); + hocon.AppendLine($" NonInteractiveMode = {config.NonInteractiveMode.ToString().ToLower()}"); + hocon.AppendLine($" NonInteractiveModeListName = \"{config.NonInteractiveModeListName}\""); + hocon.AppendLine($" NonInteractiveModePurchasedTab = {config.NonInteractiveModePurchasedTab.ToString().ToLower()}"); + hocon.AppendLine("}"); + + hocon.AppendLine("# Performance Settings"); + hocon.AppendLine("Performance {"); + hocon.AppendLine($" Timeout = {(config.Timeout.HasValue ? config.Timeout.Value : -1)}"); + hocon.AppendLine($" LimitDownloadRate = {config.LimitDownloadRate.ToString().ToLower()}"); + hocon.AppendLine($" DownloadLimitInMbPerSec = {config.DownloadLimitInMbPerSec}"); + hocon.AppendLine("}"); + + hocon.AppendLine("# Logging/Debug Settings"); + hocon.AppendLine("Logging {"); + hocon.AppendLine($" LoggingLevel = \"{config.LoggingLevel.ToString().ToLower()}\""); + hocon.AppendLine("}"); + + return hocon.ToString(); + } + + private void ValidateFileNameFormat(string? format, string settingName) + { + if (!string.IsNullOrEmpty(format) && + !format.Contains("{mediaId}", StringComparison.OrdinalIgnoreCase) && + !format.Contains("{filename}", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException( + $"{settingName} is not unique enough. Please include either '{{mediaId}}' or '{{filename}}' to ensure files are not overwritten."); + } + } + + private VideoResolution ParseVideoResolution(string value) + { + if (value.Equals("source", StringComparison.OrdinalIgnoreCase)) + return VideoResolution.source; + + return Enum.Parse("_" + value, ignoreCase: true); + } + } +} diff --git a/OF DL/Services/IAuthService.cs b/OF DL/Services/IAuthService.cs new file mode 100644 index 0000000..8d14486 --- /dev/null +++ b/OF DL/Services/IAuthService.cs @@ -0,0 +1,12 @@ +using OF_DL.Entities; + +namespace OF_DL.Services +{ + public interface IAuthService + { + Auth? CurrentAuth { get; set; } + Task LoadFromFileAsync(string filePath = "auth.json"); + Task LoadFromBrowserAsync(); + Task SaveToFileAsync(string filePath = "auth.json"); + } +} diff --git a/OF DL/Services/IConfigService.cs b/OF DL/Services/IConfigService.cs new file mode 100644 index 0000000..773e82c --- /dev/null +++ b/OF DL/Services/IConfigService.cs @@ -0,0 +1,13 @@ +using OF_DL.Entities; + +namespace OF_DL.Services +{ + public interface IConfigService + { + Config? CurrentConfig { get; } + bool IsCliNonInteractive { get; } + Task LoadConfigurationAsync(string[] args); + Task SaveConfigurationAsync(string filePath = "config.conf"); + void UpdateConfig(Config newConfig); + } +} diff --git a/OF DL/Services/ILoggingService.cs b/OF DL/Services/ILoggingService.cs new file mode 100644 index 0000000..b0516ef --- /dev/null +++ b/OF DL/Services/ILoggingService.cs @@ -0,0 +1,12 @@ +using OF_DL.Enumerations; +using Serilog.Core; + +namespace OF_DL.Services +{ + public interface ILoggingService + { + LoggingLevelSwitch LevelSwitch { get; } + void UpdateLoggingLevel(LoggingLevel newLevel); + LoggingLevel GetCurrentLoggingLevel(); + } +} diff --git a/OF DL/Services/LoggingService.cs b/OF DL/Services/LoggingService.cs new file mode 100644 index 0000000..c65511a --- /dev/null +++ b/OF DL/Services/LoggingService.cs @@ -0,0 +1,42 @@ +using OF_DL.Enumerations; +using Serilog; +using Serilog.Core; +using Serilog.Events; + +namespace OF_DL.Services +{ + public class LoggingService : ILoggingService + { + public LoggingLevelSwitch LevelSwitch { get; } + + public LoggingService() + { + LevelSwitch = new LoggingLevelSwitch(); + InitializeLogger(); + } + + private void InitializeLogger() + { + // Set the initial level to Error (until we've read from config) + LevelSwitch.MinimumLevel = LogEventLevel.Error; + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.ControlledBy(LevelSwitch) + .WriteTo.File("logs/OFDL.txt", rollingInterval: RollingInterval.Day) + .CreateLogger(); + + Log.Debug("Logging service initialized"); + } + + public void UpdateLoggingLevel(LoggingLevel newLevel) + { + LevelSwitch.MinimumLevel = (LogEventLevel)newLevel; + Log.Debug("Logging level updated to: {LoggingLevel}", newLevel); + } + + public LoggingLevel GetCurrentLoggingLevel() + { + return (LoggingLevel)LevelSwitch.MinimumLevel; + } + } +}