Compare commits

...

5 Commits

Author SHA1 Message Date
5b285acb37 WIP 2025-05-21 18:11:22 +02:00
fd14259aa0 Added console output before getting subscriptions. 2025-05-21 18:00:08 +02:00
99437048ce Updated subscription lookup logic to match the website.
It increments the offset with the limit before making the next request, until 'hasMore' is false. Also tweaked loop code, to be a bit more clean.
2025-05-21 17:56:35 +02:00
599d94176f Added retry in BuildHeaderAndExecuteRequests if TooManyRequests 2025-05-21 17:19:56 +02:00
c0ac56b95b Added "x of y" count to "Scraping Data For" console outputs. 2025-05-21 17:12:57 +02:00
2 changed files with 84 additions and 56 deletions

View File

@ -13,6 +13,7 @@ using OF_DL.Enumerations;
using OF_DL.Enumurations; using OF_DL.Enumurations;
using Serilog; using Serilog;
using Spectre.Console; using Spectre.Console;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@ -116,12 +117,21 @@ public class APIHelper : IAPIHelper
} }
private async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint, HttpClient client) private async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint, HttpClient client, int attemptNumber = 1)
{ {
Log.Debug("Calling BuildHeaderAndExecuteRequests"); const int MAX_ATTEMPTS = 10;
const int DELAY_BEFORE_RETRY = 1000;
Log.Debug("Calling BuildHeaderAndExecuteRequests -- Attempt number: {AttemptNumber}", attemptNumber);
try
{
HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint); HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint);
using var response = await client.SendAsync(request); using var response = await client.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
Debugger.Break();
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync(); string body = await response.Content.ReadAsStringAsync();
@ -129,6 +139,17 @@ public class APIHelper : IAPIHelper
return body; return body;
} }
catch (HttpRequestException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests && attemptNumber <= MAX_ATTEMPTS)
{
await Task.Delay(DELAY_BEFORE_RETRY);
return await BuildHeaderAndExecuteRequests(getParams, endpoint, client, ++attemptNumber);
}
throw;
}
}
private async Task<HttpRequestMessage> BuildHttpRequestMessage(Dictionary<string, string> getParams, string endpoint) private async Task<HttpRequestMessage> BuildHttpRequestMessage(Dictionary<string, string> getParams, string endpoint)
@ -298,47 +319,44 @@ public class APIHelper : IAPIHelper
try try
{ {
Dictionary<string, int> users = new(); Dictionary<string, int> users = new();
Subscriptions subscriptions = new();
int limit = 25;
int offset = 0;
getParams["limit"] = limit.ToString();
getParams["offset"] = offset.ToString();
Log.Debug("Calling GetAllSubscrptions"); Log.Debug("Calling GetAllSubscrptions");
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
subscriptions = JsonConvert.DeserializeObject<Subscriptions>(body);
if (subscriptions != null && subscriptions.hasMore)
{
getParams["offset"] = subscriptions.list.Count.ToString();
while (true) while (true)
{ {
Subscriptions newSubscriptions = new(); string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
if (!string.IsNullOrEmpty(loopbody) && (!loopbody.Contains("[]") || loopbody.Trim() != "[]")) if (string.IsNullOrWhiteSpace(body))
{
newSubscriptions = JsonConvert.DeserializeObject<Subscriptions>(loopbody, m_JsonSerializerSettings);
}
else
{
break; break;
}
subscriptions.list.AddRange(newSubscriptions.list); Subscriptions? subscriptions = JsonConvert.DeserializeObject<Subscriptions>(body, m_JsonSerializerSettings);
if (!newSubscriptions.hasMore)
{ if (subscriptions?.list is null)
break; break;
}
getParams["offset"] = subscriptions.list.Count.ToString(); foreach (Subscriptions.List item in subscriptions.list)
} {
if (users.ContainsKey(item.username))
continue;
bool isRestricted = item.isRestricted ?? false;
bool isRestrictedButAllowed = isRestricted && includeRestricted;
if (!isRestricted || isRestrictedButAllowed)
users.Add(item.username, item.id);
} }
foreach (Subscriptions.List subscription in subscriptions.list) if (!subscriptions.hasMore)
{ break;
if ((!(subscription.isRestricted ?? false) || ((subscription.isRestricted ?? false) && includeRestricted))
&& !users.ContainsKey(subscription.username)) offset += limit;
{ getParams["offset"] = offset.ToString();
users.Add(subscription.username, subscription.id);
}
} }
return users; return users;
@ -361,8 +379,6 @@ public class APIHelper : IAPIHelper
{ {
Dictionary<string, string> getParams = new() Dictionary<string, string> getParams = new()
{ {
{ "offset", "0" },
{ "limit", "50" },
{ "type", "active" }, { "type", "active" },
{ "format", "infinite"} { "format", "infinite"}
}; };
@ -376,8 +392,6 @@ public class APIHelper : IAPIHelper
Dictionary<string, string> getParams = new() Dictionary<string, string> getParams = new()
{ {
{ "offset", "0" },
{ "limit", "50" },
{ "type", "expired" }, { "type", "expired" },
{ "format", "infinite"} { "format", "infinite"}
}; };
@ -460,12 +474,16 @@ public class APIHelper : IAPIHelper
try try
{ {
int limit = 50;
int offset = 0; int offset = 0;
Dictionary<string, string> getParams = new() Dictionary<string, string> getParams = new()
{ {
{ "limit", limit.ToString() },
{ "offset", offset.ToString() }, { "offset", offset.ToString() },
{ "limit", "50" } { "format", "infinite"}
}; };
List<string> users = new(); List<string> users = new();
while (true) while (true)
@ -476,6 +494,8 @@ public class APIHelper : IAPIHelper
break; break;
} }
UserList? userList = JsonConvert.DeserializeObject<UserList>(body, m_JsonSerializerSettings);
List<UsersList>? usersList = JsonConvert.DeserializeObject<List<UsersList>>(body); List<UsersList>? usersList = JsonConvert.DeserializeObject<List<UsersList>>(body);
if (usersList == null || usersList.Count <= 0) if (usersList == null || usersList.Count <= 0)
@ -497,6 +517,7 @@ public class APIHelper : IAPIHelper
getParams["offset"] = Convert.ToString(offset); getParams["offset"] = Convert.ToString(offset);
} }
return users; return users;
} }
catch (Exception ex) catch (Exception ex)

