Compare commits

...

16 Commits

Author SHA1 Message Date
167d6640e3 Move DisableBrowserAuth to Auth 2025-12-14 18:47:18 +00:00
e9786f2341 Merge pull request 'Add additional logging for ffmpeg and prevent NRE errors from obscuring the actual errors from ffmpeg.' (#97) from ffmpeg_additional_logging into master
Reviewed-on: sim0n00ps/OF-DL#97
2025-12-14 15:39:10 +00:00
Melithine
0eae466368 Add additional logging for ffmpeg and prevent NRE errors from obscuring the actual errors from ffmpeg. 2025-12-13 19:55:59 -08:00
616aaef1c8 Merge pull request 'Add doc site and Discord invite after the startup banner.' (#89) from header_update into master
Reviewed-on: sim0n00ps/OF-DL#89
Reviewed-by: whimsical-c4lic0 <whimsical-c4lic0@noreply.localhost>
2025-12-13 19:30:56 +00:00
34f055b00e Merge pull request 'Fix 32-bit integers' (#94) from whimsical-c4lic0/OF-DL:fix-32-bit-integers into master
Reviewed-on: sim0n00ps/OF-DL#94
Reviewed-by: melithine <melithine@noreply.localhost>
2025-12-13 19:30:27 +00:00
e7e1556b3c Convert all sizes from int to long 2025-12-13 02:57:58 -06:00
74def34f96 Convert all ids from int to long 2025-12-13 02:52:15 -06:00
Melithine
19730436d4 Add doc site and Discord invite after the startup banner. 2025-12-10 07:32:26 -08:00
eca38116fa Update docs/config/configuration.md 2025-12-02 00:02:50 +00:00
f6a9cbd305 Update docs/config/all-configuration-options.md 2025-12-02 00:00:36 +00:00
f75fa4e04c Merge pull request 'Add a 30s timeout to the Gitea version check.' (#84) from version_timeout into master
Reviewed-on: sim0n00ps/OF-DL#84
2025-12-01 22:47:50 +00:00
af5622ba82 Merge pull request 'Add ffmpeg version to startup output and log file.' (#83) from ffmpeg_version into master
Reviewed-on: sim0n00ps/OF-DL#83
2025-12-01 22:47:32 +00:00
0bf8dfa8ab Merge pull request 'fix: object reference not set by adding retry for GetDecryptionKeyOFDL' (#82) from TeenagerNeedsHelpQQ/OF-DL:master into master
Reviewed-on: sim0n00ps/OF-DL#82
2025-12-01 22:47:20 +00:00
Melithine
e98c5f74cf Add a 30s timeout to the Gitea version check. 2025-11-29 14:45:57 -08:00
Melithine
181ea8eef1 Add ffmpeg version to startup output and log file. 2025-11-29 13:46:43 -08:00
2bddedb644 fix: object reference not set by adding retry for GetDecryptionKeyOFDL 2025-11-29 15:01:21 +01:00
26 changed files with 389 additions and 241 deletions

View File

@ -17,7 +17,7 @@ namespace OF_DL.Entities.Archived
public Counters counters { get; set; }
public class Author
{
public int id { get; set; }
public long id { get; set; }
public string _view { get; set; }
}
@ -64,7 +64,7 @@ namespace OF_DL.Entities.Archived
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -73,7 +73,7 @@ namespace OF_DL.Entities.Archived
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Thumb
@ -81,7 +81,7 @@ namespace OF_DL.Entities.Archived
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Hls
@ -105,7 +105,7 @@ namespace OF_DL.Entities.Archived
public class LinkedPost
{
public string responseType { get; set; }
public int? id { get; set; }
public long? id { get; set; }
public DateTime? postedAt { get; set; }
public string postedAtPrecise { get; set; }
public object expiredAt { get; set; }

View File

@ -9,10 +9,10 @@ namespace OF_DL.Entities.Highlights
{
public class HighlightMedia
{
public int id { get; set; }
public int userId { get; set; }
public long id { get; set; }
public long userId { get; set; }
public string title { get; set; }
public int coverStoryId { get; set; }
public long coverStoryId { get; set; }
public string cover { get; set; }
public int storiesCount { get; set; }
public DateTime? createdAt { get; set; }
@ -30,7 +30,7 @@ namespace OF_DL.Entities.Highlights
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -50,7 +50,7 @@ namespace OF_DL.Entities.Highlights
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public Sources sources { get; set; }
}
@ -60,7 +60,7 @@ namespace OF_DL.Entities.Highlights
public int width { get; set; }
public int height { get; set; }
public int duration { get; set; }
public int size { get; set; }
public long size { get; set; }
public Sources sources { get; set; }
}
@ -80,14 +80,14 @@ namespace OF_DL.Entities.Highlights
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public Sources sources { get; set; }
}
public class Story
{
public int id { get; set; }
public int userId { get; set; }
public long id { get; set; }
public long userId { get; set; }
public bool isWatched { get; set; }
public bool isReady { get; set; }
public List<Medium> media { get; set; }
@ -102,7 +102,7 @@ namespace OF_DL.Entities.Highlights
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
}
}

View File

@ -12,10 +12,10 @@ namespace OF_DL.Entities.Highlights
public bool hasMore { get; set; }
public class List
{
public int id { get; set; }
public int userId { get; set; }
public long id { get; set; }
public long userId { get; set; }
public string title { get; set; }
public int coverStoryId { get; set; }
public long coverStoryId { get; set; }
public string cover { get; set; }
public int storiesCount { get; set; }
public DateTime? createdAt { get; set; }

View File

@ -34,7 +34,7 @@ namespace OF_DL.Entities.Lists
public class User
{
public int? id { get; set; }
public long? id { get; set; }
public string _view { get; set; }
}
}

View File

@ -14,7 +14,7 @@ namespace OF_DL.Entities.Lists
public string header { get; set; }
public HeaderSize headerSize { get; set; }
public HeaderThumbs headerThumbs { get; set; }
public int? id { get; set; }
public long? id { get; set; }
public string name { get; set; }
public string username { get; set; }
public bool? canLookStory { get; set; }
@ -92,7 +92,7 @@ namespace OF_DL.Entities.Lists
public class Subscribe
{
public object id { get; set; }
public int? userId { get; set; }
public long? userId { get; set; }
public int? subscriberId { get; set; }
public DateTime? date { get; set; }
public int? duration { get; set; }
@ -160,7 +160,7 @@ namespace OF_DL.Entities.Lists
public class SubscriptionBundle
{
public int? id { get; set; }
public long? id { get; set; }
public string? discount { get; set; }
public string? duration { get; set; }
public string? price { get; set; }

View File

@ -43,7 +43,7 @@ namespace OF_DL.Entities.Messages
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -52,7 +52,7 @@ namespace OF_DL.Entities.Messages
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Thumb
@ -60,12 +60,12 @@ namespace OF_DL.Entities.Messages
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class FromUser
{
public int? id { get; set; }
public long? id { get; set; }
public string _view { get; set; }
}

View File

@ -17,7 +17,7 @@ namespace OF_DL.Entities.Messages
public string header { get; set; }
public HeaderSize headerSize { get; set; }
public HeaderThumbs headerThumbs { get; set; }
public int? id { get; set; }
public long? id { get; set; }
public string name { get; set; }
public string username { get; set; }
public bool canLookStory { get; set; }
@ -81,7 +81,7 @@ namespace OF_DL.Entities.Messages
{
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class SingleMessage

View File

@ -20,7 +20,7 @@ public class Post
public string tailMarker { get; set; }
public class Author
{
public int id { get; set; }
public long id { get; set; }
public string _view { get; set; }
}
@ -56,7 +56,7 @@ public class Post
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -65,7 +65,7 @@ public class Post
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Thumb
@ -73,7 +73,7 @@ public class Post
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Hls

View File

@ -12,7 +12,7 @@ namespace OF_DL.Entities.Post
public class SinglePost
{
public string responseType { get; set; }
public int id { get; set; }
public long id { get; set; }
public DateTime postedAt { get; set; }
public string postedAtPrecise { get; set; }
public object expiredAt { get; set; }
@ -67,7 +67,7 @@ namespace OF_DL.Entities.Post
public List<object> preview { get; set; }
public class Author
{
public int id { get; set; }
public long id { get; set; }
public string _view { get; set; }
}
@ -85,7 +85,7 @@ namespace OF_DL.Entities.Post
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -94,7 +94,7 @@ namespace OF_DL.Entities.Post
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Thumb
@ -102,7 +102,7 @@ namespace OF_DL.Entities.Post
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Info
@ -134,7 +134,7 @@ namespace OF_DL.Entities.Post
{
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public string url { get; set; }
}
@ -143,7 +143,7 @@ namespace OF_DL.Entities.Post
public string source { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public int duration { get; set; }
}

View File

@ -13,17 +13,14 @@ namespace OF_DL.Entities.Purchased
public List<List> list { get; set; }
public bool hasMore { get; set; }
public class FromUser
{
public int id { get; set; }
public long id { get; set; }
public string _view { get; set; }
}
public class Author
{
public int id { get; set; }
public long id { get; set; }
public string _view { get; set; }
}
@ -81,10 +78,5 @@ namespace OF_DL.Entities.Purchased
public string hls { get; set; }
public string dash { get; set; }
}
}
}

View File

@ -9,8 +9,8 @@ namespace OF_DL.Entities.Stories
{
public class Stories
{
public int id { get; set; }
public int userId { get; set; }
public long id { get; set; }
public long userId { get; set; }
public bool isWatched { get; set; }
public bool isReady { get; set; }
public List<Medium> media { get; set; }
@ -31,7 +31,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -51,7 +51,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public Sources sources { get; set; }
}
@ -61,7 +61,7 @@ namespace OF_DL.Entities.Stories
public int width { get; set; }
public int height { get; set; }
public int duration { get; set; }
public int size { get; set; }
public long size { get; set; }
public Sources sources { get; set; }
}
@ -81,7 +81,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public Sources sources { get; set; }
}
@ -90,7 +90,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
}
}

View File

@ -17,7 +17,7 @@ namespace OF_DL.Entities.Streams
public Counters counters { get; set; }
public class Author
{
public int id { get; set; }
public long id { get; set; }
public string _view { get; set; }
}
@ -46,7 +46,7 @@ namespace OF_DL.Entities.Streams
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public List<object> sources { get; set; }
}
@ -55,7 +55,7 @@ namespace OF_DL.Entities.Streams
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Thumb
@ -63,7 +63,7 @@ namespace OF_DL.Entities.Streams
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
}
public class Info
@ -154,7 +154,7 @@ namespace OF_DL.Entities.Streams
{
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public string url { get; set; }
}
@ -163,7 +163,7 @@ namespace OF_DL.Entities.Streams
public string source { get; set; }
public int width { get; set; }
public int height { get; set; }
public int size { get; set; }
public long size { get; set; }
public int duration { get; set; }
}

