v3.2.3.0
- Minor refactoring - Querying cooldown is now written to and read from ProgramData so it can be persistent and hopefully steam's API limit is reached a little less often - Increased steam game info querying cooldown to 600 seconds (10 minutes) and steam DLC info querying cooldown to 1200 seconds (20 minutes) - Introduced a 600 second (10 minute) cooldown for Epic game queries using the same system above
This commit is contained in:
parent
dc2bc0e9e1
commit
62f9ba8ee9
8 changed files with 91 additions and 50 deletions
|
@ -5,7 +5,7 @@
|
||||||
<UseWindowsForms>True</UseWindowsForms>
|
<UseWindowsForms>True</UseWindowsForms>
|
||||||
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
||||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||||
<Version>3.2.2.5</Version>
|
<Version>3.2.3.0</Version>
|
||||||
<PackageIcon>Resources\ini.ico</PackageIcon>
|
<PackageIcon>Resources\ini.ico</PackageIcon>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
|
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
|
||||||
|
|
|
@ -27,21 +27,18 @@ internal static class EpicLibrary
|
||||||
if (!Directory.Exists(EpicAppDataPath)) return games;
|
if (!Directory.Exists(EpicAppDataPath)) return games;
|
||||||
string manifests = EpicAppDataPath + @"\Manifests";
|
string manifests = EpicAppDataPath + @"\Manifests";
|
||||||
if (!Directory.Exists(manifests)) return games;
|
if (!Directory.Exists(manifests)) return games;
|
||||||
string[] files = Directory.GetFiles(manifests);
|
string[] files = Directory.GetFiles(manifests, "*.item");
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return games;
|
if (Program.Canceled) return games;
|
||||||
if (Path.GetExtension(file) == ".item")
|
string json = File.ReadAllText(file);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
string json = File.ReadAllText(file);
|
Manifest manifest = JsonSerializer.Deserialize<Manifest>(json);
|
||||||
try
|
if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId)
|
||||||
{
|
games.Add(manifest);
|
||||||
Manifest manifest = JsonSerializer.Deserialize<Manifest>(json);
|
|
||||||
if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId)
|
|
||||||
games.Add(manifest);
|
|
||||||
}
|
|
||||||
catch { };
|
|
||||||
}
|
}
|
||||||
|
catch { };
|
||||||
}
|
}
|
||||||
return games;
|
return games;
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,36 +16,42 @@ namespace CreamInstaller.Epic;
|
||||||
|
|
||||||
internal static class EpicStore
|
internal static class EpicStore
|
||||||
{
|
{
|
||||||
|
//private const int COOLDOWN_CATALOG_ITEM = 600;
|
||||||
|
|
||||||
/* need a method to query catalog items
|
/* need a method to query catalog items
|
||||||
internal static async Task QueryCatalogItems(Manifest manifest)
|
internal static async Task QueryCatalogItems(Manifest manifest)
|
||||||
{
|
{
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
private const int COOLDOWN_ENTITLEMENT = 600;
|
||||||
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace)
|
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace)
|
||||||
{
|
{
|
||||||
List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
|
List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
|
||||||
Response response = await QueryGraphQL(categoryNamespace);
|
|
||||||
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
|
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
|
||||||
|
bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile);
|
||||||
|
Response response = null;
|
||||||
|
if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, COOLDOWN_ENTITLEMENT))
|
||||||
|
{
|
||||||
|
response = await QueryGraphQL(categoryNamespace);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
else if (cachedExists)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = JsonConvert.DeserializeObject<Response>(File.ReadAllText(cacheFile));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
File.Delete(cacheFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (response is null)
|
if (response is null)
|
||||||
if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile))
|
return dlcIds;
|
||||||
try
|
|
||||||
{
|
|
||||||
response = JsonConvert.DeserializeObject<Response>(File.ReadAllText(cacheFile));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
File.Delete(cacheFile);
|
|
||||||
}
|
|
||||||
if (response is null) return dlcIds;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
|
|
||||||
}
|
|
||||||
catch //(Exception e)
|
|
||||||
{
|
|
||||||
//using DialogForm dialogForm = new(null);
|
|
||||||
//dialogForm.Show(SystemIcons.Error, "Unsuccessful serialization of query for category namespace " + categoryNamespace + ":\n\n" + e.ToString(), "FUCK");
|
|
||||||
}
|
|
||||||
List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements);
|
List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements);
|
||||||
foreach (Element element in searchStore)
|
foreach (Element element in searchStore)
|
||||||
{
|
{
|
||||||
|
|
|
@ -144,7 +144,7 @@ internal partial class SelectForm : CustomForm
|
||||||
RemoveFromRemainingGames(name);
|
RemoveFromRemainingGames(name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AppData appData = await SteamStore.QueryStoreAPI(appId, 60);
|
AppData appData = await SteamStore.QueryStoreAPI(appId);
|
||||||
VProperty appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId);
|
VProperty appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId);
|
||||||
if (appData is null && appInfo is null)
|
if (appData is null && appInfo is null)
|
||||||
{
|
{
|
||||||
|
@ -169,7 +169,7 @@ internal partial class SelectForm : CustomForm
|
||||||
Thread.Sleep(0);
|
Thread.Sleep(0);
|
||||||
string dlcName = null;
|
string dlcName = null;
|
||||||
string dlcIcon = null;
|
string dlcIcon = null;
|
||||||
AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId);
|
AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId, true);
|
||||||
if (dlcAppData is not null)
|
if (dlcAppData is not null)
|
||||||
{
|
{
|
||||||
dlcName = dlcAppData.name;
|
dlcName = dlcAppData.name;
|
||||||
|
|
|
@ -112,12 +112,12 @@
|
||||||
<value>2.0</value>
|
<value>2.0</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="reader">
|
<resheader name="reader">
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -68,11 +68,11 @@ internal static class SteamLibrary
|
||||||
{
|
{
|
||||||
List<Tuple<string, string, string, int, string>> games = new();
|
List<Tuple<string, string, string, int, string>> games = new();
|
||||||
if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null;
|
if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null;
|
||||||
string[] files = Directory.GetFiles(libraryDirectory);
|
string[] files = Directory.GetFiles(libraryDirectory, "*.acf");
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return null;
|
if (Program.Canceled) return null;
|
||||||
if (Path.GetExtension(file) == ".acf" && ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result))
|
if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result))
|
||||||
{
|
{
|
||||||
string appId = result.Value.GetChild("appid")?.ToString();
|
string appId = result.Value.GetChild("appid")?.ToString();
|
||||||
string installdir = result.Value.GetChild("installdir")?.ToString();
|
string installdir = result.Value.GetChild("installdir")?.ToString();
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using CreamInstaller.Utility;
|
using CreamInstaller.Utility;
|
||||||
|
@ -23,23 +20,22 @@ internal static class SteamStore
|
||||||
return dlcIds;
|
return dlcIds;
|
||||||
});
|
});
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, DateTime> lastQueries = new();
|
private const int COOLDOWN_GAME = 600;
|
||||||
internal static async Task<AppData> QueryStoreAPI(string appId, int cooldown = 10)
|
private const int COOLDOWN_DLC = 1200;
|
||||||
|
|
||||||
|
internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false)
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return null;
|
if (Program.Canceled) return null;
|
||||||
Thread.Sleep(0);
|
|
||||||
string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
|
string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
|
||||||
DateTime now = DateTime.UtcNow;
|
bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile);
|
||||||
if (!lastQueries.TryGetValue(appId, out DateTime lastQuery) || (now - lastQuery).TotalSeconds > cooldown)
|
if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? COOLDOWN_DLC : COOLDOWN_GAME))
|
||||||
{
|
{
|
||||||
lastQueries[appId] = now;
|
|
||||||
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
|
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
|
||||||
if (response is not null)
|
if (response is not null)
|
||||||
{
|
{
|
||||||
IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response);
|
IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response);
|
||||||
foreach (KeyValuePair<string, JToken> app in apps)
|
foreach (KeyValuePair<string, JToken> app in apps)
|
||||||
{
|
{
|
||||||
Thread.Sleep(0);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AppData data = JsonConvert.DeserializeObject<AppDetails>(app.Value.ToString()).data;
|
AppData data = JsonConvert.DeserializeObject<AppDetails>(app.Value.ToString()).data;
|
||||||
|
@ -65,7 +61,7 @@ internal static class SteamStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile))
|
if (cachedExists)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,8 @@ internal static class ProgramData
|
||||||
|
|
||||||
internal static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0");
|
internal static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0");
|
||||||
|
|
||||||
|
internal static readonly string CooldownPath = DirectoryPath + @"\cooldown";
|
||||||
|
|
||||||
internal static async Task Setup() => await Task.Run(() =>
|
internal static async Task Setup() => await Task.Run(() =>
|
||||||
{
|
{
|
||||||
if (Directory.Exists(DirectoryPathOld))
|
if (Directory.Exists(DirectoryPathOld))
|
||||||
|
@ -30,5 +32,45 @@ internal static class ProgramData
|
||||||
Directory.CreateDirectory(AppInfoPath);
|
Directory.CreateDirectory(AppInfoPath);
|
||||||
File.WriteAllText(AppInfoVersionPath, Application.ProductVersion, Encoding.UTF8);
|
File.WriteAllText(AppInfoVersionPath, Application.ProductVersion, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
|
if (!Directory.Exists(CooldownPath))
|
||||||
|
Directory.CreateDirectory(CooldownPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
internal static bool CheckCooldown(string identifier, int cooldown)
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
DateTime lastCheck = GetCooldown(identifier) ?? now;
|
||||||
|
bool cooldownOver = (now - lastCheck).TotalSeconds > cooldown;
|
||||||
|
if (cooldownOver || now == lastCheck)
|
||||||
|
SetCooldown(identifier, now);
|
||||||
|
return cooldownOver;
|
||||||
|
}
|
||||||
|
private static DateTime? GetCooldown(string identifier)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(CooldownPath))
|
||||||
|
{
|
||||||
|
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
|
||||||
|
if (File.Exists(cooldownFile))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown))
|
||||||
|
return cooldown;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static void SetCooldown(string identifier, DateTime time)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(CooldownPath))
|
||||||
|
Directory.CreateDirectory(CooldownPath);
|
||||||
|
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(cooldownFile, time.ToString());
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue