Download Single Paid Message also download Preview Media

This commit is contained in:
sim0n00ps 2026-01-01 23:16:18 +00:00
parent d5aa568afb
commit bd0a2b6de6
5 changed files with 325 additions and 3 deletions

View File

@ -14,5 +14,8 @@ namespace OF_DL.Entities.Purchased
public Dictionary<long, string> SingleMessages = new Dictionary<long, string>();
public List<SingleMessage> SingleMessageObjects = new List<SingleMessage>();
public List<Medium> SingleMessageMedia = new List<Medium>();
public Dictionary<long, string> PreviewSingleMessages = new Dictionary<long, string>();
public List<Medium> PreviewSingleMessageMedia = new List<Medium>();
}
}

View File

@ -1787,6 +1787,32 @@ public class APIHelper : IAPIHelper
singlePaidMessageCollection.SingleMessageMedia.Add(medium);
}
}
else if (messagePreviewIds.Contains(medium.id) && medium.canView && medium.files != null && medium.files.full != null && !string.IsNullOrEmpty(medium.files.full.url) && !medium.files.full.url.Contains("upload"))
{
if (medium.type == "photo" && !config.DownloadImages)
{
continue;
}
if (medium.type == "video" && !config.DownloadVideos)
{
continue;
}
if (medium.type == "gif" && !config.DownloadVideos)
{
continue;
}
if (medium.type == "audio" && !config.DownloadAudios)
{
continue;
}
if (!singlePaidMessageCollection.PreviewSingleMessages.ContainsKey(medium.id))
{
await m_DBHelper.AddMedia(folder, medium.id, message.id, medium.files.full.url, null, null, null, "Messages", medium.type == "photo" ? "Images" : (medium.type == "video" || medium.type == "gif" ? "Videos" : (medium.type == "audio" ? "Audios" : null)), messagePreviewIds.Contains(medium.id) ? true : false, false, null);
singlePaidMessageCollection.PreviewSingleMessages.Add(medium.id, medium.files.full.url.ToString());
singlePaidMessageCollection.PreviewSingleMessageMedia.Add(medium);
}
}
else if (!messagePreviewIds.Contains(medium.id) && medium.canView && medium.files != null && medium.files.drm != null)
{
if (medium.type == "photo" && !config.DownloadImages)
@ -1813,6 +1839,32 @@ public class APIHelper : IAPIHelper
singlePaidMessageCollection.SingleMessageMedia.Add(medium);
}
}
else if (messagePreviewIds.Contains(medium.id) && medium.canView && medium.files != null && medium.files.drm != null)
{
if (medium.type == "photo" && !config.DownloadImages)
{
continue;
}
if (medium.type == "video" && !config.DownloadVideos)
{
continue;
}
if (medium.type == "gif" && !config.DownloadVideos)
{
continue;
}
if (medium.type == "audio" && !config.DownloadAudios)
{
continue;
}
if (!singlePaidMessageCollection.PreviewSingleMessages.ContainsKey(medium.id))
{
await m_DBHelper.AddMedia(folder, medium.id, message.id, medium.files.drm.manifest.dash, null, null, null, "Messages", medium.type == "photo" ? "Images" : (medium.type == "video" || medium.type == "gif" ? "Videos" : (medium.type == "audio" ? "Audios" : null)), messagePreviewIds.Contains(medium.id) ? true : false, false, null);
singlePaidMessageCollection.PreviewSingleMessages.Add(medium.id, $"{medium.files.drm.manifest.dash},{medium.files.drm.signature.dash.CloudFrontPolicy},{medium.files.drm.signature.dash.CloudFrontSignature},{medium.files.drm.signature.dash.CloudFrontKeyPairId},{medium.id},{message.id}");
singlePaidMessageCollection.PreviewSingleMessageMedia.Add(medium);
}
}
}
}
}

View File

