diff --git a/OF DL/CLI/SpectreProgressReporter.cs b/OF DL/CLI/SpectreProgressReporter.cs
new file mode 100644
index 0000000..f6c65ab
--- /dev/null
+++ b/OF DL/CLI/SpectreProgressReporter.cs
@@ -0,0 +1,28 @@
+using OF_DL.Services;
+using Spectre.Console;
+
+namespace OF_DL.CLI;
+
+///
+/// Implementation of IProgressReporter that uses Spectre.Console's ProgressTask for CLI output.
+///
+public class SpectreProgressReporter : IProgressReporter
+{
+ private readonly ProgressTask _task;
+
+ public SpectreProgressReporter(ProgressTask task)
+ {
+ _task = task ?? throw new ArgumentNullException(nameof(task));
+ }
+
+ public void ReportProgress(long increment)
+ {
+ _task.Increment(increment);
+ }
+
+ public void ReportStatus(string message)
+ {
+ // Optionally update task description or handle status messages
+ // For now, we'll leave this empty as the task description is set when creating the progress bar
+ }
+}
diff --git a/OF DL/Entities/Config.cs b/OF DL/Entities/Config.cs
index 0934422..7fbc8c9 100644
--- a/OF DL/Entities/Config.cs
+++ b/OF DL/Entities/Config.cs
@@ -1,11 +1,12 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using OF_DL.Enumerations;
+using Serilog;
namespace OF_DL.Entities
{
- public class Config : IDownloadConfig, IFileNameFormatConfig
+ public class Config : IFileNameFormatConfig
{
[ToggleableConfig]
public bool DownloadAvatarHeaderPhoto { get; set; } = true;
@@ -107,6 +108,39 @@ namespace OF_DL.Entities
// When enabled, post/message text is stored as-is without XML stripping.
[ToggleableConfig]
public bool DisableTextSanitization { get; set; } = false;
+
+ public IFileNameFormatConfig GetCreatorFileNameFormatConfig(string username)
+ {
+ FileNameFormatConfig createFileNameFormatConfig = new FileNameFormatConfig();
+
+ Func func = (val1, val2) =>
+ {
+ if (string.IsNullOrEmpty(val1))
+ return val2;
+ else
+ return val1;
+ };
+
+ if(CreatorConfigs.TryGetValue(username, out CreatorConfig? creatorConfig))
+ {
+ createFileNameFormatConfig.PaidMessageFileNameFormat = creatorConfig.PaidMessageFileNameFormat;
+ createFileNameFormatConfig.PostFileNameFormat = creatorConfig.PostFileNameFormat;
+ createFileNameFormatConfig.MessageFileNameFormat = creatorConfig.MessageFileNameFormat;
+ createFileNameFormatConfig.PaidPostFileNameFormat = creatorConfig.PaidPostFileNameFormat;
+ }
+
+ createFileNameFormatConfig.PaidMessageFileNameFormat = func(createFileNameFormatConfig.PaidMessageFileNameFormat, PaidMessageFileNameFormat);
+ createFileNameFormatConfig.PostFileNameFormat = func(createFileNameFormatConfig.PostFileNameFormat, PostFileNameFormat);
+ createFileNameFormatConfig.MessageFileNameFormat = func(createFileNameFormatConfig.MessageFileNameFormat, MessageFileNameFormat);
+ createFileNameFormatConfig.PaidPostFileNameFormat = func(createFileNameFormatConfig.PaidPostFileNameFormat, PaidPostFileNameFormat);
+
+ Log.Debug("PaidMessageFilenameFormat: {CombinedConfigPaidMessageFileNameFormat}", createFileNameFormatConfig.PaidMessageFileNameFormat);
+ Log.Debug("PostFileNameFormat: {CombinedConfigPostFileNameFormat}", createFileNameFormatConfig.PostFileNameFormat);
+ Log.Debug("MessageFileNameFormat: {CombinedConfigMessageFileNameFormat}", createFileNameFormatConfig.MessageFileNameFormat);
+ Log.Debug("PaidPostFileNameFormat: {CombinedConfigPaidPostFileNameFormat}", createFileNameFormatConfig.PaidPostFileNameFormat);
+
+ return createFileNameFormatConfig;
+ }
}
public class CreatorConfig : IFileNameFormatConfig
diff --git a/OF DL/Entities/DownloadResult.cs b/OF DL/Entities/DownloadResult.cs
new file mode 100644
index 0000000..f44d26d
--- /dev/null
+++ b/OF DL/Entities/DownloadResult.cs
@@ -0,0 +1,37 @@
+namespace OF_DL.Entities;
+
+///
+/// Represents the result of a download operation.
+///
+public class DownloadResult
+{
+ ///
+ /// Total number of media items processed.
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// Number of newly downloaded media items.
+ ///
+ public int NewDownloads { get; set; }
+
+ ///
+ /// Number of media items that were already downloaded.
+ ///
+ public int ExistingDownloads { get; set; }
+
+ ///
+ /// The type of media downloaded (e.g., "Posts", "Messages", "Stories", etc.).
+ ///
+ public string MediaType { get; set; } = string.Empty;
+
+ ///
+ /// Indicates whether the download operation was successful.
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// Optional error message if the download failed.
+ ///
+ public string? ErrorMessage { get; set; }
+}
diff --git a/OF DL/Entities/IDownloadConfig.cs b/OF DL/Entities/IDownloadConfig.cs
deleted file mode 100644
index 21565d0..0000000
--- a/OF DL/Entities/IDownloadConfig.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using OF_DL.Enumerations;
-
-namespace OF_DL.Entities
-{
- public interface IDownloadConfig
- {
- bool DownloadAvatarHeaderPhoto { get; set; }
- bool DownloadPaidPosts { get; set; }
- bool DownloadPosts { get; set; }
- bool DownloadArchived { get; set; }
- bool DownloadStreams { get; set; }
- bool DownloadStories { get; set; }
- bool DownloadHighlights { get; set; }
- bool DownloadMessages { get; set; }
- bool DownloadPaidMessages { get; set; }
- bool DownloadImages { get; set; }
- bool DownloadVideos { get; set; }
- bool DownloadAudios { get; set; }
-
- VideoResolution DownloadVideoResolution { get; set; }
-
- int? Timeout { get; set; }
- bool FolderPerPaidPost { get; set; }
- bool FolderPerPost { get; set; }
- bool FolderPerPaidMessage { get; set; }
- bool FolderPerMessage { get; set; }
-
- bool RenameExistingFilesWhenCustomFormatIsSelected { get; set; }
- bool ShowScrapeSize { get; set; }
- bool LimitDownloadRate { get; set; }
- int DownloadLimitInMbPerSec { get; set; }
- string? FFmpegPath { get; set; }
-
- bool SkipAds { get; set; }
- bool BypassContentForCreatorsWhoNoLongerExist { get; set; }
-
- #region Download Date Configurations
-
- bool DownloadOnlySpecificDates { get; set; }
-
- // This enum will define if we want data from before or after the CustomDate.
- DownloadDateSelection DownloadDateSelection { get; set; }
-
- // This is the specific date used in combination with the above enum.
- DateTime? CustomDate { get; set; }
- #endregion
-
- bool DownloadPostsIncrementally { get; set; }
-
- bool DownloadDuplicatedMedia { get; set; }
- public LoggingLevel LoggingLevel { get; set; }
-
- bool IgnoreOwnMessages { get; set; }
- }
-
-}
diff --git a/OF DL/Helpers/AuthHelper.cs b/OF DL/Helpers/AuthHelper.cs
deleted file mode 100644
index 33d522c..0000000
--- a/OF DL/Helpers/AuthHelper.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using OF_DL.Entities;
-using OF_DL.Helpers.Interfaces;
-using PuppeteerSharp;
-using PuppeteerSharp.BrowserData;
-using Serilog;
-
-namespace OF_DL.Helpers;
-
-public class AuthHelper : IAuthHelper
-{
- private readonly LaunchOptions _options = new()
- {
- Headless = false,
- Channel = ChromeReleaseChannel.Stable,
- DefaultViewport = null,
- Args = ["--no-sandbox", "--disable-setuid-sandbox"],
- UserDataDir = Path.GetFullPath("chrome-data")
- };
-
- private readonly string[] _desiredCookies =
- [
- "auth_id",
- "sess"
- ];
-
- private const int LoginTimeout = 600000; // 10 minutes
- private const int FeedLoadTimeout = 60000; // 1 minute
-
- public async Task SetupBrowser(bool runningInDocker)
- {
- string? executablePath = Environment.GetEnvironmentVariable("OFDL_PUPPETEER_EXECUTABLE_PATH");
- if (executablePath != null)
- {
- Log.Information("OFDL_PUPPETEER_EXECUTABLE_PATH environment variable found. Using browser executable path: {executablePath}", executablePath);
- _options.ExecutablePath = executablePath;
- }
- else
- {
- var browserFetcher = new BrowserFetcher();
- var installedBrowsers = browserFetcher.GetInstalledBrowsers().ToList();
- if (installedBrowsers.Count == 0)
- {
- Log.Information("Downloading browser.");
- var downloadedBrowser = await browserFetcher.DownloadAsync();
- Log.Information("Browser downloaded. Path: {executablePath}",
- downloadedBrowser.GetExecutablePath());
- _options.ExecutablePath = downloadedBrowser.GetExecutablePath();
- }
- else
- {
- _options.ExecutablePath = installedBrowsers.First().GetExecutablePath();
- }
- }
-
- if (runningInDocker)
- {
- Log.Information("Running in Docker. Disabling sandbox and GPU.");
- _options.Args = ["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu"];
- }
- }
-
- private async Task GetBcToken(IPage page)
- {
- return await page.EvaluateExpressionAsync("window.localStorage.getItem('bcTokenSha') || ''");
- }
-
- public async Task GetAuthFromBrowser(bool isDocker = false)
- {
- try
- {
- IBrowser? browser;
- try
- {
- browser = await Puppeteer.LaunchAsync(_options);
- }
- catch (ProcessException e)
- {
- if (e.Message.Contains("Failed to launch browser") && Directory.Exists(_options.UserDataDir))
- {
- Log.Error("Failed to launch browser. Deleting chrome-data directory and trying again.");
- Directory.Delete(_options.UserDataDir, true);
- browser = await Puppeteer.LaunchAsync(_options);
- }
- else
- {
- throw;
- }
- }
-
- if (browser == null)
- {
- throw new Exception("Could not get browser");
- }
-
- IPage[]? pages = await browser.PagesAsync();
- IPage? page = pages.First();
-
- if (page == null)
- {
- throw new Exception("Could not get page");
- }
-
- Log.Debug("Navigating to OnlyFans.");
- await page.GoToAsync("https://onlyfans.com");
-
- Log.Debug("Waiting for user to login");
- await page.WaitForSelectorAsync(".b-feed", new WaitForSelectorOptions { Timeout = LoginTimeout });
- Log.Debug("Feed element detected (user logged in)");
-
- await page.ReloadAsync();
-
- await page.WaitForNavigationAsync(new NavigationOptions {
- WaitUntil = [WaitUntilNavigation.Networkidle2],
- Timeout = FeedLoadTimeout
- });
- Log.Debug("DOM loaded. Getting BC token and cookies ...");
-
- string xBc;
- try
- {
- xBc = await GetBcToken(page);
- }
- catch (Exception e)
- {
- throw new Exception("Error getting bcToken");
- }
-
- Dictionary mappedCookies = (await page.GetCookiesAsync())
- .Where(cookie => cookie.Domain.Contains("onlyfans.com"))
- .ToDictionary(cookie => cookie.Name, cookie => cookie.Value);
-
- mappedCookies.TryGetValue("auth_id", out string? userId);
- if (userId == null)
- {
- throw new Exception("Could not find 'auth_id' cookie");
- }
-
- mappedCookies.TryGetValue("sess", out string? sess);
- if (sess == null)
- {
- throw new Exception("Could not find 'sess' cookie");
- }
-
- string? userAgent = await browser.GetUserAgentAsync();
- if (userAgent == null)
- {
- throw new Exception("Could not get user agent");
- }
-
- string cookies = string.Join(" ", mappedCookies.Keys.Where(key => _desiredCookies.Contains(key))
- .Select(key => $"${key}={mappedCookies[key]};"));
-
- return new Auth()
- {
- COOKIE = cookies,
- USER_AGENT = userAgent,
- USER_ID = userId,
- X_BC = xBc
- };
- }
- catch (Exception e)
- {
- Log.Error(e, "Error getting auth from browser");
- return null;
- }
- }
-}
diff --git a/OF DL/Helpers/DownloadContext.cs b/OF DL/Helpers/DownloadContext.cs
index a3146d7..cae7598 100644
--- a/OF DL/Helpers/DownloadContext.cs
+++ b/OF DL/Helpers/DownloadContext.cs
@@ -1,28 +1,26 @@
-using OF_DL.Entities;
+using OF_DL.Entities;
+using OF_DL.Services;
namespace OF_DL.Helpers
{
internal interface IDownloadContext
{
- public IDownloadConfig DownloadConfig { get; }
public IFileNameFormatConfig FileNameFormatConfig { get; }
- public IAPIHelper ApiHelper { get; }
- public IDBHelper DBHelper { get; }
- public IDownloadHelper DownloadHelper { get; }
+ public IAPIService ApiService { get; }
+ public IDBService DBService { get; }
+ public IDownloadService DownloadService { get; }
}
internal class DownloadContext(
- IDownloadConfig downloadConfig,
IFileNameFormatConfig fileNameFormatConfig,
- IAPIHelper apiHelper,
- IDBHelper dBHelper,
- IDownloadHelper downloadHelper)
+ IAPIService apiService,
+ IDBService dBService,
+ IDownloadService downloadService)
: IDownloadContext
{
- public IAPIHelper ApiHelper { get; } = apiHelper;
- public IDBHelper DBHelper { get; } = dBHelper;
- public IDownloadHelper DownloadHelper { get; } = downloadHelper;
- public IDownloadConfig DownloadConfig { get; } = downloadConfig;
+ public IAPIService ApiService { get; } = apiService;
+ public IDBService DBService { get; } = dBService;
+ public IDownloadService DownloadService { get; } = downloadService;
public IFileNameFormatConfig FileNameFormatConfig { get; } = fileNameFormatConfig;
}
}
diff --git a/OF DL/Helpers/DownloadHelper.cs b/OF DL/Helpers/DownloadHelper.cs
deleted file mode 100644
index 8454bfc..0000000
--- a/OF DL/Helpers/DownloadHelper.cs
+++ /dev/null
@@ -1,2040 +0,0 @@
-using FFmpeg.NET;
-using FFmpeg.NET.Events;
-using FFmpeg.NET.Services;
-using OF_DL.Entities;
-using OF_DL.Entities.Archived;
-using OF_DL.Entities.Messages;
-using OF_DL.Entities.Post;
-using OF_DL.Entities.Purchased;
-using OF_DL.Entities.Stories;
-using OF_DL.Entities.Streams;
-using OF_DL.Enumerations;
-using OF_DL.Utils;
-using Org.BouncyCastle.Asn1.Tsp;
-using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Tsp;
-using Serilog;
-using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using System.Xml.Linq;
-using static OF_DL.Entities.Lists.UserList;
-using static OF_DL.Entities.Messages.Messages;
-using FromUser = OF_DL.Entities.Messages.FromUser;
-
-namespace OF_DL.Helpers;
-
-public class DownloadHelper : IDownloadHelper
-{
- private readonly Auth auth;
- private readonly IDBHelper dbHelper;
- private readonly IFileNameHelper fileNameHelper;
- private readonly IDownloadConfig downloadConfig;
- private readonly IFileNameFormatConfig fileNameFormatConfig;
- private TaskCompletionSource _completionSource;
-
- public DownloadHelper(Auth auth, IDownloadConfig downloadConfig, IFileNameFormatConfig fileNameFormatConfig, IDBHelper dbHelper, IFileNameHelper fileNameHelper)
- {
- this.auth = auth;
- this.dbHelper = dbHelper;
- this.fileNameHelper = fileNameHelper;
- this.downloadConfig = downloadConfig;
- this.fileNameFormatConfig = fileNameFormatConfig;
- }
-
- #region common
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- protected async Task CreateDirectoriesAndDownloadMedia(string path,
- string url,
- string folder,
- long media_id,
- string api_type,
- ProgressTask task,
- string serverFileName,
- string resolvedFileName)
- {
- try
- {
- string customFileName = string.Empty;
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
- string extension = Path.GetExtension(url.Split("?")[0]);
-
- path = UpdatePathBasedOnExtension(folder, path, extension);
-
- return await ProcessMediaDownload(folder, media_id, api_type, url, path, serverFileName, resolvedFileName, extension, task);
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
-
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
-
- ///
- /// Updates the given path based on the file extension.
- ///
- /// The parent folder.
- /// The initial relative path.
- /// The file extension.
- /// A string that represents the updated path based on the file extension.
- private string UpdatePathBasedOnExtension(string folder, string path, string extension)
- {
- string subdirectory = string.Empty;
-
- switch (extension.ToLower())
- {
- case ".jpg":
- case ".jpeg":
- case ".png":
- subdirectory = "/Images";
- break;
- case ".mp4":
- case ".avi":
- case ".wmv":
- case ".gif":
- case ".mov":
- subdirectory = "/Videos";
- break;
- case ".mp3":
- case ".wav":
- case ".ogg":
- subdirectory = "/Audios";
- break;
- }
-
- if (!string.IsNullOrEmpty(subdirectory))
- {
- path += subdirectory;
- string fullPath = folder + path;
-
- if (!Directory.Exists(fullPath))
- {
- Directory.CreateDirectory(fullPath);
- }
- }
-
- return path;
- }
-
-
- ///
- /// Generates a custom filename based on the given format and properties.
- ///
- /// The format string for the filename.
- /// General information about the post.
- /// Media associated with the post.
- /// Author of the post.
- /// Dictionary containing user-related data.
- /// Helper class for filename operations.
- /// A Task resulting in a string that represents the custom filename.
- private async Task GenerateCustomFileName(string filename,
- string? filenameFormat,
- object? postInfo,
- object? postMedia,
- object? author,
- string username,
- Dictionary users,
- IFileNameHelper fileNameHelper,
- CustomFileNameOption option)
- {
- if (string.IsNullOrEmpty(filenameFormat) || postInfo == null || postMedia == null || author == null)
- {
- return option switch
- {
- CustomFileNameOption.ReturnOriginal => filename,
- CustomFileNameOption.ReturnEmpty => string.Empty,
- _ => filename,
- };
- }
-
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- properties.AddRange(matches.Select(match => match.Groups[1].Value));
-
- Dictionary values = await fileNameHelper.GetFilename(postInfo, postMedia, author, properties, username, users);
- return await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
-
- private async Task GetFileSizeAsync(string url, Auth auth)
- {
- long fileSize = 0;
-
- try
- {
- Uri uri = new(url);
-
- if (uri.Host == "cdn3.onlyfans.com" && uri.LocalPath.Contains("/dash/files"))
- {
- string[] messageUrlParsed = url.Split(',');
- string mpdURL = messageUrlParsed[0];
- string policy = messageUrlParsed[1];
- string signature = messageUrlParsed[2];
- string kvp = messageUrlParsed[3];
-
- mpdURL = mpdURL.Replace(".mpd", "_source.mp4");
-
- using HttpClient client = new();
- client.DefaultRequestHeaders.Add("Cookie", $"CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {auth.COOKIE}");
- client.DefaultRequestHeaders.Add("User-Agent", auth.USER_AGENT);
-
- using HttpResponseMessage response = await client.GetAsync(mpdURL, HttpCompletionOption.ResponseHeadersRead);
- if (response.IsSuccessStatusCode)
- {
- fileSize = response.Content.Headers.ContentLength ?? 0;
- }
- }
- else
- {
- using HttpClient client = new();
- client.DefaultRequestHeaders.Add("User-Agent", auth.USER_AGENT);
- using HttpResponseMessage response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
- if (response.IsSuccessStatusCode)
- {
- fileSize = response.Content.Headers.ContentLength ?? 0;
- }
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Error getting file size for URL '{url}': {ex.Message}");
- }
-
- return fileSize;
- }
-
- public static async Task GetDRMVideoLastModified(string url, Auth auth)
- {
- Uri uri = new(url);
-
- string[] messageUrlParsed = url.Split(',');
- string mpdURL = messageUrlParsed[0];
- string policy = messageUrlParsed[1];
- string signature = messageUrlParsed[2];
- string kvp = messageUrlParsed[3];
-
- mpdURL = mpdURL.Replace(".mpd", "_source.mp4");
-
- using HttpClient client = new();
- client.DefaultRequestHeaders.Add("Cookie", $"CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {auth.COOKIE}");
- client.DefaultRequestHeaders.Add("User-Agent", auth.USER_AGENT);
-
- using HttpResponseMessage response = await client.GetAsync(mpdURL, HttpCompletionOption.ResponseHeadersRead);
- if (response.IsSuccessStatusCode)
- {
- return response.Content.Headers.LastModified.Value.DateTime;
- }
- return DateTime.Now;
- }
- public static async Task GetMediaLastModified(string url)
- {
- using HttpClient client = new();
-
- using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
- if (response.IsSuccessStatusCode)
- {
- return response.Content.Headers.LastModified.Value.DateTime;
- }
- return DateTime.Now;
- }
-
- ///
- /// Processes the download and database update of media.
- ///
- /// The folder where the media is stored.
- /// The ID of the media.
- /// The full path to the media.
- /// The URL from where to download the media.
- /// The relative path to the media.
- /// The filename after any required manipulations.
- /// The file extension.
- /// The task object for tracking progress.
- /// A Task resulting in a boolean indicating whether the media is newly downloaded or not.
- public async Task ProcessMediaDownload(string folder,
- long media_id,
- string api_type,
- string url,
- string path,
- string serverFilename,
- string resolvedFilename,
- string extension,
- ProgressTask task)
- {
-
- try
- {
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- return await HandleNewMedia(folder: folder,
- media_id: media_id,
- api_type: api_type,
- url: url,
- path: path,
- serverFilename: serverFilename,
- resolvedFilename: resolvedFilename,
- extension: extension,
- task: task);
- }
- else
- {
- bool status = await HandlePreviouslyDownloadedMediaAsync(folder, media_id, api_type, task);
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (serverFilename != resolvedFilename))
- {
- await HandleRenamingOfExistingFilesAsync(folder, media_id, api_type, path, serverFilename, resolvedFilename, extension);
- }
- return status;
- }
- }
- catch (Exception ex)
- {
- // Handle exception (e.g., log it)
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- }
-
-
- private async Task HandleRenamingOfExistingFilesAsync(string folder,
- long media_id,
- string api_type,
- string path,
- string serverFilename,
- string resolvedFilename,
- string extension)
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{serverFilename}{extension}";
- string fullPathWithTheNewFileName = $"{folder}{path}/{resolvedFilename}{extension}";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
-
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
-
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- var lastModified = File.GetLastWriteTime(fullPathWithTheNewFileName);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, resolvedFilename + extension, size, true, lastModified);
- return true;
- }
-
-
- ///
- /// Handles new media by downloading and updating the database.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// A Task resulting in a boolean indicating whether the media is newly downloaded or not.
- private async Task HandleNewMedia(string folder,
- long media_id,
- string api_type,
- string url,
- string path,
- string serverFilename,
- string resolvedFilename,
- string extension,
- ProgressTask task)
- {
- long fileSizeInBytes;
- DateTime lastModified;
- bool status;
-
- string fullPathWithTheServerFileName = $"{folder}{path}/{serverFilename}{extension}";
- string fullPathWithTheNewFileName = $"{folder}{path}/{resolvedFilename}{extension}";
-
- //there are a few possibilities here.
- //1.file has been downloaded in the past but it has the server filename
- // in that case it should be set as existing and it should be renamed
- //2.file has been downloaded in the past but it has custom filename.
- // it should be set as existing and nothing else.
- // of coures 1 and 2 depends in the fact that there may be a difference in the resolved file name
- // (ie user has selected a custom format. If he doesn't then the resolved name will be the same as the server filename
- //3.file doesn't exist and it should be downloaded.
-
- // Handle the case where the file has been downloaded in the past with the server filename
- //but it has downloaded outsite of this application so it doesn't exist in the database
- if (File.Exists(fullPathWithTheServerFileName))
- {
- string finalPath;
- if (fullPathWithTheServerFileName != fullPathWithTheNewFileName)
- {
- finalPath = fullPathWithTheNewFileName;
- //rename.
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- }
- }
- else
- {
- finalPath = fullPathWithTheServerFileName;
- }
-
- fileSizeInBytes = GetLocalFileSize(finalPath);
- lastModified = File.GetLastWriteTime(finalPath);
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- status = false;
- }
- // Handle the case where the file has been downloaded in the past with a custom filename.
- //but it has downloaded outsite of this application so it doesn't exist in the database
- // this is a bit improbable but we should check for that.
- else if (File.Exists(fullPathWithTheNewFileName))
- {
- fileSizeInBytes = GetLocalFileSize(fullPathWithTheNewFileName);
- lastModified = File.GetLastWriteTime(fullPathWithTheNewFileName);
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- status = false;
- }
- else //file doesn't exist and we should download it.
- {
- lastModified = await DownloadFile(url, fullPathWithTheNewFileName, task);
- fileSizeInBytes = GetLocalFileSize(fullPathWithTheNewFileName);
- status = true;
- }
-
- //finaly check which filename we should use. Custom or the server one.
- //if a custom is used, then the servefilename will be different from the resolved filename.
- string finalName = serverFilename == resolvedFilename ? serverFilename : resolvedFilename;
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, finalName + extension, fileSizeInBytes, true, lastModified);
- return status;
- }
-
-
- ///
- /// Handles media that has been previously downloaded and updates the task accordingly.
- ///
- ///
- ///
- ///
- ///
- /// A boolean indicating whether the media is newly downloaded or not.
- private async Task HandlePreviouslyDownloadedMediaAsync(string folder, long media_id, string api_type, ProgressTask task)
- {
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- return false;
- }
-
-
- ///
- /// Gets the file size of the media.
- ///
- /// The path to the file.
- /// The file size in bytes.
- private long GetLocalFileSize(string filePath)
- {
- return new FileInfo(filePath).Length;
- }
-
-
- ///
- /// Downloads a file from the given URL and saves it to the specified destination path.
- ///
- /// The URL to download the file from.
- /// The path where the downloaded file will be saved.
- /// Progress tracking object.
- /// A Task resulting in a DateTime indicating the last modified date of the downloaded file.
-
- private async Task DownloadFile(string url, string destinationPath, ProgressTask task)
- {
- using var client = new HttpClient();
- var request = new HttpRequestMessage
- {
- Method = HttpMethod.Get,
- RequestUri = new Uri(url)
- };
-
- using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
- response.EnsureSuccessStatusCode();
- var body = await response.Content.ReadAsStreamAsync();
-
- // Wrap the body stream with the ThrottledStream to limit read rate.
- using (ThrottledStream throttledStream = new(body, downloadConfig.DownloadLimitInMbPerSec * 1_000_000, downloadConfig.LimitDownloadRate))
- {
- using FileStream fileStream = new(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 16384, true);
- var buffer = new byte[16384];
- int read;
- while ((read = await throttledStream.ReadAsync(buffer, CancellationToken.None)) > 0)
- {
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(read);
- }
- await fileStream.WriteAsync(buffer.AsMemory(0, read), CancellationToken.None);
- }
- }
-
- File.SetLastWriteTime(destinationPath, response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now);
- if (!downloadConfig.ShowScrapeSize)
- {
- task.Increment(1);
- }
- return response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now;
- }
-
- public async Task CalculateTotalFileSize(List urls)
- {
- long totalFileSize = 0;
- if (urls.Count > 250)
- {
- int batchSize = 250;
-
- var tasks = new List>();
-
- for (int i = 0; i < urls.Count; i += batchSize)
- {
- var batchUrls = urls.Skip(i).Take(batchSize).ToList();
-
- var batchTasks = batchUrls.Select(url => GetFileSizeAsync(url, auth));
- tasks.AddRange(batchTasks);
-
- await Task.WhenAll(batchTasks);
-
- await Task.Delay(5000);
- }
-
- long[] fileSizes = await Task.WhenAll(tasks);
- foreach (long fileSize in fileSizes)
- {
- totalFileSize += fileSize;
- }
- }
- else
- {
- var tasks = new List>();
-
- foreach (string url in urls)
- {
- tasks.Add(GetFileSizeAsync(url, auth));
- }
-
- long[] fileSizes = await Task.WhenAll(tasks);
- foreach (long fileSize in fileSizes)
- {
- totalFileSize += fileSize;
- }
- }
-
- return totalFileSize;
- }
- #endregion
-
- #region drm common
-
- private async Task DownloadDrmMedia(string user_agent, string policy, string signature, string kvp, string sess, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string customFileName, string filename, string path)
- {
- try
- {
- _completionSource = new TaskCompletionSource();
-
- int pos1 = decryptionKey.IndexOf(':');
- string decKey = "";
- if (pos1 >= 0)
- {
- decKey = decryptionKey.Substring(pos1 + 1);
- }
-
- int streamIndex = 0;
- string tempFilename = $"{folder}{path}/{filename}_source.mp4";
-
- //int? streamIndex = await GetVideoStreamIndexFromMpd(url, policy, signature, kvp, downloadConfig.DownloadVideoResolution);
-
- //if (streamIndex == null)
- // throw new Exception($"Could not find video stream for resolution {downloadConfig.DownloadVideoResolution}");
-
- //string tempFilename;
-
- //switch (downloadConfig.DownloadVideoResolution)
- //{
- // case VideoResolution.source:
- // tempFilename = $"{folder}{path}/{filename}_source.mp4";
- // break;
- // case VideoResolution._240:
- // tempFilename = $"{folder}{path}/{filename}_240.mp4";
- // break;
- // case VideoResolution._720:
- // tempFilename = $"{folder}{path}/{filename}_720.mp4";
- // break;
- // default:
- // tempFilename = $"{folder}{path}/{filename}_source.mp4";
- // break;
- //}
-
- // Configure ffmpeg log level and optional report file location
- bool ffmpegDebugLogging = Log.IsEnabled(Serilog.Events.LogEventLevel.Debug);
-
- string logLevelArgs = ffmpegDebugLogging || downloadConfig.LoggingLevel is LoggingLevel.Verbose or LoggingLevel.Debug
- ? "-loglevel debug -report"
- : downloadConfig.LoggingLevel switch
- {
- LoggingLevel.Information => "-loglevel info",
- LoggingLevel.Warning => "-loglevel warning",
- LoggingLevel.Error => "-loglevel error",
- LoggingLevel.Fatal => "-loglevel fatal",
- _ => string.Empty
- };
-
- if (logLevelArgs.Contains("-report", StringComparison.OrdinalIgnoreCase))
- {
- // Direct ffmpeg report files into the same logs directory Serilog uses (relative to current working directory)
- string logDir = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "logs"));
- Directory.CreateDirectory(logDir);
- string ffReportPath = Path.Combine(logDir, "ffmpeg-%p-%t.log"); // ffmpeg will replace %p/%t
- Environment.SetEnvironmentVariable("FFREPORT", $"file={ffReportPath}:level=32");
- Log.Debug("FFREPORT enabled at: {FFREPORT} (cwd: {Cwd})", Environment.GetEnvironmentVariable("FFREPORT"), Environment.CurrentDirectory);
- }
- else
- {
- Environment.SetEnvironmentVariable("FFREPORT", null);
- Log.Debug("FFREPORT disabled (cwd: {Cwd})", Environment.CurrentDirectory);
- }
-
- string cookieHeader =
- "Cookie: " +
- $"CloudFront-Policy={policy}; " +
- $"CloudFront-Signature={signature}; " +
- $"CloudFront-Key-Pair-Id={kvp}; " +
- $"{sess}";
-
- string parameters =
- $"{logLevelArgs} " +
- $"-cenc_decryption_key {decKey} " +
- $"-headers \"{cookieHeader}\" " +
- $"-user_agent \"{user_agent}\" " +
- "-referer \"https://onlyfans.com\" " +
- "-rw_timeout 20000000 " +
- "-reconnect 1 -reconnect_streamed 1 -reconnect_on_network_error 1 -reconnect_delay_max 10 " +
- "-y " +
- $"-i \"{url}\" " +
- $"-map 0:v:{streamIndex} -map 0:a? " +
- "-c copy " +
- $"\"{tempFilename}\"";
-
- Log.Debug($"Calling FFMPEG with Parameters: {parameters}");
-
- Engine ffmpeg = new Engine(downloadConfig.FFmpegPath);
- ffmpeg.Error += OnError;
- ffmpeg.Complete += async (sender, args) =>
- {
- _completionSource.TrySetResult(true);
- await OnFFMPEGDownloadComplete(tempFilename, lastModified, folder, path, customFileName, filename, media_id, api_type, task);
- };
- await ffmpeg.ExecuteAsync(parameters, CancellationToken.None);
-
- return await _completionSource.Task;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
- #endregion
-
- #region normal posts
- public async Task DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
- {
- path = $"/Posts/Free/{postInfo.id} {postInfo.postedAt:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Posts/Free";
- }
-
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, postInfo, postMedia, author, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
-
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
- public async Task DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
- {
- path = $"/Posts/Free/{postInfo.id} {postInfo.postedAt:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Posts/Free";
- }
-
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, postInfo, postMedia, author, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
-
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
- public async Task DownloadStreamMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerPost && streamInfo != null && streamInfo?.id is not null && streamInfo?.postedAt is not null)
- {
- path = $"/Posts/Free/{streamInfo.id} {streamInfo.postedAt:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Posts/Free";
- }
-
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, streamInfo, streamMedia, author, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
-
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
-
- public async Task DownloadMessageMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Free/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Messages/Free";
- }
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, fromUser, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
- public async Task DownloadMessagePreviewMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Messages.Medium? messageMedia, FromUser? fromUser, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Free/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Messages/Free";
- }
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, fromUser, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
-
- public async Task DownloadArchivedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Archived.List? messageInfo, Archived.Medium? messageMedia, Archived.Author? author, Dictionary users)
- {
- string path = "/Archived/Posts/Free";
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, author, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
-
-
- public async Task DownloadStoryMedia(string url, string folder, long media_id, string api_type, ProgressTask task)
- {
- string path = "/Stories/Free";
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, filename);
- }
-
- public async Task DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Paid/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Messages/Paid";
- }
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, fromUser, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
- public async Task DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Paid/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Messages/Paid";
- }
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, fromUser, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
- public async Task DownloadPurchasedPostMedia(string url,
- string folder,
- long media_id,
- string api_type,
- ProgressTask task,
- string? filenameFormat,
- Purchased.List? messageInfo,
- Medium? messageMedia,
- Purchased.FromUser? fromUser,
- Dictionary users)
- {
- string path;
- if (downloadConfig.FolderPerPaidPost && messageInfo != null && messageInfo?.id is not null && messageInfo?.postedAt is not null)
- {
- path = $"/Posts/Paid/{messageInfo.id} {messageInfo.postedAt.Value:yyyy-MM-dd HH-mm-ss}";
- }
- else
- {
- path = "/Posts/Paid";
- }
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
- string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, fromUser, folder.Split("/")[^1], users, fileNameHelper, CustomFileNameOption.ReturnOriginal);
- return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
- }
-
- #endregion
- public async Task DownloadAvatarHeader(string? avatarUrl, string? headerUrl, string folder, string username)
- {
- try
- {
- string path = $"/Profile";
-
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(avatarUrl))
- {
- string avatarpath = $"{path}/Avatars";
- if (!Directory.Exists(folder + avatarpath))
- {
- Directory.CreateDirectory(folder + avatarpath);
- }
-
- List avatarMD5Hashes = WidevineClient.Utils.CalculateFolderMD5(folder + avatarpath);
-
- Uri uri = new(avatarUrl);
- string destinationPath = $"{folder}{avatarpath}/";
-
- var client = new HttpClient();
-
- var request = new HttpRequestMessage
- {
- Method = HttpMethod.Get,
- RequestUri = uri
-
- };
- using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
- response.EnsureSuccessStatusCode();
-
- using var memoryStream = new MemoryStream();
- await response.Content.CopyToAsync(memoryStream);
- memoryStream.Seek(0, SeekOrigin.Begin);
-
- MD5 md5 = MD5.Create();
- byte[] hash = md5.ComputeHash(memoryStream);
- memoryStream.Seek(0, SeekOrigin.Begin);
- if (!avatarMD5Hashes.Contains(BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant()))
- {
- destinationPath = destinationPath + string.Format("{0} {1}.jpg", username, response.Content.Headers.LastModified.HasValue ? response.Content.Headers.LastModified.Value.LocalDateTime.ToString("dd-MM-yyyy") : DateTime.Now.ToString("dd-MM-yyyy"));
-
- using (FileStream fileStream = File.Create(destinationPath))
- {
- await memoryStream.CopyToAsync(fileStream);
- }
- File.SetLastWriteTime(destinationPath, response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now);
- }
- }
-
- if (!string.IsNullOrEmpty(headerUrl))
- {
- string headerpath = $"{path}/Headers";
- if (!Directory.Exists(folder + headerpath))
- {
- Directory.CreateDirectory(folder + headerpath);
- }
-
- List headerMD5Hashes = WidevineClient.Utils.CalculateFolderMD5(folder + headerpath);
-
- Uri uri = new(headerUrl);
- string destinationPath = $"{folder}{headerpath}/";
-
- var client = new HttpClient();
-
- var request = new HttpRequestMessage
- {
- Method = HttpMethod.Get,
- RequestUri = uri
-
- };
- using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
- response.EnsureSuccessStatusCode();
-
- using var memoryStream = new MemoryStream();
- await response.Content.CopyToAsync(memoryStream);
- memoryStream.Seek(0, SeekOrigin.Begin);
-
- MD5 md5 = MD5.Create();
- byte[] hash = md5.ComputeHash(memoryStream);
- memoryStream.Seek(0, SeekOrigin.Begin);
- if (!headerMD5Hashes.Contains(BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant()))
- {
- destinationPath = destinationPath + string.Format("{0} {1}.jpg", username, response.Content.Headers.LastModified.HasValue ? response.Content.Headers.LastModified.Value.LocalDateTime.ToString("dd-MM-yyyy") : DateTime.Now.ToString("dd-MM-yyyy"));
-
- using (FileStream fileStream = File.Create(destinationPath))
- {
- await memoryStream.CopyToAsync(fileStream);
- }
- File.SetLastWriteTime(destinationPath, response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now);
- }
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
-
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- }
-
- private async Task OnFFMPEGDownloadComplete(string tempFilename, DateTime lastModified, string folder, string path, string customFileName, string filename, long media_id, string api_type, ProgressTask task)
- {
- try
- {
- if (File.Exists(tempFilename))
- {
- File.SetLastWriteTime(tempFilename, lastModified);
- }
- if (!string.IsNullOrEmpty(customFileName))
- {
- File.Move(tempFilename, $"{folder + path + "/" + customFileName + ".mp4"}");
- }
-
- // Cleanup Files
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : tempFilename).Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
-
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + ".mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- }
-
- private void OnError(object sender, ConversionErrorEventArgs e)
- {
- // Guard all fields to avoid NullReference exceptions from FFmpeg.NET
- var input = e?.Input?.Name ?? "";
- var output = e?.Output?.Name ?? "";
- var exitCode = e?.Exception?.ExitCode.ToString() ?? "";
- var message = e?.Exception?.Message ?? "";
- var inner = e?.Exception?.InnerException?.Message ?? "";
-
- Log.Error("FFmpeg failed. Input={Input} Output={Output} ExitCode={ExitCode} Message={Message} Inner={Inner}",
- input, output, exitCode, message, inner);
-
- _completionSource?.TrySetResult(false);
- }
-
-
- #region drm posts
- public async Task DownloadMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Free/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Messages/Free/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
-
- if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(messageInfo, messageMedia, fromUser, properties, folder.Split("/")[^1],users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
- public async Task DownloadSingleMessagePreviewDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Messages.Medium? messageMedia, FromUser? fromUser, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Free/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Messages/Free/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
-
- if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(messageInfo, messageMedia, fromUser, properties, folder.Split("/")[^1],users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
-
- public async Task DownloadPurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Paid/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Messages/Paid/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(messageInfo, messageMedia, fromUser, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
- public async Task DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
- {
- path = $"/Messages/Paid/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Messages/Paid/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(messageInfo, messageMedia, fromUser, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
-
- public async Task DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
- {
- path = $"/Posts/Free/{postInfo.id} {postInfo.postedAt:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Posts/Free/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(postInfo, postMedia, author, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
- public async Task DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
- {
- path = $"/Posts/Free/{postInfo.id} {postInfo.postedAt:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Posts/Free/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(postInfo, postMedia, author, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
- public async Task DownloadStreamsDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerPost && streamInfo != null && streamInfo?.id is not null && streamInfo?.postedAt is not null)
- {
- path = $"/Posts/Free/{streamInfo.id} {streamInfo.postedAt:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Posts/Free/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(filenameFormat) && streamInfo != null && streamMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(streamInfo, streamMedia, author, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
- public async Task DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Purchased.List? postInfo, Medium? postMedia, Purchased.FromUser? fromUser, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- string path;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- if (downloadConfig.FolderPerPaidPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
- {
- path = $"/Posts/Paid/{postInfo.id} {postInfo.postedAt.Value:yyyy-MM-dd HH-mm-ss}/Videos";
- }
- else
- {
- path = "/Posts/Paid/Videos";
- }
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
-
- if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(postInfo, postMedia, fromUser, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
-
-
- public async Task DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Archived.List? postInfo, Archived.Medium? postMedia, Archived.Author? author, Dictionary users)
- {
- try
- {
- string customFileName = string.Empty;
- Uri uri = new(url);
- string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
- string path = "/Archived/Posts/Free/Videos";
- if (!Directory.Exists(folder + path))
- {
- Directory.CreateDirectory(folder + path);
- }
-
- if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
- {
- List properties = new();
- string pattern = @"\{(.*?)\}";
- MatchCollection matches = Regex.Matches(filenameFormat, pattern);
- foreach (Match match in matches)
- {
- properties.Add(match.Groups[1].Value);
- }
- Dictionary values = await fileNameHelper.GetFilename(postInfo, postMedia, author, properties, folder.Split("/")[^1], users);
- customFileName = await fileNameHelper.BuildFilename(filenameFormat, values);
- }
-
- if (!await dbHelper.CheckDownloaded(folder, media_id, api_type))
- {
- if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
- {
- return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
- }
- else
- {
- long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
- if (downloadConfig.ShowScrapeSize)
- {
- task.Increment(fileSizeInBytes);
- }
- else
- {
- task.Increment(1);
- }
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(customFileName))
- {
- if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
- {
- string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
- string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
- if (!File.Exists(fullPathWithTheServerFileName))
- {
- return false;
- }
- try
- {
- File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"An error occurred: {ex.Message}");
- return false;
- }
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- await dbHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
- }
- }
-
- if (downloadConfig.ShowScrapeSize)
- {
- long size = await dbHelper.GetStoredFileSize(folder, media_id, api_type);
- task.Increment(size);
- }
- else
- {
- task.Increment(1);
- }
- }
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
- if (ex.InnerException != null)
- {
- Console.WriteLine("\nInner Exception:");
- Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
- }
- }
- return false;
- }
- #endregion
-
- private async Task GetVideoStreamIndexFromMpd(string mpdUrl, string policy, string signature, string kvp, VideoResolution resolution)
- {
- HttpClient client = new();
- HttpRequestMessage request = new(HttpMethod.Get, mpdUrl);
- request.Headers.Add("user-agent", auth.USER_AGENT);
- request.Headers.Add("Accept", "*/*");
- request.Headers.Add("Cookie", $"CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {auth.COOKIE};");
- using (var response = await client.SendAsync(request))
- {
- response.EnsureSuccessStatusCode();
- var body = await response.Content.ReadAsStringAsync();
- XDocument doc = XDocument.Parse(body);
- XNamespace ns = "urn:mpeg:dash:schema:mpd:2011";
- XNamespace cenc = "urn:mpeg:cenc:2013";
- var videoAdaptationSet = doc
- .Descendants(ns + "AdaptationSet")
- .FirstOrDefault(e => (string)e.Attribute("mimeType") == "video/mp4");
-
- if (videoAdaptationSet == null)
- return null;
-
- string targetHeight = resolution switch
- {
- VideoResolution._240 => "240",
- VideoResolution._720 => "720",
- VideoResolution.source => "1280",
- _ => throw new ArgumentOutOfRangeException(nameof(resolution))
- };
-
- var representations = videoAdaptationSet.Elements(ns + "Representation").ToList();
-
- for (int i = 0; i < representations.Count; i++)
- {
- if ((string)representations[i].Attribute("height") == targetHeight)
- return i; // this is the index FFmpeg will use for `-map 0:v:{i}`
- }
- }
-
- return null;
- }
-}
diff --git a/OF DL/Helpers/Interfaces/IAuthHelper.cs b/OF DL/Helpers/Interfaces/IAuthHelper.cs
deleted file mode 100644
index e1d0726..0000000
--- a/OF DL/Helpers/Interfaces/IAuthHelper.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using OF_DL.Entities;
-
-namespace OF_DL.Helpers.Interfaces
-{
- public interface IAuthHelper
- {
- Task SetupBrowser(bool runningInDocker);
- Task GetAuthFromBrowser(bool isDocker = false);
- }
-}
diff --git a/OF DL/Helpers/Interfaces/IDownloadHelper.cs b/OF DL/Helpers/Interfaces/IDownloadHelper.cs
deleted file mode 100644
index 62672ae..0000000
--- a/OF DL/Helpers/Interfaces/IDownloadHelper.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using OF_DL.Entities;
-using OF_DL.Entities.Archived;
-using OF_DL.Entities.Messages;
-using OF_DL.Entities.Post;
-using OF_DL.Entities.Purchased;
-using OF_DL.Entities.Streams;
-using Spectre.Console;
-using static OF_DL.Entities.Messages.Messages;
-using FromUser = OF_DL.Entities.Messages.FromUser;
-
-namespace OF_DL.Helpers
-{
- public interface IDownloadHelper
- {
- Task CalculateTotalFileSize(List urls);
- Task DownloadArchivedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Archived.List messageInfo, Archived.Medium messageMedia, Archived.Author author, Dictionary users);
- Task DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Archived.List postInfo, Archived.Medium postMedia, Archived.Author author, Dictionary users);
- Task DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary users);
- Task DownloadAvatarHeader(string? avatarUrl, string? headerUrl, string folder, string username);
- Task DownloadMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Messages.List messageInfo, Messages.Medium messageMedia, Messages.FromUser fromUser, Dictionary users);
- Task DownloadMessageMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Messages.List messageInfo, Messages.Medium messageMedia, Messages.FromUser fromUser, Dictionary users);
- Task DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Post.List postInfo, Post.Medium postMedia, Post.Author author, Dictionary users);
- Task DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary users);
- Task DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary users);
- Task DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary users);
-
- Task DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary users);
- Task DownloadPurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary users);
-
- Task DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary users);
- Task DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List postInfo, Medium postMedia, Purchased.FromUser fromUser, Dictionary users);
- Task DownloadPurchasedPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary users);
- Task DownloadStoryMedia(string url, string folder, long media_id, string api_type, ProgressTask task);
- Task DownloadStreamMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary users);
- Task DownloadStreamsDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Streams.List streamInfo, Streams.Medium streamMedia, Streams.Author author, Dictionary users);
- Task DownloadSingleMessagePreviewDRMVideo(string policy, string signature, string kvp, string url,
- string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
- ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
- FromUser? fromUser, Dictionary users);
-
- Task DownloadMessagePreviewMedia(string url, string folder, long media_id, string api_type,
- ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
- FromUser? fromUser, Dictionary users);
- }
-}
diff --git a/OF DL/Program.cs b/OF DL/Program.cs
index d79378b..041900c 100644
--- a/OF DL/Program.cs
+++ b/OF DL/Program.cs
@@ -8,6 +8,7 @@ using OF_DL.Entities.Purchased;
using OF_DL.Entities.Streams;
using OF_DL.Enumerations;
using OF_DL.Enumurations;
+using OF_DL.CLI;
using OF_DL.Helpers;
using OF_DL.Services;
using Serilog;
@@ -17,13 +18,11 @@ using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using static OF_DL.Entities.Messages.Messages;
using Microsoft.Extensions.DependencyInjection;
-using OF_DL.Helpers.Interfaces;
namespace OF_DL;
public class Program(IServiceProvider serviceProvider)
{
- public int MAX_AGE = 0;
public static List paid_post_ids = new();
private static bool clientIdBlobMissing = false;
@@ -79,68 +78,59 @@ public class Program(IServiceProvider serviceProvider)
AnsiConsole.Markup("Documentation: [link]https://docs.ofdl.tools/[/]\n");
AnsiConsole.Markup("Discord server: [link]https://discord.com/invite/6bUW8EJ53j[/]\n\n");
- // 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;
- }
-
- 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());
+ ServiceCollection services = await ConfigureServices(args);
var serviceProvider = services.BuildServiceProvider();
// Get the Program instance and run
var program = serviceProvider.GetRequiredService();
- await program.RunAsync(args, configService.IsCliNonInteractive);
+ await program.RunAsync();
}
- private static void ConfigureServices(IServiceCollection services, Entities.Config config, ILoggingService loggingService)
+ private static async Task ConfigureServices(string[] args)
{
- services.AddSingleton(config);
- services.AddSingleton(config);
- services.AddSingleton(config);
+ // Set up dependency injection with LoggingService and ConfigService
+ ServiceCollection services = new();
+ services.AddSingleton();
+ services.AddSingleton();
+ ServiceProvider tempServiceProvider = services.BuildServiceProvider();
+
+ ILoggingService loggingService = tempServiceProvider.GetRequiredService();
+ 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);
+ }
+
+ AnsiConsole.Markup("[green]config.conf located successfully!\n[/]");
+
+ // Set up full dependency injection with loaded config
+ services = [];
services.AddSingleton(loggingService);
-
- services.AddSingleton();
+ services.AddSingleton(configService);
services.AddSingleton();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
-
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
- }
- private async Task RunAsync(string[] args, bool cliNonInteractive)
+ return services;
+ }
+
+ private async Task RunAsync()
{
- var config = serviceProvider.GetRequiredService();
- var authService = serviceProvider.GetRequiredService();
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IAuthService authService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
try
{
@@ -158,7 +148,7 @@ public class Program(IServiceProvider serviceProvider)
Console.Write("Press any key to continue.\n");
Log.Error("Windows version prior to 10.x: {0}", os.VersionString);
- if (!cliNonInteractive)
+ if (!configService.CurrentConfig.NonInteractiveMode)
{
Console.ReadKey();
}
@@ -241,13 +231,13 @@ public class Program(IServiceProvider serviceProvider)
{
// File exists but failed to load
Log.Information("Auth file found but could not be deserialized");
- if (!config!.DisableBrowserAuth)
+ if (!configService.CurrentConfig!.DisableBrowserAuth)
{
Log.Debug("Deleting auth.json");
File.Delete("auth.json");
}
- if (cliNonInteractive)
+ if (configService.CurrentConfig.NonInteractiveMode)
{
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");
@@ -259,7 +249,7 @@ public class Program(IServiceProvider serviceProvider)
}
- if (!config!.DisableBrowserAuth)
+ if (!configService.CurrentConfig!.DisableBrowserAuth)
{
await LoadAuthFromBrowser();
}
@@ -276,7 +266,7 @@ public class Program(IServiceProvider serviceProvider)
}
else
{
- if (cliNonInteractive)
+ if (configService.CurrentConfig.NonInteractiveMode)
{
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");
@@ -287,7 +277,7 @@ public class Program(IServiceProvider serviceProvider)
Environment.Exit(2);
}
- if (!config!.DisableBrowserAuth)
+ if (!configService.CurrentConfig!.DisableBrowserAuth)
{
await LoadAuthFromBrowser();
}
@@ -323,7 +313,7 @@ public class Program(IServiceProvider serviceProvider)
AnsiConsole.MarkupLine($"[red]Press any key to exit.[/]");
Log.Error("rules.json processing failed.", e.Message);
- if (!cliNonInteractive)
+ if (!configService.CurrentConfig.NonInteractiveMode)
{
Console.ReadKey();
}
@@ -331,37 +321,37 @@ public class Program(IServiceProvider serviceProvider)
}
}
- if(cliNonInteractive)
+ if(configService.CurrentConfig.NonInteractiveMode)
{
// CLI argument overrides configuration
- config!.NonInteractiveMode = true;
+ configService.CurrentConfig!.NonInteractiveMode = true;
Log.Debug("NonInteractiveMode = true");
}
- if(config!.NonInteractiveMode)
+ if(configService.CurrentConfig!.NonInteractiveMode)
{
- cliNonInteractive = true; // If it was set in the config, reset the cli value so exception handling works
+ configService.CurrentConfig.NonInteractiveMode = true; // If it was set in the config, reset the cli value so exception handling works
Log.Debug("NonInteractiveMode = true (set via config)");
}
var ffmpegFound = false;
var pathAutoDetected = false;
- if (!string.IsNullOrEmpty(config!.FFmpegPath) && ValidateFilePath(config.FFmpegPath))
+ if (!string.IsNullOrEmpty(configService.CurrentConfig!.FFmpegPath) && ValidateFilePath(configService.CurrentConfig.FFmpegPath))
{
// FFmpeg path is set in config.json and is valid
ffmpegFound = true;
- Log.Debug($"FFMPEG found: {config.FFmpegPath}");
+ Log.Debug($"FFMPEG found: {configService.CurrentConfig.FFmpegPath}");
Log.Debug("FFMPEG path set in config.conf");
}
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 = authService.CurrentAuth.FFMPEG_PATH;
- Log.Debug($"FFMPEG found: {config.FFmpegPath}");
+ configService.CurrentConfig.FFmpegPath = authService.CurrentAuth.FFMPEG_PATH;
+ Log.Debug($"FFMPEG found: {configService.CurrentConfig.FFmpegPath}");
Log.Debug("FFMPEG path set in auth.json");
}
- else if (string.IsNullOrEmpty(config.FFmpegPath))
+ else if (string.IsNullOrEmpty(configService.CurrentConfig.FFmpegPath))
{
// FFmpeg path is not set in config.conf, so we will try to locate it in the PATH or current directory
var ffmpegPath = GetFullPath("ffmpeg");
@@ -370,7 +360,7 @@ public class Program(IServiceProvider serviceProvider)
// FFmpeg is found in the PATH or current directory
ffmpegFound = true;
pathAutoDetected = true;
- config.FFmpegPath = ffmpegPath;
+ configService.CurrentConfig.FFmpegPath = ffmpegPath;
Log.Debug($"FFMPEG found: {ffmpegPath}");
Log.Debug("FFMPEG path found via PATH or current directory");
}
@@ -383,7 +373,7 @@ public class Program(IServiceProvider serviceProvider)
// FFmpeg windows executable is found in the PATH or current directory
ffmpegFound = true;
pathAutoDetected = true;
- config.FFmpegPath = ffmpegPath;
+ configService.CurrentConfig.FFmpegPath = ffmpegPath;
Log.Debug($"FFMPEG found: {ffmpegPath}");
Log.Debug("FFMPEG path found in windows excutable directory");
}
@@ -394,7 +384,7 @@ public class Program(IServiceProvider serviceProvider)
{
if (pathAutoDetected)
{
- AnsiConsole.Markup($"[green]FFmpeg located successfully. Path auto-detected: {config.FFmpegPath}\n[/]");
+ AnsiConsole.Markup($"[green]FFmpeg located successfully. Path auto-detected: {configService.CurrentConfig.FFmpegPath}\n[/]");
}
else
{
@@ -402,9 +392,9 @@ public class Program(IServiceProvider serviceProvider)
}
// Escape backslashes in the path for Windows
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && config.FFmpegPath!.Contains(@":\") && !config.FFmpegPath.Contains(@":\\"))
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && configService.CurrentConfig.FFmpegPath!.Contains(@":\") && !configService.CurrentConfig.FFmpegPath.Contains(@":\\"))
{
- config.FFmpegPath = config.FFmpegPath.Replace(@"\", @"\\");
+ configService.CurrentConfig.FFmpegPath = configService.CurrentConfig.FFmpegPath.Replace(@"\", @"\\");
}
// Get FFmpeg version
@@ -412,7 +402,7 @@ public class Program(IServiceProvider serviceProvider)
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo
{
- FileName = config.FFmpegPath,
+ FileName = configService.CurrentConfig.FFmpegPath,
Arguments = "-version",
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -465,8 +455,8 @@ public class Program(IServiceProvider serviceProvider)
else
{
AnsiConsole.Markup("[red]Cannot locate FFmpeg; please modify config.conf with the correct path. Press any key to exit.[/]");
- Log.Error($"Cannot locate FFmpeg with path: {config.FFmpegPath}");
- if (!config.NonInteractiveMode)
+ Log.Error($"Cannot locate FFmpeg with path: {configService.CurrentConfig.FFmpegPath}");
+ if (!configService.CurrentConfig.NonInteractiveMode)
{
Console.ReadKey();
}
@@ -500,16 +490,13 @@ public class Program(IServiceProvider serviceProvider)
AnsiConsole.Markup("[yellow]device_client_id_blob and/or device_private_key missing, https://ofdl.tools/ or https://cdrm-project.com/ will be used instead for DRM protected videos\n[/]");
}
- //Check if auth is valid
- var apiHelper = ActivatorUtilities.CreateInstance(serviceProvider, authService.CurrentAuth);
-
- Entities.User? validate = await apiHelper.GetUserInfo($"/users/me");
+ Entities.User? validate = await apiService.GetUserInfo($"/users/me");
if (validate == null || (validate?.name == null && validate?.username == null))
{
Log.Error("Auth failed");
authService.CurrentAuth = null;
- if (!config!.DisableBrowserAuth)
+ if (!configService.CurrentConfig!.DisableBrowserAuth)
{
if (File.Exists("auth.json"))
{
@@ -517,7 +504,7 @@ public class Program(IServiceProvider serviceProvider)
}
}
- if (!cliNonInteractive && !config!.DisableBrowserAuth)
+ if (!configService.CurrentConfig.NonInteractiveMode && !configService.CurrentConfig!.DisableBrowserAuth)
{
await LoadAuthFromBrowser();
}
@@ -532,7 +519,7 @@ public class Program(IServiceProvider serviceProvider)
}
AnsiConsole.Markup($"[green]Logged In successfully as {validate.name} {validate.username}\n[/]");
- await DownloadAllData(apiHelper, authService.CurrentAuth, config);
+ await DownloadAllData();
}
catch (Exception ex)
{
@@ -545,7 +532,7 @@ public class Program(IServiceProvider serviceProvider)
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
}
Console.WriteLine("\nPress any key to exit.");
- if (!cliNonInteractive)
+ if (!configService.CurrentConfig.NonInteractiveMode)
{
Console.ReadKey();
}
@@ -553,10 +540,15 @@ public class Program(IServiceProvider serviceProvider)
}
}
- private async Task DownloadAllData(IAPIHelper m_ApiHelper, Auth Auth, Entities.Config Config)
+ private async Task DownloadAllData()
{
- IDBHelper dBHelper = serviceProvider.GetRequiredService();
+ IDBService dbService = serviceProvider.GetRequiredService();
IConfigService configService = serviceProvider.GetRequiredService();
+ IAuthService authService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
+
+ Entities.Config Config = configService.CurrentConfig!;
Log.Debug("Calling DownloadAllData");
@@ -564,7 +556,7 @@ public class Program(IServiceProvider serviceProvider)
{
DateTime startTime = DateTime.Now;
Dictionary users = new();
- Dictionary activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
+ Dictionary activeSubs = await apiService.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions);
Log.Debug("Subscriptions: ");
@@ -580,7 +572,7 @@ public class Program(IServiceProvider serviceProvider)
{
Log.Debug("Inactive Subscriptions: ");
- Dictionary expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
+ Dictionary expiredSubs = await apiService.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions);
foreach (KeyValuePair expiredSub in expiredSubs)
{
if (!users.ContainsKey(expiredSub.Key))
@@ -591,7 +583,7 @@ public class Program(IServiceProvider serviceProvider)
}
}
- Dictionary lists = await m_ApiHelper.GetLists("/lists", Config);
+ Dictionary lists = await apiService.GetLists("/lists");
// Remove users from the list if they are in the ignored list
if (!string.IsNullOrEmpty(Config.IgnoredUsersListName))
@@ -603,12 +595,12 @@ public class Program(IServiceProvider serviceProvider)
}
else
{
- var ignoredUsernames = await m_ApiHelper.GetListUsers($"/lists/{ignoredUsersListId}/users", Config) ?? [];
+ var ignoredUsernames = await apiService.GetListUsers($"/lists/{ignoredUsersListId}/users") ?? [];
users = users.Where(x => !ignoredUsernames.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
}
}
- await dBHelper.CreateUsersDB(users);
+ await dbService.CreateUsersDB(users);
KeyValuePair> hasSelectedUsersKVP;
if(Config.NonInteractiveMode && Config.NonInteractiveModePurchasedTab)
{
@@ -621,16 +613,16 @@ public class Program(IServiceProvider serviceProvider)
else if (Config.NonInteractiveMode && !string.IsNullOrEmpty(Config.NonInteractiveModeListName))
{
var listId = lists[Config.NonInteractiveModeListName];
- var listUsernames = await m_ApiHelper.GetListUsers($"/lists/{listId}/users", Config) ?? [];
+ var listUsernames = await apiService.GetListUsers($"/lists/{listId}/users") ?? [];
var selectedUsers = users.Where(x => listUsernames.Contains(x.Key)).Distinct().ToDictionary(x => x.Key, x => x.Value);
hasSelectedUsersKVP = new KeyValuePair>(true, selectedUsers);
}
else
{
var loggingService = serviceProvider.GetRequiredService();
- var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists, configService, loggingService);
+ var userSelectionResult = await HandleUserSelection(users, lists);
- Config = userSelectionResult.updatedConfig;
+ Config = configService.CurrentConfig!;
hasSelectedUsersKVP = new KeyValuePair>(userSelectionResult.IsExit, userSelectionResult.selectedUsers);
}
@@ -687,22 +679,15 @@ public class Program(IServiceProvider serviceProvider)
AnsiConsole.Markup($"[red]Folder for {username} already created\n[/]");
}
- await dBHelper.CreateDB(path);
+ await dbService.CreateDB(path);
- var downloadContext = ActivatorUtilities.CreateInstance(
- serviceProvider,
- Config,
- GetCreatorFileNameFormatConfig(Config, username),
- m_ApiHelper,
- dBHelper);
-
- await DownloadSinglePost(downloadContext, post_id, path, users);
+ await DownloadSinglePost(username, post_id, path, users);
}
}
}
else if (hasSelectedUsersKVP.Key && hasSelectedUsersKVP.Value != null && hasSelectedUsersKVP.Value.ContainsKey("PurchasedTab"))
{
- Dictionary purchasedTabUsers = await m_ApiHelper.GetPurchasedTabUsers("/posts/paid/all", Config, users);
+ Dictionary purchasedTabUsers = await apiService.GetPurchasedTabUsers("/posts/paid/all", users);
AnsiConsole.Markup($"[red]Checking folders for Users in Purchased Tab\n[/]");
foreach (KeyValuePair user in purchasedTabUsers)
{
@@ -718,7 +703,7 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Download path: {path}");
- await dBHelper.CheckUsername(user, path);
+ await dbService.CheckUsername(user, path);
if (!Directory.Exists(path))
{
@@ -732,9 +717,9 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Folder for {user.Key} already created");
}
- Entities.User user_info = await m_ApiHelper.GetUserInfo($"/users/{user.Key}");
+ Entities.User user_info = await apiService.GetUserInfo($"/users/{user.Key}");
- await dBHelper.CreateDB(path);
+ await dbService.CreateDB(path);
}
string p = "";
@@ -749,7 +734,7 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Download path: {p}");
- List purchasedTabCollections = await m_ApiHelper.GetPurchasedTab("/posts/paid/all", p, Config, users);
+ List purchasedTabCollections = await apiService.GetPurchasedTab("/posts/paid/all", p, users);
foreach(PurchasedTabCollection purchasedTabCollection in purchasedTabCollections)
{
AnsiConsole.Markup($"[red]\nScraping Data for {purchasedTabCollection.Username}\n[/]");
@@ -766,17 +751,10 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Download path: {path}");
- var downloadContext = ActivatorUtilities.CreateInstance(
- serviceProvider,
- Config,
- GetCreatorFileNameFormatConfig(Config, purchasedTabCollection.Username),
- m_ApiHelper,
- dBHelper);
-
int paidPostCount = 0;
int paidMessagesCount = 0;
- paidPostCount = await DownloadPaidPostsPurchasedTab(downloadContext, purchasedTabCollection.PaidPosts, users.FirstOrDefault(u => u.Value == purchasedTabCollection.UserId), paidPostCount, path, users);
- paidMessagesCount = await DownloadPaidMessagesPurchasedTab(downloadContext, purchasedTabCollection.PaidMessages, users.FirstOrDefault(u => u.Value == purchasedTabCollection.UserId), paidMessagesCount, path, users);
+ paidPostCount = await DownloadPaidPostsPurchasedTab(purchasedTabCollection.Username, purchasedTabCollection.PaidPosts, users.FirstOrDefault(u => u.Value == purchasedTabCollection.UserId), paidPostCount, path, users);
+ paidMessagesCount = await DownloadPaidMessagesPurchasedTab(purchasedTabCollection.Username, purchasedTabCollection.PaidMessages, users.FirstOrDefault(u => u.Value == purchasedTabCollection.UserId), paidMessagesCount, path, users);
AnsiConsole.Markup("\n");
AnsiConsole.Write(new BreakdownChart()
@@ -816,7 +794,7 @@ public class Program(IServiceProvider serviceProvider)
{
long message_id = Convert.ToInt64(messageUrl.Split("?firstId=")[1]);
long user_id = Convert.ToInt64(messageUrl.Split("/")[6]);
- JObject user = await m_ApiHelper.GetUserInfoById($"/users/list?x[]={user_id.ToString()}");
+ JObject user = await apiService.GetUserInfoById($"/users/list?x[]={user_id.ToString()}");
string username = string.Empty;
Log.Debug($"Message ID: {message_id}");
@@ -857,16 +835,9 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Folder for {username} already created");
}
- await dBHelper.CreateDB(path);
+ await dbService.CreateDB(path);
- var downloadContext = ActivatorUtilities.CreateInstance(
- serviceProvider,
- Config,
- GetCreatorFileNameFormatConfig(Config, username),
- m_ApiHelper,
- dBHelper);
-
- await DownloadPaidMessage(downloadContext, hasSelectedUsersKVP, username, 1, path, message_id);
+ await DownloadPaidMessage(username, hasSelectedUsersKVP, 1, path, message_id);
}
}
else if (hasSelectedUsersKVP.Key && !hasSelectedUsersKVP.Value.ContainsKey("ConfigChanged"))
@@ -898,7 +869,7 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug("Download path: ", path);
- await dBHelper.CheckUsername(user, path);
+ await dbService.CheckUsername(user, path);
if (!Directory.Exists(path))
{
@@ -912,62 +883,55 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Folder for {user.Key} already created");
}
- await dBHelper.CreateDB(path);
-
- var downloadContext = ActivatorUtilities.CreateInstance(
- serviceProvider,
- Config,
- GetCreatorFileNameFormatConfig(Config, user.Key),
- m_ApiHelper,
- dBHelper);
+ await dbService.CreateDB(path);
if (Config.DownloadAvatarHeaderPhoto)
{
- Entities.User? user_info = await m_ApiHelper.GetUserInfo($"/users/{user.Key}");
+ Entities.User? user_info = await apiService.GetUserInfo($"/users/{user.Key}");
if (user_info != null)
{
- await downloadContext.DownloadHelper.DownloadAvatarHeader(user_info.avatar, user_info.header, path, user.Key);
+ await downloadService.DownloadAvatarHeader(user_info.avatar, user_info.header, path, user.Key);
}
}
if (Config.DownloadPaidPosts)
{
- paidPostCount = await DownloadPaidPosts(downloadContext, hasSelectedUsersKVP, user, paidPostCount, path);
+ paidPostCount = await DownloadPaidPosts(user.Key, hasSelectedUsersKVP, user, paidPostCount, path);
}
if (Config.DownloadPosts)
{
- postCount = await DownloadFreePosts(downloadContext, hasSelectedUsersKVP, user, postCount, path);
+ postCount = await DownloadFreePosts(user.Key, hasSelectedUsersKVP, user, postCount, path);
}
if (Config.DownloadArchived)
{
- archivedCount = await DownloadArchived(downloadContext, hasSelectedUsersKVP, user, archivedCount, path);
+ archivedCount = await DownloadArchived(user.Key, hasSelectedUsersKVP, user, archivedCount, path);
}
if (Config.DownloadStreams)
{
- streamsCount = await DownloadStreams(downloadContext, hasSelectedUsersKVP, user, streamsCount, path);
+ streamsCount = await DownloadStreams(user.Key, hasSelectedUsersKVP, user, streamsCount, path);
}
if (Config.DownloadStories)
{
- storiesCount = await DownloadStories(downloadContext, user, storiesCount, path);
+ storiesCount = await DownloadStories(user.Key, user, storiesCount, path);
}
if (Config.DownloadHighlights)
{
- highlightsCount = await DownloadHighlights(downloadContext, user, highlightsCount, path);
+ highlightsCount = await DownloadHighlights(user.Key, user, highlightsCount, path);
}
if (Config.DownloadMessages)
{
- messagesCount = await DownloadMessages(downloadContext, hasSelectedUsersKVP, user, messagesCount, path);
+ messagesCount = await DownloadMessages(user.Key, hasSelectedUsersKVP, user, messagesCount, path);
}
if (Config.DownloadPaidMessages)
{
- paidMessagesCount = await DownloadPaidMessages(downloadContext, hasSelectedUsersKVP, user, paidMessagesCount, path);
+ paidMessagesCount = await DownloadPaidMessages(user.Key, hasSelectedUsersKVP, user, paidMessagesCount, path);
}
AnsiConsole.Markup("\n");
@@ -998,570 +962,276 @@ public class Program(IServiceProvider serviceProvider)
} while (!Config.NonInteractiveMode);
}
- private static IFileNameFormatConfig GetCreatorFileNameFormatConfig(Entities.Config config, string userName)
+ private async Task DownloadPaidMessages(string username, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int paidMessagesCount, string path)
{
- FileNameFormatConfig combinedConfig = new FileNameFormatConfig();
-
- Func func = (val1, val2) =>
- {
- if (string.IsNullOrEmpty(val1))
- return val2;
- else
- return val1;
- };
-
- if(config.CreatorConfigs.ContainsKey(userName))
- {
- CreatorConfig creatorConfig = config.CreatorConfigs[userName];
- if(creatorConfig != null)
- {
- combinedConfig.PaidMessageFileNameFormat = creatorConfig.PaidMessageFileNameFormat;
- combinedConfig.PostFileNameFormat = creatorConfig.PostFileNameFormat;
- combinedConfig.MessageFileNameFormat = creatorConfig.MessageFileNameFormat;
- combinedConfig.PaidPostFileNameFormat = creatorConfig.PaidPostFileNameFormat;
- }
- }
-
- combinedConfig.PaidMessageFileNameFormat = func(combinedConfig.PaidMessageFileNameFormat, config.PaidMessageFileNameFormat);
- combinedConfig.PostFileNameFormat = func(combinedConfig.PostFileNameFormat, config.PostFileNameFormat);
- combinedConfig.MessageFileNameFormat = func(combinedConfig.MessageFileNameFormat, config.MessageFileNameFormat);
- combinedConfig.PaidPostFileNameFormat = func(combinedConfig.PaidPostFileNameFormat, config.PaidPostFileNameFormat);
-
- Log.Debug($"PaidMessageFilenameFormat: {combinedConfig.PaidMessageFileNameFormat}");
- Log.Debug($"PostFileNameFormat: {combinedConfig.PostFileNameFormat}");
- Log.Debug($"MessageFileNameFormat: {combinedConfig.MessageFileNameFormat}");
- Log.Debug($"PaidPostFileNameFormatt: {combinedConfig.PaidPostFileNameFormat}");
-
- return combinedConfig;
- }
-
- private static async Task DownloadPaidMessages(IDownloadContext downloadContext, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int paidMessagesCount, string path)
- {
- Log.Debug($"Calling DownloadPaidMessages - {user.Key}");
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
PaidMessageCollection paidMessageCollection = new PaidMessageCollection();
await AnsiConsole.Status()
.StartAsync("[red]Getting Paid Messages[/]", async ctx =>
{
- paidMessageCollection = await downloadContext.ApiHelper.GetPaidMessages("/posts/paid/chat", path, user.Key, downloadContext.DownloadConfig!, ctx);
+ paidMessageCollection = await apiService.GetPaidMessages("/posts/paid/chat", path, user.Key, ctx);
});
- int oldPaidMessagesCount = 0;
- int newPaidMessagesCount = 0;
+
if (paidMessageCollection != null && paidMessageCollection.PaidMessages.Count > 0)
{
AnsiConsole.Markup($"[red]Found {paidMessageCollection.PaidMessages.Count} Media from {paidMessageCollection.PaidMessageObjects.Count} Paid Messages\n[/]");
- Log.Debug($"Found {paidMessageCollection.PaidMessages.Count} Media from {paidMessageCollection.PaidMessageObjects.Count} Paid Messages");
- paidMessagesCount = paidMessageCollection.PaidMessages.Count;
+
long totalSize = 0;
- if (downloadContext.DownloadConfig.ShowScrapeSize)
+ if (configService.CurrentConfig.ShowScrapeSize)
{
- totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(paidMessageCollection.PaidMessages.Values.ToList());
+ totalSize = await downloadService.CalculateTotalFileSize(paidMessageCollection.PaidMessages.Values.ToList());
}
else
{
- totalSize = paidMessagesCount;
+ totalSize = paidMessageCollection.PaidMessages.Count;
}
+
+ DownloadResult result = null;
await AnsiConsole.Progress()
- .Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
+ .Columns(GetProgressColumns(configService.CurrentConfig.ShowScrapeSize))
.StartAsync(async ctx =>
{
- // Define tasks
var task = ctx.AddTask($"[red]Downloading {paidMessageCollection.PaidMessages.Count} Paid Messages[/]", autoStart: false);
- Log.Debug($"Downloading {paidMessageCollection.PaidMessages.Count} Paid Messages");
task.MaxValue = totalSize;
task.StartTask();
- foreach (KeyValuePair paidMessageKVP in paidMessageCollection.PaidMessages)
- {
- bool isNew;
- if (paidMessageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
- {
- string[] messageUrlParsed = paidMessageKVP.Value.Split(',');
- string mpdURL = messageUrlParsed[0];
- string policy = messageUrlParsed[1];
- string signature = messageUrlParsed[2];
- string kvp = messageUrlParsed[3];
- string mediaId = messageUrlParsed[4];
- string messageId = messageUrlParsed[5];
- string? licenseURL = null;
- string? pssh = await downloadContext.ApiHelper.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
- if (pssh != null)
- {
- DateTime lastModified = await downloadContext.ApiHelper.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
- Dictionary drmHeaders = downloadContext.ApiHelper.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/message/{messageId}", "?type=widevine");
- string decryptionKey;
- if (clientIdBlobMissing || devicePrivateKeyMissing)
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyOFDL(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine", pssh);
- }
- else
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyCDM(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine", pssh);
- }
+ var progressReporter = new SpectreProgressReporter(task);
+ result = await downloadService.DownloadPaidMessages(username, path, hasSelectedUsersKVP.Value, clientIdBlobMissing, devicePrivateKeyMissing, paidMessageCollection, progressReporter);
- Medium? mediaInfo = paidMessageCollection.PaidMessageMedia.FirstOrDefault(m => m.id == paidMessageKVP.Key);
- Purchased.List? messageInfo = paidMessageCollection.PaidMessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
-
- isNew = await downloadContext.DownloadHelper.DownloadPurchasedMessageDRMVideo(
- policy: policy,
- signature: signature,
- kvp: kvp,
- url: mpdURL,
- decryptionKey: decryptionKey,
- folder: path,
- lastModified: lastModified,
- media_id: paidMessageKVP.Key,
- api_type: "Messages",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.PaidMessageFileNameFormat ?? string.Empty,
- messageInfo: messageInfo,
- messageMedia: mediaInfo,
- fromUser: messageInfo?.fromUser,
- users: hasSelectedUsersKVP.Value);
-
- if (isNew)
- {
- newPaidMessagesCount++;
- }
- else
- {
- oldPaidMessagesCount++;
- }
- }
- }
- else
- {
- Medium? mediaInfo = paidMessageCollection.PaidMessageMedia.FirstOrDefault(m => m.id == paidMessageKVP.Key);
- Purchased.List messageInfo = paidMessageCollection.PaidMessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
-
- isNew = await downloadContext.DownloadHelper.DownloadPurchasedMedia(
- url: paidMessageKVP.Value,
- folder: path,
- media_id: paidMessageKVP.Key,
- api_type: "Messages",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.PaidMessageFileNameFormat ?? string.Empty,
- messageInfo: messageInfo,
- messageMedia: mediaInfo,
- fromUser: messageInfo?.fromUser,
- users: hasSelectedUsersKVP.Value);
- if (isNew)
- {
- newPaidMessagesCount++;
- }
- else
- {
- oldPaidMessagesCount++;
- }
- }
- }
task.StopTask();
});
- AnsiConsole.Markup($"[red]Paid Messages Already Downloaded: {oldPaidMessagesCount} New Paid Messages Downloaded: {newPaidMessagesCount}[/]\n");
+
+ AnsiConsole.Markup($"[red]Paid Messages Already Downloaded: {result.ExistingDownloads} New Paid Messages Downloaded: {result.NewDownloads}[/]\n");
+ return result.TotalCount;
}
else
{
AnsiConsole.Markup($"[red]Found 0 Paid Messages\n[/]");
+ return 0;
}
-
- return paidMessagesCount;
}
-
- private static async Task DownloadMessages(IDownloadContext downloadContext, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int messagesCount, string path)
+ private async Task DownloadMessages(string username, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int messagesCount, string path)
{
- Log.Debug($"Calling DownloadMessages - {user.Key}");
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
MessageCollection messages = new MessageCollection();
await AnsiConsole.Status()
.StartAsync("[red]Getting Messages[/]", async ctx =>
{
- messages = await downloadContext.ApiHelper.GetMessages($"/chats/{user.Value}/messages", path, downloadContext.DownloadConfig!, ctx);
+ messages = await apiService.GetMessages($"/chats/{user.Value}/messages", path, ctx);
});
- int oldMessagesCount = 0;
- int newMessagesCount = 0;
+
if (messages != null && messages.Messages.Count > 0)
{
AnsiConsole.Markup($"[red]Found {messages.Messages.Count} Media from {messages.MessageObjects.Count} Messages\n[/]");
- Log.Debug($"[red]Found {messages.Messages.Count} Media from {messages.MessageObjects.Count} Messages");
- messagesCount = messages.Messages.Count;
+
long totalSize = 0;
- if (downloadContext.DownloadConfig.ShowScrapeSize)
+ if (configService.CurrentConfig.ShowScrapeSize)
{
- totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(messages.Messages.Values.ToList());
+ totalSize = await downloadService.CalculateTotalFileSize(messages.Messages.Values.ToList());
}
else
{
- totalSize = messagesCount;
+ totalSize = messages.Messages.Count;
}
+
+ DownloadResult result = null;
await AnsiConsole.Progress()
- .Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
+ .Columns(GetProgressColumns(configService.CurrentConfig.ShowScrapeSize))
.StartAsync(async ctx =>
{
- // Define tasks
var task = ctx.AddTask($"[red]Downloading {messages.Messages.Count} Messages[/]", autoStart: false);
- Log.Debug($"Downloading {messages.Messages.Count} Messages");
task.MaxValue = totalSize;
task.StartTask();
- foreach (KeyValuePair messageKVP in messages.Messages)
- {
- bool isNew;
- if (messageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
- {
- string[] messageUrlParsed = messageKVP.Value.Split(',');
- string mpdURL = messageUrlParsed[0];
- string policy = messageUrlParsed[1];
- string signature = messageUrlParsed[2];
- string kvp = messageUrlParsed[3];
- string mediaId = messageUrlParsed[4];
- string messageId = messageUrlParsed[5];
- string? licenseURL = null;
- string? pssh = await downloadContext.ApiHelper.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
- if (pssh != null)
- {
- DateTime lastModified = await downloadContext.ApiHelper.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
- Dictionary drmHeaders = downloadContext.ApiHelper.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/message/{messageId}", "?type=widevine");
- string decryptionKey;
- if (clientIdBlobMissing || devicePrivateKeyMissing)
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyOFDL(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine", pssh);
- }
- else
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyCDM(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine", pssh);
- }
- Messages.Medium? mediaInfo = messages.MessageMedia.FirstOrDefault(m => m.id == messageKVP.Key);
- Messages.List? messageInfo = messages.MessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
- isNew = await downloadContext.DownloadHelper.DownloadMessageDRMVideo(
- policy: policy,
- signature: signature,
- kvp: kvp,
- url: mpdURL,
- decryptionKey: decryptionKey,
- folder: path,
- lastModified: lastModified,
- media_id: messageKVP.Key,
- api_type: "Messages",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.MessageFileNameFormat ?? string.Empty,
- messageInfo: messageInfo,
- messageMedia: mediaInfo,
- fromUser: messageInfo?.fromUser,
- users: hasSelectedUsersKVP.Value);
+ var progressReporter = new SpectreProgressReporter(task);
+ result = await downloadService.DownloadMessages(username, user.Value, path, hasSelectedUsersKVP.Value, clientIdBlobMissing, devicePrivateKeyMissing, messages, progressReporter);
-
- if (isNew)
- {
- newMessagesCount++;
- }
- else
- {
- oldMessagesCount++;
- }
- }
- }
- else
- {
- Messages.Medium? mediaInfo = messages.MessageMedia.FirstOrDefault(m => m.id == messageKVP.Key);
- Messages.List? messageInfo = messages.MessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
-
- isNew = await downloadContext.DownloadHelper.DownloadMessageMedia(
- url: messageKVP.Value,
- folder: path,
- media_id: messageKVP.Key,
- api_type: "Messages",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig!.MessageFileNameFormat ?? string.Empty,
- messageInfo: messageInfo,
- messageMedia: mediaInfo,
- fromUser: messageInfo?.fromUser,
- users: hasSelectedUsersKVP.Value);
-
- if (isNew)
- {
- newMessagesCount++;
- }
- else
- {
- oldMessagesCount++;
- }
- }
- }
task.StopTask();
});
- AnsiConsole.Markup($"[red]Messages Already Downloaded: {oldMessagesCount} New Messages Downloaded: {newMessagesCount}[/]\n");
+
+ AnsiConsole.Markup($"[red]Messages Already Downloaded: {result.ExistingDownloads} New Messages Downloaded: {result.NewDownloads}[/]\n");
+ return result.TotalCount;
}
else
{
AnsiConsole.Markup($"[red]Found 0 Messages\n[/]");
+ return 0;
}
-
- return messagesCount;
}
- private static async Task DownloadHighlights(IDownloadContext downloadContext, KeyValuePair user, int highlightsCount, string path)
+ private async Task DownloadHighlights(string username, KeyValuePair user, int highlightsCount, string path)
{
- Log.Debug($"Calling DownloadHighlights - {user.Key}");
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
AnsiConsole.Markup($"[red]Getting Highlights\n[/]");
- Dictionary highlights = await downloadContext.ApiHelper.GetMedia(MediaType.Highlights, $"/users/{user.Value}/stories/highlights", null, path, downloadContext.DownloadConfig!, paid_post_ids);
- int oldHighlightsCount = 0;
- int newHighlightsCount = 0;
- if (highlights != null && highlights.Count > 0)
+
+ // Calculate total size for progress bar
+ long totalSize = 0;
+ var tempHighlights = await apiService.GetMedia(MediaType.Highlights, $"/users/{user.Value}/stories/highlights", null, path, paid_post_ids);
+ if (tempHighlights != null && tempHighlights.Count > 0)
{
- AnsiConsole.Markup($"[red]Found {highlights.Count} Highlights\n[/]");
- Log.Debug($"Found {highlights.Count} Highlights");
- highlightsCount = highlights.Count;
- long totalSize = 0;
- if (downloadContext.DownloadConfig.ShowScrapeSize)
+ AnsiConsole.Markup($"[red]Found {tempHighlights.Count} Highlights\n[/]");
+ if (configService.CurrentConfig.ShowScrapeSize)
{
- totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(highlights.Values.ToList());
+ totalSize = await downloadService.CalculateTotalFileSize(tempHighlights.Values.ToList());
}
else
{
- totalSize = highlightsCount;
+ totalSize = tempHighlights.Count;
}
- await AnsiConsole.Progress()
- .Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
- .StartAsync(async ctx =>
- {
- // Define tasks
- var task = ctx.AddTask($"[red]Downloading {highlights.Count} Highlights[/]", autoStart: false);
- Log.Debug($"Downloading {highlights.Count} Highlights");
- task.MaxValue = totalSize;
- task.StartTask();
- foreach (KeyValuePair highlightKVP in highlights)
- {
- bool isNew = await downloadContext.DownloadHelper.DownloadStoryMedia(highlightKVP.Value, path, highlightKVP.Key, "Stories", task);
- if (isNew)
- {
- newHighlightsCount++;
- }
- else
- {
- oldHighlightsCount++;
- }
- }
- task.StopTask();
- });
- AnsiConsole.Markup($"[red]Highlights Already Downloaded: {oldHighlightsCount} New Highlights Downloaded: {newHighlightsCount}[/]\n");
- Log.Debug($"Highlights Already Downloaded: {oldHighlightsCount} New Highlights Downloaded: {newHighlightsCount}");
}
else
{
AnsiConsole.Markup($"[red]Found 0 Highlights\n[/]");
- Log.Debug($"Found 0 Highlights");
+ return 0;
}
- return highlightsCount;
+ DownloadResult result = null;
+ await AnsiConsole.Progress()
+ .Columns(GetProgressColumns(configService.CurrentConfig.ShowScrapeSize))
+ .StartAsync(async ctx =>
+ {
+ var task = ctx.AddTask($"[red]Downloading {tempHighlights.Count} Highlights[/]", autoStart: false);
+ task.MaxValue = totalSize;
+ task.StartTask();
+
+ var progressReporter = new SpectreProgressReporter(task);
+ result = await downloadService.DownloadHighlights(username, user.Value, path, paid_post_ids.ToHashSet(), progressReporter);
+
+ task.StopTask();
+ });
+
+ AnsiConsole.Markup($"[red]Highlights Already Downloaded: {result.ExistingDownloads} New Highlights Downloaded: {result.NewDownloads}[/]\n");
+ return result.TotalCount;
}
- private static async Task DownloadStories(IDownloadContext downloadContext, KeyValuePair user, int storiesCount, string path)
+ private async Task DownloadStories(string username, KeyValuePair user, int storiesCount, string path)
{
- Log.Debug($"Calling DownloadStories - {user.Key}");
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
AnsiConsole.Markup($"[red]Getting Stories\n[/]");
- Dictionary stories = await downloadContext.ApiHelper.GetMedia(MediaType.Stories, $"/users/{user.Value}/stories", null, path, downloadContext.DownloadConfig!, paid_post_ids);
- int oldStoriesCount = 0;
- int newStoriesCount = 0;
- if (stories != null && stories.Count > 0)
+
+ // Calculate total size for progress bar
+ long totalSize = 0;
+ var tempStories = await serviceProvider.GetRequiredService().GetMedia(MediaType.Stories, $"/users/{user.Value}/stories", null, path, paid_post_ids);
+ if (tempStories != null && tempStories.Count > 0)
{
- AnsiConsole.Markup($"[red]Found {stories.Count} Stories\n[/]");
- Log.Debug($"Found {stories.Count} Stories");
- storiesCount = stories.Count;
- long totalSize = 0;
- if (downloadContext.DownloadConfig.ShowScrapeSize)
+ AnsiConsole.Markup($"[red]Found {tempStories.Count} Stories\n[/]");
+ if (configService.CurrentConfig.ShowScrapeSize)
{
- totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(stories.Values.ToList());
+ totalSize = await downloadService.CalculateTotalFileSize(tempStories.Values.ToList());
}
else
{
- totalSize = storiesCount;
+ totalSize = tempStories.Count;
}
- await AnsiConsole.Progress()
- .Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
- .StartAsync(async ctx =>
- {
- // Define tasks
- var task = ctx.AddTask($"[red]Downloading {stories.Count} Stories[/]", autoStart: false);
- Log.Debug($"Downloading {stories.Count} Stories");
- task.MaxValue = totalSize;
- task.StartTask();
- foreach (KeyValuePair storyKVP in stories)
- {
- bool isNew = await downloadContext.DownloadHelper.DownloadStoryMedia(storyKVP.Value, path, storyKVP.Key, "Stories", task);
- if (isNew)
- {
- newStoriesCount++;
- }
- else
- {
- oldStoriesCount++;
- }
- }
- task.StopTask();
- });
- AnsiConsole.Markup($"[red]Stories Already Downloaded: {oldStoriesCount} New Stories Downloaded: {newStoriesCount}[/]\n");
- Log.Debug($"Stories Already Downloaded: {oldStoriesCount} New Stories Downloaded: {newStoriesCount}");
}
else
{
AnsiConsole.Markup($"[red]Found 0 Stories\n[/]");
- Log.Debug($"Found 0 Stories");
+ return 0;
}
- return storiesCount;
+ DownloadResult result = null;
+ await AnsiConsole.Progress()
+ .Columns(GetProgressColumns(configService.CurrentConfig.ShowScrapeSize))
+ .StartAsync(async ctx =>
+ {
+ var task = ctx.AddTask($"[red]Downloading {tempStories.Count} Stories[/]", autoStart: false);
+ task.MaxValue = totalSize;
+ task.StartTask();
+
+ var progressReporter = new SpectreProgressReporter(task);
+ result = await downloadService.DownloadStories(username, user.Value, path, paid_post_ids.ToHashSet(), progressReporter);
+
+ task.StopTask();
+ });
+
+ AnsiConsole.Markup($"[red]Stories Already Downloaded: {result.ExistingDownloads} New Stories Downloaded: {result.NewDownloads}[/]\n");
+ return result.TotalCount;
}
- private static async Task DownloadArchived(IDownloadContext downloadContext, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int archivedCount, string path)
+ private async Task DownloadArchived(string username, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int archivedCount, string path)
{
- Log.Debug($"Calling DownloadArchived - {user.Key}");
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
ArchivedCollection archived = new ArchivedCollection();
await AnsiConsole.Status()
.StartAsync("[red]Getting Archived Posts[/]", async ctx =>
{
- archived = await downloadContext.ApiHelper.GetArchived($"/users/{user.Value}/posts", path, downloadContext.DownloadConfig!, ctx);
+ archived = await apiService.GetArchived($"/users/{user.Value}/posts", path, ctx);
});
- int oldArchivedCount = 0;
- int newArchivedCount = 0;
if (archived != null && archived.ArchivedPosts.Count > 0)
{
AnsiConsole.Markup($"[red]Found {archived.ArchivedPosts.Count} Media from {archived.ArchivedPostObjects.Count} Archived Posts\n[/]");
- Log.Debug($"Found {archived.ArchivedPosts.Count} Media from {archived.ArchivedPostObjects.Count} Archived Posts");
- archivedCount = archived.ArchivedPosts.Count;
+
long totalSize = 0;
- if (downloadContext.DownloadConfig.ShowScrapeSize)
+ if (configService.CurrentConfig.ShowScrapeSize)
{
- totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(archived.ArchivedPosts.Values.ToList());
+ totalSize = await downloadService.CalculateTotalFileSize(archived.ArchivedPosts.Values.ToList());
}
else
{
- totalSize = archivedCount;
+ totalSize = archived.ArchivedPosts.Count;
}
+
+ DownloadResult result = null;
await AnsiConsole.Progress()
- .Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
+ .Columns(GetProgressColumns(configService.CurrentConfig.ShowScrapeSize))
.StartAsync(async ctx =>
{
- // Define tasks
var task = ctx.AddTask($"[red]Downloading {archived.ArchivedPosts.Count} Archived Posts[/]", autoStart: false);
- Log.Debug($"Downloading {archived.ArchivedPosts.Count} Archived Posts");
task.MaxValue = totalSize;
task.StartTask();
- foreach (KeyValuePair archivedKVP in archived.ArchivedPosts)
- {
- bool isNew;
- if (archivedKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
- {
- string[] messageUrlParsed = archivedKVP.Value.Split(',');
- string mpdURL = messageUrlParsed[0];
- string policy = messageUrlParsed[1];
- string signature = messageUrlParsed[2];
- string kvp = messageUrlParsed[3];
- string mediaId = messageUrlParsed[4];
- string postId = messageUrlParsed[5];
- string? licenseURL = null;
- string? pssh = await downloadContext.ApiHelper.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
- if (pssh != null)
- {
- DateTime lastModified = await downloadContext.ApiHelper.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
- Dictionary drmHeaders = downloadContext.ApiHelper.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/post/{postId}", "?type=widevine");
- string decryptionKey;
- if (clientIdBlobMissing || devicePrivateKeyMissing)
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyOFDL(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/post/{postId}?type=widevine", pssh);
- }
- else
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyCDM(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/post/{postId}?type=widevine", pssh);
- }
- Archived.Medium? mediaInfo = archived.ArchivedPostMedia.FirstOrDefault(m => m.id == archivedKVP.Key);
- Archived.List? postInfo = archived.ArchivedPostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
- isNew = await downloadContext.DownloadHelper.DownloadArchivedPostDRMVideo(
- policy: policy,
- signature: signature,
- kvp: kvp,
- url: mpdURL,
- decryptionKey: decryptionKey,
- folder: path,
- lastModified: lastModified,
- media_id: archivedKVP.Key,
- api_type: "Posts",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.PostFileNameFormat ?? string.Empty,
- postInfo: postInfo,
- postMedia: mediaInfo,
- author: postInfo?.author,
- users: hasSelectedUsersKVP.Value);
+ var progressReporter = new SpectreProgressReporter(task);
+ result = await downloadService.DownloadArchived(username, user.Value, path, hasSelectedUsersKVP.Value, clientIdBlobMissing, devicePrivateKeyMissing, archived, progressReporter);
- if (isNew)
- {
- newArchivedCount++;
- }
- else
- {
- oldArchivedCount++;
- }
- }
- }
- else
- {
- Archived.Medium? mediaInfo = archived.ArchivedPostMedia.FirstOrDefault(m => m.id == archivedKVP.Key);
- Archived.List? postInfo = archived.ArchivedPostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
-
- isNew = await downloadContext.DownloadHelper.DownloadArchivedMedia(
- url: archivedKVP.Value,
- folder: path,
- media_id: archivedKVP.Key,
- api_type: "Posts",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.PostFileNameFormat ?? string.Empty,
- messageInfo: postInfo,
- messageMedia: mediaInfo,
- author: postInfo?.author,
- users: hasSelectedUsersKVP.Value);
-
- if (isNew)
- {
- newArchivedCount++;
- }
- else
- {
- oldArchivedCount++;
- }
- }
- }
task.StopTask();
});
- AnsiConsole.Markup($"[red]Archived Posts Already Downloaded: {oldArchivedCount} New Archived Posts Downloaded: {newArchivedCount}[/]\n");
+
+ AnsiConsole.Markup($"[red]Archived Posts Already Downloaded: {result.ExistingDownloads} New Archived Posts Downloaded: {result.NewDownloads}[/]\n");
+ return result.TotalCount;
}
else
{
AnsiConsole.Markup($"[red]Found 0 Archived Posts\n[/]");
+ return 0;
}
-
- return archivedCount;
}
- private static async Task DownloadFreePosts(IDownloadContext downloadContext, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int postCount, string path)
+ private async Task DownloadFreePosts(string username, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int postCount, string path)
{
+ IConfigService configService = serviceProvider.GetRequiredService();
+ IAPIService apiService = serviceProvider.GetRequiredService();
+ IDownloadService downloadService = serviceProvider.GetRequiredService();
+
+ AnsiConsole.Markup($"[red]Getting Posts (this may take a long time, depending on the number of Posts the creator has)\n[/]");
Log.Debug($"Calling DownloadFreePosts - {user.Key}");
PostCollection posts = new PostCollection();
await AnsiConsole.Status()
- .StartAsync("[red]Getting Posts (this may take a long time, depending on the number of Posts the creator has)[/]", async ctx =>
+ .StartAsync("[red]Getting Posts[/]", async ctx =>
{
- posts = await downloadContext.ApiHelper.GetPosts($"/users/{user.Value}/posts", path, downloadContext.DownloadConfig!, paid_post_ids, ctx);
+ posts = await apiService.GetPosts($"/users/{user.Value}/posts", path, paid_post_ids, ctx);
});
- int oldPostCount = 0;
- int newPostCount = 0;
if (posts == null || posts.Posts.Count <= 0)
{
AnsiConsole.Markup($"[red]Found 0 Posts\n[/]");
@@ -1571,125 +1241,39 @@ public class Program(IServiceProvider serviceProvider)
AnsiConsole.Markup($"[red]Found {posts.Posts.Count} Media from {posts.PostObjects.Count} Posts\n[/]");
Log.Debug($"Found {posts.Posts.Count} Media from {posts.PostObjects.Count} Posts");
- postCount = posts.Posts.Count;
- long totalSize = 0;
- if (downloadContext.DownloadConfig.ShowScrapeSize)
- {
- totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(posts.Posts.Values.ToList());
- }
- else
- {
- totalSize = postCount;
- }
+
+ long totalSize = configService.CurrentConfig.ShowScrapeSize
+ ? await downloadService.CalculateTotalFileSize(posts.Posts.Values.ToList())
+ : posts.Posts.Count;
+
+ DownloadResult result = null;
await AnsiConsole.Progress()
- .Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
+ .Columns(GetProgressColumns(configService.CurrentConfig.ShowScrapeSize))
.StartAsync(async ctx =>
{
var task = ctx.AddTask($"[red]Downloading {posts.Posts.Count} Posts[/]", autoStart: false);
- Log.Debug($"Downloading {posts.Posts.Count} Posts");
task.MaxValue = totalSize;
task.StartTask();
- foreach (KeyValuePair postKVP in posts.Posts)
- {
- bool isNew;
- if (postKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
- {
- string[] messageUrlParsed = postKVP.Value.Split(',');
- string mpdURL = messageUrlParsed[0];
- string policy = messageUrlParsed[1];
- string signature = messageUrlParsed[2];
- string kvp = messageUrlParsed[3];
- string mediaId = messageUrlParsed[4];
- string postId = messageUrlParsed[5];
- string? licenseURL = null;
- string? pssh = await downloadContext.ApiHelper.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
- if (pssh == null)
- {
- continue;
- }
- DateTime lastModified = await downloadContext.ApiHelper.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
- Dictionary drmHeaders = downloadContext.ApiHelper.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/post/{postId}", "?type=widevine");
- string decryptionKey;
- if (clientIdBlobMissing || devicePrivateKeyMissing)
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyOFDL(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/post/{postId}?type=widevine", pssh);
- }
- else
- {
- decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyCDM(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/post/{postId}?type=widevine", pssh);
- }
- Post.Medium mediaInfo = posts.PostMedia.FirstOrDefault(m => m.id == postKVP.Key);
- Post.List postInfo = posts.PostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
+ var progressReporter = new SpectreProgressReporter(task);
+ result = await downloadService.DownloadFreePosts(username, user.Value, path, hasSelectedUsersKVP.Value, clientIdBlobMissing, devicePrivateKeyMissing, posts, progressReporter);
- isNew = await downloadContext.DownloadHelper.DownloadPostDRMVideo(
- policy: policy,
- signature: signature,
- kvp: kvp,
- url: mpdURL,
- decryptionKey: decryptionKey,
- folder: path,
- lastModified: lastModified,
- media_id: postKVP.Key,
- api_type: "Posts",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.PostFileNameFormat ?? string.Empty,
- postInfo: postInfo,
- postMedia: mediaInfo,
- author: postInfo?.author,
- users: hasSelectedUsersKVP.Value);
- if (isNew)
- {
- newPostCount++;
- }
- else
- {
- oldPostCount++;
- }
- }
- else
- {
- try
- {
- Post.Medium? mediaInfo = posts.PostMedia.FirstOrDefault(m => (m?.id == postKVP.Key) == true);
- Post.List? postInfo = posts.PostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
-
- isNew = await downloadContext.DownloadHelper.DownloadPostMedia(
- url: postKVP.Value,
- folder: path,
- media_id: postKVP.Key,
- api_type: "Posts",
- task: task,
- filenameFormat: downloadContext.FileNameFormatConfig.PostFileNameFormat ?? string.Empty,
- postInfo: postInfo,
- postMedia: mediaInfo,
- author: postInfo?.author,
- users: hasSelectedUsersKVP.Value);
- if (isNew)
- {
- newPostCount++;
- }
- else
- {
- oldPostCount++;
- }
- }
- catch
- {
- Console.WriteLine("Media was null");
- }
- }
- }
task.StopTask();
});
- AnsiConsole.Markup($"[red]Posts Already Downloaded: {oldPostCount} New Posts Downloaded: {newPostCount}[/]\n");
- Log.Debug("Posts Already Downloaded: {oldPostCount} New Posts Downloaded: {newPostCount}");
- return postCount;
+ AnsiConsole.Markup($"[red]Posts Already Downloaded: {result.ExistingDownloads} New Posts Downloaded: {result.NewDownloads}[/]\n");
+ Log.Debug($"Posts Already Downloaded: {result.ExistingDownloads} New Posts Downloaded: {result.NewDownloads}");
+
+ return result.TotalCount;
}
- private static async Task DownloadPaidPosts(IDownloadContext downloadContext, KeyValuePair> hasSelectedUsersKVP, KeyValuePair user, int paidPostCount, string path)
+ private async Task DownloadPaidPosts(string username, KeyValuePair> hasSelectedUsersKVP, KeyValuePair