Compare commits

...

20 Commits

Author SHA1 Message Date
3829a1cf26 Merge branch 'refs/heads/cuttingedge' into cuttingedge-merge-candidate 2024-09-27 15:03:51 +02:00
c3daa0b751 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-27 15:03:44 +02:00
3a072beea3 Update Readme:
* Fix dotnet Version
* Link directly to new issue for new Connectors
* Add Ntfy as Notification Connector
* Remove Roadmap
2024-09-27 15:03:06 +02:00
8e6f2798a9 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge-merge-candidate 2024-09-27 14:58:07 +02:00
9cbde9a6b4 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-27 14:57:57 +02:00
0870aa9fdb Merge branch 'refs/heads/master' into cuttingedge-merge-ServerV2 2024-09-27 14:57:36 +02:00
52ff2e54a8 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-27 14:51:11 +02:00
61d80a93cf Fix #255 MangaKatana sanitization. 2024-09-27 14:50:57 +02:00
7be3ee52e9 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-23 15:40:53 +02:00
981eb0fd9f Fix notification batching:
Do not resend old notifications.
2024-09-23 15:40:43 +02:00
47f3044a6d Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-22 00:15:59 +02:00
6d03cc5f8d Fix incorrect setting check for notificationsbuffer 2024-09-22 00:15:50 +02:00
290c405f52 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-22 00:09:54 +02:00
fcdbd32872 Include amount of notifications of type in title 2024-09-22 00:09:45 +02:00
eb6c37cc53 Output settings.json on startup 2024-09-22 00:05:09 +02:00
d922842186 Add NotificationBuffer, so Notification are not spammed on every chapter. 2024-09-22 00:02:43 +02:00
69323d6d60 Add LibraryBuffer, so Libraries are not spammed with scans on every download. 2024-09-21 21:02:55 +02:00
46a0fb8c48 Merge branch 'refs/heads/cuttingedge-merge-ServerV2' into cuttingedge 2024-09-21 20:34:57 +02:00
ec8eb40941 Allow Versions to lose their volume number, if site no longer lists it. 2024-09-21 20:30:55 +02:00
d2074fae35 Readable CheckChapterIsDownloaded check 2024-09-21 20:23:21 +02:00
14 changed files with 168 additions and 59 deletions

View File