@ -29,6 +29,7 @@ using System.Threading.Tasks;
using System.Xml.Linq;
using static OF_DL.Entities.Lists.UserList;
using static OF_DL.Entities.Messages.Messages;
using FromUser = OF_DL.Entities.Messages.FromUser;
namespace OF_DL.Helpers;
@ -659,7 +660,26 @@ public class DownloadHelper : IDownloadHelper
Log.Debug("FFREPORT disabled (cwd: {Cwd})", Environment.CurrentDirectory);
}
string parameters = $"{logLevelArgs} -cenc_decryption_key {decKey} -headers \"Cookie:CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {sess} Origin: https://onlyfans.com Referer: https://onlyfans.com User-Agent: {user_agent}\" -y -i \"{url}\" -map 0:v:{streamIndex} -map 0:a? -codec copy \"{tempFilename}\"".Trim();
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}");
@ -763,6 +783,23 @@ public class DownloadHelper : IDownloadHelper
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
}
public async Task<bool> DownloadMessagePreviewMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Messages.Medium? messageMedia, FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
{
path = $"/Messages/Free/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}";
}
else
{
path = "/Messages/Free";
}
Uri uri = new(url);
string filename = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath);
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, messageInfo, messageMedia, fromUser, folder.Split("/")[^1], users, _FileNameHelper, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
}
public async Task<bool> DownloadArchivedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Archived.List? messageInfo, Archived.Medium? messageMedia, Archived.Author? author, Dictionary<string, long> users)
{
@ -1116,6 +1153,113 @@ public class DownloadHelper : IDownloadHelper
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, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Messages.Medium? messageMedia, FromUser? fromUser, Dictionary<string, long> users)
{
try
{
string customFileName = string.Empty;
string path;
Uri uri = new(url);
string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
{
path = $"/Messages/Free/{messageInfo.id} {messageInfo.createdAt.Value:yyyy-MM-dd HH-mm-ss}/Videos";
}
else
{
path = "/Messages/Free/Videos";
}
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
}
if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
{
List<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 _FileNameHelper.GetFilename(messageInfo, messageMedia, fromUser, properties, folder.Split("/")[^1],users);
customFileName = await _FileNameHelper.BuildFilename(filenameFormat, values);
}
if (!await m_DBHelper.CheckDownloaded(folder, media_id, api_type))
{
if (!string.IsNullOrEmpty(customFileName) ? !File.Exists(folder + path + "/" + customFileName + ".mp4") : !File.Exists(folder + path + "/" + filename + "_source.mp4"))
{
return await DownloadDrmMedia(auth.USER_AGENT, policy, signature, kvp, auth.COOKIE, url, decryptionKey, folder, lastModified, media_id, api_type, task, customFileName, filename, path);
}
else
{
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName) ? folder + path + "/" + customFileName + ".mp4" : folder + path + "/" + filename + "_source.mp4").Length;
if (downloadConfig.ShowScrapeSize)
{
task.Increment(fileSizeInBytes);
}
else
{
task.Increment(1);
}
await m_DBHelper.UpdateMedia(folder, media_id, api_type, folder + path, !string.IsNullOrEmpty(customFileName) ? customFileName + "mp4" : filename + "_source.mp4", fileSizeInBytes, true, lastModified);
}
}
else
{
if (!string.IsNullOrEmpty(customFileName))
{
if (downloadConfig.RenameExistingFilesWhenCustomFormatIsSelected && (filename + "_source" != customFileName))
{
string fullPathWithTheServerFileName = $"{folder}{path}/{filename}_source.mp4";
string fullPathWithTheNewFileName = $"{folder}{path}/{customFileName}.mp4";
if (!File.Exists(fullPathWithTheServerFileName))
{
return false;
}
try
{
File.Move(fullPathWithTheServerFileName, fullPathWithTheNewFileName);
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
return false;
}
long size = await m_DBHelper.GetStoredFileSize(folder, media_id, api_type);
await m_DBHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
}
}
if (downloadConfig.ShowScrapeSize)
{
long size = await m_DBHelper.GetStoredFileSize(folder, media_id, api_type);
task.Increment(size);
}
else
{
task.Increment(1);
}
}
return false;
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
if (ex.InnerException != null)
{
Console.WriteLine("\nInner Exception:");
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
}
}
return false;
}
public async Task<bool> DownloadPurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users)
{

View File

@ -6,6 +6,7 @@ using OF_DL.Entities.Purchased;
using OF_DL.Entities.Streams;
using Spectre.Console;
using static OF_DL.Entities.Messages.Messages;
using FromUser = OF_DL.Entities.Messages.FromUser;
namespace OF_DL.Helpers
{
@ -32,5 +33,13 @@ namespace OF_DL.Helpers
Task<bool> DownloadStoryMedia(string url, string folder, long media_id, string api_type, ProgressTask task);
Task<bool> DownloadStreamMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users);
Task<bool> DownloadStreamsDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Streams.List streamInfo, Streams.Medium streamMedia, Streams.Author author, Dictionary<string, long> users);
Task<bool> DownloadSingleMessagePreviewDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
FromUser? fromUser, Dictionary<string, long> users);
Task<bool> DownloadMessagePreviewMedia(string url, string folder, long media_id, string api_type,
ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
FromUser? fromUser, Dictionary<string, long> users);
}
}

View File