View File

@ -36,7 +36,7 @@ namespace OF_DL.Entities
public string header { get; set; }
public HeaderSize headerSize { get; set; }
public HeaderThumbs headerThumbs { get; set; }
public int id { get; set; }
public long id { get; set; }
public string name { get; set; }
public string username { get; set; }
public bool? canLookStory { get; set; }
@ -96,7 +96,7 @@ namespace OF_DL.Entities
public class Subscribe
{
public object id { get; set; }
public int? userId { get; set; }
public long? userId { get; set; }
public int? subscriberId { get; set; }
public DateTime? date { get; set; }
public int? duration { get; set; }

View File

@ -14,7 +14,7 @@ namespace OF_DL.Entities
public string? header { get; set; }
public HeaderSize headerSize { get; set; }
public HeaderThumbs headerThumbs { get; set; }
public int? id { get; set; }
public long? id { get; set; }
public string name { get; set; }
public string username { get; set; }
public bool? canLookStory { get; set; }
@ -124,7 +124,7 @@ namespace OF_DL.Entities
public class Subscribe
{
public long? id { get; set; }
public int? userId { get; set; }
public long? userId { get; set; }
public int? subscriberId { get; set; }
public DateTime? date { get; set; }
public int? duration { get; set; }

View File

@ -31,6 +31,8 @@ public class APIHelper : IAPIHelper
private readonly Auth auth;
private static DateTime? cachedDynamicRulesExpiration;
private static DynamicRules? cachedDynamicRules;
private const int MaxAttempts = 30;
private const int DelayBetweenAttempts = 3000;
static APIHelper()
{
@ -293,11 +295,11 @@ public class APIHelper : IAPIHelper
}
public async Task<Dictionary<string, int>?> GetAllSubscriptions(Dictionary<string, string> getParams, string endpoint, bool includeRestricted, IDownloadConfig config)
public async Task<Dictionary<string, long>?> GetAllSubscriptions(Dictionary<string, string> getParams, string endpoint, bool includeRestricted, IDownloadConfig config)
{
try
{
Dictionary<string, int> users = new();
Dictionary<string, long> users = new();
Subscriptions subscriptions = new();
Log.Debug("Calling GetAllSubscrptions");
@ -357,7 +359,7 @@ public class APIHelper : IAPIHelper
return null;
}
public async Task<Dictionary<string, int>?> GetActiveSubscriptions(string endpoint, bool includeRestricted, IDownloadConfig config)
public async Task<Dictionary<string, long>?> GetActiveSubscriptions(string endpoint, bool includeRestricted, IDownloadConfig config)
{
Dictionary<string, string> getParams = new()
{
@ -371,7 +373,7 @@ public class APIHelper : IAPIHelper
}
public async Task<Dictionary<string, int>?> GetExpiredSubscriptions(string endpoint, bool includeRestricted, IDownloadConfig config)
public async Task<Dictionary<string, long>?> GetExpiredSubscriptions(string endpoint, bool includeRestricted, IDownloadConfig config)
{
Dictionary<string, string> getParams = new()
@ -388,7 +390,7 @@ public class APIHelper : IAPIHelper
}
public async Task<Dictionary<string, int>> GetLists(string endpoint, IDownloadConfig config)
public async Task<Dictionary<string, long>> GetLists(string endpoint, IDownloadConfig config)
{
Log.Debug("Calling GetLists");
@ -402,7 +404,7 @@ public class APIHelper : IAPIHelper
{ "limit", "50" },
{ "format", "infinite" }
};
Dictionary<string, int> lists = new();
Dictionary<string, long> lists = new();
while (true)
{
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
@ -2065,13 +2067,13 @@ public class APIHelper : IAPIHelper
return null;
}
public async Task<Dictionary<string, int>> GetPurchasedTabUsers(string endpoint, IDownloadConfig config, Dictionary<string, int> users)
public async Task<Dictionary<string, long>> GetPurchasedTabUsers(string endpoint, IDownloadConfig config, Dictionary<string, long> users)
{
Log.Debug($"Calling GetPurchasedTabUsers - {endpoint}");
try
{
Dictionary<string, int> purchasedTabUsers = new();
Dictionary<string, long> purchasedTabUsers = new();
Purchased purchased = new();
int post_limit = 50;
Dictionary<string, string> getParams = new()
@ -2238,7 +2240,7 @@ public class APIHelper : IAPIHelper
return null;
}
public async Task<List<PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder, IDownloadConfig config, Dictionary<string, int> users)
public async Task<List<PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder, IDownloadConfig config, Dictionary<string, long> users)
{
Log.Debug($"Calling GetPurchasedTab - {endpoint}");
@ -2674,13 +2676,10 @@ public class APIHelper : IAPIHelper
return DateTime.Now;
}
public async Task<string> GetDecryptionKeyCDRMProject(Dictionary<string, string> drmHeaders, string licenceURL, string pssh)
{
Log.Debug("Calling GetDecryptionKey");
const int maxAttempts = 30;
const int delayBetweenAttempts = 3000;
int attempt = 0;
try
@ -2701,7 +2700,7 @@ public class APIHelper : IAPIHelper
Log.Debug($"Posting to CDRM Project: {json}");
while (attempt < maxAttempts)
while (attempt < MaxAttempts)
{
attempt++;
@ -2727,19 +2726,19 @@ public class APIHelper : IAPIHelper
}
else
{
Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {maxAttempts}");
if (attempt < maxAttempts)
Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {MaxAttempts}");
if (attempt < MaxAttempts)
{
await Task.Delay(delayBetweenAttempts);
await Task.Delay(DelayBetweenAttempts);
}
}
}
else
{
Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {maxAttempts}");
if (attempt < maxAttempts)
Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {MaxAttempts}");
if (attempt < MaxAttempts)
{
await Task.Delay(delayBetweenAttempts);
await Task.Delay(DelayBetweenAttempts);
}
}
}
@ -2764,11 +2763,10 @@ public class APIHelper : IAPIHelper
{
Log.Debug("Calling GetDecryptionOFDL");
try
{
string dcValue = string.Empty;
HttpClient client = new();
int attempt = 0;
OFDLRequest ofdlRequest = new OFDLRequest
{
@ -2781,17 +2779,27 @@ public class APIHelper : IAPIHelper
Log.Debug($"Posting to ofdl.tools: {json}");
HttpRequestMessage request = new(HttpMethod.Post, "https://ofdl.tools/WV")
while (attempt < MaxAttempts)
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
attempt++;
using var response = await client.SendAsync(request);
HttpRequestMessage request = new(HttpMethod.Post, "https://ofdl.tools/WV")
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
using var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
continue;
if (response.IsSuccessStatusCode)
{
string body = await response.Content.ReadAsStringAsync();
return body;
if (!body.TrimStart().StartsWith('{'))
return body;
Log.Debug($"Received JSON object instead of string. Retrying... Attempt {attempt} of {MaxAttempts}");
await Task.Delay(DelayBetweenAttempts);
}
}
catch (Exception ex)
@ -2805,6 +2813,7 @@ public class APIHelper : IAPIHelper
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
}
}
return null;
}

View File

@ -135,7 +135,7 @@ namespace OF_DL.Helpers
}
}
public async Task CreateUsersDB(Dictionary<string, int> users)
public async Task CreateUsersDB(Dictionary<string, long> users)
{
try
{
@ -150,7 +150,7 @@ namespace OF_DL.Helpers
}
Log.Debug("Adding missing creators");
foreach (KeyValuePair<string, int> user in users)
foreach (KeyValuePair<string, long> user in users)
{
using (SqliteCommand checkCmd = new($"SELECT user_id, username FROM users WHERE user_id = @userId;", connection))
{
@ -190,7 +190,7 @@ namespace OF_DL.Helpers
}
}
public async Task CheckUsername(KeyValuePair<string, int> user, string path)
public async Task CheckUsername(KeyValuePair<string, long> user, string path)
{
try
{
@ -243,7 +243,7 @@ namespace OF_DL.Helpers
}
}
public async Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at, int user_id)
public async Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at, long user_id)
{
try
{
@ -495,7 +495,7 @@ namespace OF_DL.Helpers
FROM posts AS P
INNER JOIN medias AS m
ON P.post_id = m.post_id
WHERE m.downloaded = 0
WHERE m.downloaded = 0
)", connection);
var scalarValue = await cmd.ExecuteScalarAsync();
if(scalarValue != null && scalarValue != DBNull.Value)

View File

@ -77,9 +77,9 @@ public class DownloadHelper : IDownloadHelper
try
{
string customFileName = string.Empty;
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
string extension = Path.GetExtension(url.Split("?")[0]);
@ -164,7 +164,7 @@ public class DownloadHelper : IDownloadHelper
object? postMedia,
object? author,
string username,
Dictionary<string, int> users,
Dictionary<string, long> users,
IFileNameHelper fileNameHelper,
CustomFileNameOption option)
{
@ -628,9 +628,38 @@ public class DownloadHelper : IDownloadHelper
// default:
// tempFilename = $"{folder}{path}/{filename}_source.mp4";
// break;
//}
//}
string parameters = $"-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}\"";
// Configure ffmpeg log level and optional report file location
bool ffmpegDebugLogging = Log.IsEnabled(Serilog.Events.LogEventLevel.Debug);
string logLevelArgs = ffmpegDebugLogging || downloadConfig.LoggingLevel is LoggingLevel.Verbose or LoggingLevel.Debug
? "-loglevel debug -report"
: downloadConfig.LoggingLevel switch
{
LoggingLevel.Information => "-loglevel info",
LoggingLevel.Warning => "-loglevel warning",
LoggingLevel.Error => "-loglevel error",
LoggingLevel.Fatal => "-loglevel fatal",
_ => string.Empty
};
if (logLevelArgs.Contains("-report", StringComparison.OrdinalIgnoreCase))
{
// Direct ffmpeg report files into the same logs directory Serilog uses (relative to current working directory)
string logDir = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "logs"));
Directory.CreateDirectory(logDir);
string ffReportPath = Path.Combine(logDir, "ffmpeg-%p-%t.log"); // ffmpeg will replace %p/%t
Environment.SetEnvironmentVariable("FFREPORT", $"file={ffReportPath}:level=32");
Log.Debug("FFREPORT enabled at: {FFREPORT} (cwd: {Cwd})", Environment.GetEnvironmentVariable("FFREPORT"), Environment.CurrentDirectory);
}
else
{
Environment.SetEnvironmentVariable("FFREPORT", null);
Log.Debug("FFREPORT disabled (cwd: {Cwd})", Environment.CurrentDirectory);
}
string 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();
Log.Debug($"Calling FFMPEG with Parameters: {parameters}");
@ -661,7 +690,7 @@ public class DownloadHelper : IDownloadHelper
#endregion
#region normal posts
public async Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, int> users)
public async Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
@ -679,7 +708,7 @@ public class DownloadHelper : IDownloadHelper
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
}
public async Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary<string, int> users)
public async Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null)
@ -697,7 +726,7 @@ public class DownloadHelper : IDownloadHelper
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
}
public async 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, int> users)
public async 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)
{
string path;
if (downloadConfig.FolderPerPost && streamInfo != null && streamInfo?.id is not null && streamInfo?.postedAt is not null)
@ -717,7 +746,7 @@ public class DownloadHelper : IDownloadHelper
}
public async Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary<string, int> users)
public async Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
@ -735,7 +764,7 @@ public class DownloadHelper : IDownloadHelper
}
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, int> users)
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)
{
string path = "/Archived/Posts/Free";
Uri uri = new(url);
@ -754,7 +783,7 @@ public class DownloadHelper : IDownloadHelper
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, filename);
}
public async Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary<string, int> users)
public async Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
@ -771,7 +800,7 @@ public class DownloadHelper : IDownloadHelper
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, task, filename, resolvedFilename);
}
public async Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, int> users)
public async Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null)
@ -797,7 +826,7 @@ public class DownloadHelper : IDownloadHelper
Purchased.List? messageInfo,
Medium? messageMedia,
Purchased.FromUser? fromUser,
Dictionary<string, int> users)
Dictionary<string, long> users)
{
string path;
if (downloadConfig.FolderPerPaidPost && messageInfo != null && messageInfo?.id is not null && messageInfo?.postedAt is not null)
@ -819,19 +848,19 @@ public class DownloadHelper : IDownloadHelper
{
try
{
string path = $"/Profile";
string path = $"/Profile";
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
if (!string.IsNullOrEmpty(avatarUrl))
{
string avatarpath = $"{path}/Avatars";
if (!Directory.Exists(folder + avatarpath))
if (!Directory.Exists(folder + avatarpath))
{
Directory.CreateDirectory(folder + avatarpath);
Directory.CreateDirectory(folder + avatarpath);
}
List<string> avatarMD5Hashes = WidevineClient.Utils.CalculateFolderMD5(folder + avatarpath);
@ -872,9 +901,9 @@ public class DownloadHelper : IDownloadHelper
if (!string.IsNullOrEmpty(headerUrl))
{
string headerpath = $"{path}/Headers";
if (!Directory.Exists(folder + headerpath))
if (!Directory.Exists(folder + headerpath))
{
Directory.CreateDirectory(folder + headerpath);
Directory.CreateDirectory(folder + headerpath);
}
List<string> headerMD5Hashes = WidevineClient.Utils.CalculateFolderMD5(folder + headerpath);
@ -965,12 +994,22 @@ public class DownloadHelper : IDownloadHelper
private void OnError(object sender, ConversionErrorEventArgs e)
{
Log.Debug("[{0} => {1}]: Error: {2}\n{3}", e.Input.Name, e.Output.Name, e.Exception.ExitCode, e.Exception.InnerException);
// Guard all fields to avoid NullReference exceptions from FFmpeg.NET
var input = e?.Input?.Name ?? "<none>";
var output = e?.Output?.Name ?? "<none>";
var exitCode = e?.Exception?.ExitCode.ToString() ?? "<unknown>";
var message = e?.Exception?.Message ?? "<no message>";
var 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);
}
#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, ProgressTask task, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary<string, int> users)
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, ProgressTask task, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary<string, long> users)
{
try
{
@ -986,9 +1025,9 @@ public class DownloadHelper : IDownloadHelper
{
path = "/Messages/Free/Videos";
}
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
@ -1078,7 +1117,7 @@ public class DownloadHelper : IDownloadHelper
}
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, int> users)
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)
{
try
{
@ -1094,9 +1133,9 @@ public class DownloadHelper : IDownloadHelper
{
path = "/Messages/Paid/Videos";
}
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
@ -1184,7 +1223,7 @@ public class DownloadHelper : IDownloadHelper
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, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, int> users)
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, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, long> users)
{
try
{
@ -1291,7 +1330,7 @@ public class DownloadHelper : IDownloadHelper
}
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, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, int> users)
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, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, long> users)
{
try
{
@ -1307,9 +1346,9 @@ public class DownloadHelper : IDownloadHelper
{
path = "/Posts/Free/Videos";
}
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
@ -1396,7 +1435,7 @@ public class DownloadHelper : IDownloadHelper
}
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, ProgressTask task, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary<string, int> users)
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, ProgressTask task, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary<string, long> users)
{
try
{
@ -1412,9 +1451,9 @@ public class DownloadHelper : IDownloadHelper
{
path = "/Posts/Free/Videos";
}
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
@ -1475,7 +1514,7 @@ public class DownloadHelper : IDownloadHelper
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);
@ -1501,7 +1540,7 @@ public class DownloadHelper : IDownloadHelper
}
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, ProgressTask task, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, int> users)
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, ProgressTask task, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users)
{
try
{
@ -1607,7 +1646,7 @@ public class DownloadHelper : IDownloadHelper
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, ProgressTask task, string? filenameFormat, Purchased.List? postInfo, Medium? postMedia, Purchased.FromUser? fromUser, Dictionary<string, int> users)
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, ProgressTask task, string? filenameFormat, Purchased.List? postInfo, Medium? postMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users)
{
try
{
@ -1623,9 +1662,9 @@ public class DownloadHelper : IDownloadHelper
{
path = "/Posts/Paid/Videos";
}
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
@ -1715,7 +1754,7 @@ public class DownloadHelper : IDownloadHelper
}
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, ProgressTask task, string? filenameFormat, Archived.List? postInfo, Archived.Medium? postMedia, Archived.Author? author, Dictionary<string, int> users)
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, ProgressTask task, string? filenameFormat, Archived.List? postInfo, Archived.Medium? postMedia, Archived.Author? author, Dictionary<string, long> users)
{
try
{
@ -1723,9 +1762,9 @@ public class DownloadHelper : IDownloadHelper
Uri uri = new(url);
string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
string path = "/Archived/Posts/Free/Videos";
if (!Directory.Exists(folder + path))
if (!Directory.Exists(folder + path))
{
Directory.CreateDirectory(folder + path);
Directory.CreateDirectory(folder + path);
}
if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)

View File

@ -18,7 +18,7 @@ namespace OF_DL.Helpers
this.auth = auth;
}
public async Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, int> users = null)
public async Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, long> users = null)
{
Dictionary<string, string> values = new();
Type type1 = obj1.GetType();
@ -36,7 +36,7 @@ namespace OF_DL.Helpers
{
drmProperty = GetNestedPropertyValue(obj2, "files.drm");
}
if(fileProperty != null && drmProperty != null && propertyName == "mediaCreatedAt")
{
object mpdurl = GetNestedPropertyValue(obj2, "files.drm.manifest.dash");
@ -66,7 +66,7 @@ namespace OF_DL.Helpers
continue;
}
}
}
PropertyInfo? property = Array.Find(properties2, p => p.Name.Equals(propertyName.Replace("media", ""), StringComparison.OrdinalIgnoreCase));
if (property != null)

