Refactor services

This commit is contained in:
whimsical-c4lic0 2026-02-10 12:01:33 -06:00
parent 70738fd4ae
commit 04004c7084
12 changed files with 709 additions and 704 deletions

View File

@ -2,8 +2,11 @@ namespace OF_DL.Helpers;
public static class Constants
{
public const string API_URL = "https://onlyfans.com/api2/v2";
public const string ApiUrl = "https://onlyfans.com/api2/v2";
public const int WIDEVINE_RETRY_DELAY = 10;
public const int WIDEVINE_MAX_RETRIES = 3;
public const int ApiPageSize = 50;
public const int WidevineRetryDelay = 10;
public const int WidevineMaxRetries = 3;
}

View File

@ -6,15 +6,15 @@ namespace OF_DL.Helpers;
public static class VersionHelper
{
private const string url = "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest";
private static readonly HttpClient httpClient = new();
private const string Url = "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest";
private static readonly HttpClient s_httpClient = new();
public static async Task<string?> GetLatestReleaseTag(CancellationToken cancellationToken = default)
{
Log.Debug("Calling GetLatestReleaseTag");
try
{
HttpResponseMessage response = await httpClient.GetAsync(url, cancellationToken);
HttpResponseMessage response = await s_httpClient.GetAsync(Url, cancellationToken);
if (!response.IsSuccessStatusCode)
{
@ -22,21 +22,20 @@ public static class VersionHelper
return null;
}
string body = await response.Content.ReadAsStringAsync();
string body = await response.Content.ReadAsStringAsync(cancellationToken);
Log.Debug("GetLatestReleaseTag API Response: ");
Log.Debug(body);
Log.Debug("GetLatestReleaseTag API Response: {Body}", body);
LatestReleaseApiResponse? versionCheckResponse =
JsonConvert.DeserializeObject<LatestReleaseApiResponse>(body);
if (versionCheckResponse == null || versionCheckResponse.TagName == "")
if (versionCheckResponse != null && versionCheckResponse.TagName != "")
{
Log.Debug("GetLatestReleaseTag did not return a valid tag name");
return null;
return versionCheckResponse.TagName;
}
return versionCheckResponse.TagName;
Log.Debug("GetLatestReleaseTag did not return a valid tag name");
return null;
}
catch (OperationCanceledException)
{

View File

@ -74,7 +74,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
else
{
//Get rules from GitHub and fallback to local file
// Get rules from GitHub and fallback to a local file
string? dynamicRulesJson = GetDynamicRules();
if (!string.IsNullOrEmpty(dynamicRulesJson))
{
@ -90,7 +90,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
Log.Debug("Using dynamic rules from local file");
root = JsonConvert.DeserializeObject<DynamicRules>(File.ReadAllText("rules.json"));
// Cache the dynamic rules from local file to prevent unnecessary disk
// Cache the dynamic rules from a local file to prevent unnecessary disk
// operations and frequent call to GitHub. Since the GitHub dynamic rules
// are preferred to the local file, the cache time is shorter than when dynamic rules
// are successfully retrieved from GitHub.
@ -160,10 +160,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
UserEntities.User user = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() }, { "order", "publish_date_asc" }
{ "limit", Constants.ApiPageSize.ToString() }, { "order", "publish_date_asc" }
};
HttpClient client = new();
@ -386,20 +385,17 @@ public class ApiService(IAuthService authService, IConfigService configService,
/// <param name="endpoint">The endpoint to query.</param>
/// <param name="username">Optional username context.</param>
/// <param name="folder">The creator folder path.</param>
/// <param name="paidPostIds">Paid post media IDs.</param>
/// <returns>A mediaId-to-URL map.</returns>
public async Task<Dictionary<long, string>?> GetMedia(MediaType mediatype,
string endpoint,
string? username,
string folder,
List<long> paidPostIds)
string folder)
{
Log.Debug($"Calling GetMedia - {username}");
try
{
Dictionary<long, string> returnUrls = new();
const int postLimit = 50;
const int limit = 5;
int offset = 0;
@ -410,7 +406,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
case MediaType.Stories:
getParams = new Dictionary<string, string>
{
{ "limit", postLimit.ToString() }, { "order", "publish_date_desc" }, { "skip_users", "all" }
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "skip_users", "all" }
};
break;
@ -478,9 +476,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
continue;
}
if (medium.CanView && !returnUrls.ContainsKey(medium.Id))
if (medium.CanView)
{
returnUrls.Add(medium.Id, mediaUrl);
returnUrls.TryAdd(medium.Id, mediaUrl);
}
}
}
@ -555,7 +553,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
foreach (HighlightEntities.Story item in highlightMedia.Stories)
{
DateTime? createdAt = item.Media != null && item.Media.Count > 0
DateTime? createdAt = item.Media is { Count: > 0 }
? item.Media[0].CreatedAt
: null;
@ -572,8 +570,11 @@ public class ApiService(IAuthService authService, IConfigService configService,
await dbService.AddStory(folder, item.Id, "", "0", false, false, DateTime.Now);
}
if (item.Media != null && item.Media.Count > 0 && item.Media[0].CanView)
if (item.Media is not { Count: > 0 } || !item.Media[0].CanView)
{
continue;
}
string? storyUrl = item.Media[0].Files?.Full?.Url;
string storyUrlValue = storyUrl ?? string.Empty;
foreach (HighlightEntities.Medium medium in item.Media)
@ -594,7 +595,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return returnUrls;
}
@ -625,10 +625,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
PurchasedEntities.PaidPostCollection paidPostCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "skip_users", "all" },
{ "order", "publish_date_desc" },
{ "format", "infinite" },
@ -657,25 +656,29 @@ public class ApiService(IAuthService authService, IConfigService configService,
break;
}
getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + postLimit);
getParams["offset"] =
Convert.ToString(Convert.ToInt32(getParams["offset"]) + Constants.ApiPageSize);
}
}
List<PurchasedEntities.ListItem> paidPostList = paidPosts.List;
foreach (PurchasedEntities.ListItem purchase in paidPostList)
{
if (purchase.ResponseType == "post" && purchase.Media != null && purchase.Media.Count > 0)
if (purchase.ResponseType != "post" || purchase.Media is not { Count: > 0 })
{
List<long> previewids = new();
continue;
}
List<long> previewIds = [];
if (purchase.Previews != null)
{
for (int i = 0; i < purchase.Previews.Count; i++)
{
if (purchase.Previews[i] is long previewId)
{
if (!previewids.Contains(previewId))
if (!previewIds.Contains(previewId))
{
previewids.Add(previewId);
previewIds.Add(previewId);
}
}
}
@ -686,9 +689,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
{
if (purchase.Preview[i] is long previewId)
{
if (!previewids.Contains(previewId))
if (!previewIds.Contains(previewId))
{
previewids.Add(previewId);
previewIds.Add(previewId);
}
}
}
@ -698,11 +701,11 @@ public class ApiService(IAuthService authService, IConfigService configService,
bool isArchived = purchase.IsArchived ?? false;
await dbService.AddPost(folder, purchase.Id, purchase.Text ?? "",
purchase.Price ?? "0",
purchase.Price != null && purchase.IsOpened, isArchived, createdAt);
purchase is { Price: not null, IsOpened: true }, isArchived, createdAt);
paidPostCollection.PaidPostObjects.Add(purchase);
foreach (MessageEntities.Medium medium in purchase.Media)
{
if (!previewids.Contains(medium.Id))
if (!previewIds.Contains(medium.Id))
{
paidPostIds.Add(medium.Id);
}
@ -714,11 +717,11 @@ public class ApiService(IAuthService authService, IConfigService configService,
string mediaType = ResolveMediaType(medium.Type) ?? "";
string? fullUrl = medium.Files?.Full?.Url;
bool isPreview = previewids.Contains(medium.Id);
bool isPreview = previewIds.Contains(medium.Id);
if (previewids.Count > 0)
if (previewIds.Count > 0)
{
bool has = previewids.Any(cus => cus.Equals(medium.Id));
bool has = previewIds.Any(cus => cus.Equals(medium.Id));
if (!has && medium.CanView && !string.IsNullOrEmpty(fullUrl))
{
if (!paidPostCollection.PaidPosts.ContainsKey(medium.Id))
@ -775,7 +778,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return paidPostCollection;
}
@ -804,10 +806,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
PostEntities.PostCollection postCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "format", "infinite" },
{ "skip_users", "all" }
@ -816,8 +817,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
DownloadDateSelection downloadDateSelection = DownloadDateSelection.before;
DateTime? downloadAsOf = null;
if (configService.CurrentConfig.DownloadOnlySpecificDates &&
configService.CurrentConfig.CustomDate.HasValue)
if (configService.CurrentConfig is { DownloadOnlySpecificDates: true, CustomDate: not null })
{
downloadDateSelection = configService.CurrentConfig.DownloadDateSelection;
downloadAsOf = configService.CurrentConfig.CustomDate;
@ -890,27 +890,34 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
List<long> postPreviewIds = new();
if (post.Preview != null && post.Preview.Count > 0)
List<long> postPreviewIds = [];
if (post.Preview is { Count: > 0 })
{
for (int i = 0; i < post.Preview.Count; i++)
{
if (post.Preview[i] is long previewId)
if (post.Preview[i] is not long previewId)
{
continue;
}
if (!postPreviewIds.Contains(previewId))
{
postPreviewIds.Add(previewId);
}
}
}
}
await dbService.AddPost(folder, post.Id, !string.IsNullOrEmpty(post.RawText) ? post.RawText : "",
post.Price ?? "0", post.Price != null && post.IsOpened,
post.Price ?? "0", post is { Price: not null, IsOpened: true },
post.IsArchived, post.PostedAt);
postCollection.PostObjects.Add(post);
if (post.Media != null && post.Media.Count > 0)
if (post.Media is not { Count: > 0 })
{
continue;
}
foreach (PostEntities.Medium medium in post.Media)
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -963,7 +970,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return postCollection;
}
@ -997,29 +1003,35 @@ public class ApiService(IAuthService authService, IConfigService configService,
if (singlePostDto != null)
{
List<long> postPreviewIds = new();
if (singlePost.Preview != null && singlePost.Preview.Count > 0)
List<long> postPreviewIds = [];
if (singlePost.Preview is { Count: > 0 })
{
for (int i = 0; i < singlePost.Preview.Count; i++)
{
if (singlePost.Preview[i] is long previewId)
if (singlePost.Preview[i] is not long previewId)
{
continue;
}
if (!postPreviewIds.Contains(previewId))
{
postPreviewIds.Add(previewId);
}
}
}
}
await dbService.AddPost(folder, singlePost.Id,
!string.IsNullOrEmpty(singlePost.Text) ? singlePost.Text : "",
singlePost.Price ?? "0",
singlePost.Price != null && singlePost.IsOpened, singlePost.IsArchived,
singlePost is { Price: not null, IsOpened: true }, singlePost.IsArchived,
singlePost.PostedAt);
singlePostCollection.SinglePostObjects.Add(singlePost);
if (singlePost.Media != null && singlePost.Media.Count > 0)
if (singlePost.Media == null || singlePost.Media.Count <= 0)
{
return singlePostCollection;
}
foreach (PostEntities.Medium medium in singlePost.Media)
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -1104,7 +1116,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return singlePostCollection;
}
@ -1133,18 +1144,16 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
StreamEntities.StreamsCollection streamsCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "format", "infinite" },
{ "skip_users", "all" }
};
DownloadDateSelection downloadDateSelection = DownloadDateSelection.before;
if (configService.CurrentConfig.DownloadOnlySpecificDates &&
configService.CurrentConfig.CustomDate.HasValue)
if (configService.CurrentConfig is { DownloadOnlySpecificDates: true, CustomDate: not null })
{
downloadDateSelection = configService.CurrentConfig.DownloadDateSelection;
}
@ -1190,27 +1199,34 @@ public class ApiService(IAuthService authService, IConfigService configService,
List<StreamEntities.ListItem> streamList = streams.List;
foreach (StreamEntities.ListItem stream in streamList)
{
List<long> streamPreviewIds = new();
if (stream.Preview != null && stream.Preview.Count > 0)
List<long> streamPreviewIds = [];
if (stream.Preview is { Count: > 0 })
{
for (int i = 0; i < stream.Preview.Count; i++)
{
if (stream.Preview[i] is long previewId)
if (stream.Preview[i] is not long previewId)
{
continue;
}
if (!streamPreviewIds.Contains(previewId))
{
streamPreviewIds.Add(previewId);
}
}
}
}
await dbService.AddPost(folder, stream.Id, !string.IsNullOrEmpty(stream.Text) ? stream.Text : "",
stream.Price ?? "0", stream.Price != null && stream.IsOpened,
stream.Price ?? "0", stream is { Price: not null, IsOpened: true },
stream.IsArchived, stream.PostedAt);
streamsCollection.StreamObjects.Add(stream);
if (stream.Media != null && stream.Media.Count > 0)
if (stream.Media is not { Count: > 0 })
{
continue;
}
foreach (StreamEntities.Medium medium in stream.Media)
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -1252,7 +1268,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return streamsCollection;
}
@ -1280,10 +1295,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
ArchivedEntities.ArchivedCollection archivedCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "skip_users", "all" },
{ "format", "infinite" },
@ -1292,8 +1306,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
};
DownloadDateSelection downloadDateSelection = DownloadDateSelection.before;
if (configService.CurrentConfig.DownloadOnlySpecificDates &&
configService.CurrentConfig.CustomDate.HasValue)
if (configService.CurrentConfig is { DownloadOnlySpecificDates: true, CustomDate: not null })
{
downloadDateSelection = configService.CurrentConfig.DownloadDateSelection;
}
@ -1364,10 +1377,15 @@ public class ApiService(IAuthService authService, IConfigService configService,
await dbService.AddPost(folder, archive.Id, archive.Text ?? "",
archive.Price ?? "0",
archive.Price != null && archive.IsOpened, archive.IsArchived, archive.PostedAt);
archive is { Price: not null, IsOpened: true }, archive.IsArchived, archive.PostedAt);
archivedCollection.ArchivedPostObjects.Add(archive);
if (archive.Media != null && archive.Media.Count > 0)
if (archive.Media is not { Count: > 0 })
{
continue;
}
foreach (ArchivedEntities.Medium medium in archive.Media)
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -1404,7 +1422,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return archivedCollection;
}
@ -1432,10 +1449,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
MessageEntities.MessageCollection messageCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() }, { "order", "desc" }, { "skip_users", "all" }
{ "limit", Constants.ApiPageSize.ToString() }, { "order", "desc" }, { "skip_users", "all" }
};
int currentUserId = GetCurrentUserIdOrDefault();
@ -1476,33 +1492,41 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
List<long> messagePreviewIds = new();
if (list.Previews != null && list.Previews.Count > 0)
List<long> messagePreviewIds = [];
if (list.Previews is { Count: > 0 })
{
for (int i = 0; i < list.Previews.Count; i++)
{
if (list.Previews[i] is long previewId)
if (list.Previews[i] is not long previewId)
{
continue;
}
if (!messagePreviewIds.Contains(previewId))
{
messagePreviewIds.Add(previewId);
}
}
}
if (configService.CurrentConfig.IgnoreOwnMessages && list.FromUser?.Id == currentUserId)
{
continue;
}
if (!configService.CurrentConfig.IgnoreOwnMessages || list.FromUser?.Id != currentUserId)
{
DateTime createdAt = list.CreatedAt ?? DateTime.Now;
await dbService.AddMessage(folder, list.Id, list.Text ?? "", list.Price ?? "0",
list.CanPurchaseReason == "opened" ||
(list.CanPurchaseReason == "opened" && ((bool?)null ?? false)), false,
createdAt,
list.FromUser?.Id ?? int.MinValue);
messageCollection.MessageObjects.Add(list);
if (list.CanPurchaseReason != "opened" && list.Media != null && list.Media.Count > 0)
if (list.CanPurchaseReason != "opened" && list.Media is { Count: > 0 })
{
foreach (MessageEntities.Medium medium in list.Media ?? new List<MessageEntities.Medium>())
foreach (MessageEntities.Medium medium in list.Media ?? [])
{
string mediaType = ResolveMediaType(medium.Type) ?? string.Empty;
string? fullUrl = medium.Files?.Full?.Url;
@ -1589,7 +1613,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
return messageCollection;
}
@ -1614,8 +1637,8 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
PurchasedEntities.SinglePaidMessageCollection singlePaidMessageCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new() { { "limit", postLimit.ToString() }, { "order", "desc" } };
Dictionary<string, string> getParams =
new() { { "limit", Constants.ApiPageSize.ToString() }, { "order", "desc" } };
int currentUserId = GetCurrentUserIdOrDefault();
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
@ -1623,31 +1646,41 @@ public class ApiService(IAuthService authService, IConfigService configService,
DeserializeJson<MessageDtos.SingleMessageDto>(body, s_mJsonSerializerSettings);
MessageEntities.SingleMessage message = MessagesMapper.FromDto(messageDto);
if (!configService.CurrentConfig.IgnoreOwnMessages || message.FromUser?.Id != currentUserId)
if (configService.CurrentConfig.IgnoreOwnMessages && message.FromUser?.Id == currentUserId)
{
return singlePaidMessageCollection;
}
DateTime createdAt = message.CreatedAt ?? DateTime.Now;
await dbService.AddMessage(folder, message.Id, message.Text ?? "",
message.Price?.ToString() ?? "0", true, false,
createdAt,
message.FromUser?.Id ?? int.MinValue);
singlePaidMessageCollection.SingleMessageObjects.Add(message);
List<long> messagePreviewIds = new();
if (message.Previews != null && message.Previews.Count > 0)
List<long> messagePreviewIds = [];
if (message.Previews is { Count: > 0 })
{
for (int i = 0; i < message.Previews.Count; i++)
{
if (message.Previews[i] is long previewId)
if (message.Previews[i] is not long previewId)
{
continue;
}
if (!messagePreviewIds.Contains(previewId))
{
messagePreviewIds.Add(previewId);
}
}
}
if (message.Media is not { Count: > 0 })
{
return singlePaidMessageCollection;
}
if (message.Media != null && message.Media.Count > 0)
{
foreach (MessageEntities.Medium medium in message.Media)
{
string mediaType = ResolveMediaType(medium.Type) ?? string.Empty;
@ -1661,14 +1694,15 @@ public class ApiService(IAuthService authService, IConfigService configService,
continue;
}
if (!singlePaidMessageCollection.SingleMessages.ContainsKey(medium.Id))
if (!singlePaidMessageCollection.SingleMessages.TryAdd(medium.Id, fullUrl))
{
continue;
}
await dbService.AddMedia(folder, medium.Id, message.Id, fullUrl, null, null, null,
"Messages", mediaType, isPreview, false, null);
singlePaidMessageCollection.SingleMessages.Add(medium.Id, fullUrl);
singlePaidMessageCollection.SingleMessageMedia.Add(medium);
}
}
else if (isPreview && medium.CanView && !string.IsNullOrEmpty(fullUrl))
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -1722,8 +1756,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
}
return singlePaidMessageCollection;
}
@ -1753,10 +1785,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
PurchasedEntities.PaidMessageCollection paidMessageCollection = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "format", "infinite" },
{ "author", username },
@ -1780,7 +1811,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
HttpClient loopclient = GetHttpClient();
HttpRequestMessage looprequest =
new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{loopqueryParams}");
new(HttpMethod.Get, $"{Constants.ApiUrl}{endpoint}{loopqueryParams}");
foreach (KeyValuePair<string, string> keyValuePair in loopheaders)
{
@ -1803,7 +1834,8 @@ public class ApiService(IAuthService authService, IConfigService configService,
break;
}
getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + postLimit);
getParams["offset"] =
Convert.ToString(Convert.ToInt32(getParams["offset"]) + Constants.ApiPageSize);
}
}
@ -1816,8 +1848,11 @@ public class ApiService(IAuthService authService, IConfigService configService,
.OrderByDescending(p => p.PostedAt ?? p.CreatedAt))
{
long fromUserId = purchase.FromUser?.Id ?? long.MinValue;
if (!configService.CurrentConfig.IgnoreOwnMessages || fromUserId != currentUserId)
if (configService.CurrentConfig.IgnoreOwnMessages && fromUserId == currentUserId)
{
continue;
}
DateTime createdAt = purchase.PostedAt ?? purchase.CreatedAt ?? DateTime.Now;
await dbService.AddMessage(folder, purchase.Id,
purchase.Text ?? "",
@ -1825,19 +1860,24 @@ public class ApiService(IAuthService authService, IConfigService configService,
fromUserId);
paidMessageCollection.PaidMessageObjects.Add(purchase);
if (purchase.Media != null && purchase.Media.Count > 0)
if (purchase.Media is not { Count: > 0 })
{
List<long> previewids = new();
continue;
}
List<long> previewIds = [];
if (purchase.Previews != null)
{
for (int i = 0; i < purchase.Previews.Count; i++)
{
if (purchase.Previews[i] is long previewId)
if (purchase.Previews[i] is not long previewId)
{
if (!previewids.Contains(previewId))
{
previewids.Add(previewId);
continue;
}
if (!previewIds.Contains(previewId))
{
previewIds.Add(previewId);
}
}
}
@ -1847,9 +1887,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
{
if (purchase.Preview[i] is long previewId)
{
if (!previewids.Contains(previewId))
if (!previewIds.Contains(previewId))
{
previewids.Add(previewId);
previewIds.Add(previewId);
}
}
}
@ -1859,11 +1899,11 @@ public class ApiService(IAuthService authService, IConfigService configService,
{
string mediaType = ResolveMediaType(medium.Type) ?? string.Empty;
string? fullUrl = medium.Files?.Full?.Url;
bool isPreview = previewids.Contains(medium.Id);
bool isPreview = previewIds.Contains(medium.Id);
if (previewids.Count > 0)
if (previewIds.Count > 0)
{
bool has = previewids.Any(cus => cus.Equals(medium.Id));
bool has = previewIds.Any(cus => cus.Equals(medium.Id));
if (!has && medium.CanView && !string.IsNullOrEmpty(fullUrl))
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -1943,8 +1983,6 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
}
}
}
}
return paidMessageCollection;
}
@ -1969,10 +2007,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
try
{
Dictionary<string, long> purchasedTabUsers = new();
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "format", "infinite" },
{ "skip_users", "all" }
@ -1998,7 +2035,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
HttpClient loopclient = GetHttpClient();
HttpRequestMessage looprequest =
new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{loopqueryParams}");
new(HttpMethod.Get, $"{Constants.ApiUrl}{endpoint}{loopqueryParams}");
foreach (KeyValuePair<string, string> keyValuePair in loopheaders)
{
@ -2020,7 +2057,8 @@ public class ApiService(IAuthService authService, IConfigService configService,
break;
}
getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + postLimit);
getParams["offset"] =
Convert.ToString(Convert.ToInt32(getParams["offset"]) + Constants.ApiPageSize);
}
}
@ -2039,10 +2077,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
string? matchedUsername = users.FirstOrDefault(x => x.Value == fromUserId).Key;
if (!string.IsNullOrEmpty(matchedUsername))
{
if (!purchasedTabUsers.ContainsKey(matchedUsername))
{
purchasedTabUsers.Add(matchedUsername, fromUserId);
}
purchasedTabUsers.TryAdd(matchedUsername, fromUserId);
}
else if (!purchasedTabUsers.ContainsKey($"Deleted User - {fromUserId}"))
{
@ -2064,9 +2099,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
Log.Debug("Content creator not longer exists - {0}", fromUserId);
}
else if (!purchasedTabUsers.ContainsKey(fetchedUsername))
else
{
purchasedTabUsers.Add(fetchedUsername, fromUserId);
purchasedTabUsers.TryAdd(fetchedUsername, fromUserId);
}
}
}
@ -2139,10 +2174,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
{
Dictionary<long, List<PurchasedEntities.ListItem>> userPurchases = new();
List<PurchasedEntities.PurchasedTabCollection> purchasedTabCollections = [];
const int postLimit = 50;
Dictionary<string, string> getParams = new()
{
{ "limit", postLimit.ToString() },
{ "limit", Constants.ApiPageSize.ToString() },
{ "order", "publish_date_desc" },
{ "format", "infinite" },
{ "skip_users", "all" }
@ -2163,7 +2197,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
HttpClient loopclient = GetHttpClient();
HttpRequestMessage looprequest =
new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{loopqueryParams}");
new(HttpMethod.Get, $"{Constants.ApiUrl}{endpoint}{loopqueryParams}");
foreach (KeyValuePair<string, string> keyValuePair in loopheaders)
{
@ -2185,7 +2219,8 @@ public class ApiService(IAuthService authService, IConfigService configService,
break;
}
getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + postLimit);
getParams["offset"] =
Convert.ToString(Convert.ToInt32(getParams["offset"]) + Constants.ApiPageSize);
}
}
@ -2274,10 +2309,12 @@ public class ApiService(IAuthService authService, IConfigService configService,
await dbService.AddPost(path, purchase.Id,
purchase.Text ?? "",
purchase.Price ?? "0",
purchase.Price != null && purchase.IsOpened,
purchase is { Price: not null, IsOpened: true },
isArchived,
createdAt);
purchasedTabCollection.PaidPosts.PaidPostObjects.Add(purchase);
foreach (MessageEntities.Medium medium in purchase.Media)
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -2356,18 +2393,18 @@ public class ApiService(IAuthService authService, IConfigService configService,
messageCreatedAt, fromUserId);
purchasedTabCollection.PaidMessages.PaidMessageObjects.Add(purchase);
if (purchase.Media != null && purchase.Media.Count > 0)
if (purchase.Media is { Count: > 0 })
{
List<long> paidMessagePreviewids = new();
List<long> paidMessagePreviewIds = [];
if (purchase.Previews != null)
{
for (int i = 0; i < purchase.Previews.Count; i++)
{
if (purchase.Previews[i] is long previewId)
{
if (!paidMessagePreviewids.Contains(previewId))
if (!paidMessagePreviewIds.Contains(previewId))
{
paidMessagePreviewids.Add(previewId);
paidMessagePreviewIds.Add(previewId);
}
}
}
@ -2378,9 +2415,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
{
if (purchase.Preview[i] is long previewId)
{
if (!paidMessagePreviewids.Contains(previewId))
if (!paidMessagePreviewIds.Contains(previewId))
{
paidMessagePreviewids.Add(previewId);
paidMessagePreviewIds.Add(previewId);
}
}
}
@ -2388,12 +2425,12 @@ public class ApiService(IAuthService authService, IConfigService configService,
foreach (MessageEntities.Medium medium in purchase.Media)
{
if (paidMessagePreviewids.Count > 0)
if (paidMessagePreviewIds.Count > 0)
{
string mediaType = ResolveMediaType(medium.Type) ?? string.Empty;
string? fullUrl = medium.Files?.Full?.Url;
bool isPreview = paidMessagePreviewids.Contains(medium.Id);
bool has = paidMessagePreviewids.Any(cus => cus.Equals(medium.Id));
bool isPreview = paidMessagePreviewIds.Contains(medium.Id);
bool has = paidMessagePreviewIds.Any(cus => cus.Equals(medium.Id));
if (!has && medium.CanView && !string.IsNullOrEmpty(fullUrl))
{
if (!IsMediaTypeDownloadEnabled(medium.Type))
@ -2440,7 +2477,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
{
string mediaType = ResolveMediaType(medium.Type) ?? string.Empty;
string? fullUrl = medium.Files?.Full?.Url;
bool isPreview = paidMessagePreviewids.Contains(medium.Id);
bool isPreview = paidMessagePreviewIds.Contains(medium.Id);
if (medium.CanView && !string.IsNullOrEmpty(fullUrl))
{
@ -2729,9 +2766,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
Dictionary<string, string> headers = GetDynamicHeaders($"/api2/v2{endpoint}", queryParams);
HttpRequestMessage request = new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{queryParams}");
HttpRequestMessage request = new(HttpMethod.Get, $"{Constants.ApiUrl}{endpoint}{queryParams}");
Log.Debug($"Full request URL: {Constants.API_URL}{endpoint}{queryParams}");
Log.Debug($"Full request URL: {Constants.ApiUrl}{endpoint}{queryParams}");
foreach (KeyValuePair<string, string> keyValuePair in headers)
{
@ -2756,7 +2793,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
private HttpClient GetHttpClient()
{
HttpClient client = new();
if (configService.CurrentConfig.Timeout != null && configService.CurrentConfig.Timeout > 0)
if (configService.CurrentConfig.Timeout is > 0)
{
client.Timeout = TimeSpan.FromSeconds(configService.CurrentConfig.Timeout.Value);
}

View File

@ -572,12 +572,14 @@ public class DbService(IConfigService configService) : IDbService
while (await reader.ReadAsync())
{
if (reader["name"].ToString() == "record_created_at")
if (reader["name"].ToString() != "record_created_at")
{
continue;
}
columnExists = true;
break;
}
}
if (!columnExists)
{

View File

@ -18,10 +18,10 @@ public class DownloadOrchestrationService(
/// <summary>
/// Gets the list of paid post media IDs to avoid duplicates.
/// </summary>
public List<long> PaidPostIds { get; } = new();
public List<long> PaidPostIds { get; } = [];
/// <summary>
/// Retrieves the available users and lists based on current configuration.
/// Retrieves the available users and lists based on the current configuration.
/// </summary>
/// <returns>A result containing users, lists, and any errors.</returns>
public async Task<UserListResult> GetAvailableUsersAsync()
@ -235,9 +235,9 @@ public class DownloadOrchestrationService(
{
eventHandler.OnMessage("Getting Stories");
Dictionary<long, string>? tempStories = await apiService.GetMedia(MediaType.Stories,
$"/users/{userId}/stories", null, path, PaidPostIds);
$"/users/{userId}/stories", null, path);
if (tempStories != null && tempStories.Count > 0)
if (tempStories is { Count: > 0 })
{
eventHandler.OnContentFound("Stories", tempStories.Count, tempStories.Count);
@ -263,9 +263,9 @@ public class DownloadOrchestrationService(
{
eventHandler.OnMessage("Getting Highlights");
Dictionary<long, string>? tempHighlights = await apiService.GetMedia(MediaType.Highlights,
$"/users/{userId}/stories/highlights", null, path, PaidPostIds);
$"/users/{userId}/stories/highlights", null, path);
if (tempHighlights != null && tempHighlights.Count > 0)
if (tempHighlights is { Count: > 0 })
{
eventHandler.OnContentFound("Highlights", tempHighlights.Count, tempHighlights.Count);

View File

@ -500,15 +500,9 @@ public class DownloadService(
client.DefaultRequestHeaders.Add("User-Agent", auth.UserAgent);
using HttpResponseMessage response = await client.GetAsync(mpdUrl, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
if (response.Content.Headers.LastModified != null)
{
return response.Content.Headers.LastModified.Value.DateTime;
}
}
return DateTime.Now;
return response is { IsSuccessStatusCode: true, Content.Headers.LastModified: not null }
? response.Content.Headers.LastModified.Value.DateTime
: DateTime.Now;
}
/// <summary>
@ -842,7 +836,7 @@ public class DownloadService(
/// <param name="author">Author info.</param>
/// <param name="users">Known users map.</param>
/// <returns>True when the media is newly downloaded.</returns>
public async Task<bool> DownloadMedia(string url, string folder, long mediaId, string apiType,
private async Task<bool> DownloadMedia(string url, string folder, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users)
@ -876,7 +870,7 @@ public class DownloadService(
/// <param name="author">Author info.</param>
/// <param name="users">Known users map.</param>
/// <returns>True when the media is newly downloaded.</returns>
public async Task<bool> DownloadDrmVideo(string policy, string signature, string kvp, string url,
private async Task<bool> DownloadDrmVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
@ -1028,7 +1022,7 @@ public class DownloadService(
Log.Debug($"Calling DownloadHighlights - {username}");
Dictionary<long, string>? highlights = await apiService.GetMedia(MediaType.Highlights,
$"/users/{userId}/stories/highlights", null, path, paidPostIds.ToList());
$"/users/{userId}/stories/highlights", null, path);
if (highlights == null || highlights.Count == 0)
{
@ -1091,7 +1085,7 @@ public class DownloadService(
Log.Debug($"Calling DownloadStories - {username}");
Dictionary<long, string>? stories = await apiService.GetMedia(MediaType.Stories, $"/users/{userId}/stories",
null, path, paidPostIds.ToList());
null, path);
if (stories == null || stories.Count == 0)
{

View File

@ -39,8 +39,7 @@ public interface IApiService
/// <summary>
/// Retrieves media URLs for stories or highlights.
/// </summary>
Task<Dictionary<long, string>?> GetMedia(MediaType mediaType, string endpoint, string? username, string folder,
List<long> paidPostIds);
Task<Dictionary<long, string>?> GetMedia(MediaType mediaType, string endpoint, string? username, string folder);
/// <summary>
/// Retrieves paid posts and their media.
@ -125,7 +124,7 @@ public interface IApiService
Task<Dictionary<string, long>?> GetExpiredSubscriptions(string endpoint, bool includeRestrictedSubscriptions);
/// <summary>
/// Retrieves a decryption key via the OFDL fallback service.
/// Retrieves a decryption key via the OF DL fallback service.
/// </summary>
Task<string> GetDecryptionKeyOfdl(Dictionary<string, string> drmHeaders, string licenceUrl, string pssh);
}

View File

@ -11,7 +11,7 @@ public interface IAuthService
Auth? CurrentAuth { get; set; }
/// <summary>
/// Loads authentication data from disk.
/// Loads authentication data from the disk.
/// </summary>
Task<bool> LoadFromFileAsync(string filePath = "auth.json");

View File

@ -20,23 +20,6 @@ public interface IDownloadService
Task<bool> ProcessMediaDownload(string folder, long mediaId, string apiType, string url, string path,
string serverFileName, string resolvedFileName, string extension, IProgressReporter progressReporter);
/// <summary>
/// Downloads a single media item.
/// </summary>
Task<bool> DownloadMedia(string url, string folder, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users);
/// <summary>
/// Downloads a DRM-protected video.
/// </summary>
Task<bool> DownloadDrmVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users);
/// <summary>
/// Retrieves decryption information for a DRM media item.
/// </summary>

View File

@ -11,10 +11,4 @@ public interface IProgressReporter
/// </summary>
/// <param name="increment">The amount to increment progress by</param>
void ReportProgress(long increment);
/// <summary>
/// Reports a status message (optional for implementations).
/// </summary>
/// <param name="message">The status message to report</param>
void ReportStatus(string message);
}

View File

@ -111,13 +111,13 @@ internal class HttpUtil
int retryCount = 0;
while (retryCount < Constants.WIDEVINE_MAX_RETRIES && response.StatusCode == HttpStatusCode.TooManyRequests)
while (retryCount < Constants.WidevineMaxRetries && response.StatusCode == HttpStatusCode.TooManyRequests)
{
//
// We've hit a rate limit, so we should wait before retrying.
//
int retryAfterSeconds =
Constants.WIDEVINE_RETRY_DELAY * (retryCount + 1); // Default retry time. Increases with each retry.
Constants.WidevineRetryDelay * (retryCount + 1); // Default retry time. Increases with each retry.
if (response.Headers.RetryAfter != null && response.Headers.RetryAfter.Delta.HasValue)
{
if (response.Headers.RetryAfter.Delta.Value.TotalSeconds > 0)

View File

@ -11,10 +11,4 @@ public class SpectreProgressReporter(ProgressTask task) : IProgressReporter
private readonly ProgressTask _task = task ?? throw new ArgumentNullException(nameof(task));
public void ReportProgress(long increment) => _task.Increment(increment);
public void ReportStatus(string message)
{
// Optionally update task description or handle status messages
// For now, we'll leave this empty as the task description is set when creating the progress bar
}
}