From 18fe2580adb8243ed3fc6e4bc4da09847c34c6b7 Mon Sep 17 00:00:00 2001 From: whimsical-c4lic0 Date: Tue, 4 Nov 2025 18:18:29 -0600 Subject: [PATCH 1/6] Fix purchased tab API endpoints --- OF DL/Helpers/APIHelper.cs | 8 +++++--- OF DL/Program.cs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OF DL/Helpers/APIHelper.cs b/OF DL/Helpers/APIHelper.cs index 80bd932..80c595c 100644 --- a/OF DL/Helpers/APIHelper.cs +++ b/OF DL/Helpers/APIHelper.cs @@ -1181,7 +1181,7 @@ public class APIHelper : IAPIHelper } } break; - + } } else if (medium.canView && medium.files != null && medium.files.drm != null) @@ -2078,7 +2078,8 @@ public class APIHelper : IAPIHelper { { "limit", post_limit.ToString() }, { "order", "publish_date_desc" }, - { "format", "infinite" } + { "format", "infinite" }, + { "skip_users", "all" } }; var body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient(config)); @@ -2251,7 +2252,8 @@ public class APIHelper : IAPIHelper { { "limit", post_limit.ToString() }, { "order", "publish_date_desc" }, - { "format", "infinite" } + { "format", "infinite" }, + { "skip_users", "all" } }; var body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient(config)); diff --git a/OF DL/Program.cs b/OF DL/Program.cs index 539e3d4..70d66cd 100644 --- a/OF DL/Program.cs +++ b/OF DL/Program.cs @@ -983,7 +983,7 @@ public class Program } else if (hasSelectedUsersKVP.Key && hasSelectedUsersKVP.Value != null && hasSelectedUsersKVP.Value.ContainsKey("PurchasedTab")) { - Dictionary purchasedTabUsers = await m_ApiHelper.GetPurchasedTabUsers("/posts/paid", Config, users); + Dictionary purchasedTabUsers = await m_ApiHelper.GetPurchasedTabUsers("/posts/paid/all", Config, users); AnsiConsole.Markup($"[red]Checking folders for Users in Purchased Tab\n[/]"); foreach (KeyValuePair user in purchasedTabUsers) { @@ -1030,7 +1030,7 @@ public class Program Log.Debug($"Download path: {p}"); - List purchasedTabCollections = await m_ApiHelper.GetPurchasedTab("/posts/paid", p, Config, users); + List purchasedTabCollections = await m_ApiHelper.GetPurchasedTab("/posts/paid/all", p, Config, users); foreach(PurchasedTabCollection purchasedTabCollection in purchasedTabCollections) { AnsiConsole.Markup($"[red]\nScraping Data for {purchasedTabCollection.Username}\n[/]"); From 2bddedb6445ecb4fa5a896eedf5d5402f467bfbb Mon Sep 17 00:00:00 2001 From: "Spam.China" Date: Sat, 29 Nov 2025 15:01:21 +0100 Subject: [PATCH 2/6] fix: object reference not set by adding retry for GetDecryptionKeyOFDL --- OF DL/Helpers/APIHelper.cs | 47 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/OF DL/Helpers/APIHelper.cs b/OF DL/Helpers/APIHelper.cs index 80c595c..a1e534f 100644 --- a/OF DL/Helpers/APIHelper.cs +++ b/OF DL/Helpers/APIHelper.cs @@ -31,6 +31,8 @@ public class APIHelper : IAPIHelper private readonly Auth auth; private static DateTime? cachedDynamicRulesExpiration; private static DynamicRules? cachedDynamicRules; + private const int MaxAttempts = 30; + private const int DelayBetweenAttempts = 3000; static APIHelper() { @@ -2674,13 +2676,10 @@ public class APIHelper : IAPIHelper return DateTime.Now; } - public async Task GetDecryptionKeyCDRMProject(Dictionary drmHeaders, string licenceURL, string pssh) { Log.Debug("Calling GetDecryptionKey"); - const int maxAttempts = 30; - const int delayBetweenAttempts = 3000; int attempt = 0; try @@ -2701,7 +2700,7 @@ public class APIHelper : IAPIHelper Log.Debug($"Posting to CDRM Project: {json}"); - while (attempt < maxAttempts) + while (attempt < MaxAttempts) { attempt++; @@ -2727,19 +2726,19 @@ public class APIHelper : IAPIHelper } else { - Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {maxAttempts}"); - if (attempt < maxAttempts) + Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {MaxAttempts}"); + if (attempt < MaxAttempts) { - await Task.Delay(delayBetweenAttempts); + await Task.Delay(DelayBetweenAttempts); } } } else { - Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {maxAttempts}"); - if (attempt < maxAttempts) + Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {MaxAttempts}"); + if (attempt < MaxAttempts) { - await Task.Delay(delayBetweenAttempts); + await Task.Delay(DelayBetweenAttempts); } } } @@ -2764,11 +2763,10 @@ public class APIHelper : IAPIHelper { Log.Debug("Calling GetDecryptionOFDL"); - try { - string dcValue = string.Empty; HttpClient client = new(); + int attempt = 0; OFDLRequest ofdlRequest = new OFDLRequest { @@ -2781,17 +2779,27 @@ public class APIHelper : IAPIHelper Log.Debug($"Posting to ofdl.tools: {json}"); - HttpRequestMessage request = new(HttpMethod.Post, "https://ofdl.tools/WV") + while (attempt < MaxAttempts) { - Content = new StringContent(json, Encoding.UTF8, "application/json") - }; + attempt++; - using var response = await client.SendAsync(request); + HttpRequestMessage request = new(HttpMethod.Post, "https://ofdl.tools/WV") + { + Content = new StringContent(json, Encoding.UTF8, "application/json") + }; + + using var response = await client.SendAsync(request); + + if (!response.IsSuccessStatusCode) + continue; - if (response.IsSuccessStatusCode) - { string body = await response.Content.ReadAsStringAsync(); - return body; + + if (!body.TrimStart().StartsWith('{')) + return body; + + Log.Debug($"Received JSON object instead of string. Retrying... Attempt {attempt} of {MaxAttempts}"); + await Task.Delay(DelayBetweenAttempts); } } catch (Exception ex) @@ -2805,6 +2813,7 @@ public class APIHelper : IAPIHelper Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace); } } + return null; } From 181ea8eef1e65b2c68d7425f907a22d0ff9de5f0 Mon Sep 17 00:00:00 2001 From: Melithine Date: Sat, 29 Nov 2025 13:46:43 -0800 Subject: [PATCH 3/6] Add ffmpeg version to startup output and log file. --- OF DL/Program.cs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/OF DL/Program.cs b/OF DL/Program.cs index 70d66cd..2d1dbef 100644 --- a/OF DL/Program.cs +++ b/OF DL/Program.cs @@ -748,6 +748,61 @@ public class Program { config.FFmpegPath = config.FFmpegPath.Replace(@"\", @"\\"); } + + // Get FFmpeg version + try + { + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = config.FFmpegPath, + Arguments = "-version", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using (var process = System.Diagnostics.Process.Start(processStartInfo)) + { + if (process != null) + { + string output = await process.StandardOutput.ReadToEndAsync(); + await process.WaitForExitAsync(); + + // Log full output + Log.Information("FFmpeg version output:\n{Output}", output); + + // Parse first line for console output + string firstLine = output.Split('\n')[0].Trim(); + if (firstLine.StartsWith("ffmpeg version")) + { + // Extract version string (text between "ffmpeg version " and " Copyright") + int versionStart = "ffmpeg version ".Length; + int copyrightIndex = firstLine.IndexOf(" Copyright"); + if (copyrightIndex > versionStart) + { + string version = firstLine.Substring(versionStart, copyrightIndex - versionStart); + AnsiConsole.Markup($"[green]ffmpeg version detected as {version}[/]\n"); + } + else + { + // Fallback if Copyright not found + string version = firstLine.Substring(versionStart); + AnsiConsole.Markup($"[green]ffmpeg version detected as {version}[/]\n"); + } + } + else + { + AnsiConsole.Markup($"[yellow]ffmpeg version could not be parsed[/]\n"); + } + } + } + } + catch (Exception ex) + { + Log.Warning(ex, "Failed to get FFmpeg version"); + AnsiConsole.Markup($"[yellow]Could not retrieve ffmpeg version[/]\n"); + } } else { From e98c5f74cf2d7ec240bd3d73bd162dafefb522f2 Mon Sep 17 00:00:00 2001 From: Melithine Date: Sat, 29 Nov 2025 14:16:36 -0800 Subject: [PATCH 4/6] Add a 30s timeout to the Gitea version check. --- OF DL/Helpers/VersionHelper.cs | 15 +++++--- OF DL/Program.cs | 70 ++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/OF DL/Helpers/VersionHelper.cs b/OF DL/Helpers/VersionHelper.cs index c21d835..dc2b163 100644 --- a/OF DL/Helpers/VersionHelper.cs +++ b/OF DL/Helpers/VersionHelper.cs @@ -6,14 +6,15 @@ namespace OF_DL.Helpers; public static class VersionHelper { - public static string? GetLatestReleaseTag() + private static readonly HttpClient httpClient = new HttpClient(); + private const string url = "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest"; + + public static async Task GetLatestReleaseTag(CancellationToken cancellationToken = default) { Log.Debug("Calling GetLatestReleaseTag"); try { - HttpClient client = new(); - HttpRequestMessage request = new(HttpMethod.Get, "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest"); - using var response = client.Send(request); + var response = await httpClient.GetAsync(url, cancellationToken); if (!response.IsSuccessStatusCode) { @@ -21,7 +22,7 @@ public static class VersionHelper return null; } - var body = response.Content.ReadAsStringAsync().Result; + var body = await response.Content.ReadAsStringAsync(); Log.Debug("GetLatestReleaseTag API Response: "); Log.Debug(body); @@ -36,6 +37,10 @@ public static class VersionHelper return versionCheckResponse.TagName; } + catch (OperationCanceledException) + { + throw; // Rethrow timeout exceptions to be handled by the caller + } catch (Exception ex) { Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace); diff --git a/OF DL/Program.cs b/OF DL/Program.cs index 70d66cd..f2ec7dd 100644 --- a/OF DL/Program.cs +++ b/OF DL/Program.cs @@ -526,36 +526,50 @@ public class Program // Only run the version check if not in DEBUG mode #if !DEBUG Version localVersion = Assembly.GetEntryAssembly()?.GetName().Version; //Only tested with numeric values. - String? latestReleaseTag = VersionHelper.GetLatestReleaseTag(); - if (latestReleaseTag == null) - { - AnsiConsole.Markup("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]"); - Log.Error("Failed to get the latest release tag."); - } - else - { - Version latestGiteaRelease = new Version(latestReleaseTag.Replace("OFDLV", "")); + // Create a cancellation token with 30 second timeout + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + String? latestReleaseTag = null; - // Compare the Versions - int versionComparison = localVersion.CompareTo(latestGiteaRelease); - if (versionComparison < 0) - { - // The version on GitHub is more up to date than this local release. - AnsiConsole.Markup("[red]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]"); - AnsiConsole.Markup("[red]Please update to the current release, " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}: [link]https://git.ofdl.tools/sim0n00ps/OF-DL/releases[/]\n[/]"); - Log.Debug("Detected outdated client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}"); - Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}"); - } - else - { - // This local version is greater than the release version on GitHub. - AnsiConsole.Markup("[green]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]"); - AnsiConsole.Markup("[green]Latest Release version: " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}\n[/]"); - Log.Debug("Detected client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}"); - Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}"); - } - } + try + { + latestReleaseTag = await VersionHelper.GetLatestReleaseTag(cts.Token); + } + catch (OperationCanceledException) + { + AnsiConsole.Markup("[yellow]Version check timed out after 30 seconds.\n[/]"); + Log.Warning("Version check timed out after 30 seconds"); + latestReleaseTag = null; + } + + if (latestReleaseTag == null) + { + AnsiConsole.Markup("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]"); + Log.Error("Failed to get the latest release tag."); + } + else + { + Version latestGiteaRelease = new Version(latestReleaseTag.Replace("OFDLV", "")); + + // Compare the Versions + int versionComparison = localVersion.CompareTo(latestGiteaRelease); + if (versionComparison < 0) + { + // The version on GitHub is more up to date than this local release. + AnsiConsole.Markup("[red]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]"); + AnsiConsole.Markup("[red]Please update to the current release, " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}: [link=https://git.ofdl.tools/sim0n00ps/OF-DL/releases]https://git.ofdl.tools/sim0n00ps/OF-DL/releases[/]\n[/]"); + Log.Debug("Detected outdated client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}"); + Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}"); + } + else + { + // This local version is greater than the release version on GitHub. + AnsiConsole.Markup("[green]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]"); + AnsiConsole.Markup("[green]Latest Release version: " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}\n[/]"); + Log.Debug("Detected client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}"); + Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}"); + } + } #else AnsiConsole.Markup("[yellow]Running in Debug/Local mode. Version check skipped.\n[/]"); From f6a9cbd305807519f74cafb33ba71dc3e38a6f82 Mon Sep 17 00:00:00 2001 From: sim0n00ps Date: Tue, 2 Dec 2025 00:00:36 +0000 Subject: [PATCH 5/6] Update docs/config/all-configuration-options.md --- docs/config/all-configuration-options.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/config/all-configuration-options.md b/docs/config/all-configuration-options.md index 60583a6..6769362 100644 --- a/docs/config/all-configuration-options.md +++ b/docs/config/all-configuration-options.md @@ -516,3 +516,23 @@ Allowed values: Any positive integer or `-1` Description: You won't need to set this, but if you see errors about the configured timeout of 100 seconds elapsing then you could set this to be more than 100. It is recommended that you leave this as the default value. + +## DisableTextSanitization + +Type: `boolean` + +Default: `false` + +Allowed values: `true`, `false` + +Description: When enabled, post/message text is stored as-is without XML stripping. + +## DownloadVideoResolution + +Type: `string` + +Default: `"source"` + +Allowed values: `"source"`, `"240"`, `"720"` + +Description: This allows you to download videos in alternative resolutions, by default videos are downloaded in source resolution but some people prefer smoother videos at a lower resolution. \ No newline at end of file From eca38116fa450ed4627c62854c6f1491ed135461 Mon Sep 17 00:00:00 2001 From: sim0n00ps Date: Tue, 2 Dec 2025 00:02:50 +0000 Subject: [PATCH 6/6] Update docs/config/configuration.md --- docs/config/configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/config/configuration.md b/docs/config/configuration.md index 83b1ece..6f87744 100644 --- a/docs/config/configuration.md +++ b/docs/config/configuration.md @@ -20,6 +20,8 @@ information about what it does, its default value, and the allowed values. - [DownloadDateSelection](/config/all-configuration-options#downloaddateselection) - [CustomDate](/config/all-configuration-options#customdate) - [ShowScrapeSize](/config/all-configuration-options#showscrapesize) + - [DisableTextSanitization](/config/all-configuration-options#disabletextsanitization) + - [DownloadVideoResolution](/config/all-configuration-options#downloadvideoresolution) - Media - [DownloadAvatarHeaderPhoto](/config/all-configuration-options#downloadavatarheaderphoto) - [DownloadPaidPosts](/config/all-configuration-options#downloadpaidposts)