View File

@ -844,6 +844,8 @@ public class Program
{ {
DateTime startTime = DateTime.Now; DateTime startTime = DateTime.Now;
Dictionary<string, int> users = new(); Dictionary<string, int> users = new();
AnsiConsole.Markup($"[green]Getting Active Subscriptions (Include Restricted: {Config.IncludeRestrictedSubscriptions})\n[/]");
Dictionary<string, int> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config); Dictionary<string, int> activeSubs = await m_ApiHelper.GetActiveSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
Log.Debug("Subscriptions: "); Log.Debug("Subscriptions: ");
@ -860,6 +862,7 @@ public class Program
{ {
Log.Debug("Inactive Subscriptions: "); Log.Debug("Inactive Subscriptions: ");
AnsiConsole.Markup($"[green]Getting Expired Subscriptions (Include Restricted: {Config.IncludeRestrictedSubscriptions})\n[/]");
Dictionary<string, int> expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config); Dictionary<string, int> expiredSubs = await m_ApiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", Config.IncludeRestrictedSubscriptions, Config);
foreach (KeyValuePair<string, int> expiredSub in expiredSubs) foreach (KeyValuePair<string, int> expiredSub in expiredSubs)
{ {
@ -1024,9 +1027,11 @@ public class Program
Log.Debug($"Download path: {p}"); Log.Debug($"Download path: {p}");
List<PurchasedTabCollection> purchasedTabCollections = await m_ApiHelper.GetPurchasedTab("/posts/paid", p, Config, users); List<PurchasedTabCollection> purchasedTabCollections = await m_ApiHelper.GetPurchasedTab("/posts/paid", p, Config, users);
int userNum = 1;
int userCount = purchasedTabCollections.Count;
foreach (PurchasedTabCollection purchasedTabCollection in purchasedTabCollections) foreach (PurchasedTabCollection purchasedTabCollection in purchasedTabCollections)
{ {
AnsiConsole.Markup($"[red]\nScraping Data for {purchasedTabCollection.Username}\n[/]"); AnsiConsole.Markup($"[red]\nScraping Data for {purchasedTabCollection.Username} ({userNum++} of {userCount})\n[/]");
string path = ""; string path = "";
if (!string.IsNullOrEmpty(Config.DownloadPath)) if (!string.IsNullOrEmpty(Config.DownloadPath))
{ {
@ -1136,6 +1141,8 @@ 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
int userNum = 1;
int userCount = hasSelectedUsersKVP.Value.Count;
foreach (KeyValuePair<string, int> user in hasSelectedUsersKVP.Value) foreach (KeyValuePair<string, int> user in hasSelectedUsersKVP.Value)
{ {
int paidPostCount = 0; int paidPostCount = 0;
@ -1146,7 +1153,7 @@ public class Program
int highlightsCount = 0; int highlightsCount = 0;
int messagesCount = 0; int messagesCount = 0;
int paidMessagesCount = 0; int paidMessagesCount = 0;
AnsiConsole.Markup($"[red]\nScraping Data for {user.Key}\n[/]"); AnsiConsole.Markup($"[red]\nScraping Data for {user.Key} ({userNum++} of {userCount})\n[/]");
Log.Debug($"Scraping Data for {user.Key}"); Log.Debug($"Scraping Data for {user.Key}");