@ -2559,9 +2559,123 @@ public class Program
AnsiConsole.Markup($"[red]Getting Paid Message\n[/]");
SinglePaidMessageCollection singlePaidMessageCollection = await downloadContext.ApiHelper.GetPaidMessage($"/messages/{message_id.ToString()}", path, downloadContext.DownloadConfig!);
int oldPaidMessagesCount = 0;
int oldPreviewPaidMessagesCount = 0;
int newPreviewPaidMessagesCount = 0;
int oldPaidMessagesCount = 0;
int newPaidMessagesCount = 0;
if (singlePaidMessageCollection != null && singlePaidMessageCollection.SingleMessages.Count > 0)
if (singlePaidMessageCollection != null && singlePaidMessageCollection.PreviewSingleMessages.Count > 0)
{
AnsiConsole.Markup($"[red]Found {singlePaidMessageCollection.PreviewSingleMessages.Count} Preview Media from {singlePaidMessageCollection.SingleMessageObjects.Count} Paid Messages\n[/]");
Log.Debug($"Found {singlePaidMessageCollection.PreviewSingleMessages.Count} Preview Media from {singlePaidMessageCollection.SingleMessageObjects.Count} Paid Messages");
paidMessagesCount = singlePaidMessageCollection.SingleMessages.Count;
long totalSize = 0;
if (downloadContext.DownloadConfig.ShowScrapeSize)
{
totalSize = await downloadContext.DownloadHelper.CalculateTotalFileSize(singlePaidMessageCollection.PreviewSingleMessages.Values.ToList());
}
else
{
totalSize = paidMessagesCount;
}
await AnsiConsole.Progress()
.Columns(GetProgressColumns(downloadContext.DownloadConfig.ShowScrapeSize))
.StartAsync(async ctx =>
{
// Define tasks
var task = ctx.AddTask($"[red]Downloading {singlePaidMessageCollection.PreviewSingleMessages.Count} Preview Paid Messages[/]", autoStart: false);
Log.Debug($"Downloading {singlePaidMessageCollection.PreviewSingleMessages.Count} Paid Messages");
task.MaxValue = totalSize;
task.StartTask();
foreach (KeyValuePair<long, string> paidMessageKVP in singlePaidMessageCollection.PreviewSingleMessages)
{
bool isNew;
if (paidMessageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] messageUrlParsed = paidMessageKVP.Value.Split(',');
string mpdURL = messageUrlParsed[0];
string policy = messageUrlParsed[1];
string signature = messageUrlParsed[2];
string kvp = messageUrlParsed[3];
string mediaId = messageUrlParsed[4];
string messageId = messageUrlParsed[5];
string? licenseURL = null;
string? pssh = await downloadContext.ApiHelper.GetDRMMPDPSSH(mpdURL, policy, signature, kvp);
if (pssh != null)
{
DateTime lastModified = await downloadContext.ApiHelper.GetDRMMPDLastModified(mpdURL, policy, signature, kvp);
Dictionary<string, string> drmHeaders = downloadContext.ApiHelper.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/message/{messageId}", "?type=widevine");
string decryptionKey;
if (clientIdBlobMissing || devicePrivateKeyMissing)
{
decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyOFDL(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine", pssh);
}
else
{
decryptionKey = await downloadContext.ApiHelper.GetDecryptionKeyCDM(drmHeaders, $"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/message/{messageId}?type=widevine", pssh);
}
Medium? mediaInfo = singlePaidMessageCollection.PreviewSingleMessageMedia.FirstOrDefault(m => m.id == paidMessageKVP.Key);
SingleMessage? messageInfo = singlePaidMessageCollection.SingleMessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await downloadContext.DownloadHelper.DownloadSingleMessagePreviewDRMVideo(
policy: policy,
signature: signature,
kvp: kvp,
url: mpdURL,
decryptionKey: decryptionKey,
folder: path,
lastModified: lastModified,
media_id: paidMessageKVP.Key,
api_type: "Messages",
task: task,
filenameFormat: downloadContext.FileNameFormatConfig.PaidMessageFileNameFormat ?? string.Empty,
messageInfo: messageInfo,
messageMedia: mediaInfo,
fromUser: messageInfo?.fromUser,
users: hasSelectedUsersKVP.Value);
if (isNew)
{
newPreviewPaidMessagesCount++;
}
else
{
oldPreviewPaidMessagesCount++;
}
}
}
else
{
Medium? mediaInfo = singlePaidMessageCollection.PreviewSingleMessageMedia.FirstOrDefault(m => m.id == paidMessageKVP.Key);
SingleMessage? messageInfo = singlePaidMessageCollection.SingleMessageObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true);
isNew = await downloadContext.DownloadHelper.DownloadMessagePreviewMedia(
url: paidMessageKVP.Value,
folder: path,
media_id: paidMessageKVP.Key,
api_type: "Messages",
task: task,
filenameFormat: downloadContext.FileNameFormatConfig.PaidMessageFileNameFormat ?? string.Empty,
messageInfo: messageInfo,
messageMedia: mediaInfo,
fromUser: messageInfo?.fromUser,
users: hasSelectedUsersKVP.Value);
if (isNew)
{
newPreviewPaidMessagesCount++;
}
else
{
oldPreviewPaidMessagesCount++;
}
}
}
task.StopTask();
});
AnsiConsole.Markup($"[red]Preview Paid Messages Already Downloaded: {oldPreviewPaidMessagesCount} New Preview Paid Messages Downloaded: {newPreviewPaidMessagesCount}[/]\n");
Log.Debug($"Preview Paid Messages Already Downloaded: {oldPreviewPaidMessagesCount} New Preview Paid Messages Downloaded: {newPreviewPaidMessagesCount}");
}
if (singlePaidMessageCollection != null && singlePaidMessageCollection.SingleMessages.Count > 0)
{
AnsiConsole.Markup($"[red]Found {singlePaidMessageCollection.SingleMessages.Count} Media from {singlePaidMessageCollection.SingleMessageObjects.Count} Paid Messages\n[/]");
Log.Debug($"Found {singlePaidMessageCollection.SingleMessages.Count} Media from {singlePaidMessageCollection.SingleMessageObjects.Count} Paid Messages");