View File

@ -16,7 +16,7 @@ namespace OF_DL.Helpers
Task<string> GetDecryptionKeyCDM(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
Task<DateTime> GetDRMMPDLastModified(string mpdUrl, string policy, string signature, string kvp);
Task<string> GetDRMMPDPSSH(string mpdUrl, string policy, string signature, string kvp);
Task<Dictionary<string, int>> GetLists(string endpoint, IDownloadConfig config);
Task<Dictionary<string, long>> GetLists(string endpoint, IDownloadConfig config);
Task<List<string>> GetListUsers(string endpoint, IDownloadConfig config);
Task<Dictionary<long, string>> GetMedia(MediaType mediatype, string endpoint, string? username, string folder, IDownloadConfig config, List<long> paid_post_ids);
Task<PaidPostCollection> GetPaidPosts(string endpoint, string folder, string username, IDownloadConfig config, List<long> paid_post_ids, StatusContext ctx);
@ -26,13 +26,13 @@ namespace OF_DL.Helpers
Task<ArchivedCollection> GetArchived(string endpoint, string folder, IDownloadConfig config, StatusContext ctx);
Task<MessageCollection> GetMessages(string endpoint, string folder, IDownloadConfig config, StatusContext ctx);
Task<PaidMessageCollection> GetPaidMessages(string endpoint, string folder, string username, IDownloadConfig config, StatusContext ctx);
Task<Dictionary<string, int>> GetPurchasedTabUsers(string endpoint, IDownloadConfig config, Dictionary<string, int> users);
Task<List<PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder, IDownloadConfig config, Dictionary<string, int> users);
Task<Dictionary<string, long>> GetPurchasedTabUsers(string endpoint, IDownloadConfig config, Dictionary<string, long> users);
Task<List<PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder, IDownloadConfig config, Dictionary<string, long> users);
Task<User> GetUserInfo(string endpoint);
Task<JObject> GetUserInfoById(string endpoint);
Dictionary<string, string> GetDynamicHeaders(string path, string queryParam);
Task<Dictionary<string, int>> GetActiveSubscriptions(string endpoint, bool includeRestrictedSubscriptions, IDownloadConfig config);
Task<Dictionary<string, int>> GetExpiredSubscriptions(string endpoint, bool includeRestrictedSubscriptions, IDownloadConfig config);
Task<Dictionary<string, long>> GetActiveSubscriptions(string endpoint, bool includeRestrictedSubscriptions, IDownloadConfig config);
Task<Dictionary<string, long>> GetExpiredSubscriptions(string endpoint, bool includeRestrictedSubscriptions, IDownloadConfig config);
Task<string> GetDecryptionKeyOFDL(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
}
}

