merge upstream

This commit is contained in:
nyc_tk 2025-12-14 22:16:01 +00:00
commit 14d2411e88
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 Counters counters { get; set; }
public class Author public class Author
{ {
public int id { get; set; } public long id { get; set; }
public string _view { get; set; } public string _view { get; set; }
} }
@ -64,7 +64,7 @@ namespace OF_DL.Entities.Archived
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public List<object> sources { get; set; } public List<object> sources { get; set; }
} }
@ -73,7 +73,7 @@ namespace OF_DL.Entities.Archived
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
} }
public class Thumb public class Thumb
@ -81,7 +81,7 @@ namespace OF_DL.Entities.Archived
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
} }
public class Hls public class Hls
@ -105,7 +105,7 @@ namespace OF_DL.Entities.Archived
public class LinkedPost public class LinkedPost
{ {
public string responseType { get; set; } public string responseType { get; set; }
public int? id { get; set; } public long? id { get; set; }
public DateTime? postedAt { get; set; } public DateTime? postedAt { get; set; }
public string postedAtPrecise { get; set; } public string postedAtPrecise { get; set; }
public object expiredAt { get; set; } public object expiredAt { get; set; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ namespace OF_DL.Entities.Post
public class SinglePost public class SinglePost
{ {
public string responseType { get; set; } public string responseType { get; set; }
public int id { get; set; } public long id { get; set; }
public DateTime postedAt { get; set; } public DateTime postedAt { get; set; }
public string postedAtPrecise { get; set; } public string postedAtPrecise { get; set; }
public object expiredAt { get; set; } public object expiredAt { get; set; }
@ -67,7 +67,7 @@ namespace OF_DL.Entities.Post
public List<object> preview { get; set; } public List<object> preview { get; set; }
public class Author public class Author
{ {
public int id { get; set; } public long id { get; set; }
public string _view { get; set; } public string _view { get; set; }
} }
@ -85,7 +85,7 @@ namespace OF_DL.Entities.Post
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public List<object> sources { get; set; } public List<object> sources { get; set; }
} }
@ -94,7 +94,7 @@ namespace OF_DL.Entities.Post
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
} }
public class Thumb public class Thumb
@ -102,7 +102,7 @@ namespace OF_DL.Entities.Post
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
} }
public class Info public class Info
@ -134,7 +134,7 @@ namespace OF_DL.Entities.Post
{ {
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public string url { get; set; } public string url { get; set; }
} }
@ -143,7 +143,7 @@ namespace OF_DL.Entities.Post
public string source { get; set; } public string source { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public int duration { 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 List<List> list { get; set; }
public bool hasMore { get; set; } public bool hasMore { get; set; }
public class FromUser public class FromUser
{ {
public int id { get; set; } public long id { get; set; }
public string _view { get; set; } public string _view { get; set; }
} }
public class Author public class Author
{ {
public int id { get; set; } public long id { get; set; }
public string _view { get; set; } public string _view { get; set; }
} }
@ -81,10 +78,5 @@ namespace OF_DL.Entities.Purchased
public string hls { get; set; } public string hls { get; set; }
public string dash { get; set; } public string dash { get; set; }
} }
} }
} }

View File

