Major refactor #141

Merged
sim0n00ps merged 55 commits from whimsical-c4lic0/OF-DL:refactor-architecture into master 2026-02-13 00:21:58 +00:00
7 changed files with 649 additions and 752 deletions
Showing only changes of commit d7bae3e260 - Show all commits

View File

@ -9,60 +9,36 @@ 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<long> 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()
{
var authService = serviceProvider.GetRequiredService<IAuthService>();
bool runningInDocker = Environment.GetEnvironmentVariable("OFDL_DOCKER") != null;
try
{
Task setupBrowserTask = authHelper.SetupBrowser(runningInDocker);
Task.Delay(1000).Wait();
if (!setupBrowserTask.IsCompleted)
{
// Show initial message
AnsiConsole.MarkupLine($"[yellow]Downloading dependencies. Please wait ...[/]");
}
setupBrowserTask.Wait();
Task<Auth?> getAuthTask = authHelper.GetAuthFromBrowser();
Task.Delay(5000).Wait();
if (!getAuthTask.IsCompleted)
{
// Show instructions based on the environment
await Task.Delay(5000);
if (runningInDocker)
{
AnsiConsole.MarkupLine(
@ -78,22 +54,11 @@ public class Program
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;
}
catch (Exception e)
{
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);
}
// Load auth from browser using the service
bool success = await authService.LoadFromBrowserAsync();
if (auth == null)
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.[/]");
@ -104,49 +69,65 @@ public class Program
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<ILoggingService, LoggingService>();
services.AddSingleton<IConfigService, ConfigService>();
ServiceProvider tempServiceProvider = services.BuildServiceProvider();
// Load configuration using ConfigService
IConfigService configService = tempServiceProvider.GetRequiredService<IConfigService>();
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<ILoggingService>());
var serviceProvider = services.BuildServiceProvider();
// Get the Program instance and run
var program = serviceProvider.GetRequiredService<Program>();
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<IDownloadConfig>(config);
services.AddSingleton<IFileNameFormatConfig>(config);
services.AddSingleton(loggingService);
services.AddSingleton<IConfigService, ConfigService>();
services.AddSingleton<IAuthService, AuthService>();
services.AddTransient<IAuthHelper, AuthHelper>();
services.AddTransient<IAPIHelper, APIHelper>();
services.AddTransient<IDBHelper, DBHelper>();
@ -156,409 +137,11 @@ public class Program
services.AddSingleton<Program>();
}
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<Entities.Config>(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<DownloadDateSelection>(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<LoggingLevel>(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<Entities.Config>();
var authService = serviceProvider.GetRequiredService<IAuthService>();
try
{
var os = Environment.OSVersion;
@ -650,17 +233,13 @@ 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
{
auth = JsonConvert.DeserializeObject<Auth>(await File.ReadAllTextAsync("auth.json"));
Log.Debug("Auth file found and deserialized");
}
catch (Exception _)
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)
{
@ -693,7 +272,6 @@ public class Program
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<APIHelper>(serviceProvider, auth);
var apiHelper = ActivatorUtilities.CreateInstance<APIHelper>(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<IDBHelper>();
IConfigService configService = serviceProvider.GetRequiredService<IConfigService>();
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<ILoggingService>();
var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists, configService, loggingService);
Config = userSelectionResult.updatedConfig;
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(userSelectionResult.IsExit, userSelectionResult.selectedUsers);
@ -2991,7 +2571,7 @@ public class Program
}
}
public static async Task<(bool IsExit, Dictionary<string, long>? selectedUsers, Entities.Config? updatedConfig)> HandleUserSelection(IAPIHelper apiHelper, Entities.Config currentConfig, Dictionary<string, long> users, Dictionary<string, long> lists)
public static async Task<(bool IsExit, Dictionary<string, long>? selectedUsers, Entities.Config? updatedConfig)> HandleUserSelection(IAPIHelper apiHelper, Entities.Config currentConfig, Dictionary<string, long> users, Dictionary<string, long> lists, IConfigService configService, ILoggingService loggingService)
{
bool hasSelectedUsers = false;
Dictionary<string, long> selectedUsers = new Dictionary<string, long>();
@ -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<string> selectionPrompt = new SelectionPrompt<string>()
@ -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<VideoResolution>("_" + value, ignoreCase: true);
}
}

View File

@ -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<bool> 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<Auth>(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<bool> 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");
}
}
}
}

View File

@ -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<bool> 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<Config>(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<bool> 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<DownloadDateSelection>(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<LoggingLevel>(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<VideoResolution>("_" + value, ignoreCase: true);
}
}
}

View File

@ -0,0 +1,12 @@
using OF_DL.Entities;
namespace OF_DL.Services
{
public interface IAuthService
{
Auth? CurrentAuth { get; set; }
Task<bool> LoadFromFileAsync(string filePath = "auth.json");
Task<bool> LoadFromBrowserAsync();
Task SaveToFileAsync(string filePath = "auth.json");
}
}

View File

@ -0,0 +1,13 @@
using OF_DL.Entities;
namespace OF_DL.Services
{
public interface IConfigService
{
Config? CurrentConfig { get; }
bool IsCliNonInteractive { get; }
Task<bool> LoadConfigurationAsync(string[] args);
Task SaveConfigurationAsync(string filePath = "config.conf");
void UpdateConfig(Config newConfig);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}