@ -51,11 +51,12 @@ Tranga can download Chapters and Metadata from "Scanlation" sites such as
- [Bato.to](https://bato.to/v3x) (en) - [Bato.to](https://bato.to/v3x) (en)
- [Manga4Life](https://manga4life.com) (en) - [Manga4Life](https://manga4life.com) (en)
- [ManhuaPlus](https://manhuaplus.org/) (en) - [ManhuaPlus](https://manhuaplus.org/) (en)
- [MangaHere](https://www.mangahere.cc/) (en) (Their covers suck) - [MangaHere](https://www.mangahere.cc/) (en) (Their covers aren't scrapeable.)
- ❓ Open an [issue](https://github.com/C9Glax/tranga/issues) - ❓ Open an [issue](https://github.com/C9Glax/tranga/issues/new?assignees=&labels=New+Connector&projects=&template=new_connector.yml&title=%5BNew+Connector%5D%3A+)
and trigger a library-scan with [Komga](https://komga.org/) and [Kavita](https://www.kavitareader.com/). and trigger a library-scan with [Komga](https://komga.org/) and [Kavita](https://www.kavitareader.com/).
Notifications can be sent to your devices using [Gotify](https://gotify.net/) and [LunaSea](https://www.lunasea.app/). Notifications can be sent to your devices using [Gotify](https://gotify.net/), [LunaSea](https://www.lunasea.app/) or [Ntfy](https://ntfy.sh/
).
### What this does and doesn't do ### What this does and doesn't do
@ -107,14 +108,9 @@ access the folder.
### Prerequisites ### Prerequisites
#### To Build #### To Build
[.NET-Core 7.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) [.NET-Core 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
#### To Run #### To Run
[.NET-Core 7.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) scroll down a bit, should be on the right the second item. [.NET-Core 8.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) scroll down a bit, should be on the right the second item.
<!-- ROADMAP -->
## Roadmap
- [ ] ❓
See the [open issues](https://github.com/C9Glax/tranga/issues) for a full list of proposed features (and known issues). See the [open issues](https://github.com/C9Glax/tranga/issues) for a full list of proposed features (and known issues).

View File

@ -84,24 +84,26 @@ public readonly struct Chapter : IComparable
/// <returns>true if chapter is present</returns> /// <returns>true if chapter is present</returns>
internal bool CheckChapterIsDownloaded() internal bool CheckChapterIsDownloaded()
{ {
if (!Directory.Exists(Path.Join(TrangaSettings.downloadLocation, parentManga.folderName))) string mangaDirectory = Path.Join(TrangaSettings.downloadLocation, parentManga.folderName);
if (!Directory.Exists(mangaDirectory))
return false; return false;
FileInfo[] archives = new DirectoryInfo(Path.Join(TrangaSettings.downloadLocation, parentManga.folderName)).GetFiles().Where(file => file.Name.Split('.')[^1] == "cbz").ToArray(); FileInfo[] archives = new DirectoryInfo(mangaDirectory).GetFiles("*.cbz");
Regex volChRex = new(@"(?:Vol(?:ume)?\.([0-9]+)\D*)?Ch(?:apter)?\.([0-9]+(?:\.[0-9]+)*)"); Regex volChRex = new(@"(?:Vol(?:ume)?\.([0-9]+)\D*)?Ch(?:apter)?\.([0-9]+(?:\.[0-9]+)*)");
Chapter t = this; Chapter t = this;
string thisPath = GetArchiveFilePath(); string correctPath = GetArchiveFilePath();
FileInfo? archive = archives.FirstOrDefault(archive => FileInfo? archive = archives.FirstOrDefault(archive =>
{ {
Match m = volChRex.Match(archive.Name); Match m = volChRex.Match(archive.Name);
string archiveVolNum = m.Groups[1].Success ? m.Groups[1].Value : "0"; /*Uncommenting this section will only allow *Version without Volume number* -> *Version with Volume number* but not the other way
string archiveChNum = m.Groups[2].Value; if (m.Groups[1].Success)
return archiveVolNum == t.volumeNumber && archiveChNum == t.chapterNumber || return m.Groups[1].Value == t.volumeNumber && m.Groups[2].Value == t.chapterNumber;
archiveVolNum == "0" && archiveChNum == t.chapterNumber; else*/
return m.Groups[2].Value == t.chapterNumber;
}); });
if(archive is not null && thisPath != archive.FullName) if(archive is not null && archive.FullName != correctPath)
archive.MoveTo(thisPath, true); archive.MoveTo(correctPath, true);
return archive is not null; return (archive is not null);
} }
/// <summary> /// <summary>
/// Creates full file path of chapter-archive /// Creates full file path of chapter-archive

View File

@ -66,10 +66,10 @@ public abstract class GlobalBase
Log(string.Format(fStr, replace)); Log(string.Format(fStr, replace));
} }
protected void SendNotifications(string title, string text) protected void SendNotifications(string title, string text, bool buffer = false)
{ {
foreach (NotificationConnector nc in notificationConnectors) foreach (NotificationConnector nc in notificationConnectors)
nc.SendNotification(title, text); nc.SendNotification(title, text, buffer);
} }
protected void AddNotificationConnector(NotificationConnector notificationConnector) protected void AddNotificationConnector(NotificationConnector notificationConnector)

View File

@ -37,7 +37,7 @@ public class DownloadChapter : Job
if (success == HttpStatusCode.OK) if (success == HttpStatusCode.OK)
{ {
UpdateLibraries(); UpdateLibraries();
SendNotifications("Chapter downloaded", $"{chapter.parentManga.sortName} - {chapter.chapterNumber}"); SendNotifications("Chapter downloaded", $"{chapter.parentManga.sortName} - {chapter.chapterNumber}", true);
} }
}); });
downloadTask.Start(); downloadTask.Start();

View File

@ -61,7 +61,7 @@ public class Kavita : LibraryConnector
return ""; return "";
} }
public override void UpdateLibrary() protected override void UpdateLibraryInternal()
{ {
Log("Updating libraries."); Log("Updating libraries.");
foreach (KavitaLibrary lib in GetLibraries()) foreach (KavitaLibrary lib in GetLibraries())

View File

@ -25,7 +25,7 @@ public class Komga : LibraryConnector
return $"Komga {baseUrl}"; return $"Komga {baseUrl}";
} }
public override void UpdateLibrary() protected override void UpdateLibraryInternal()
{ {
Log("Updating libraries."); Log("Updating libraries.");
foreach (KomgaLibrary lib in GetLibraries()) foreach (KomgaLibrary lib in GetLibraries())

View File

@ -17,6 +17,9 @@ public abstract class LibraryConnector : GlobalBase
public string baseUrl { get; } public string baseUrl { get; }
// ReSharper disable once MemberCanBeProtected.Global // ReSharper disable once MemberCanBeProtected.Global
public string auth { get; } //Base64 encoded, if you use your password everywhere, you have problems public string auth { get; } //Base64 encoded, if you use your password everywhere, you have problems
private DateTime? _updateLibraryRequested = null;
private readonly Thread? _libraryBufferThread = null;
private const int NoChangeTimeout = 2, BiggestInterval = 20;
protected LibraryConnector(GlobalBase clone, string baseUrl, string auth, LibraryType libraryType) : base(clone) protected LibraryConnector(GlobalBase clone, string baseUrl, string auth, LibraryType libraryType) : base(clone)
{ {
@ -28,8 +31,47 @@ public abstract class LibraryConnector : GlobalBase
this.baseUrl = baseUrlRex.Match(baseUrl).Value; this.baseUrl = baseUrlRex.Match(baseUrl).Value;
this.auth = auth; this.auth = auth;
this.libraryType = libraryType; this.libraryType = libraryType;
if (TrangaSettings.bufferLibraryUpdates)
{
_libraryBufferThread = new(CheckLibraryBuffer);
_libraryBufferThread.Start();
}
} }
public abstract void UpdateLibrary();
private void CheckLibraryBuffer()
{
while (true)
{
if (_updateLibraryRequested is not null && DateTime.Now.Subtract((DateTime)_updateLibraryRequested) > TimeSpan.FromMinutes(NoChangeTimeout)) //If no updates have been requested for NoChangeTimeout minutes, update library
{
UpdateLibraryInternal();
_updateLibraryRequested = null;
}
Thread.Sleep(100);
}
}
public void UpdateLibrary()
{
_updateLibraryRequested ??= DateTime.Now;
if (!TrangaSettings.bufferLibraryUpdates)
{
UpdateLibraryInternal();
return;
}else if (_updateLibraryRequested is not null &&
DateTime.Now.Subtract((DateTime)_updateLibraryRequested) > TimeSpan.FromMinutes(BiggestInterval)) //If the last update has been more than BiggestInterval minutes ago, update library
{
UpdateLibraryInternal();
_updateLibraryRequested = null;
}
else if(_updateLibraryRequested is not null)
{
Log($"Buffering Library Updates (Updates in latest {((DateTime)_updateLibraryRequested).Add(TimeSpan.FromMinutes(BiggestInterval)).Subtract(DateTime.Now)} or {((DateTime)_updateLibraryRequested).Add(TimeSpan.FromMinutes(NoChangeTimeout)).Subtract(DateTime.Now)})");
}
}
protected abstract void UpdateLibraryInternal();
internal abstract bool Test(); internal abstract bool Test();
protected static class NetClient protected static class NetClient

View File

@ -15,7 +15,7 @@ public class MangaKatana : MangaConnector
public override Manga[] GetManga(string publicationTitle = "") public override Manga[] GetManga(string publicationTitle = "")
{ {
Log($"Searching Publications. Term=\"{publicationTitle}\""); Log($"Searching Publications. Term=\"{publicationTitle}\"");
string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string sanitizedTitle = string.Join("%20", Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower();
string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name"; string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, RequestType.Default); downloadClient.MakeRequest(requestUrl, RequestType.Default);

View File

@ -24,7 +24,7 @@ public class Gotify : NotificationConnector
return $"Gotify {endpoint}"; return $"Gotify {endpoint}";
} }
public override void SendNotification(string title, string notificationText) protected override void SendNotificationInternal(string title, string notificationText)
{ {
Log($"Sending notification: {title} - {notificationText}"); Log($"Sending notification: {title} - {notificationText}");
MessageData message = new(title, notificationText); MessageData message = new(title, notificationText);

View File

@ -20,7 +20,7 @@ public class LunaSea : NotificationConnector
return $"LunaSea {id}"; return $"LunaSea {id}";
} }
public override void SendNotification(string title, string notificationText) protected override void SendNotificationInternal(string title, string notificationText)
{ {
Log($"Sending notification: {title} - {notificationText}"); Log($"Sending notification: {title} - {notificationText}");
MessageData message = new(title, notificationText); MessageData message = new(title, notificationText);

View File

@ -3,14 +3,72 @@
public abstract class NotificationConnector : GlobalBase public abstract class NotificationConnector : GlobalBase
{ {
public readonly NotificationConnectorType notificationConnectorType; public readonly NotificationConnectorType notificationConnectorType;
private DateTime? _notificationRequested = null;
private readonly Thread? _notificationBufferThread = null;
private const int NoChangeTimeout = 3, BiggestInterval = 30;
private List<KeyValuePair<string, string>> _notifications = new();
protected NotificationConnector(GlobalBase clone, NotificationConnectorType notificationConnectorType) : base(clone) protected NotificationConnector(GlobalBase clone, NotificationConnectorType notificationConnectorType) : base(clone)
{ {
Log($"Creating notificationConnector {Enum.GetName(notificationConnectorType)}"); Log($"Creating notificationConnector {Enum.GetName(notificationConnectorType)}");
this.notificationConnectorType = notificationConnectorType; this.notificationConnectorType = notificationConnectorType;
if (TrangaSettings.bufferLibraryUpdates)
{
_notificationBufferThread = new(CheckNotificationBuffer);
_notificationBufferThread.Start();
}
}
private void CheckNotificationBuffer()
{
while (true)
{
if (_notificationRequested is not null && DateTime.Now.Subtract((DateTime)_notificationRequested) > TimeSpan.FromMinutes(NoChangeTimeout)) //If no updates have been requested for NoChangeTimeout minutes, update library
{
string[] uniqueTitles = _notifications.DistinctBy(n => n.Key).Select(n => n.Key).ToArray();
Log($"Notification Buffer sending! Notifications: {string.Join(", ", uniqueTitles)}");
foreach (string ut in uniqueTitles)
{
string[] texts = _notifications.Where(n => n.Key == ut).Select(n => n.Value).ToArray();
SendNotificationInternal($"{ut} ({texts.Length})", string.Join('\n', texts));
}
_notificationRequested = null;
_notifications.Clear();
}
Thread.Sleep(100);
}
} }
public enum NotificationConnectorType : byte { Gotify = 0, LunaSea = 1, Ntfy = 2 } public enum NotificationConnectorType : byte { Gotify = 0, LunaSea = 1, Ntfy = 2 }
public void SendNotification(string title, string notificationText, bool buffer = false)
{
_notificationRequested ??= DateTime.Now;
if (!TrangaSettings.bufferNotifications || !buffer)
{
SendNotificationInternal(title, notificationText);
return;
}
_notifications.Add(new(title, notificationText));
if (_notificationRequested is not null &&
DateTime.Now.Subtract((DateTime)_notificationRequested) > TimeSpan.FromMinutes(BiggestInterval)) //If the last update has been more than BiggestInterval minutes ago, update library
{
string[] uniqueTitles = _notifications.DistinctBy(n => n.Key).Select(n => n.Key).ToArray();
foreach (string ut in uniqueTitles)
{
string[] texts = _notifications.Where(n => n.Key == ut).Select(n => n.Value).ToArray();
SendNotificationInternal(ut, string.Join('\n', texts));
}
_notificationRequested = null;
_notifications.Clear();
}
else if(_notificationRequested is not null)
{
Log($"Buffering Notifications (Updates in latest {((DateTime)_notificationRequested).Add(TimeSpan.FromMinutes(BiggestInterval)).Subtract(DateTime.Now)} or {((DateTime)_notificationRequested).Add(TimeSpan.FromMinutes(NoChangeTimeout)).Subtract(DateTime.Now)})");
}
}
public abstract void SendNotification(string title, string notificationText); protected abstract void SendNotificationInternal(string title, string notificationText);
} }

View File

@ -54,7 +54,7 @@ public class Ntfy : NotificationConnector
return $"Ntfy {endpoint} {topic}"; return $"Ntfy {endpoint} {topic}";
} }
public override void SendNotification(string title, string notificationText) protected override void SendNotificationInternal(string title, string notificationText)
{ {
Log($"Sending notification: {title} - {notificationText}"); Log($"Sending notification: {title} - {notificationText}");
MessageData message = new(title, topic, notificationText); MessageData message = new(title, topic, notificationText);

View File

@ -34,6 +34,7 @@ public partial class Tranga : GlobalBase
this._server = new Server(this); this._server = new Server(this);
string[] emojis = { "(•‿•)", "(づ \u25d5‿\u25d5 )づ", "( \u02d8\u25bd\u02d8)っ\u2668", "=\uff3e\u25cf \u22cf \u25cf\uff3e=", "(ΦωΦ)", "(\u272a\u3268\u272a)", "( ノ・o・ )ノ", "(〜^\u2207^ )〜", "~(\u2267ω\u2266)~","૮ \u00b4• ﻌ \u00b4• ა", "(\u02c3ᆺ\u02c2)", "(=\ud83d\udf66 \u0f1d \ud83d\udf66=)"}; string[] emojis = { "(•‿•)", "(づ \u25d5‿\u25d5 )づ", "( \u02d8\u25bd\u02d8)っ\u2668", "=\uff3e\u25cf \u22cf \u25cf\uff3e=", "(ΦωΦ)", "(\u272a\u3268\u272a)", "( ノ・o・ )ノ", "(〜^\u2207^ )〜", "~(\u2267ω\u2266)~","૮ \u00b4• ﻌ \u00b4• ა", "(\u02c3ᆺ\u02c2)", "(=\ud83d\udf66 \u0f1d \ud83d\udf66=)"};
SendNotifications("Tranga Started", emojis[Random.Shared.Next(0,emojis.Length-1)]); SendNotifications("Tranga Started", emojis[Random.Shared.Next(0,emojis.Length-1)]);
Log(TrangaSettings.AsJObject().ToString());
} }
public MangaConnector? GetConnector(string name) public MangaConnector? GetConnector(string name)

View File

@ -15,6 +15,8 @@ public static class TrangaSettings
public static string workingDirectory { get; private set; } = Path.Join(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/share" : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "tranga-api"); public static string workingDirectory { get; private set; } = Path.Join(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/share" : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "tranga-api");
public static int apiPortNumber { get; private set; } = 6531; public static int apiPortNumber { get; private set; } = 6531;
public static string userAgent { get; private set; } = DefaultUserAgent; public static string userAgent { get; private set; } = DefaultUserAgent;
public static bool bufferLibraryUpdates { get; private set; } = false;
public static bool bufferNotifications { get; private set; } = false;
[JsonIgnore] public static string settingsFilePath => Path.Join(workingDirectory, "settings.json"); [JsonIgnore] public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
[JsonIgnore] public static string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json"); [JsonIgnore] public static string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json");
[JsonIgnore] public static string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json"); [JsonIgnore] public static string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json");
@ -46,15 +48,17 @@ public static class TrangaSettings
ExportSettings(); ExportSettings();
} }
public static void CreateOrUpdate(string? downloadDirectory = null, string? pWorkingDirectory = null, int? pApiPortNumber = null, string? pUserAgent = null, bool? pAprilFoolsMode = null) public static void CreateOrUpdate(string? downloadDirectory = null, string? pWorkingDirectory = null, int? pApiPortNumber = null, string? pUserAgent = null, bool? pAprilFoolsMode = null, bool? pBufferLibraryUpdates = null, bool? pBufferNotifications = null)
{ {
if(pWorkingDirectory is null && File.Exists(settingsFilePath)) if(pWorkingDirectory is null && File.Exists(settingsFilePath))
LoadFromWorkingDirectory(workingDirectory); LoadFromWorkingDirectory(workingDirectory);
TrangaSettings.downloadLocation = downloadDirectory ?? TrangaSettings.downloadLocation; downloadLocation = downloadDirectory ?? downloadLocation;
TrangaSettings.workingDirectory = pWorkingDirectory ?? TrangaSettings.workingDirectory; workingDirectory = pWorkingDirectory ?? workingDirectory;
TrangaSettings.apiPortNumber = pApiPortNumber ?? TrangaSettings.apiPortNumber; apiPortNumber = pApiPortNumber ?? apiPortNumber;
TrangaSettings.userAgent = pUserAgent ?? TrangaSettings.userAgent; userAgent = pUserAgent ?? userAgent;
TrangaSettings.aprilFoolsMode = pAprilFoolsMode ?? TrangaSettings.aprilFoolsMode; aprilFoolsMode = pAprilFoolsMode ?? aprilFoolsMode;
bufferLibraryUpdates = pBufferLibraryUpdates ?? bufferLibraryUpdates;
bufferNotifications = pBufferNotifications ?? bufferNotifications;
Directory.CreateDirectory(downloadLocation); Directory.CreateDirectory(downloadLocation);
Directory.CreateDirectory(workingDirectory); Directory.CreateDirectory(workingDirectory);
ExportSettings(); ExportSettings();
@ -90,7 +94,7 @@ public static class TrangaSettings
public static void UpdateAprilFoolsMode(bool enabled) public static void UpdateAprilFoolsMode(bool enabled)
{ {
TrangaSettings.aprilFoolsMode = enabled; aprilFoolsMode = enabled;
ExportSettings(); ExportSettings();
} }
@ -102,10 +106,10 @@ public static class TrangaSettings
else else
Directory.CreateDirectory(newPath); Directory.CreateDirectory(newPath);
if (moveFiles && Directory.Exists(TrangaSettings.downloadLocation)) if (moveFiles && Directory.Exists(downloadLocation))
Directory.Move(TrangaSettings.downloadLocation, newPath); Directory.Move(downloadLocation, newPath);
TrangaSettings.downloadLocation = newPath; downloadLocation = newPath;
ExportSettings(); ExportSettings();
} }
@ -116,26 +120,26 @@ public static class TrangaSettings
GroupRead | GroupWrite | None | OtherRead | OtherWrite | UserRead | UserWrite); GroupRead | GroupWrite | None | OtherRead | OtherWrite | UserRead | UserWrite);
else else
Directory.CreateDirectory(newPath); Directory.CreateDirectory(newPath);
Directory.Move(TrangaSettings.workingDirectory, newPath); Directory.Move(workingDirectory, newPath);
TrangaSettings.workingDirectory = newPath; workingDirectory = newPath;
ExportSettings(); ExportSettings();
} }
public static void UpdateUserAgent(string? customUserAgent) public static void UpdateUserAgent(string? customUserAgent)
{ {
TrangaSettings.userAgent = customUserAgent ?? DefaultUserAgent; userAgent = customUserAgent ?? DefaultUserAgent;
ExportSettings(); ExportSettings();
} }
public static void UpdateRateLimit(RequestType requestType, int newLimit) public static void UpdateRateLimit(RequestType requestType, int newLimit)
{ {
TrangaSettings.requestLimits[requestType] = newLimit; requestLimits[requestType] = newLimit;
ExportSettings(); ExportSettings();
} }
public static void ResetRateLimits() public static void ResetRateLimits()
{ {
TrangaSettings.requestLimits = DefaultRequestLimits; requestLimits = DefaultRequestLimits;
ExportSettings(); ExportSettings();
} }
@ -154,13 +158,15 @@ public static class TrangaSettings
public static JObject AsJObject() public static JObject AsJObject()
{ {
JObject jobj = new JObject(); JObject jobj = new JObject();
jobj.Add("downloadLocation", JToken.FromObject(TrangaSettings.downloadLocation)); jobj.Add("downloadLocation", JToken.FromObject(downloadLocation));
jobj.Add("workingDirectory", JToken.FromObject(TrangaSettings.workingDirectory)); jobj.Add("workingDirectory", JToken.FromObject(workingDirectory));
jobj.Add("apiPortNumber", JToken.FromObject(TrangaSettings.apiPortNumber)); jobj.Add("apiPortNumber", JToken.FromObject(apiPortNumber));
jobj.Add("userAgent", JToken.FromObject(TrangaSettings.userAgent)); jobj.Add("userAgent", JToken.FromObject(userAgent));
jobj.Add("aprilFoolsMode", JToken.FromObject(TrangaSettings.aprilFoolsMode)); jobj.Add("aprilFoolsMode", JToken.FromObject(aprilFoolsMode));
jobj.Add("version", JToken.FromObject(TrangaSettings.version)); jobj.Add("version", JToken.FromObject(version));
jobj.Add("requestLimits", JToken.FromObject(TrangaSettings.requestLimits)); jobj.Add("requestLimits", JToken.FromObject(requestLimits));
jobj.Add("bufferLibraryUpdates", JToken.FromObject(bufferLibraryUpdates));
jobj.Add("bufferNotifications", JToken.FromObject(bufferNotifications));
return jobj; return jobj;
} }
@ -170,16 +176,20 @@ public static class TrangaSettings
{ {
JObject jobj = JObject.Parse(serialized); JObject jobj = JObject.Parse(serialized);
if (jobj.TryGetValue("downloadLocation", out JToken? dl)) if (jobj.TryGetValue("downloadLocation", out JToken? dl))
TrangaSettings.downloadLocation = dl.Value<string>()!; downloadLocation = dl.Value<string>()!;
if (jobj.TryGetValue("workingDirectory", out JToken? wd)) if (jobj.TryGetValue("workingDirectory", out JToken? wd))
TrangaSettings.workingDirectory = wd.Value<string>()!; workingDirectory = wd.Value<string>()!;
if (jobj.TryGetValue("apiPortNumber", out JToken? apn)) if (jobj.TryGetValue("apiPortNumber", out JToken? apn))
TrangaSettings.apiPortNumber = apn.Value<int>(); apiPortNumber = apn.Value<int>();
if (jobj.TryGetValue("userAgent", out JToken? ua)) if (jobj.TryGetValue("userAgent", out JToken? ua))
TrangaSettings.userAgent = ua.Value<string>()!; userAgent = ua.Value<string>()!;
if (jobj.TryGetValue("aprilFoolsMode", out JToken? afm)) if (jobj.TryGetValue("aprilFoolsMode", out JToken? afm))
TrangaSettings.aprilFoolsMode = afm.Value<bool>()!; aprilFoolsMode = afm.Value<bool>()!;
if (jobj.TryGetValue("requestLimits", out JToken? rl)) if (jobj.TryGetValue("requestLimits", out JToken? rl))
TrangaSettings.requestLimits = rl.ToObject<Dictionary<RequestType, int>>()!; requestLimits = rl.ToObject<Dictionary<RequestType, int>>()!;
if (jobj.TryGetValue("bufferLibraryUpdates", out JToken? blu))
bufferLibraryUpdates = blu.Value<bool>()!;
if (jobj.TryGetValue("bufferNotifications", out JToken? bn))
bufferNotifications = bn.Value<bool>()!;
} }
} }