@ -9,8 +9,8 @@ namespace OF_DL.Entities.Stories
{ {
public class Stories public class Stories
{ {
public int id { get; set; } public long id { get; set; }
public int userId { get; set; } public long userId { get; set; }
public bool isWatched { get; set; } public bool isWatched { get; set; }
public bool isReady { get; set; } public bool isReady { get; set; }
public List<Medium> media { get; set; } public List<Medium> media { get; set; }
@ -31,7 +31,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public List<object> sources { get; set; } public List<object> sources { get; set; }
} }
@ -51,7 +51,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public Sources sources { get; set; } public Sources sources { get; set; }
} }
@ -61,7 +61,7 @@ namespace OF_DL.Entities.Stories
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int duration { get; set; } public int duration { get; set; }
public int size { get; set; } public long size { get; set; }
public Sources sources { get; set; } public Sources sources { get; set; }
} }
@ -81,7 +81,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public Sources sources { get; set; } public Sources sources { get; set; }
} }
@ -90,7 +90,7 @@ namespace OF_DL.Entities.Stories
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { 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 Counters counters { get; set; }
public class Author public class Author
{ {
public int id { get; set; } public long id { get; set; }
public string _view { get; set; } public string _view { get; set; }
} }
@ -46,7 +46,7 @@ namespace OF_DL.Entities.Streams
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public List<object> sources { get; set; } public List<object> sources { get; set; }
} }
@ -55,7 +55,7 @@ namespace OF_DL.Entities.Streams
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
} }
public class Thumb public class Thumb
@ -63,7 +63,7 @@ namespace OF_DL.Entities.Streams
public string url { get; set; } public string url { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
} }
public class Info public class Info
@ -154,7 +154,7 @@ namespace OF_DL.Entities.Streams
{ {
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public string url { get; set; } public string url { get; set; }
} }
@ -163,7 +163,7 @@ namespace OF_DL.Entities.Streams
public string source { get; set; } public string source { get; set; }
public int width { get; set; } public int width { get; set; }
public int height { get; set; } public int height { get; set; }
public int size { get; set; } public long size { get; set; }
public int duration { get; set; } public int duration { get; set; }
} }

View File

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

View File

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

View File