View File

@ -2,12 +2,12 @@ namespace OF_DL.Helpers
{
public interface IDBHelper
{
Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at, int user_id);
Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at, long user_id);
Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at);
Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at);
Task CreateDB(string folder);
Task CreateUsersDB(Dictionary<string, int> users);
Task CheckUsername(KeyValuePair<string, int> user, string path);
Task CreateUsersDB(Dictionary<string, long> users);
Task CheckUsername(KeyValuePair<string, long> user, string path);
Task AddMedia(string folder, long media_id, long post_id, string link, string? directory, string? filename, long? size, string api_type, string media_type, bool preview, bool downloaded, DateTime? created_at);
Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename, long size, bool downloaded, DateTime created_at);
Task<long> GetStoredFileSize(string folder, long media_id, string api_type);

View File

@ -12,25 +12,25 @@ namespace OF_DL.Helpers
public interface IDownloadHelper
{
Task<long> CalculateTotalFileSize(List<string> urls);
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, int> users);
Task<bool> DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Archived.List postInfo, Archived.Medium postMedia, Archived.Author author, Dictionary<string, int> users);
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary<string, int> users);
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);
Task<bool> DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Archived.List postInfo, Archived.Medium postMedia, Archived.Author author, Dictionary<string, long> users);
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary<string, long> users);
Task DownloadAvatarHeader(string? avatarUrl, string? headerUrl, string folder, string username);
Task<bool> DownloadMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Messages.List messageInfo, Messages.Medium messageMedia, Messages.FromUser fromUser, Dictionary<string, int> users);
Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Messages.List messageInfo, Messages.Medium messageMedia, Messages.FromUser fromUser, Dictionary<string, int> users);
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Post.List postInfo, Post.Medium postMedia, Post.Author author, Dictionary<string, int> users);
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, int> users);
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary<string, int> users);
Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary<string, int> users);
Task<bool> DownloadMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Messages.List messageInfo, Messages.Medium messageMedia, Messages.FromUser fromUser, Dictionary<string, long> users);
Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Messages.List messageInfo, Messages.Medium messageMedia, Messages.FromUser fromUser, Dictionary<string, long> users);
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Post.List postInfo, Post.Medium postMedia, Post.Author author, Dictionary<string, long> users);
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, long> users);
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary<string, long> users);
Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary<string, long> users);
Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, int> users);
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, int> users);
Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, long> users);
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);
Task<bool> DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, int> users);
Task<bool> DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List postInfo, Medium postMedia, Purchased.FromUser fromUser, Dictionary<string, int> users);
Task<bool> DownloadPurchasedPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary<string, int> users);
Task<bool> DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, long> users);
Task<bool> DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List postInfo, Medium postMedia, Purchased.FromUser fromUser, Dictionary<string, long> users);
Task<bool> DownloadPurchasedPostMedia(string url, string folder, long media_id, string api_type, ProgressTask task, string filenameFormat, Purchased.List messageInfo, Medium messageMedia, Purchased.FromUser fromUser, Dictionary<string, long> users);
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, int> 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, int> users);
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);
}
}

