OF-DL/OF DL/Services/DownloadService.cs

3082 lines
128 KiB
C#

using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using FFmpeg.NET;
using FFmpeg.NET.Events;
using OF_DL.Models;
using OF_DL.Models.Messages;
using OF_DL.Models.Post;
using OF_DL.Models.Purchased;
using OF_DL.Models.Streams;
using OF_DL.Enumerations;
using ArchivedModels = OF_DL.Models.Entities.Archived;
using OF_DL.Utils;
using Serilog;
using Serilog.Events;
using static OF_DL.Models.Messages.Messages;
using FromUser = OF_DL.Models.Messages.FromUser;
namespace OF_DL.Services;
public class DownloadService(
IAuthService authService,
IConfigService configService,
IDBService dbService,
IFileNameService fileNameService,
IAPIService apiService)
: IDownloadService
{
private TaskCompletionSource<bool> _completionSource;
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<string> avatarMD5Hashes = CalculateFolderMD5(folder + avatarpath);
Uri uri = new(avatarUrl);
string destinationPath = $"{folder}{avatarpath}/";
HttpClient client = new();
HttpRequestMessage request = new() { Method = HttpMethod.Get, RequestUri = uri };
using HttpResponseMessage response =
await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using MemoryStream memoryStream = new();
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<string> headerMD5Hashes = CalculateFolderMD5(folder + headerpath);
Uri uri = new(headerUrl);
string destinationPath = $"{folder}{headerpath}/";
HttpClient client = new();
HttpRequestMessage request = new() { Method = HttpMethod.Get, RequestUri = uri };
using HttpResponseMessage response =
await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using MemoryStream memoryStream = new();
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);
}
}
}
#region drm common
private async Task<bool> 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, IProgressReporter progressReporter, string customFileName, string filename, string path)
{
try
{
_completionSource = new TaskCompletionSource<bool>();
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(LogEventLevel.Debug);
string logLevelArgs = ffmpegDebugLogging ||
configService.CurrentConfig.LoggingLevel is LoggingLevel.Verbose or LoggingLevel.Debug
? "-loglevel debug -report"
: configService.CurrentConfig.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(configService.CurrentConfig.FFmpegPath);
ffmpeg.Error += OnError;
ffmpeg.Complete += async (sender, args) =>
{
_completionSource.TrySetResult(true);
await OnFFMPEGDownloadComplete(tempFilename, lastModified, folder, path, customFileName, filename,
media_id, api_type, progressReporter);
};
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
private async Task OnFFMPEGDownloadComplete(string tempFilename, DateTime lastModified, string folder, string path,
string customFileName, string filename, long media_id, string api_type, IProgressReporter progressReporter)
{
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 (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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
string input = e?.Input?.Name ?? "<none>";
string output = e?.Output?.Name ?? "<none>";
string exitCode = e?.Exception?.ExitCode.ToString() ?? "<unknown>";
string message = e?.Exception?.Message ?? "<no message>";
string inner = e?.Exception?.InnerException?.Message ?? "<no inner>";
Log.Error("FFmpeg failed. Input={Input} Output={Output} ExitCode={ExitCode} Message={Message} Inner={Inner}",
input, output, exitCode, message, inner);
_completionSource?.TrySetResult(false);
}
private async Task<int?> 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", authService.CurrentAuth.USER_AGENT);
request.Headers.Add("Accept", "*/*");
request.Headers.Add("Cookie",
$"CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {authService.CurrentAuth.COOKIE};");
using (HttpResponseMessage response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
XDocument doc = XDocument.Parse(body);
XNamespace ns = "urn:mpeg:dash:schema:mpd:2011";
XNamespace cenc = "urn:mpeg:cenc:2013";
XElement? 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))
};
List<XElement> 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;
}
private static List<string> CalculateFolderMD5(string folder)
{
List<string> md5Hashes = new();
if (Directory.Exists(folder))
{
string[] files = Directory.GetFiles(folder);
foreach (string file in files)
{
md5Hashes.Add(CalculateMD5(file));
}
}
return md5Hashes;
}
private static string CalculateMD5(string filePath)
{
using (MD5 md5 = MD5.Create())
{
using (FileStream stream = File.OpenRead(filePath))
{
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
#region common
/// <summary>
/// </summary>
/// <param name="path"></param>
/// <param name="url"></param>
/// <param name="folder"></param>
/// <param name="media_id"></param>
/// <param name="task"></param>
/// <param name="filenameFormat"></param>
/// <param name="generalInfo"></param>
/// <param name="generalMedia"></param>
/// <param name="generalAuthor"></param>
/// <param name="users"></param>
/// <returns></returns>
protected async Task<bool> CreateDirectoriesAndDownloadMedia(string path,
string url,
string folder,
long media_id,
string api_type,
IProgressReporter progressReporter,
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, progressReporter);
}
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;
}
/// <summary>
/// Updates the given path based on the file extension.
/// </summary>
/// <param name="folder">The parent folder.</param>
/// <param name="path">The initial relative path.</param>
/// <param name="extension">The file extension.</param>
/// <returns>A string that represents the updated path based on the file extension.</returns>
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;
}
/// <summary>
/// Generates a custom filename based on the given format and properties.
/// </summary>
/// <param name="filenameFormat">The format string for the filename.</param>
/// <param name="postInfo">General information about the post.</param>
/// <param name="postMedia">Media associated with the post.</param>
/// <param name="author">Author of the post.</param>
/// <param name="users">Dictionary containing user-related data.</param>
/// <param name="fileNameHelper">Helper class for filename operations.</param>
/// <returns>A Task resulting in a string that represents the custom filename.</returns>
private async Task<string> GenerateCustomFileName(string filename,
string? filenameFormat,
object? postInfo,
object? postMedia,
object? author,
string username,
Dictionary<string, long> users,
IFileNameService fileNameService,
CustomFileNameOption option)
{
if (string.IsNullOrEmpty(filenameFormat) || postInfo == null || postMedia == null || author == null)
{
return option switch
{
CustomFileNameOption.ReturnOriginal => filename,
CustomFileNameOption.ReturnEmpty => string.Empty,
_ => filename
};
}
List<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
properties.AddRange(matches.Select(match => match.Groups[1].Value));
Dictionary<string, string> values =
await fileNameService.GetFilename(postInfo, postMedia, author, properties, username, users);
return await fileNameService.BuildFilename(filenameFormat, values);
}
private async Task<long> GetFileSizeAsync(string url)
{
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}; {authService.CurrentAuth.COOKIE}");
client.DefaultRequestHeaders.Add("User-Agent", authService.CurrentAuth.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", authService.CurrentAuth.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<DateTime> 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<DateTime> 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;
}
/// <summary>
/// Processes the download and database update of media.
/// </summary>
/// <param name="folder">The folder where the media is stored.</param>
/// <param name="media_id">The ID of the media.</param>
/// <param name="fullPath">The full path to the media.</param>
/// <param name="url">The URL from where to download the media.</param>
/// <param name="path">The relative path to the media.</param>
/// <param name="resolvedFilename">The filename after any required manipulations.</param>
/// <param name="extension">The file extension.</param>
/// <param name="task">The task object for tracking progress.</param>
/// <returns>A Task resulting in a boolean indicating whether the media is newly downloaded or not.</returns>
public async Task<bool> ProcessMediaDownload(string folder,
long media_id,
string api_type,
string url,
string path,
string serverFilename,
string resolvedFilename,
string extension,
IProgressReporter progressReporter)
{
try
{
if (!await dbService.CheckDownloaded(folder, media_id, api_type))
{
return await HandleNewMedia(folder,
media_id,
api_type,
url,
path,
serverFilename,
resolvedFilename,
extension,
progressReporter);
}
bool status = await HandlePreviouslyDownloadedMediaAsync(folder, media_id, api_type, progressReporter);
if (configService.CurrentConfig.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<bool> 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 dbService.GetStoredFileSize(folder, media_id, api_type);
DateTime lastModified = File.GetLastWriteTime(fullPathWithTheNewFileName);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, resolvedFilename + extension, size, true,
lastModified);
return true;
}
/// <summary>
/// Handles new media by downloading and updating the database.
/// </summary>
/// <param name="folder"></param>
/// <param name="media_id"></param>
/// <param name="url"></param>
/// <param name="path"></param>
/// <param name="resolvedFilename"></param>
/// <param name="extension"></param>
/// <param name="task"></param>
/// <param name="dBHelper"></param>
/// <returns>A Task resulting in a boolean indicating whether the media is newly downloaded or not.</returns>
private async Task<bool> HandleNewMedia(string folder,
long media_id,
string api_type,
string url,
string path,
string serverFilename,
string resolvedFilename,
string extension,
IProgressReporter progressReporter)
{
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 (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(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 (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
status = false;
}
else //file doesn't exist and we should download it.
{
lastModified = await DownloadFile(url, fullPathWithTheNewFileName, progressReporter);
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 dbService.UpdateMedia(folder, media_id, api_type, folder + path, finalName + extension, fileSizeInBytes,
true, lastModified);
return status;
}
/// <summary>
/// Handles media that has been previously downloaded and updates the task accordingly.
/// </summary>
/// <param name="folder"></param>
/// <param name="media_id"></param>
/// <param name="task"></param>
/// <param name="dBHelper"></param>
/// <returns>A boolean indicating whether the media is newly downloaded or not.</returns>
private async Task<bool> HandlePreviouslyDownloadedMediaAsync(string folder, long media_id, string api_type,
IProgressReporter progressReporter)
{
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(1);
}
return false;
}
/// <summary>
/// Gets the file size of the media.
/// </summary>
/// <param name="filePath">The path to the file.</param>
/// <returns>The file size in bytes.</returns>
private long GetLocalFileSize(string filePath) => new FileInfo(filePath).Length;
/// <summary>
/// Downloads a file from the given URL and saves it to the specified destination path.
/// </summary>
/// <param name="url">The URL to download the file from.</param>
/// <param name="destinationPath">The path where the downloaded file will be saved.</param>
/// <param name="task">Progress tracking object.</param>
/// <returns>A Task resulting in a DateTime indicating the last modified date of the downloaded file.</returns>
private async Task<DateTime> DownloadFile(string url, string destinationPath, IProgressReporter progressReporter)
{
using HttpClient client = new();
HttpRequestMessage request = new() { Method = HttpMethod.Get, RequestUri = new Uri(url) };
using HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
Stream body = await response.Content.ReadAsStreamAsync();
// Wrap the body stream with the ThrottledStream to limit read rate.
using (ThrottledStream throttledStream = new(body,
configService.CurrentConfig.DownloadLimitInMbPerSec * 1_000_000,
configService.CurrentConfig.LimitDownloadRate))
{
using FileStream fileStream = new(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 16384,
true);
byte[] buffer = new byte[16384];
int read;
while ((read = await throttledStream.ReadAsync(buffer, CancellationToken.None)) > 0)
{
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(read);
}
await fileStream.WriteAsync(buffer.AsMemory(0, read), CancellationToken.None);
}
}
File.SetLastWriteTime(destinationPath, response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now);
if (!configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(1);
}
return response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now;
}
public async Task<long> CalculateTotalFileSize(List<string> urls)
{
long totalFileSize = 0;
if (urls.Count > 250)
{
int batchSize = 250;
List<Task<long>> tasks = new();
for (int i = 0; i < urls.Count; i += batchSize)
{
List<string> batchUrls = urls.Skip(i).Take(batchSize).ToList();
IEnumerable<Task<long>> batchTasks = batchUrls.Select(GetFileSizeAsync);
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
{
List<Task<long>> tasks = new();
foreach (string url in urls)
{
tasks.Add(GetFileSizeAsync(url));
}
long[] fileSizes = await Task.WhenAll(tasks);
foreach (long fileSize in fileSizes)
{
totalFileSize += fileSize;
}
}
return totalFileSize;
}
#endregion
#region normal posts
public async Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia,
Post.Author? author, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, postInfo, postMedia, author,
folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia,
SinglePost.Author? author, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, postInfo, postMedia, author,
folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadStreamMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Streams.List? streamInfo,
Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, streamInfo, streamMedia,
author, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, List? messageInfo, Medium? messageMedia,
Messages.FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia,
fromUser, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadMessagePreviewMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo,
Medium? messageMedia,
FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia,
fromUser, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadArchivedMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, ArchivedModels.ListItem? messageInfo,
ArchivedModels.Medium? messageMedia, Models.Entities.Common.Author? author,
Dictionary<string, long> users)
{
string path = "/Archived/Posts/Free";
Uri uri = new(url);
string filename = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia,
author, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadStoryMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter)
{
string path = "/Stories/Free";
Uri uri = new(url);
string filename = Path.GetFileNameWithoutExtension(uri.LocalPath);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, filename);
}
public async Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo,
Medium? messageMedia,
Purchased.FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia,
fromUser, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo,
Medium? messageMedia,
FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia,
fromUser, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadPurchasedPostMedia(string url,
string folder,
long media_id,
string api_type,
IProgressReporter progressReporter,
string? filenameFormat,
Purchased.List? messageInfo,
Medium? messageMedia,
Purchased.FromUser? fromUser,
Dictionary<string, long> users)
{
string path;
if (configService.CurrentConfig.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 = Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia,
fromUser, folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
filename, resolvedFilename);
}
#endregion
#region drm posts
public async Task<bool> DownloadMessageDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, List? messageInfo, Medium? messageMedia,
Messages.FromUser? fromUser, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(messageInfo, messageMedia,
fromUser, properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadSingleMessagePreviewDRMVideo(string policy, string signature, string kvp,
string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo,
Medium? messageMedia,
FromUser? fromUser, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(messageInfo, messageMedia,
fromUser, properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadPurchasedMessageDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo,
Medium? messageMedia,
Purchased.FromUser? fromUser, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(messageInfo, messageMedia,
fromUser, properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp,
string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo,
Medium? messageMedia,
FromUser? fromUser, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(messageInfo, messageMedia,
fromUser, properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia,
Post.Author? author, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(postInfo, postMedia, author,
properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia,
SinglePost.Author author, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(postInfo, postMedia, author,
properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadStreamsDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Streams.List? streamInfo,
Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(streamInfo, streamMedia, author,
properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? postInfo,
Medium? postMedia,
Purchased.FromUser? fromUser, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = Path.GetFileName(uri.LocalPath).Split(".")[0];
if (configService.CurrentConfig.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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(postInfo, postMedia, fromUser,
properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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<bool> DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
IProgressReporter progressReporter, string? filenameFormat, ArchivedModels.ListItem? postInfo,
ArchivedModels.Medium? postMedia,
Models.Entities.Common.Author? author, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
Uri uri = new(url);
string filename = 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<string> properties = new();
string pattern = @"\{(.*?)\}";
MatchCollection matches = Regex.Matches(filenameFormat, pattern);
foreach (Match match in matches)
{
properties.Add(match.Groups[1].Value);
}
Dictionary<string, string> values = await fileNameService.GetFilename(postInfo, postMedia, author,
properties, folder.Split("/")[^1], users);
customFileName = await fileNameService.BuildFilename(filenameFormat, values);
}
if (!await dbService.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(authService.CurrentAuth.USER_AGENT, policy, signature, kvp,
authService.CurrentAuth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type,
progressReporter, customFileName, filename, path);
}
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: folder + path + "/" + filename + "_source.mp4").Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
await dbService.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 (configService.CurrentConfig.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 dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
progressReporter.ReportProgress(size);
}
else
{
progressReporter.ReportProgress(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
#region Collection Download Methods
public async Task<DownloadResult> DownloadHighlights(string username, long userId, string path,
HashSet<long> paidPostIds, IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadHighlights - {username}");
Dictionary<long, string> highlights = await apiService.GetMedia(MediaType.Highlights,
$"/users/{userId}/stories/highlights", null, path, paidPostIds.ToList());
if (highlights == null || highlights.Count == 0)
{
Log.Debug("Found 0 Highlights");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Highlights",
Success = true
};
}
Log.Debug($"Found {highlights.Count} Highlights");
int oldHighlightsCount = 0;
int newHighlightsCount = 0;
foreach (KeyValuePair<long, string> highlightKVP in highlights)
{
bool isNew =
await DownloadStoryMedia(highlightKVP.Value, path, highlightKVP.Key, "Stories", progressReporter);
if (isNew)
{
newHighlightsCount++;
}
else
{
oldHighlightsCount++;
}
}
Log.Debug(
$"Highlights Already Downloaded: {oldHighlightsCount} New Highlights Downloaded: {newHighlightsCount}");
return new DownloadResult
{
TotalCount = highlights.Count,
NewDownloads = newHighlightsCount,
ExistingDownloads = oldHighlightsCount,
MediaType = "Highlights",
Success = true
};
}
public async Task<DownloadResult> DownloadStories(string username, long userId, string path,
HashSet<long> paidPostIds, IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadStories - {username}");
Dictionary<long, string> stories = await apiService.GetMedia(MediaType.Stories, $"/users/{userId}/stories",
null, path, paidPostIds.ToList());
if (stories == null || stories.Count == 0)
{
Log.Debug("Found 0 Stories");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Stories",
Success = true
};
}
Log.Debug($"Found {stories.Count} Stories");
int oldStoriesCount = 0;
int newStoriesCount = 0;
foreach (KeyValuePair<long, string> storyKVP in stories)
{
bool isNew = await DownloadStoryMedia(storyKVP.Value, path, storyKVP.Key, "Stories", progressReporter);
if (isNew)
{
newStoriesCount++;
}
else
{
oldStoriesCount++;
}
}
Log.Debug($"Stories Already Downloaded: {oldStoriesCount} New Stories Downloaded: {newStoriesCount}");
return new DownloadResult
{
TotalCount = stories.Count,
NewDownloads = newStoriesCount,
ExistingDownloads = oldStoriesCount,
MediaType = "Stories",
Success = true
};
}
public async Task<DownloadResult> DownloadArchived(string username, long userId, string path,
Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing,
ArchivedModels.ArchivedCollection archived, IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadArchived - {username}");
if (archived == null || archived.ArchivedPosts.Count == 0)
{
Log.Debug("Found 0 Archived Posts");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Archived Posts",
Success = true
};
}
Log.Debug(
$"Found {archived.ArchivedPosts.Count} Media from {archived.ArchivedPostObjects.Count} Archived Posts");
int oldArchivedCount = 0;
int newArchivedCount = 0;
foreach (KeyValuePair<long, string> 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? pssh = await apiService.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
if (pssh != null)
{
DateTime lastModified = await apiService.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/post/{postId}",
"?type=widevine");
string decryptionKey;
if (clientIdBlobMissing || devicePrivateKeyMissing)
{
decryptionKey = await apiService.GetDecryptionKeyOFDL(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/post/{postId}?type=widevine",
pssh);
}
else
{
decryptionKey = await apiService.GetDecryptionKeyCDM(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/post/{postId}?type=widevine",
pssh);
}
ArchivedModels.Medium? mediaInfo =
archived.ArchivedPostMedia.FirstOrDefault(m => m.Id == archivedKVP.Key);
ArchivedModels.ListItem? postInfo =
archived.ArchivedPostObjects.FirstOrDefault(p => p?.Media?.Contains(mediaInfo) == true);
isNew = await DownloadArchivedPostDRMVideo(
policy,
signature,
kvp,
mpdURL,
decryptionKey,
path,
lastModified,
archivedKVP.Key,
"Posts",
progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty,
postInfo,
mediaInfo,
postInfo?.Author,
users);
}
else
{
continue;
}
}
else
{
ArchivedModels.Medium? mediaInfo =
archived.ArchivedPostMedia.FirstOrDefault(m => m.Id == archivedKVP.Key);
ArchivedModels.ListItem? postInfo =
archived.ArchivedPostObjects.FirstOrDefault(p => p?.Media?.Contains(mediaInfo) == true);
isNew = await DownloadArchivedMedia(
archivedKVP.Value,
path,
archivedKVP.Key,
"Posts",
progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty,
postInfo,
mediaInfo,
postInfo?.Author,
users);
}
if (isNew)
{
newArchivedCount++;
}
else
{
oldArchivedCount++;
}
}
Log.Debug(
$"Archived Posts Already Downloaded: {oldArchivedCount} New Archived Posts Downloaded: {newArchivedCount}");
return new DownloadResult
{
TotalCount = archived.ArchivedPosts.Count,
NewDownloads = newArchivedCount,
ExistingDownloads = oldArchivedCount,
MediaType = "Archived Posts",
Success = true
};
}
public async Task<DownloadResult> DownloadMessages(string username, long userId, string path,
Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing,
MessageCollection messages, IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadMessages - {username}");
if (messages == null || messages.Messages.Count == 0)
{
Log.Debug("Found 0 Messages");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Messages",
Success = true
};
}
Log.Debug($"Found {messages.Messages.Count} Media from {messages.MessageObjects.Count} Messages");
int oldMessagesCount = 0;
int newMessagesCount = 0;
foreach (KeyValuePair<long, string> 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? pssh = await apiService.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
if (pssh != null)
{
DateTime lastModified = await apiService.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/message/{messageId}",
"?type=widevine");
string decryptionKey;
if (clientIdBlobMissing || devicePrivateKeyMissing)
{
decryptionKey = await apiService.GetDecryptionKeyOFDL(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine",
pssh);
}
else
{
decryptionKey = await apiService.GetDecryptionKeyCDM(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine",
pssh);
}
Medium? mediaInfo = messages.MessageMedia.FirstOrDefault(m => m.id == messageKVP.Key);
List? messageInfo =
messages.MessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadMessageDRMVideo(
policy,
signature,
kvp,
mpdURL,
decryptionKey,
path,
lastModified,
messageKVP.Key,
"Messages",
progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).MessageFileNameFormat ??
string.Empty,
messageInfo,
mediaInfo,
messageInfo?.fromUser,
users);
}
else
{
continue;
}
}
else
{
Medium? mediaInfo = messages.MessageMedia.FirstOrDefault(m => m.id == messageKVP.Key);
List? messageInfo = messages.MessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadMessageMedia(
messageKVP.Value,
path,
messageKVP.Key,
"Messages",
progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).MessageFileNameFormat ??
string.Empty,
messageInfo,
mediaInfo,
messageInfo?.fromUser,
users);
}
if (isNew)
{
newMessagesCount++;
}
else
{
oldMessagesCount++;
}
}
Log.Debug($"Messages Already Downloaded: {oldMessagesCount} New Messages Downloaded: {newMessagesCount}");
return new DownloadResult
{
TotalCount = messages.Messages.Count,
NewDownloads = newMessagesCount,
ExistingDownloads = oldMessagesCount,
MediaType = "Messages",
Success = true
};
}
public async Task<DownloadResult> DownloadPaidMessages(string username, string path, Dictionary<string, long> users,
bool clientIdBlobMissing, bool devicePrivateKeyMissing, PaidMessageCollection paidMessageCollection,
IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadPaidMessages - {username}");
if (paidMessageCollection == null || paidMessageCollection.PaidMessages.Count == 0)
{
Log.Debug("Found 0 Paid Messages");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Paid Messages",
Success = true
};
}
Log.Debug(
$"Found {paidMessageCollection.PaidMessages.Count} Media from {paidMessageCollection.PaidMessageObjects.Count} Paid Messages");
int oldCount = 0;
int newCount = 0;
foreach (KeyValuePair<long, string> kvp in paidMessageCollection.PaidMessages)
{
bool isNew;
if (kvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = kvp.Value.Split(',');
string? pssh = await apiService.GetDRMMPDPSSH(parsed[0], parsed[1], parsed[2], parsed[3]);
if (pssh != null)
{
DateTime lastModified =
await apiService.GetDRMMPDLastModified(parsed[0], parsed[1], parsed[2], parsed[3]);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{parsed[4]}/drm/message/{parsed[5]}",
"?type=widevine");
string decryptionKey = clientIdBlobMissing || devicePrivateKeyMissing
? await apiService.GetDecryptionKeyOFDL(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/message/{parsed[5]}?type=widevine",
pssh)
: await apiService.GetDecryptionKeyCDM(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/message/{parsed[5]}?type=widevine",
pssh);
Medium? mediaInfo =
paidMessageCollection.PaidMessageMedia.FirstOrDefault(m => m.id == kvp.Key);
Purchased.List? messageInfo =
paidMessageCollection.PaidMessageObjects.FirstOrDefault(p =>
p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadPurchasedMessageDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
decryptionKey, path, lastModified, kvp.Key, "Messages", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).MessageFileNameFormat ??
string.Empty, messageInfo, mediaInfo, messageInfo?.fromUser, users);
}
else
{
continue;
}
}
else
{
Medium? mediaInfo =
paidMessageCollection.PaidMessageMedia.FirstOrDefault(m => m.id == kvp.Key);
Purchased.List? messageInfo =
paidMessageCollection.PaidMessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadPurchasedMedia(kvp.Value, path, kvp.Key, "Messages", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).MessageFileNameFormat ??
string.Empty, messageInfo, mediaInfo, messageInfo?.fromUser, users);
}
if (isNew)
{
newCount++;
}
else
{
oldCount++;
}
}
Log.Debug($"Paid Messages Already Downloaded: {oldCount} New Paid Messages Downloaded: {newCount}");
return new DownloadResult
{
TotalCount = paidMessageCollection.PaidMessages.Count,
NewDownloads = newCount,
ExistingDownloads = oldCount,
MediaType = "Paid Messages",
Success = true
};
}
public async Task<DownloadResult> DownloadStreams(string username, long userId, string path,
Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing,
StreamsCollection streams, IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadStreams - {username}");
if (streams == null || streams.Streams.Count == 0)
{
Log.Debug("Found 0 Streams");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Streams",
Success = true
};
}
Log.Debug($"Found {streams.Streams.Count} Media from {streams.StreamObjects.Count} Streams");
int oldCount = 0;
int newCount = 0;
foreach (KeyValuePair<long, string> kvp in streams.Streams)
{
bool isNew;
if (kvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = kvp.Value.Split(',');
string? pssh = await apiService.GetDRMMPDPSSH(parsed[0], parsed[1], parsed[2], parsed[3]);
if (pssh != null)
{
DateTime lastModified =
await apiService.GetDRMMPDLastModified(parsed[0], parsed[1], parsed[2], parsed[3]);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}",
"?type=widevine");
string decryptionKey = clientIdBlobMissing || devicePrivateKeyMissing
? await apiService.GetDecryptionKeyOFDL(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?type=widevine",
pssh)
: await apiService.GetDecryptionKeyCDM(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?type=widevine",
pssh);
Streams.Medium? mediaInfo = streams.StreamMedia.FirstOrDefault(m => m.id == kvp.Key);
Streams.List? streamInfo =
streams.StreamObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadStreamsDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0], decryptionKey,
path, lastModified, kvp.Key, "Streams", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty, streamInfo, mediaInfo, streamInfo?.author, users);
}
else
{
continue;
}
}
else
{
Streams.Medium? mediaInfo = streams.StreamMedia.FirstOrDefault(m => m.id == kvp.Key);
Streams.List? streamInfo =
streams.StreamObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadStreamMedia(kvp.Value, path, kvp.Key, "Streams", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty, streamInfo, mediaInfo, streamInfo?.author, users);
}
if (isNew)
{
newCount++;
}
else
{
oldCount++;
}
}
Log.Debug($"Streams Already Downloaded: {oldCount} New Streams Downloaded: {newCount}");
return new DownloadResult
{
TotalCount = streams.Streams.Count,
NewDownloads = newCount,
ExistingDownloads = oldCount,
MediaType = "Streams",
Success = true
};
}
// Add these methods to DownloadService.cs before the #endregion line
public async Task<DownloadResult> DownloadFreePosts(string username, long userId, string path,
Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, PostCollection posts,
IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadFreePosts - {username}");
if (posts == null || posts.Posts.Count == 0)
{
Log.Debug("Found 0 Posts");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Posts",
Success = true
};
}
Log.Debug($"Found {posts.Posts.Count} Media from {posts.PostObjects.Count} Posts");
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> postKVP in posts.Posts)
{
bool isNew;
if (postKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = postKVP.Value.Split(',');
string? pssh = await apiService.GetDRMMPDPSSH(parsed[0], parsed[1], parsed[2], parsed[3]);
if (pssh != null)
{
DateTime lastModified =
await apiService.GetDRMMPDLastModified(parsed[0], parsed[1], parsed[2], parsed[3]);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}",
"?type=widevine");
string decryptionKey = clientIdBlobMissing || devicePrivateKeyMissing
? await apiService.GetDecryptionKeyOFDL(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?type=widevine",
pssh)
: await apiService.GetDecryptionKeyCDM(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?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);
isNew = await DownloadPostDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0], decryptionKey, path,
lastModified, postKVP.Key, "Posts", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty, postInfo, mediaInfo, postInfo?.author, users);
}
else
{
continue;
}
}
else
{
Post.Medium? mediaInfo = posts.PostMedia.FirstOrDefault(m => m?.id == postKVP.Key);
Post.List? postInfo = posts.PostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadPostMedia(postKVP.Value, path, postKVP.Key, "Posts", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty, postInfo, mediaInfo, postInfo?.author, users);
}
if (isNew)
{
newCount++;
}
else
{
oldCount++;
}
}
Log.Debug($"Posts Already Downloaded: {oldCount} New Posts Downloaded: {newCount}");
return new DownloadResult
{
TotalCount = posts.Posts.Count,
NewDownloads = newCount,
ExistingDownloads = oldCount,
MediaType = "Posts",
Success = true
};
}
public async Task<DownloadResult> DownloadPaidPosts(string username, long userId, string path,
Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing,
PaidPostCollection purchasedPosts, IProgressReporter progressReporter)
{
Log.Debug($"Calling DownloadPaidPosts - {username}");
if (purchasedPosts == null || purchasedPosts.PaidPosts.Count == 0)
{
Log.Debug("Found 0 Paid Posts");
return new DownloadResult
{
TotalCount = 0,
NewDownloads = 0,
ExistingDownloads = 0,
MediaType = "Paid Posts",
Success = true
};
}
Log.Debug(
$"Found {purchasedPosts.PaidPosts.Count} Media from {purchasedPosts.PaidPostObjects.Count} Paid Posts");
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> postKVP in purchasedPosts.PaidPosts)
{
bool isNew;
if (postKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = postKVP.Value.Split(',');
string? pssh = await apiService.GetDRMMPDPSSH(parsed[0], parsed[1], parsed[2], parsed[3]);
if (pssh != null)
{
DateTime lastModified =
await apiService.GetDRMMPDLastModified(parsed[0], parsed[1], parsed[2], parsed[3]);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}",
"?type=widevine");
string decryptionKey = clientIdBlobMissing || devicePrivateKeyMissing
? await apiService.GetDecryptionKeyOFDL(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?type=widevine",
pssh)
: await apiService.GetDecryptionKeyCDM(drmHeaders,
$"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?type=widevine",
pssh);
Medium? mediaInfo = purchasedPosts.PaidPostMedia.FirstOrDefault(m => m.id == postKVP.Key);
Purchased.List? postInfo =
purchasedPosts.PaidPostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadPurchasedPostDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
decryptionKey, path, lastModified, postKVP.Key, "Posts", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty, postInfo, mediaInfo, postInfo?.fromUser, users);
}
else
{
continue;
}
}
else
{
Medium? mediaInfo = purchasedPosts.PaidPostMedia.FirstOrDefault(m => m.id == postKVP.Key);
Purchased.List? postInfo =
purchasedPosts.PaidPostObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await DownloadPurchasedPostMedia(postKVP.Value, path, postKVP.Key, "Posts", progressReporter,
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ??
string.Empty, postInfo, mediaInfo, postInfo?.fromUser, users);
}
if (isNew)
{
newCount++;
}
else
{
oldCount++;
}
}
Log.Debug($"Paid Posts Already Downloaded: {oldCount} New Paid Posts Downloaded: {newCount}");
return new DownloadResult
{
TotalCount = purchasedPosts.PaidPosts.Count,
NewDownloads = newCount,
ExistingDownloads = oldCount,
MediaType = "Paid Posts",
Success = true
};
}
#endregion
}