@ -31,6 +31,8 @@ public class APIHelper : IAPIHelper
private readonly Auth auth; private readonly Auth auth;
private static DateTime? cachedDynamicRulesExpiration; private static DateTime? cachedDynamicRulesExpiration;
private static DynamicRules? cachedDynamicRules; private static DynamicRules? cachedDynamicRules;
private const int MaxAttempts = 30;
private const int DelayBetweenAttempts = 3000;
static APIHelper() 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 try
{ {
Dictionary<string, int> users = new(); Dictionary<string, long> users = new();
Subscriptions subscriptions = new(); Subscriptions subscriptions = new();
Log.Debug("Calling GetAllSubscrptions"); Log.Debug("Calling GetAllSubscrptions");
@ -357,7 +359,7 @@ public class APIHelper : IAPIHelper
return null; 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() 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() 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"); Log.Debug("Calling GetLists");
@ -402,7 +404,7 @@ public class APIHelper : IAPIHelper
{ "limit", "50" }, { "limit", "50" },
{ "format", "infinite" } { "format", "infinite" }
}; };
Dictionary<string, int> lists = new(); Dictionary<string, long> lists = new();
while (true) while (true)
{ {
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient()); string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
@ -2065,13 +2067,13 @@ public class APIHelper : IAPIHelper
return null; 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}"); Log.Debug($"Calling GetPurchasedTabUsers - {endpoint}");
try try
{ {
Dictionary<string, int> purchasedTabUsers = new(); Dictionary<string, long> purchasedTabUsers = new();
Purchased purchased = new(); Purchased purchased = new();
int post_limit = 50; int post_limit = 50;
Dictionary<string, string> getParams = new() Dictionary<string, string> getParams = new()
@ -2238,7 +2240,7 @@ public class APIHelper : IAPIHelper
return null; 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}"); Log.Debug($"Calling GetPurchasedTab - {endpoint}");
@ -2674,13 +2676,10 @@ public class APIHelper : IAPIHelper
return DateTime.Now; return DateTime.Now;
} }
public async Task<string> GetDecryptionKeyCDRMProject(Dictionary<string, string> drmHeaders, string licenceURL, string pssh) public async Task<string> GetDecryptionKeyCDRMProject(Dictionary<string, string> drmHeaders, string licenceURL, string pssh)
{ {
Log.Debug("Calling GetDecryptionKey"); Log.Debug("Calling GetDecryptionKey");
const int maxAttempts = 30;
const int delayBetweenAttempts = 3000;
int attempt = 0; int attempt = 0;
try try
@ -2701,7 +2700,7 @@ public class APIHelper : IAPIHelper
Log.Debug($"Posting to CDRM Project: {json}"); Log.Debug($"Posting to CDRM Project: {json}");
while (attempt < maxAttempts) while (attempt < MaxAttempts)
{ {
attempt++; attempt++;
@ -2727,19 +2726,19 @@ public class APIHelper : IAPIHelper
} }
else else
{ {
Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {maxAttempts}"); Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {MaxAttempts}");
if (attempt < maxAttempts) if (attempt < MaxAttempts)
{ {
await Task.Delay(delayBetweenAttempts); await Task.Delay(DelayBetweenAttempts);
} }
} }
} }
else else
{ {
Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {maxAttempts}"); Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {MaxAttempts}");
if (attempt < maxAttempts) if (attempt < MaxAttempts)
{ {
await Task.Delay(delayBetweenAttempts); await Task.Delay(DelayBetweenAttempts);
} }
} }
} }
@ -2764,11 +2763,10 @@ public class APIHelper : IAPIHelper
{ {
Log.Debug("Calling GetDecryptionOFDL"); Log.Debug("Calling GetDecryptionOFDL");
try try
{ {
string dcValue = string.Empty;
HttpClient client = new(); HttpClient client = new();
int attempt = 0;
OFDLRequest ofdlRequest = new OFDLRequest OFDLRequest ofdlRequest = new OFDLRequest
{ {
@ -2781,17 +2779,27 @@ public class APIHelper : IAPIHelper
Log.Debug($"Posting to ofdl.tools: {json}"); 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(); 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) 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); Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
} }
} }
return null; 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 try
{ {
@ -150,7 +150,7 @@ namespace OF_DL.Helpers
} }
Log.Debug("Adding missing creators"); 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)) 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 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 try
{ {
@ -495,7 +495,7 @@ namespace OF_DL.Helpers
FROM posts AS P FROM posts AS P
INNER JOIN medias AS m INNER JOIN medias AS m
ON P.post_id = m.post_id ON P.post_id = m.post_id
WHERE m.downloaded = 0 WHERE m.downloaded = 0
)", connection); )", connection);
var scalarValue = await cmd.ExecuteScalarAsync(); var scalarValue = await cmd.ExecuteScalarAsync();
if(scalarValue != null && scalarValue != DBNull.Value) if(scalarValue != null && scalarValue != DBNull.Value)

View File