View File

@ -3,6 +3,6 @@ namespace OF_DL.Helpers
public interface IFileNameHelper
{
Task<string> BuildFilename(string fileFormat, Dictionary<string, string> values);
Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, int> users = null);
Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, long> users = null);
}
}

View File

@ -6,14 +6,15 @@ namespace OF_DL.Helpers;
public static class VersionHelper
{
public static string? GetLatestReleaseTag()
private static readonly HttpClient httpClient = new HttpClient();
private const string url = "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest";
public static async Task<string?> GetLatestReleaseTag(CancellationToken cancellationToken = default)
{
Log.Debug("Calling GetLatestReleaseTag");
try
{
HttpClient client = new();
HttpRequestMessage request = new(HttpMethod.Get, "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest");
using var response = client.Send(request);
var response = await httpClient.GetAsync(url, cancellationToken);
if (!response.IsSuccessStatusCode)
{
@ -21,7 +22,7 @@ public static class VersionHelper
return null;
}
var body = response.Content.ReadAsStringAsync().Result;
var body = await response.Content.ReadAsStringAsync();
Log.Debug("GetLatestReleaseTag API Response: ");
Log.Debug(body);
@ -36,6 +37,10 @@ public static class VersionHelper
return versionCheckResponse.TagName;
}
catch (OperationCanceledException)
{
throw; // Rethrow timeout exceptions to be handled by the caller
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);

View File

@ -116,6 +116,8 @@ public class Program
.CreateLogger();
AnsiConsole.Write(new FigletText("Welcome to OF-DL").Color(Color.Red));
AnsiConsole.Markup("Documentation: [link]https://docs.ofdl.tools/[/]\n");
AnsiConsole.Markup("Discord server: [link]https://discord.com/invite/6bUW8EJ53j[/]\n\n");
//Remove config.json and convert to config.conf
if (File.Exists("config.json"))
@ -129,6 +131,10 @@ public class Program
if (jsonConfig != null)
{
var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth");
hoconConfig.AppendLine("Auth {");
hoconConfig.AppendLine($" DisableBrowserAuth = \"{jsonConfig.DisableBrowserAuth.ToString().ToLower()}\"");
hoconConfig.AppendLine("}");
hoconConfig.AppendLine("# External Tools");
hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\"");
@ -253,7 +259,7 @@ public class Program
config = new Entities.Config
{
//Auth
DisableBrowserAuth = hoconConfig.GetBoolean("DisableBrowserAuth"),
DisableBrowserAuth = hoconConfig.GetBoolean("Auth.DisableBrowserAuth"),
// FFmpeg Settings
FFmpegPath = hoconConfig.GetString("External.FFmpegPath"),
@ -373,7 +379,9 @@ public class Program
Entities.Config jsonConfig = new Entities.Config();
var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth");
hoconConfig.AppendLine($"DisableBrowserAuth = {jsonConfig.DisableBrowserAuth.ToString().ToLower()}");
hoconConfig.AppendLine("Auth {");
hoconConfig.AppendLine($" DisableBrowserAuth = \"{jsonConfig.DisableBrowserAuth.ToString().ToLower()}\"");
hoconConfig.AppendLine("}");
hoconConfig.AppendLine("# External Tools");
hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\"");
@ -526,36 +534,50 @@ public class Program
// Only run the version check if not in DEBUG mode
#if !DEBUG
Version localVersion = Assembly.GetEntryAssembly()?.GetName().Version; //Only tested with numeric values.
String? latestReleaseTag = VersionHelper.GetLatestReleaseTag();
if (latestReleaseTag == null)
{
AnsiConsole.Markup("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]");
Log.Error("Failed to get the latest release tag.");
}
else
{
Version latestGiteaRelease = new Version(latestReleaseTag.Replace("OFDLV", ""));
// Create a cancellation token with 30 second timeout
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
String? latestReleaseTag = null;
// Compare the Versions
int versionComparison = localVersion.CompareTo(latestGiteaRelease);
if (versionComparison < 0)
{
// The version on GitHub is more up to date than this local release.
AnsiConsole.Markup("[red]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]");
AnsiConsole.Markup("[red]Please update to the current release, " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}: [link]https://git.ofdl.tools/sim0n00ps/OF-DL/releases[/]\n[/]");
Log.Debug("Detected outdated client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}");
Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}");
}
else
{
// This local version is greater than the release version on GitHub.
AnsiConsole.Markup("[green]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]");
AnsiConsole.Markup("[green]Latest Release version: " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}\n[/]");
Log.Debug("Detected client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}");
Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}");
}
}
try
{
latestReleaseTag = await VersionHelper.GetLatestReleaseTag(cts.Token);
}
catch (OperationCanceledException)
{
AnsiConsole.Markup("[yellow]Version check timed out after 30 seconds.\n[/]");
Log.Warning("Version check timed out after 30 seconds");
latestReleaseTag = null;
}
if (latestReleaseTag == null)
{
AnsiConsole.Markup("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]");
Log.Error("Failed to get the latest release tag.");
}
else
{
Version latestGiteaRelease = new Version(latestReleaseTag.Replace("OFDLV", ""));
// Compare the Versions
int versionComparison = localVersion.CompareTo(latestGiteaRelease);
if (versionComparison < 0)
{
// The version on GitHub is more up to date than this local release.
AnsiConsole.Markup("[red]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]");
AnsiConsole.Markup("[red]Please update to the current release, " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}: [link=https://git.ofdl.tools/sim0n00ps/OF-DL/releases]https://git.ofdl.tools/sim0n00ps/OF-DL/releases[/]\n[/]");
Log.Debug("Detected outdated client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}");
Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}");
}
else
{
// This local version is greater than the release version on GitHub.
AnsiConsole.Markup("[green]You are running OF-DL version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}\n[/]");
AnsiConsole.Markup("[green]Latest Release version: " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}\n[/]");
Log.Debug("Detected client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}");
Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}");
}
}
#else
AnsiConsole.Markup("[yellow]Running in Debug/Local mode. Version check skipped.\n[/]");
@ -748,6 +770,61 @@ public class Program
{
config.FFmpegPath = config.FFmpegPath.Replace(@"\", @"\\");
}
// Get FFmpeg version
try
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = config.FFmpegPath,
Arguments = "-version",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = System.Diagnostics.Process.Start(processStartInfo))
{
if (process != null)
{
string output = await process.StandardOutput.ReadToEndAsync();
await process.WaitForExitAsync();
// Log full output
Log.Information("FFmpeg version output:\n{Output}", output);
// Parse first line for console output
string firstLine = output.Split('\n')[0].Trim();
if (firstLine.StartsWith("ffmpeg version"))
{
// Extract version string (text between "ffmpeg version " and " Copyright")
int versionStart = "ffmpeg version ".Length;
int copyrightIndex = firstLine.IndexOf(" Copyright");
if (copyrightIndex > versionStart)
{
string version = firstLine.Substring(versionStart, copyrightIndex - versionStart);
AnsiConsole.Markup($"[green]ffmpeg version detected as {version}[/]\n");
}
else
{
// Fallback if Copyright not found
string version = firstLine.Substring(versionStart);
AnsiConsole.Markup($"[green]ffmpeg version detected as {version}[/]\n");
}
}
else
{
AnsiConsole.Markup($"[yellow]ffmpeg version could not be parsed[/]\n");
}
}
}
}
catch (Exception ex)
{
Log.Warning(ex, "Failed to get FFmpeg version");
AnsiConsole.Markup($"[yellow]Could not retrieve ffmpeg version[/]\n");
}
}
else
{
@ -850,12 +927,12 @@ public class Program
do
{
DateTime startTime = DateTime.Now;
Dictionary<string, int> users = new();
Dictionary<string, int> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
Dictionary<string, long> users = new();
Dictionary<string, long> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
Log.Debug("Subscriptions: ");
foreach (KeyValuePair<string, int> activeSub in activeSubs)
foreach (KeyValuePair<string, long> activeSub in activeSubs)
{
if (!users.ContainsKey(activeSub.Key))
{
@ -867,8 +944,8 @@ public class Program
{
Log.Debug("Inactive Subscriptions: ");
Dictionary<string, int> expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
foreach (KeyValuePair<string, int> expiredSub in expiredSubs)
Dictionary<string, long> expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
foreach (KeyValuePair<string, long> expiredSub in expiredSubs)
{
if (!users.ContainsKey(expiredSub.Key))
{
@ -878,7 +955,7 @@ public class Program
}
}
Dictionary<string, int> lists = await m_ApiHelper.GetLists("/lists", Config);
Dictionary<string, long> lists = await m_ApiHelper.GetLists("/lists", Config);
// Remove users from the list if they are in the ignored list
if (!string.IsNullOrEmpty(Config.IgnoredUsersListName))
@ -896,28 +973,28 @@ public class Program
}
await dBHelper.CreateUsersDB(users);
KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP;
KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP;
if(Config.NonInteractiveMode && Config.NonInteractiveModePurchasedTab)
{
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, int>>(true, new Dictionary<string, int> { { "PurchasedTab", 0 } });
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(true, new Dictionary<string, long> { { "PurchasedTab", 0 } });
}
else if (Config.NonInteractiveMode && string.IsNullOrEmpty(Config.NonInteractiveModeListName))
{
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, int>>(true, users);
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(true, users);
}
else if (Config.NonInteractiveMode && !string.IsNullOrEmpty(Config.NonInteractiveModeListName))
{
var listId = lists[Config.NonInteractiveModeListName];
var listUsernames = await m_ApiHelper.GetListUsers($"/lists/{listId}/users", Config) ?? [];
var selectedUsers = users.Where(x => listUsernames.Contains(x.Key)).Distinct().ToDictionary(x => x.Key, x => x.Value);
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, int>>(true, selectedUsers);
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(true, selectedUsers);
}
else
{
var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists);
Config = userSelectionResult.updatedConfig;
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, int>>(userSelectionResult.IsExit, userSelectionResult.selectedUsers);
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(userSelectionResult.IsExit, userSelectionResult.selectedUsers);
}
if (hasSelectedUsersKVP.Key && hasSelectedUsersKVP.Value != null && hasSelectedUsersKVP.Value.ContainsKey("SinglePost"))
@ -983,9 +1060,9 @@ public class Program
}
else if (hasSelectedUsersKVP.Key && hasSelectedUsersKVP.Value != null && hasSelectedUsersKVP.Value.ContainsKey("PurchasedTab"))
{
Dictionary<string, int> purchasedTabUsers = await m_ApiHelper.GetPurchasedTabUsers("/posts/paid/all", Config, users);
Dictionary<string, long> purchasedTabUsers = await m_ApiHelper.GetPurchasedTabUsers("/posts/paid/all", Config, users);
AnsiConsole.Markup($"[red]Checking folders for Users in Purchased Tab\n[/]");
foreach (KeyValuePair<string, int> user in purchasedTabUsers)
foreach (KeyValuePair<string, long> user in purchasedTabUsers)
{
string path = "";
if (!string.IsNullOrEmpty(Config.DownloadPath))
@ -1143,7 +1220,7 @@ public class Program
else if (hasSelectedUsersKVP.Key && !hasSelectedUsersKVP.Value.ContainsKey("ConfigChanged"))
{
//Iterate over each user in the list of users
foreach (KeyValuePair<string, int> user in hasSelectedUsersKVP.Value)
foreach (KeyValuePair<string, long> user in hasSelectedUsersKVP.Value)
{
int paidPostCount = 0;
int postCount = 0;
@ -1301,7 +1378,7 @@ public class Program
return combinedConfig;
}
private static async Task<int> DownloadPaidMessages(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, KeyValuePair<string, int> user, int paidMessagesCount, string path)
private static async Task<int> DownloadPaidMessages(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, KeyValuePair<string, long> user, int paidMessagesCount, string path)
{
Log.Debug($"Calling DownloadPaidMessages - {user.Key}");
@ -1434,7 +1511,7 @@ public class Program
return paidMessagesCount;
}
private static async Task<int> DownloadMessages(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, KeyValuePair<string, int> user, int messagesCount, string path)
private static async Task<int> DownloadMessages(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, KeyValuePair<string, long> user, int messagesCount, string path)
{
Log.Debug($"Calling DownloadMessages - {user.Key}");
@ -1567,7 +1644,7 @@ public class Program
return messagesCount;
}
private static async Task<int> DownloadHighlights(IDownloadContext downloadContext, KeyValuePair<string, int> user, int highlightsCount, string path)
private static async Task<int> DownloadHighlights(IDownloadContext downloadContext, KeyValuePair<string, long> user, int highlightsCount, string path)
{
Log.Debug($"Calling DownloadHighlights - {user.Key}");
@ -1624,7 +1701,7 @@ public class Program
return highlightsCount;
}
private static async Task<int> DownloadStories(IDownloadContext downloadContext, KeyValuePair<string, int> user, int storiesCount, string path)
private static async Task<int> DownloadStories(IDownloadContext downloadContext, KeyValuePair<string, long> user, int storiesCount, string path)
{
Log.Debug($"Calling DownloadStories - {user.Key}");
@ -1681,7 +1758,7 @@ public class Program
return storiesCount;
}
private static async Task<int> DownloadArchived(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, KeyValuePair<string, int> user, int archivedCount, string path)
private static async Task<int> DownloadArchived(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, KeyValuePair<string, long> user, int archivedCount, string path)
{
Log.Debug($"Calling DownloadArchived - {user.Key}");
@ -1814,7 +1891,7 @@ public class Program
return archivedCount;
}
private static async Task<int> DownloadFreePosts(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, KeyValuePair<string, int> user, int postCount, string path)
private static async Task<int> DownloadFreePosts(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, KeyValuePair<string, long> user, int postCount, string path)
{
Log.Debug($"Calling DownloadFreePosts - {user.Key}");
@ -1954,7 +2031,7 @@ public class Program
return postCount;
}
private static async Task<int> DownloadPaidPosts(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, KeyValuePair<string, int> user, int paidPostCount, string path)
private static async Task<int> DownloadPaidPosts(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, KeyValuePair<string, long> user, int paidPostCount, string path)
{
Log.Debug($"Calling DownloadPaidPosts - {user.Key}");
@ -2087,7 +2164,7 @@ public class Program
return paidPostCount;
}
private static async Task<int> DownloadPaidPostsPurchasedTab(IDownloadContext downloadContext, PaidPostCollection purchasedPosts, KeyValuePair<string, int> user, int paidPostCount, string path, Dictionary<string, int> users)
private static async Task<int> DownloadPaidPostsPurchasedTab(IDownloadContext downloadContext, PaidPostCollection purchasedPosts, KeyValuePair<string, long> user, int paidPostCount, string path, Dictionary<string, long> users)
{
int oldPaidPostCount = 0;
int newPaidPostCount = 0;
@ -2211,7 +2288,7 @@ public class Program
return paidPostCount;
}
private static async Task<int> DownloadPaidMessagesPurchasedTab(IDownloadContext downloadContext, PaidMessageCollection paidMessageCollection, KeyValuePair<string, int> user, int paidMessagesCount, string path, Dictionary<string, int> users)
private static async Task<int> DownloadPaidMessagesPurchasedTab(IDownloadContext downloadContext, PaidMessageCollection paidMessageCollection, KeyValuePair<string, long> user, int paidMessagesCount, string path, Dictionary<string, long> users)
{
int oldPaidMessagesCount = 0;
int newPaidMessagesCount = 0;
@ -2336,7 +2413,7 @@ public class Program
return paidMessagesCount;
}
private static async Task<int> DownloadStreams(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, KeyValuePair<string, int> user, int streamsCount, string path)
private static async Task<int> DownloadStreams(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, KeyValuePair<string, long> user, int streamsCount, string path)
{
Log.Debug($"Calling DownloadStreams - {user.Key}");
@ -2475,7 +2552,7 @@ public class Program
return streamsCount;
}
private static async Task<int> DownloadPaidMessage(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP, string username, int paidMessagesCount, string path, long message_id)
private static async Task<int> DownloadPaidMessage(IDownloadContext downloadContext, KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP, string username, int paidMessagesCount, string path, long message_id)
{
Log.Debug($"Calling DownloadPaidMessage - {username}");
@ -2605,7 +2682,7 @@ public class Program
return paidMessagesCount;
}
private static async Task DownloadSinglePost(IDownloadContext downloadContext, long post_id, string path, Dictionary<string, int> users)
private static async Task DownloadSinglePost(IDownloadContext downloadContext, long post_id, string path, Dictionary<string, long> users)
{
Log.Debug($"Calling DownloadSinglePost - {post_id.ToString()}");
@ -2723,10 +2800,10 @@ public class Program
}
}
public static async Task<(bool IsExit, Dictionary<string, int>? selectedUsers, Entities.Config? updatedConfig)> HandleUserSelection(APIHelper apiHelper, Entities.Config currentConfig, Dictionary<string, int> users, Dictionary<string, int> lists)
public static async Task<(bool IsExit, Dictionary<string, long>? selectedUsers, Entities.Config? updatedConfig)> HandleUserSelection(APIHelper apiHelper, Entities.Config currentConfig, Dictionary<string, long> users, Dictionary<string, long> lists)
{
bool hasSelectedUsers = false;
Dictionary<string, int> selectedUsers = new Dictionary<string, int>();
Dictionary<string, long> selectedUsers = new Dictionary<string, long>();
while (!hasSelectedUsers)
{
@ -2767,7 +2844,7 @@ public class Program
List<string> listUsernames = new();
foreach (var item in listSelection)
{
int listId = lists[item.Replace("[red]", "").Replace("[/]", "")];
long listId = lists[item.Replace("[red]", "").Replace("[/]", "")];
List<string> usernames = await apiHelper.GetListUsers($"/lists/{listId}/users", config);
foreach (string user in usernames)
{
@ -2807,11 +2884,11 @@ public class Program
}
break;
case "[red]Download Single Post[/]":
return (true, new Dictionary<string, int> { { "SinglePost", 0 } }, currentConfig);
return (true, new Dictionary<string, long> { { "SinglePost", 0 } }, currentConfig);
case "[red]Download Single Paid Message[/]":
return (true, new Dictionary<string, int> { { "SingleMessage", 0 } }, currentConfig);
return (true, new Dictionary<string, long> { { "SingleMessage", 0 } }, currentConfig);
case "[red]Download Purchased Tab[/]":
return (true, new Dictionary<string, int> { { "PurchasedTab", 0 } }, currentConfig);
return (true, new Dictionary<string, long> { { "PurchasedTab", 0 } }, currentConfig);
case "[red]Edit config.conf[/]":
while (true)
{
@ -2879,7 +2956,9 @@ public class Program
var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth");
hoconConfig.AppendLine($"DisableBrowserAuth = {newConfig.DisableBrowserAuth.ToString().ToLower()}");
hoconConfig.AppendLine("Auth {");
hoconConfig.AppendLine($" DisableBrowserAuth = \"{newConfig.DisableBrowserAuth.ToString().ToLower()}\"");
hoconConfig.AppendLine("}");
hoconConfig.AppendLine("# External Tools");
hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\"");
@ -2981,7 +3060,7 @@ public class Program
currentConfig = newConfig;
if (configChanged)
{
return (true, new Dictionary<string, int> { { "ConfigChanged", 0 } }, currentConfig);
return (true, new Dictionary<string, long> { { "ConfigChanged", 0 } }, currentConfig);
}
break;
}
@ -3039,7 +3118,9 @@ public class Program
var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth");
hoconConfig.AppendLine($"DisableBrowserAuth = {newConfig.DisableBrowserAuth.ToString().ToLower()}");
hoconConfig.AppendLine("Auth {");
hoconConfig.AppendLine($" DisableBrowserAuth = \"{newConfig.DisableBrowserAuth.ToString().ToLower()}\"");
hoconConfig.AppendLine("}");
hoconConfig.AppendLine("# External Tools");
hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\"");
@ -3135,7 +3216,7 @@ public class Program
if (configChanged)
{
return (true, new Dictionary<string, int> { { "ConfigChanged", 0 } }, currentConfig);
return (true, new Dictionary<string, long> { { "ConfigChanged", 0 } }, currentConfig);
}
break;
@ -3161,7 +3242,7 @@ public class Program
return (true, selectedUsers, currentConfig); // Return true to indicate selected users
}
public static List<string> GetMainMenuOptions(Dictionary<string, int> users, Dictionary<string, int> lists)
public static List<string> GetMainMenuOptions(Dictionary<string, long> users, Dictionary<string, long> lists)
{
if (lists.Count > 0)
{

View File

@ -516,3 +516,23 @@ Allowed values: Any positive integer or `-1`
Description: You won't need to set this, but if you see errors about the configured timeout of 100 seconds elapsing then
you could set this to be more than 100. It is recommended that you leave this as the default value.
## DisableTextSanitization
Type: `boolean`
Default: `false`
Allowed values: `true`, `false`
Description: When enabled, post/message text is stored as-is without XML stripping.
## DownloadVideoResolution
Type: `string`
Default: `"source"`
Allowed values: `"source"`, `"240"`, `"720"`
Description: This allows you to download videos in alternative resolutions, by default videos are downloaded in source resolution but some people prefer smoother videos at a lower resolution.

View File

@ -20,6 +20,8 @@ information about what it does, its default value, and the allowed values.
- [DownloadDateSelection](/config/all-configuration-options#downloaddateselection)
- [CustomDate](/config/all-configuration-options#customdate)
- [ShowScrapeSize](/config/all-configuration-options#showscrapesize)
- [DisableTextSanitization](/config/all-configuration-options#disabletextsanitization)
- [DownloadVideoResolution](/config/all-configuration-options#downloadvideoresolution)
- Media
- [DownloadAvatarHeaderPhoto](/config/all-configuration-options#downloadavatarheaderphoto)
- [DownloadPaidPosts](/config/all-configuration-options#downloadpaidposts)