@ -77,9 +77,9 @@ public class DownloadHelper : IDownloadHelper
try try
{ {
string customFileName = string.Empty; 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]); string extension = Path.GetExtension(url.Split("?")[0]);
@ -164,7 +164,7 @@ public class DownloadHelper : IDownloadHelper
object? postMedia, object? postMedia,
object? author, object? author,
string username, string username,
Dictionary<string, int> users, Dictionary<string, long> users,
IFileNameHelper fileNameHelper, IFileNameHelper fileNameHelper,
CustomFileNameOption option) CustomFileNameOption option)
{ {
@ -628,9 +628,38 @@ public class DownloadHelper : IDownloadHelper
// default: // default:
// tempFilename = $"{folder}{path}/{filename}_source.mp4"; // tempFilename = $"{folder}{path}/{filename}_source.mp4";
// break; // 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}"); Log.Debug($"Calling FFMPEG with Parameters: {parameters}");
@ -661,7 +690,7 @@ public class DownloadHelper : IDownloadHelper
#endregion #endregion
#region normal posts #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; string path;
if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null) 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); 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; string path;
if (downloadConfig.FolderPerPost && postInfo != null && postInfo?.id is not null && postInfo?.postedAt is not null) 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); 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; string path;
if (downloadConfig.FolderPerPost && streamInfo != null && streamInfo?.id is not null && streamInfo?.postedAt is not null) 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; string path;
if (downloadConfig.FolderPerMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null) 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"; string path = "/Archived/Posts/Free";
Uri uri = new(url); 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); 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; string path;
if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null) 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); 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; string path;
if (downloadConfig.FolderPerPaidMessage && messageInfo != null && messageInfo?.id is not null && messageInfo?.createdAt is not null) 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, Purchased.List? messageInfo,
Medium? messageMedia, Medium? messageMedia,
Purchased.FromUser? fromUser, Purchased.FromUser? fromUser,
Dictionary<string, int> users) Dictionary<string, long> users)
{ {
string path; string path;
if (downloadConfig.FolderPerPaidPost && messageInfo != null && messageInfo?.id is not null && messageInfo?.postedAt is not null) if (downloadConfig.FolderPerPaidPost && messageInfo != null && messageInfo?.id is not null && messageInfo?.postedAt is not null)
@ -819,19 +848,19 @@ public class DownloadHelper : IDownloadHelper
{ {
try 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)) if (!string.IsNullOrEmpty(avatarUrl))
{ {
string avatarpath = $"{path}/Avatars"; 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); List<string> avatarMD5Hashes = WidevineClient.Utils.CalculateFolderMD5(folder + avatarpath);
@ -872,9 +901,9 @@ public class DownloadHelper : IDownloadHelper
if (!string.IsNullOrEmpty(headerUrl)) if (!string.IsNullOrEmpty(headerUrl))
{ {
string headerpath = $"{path}/Headers"; 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); List<string> headerMD5Hashes = WidevineClient.Utils.CalculateFolderMD5(folder + headerpath);
@ -965,12 +994,22 @@ public class DownloadHelper : IDownloadHelper
private void OnError(object sender, ConversionErrorEventArgs e) 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); _completionSource?.TrySetResult(false);
} }
#region drm posts #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 try
{ {
@ -986,9 +1025,9 @@ public class DownloadHelper : IDownloadHelper
{ {
path = "/Messages/Free/Videos"; 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 try
{ {
@ -1094,9 +1133,9 @@ public class DownloadHelper : IDownloadHelper
{ {
path = "/Messages/Paid/Videos"; 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) if (!string.IsNullOrEmpty(filenameFormat) && messageInfo != null && messageMedia != null)
@ -1184,7 +1223,7 @@ public class DownloadHelper : IDownloadHelper
return false; 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 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 try
{ {
@ -1307,9 +1346,9 @@ public class DownloadHelper : IDownloadHelper
{ {
path = "/Posts/Free/Videos"; 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) if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)
@ -1396,7 +1435,7 @@ public class DownloadHelper : IDownloadHelper
} }
return false; 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 try
{ {
@ -1412,9 +1451,9 @@ public class DownloadHelper : IDownloadHelper
{ {
path = "/Posts/Free/Videos"; 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) 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); await m_DBHelper.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4", size, true, lastModified);
} }
} }
if (downloadConfig.ShowScrapeSize) if (downloadConfig.ShowScrapeSize)
{ {
long size = await m_DBHelper.GetStoredFileSize(folder, media_id, api_type); long size = await m_DBHelper.GetStoredFileSize(folder, media_id, api_type);
@ -1501,7 +1540,7 @@ public class DownloadHelper : IDownloadHelper
} }
return false; 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 try
{ {
@ -1607,7 +1646,7 @@ public class DownloadHelper : IDownloadHelper
return false; 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 try
{ {
@ -1623,9 +1662,9 @@ public class DownloadHelper : IDownloadHelper
{ {
path = "/Posts/Paid/Videos"; 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 try
{ {
@ -1723,9 +1762,9 @@ public class DownloadHelper : IDownloadHelper
Uri uri = new(url); Uri uri = new(url);
string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0]; string filename = System.IO.Path.GetFileName(uri.LocalPath).Split(".")[0];
string path = "/Archived/Posts/Free/Videos"; 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) if (!string.IsNullOrEmpty(filenameFormat) && postInfo != null && postMedia != null)

View File

@ -18,7 +18,7 @@ namespace OF_DL.Helpers
this.auth = auth; 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(); Dictionary<string, string> values = new();
Type type1 = obj1.GetType(); Type type1 = obj1.GetType();
@ -36,7 +36,7 @@ namespace OF_DL.Helpers
{ {
drmProperty = GetNestedPropertyValue(obj2, "files.drm"); drmProperty = GetNestedPropertyValue(obj2, "files.drm");
} }
if(fileProperty != null && drmProperty != null && propertyName == "mediaCreatedAt") if(fileProperty != null && drmProperty != null && propertyName == "mediaCreatedAt")
{ {
object mpdurl = GetNestedPropertyValue(obj2, "files.drm.manifest.dash"); object mpdurl = GetNestedPropertyValue(obj2, "files.drm.manifest.dash");
@ -66,7 +66,7 @@ namespace OF_DL.Helpers
continue; continue;
} }
} }
} }
PropertyInfo? property = Array.Find(properties2, p => p.Name.Equals(propertyName.Replace("media", ""), StringComparison.OrdinalIgnoreCase)); PropertyInfo? property = Array.Find(properties2, p => p.Name.Equals(propertyName.Replace("media", ""), StringComparison.OrdinalIgnoreCase));
if (property != null) 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<string> GetDecryptionKeyCDM(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
Task<DateTime> GetDRMMPDLastModified(string mpdUrl, string policy, string signature, string kvp); Task<DateTime> GetDRMMPDLastModified(string mpdUrl, string policy, string signature, string kvp);
Task<string> GetDRMMPDPSSH(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<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<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); 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<ArchivedCollection> GetArchived(string endpoint, string folder, IDownloadConfig config, StatusContext ctx);
Task<MessageCollection> GetMessages(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<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<Dictionary<string, long>> GetPurchasedTabUsers(string endpoint, IDownloadConfig config, Dictionary<string, long> users);
Task<List<PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder, IDownloadConfig config, Dictionary<string, int> users); Task<List<PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder, IDownloadConfig config, Dictionary<string, long> users);
Task<User> GetUserInfo(string endpoint); Task<User> GetUserInfo(string endpoint);
Task<JObject> GetUserInfoById(string endpoint); Task<JObject> GetUserInfoById(string endpoint);
Dictionary<string, string> GetDynamicHeaders(string path, string queryParam); Dictionary<string, string> GetDynamicHeaders(string path, string queryParam);
Task<Dictionary<string, int>> GetActiveSubscriptions(string endpoint, bool includeRestrictedSubscriptions, IDownloadConfig config); Task<Dictionary<string, long>> GetActiveSubscriptions(string endpoint, bool includeRestrictedSubscriptions, IDownloadConfig config);
Task<Dictionary<string, int>> GetExpiredSubscriptions(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); Task<string> GetDecryptionKeyOFDL(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
} }
} }

View File

@ -2,12 +2,12 @@ namespace OF_DL.Helpers
{ {
public interface IDBHelper 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 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 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 CreateDB(string folder);
Task CreateUsersDB(Dictionary<string, int> users); Task CreateUsersDB(Dictionary<string, long> users);
Task CheckUsername(KeyValuePair<string, int> user, string path); 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 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 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); Task<long> GetStoredFileSize(string folder, long media_id, string api_type);

View File

@ -12,25 +12,25 @@ namespace OF_DL.Helpers
public interface IDownloadHelper public interface IDownloadHelper
{ {
Task<long> CalculateTotalFileSize(List<string> urls); 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> 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, 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, 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, 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, long> users);
Task DownloadAvatarHeader(string? avatarUrl, string? headerUrl, string folder, string username); 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> 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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> 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, 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, 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> 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, 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, 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, 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, long> users);
Task<bool> DownloadStoryMedia(string url, string folder, long media_id, string api_type, ProgressTask task); 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> 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, 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, long> users);
} }
} }

View File

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

View File

@ -116,6 +116,8 @@ public class Program
.CreateLogger(); .CreateLogger();
AnsiConsole.Write(new FigletText("Welcome to OF-DL").Color(Color.Red)); 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 //Remove config.json and convert to config.conf
if (File.Exists("config.json")) if (File.Exists("config.json"))
@ -129,6 +131,10 @@ public class Program
if (jsonConfig != null) if (jsonConfig != null)
{ {
var hoconConfig = new StringBuilder(); 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 Tools");
hoconConfig.AppendLine("External {"); hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\""); hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\"");
@ -253,7 +259,7 @@ public class Program
config = new Entities.Config config = new Entities.Config
{ {
//Auth //Auth
DisableBrowserAuth = hoconConfig.GetBoolean("DisableBrowserAuth"), DisableBrowserAuth = hoconConfig.GetBoolean("Auth.DisableBrowserAuth"),
// FFmpeg Settings // FFmpeg Settings
FFmpegPath = hoconConfig.GetString("External.FFmpegPath"), FFmpegPath = hoconConfig.GetString("External.FFmpegPath"),
@ -373,7 +379,9 @@ public class Program
Entities.Config jsonConfig = new Entities.Config(); Entities.Config jsonConfig = new Entities.Config();
var hoconConfig = new StringBuilder(); var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth"); 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 Tools");
hoconConfig.AppendLine("External {"); hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\""); hoconConfig.AppendLine($" FFmpegPath = \"{jsonConfig.FFmpegPath}\"");
@ -526,36 +534,50 @@ public class Program
// Only run the version check if not in DEBUG mode // Only run the version check if not in DEBUG mode
#if !DEBUG #if !DEBUG
Version localVersion = Assembly.GetEntryAssembly()?.GetName().Version; //Only tested with numeric values. Version localVersion = Assembly.GetEntryAssembly()?.GetName().Version; //Only tested with numeric values.
String? latestReleaseTag = VersionHelper.GetLatestReleaseTag();
if (latestReleaseTag == null) // Create a cancellation token with 30 second timeout
{ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
AnsiConsole.Markup("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]"); String? latestReleaseTag = null;
Log.Error("Failed to get the latest release tag.");
}
else
{
Version latestGiteaRelease = new Version(latestReleaseTag.Replace("OFDLV", ""));
// Compare the Versions try
int versionComparison = localVersion.CompareTo(latestGiteaRelease); {
if (versionComparison < 0) latestReleaseTag = await VersionHelper.GetLatestReleaseTag(cts.Token);
{ }
// The version on GitHub is more up to date than this local release. catch (OperationCanceledException)
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[/]"); AnsiConsole.Markup("[yellow]Version check timed out after 30 seconds.\n[/]");
Log.Debug("Detected outdated client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}"); Log.Warning("Version check timed out after 30 seconds");
Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}"); latestReleaseTag = null;
} }
else
{ if (latestReleaseTag == null)
// 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("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]");
AnsiConsole.Markup("[green]Latest Release version: " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}\n[/]"); Log.Error("Failed to get the latest release tag.");
Log.Debug("Detected client running version " + $"{localVersion.Major}.{localVersion.Minor}.{localVersion.Build}"); }
Log.Debug("Latest release version " + $"{latestGiteaRelease.Major}.{latestGiteaRelease.Minor}.{latestGiteaRelease.Build}"); 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 #else
AnsiConsole.Markup("[yellow]Running in Debug/Local mode. Version check skipped.\n[/]"); AnsiConsole.Markup("[yellow]Running in Debug/Local mode. Version check skipped.\n[/]");
@ -748,6 +770,61 @@ public class Program
{ {
config.FFmpegPath = config.FFmpegPath.Replace(@"\", @"\\"); 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 else
{ {
@ -850,12 +927,12 @@ public class Program
do do
{ {
DateTime startTime = DateTime.Now; DateTime startTime = DateTime.Now;
Dictionary<string, int> users = new(); Dictionary<string, long> users = new();
Dictionary<string, int> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config); Dictionary<string, long> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
Log.Debug("Subscriptions: "); Log.Debug("Subscriptions: ");
foreach (KeyValuePair<string, int> activeSub in activeSubs) foreach (KeyValuePair<string, long> activeSub in activeSubs)
{ {
if (!users.ContainsKey(activeSub.Key)) if (!users.ContainsKey(activeSub.Key))
{ {
@ -867,8 +944,8 @@ public class Program
{ {
Log.Debug("Inactive Subscriptions: "); Log.Debug("Inactive Subscriptions: ");
Dictionary<string, int> expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config); Dictionary<string, long> expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
foreach (KeyValuePair<string, int> expiredSub in expiredSubs) foreach (KeyValuePair<string, long> expiredSub in expiredSubs)
{ {
if (!users.ContainsKey(expiredSub.Key)) 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 // Remove users from the list if they are in the ignored list
if (!string.IsNullOrEmpty(Config.IgnoredUsersListName)) if (!string.IsNullOrEmpty(Config.IgnoredUsersListName))
@ -896,28 +973,28 @@ public class Program
} }
await dBHelper.CreateUsersDB(users); await dBHelper.CreateUsersDB(users);
KeyValuePair<bool, Dictionary<string, int>> hasSelectedUsersKVP; KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP;
if(Config.NonInteractiveMode && Config.NonInteractiveModePurchasedTab) 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)) 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)) else if (Config.NonInteractiveMode && !string.IsNullOrEmpty(Config.NonInteractiveModeListName))
{ {
var listId = lists[Config.NonInteractiveModeListName]; var listId = lists[Config.NonInteractiveModeListName];
var listUsernames = await m_ApiHelper.GetListUsers($"/lists/{listId}/users", Config) ?? []; 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); 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 else
{ {
var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists); var userSelectionResult = await HandleUserSelection(m_ApiHelper, Config, users, lists);
Config = userSelectionResult.updatedConfig; 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")) 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")) 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[/]"); 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 = ""; string path = "";
if (!string.IsNullOrEmpty(Config.DownloadPath)) if (!string.IsNullOrEmpty(Config.DownloadPath))
@ -1143,7 +1220,7 @@ public class Program
else if (hasSelectedUsersKVP.Key && !hasSelectedUsersKVP.Value.ContainsKey("ConfigChanged")) else if (hasSelectedUsersKVP.Key && !hasSelectedUsersKVP.Value.ContainsKey("ConfigChanged"))
{ {
//Iterate over each user in the list of users //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 paidPostCount = 0;
int postCount = 0; int postCount = 0;
@ -1301,7 +1378,7 @@ public class Program
return combinedConfig; 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}"); Log.Debug($"Calling DownloadPaidMessages - {user.Key}");
@ -1434,7 +1511,7 @@ public class Program
return paidMessagesCount; 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}"); Log.Debug($"Calling DownloadMessages - {user.Key}");
@ -1567,7 +1644,7 @@ public class Program
return messagesCount; 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}"); Log.Debug($"Calling DownloadHighlights - {user.Key}");
@ -1624,7 +1701,7 @@ public class Program
return highlightsCount; 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}"); Log.Debug($"Calling DownloadStories - {user.Key}");
@ -1681,7 +1758,7 @@ public class Program
return storiesCount; 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}"); Log.Debug($"Calling DownloadArchived - {user.Key}");
@ -1814,7 +1891,7 @@ public class Program
return archivedCount; 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}"); Log.Debug($"Calling DownloadFreePosts - {user.Key}");
@ -1954,7 +2031,7 @@ public class Program
return postCount; 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}"); Log.Debug($"Calling DownloadPaidPosts - {user.Key}");
@ -2087,7 +2164,7 @@ public class Program
return paidPostCount; 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 oldPaidPostCount = 0;
int newPaidPostCount = 0; int newPaidPostCount = 0;
@ -2211,7 +2288,7 @@ public class Program
return paidPostCount; 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 oldPaidMessagesCount = 0;
int newPaidMessagesCount = 0; int newPaidMessagesCount = 0;
@ -2336,7 +2413,7 @@ public class Program
return paidMessagesCount; 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}"); Log.Debug($"Calling DownloadStreams - {user.Key}");
@ -2475,7 +2552,7 @@ public class Program
return streamsCount; 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}"); Log.Debug($"Calling DownloadPaidMessage - {username}");
@ -2605,7 +2682,7 @@ public class Program
return paidMessagesCount; 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()}"); 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; bool hasSelectedUsers = false;
Dictionary<string, int> selectedUsers = new Dictionary<string, int>(); Dictionary<string, long> selectedUsers = new Dictionary<string, long>();
while (!hasSelectedUsers) while (!hasSelectedUsers)
{ {
@ -2767,7 +2844,7 @@ public class Program
List<string> listUsernames = new(); List<string> listUsernames = new();
foreach (var item in listSelection) 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); List<string> usernames = await apiHelper.GetListUsers($"/lists/{listId}/users", config);
foreach (string user in usernames) foreach (string user in usernames)
{ {
@ -2807,11 +2884,11 @@ public class Program
} }
break; break;
case "[red]Download Single Post[/]": 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[/]": 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[/]": 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[/]": case "[red]Edit config.conf[/]":
while (true) while (true)
{ {
@ -2879,7 +2956,9 @@ public class Program
var hoconConfig = new StringBuilder(); var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth"); 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 Tools");
hoconConfig.AppendLine("External {"); hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\""); hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\"");
@ -2981,7 +3060,7 @@ public class Program
currentConfig = newConfig; currentConfig = newConfig;
if (configChanged) if (configChanged)
{ {
return (true, new Dictionary<string, int> { { "ConfigChanged", 0 } }, currentConfig); return (true, new Dictionary<string, long> { { "ConfigChanged", 0 } }, currentConfig);
} }
break; break;
} }
@ -3039,7 +3118,9 @@ public class Program
var hoconConfig = new StringBuilder(); var hoconConfig = new StringBuilder();
hoconConfig.AppendLine("# Auth"); 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 Tools");
hoconConfig.AppendLine("External {"); hoconConfig.AppendLine("External {");
hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\""); hoconConfig.AppendLine($" FFmpegPath = \"{newConfig.FFmpegPath}\"");
@ -3135,7 +3216,7 @@ public class Program
if (configChanged) if (configChanged)
{ {
return (true, new Dictionary<string, int> { { "ConfigChanged", 0 } }, currentConfig); return (true, new Dictionary<string, long> { { "ConfigChanged", 0 } }, currentConfig);
} }
break; break;
@ -3161,7 +3242,7 @@ public class Program
return (true, selectedUsers, currentConfig); // Return true to indicate selected users 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) 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 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. 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) - [DownloadDateSelection](/config/all-configuration-options#downloaddateselection)
- [CustomDate](/config/all-configuration-options#customdate) - [CustomDate](/config/all-configuration-options#customdate)
- [ShowScrapeSize](/config/all-configuration-options#showscrapesize) - [ShowScrapeSize](/config/all-configuration-options#showscrapesize)
- [DisableTextSanitization](/config/all-configuration-options#disabletextsanitization)
- [DownloadVideoResolution](/config/all-configuration-options#downloadvideoresolution)
- Media - Media
- [DownloadAvatarHeaderPhoto](/config/all-configuration-options#downloadavatarheaderphoto) - [DownloadAvatarHeaderPhoto](/config/all-configuration-options#downloadavatarheaderphoto)
- [DownloadPaidPosts](/config/all-configuration-options#downloadpaidposts) - [DownloadPaidPosts](/config/all-configuration-options#downloadpaidposts)