From 294ce01bc3ece7ca8e845713f4932ae0404c7086 Mon Sep 17 00:00:00 2001 From: Glax Date: Fri, 19 Apr 2024 02:37:17 +0200 Subject: [PATCH 01/45] Set Manga.releaseStatus to new releaseStatus. Fix #119 --- Tranga/Manga.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 4949a2c..36ae388 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -28,7 +28,7 @@ public struct Manga public string? originalLanguage { get; } // ReSharper disable twice MemberCanBePrivate.Global public string status { get; private set; } - public ReleaseStatusByte releaseStatus { get; } + public ReleaseStatusByte releaseStatus { get; private set; } public enum ReleaseStatusByte : byte { Continuing = 0, @@ -80,6 +80,7 @@ public struct Manga if(!this.authors.Contains(author)) this.authors.Add(author); this.status = newManga.status; + this.releaseStatus = newManga.releaseStatus; this.year = newManga.year; } From 95236daf41a52c4d7912a3a5b42fbb69e834e5ce Mon Sep 17 00:00:00 2001 From: Glax Date: Fri, 19 Apr 2024 03:00:31 +0200 Subject: [PATCH 02/45] Check if tags and authors are the same on Manga equals. UpdateManga performs union/concat operation on alttitles, tags and authors --- Tranga/Manga.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 36ae388..85ec6d6 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -12,14 +12,14 @@ namespace Tranga; public struct Manga { public string sortName { get; private set; } - public List authors { get; } + public List authors { get; private set; } // ReSharper disable once UnusedAutoPropertyAccessor.Global - public Dictionary altTitles { get; } + public Dictionary altTitles { get; private set; } // ReSharper disable once MemberCanBePrivate.Global public string? description { get; private set; } - public string[] tags { get; } + public string[] tags { get; private set; } // ReSharper disable once UnusedAutoPropertyAccessor.Global - public string? coverUrl { get; } + public string? coverUrl { get; private set; } public string? coverFileNameInCache { get; } // ReSharper disable once UnusedAutoPropertyAccessor.Global public Dictionary links { get; } @@ -76,9 +76,10 @@ public struct Manga { this.sortName = newManga.sortName; this.description = newManga.description; - foreach (string author in newManga.authors) - if(!this.authors.Contains(author)) - this.authors.Add(author); + this.coverUrl = newManga.coverUrl; + this.authors = authors.Union(newManga.authors).ToList(); + this.altTitles = altTitles.Concat(newManga.altTitles).ToDictionary(x => x.Key, x => x.Value); + this.tags = tags.Union(newManga.tags).ToArray(); this.status = newManga.status; this.releaseStatus = newManga.releaseStatus; this.year = newManga.year; @@ -94,7 +95,8 @@ public struct Manga this.releaseStatus == compareManga.releaseStatus && this.sortName == compareManga.sortName && this.latestChapterAvailable.Equals(compareManga.latestChapterAvailable) && - this.tags.SequenceEqual(compareManga.tags); + this.authors.All(a => compareManga.authors.Contains(a)) && + this.tags.All(t => compareManga.tags.Contains(t)); } public override string ToString() From 0ab2ae03ce82bb27a355c18cf1d6ca141b5758ac Mon Sep 17 00:00:00 2001 From: Glax Date: Fri, 19 Apr 2024 03:07:46 +0200 Subject: [PATCH 03/45] unionby isntead of concat --- Tranga/Manga.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 85ec6d6..0267286 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -78,7 +78,7 @@ public struct Manga this.description = newManga.description; this.coverUrl = newManga.coverUrl; this.authors = authors.Union(newManga.authors).ToList(); - this.altTitles = altTitles.Concat(newManga.altTitles).ToDictionary(x => x.Key, x => x.Value); + this.altTitles = altTitles.UnionBy(newManga.altTitles, kv => kv.Key).ToDictionary(x => x.Key, x => x.Value); this.tags = tags.Union(newManga.tags).ToArray(); this.status = newManga.status; this.releaseStatus = newManga.releaseStatus; From df2fc4a036308b650c6fd57b036caa4ca4be14c4 Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 20 Apr 2024 18:39:49 +0200 Subject: [PATCH 04/45] Remove README CLI reference --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 655dcfe..05d6aee 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,6 @@ Notifications can be sent to your devices using [Gotify](https://gotify.net/) an Tranga (this git-repo) will open a port (standard 6531) and listen for requests to add Jobs to Monitor and/or download specific Manga. The configuration is all done through HTTP-Requests. -The frontend in this repo is **CLI**-based. _**For a web-frontend use [tranga-website](https://github.com/C9Glax/tranga-website).**_ This project downloads the images for a Manga from the specified Scanlation-Website and packages them with some metadata - from that same website - in a .cbz-archive (per chapter). From 5bdbd9e2e4fe94ab455c46c9be471b2c2070a3ad Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 02:25:39 +0200 Subject: [PATCH 05/45] Hack to resolve #60 Website-URL. Field will have same name, just acquisition will be better. --- Tranga/Manga.cs | 38 +++++++++------------------ Tranga/MangaConnectors/Bato.cs | 6 ++--- Tranga/MangaConnectors/MangaDex.cs | 6 ++--- Tranga/MangaConnectors/MangaKatana.cs | 12 ++++----- Tranga/MangaConnectors/MangaLife.cs | 6 ++--- Tranga/MangaConnectors/Manganato.cs | 10 +++---- Tranga/MangaConnectors/Mangasee.cs | 6 ++--- Tranga/MangaConnectors/Mangaworld.cs | 6 ++--- 8 files changed, 36 insertions(+), 54 deletions(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 0267286..a29f2b6 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -43,11 +43,13 @@ public struct Manga public float ignoreChaptersBelow { get; set; } public float latestChapterDownloaded { get; set; } public float latestChapterAvailable { get; set; } + + public string websiteUrl { get; private set; } private static readonly Regex LegalCharacters = new (@"[A-Za-zÀ-ÖØ-öø-ÿ0-9 \.\-,'\'\)\(~!\+]*"); [JsonConstructor] - public Manga(string sortName, List authors, string? description, Dictionary altTitles, string[] tags, string? coverUrl, string? coverFileNameInCache, Dictionary? links, int? year, string? originalLanguage, string status, string publicationId, ReleaseStatusByte releaseStatus = 0, string? websiteUrl = null, string? folderName = null, float? ignoreChaptersBelow = 0) + public Manga(string sortName, List authors, string? description, Dictionary altTitles, string[] tags, string? coverUrl, string? coverFileNameInCache, Dictionary? links, int? year, string? originalLanguage, string publicationId, ReleaseStatusByte releaseStatus, string? websiteUrl, string? folderName = null, float? ignoreChaptersBelow = 0) { this.sortName = sortName; this.authors = authors; @@ -59,7 +61,6 @@ public struct Manga this.links = links ?? new Dictionary(); this.year = year; this.originalLanguage = originalLanguage; - this.status = status; this.publicationId = publicationId; this.folderName = folderName ?? string.Concat(LegalCharacters.Matches(sortName)); while (this.folderName.EndsWith('.')) @@ -70,6 +71,7 @@ public struct Manga this.latestChapterDownloaded = 0; this.latestChapterAvailable = 0; this.releaseStatus = releaseStatus; + this.websiteUrl = websiteUrl; } public void UpdateMetadata(Manga newManga) @@ -171,38 +173,22 @@ public struct Manga [JsonRequired]public string year { get; } [JsonRequired]public string status { get; } [JsonRequired]public string description_text { get; } - [JsonIgnore] public static string[] continuing = new[] - { - "ongoing", - "hiatus", - "in corso", - "in pausa" - }; - [JsonIgnore] public static string[] ended = new[] - { - "completed", - "cancelled", - "discontinued", - "finito", - "cancellato", - "droppato" - }; - public Metadata(Manga manga) : this(manga.sortName, manga.year.ToString() ?? string.Empty, manga.status, manga.description ?? "") + public Metadata(Manga manga) : this(manga.sortName, manga.year.ToString() ?? string.Empty, manga.releaseStatus, manga.description ?? "") { } - public Metadata(string name, string year, string status, string description_text) + public Metadata(string name, string year, ReleaseStatusByte status, string description_text) { this.name = name; this.year = year; - if(continuing.Contains(status.ToLower())) - this.status = "Continuing"; - else if(ended.Contains(status.ToLower())) - this.status = "Ended"; - else - this.status = status; + this.status = status switch + { + ReleaseStatusByte.Continuing => "Continuing", + ReleaseStatusByte.Completed => "Ended", + _ => Enum.GetName(status) ?? "Ended" + }; this.description_text = description_text; //kill it with fire, but otherwise Komga will not parse diff --git a/Tranga/MangaConnectors/Bato.cs b/Tranga/MangaConnectors/Bato.cs index 96d97c0..edc6dc9 100644 --- a/Tranga/MangaConnectors/Bato.cs +++ b/Tranga/MangaConnectors/Bato.cs @@ -49,7 +49,7 @@ public class Bato : MangaConnector Log($"Failed to retrieve site"); return null; } - return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1]); + return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); } private Manga[] ParsePublicationsFromHtml(HtmlDocument document) @@ -72,7 +72,7 @@ public class Bato : MangaConnector return ret.ToArray(); } - private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId) + private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { HtmlNode infoNode = document.DocumentNode.SelectSingleNode("/html/body/div/main/div[1]/div[2]"); @@ -115,7 +115,7 @@ public class Bato : MangaConnector } Manga manga = new (sortName, authors, description, altTitles, tags, posterUrl, coverFileNameInCache, new Dictionary(), - year, originalLanguage, status, publicationId, releaseStatus); + year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); cachedPublications.Add(manga); return manga; } diff --git a/Tranga/MangaConnectors/MangaDex.cs b/Tranga/MangaConnectors/MangaDex.cs index d5af20c..550e34f 100644 --- a/Tranga/MangaConnectors/MangaDex.cs +++ b/Tranga/MangaConnectors/MangaDex.cs @@ -116,7 +116,7 @@ public class MangaDex : MangaConnector Dictionary linksDict = new(); if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode)) - foreach (KeyValuePair linkKv in linksNode!.AsObject()) + foreach (KeyValuePair linkKv in linksNode!.AsObject()) linksDict.TryAdd(linkKv.Key, linkKv.Value.GetValue()); string? originalLanguage = @@ -183,9 +183,9 @@ public class MangaDex : MangaConnector linksDict, year, originalLanguage, - Enum.GetName(status) ?? "", publicationId, - status + status, + websiteUrl: $"https://mangadex.org/title/{publicationId}" ); cachedPublications.Add(pub); return pub; diff --git a/Tranga/MangaConnectors/MangaKatana.cs b/Tranga/MangaConnectors/MangaKatana.cs index a62315b..de59030 100644 --- a/Tranga/MangaConnectors/MangaKatana.cs +++ b/Tranga/MangaConnectors/MangaKatana.cs @@ -28,7 +28,7 @@ public class MangaKatana : MangaConnector && requestResult.redirectedToUrl is not null && requestResult.redirectedToUrl.Contains("mangakatana.com/manga")) { - return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1]) }; + return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1], requestResult.redirectedToUrl) }; } Manga[] publications = ParsePublicationsFromHtml(requestResult.result); @@ -47,7 +47,7 @@ public class MangaKatana : MangaConnector downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; - return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1]); + return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1], url); } private Manga[] ParsePublicationsFromHtml(Stream html) @@ -77,13 +77,12 @@ public class MangaKatana : MangaConnector return ret.ToArray(); } - private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId) + private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl) { StreamReader reader = new(html); string htmlString = reader.ReadToEnd(); HtmlDocument document = new(); document.LoadHtml(htmlString); - string status = ""; Dictionary altTitles = new(); Dictionary? links = null; HashSet tags = new(); @@ -112,8 +111,7 @@ public class MangaKatana : MangaConnector authors = value.Split(','); break; case "status": - status = value; - switch (status.ToLower()) + switch (value.ToLower()) { case "ongoing": releaseStatus = Manga.ReleaseStatusByte.Continuing; break; case "completed": releaseStatus = Manga.ReleaseStatusByte.Completed; break; @@ -144,7 +142,7 @@ public class MangaKatana : MangaConnector } Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, - year, originalLanguage, status, publicationId, releaseStatus); + year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); cachedPublications.Add(manga); return manga; } diff --git a/Tranga/MangaConnectors/MangaLife.cs b/Tranga/MangaConnectors/MangaLife.cs index fd92377..3f87953 100644 --- a/Tranga/MangaConnectors/MangaLife.cs +++ b/Tranga/MangaConnectors/MangaLife.cs @@ -41,7 +41,7 @@ public class MangaLife : MangaConnector RequestResult requestResult = this.downloadClient.MakeRequest(url, RequestType.MangaInfo); if(requestResult.htmlDocument is not null) - return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId); + return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId, url); return null; } @@ -69,7 +69,7 @@ public class MangaLife : MangaConnector } - private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId) + private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string originalLanguage = "", status = ""; Dictionary altTitles = new(), links = new(); @@ -122,7 +122,7 @@ public class MangaLife : MangaConnector string description = descriptionNode.InnerText; Manga manga = new(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, - coverFileNameInCache, links, year, originalLanguage, status, publicationId, releaseStatus); + coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); cachedPublications.Add(manga); return manga; } diff --git a/Tranga/MangaConnectors/Manganato.cs b/Tranga/MangaConnectors/Manganato.cs index d7bbd79..cbbf0f0 100644 --- a/Tranga/MangaConnectors/Manganato.cs +++ b/Tranga/MangaConnectors/Manganato.cs @@ -65,12 +65,11 @@ public class Manganato : MangaConnector if (requestResult.htmlDocument is null) return null; - return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1]); + return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); } - private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId) + private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { - string status = ""; Dictionary altTitles = new(); Dictionary? links = null; HashSet tags = new(); @@ -101,8 +100,7 @@ public class Manganato : MangaConnector authors = value.Split('-'); break; case "status": - status = value; - switch (status.ToLower()) + switch (value.ToLower()) { case "ongoing": releaseStatus = Manga.ReleaseStatusByte.Continuing; break; case "completed": releaseStatus = Manga.ReleaseStatusByte.Completed; break; @@ -130,7 +128,7 @@ public class Manganato : MangaConnector int year = Convert.ToInt32(yearString.Split(',')[^1]) + 2000; Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, - year, originalLanguage, status, publicationId, releaseStatus); + year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); cachedPublications.Add(manga); return manga; } diff --git a/Tranga/MangaConnectors/Mangasee.cs b/Tranga/MangaConnectors/Mangasee.cs index c354d1c..f99e108 100644 --- a/Tranga/MangaConnectors/Mangasee.cs +++ b/Tranga/MangaConnectors/Mangasee.cs @@ -120,11 +120,11 @@ public class Mangasee : MangaConnector RequestResult requestResult = this.downloadClient.MakeRequest(url, RequestType.MangaInfo); if((int)requestResult.statusCode < 300 && (int)requestResult.statusCode >= 200 && requestResult.htmlDocument is not null) - return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId); + return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId, url); return null; } - private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId) + private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string originalLanguage = "", status = ""; Dictionary altTitles = new(), links = new(); @@ -178,7 +178,7 @@ public class Mangasee : MangaConnector Manga manga = new(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, - year, originalLanguage, status, publicationId, releaseStatus); + year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); cachedPublications.Add(manga); return manga; } diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index a457999..0f2e80d 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -68,10 +68,10 @@ public class Mangaworld: MangaConnector Regex idRex = new (@"https:\/\/www\.mangaworld\.[a-z]{0,63}\/manga\/([0-9]+\/[0-9A-z\-]+).*"); string id = idRex.Match(url).Groups[1].Value; - return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id); + return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url); } - private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId) + private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { Dictionary altTitles = new(); Dictionary? links = null; @@ -119,7 +119,7 @@ public class Mangaworld: MangaConnector int year = Convert.ToInt32(yearString); Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, - year, originalLanguage, status, publicationId, releaseStatus); + year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); cachedPublications.Add(manga); return manga; } From c69f1f65691a3c53ac5791d4061e7ea1b33cb52c Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 04:45:49 +0200 Subject: [PATCH 06/45] Addresses #170 Manganato authors and genres include "\r\n" --- Tranga/MangaConnectors/Manganato.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tranga/MangaConnectors/Manganato.cs b/Tranga/MangaConnectors/Manganato.cs index cbbf0f0..f6ff4c1 100644 --- a/Tranga/MangaConnectors/Manganato.cs +++ b/Tranga/MangaConnectors/Manganato.cs @@ -98,6 +98,8 @@ public class Manganato : MangaConnector break; case "authors": authors = value.Split('-'); + for (int i = 0; i < authors.Length; i++) + authors[i] = authors[i].Replace("\r\n", ""); break; case "status": switch (value.ToLower()) @@ -108,6 +110,8 @@ public class Manganato : MangaConnector break; case "genres": string[] genres = value.Split(" - "); + for (int i = 0; i < genres.Length; i++) + genres[i] = genres[i].Replace("\r\n", ""); tags = genres.ToHashSet(); break; } From 5f820c53f53513125df305530798dc9d822a8238 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 22:03:09 +0200 Subject: [PATCH 07/45] Update websiteUrl on metadata-refresh https://github.com/C9Glax/tranga-website/issues/60 --- Tranga/Manga.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index a29f2b6..89f2596 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -84,6 +84,7 @@ public struct Manga this.tags = tags.Union(newManga.tags).ToArray(); this.status = newManga.status; this.releaseStatus = newManga.releaseStatus; + this.websiteUrl = newManga.websiteUrl; this.year = newManga.year; } @@ -98,6 +99,7 @@ public struct Manga this.sortName == compareManga.sortName && this.latestChapterAvailable.Equals(compareManga.latestChapterAvailable) && this.authors.All(a => compareManga.authors.Contains(a)) && + this.websiteUrl.Equals(compareManga.websiteUrl) && this.tags.All(t => compareManga.tags.Contains(t)); } From 06fe98323a4e2ef0e4727a49f34073f95ba3d2c4 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 22:09:43 +0200 Subject: [PATCH 08/45] Fix crashing when comparing old Manga (missing websiteUrl) --- Tranga/Manga.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 89f2596..32ea54b 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -44,12 +44,12 @@ public struct Manga public float latestChapterDownloaded { get; set; } public float latestChapterAvailable { get; set; } - public string websiteUrl { get; private set; } + public string? websiteUrl { get; private set; } private static readonly Regex LegalCharacters = new (@"[A-Za-zÀ-ÖØ-öø-ÿ0-9 \.\-,'\'\)\(~!\+]*"); [JsonConstructor] - public Manga(string sortName, List authors, string? description, Dictionary altTitles, string[] tags, string? coverUrl, string? coverFileNameInCache, Dictionary? links, int? year, string? originalLanguage, string publicationId, ReleaseStatusByte releaseStatus, string? websiteUrl, string? folderName = null, float? ignoreChaptersBelow = 0) + public Manga(string sortName, List authors, string? description, Dictionary altTitles, string[] tags, string? coverUrl, string? coverFileNameInCache, Dictionary? links, int? year, string? originalLanguage, string publicationId, ReleaseStatusByte releaseStatus, string? websiteUrl = null, string? folderName = null, float? ignoreChaptersBelow = 0) { this.sortName = sortName; this.authors = authors; @@ -71,6 +71,7 @@ public struct Manga this.latestChapterDownloaded = 0; this.latestChapterAvailable = 0; this.releaseStatus = releaseStatus; + this.status = Enum.GetName(releaseStatus) ?? ""; this.websiteUrl = websiteUrl; } @@ -99,7 +100,7 @@ public struct Manga this.sortName == compareManga.sortName && this.latestChapterAvailable.Equals(compareManga.latestChapterAvailable) && this.authors.All(a => compareManga.authors.Contains(a)) && - this.websiteUrl.Equals(compareManga.websiteUrl) && + (this.websiteUrl??"").Equals(compareManga.websiteUrl) && this.tags.All(t => compareManga.tags.Contains(t)); } From 79e61a62c7e132ace15cb68f8300afaf6146b583 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 22:29:22 +0200 Subject: [PATCH 09/45] Export Jobfiles after execution, update metadata in jobfiles --- Tranga/Jobs/JobBoss.cs | 6 ++++-- Tranga/Jobs/UpdateMetadata.cs | 2 +- Tranga/Manga.cs | 25 ++++++++++++++----------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 99670b4..4380497 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -171,7 +171,7 @@ public class JobBoss : GlobalBase } } - private void UpdateJobFile(Job job) + internal void UpdateJobFile(Job job) { string jobFilePath = Path.Join(settings.jobsFolderPath, $"{job.id}.json"); @@ -245,7 +245,9 @@ public class JobBoss : GlobalBase Log($"Next job in {jobs.MinBy(job => job.nextExecution)?.nextExecution.Subtract(DateTime.Now)} {jobs.MinBy(job => job.nextExecution)?.id}"); }else if (queueHead.progressToken.state is ProgressToken.State.Standby) { - Job[] subJobs = jobQueue.Peek().ExecuteReturnSubTasks(this).ToArray(); + Job eJob = jobQueue.Peek(); + Job[] subJobs = eJob.ExecuteReturnSubTasks(this).ToArray(); + UpdateJobFile(eJob); AddJobs(subJobs); AddJobsToQueue(subJobs); }else if (queueHead.progressToken.state is ProgressToken.State.Running && DateTime.Now.Subtract(queueHead.progressToken.lastUpdate) > TimeSpan.FromMinutes(5)) diff --git a/Tranga/Jobs/UpdateMetadata.cs b/Tranga/Jobs/UpdateMetadata.cs index e9107ce..5471685 100644 --- a/Tranga/Jobs/UpdateMetadata.cs +++ b/Tranga/Jobs/UpdateMetadata.cs @@ -33,7 +33,7 @@ public class UpdateMetadata : Job return Array.Empty(); } - this.manga.UpdateMetadata(updatedManga); + this.manga = manga.WithMetadata(updatedManga); this.manga.SaveSeriesInfoJson(settings.downloadLocation, true); this.progressToken.Complete(); } diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 32ea54b..388d02d 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -75,18 +75,21 @@ public struct Manga this.websiteUrl = websiteUrl; } - public void UpdateMetadata(Manga newManga) + public Manga WithMetadata(Manga newManga) { - this.sortName = newManga.sortName; - this.description = newManga.description; - this.coverUrl = newManga.coverUrl; - this.authors = authors.Union(newManga.authors).ToList(); - this.altTitles = altTitles.UnionBy(newManga.altTitles, kv => kv.Key).ToDictionary(x => x.Key, x => x.Value); - this.tags = tags.Union(newManga.tags).ToArray(); - this.status = newManga.status; - this.releaseStatus = newManga.releaseStatus; - this.websiteUrl = newManga.websiteUrl; - this.year = newManga.year; + return this with + { + sortName = newManga.sortName, + description = newManga.description, + coverUrl = newManga.coverUrl, + authors = authors.Union(newManga.authors).ToList(), + altTitles = altTitles.UnionBy(newManga.altTitles, kv => kv.Key).ToDictionary(x => x.Key, x => x.Value), + tags = tags.Union(newManga.tags).ToArray(), + status = newManga.status, + releaseStatus = newManga.releaseStatus, + websiteUrl = newManga.websiteUrl, + year = newManga.year + }; } public override bool Equals(object? obj) From daba940b456a76b82f3c59a63fa629496ec4faa5 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 22:38:23 +0200 Subject: [PATCH 10/45] Make cachePublications a dictionary with internalId as key. --- Tranga/GlobalBase.cs | 2 +- Tranga/Jobs/JobBoss.cs | 4 ++-- Tranga/MangaConnectors/Bato.cs | 2 +- Tranga/MangaConnectors/MangaDex.cs | 2 +- Tranga/MangaConnectors/MangaKatana.cs | 2 +- Tranga/MangaConnectors/MangaLife.cs | 2 +- Tranga/MangaConnectors/Manganato.cs | 2 +- Tranga/MangaConnectors/Mangasee.cs | 2 +- Tranga/MangaConnectors/Mangaworld.cs | 2 +- Tranga/Tranga.cs | 8 +++++--- 10 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Tranga/GlobalBase.cs b/Tranga/GlobalBase.cs index ce01a79..9258a6e 100644 --- a/Tranga/GlobalBase.cs +++ b/Tranga/GlobalBase.cs @@ -14,7 +14,7 @@ public abstract class GlobalBase protected TrangaSettings settings { get; init; } protected HashSet notificationConnectors { get; init; } protected HashSet libraryConnectors { get; init; } - protected List cachedPublications { get; init; } + protected Dictionary cachedPublications { get; init; } public static readonly NumberFormatInfo numberFormatDecimalPoint = new (){ NumberDecimalSeparator = "." }; protected static readonly Regex baseUrlRex = new(@"https?:\/\/[0-9A-z\.-]+(:[0-9]+)?"); diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 4380497..77cba2f 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -160,10 +160,10 @@ public class JobBoss : GlobalBase { this.jobs.FirstOrDefault(jjob => jjob.id == job.parentJobId)?.AddSubJob(job); if (job is DownloadNewChapters dncJob) - cachedPublications.Add(dncJob.manga); + cachedPublications.Add(dncJob.manga.internalId, dncJob.manga); } - HashSet coverFileNames = cachedPublications.Select(manga => manga.coverFileNameInCache!).ToHashSet(); + HashSet coverFileNames = cachedPublications.Select(manga => manga.Value.coverFileNameInCache!).ToHashSet(); foreach (string fileName in Directory.GetFiles(settings.coverImageCache)) //Cleanup Unused Covers { if(!coverFileNames.Any(existingManga => fileName.Contains(existingManga))) diff --git a/Tranga/MangaConnectors/Bato.cs b/Tranga/MangaConnectors/Bato.cs index edc6dc9..58582e7 100644 --- a/Tranga/MangaConnectors/Bato.cs +++ b/Tranga/MangaConnectors/Bato.cs @@ -116,7 +116,7 @@ public class Bato : MangaConnector Manga manga = new (sortName, authors, description, altTitles, tags, posterUrl, coverFileNameInCache, new Dictionary(), year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga); + cachedPublications.Add(manga.internalId, manga); return manga; } diff --git a/Tranga/MangaConnectors/MangaDex.cs b/Tranga/MangaConnectors/MangaDex.cs index 550e34f..135261b 100644 --- a/Tranga/MangaConnectors/MangaDex.cs +++ b/Tranga/MangaConnectors/MangaDex.cs @@ -187,7 +187,7 @@ public class MangaDex : MangaConnector status, websiteUrl: $"https://mangadex.org/title/{publicationId}" ); - cachedPublications.Add(pub); + cachedPublications.Add(pub.internalId, pub); return pub; } diff --git a/Tranga/MangaConnectors/MangaKatana.cs b/Tranga/MangaConnectors/MangaKatana.cs index de59030..5996439 100644 --- a/Tranga/MangaConnectors/MangaKatana.cs +++ b/Tranga/MangaConnectors/MangaKatana.cs @@ -143,7 +143,7 @@ public class MangaKatana : MangaConnector Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga); + cachedPublications.Add(manga.internalId, manga); return manga; } diff --git a/Tranga/MangaConnectors/MangaLife.cs b/Tranga/MangaConnectors/MangaLife.cs index 3f87953..d54454f 100644 --- a/Tranga/MangaConnectors/MangaLife.cs +++ b/Tranga/MangaConnectors/MangaLife.cs @@ -123,7 +123,7 @@ public class MangaLife : MangaConnector Manga manga = new(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga); + cachedPublications.Add(manga.internalId, manga); return manga; } diff --git a/Tranga/MangaConnectors/Manganato.cs b/Tranga/MangaConnectors/Manganato.cs index f6ff4c1..437f1b9 100644 --- a/Tranga/MangaConnectors/Manganato.cs +++ b/Tranga/MangaConnectors/Manganato.cs @@ -133,7 +133,7 @@ public class Manganato : MangaConnector Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga); + cachedPublications.Add(manga.internalId, manga); return manga; } diff --git a/Tranga/MangaConnectors/Mangasee.cs b/Tranga/MangaConnectors/Mangasee.cs index f99e108..32053b6 100644 --- a/Tranga/MangaConnectors/Mangasee.cs +++ b/Tranga/MangaConnectors/Mangasee.cs @@ -179,7 +179,7 @@ public class Mangasee : MangaConnector Manga manga = new(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga); + cachedPublications.Add(manga.internalId, manga); return manga; } diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index 0f2e80d..fbea159 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -120,7 +120,7 @@ public class Mangaworld: MangaConnector Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga); + cachedPublications.Add(manga.internalId, manga); return manga; } diff --git a/Tranga/Tranga.cs b/Tranga/Tranga.cs index cd3b3bc..ca01d88 100644 --- a/Tranga/Tranga.cs +++ b/Tranga/Tranga.cs @@ -56,9 +56,11 @@ public partial class Tranga : GlobalBase public Manga? GetPublicationById(string internalId) { - if (cachedPublications.Exists(publication => publication.internalId == internalId)) - return cachedPublications.First(publication => publication.internalId == internalId); - return null; + return cachedPublications.TryGetValue(internalId, out Manga manga) switch + { + true => manga, + _ => null + }; } public bool TryGetPublicationById(string internalId, out Manga? manga) From 762da4c8598ff5978b3680f4d612ca3f09623fcd Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 22 Apr 2024 22:43:42 +0200 Subject: [PATCH 11/45] Make cachedPublications private with getter-setter --- Tranga/GlobalBase.cs | 25 ++++++++++++++++++++++++- Tranga/Jobs/JobBoss.cs | 4 ++-- Tranga/MangaConnectors/Bato.cs | 2 +- Tranga/MangaConnectors/MangaDex.cs | 2 +- Tranga/MangaConnectors/MangaKatana.cs | 2 +- Tranga/MangaConnectors/MangaLife.cs | 2 +- Tranga/MangaConnectors/Manganato.cs | 2 +- Tranga/MangaConnectors/Mangasee.cs | 2 +- Tranga/MangaConnectors/Mangaworld.cs | 2 +- Tranga/Tranga.cs | 9 +-------- 10 files changed, 34 insertions(+), 18 deletions(-) diff --git a/Tranga/GlobalBase.cs b/Tranga/GlobalBase.cs index 9258a6e..f372f2c 100644 --- a/Tranga/GlobalBase.cs +++ b/Tranga/GlobalBase.cs @@ -14,7 +14,7 @@ public abstract class GlobalBase protected TrangaSettings settings { get; init; } protected HashSet notificationConnectors { get; init; } protected HashSet libraryConnectors { get; init; } - protected Dictionary cachedPublications { get; init; } + private Dictionary cachedPublications { get; init; } public static readonly NumberFormatInfo numberFormatDecimalPoint = new (){ NumberDecimalSeparator = "." }; protected static readonly Regex baseUrlRex = new(@"https?:\/\/[0-9A-z\.-]+(:[0-9]+)?"); @@ -36,6 +36,29 @@ public abstract class GlobalBase this.cachedPublications = new(); } + protected void AddMangaToCache(Manga manga) + { + if (!this.cachedPublications.TryAdd(manga.internalId, manga)) + { + Log($"Overwriting Manga {manga.internalId}"); + this.cachedPublications[manga.internalId] = manga; + } + } + + protected Manga? GetCachedManga(string internalId) + { + return cachedPublications.TryGetValue(internalId, out Manga manga) switch + { + true => manga, + _ => null + }; + } + + protected IEnumerable GetAllCachedManga() + { + return cachedPublications.Values; + } + protected void Log(string message) { logger?.WriteLine(this.GetType().Name, message); diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 77cba2f..75e01a6 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -160,10 +160,10 @@ public class JobBoss : GlobalBase { this.jobs.FirstOrDefault(jjob => jjob.id == job.parentJobId)?.AddSubJob(job); if (job is DownloadNewChapters dncJob) - cachedPublications.Add(dncJob.manga.internalId, dncJob.manga); + AddMangaToCache(dncJob.manga); } - HashSet coverFileNames = cachedPublications.Select(manga => manga.Value.coverFileNameInCache!).ToHashSet(); + HashSet coverFileNames = GetAllCachedManga().Select(manga => manga.coverFileNameInCache!).ToHashSet(); foreach (string fileName in Directory.GetFiles(settings.coverImageCache)) //Cleanup Unused Covers { if(!coverFileNames.Any(existingManga => fileName.Contains(existingManga))) diff --git a/Tranga/MangaConnectors/Bato.cs b/Tranga/MangaConnectors/Bato.cs index 58582e7..f077ea9 100644 --- a/Tranga/MangaConnectors/Bato.cs +++ b/Tranga/MangaConnectors/Bato.cs @@ -116,7 +116,7 @@ public class Bato : MangaConnector Manga manga = new (sortName, authors, description, altTitles, tags, posterUrl, coverFileNameInCache, new Dictionary(), year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga.internalId, manga); + AddMangaToCache(manga); return manga; } diff --git a/Tranga/MangaConnectors/MangaDex.cs b/Tranga/MangaConnectors/MangaDex.cs index 135261b..6bcf22f 100644 --- a/Tranga/MangaConnectors/MangaDex.cs +++ b/Tranga/MangaConnectors/MangaDex.cs @@ -187,7 +187,7 @@ public class MangaDex : MangaConnector status, websiteUrl: $"https://mangadex.org/title/{publicationId}" ); - cachedPublications.Add(pub.internalId, pub); + AddMangaToCache(pub); return pub; } diff --git a/Tranga/MangaConnectors/MangaKatana.cs b/Tranga/MangaConnectors/MangaKatana.cs index 5996439..4976d28 100644 --- a/Tranga/MangaConnectors/MangaKatana.cs +++ b/Tranga/MangaConnectors/MangaKatana.cs @@ -143,7 +143,7 @@ public class MangaKatana : MangaConnector Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga.internalId, manga); + AddMangaToCache(manga); return manga; } diff --git a/Tranga/MangaConnectors/MangaLife.cs b/Tranga/MangaConnectors/MangaLife.cs index d54454f..b99e0e9 100644 --- a/Tranga/MangaConnectors/MangaLife.cs +++ b/Tranga/MangaConnectors/MangaLife.cs @@ -123,7 +123,7 @@ public class MangaLife : MangaConnector Manga manga = new(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga.internalId, manga); + AddMangaToCache(manga); return manga; } diff --git a/Tranga/MangaConnectors/Manganato.cs b/Tranga/MangaConnectors/Manganato.cs index 437f1b9..7fc5bec 100644 --- a/Tranga/MangaConnectors/Manganato.cs +++ b/Tranga/MangaConnectors/Manganato.cs @@ -133,7 +133,7 @@ public class Manganato : MangaConnector Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga.internalId, manga); + AddMangaToCache(manga); return manga; } diff --git a/Tranga/MangaConnectors/Mangasee.cs b/Tranga/MangaConnectors/Mangasee.cs index 32053b6..e1ce0c2 100644 --- a/Tranga/MangaConnectors/Mangasee.cs +++ b/Tranga/MangaConnectors/Mangasee.cs @@ -179,7 +179,7 @@ public class Mangasee : MangaConnector Manga manga = new(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga.internalId, manga); + AddMangaToCache(manga); return manga; } diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index fbea159..b5c1dc3 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -120,7 +120,7 @@ public class Mangaworld: MangaConnector Manga manga = new (sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl: websiteUrl); - cachedPublications.Add(manga.internalId, manga); + AddMangaToCache(manga); return manga; } diff --git a/Tranga/Tranga.cs b/Tranga/Tranga.cs index ca01d88..5413fa5 100644 --- a/Tranga/Tranga.cs +++ b/Tranga/Tranga.cs @@ -54,14 +54,7 @@ public partial class Tranga : GlobalBase return _connectors; } - public Manga? GetPublicationById(string internalId) - { - return cachedPublications.TryGetValue(internalId, out Manga manga) switch - { - true => manga, - _ => null - }; - } + public Manga? GetPublicationById(string internalId) => GetCachedManga(internalId); public bool TryGetPublicationById(string internalId, out Manga? manga) { From 51a26a3cbaadb29d7403773c69737ccda71f5d7e Mon Sep 17 00:00:00 2001 From: Glax Date: Tue, 23 Apr 2024 00:20:34 +0200 Subject: [PATCH 12/45] Fix https://github.com/C9Glax/tranga/issues/143 ImageCache could never find files, because they were not in the expected location. --- Tranga/MangaConnectors/MangaConnector.cs | 4 ++-- Tranga/Server.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 1aa7a50..fdbec1c 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -175,7 +175,7 @@ public abstract class MangaConnector : GlobalBase return; } - string fileInCache = Path.Join(settings.coverImageCache, manga.coverFileNameInCache); + string fileInCache = manga.coverFileNameInCache ?? Path.Join(settings.coverImageCache, manga.coverFileNameInCache); if (!File.Exists(fileInCache)) { Log($"Cloning cover failed: File missing {fileInCache}."); @@ -299,6 +299,6 @@ public abstract class MangaConnector : GlobalBase coverResult.result.CopyTo(ms); File.WriteAllBytes(saveImagePath, ms.ToArray()); Log($"Saving cover to {saveImagePath}"); - return filename; + return saveImagePath; } } \ No newline at end of file diff --git a/Tranga/Server.cs b/Tranga/Server.cs index 9b99c7a..77e2fba 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -122,7 +122,7 @@ public class Server : GlobalBase break; } - string filePath = settings.GetFullCoverPath((Manga)manga!); + string filePath = manga?.coverFileNameInCache ?? ""; if (File.Exists(filePath)) { FileStream coverStream = new(filePath, FileMode.Open); From 783f229a6ac4acf963a9e91dfed863b178bdefe6 Mon Sep 17 00:00:00 2001 From: Glax Date: Tue, 23 Apr 2024 00:48:08 +0200 Subject: [PATCH 13/45] Add LibraryConnector.Test to see if requests can be made to endpoint. --- Tranga/LibraryConnectors/Kavita.cs | 10 +++++++++- Tranga/LibraryConnectors/Komga.cs | 8 ++++++++ Tranga/LibraryConnectors/LibraryConnector.cs | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Tranga/LibraryConnectors/Kavita.cs b/Tranga/LibraryConnectors/Kavita.cs index 3fb8983..4dfb02b 100644 --- a/Tranga/LibraryConnectors/Kavita.cs +++ b/Tranga/LibraryConnectors/Kavita.cs @@ -67,7 +67,15 @@ public class Kavita : LibraryConnector foreach (KavitaLibrary lib in GetLibraries()) NetClient.MakePost($"{baseUrl}/api/Library/scan?libraryId={lib.id}", "Bearer", auth, logger); } - + + internal override bool Test() + { + foreach (KavitaLibrary lib in GetLibraries()) + if (NetClient.MakePost($"{baseUrl}/api/Library/scan?libraryId={lib.id}", "Bearer", auth, logger)) + return true; + return false; + } + /// /// Fetches all libraries available to the user /// diff --git a/Tranga/LibraryConnectors/Komga.cs b/Tranga/LibraryConnectors/Komga.cs index 7a0c485..557d934 100644 --- a/Tranga/LibraryConnectors/Komga.cs +++ b/Tranga/LibraryConnectors/Komga.cs @@ -32,6 +32,14 @@ public class Komga : LibraryConnector NetClient.MakePost($"{baseUrl}/api/v1/libraries/{lib.id}/scan", "Basic", auth, logger); } + internal override bool Test() + { + foreach (KomgaLibrary lib in GetLibraries()) + if (NetClient.MakePost($"{baseUrl}/api/v1/libraries/{lib.id}/scan", "Basic", auth, logger)) + return true; + return false; + } + /// /// Fetches all libraries available to the user /// diff --git a/Tranga/LibraryConnectors/LibraryConnector.cs b/Tranga/LibraryConnectors/LibraryConnector.cs index 0a66890..80dd5b2 100644 --- a/Tranga/LibraryConnectors/LibraryConnector.cs +++ b/Tranga/LibraryConnectors/LibraryConnector.cs @@ -30,6 +30,7 @@ public abstract class LibraryConnector : GlobalBase this.libraryType = libraryType; } public abstract void UpdateLibrary(); + internal abstract bool Test(); protected static class NetClient { From 58cff6513aee85e6677edc79d5a06162a7018dda Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 20:46:26 +0200 Subject: [PATCH 14/45] Possible fix #175 --- Tranga/Jobs/UpdateMetadata.cs | 1 + Tranga/Manga.cs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Tranga/Jobs/UpdateMetadata.cs b/Tranga/Jobs/UpdateMetadata.cs index 5471685..b855f14 100644 --- a/Tranga/Jobs/UpdateMetadata.cs +++ b/Tranga/Jobs/UpdateMetadata.cs @@ -35,6 +35,7 @@ public class UpdateMetadata : Job this.manga = manga.WithMetadata(updatedManga); this.manga.SaveSeriesInfoJson(settings.downloadLocation, true); + this.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga); this.progressToken.Complete(); } else diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 388d02d..4e079c5 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -20,7 +20,7 @@ public struct Manga public string[] tags { get; private set; } // ReSharper disable once UnusedAutoPropertyAccessor.Global public string? coverUrl { get; private set; } - public string? coverFileNameInCache { get; } + public string? coverFileNameInCache { get; private set; } // ReSharper disable once UnusedAutoPropertyAccessor.Global public Dictionary links { get; } // ReSharper disable once MemberCanBePrivate.Global @@ -88,7 +88,8 @@ public struct Manga status = newManga.status, releaseStatus = newManga.releaseStatus, websiteUrl = newManga.websiteUrl, - year = newManga.year + year = newManga.year, + coverFileNameInCache = coverFileNameInCache }; } @@ -103,6 +104,7 @@ public struct Manga this.sortName == compareManga.sortName && this.latestChapterAvailable.Equals(compareManga.latestChapterAvailable) && this.authors.All(a => compareManga.authors.Contains(a)) && + (this.coverFileNameInCache??"").Equals(compareManga.coverFileNameInCache) && (this.websiteUrl??"").Equals(compareManga.websiteUrl) && this.tags.All(t => compareManga.tags.Contains(t)); } From 7ec262a2e493b84c8a5e6be6182f263a2dd82357 Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 20:57:46 +0200 Subject: [PATCH 15/45] Possible fix #175 Export jobs when Manga-Metadata is updated. --- Tranga/Jobs/UpdateMetadata.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tranga/Jobs/UpdateMetadata.cs b/Tranga/Jobs/UpdateMetadata.cs index b855f14..97464f3 100644 --- a/Tranga/Jobs/UpdateMetadata.cs +++ b/Tranga/Jobs/UpdateMetadata.cs @@ -36,6 +36,8 @@ public class UpdateMetadata : Job this.manga = manga.WithMetadata(updatedManga); this.manga.SaveSeriesInfoJson(settings.downloadLocation, true); this.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga); + foreach (Job job in jobBoss.GetJobsLike(publication: manga)) + jobBoss.UpdateJobFile(job); this.progressToken.Complete(); } else From 8b62e2c4671f9d92593f350479074e2ef06aa703 Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 20:57:46 +0200 Subject: [PATCH 16/45] Possible fix #175 Export jobs when Manga-Metadata is updated. --- Tranga/Jobs/UpdateMetadata.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tranga/Jobs/UpdateMetadata.cs b/Tranga/Jobs/UpdateMetadata.cs index b855f14..97464f3 100644 --- a/Tranga/Jobs/UpdateMetadata.cs +++ b/Tranga/Jobs/UpdateMetadata.cs @@ -36,6 +36,8 @@ public class UpdateMetadata : Job this.manga = manga.WithMetadata(updatedManga); this.manga.SaveSeriesInfoJson(settings.downloadLocation, true); this.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga); + foreach (Job job in jobBoss.GetJobsLike(publication: manga)) + jobBoss.UpdateJobFile(job); this.progressToken.Complete(); } else From 4bd1150a0e212cdb085b4566a1baf2f644e9bf5e Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 21:03:44 +0200 Subject: [PATCH 17/45] fix bug Manga.WithMetadata coverfilenameincache not being replaced. --- Tranga/Manga.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 4e079c5..171daad 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -89,7 +89,7 @@ public struct Manga releaseStatus = newManga.releaseStatus, websiteUrl = newManga.websiteUrl, year = newManga.year, - coverFileNameInCache = coverFileNameInCache + coverFileNameInCache = newManga.coverFileNameInCache }; } From 9f9ea569d59cf7cfd84249881b1c4c769183cc6f Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 21:03:44 +0200 Subject: [PATCH 18/45] fix bug Manga.WithMetadata coverfilenameincache not being replaced. --- Tranga/Manga.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/Manga.cs b/Tranga/Manga.cs index 4e079c5..171daad 100644 --- a/Tranga/Manga.cs +++ b/Tranga/Manga.cs @@ -89,7 +89,7 @@ public struct Manga releaseStatus = newManga.releaseStatus, websiteUrl = newManga.websiteUrl, year = newManga.year, - coverFileNameInCache = coverFileNameInCache + coverFileNameInCache = newManga.coverFileNameInCache }; } From 6b91796e5a9b480748e9909dd44ce875e7e9fa6b Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 21:10:26 +0200 Subject: [PATCH 19/45] Update manga in DownloadNewChapters Jobs --- Tranga/Jobs/UpdateMetadata.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tranga/Jobs/UpdateMetadata.cs b/Tranga/Jobs/UpdateMetadata.cs index 97464f3..9fab4dc 100644 --- a/Tranga/Jobs/UpdateMetadata.cs +++ b/Tranga/Jobs/UpdateMetadata.cs @@ -36,8 +36,16 @@ public class UpdateMetadata : Job this.manga = manga.WithMetadata(updatedManga); this.manga.SaveSeriesInfoJson(settings.downloadLocation, true); this.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga); - foreach (Job job in jobBoss.GetJobsLike(publication: manga)) + foreach (Job job in jobBoss.GetJobsLike(publication: this.manga)) + { + if (job is DownloadNewChapters dc) + dc.manga = this.manga; + else if (job is UpdateMetadata um) + um.manga = this.manga; + else + continue; jobBoss.UpdateJobFile(job); + } this.progressToken.Complete(); } else From d52213002ee370d09714b3455bd156e9ee6736e2 Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 21:24:11 +0200 Subject: [PATCH 20/45] Delete old jobfiles. --- Tranga/Jobs/JobBoss.cs | 29 +++++++++++++++++++++-------- Tranga/Jobs/UpdateMetadata.cs | 9 ++++++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 75e01a6..637b3ae 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -171,18 +171,18 @@ public class JobBoss : GlobalBase } } - internal void UpdateJobFile(Job job) + internal void UpdateJobFile(Job job, string? oldFile = null) { - string jobFilePath = Path.Join(settings.jobsFolderPath, $"{job.id}.json"); + string newJobFilePath = Path.Join(settings.jobsFolderPath, $"{job.id}.json"); if (!this.jobs.Any(jjob => jjob.id == job.id)) { try { - Log($"Deleting Job-file {jobFilePath}"); - while(IsFileInUse(jobFilePath)) + Log($"Deleting Job-file {newJobFilePath}"); + while(IsFileInUse(newJobFilePath)) Thread.Sleep(10); - File.Delete(jobFilePath); + File.Delete(newJobFilePath); } catch (Exception e) { @@ -191,12 +191,25 @@ public class JobBoss : GlobalBase } else { - Log($"Exporting Job {jobFilePath}"); + Log($"Exporting Job {newJobFilePath}"); string jobStr = JsonConvert.SerializeObject(job); - while(IsFileInUse(jobFilePath)) + while(IsFileInUse(newJobFilePath)) Thread.Sleep(10); - File.WriteAllText(jobFilePath, jobStr); + File.WriteAllText(newJobFilePath, jobStr); } + + if(oldFile is not null) + try + { + Log($"Deleting old Job-file {oldFile}"); + while(IsFileInUse(oldFile)) + Thread.Sleep(10); + File.Delete(oldFile); + } + catch (Exception e) + { + Log(e.ToString()); + } } private void UpdateAllJobFiles() diff --git a/Tranga/Jobs/UpdateMetadata.cs b/Tranga/Jobs/UpdateMetadata.cs index 9fab4dc..4dc3ae7 100644 --- a/Tranga/Jobs/UpdateMetadata.cs +++ b/Tranga/Jobs/UpdateMetadata.cs @@ -38,13 +38,20 @@ public class UpdateMetadata : Job this.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga); foreach (Job job in jobBoss.GetJobsLike(publication: this.manga)) { + string oldFile; if (job is DownloadNewChapters dc) + { + oldFile = dc.id; dc.manga = this.manga; + } else if (job is UpdateMetadata um) + { + oldFile = um.id; um.manga = this.manga; + } else continue; - jobBoss.UpdateJobFile(job); + jobBoss.UpdateJobFile(job, oldFile); } this.progressToken.Complete(); } From c6d0168d2f37097dd06164fb8f4524b3c0bc007a Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 21:29:05 +0200 Subject: [PATCH 21/45] Fix #174 auth not being written to file for ntfy. --- Tranga/NotificationConnectors/Ntfy.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tranga/NotificationConnectors/Ntfy.cs b/Tranga/NotificationConnectors/Ntfy.cs index a7fed50..fef0f90 100644 --- a/Tranga/NotificationConnectors/Ntfy.cs +++ b/Tranga/NotificationConnectors/Ntfy.cs @@ -5,9 +5,9 @@ namespace Tranga.NotificationConnectors; public class Ntfy : NotificationConnector { - // ReSharper disable once MemberCanBePrivate.Global + // ReSharper disable twice MemberCanBePrivate.Global public string endpoint { get; init; } - private string auth { get; init; } + public string auth { get; init; } private const string Topic = "tranga"; private readonly HttpClient _client = new(); From 3d855020eb2706e47180bfc679d70284bfdadbcb Mon Sep 17 00:00:00 2001 From: Glax Date: Thu, 25 Apr 2024 21:32:48 +0200 Subject: [PATCH 22/45] Export job files indented. --- Tranga/Jobs/JobBoss.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 637b3ae..51fdece 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -192,7 +192,7 @@ public class JobBoss : GlobalBase else { Log($"Exporting Job {newJobFilePath}"); - string jobStr = JsonConvert.SerializeObject(job); + string jobStr = JsonConvert.SerializeObject(job, Formatting.Indented); while(IsFileInUse(newJobFilePath)) Thread.Sleep(10); File.WriteAllText(newJobFilePath, jobStr); From 4692cc297a3b0642864e110b55f66745b82d41a5 Mon Sep 17 00:00:00 2001 From: Glax Date: Fri, 26 Apr 2024 00:48:55 +0200 Subject: [PATCH 23/45] Fix MangaDex linksNode is null --- Tranga/MangaConnectors/MangaDex.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/MangaConnectors/MangaDex.cs b/Tranga/MangaConnectors/MangaDex.cs index 6bcf22f..7b3766a 100644 --- a/Tranga/MangaConnectors/MangaDex.cs +++ b/Tranga/MangaConnectors/MangaDex.cs @@ -115,7 +115,7 @@ public class MangaDex : MangaConnector }; Dictionary linksDict = new(); - if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode)) + if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode) && linksNode is not null) foreach (KeyValuePair linkKv in linksNode!.AsObject()) linksDict.TryAdd(linkKv.Key, linkKv.Value.GetValue()); From 2e8b896f3ba03d8edd6c481e51f8b186f9b8b6e6 Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 27 Apr 2024 17:53:08 +0200 Subject: [PATCH 24/45] Fix #178 wrong check on parsing variable aprilfoolsmode --- Tranga/Server.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/Server.cs b/Tranga/Server.cs index 77e2fba..bef0941 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -410,7 +410,7 @@ public class Server : GlobalBase break; case "Settings/AprilFoolsMode": if (!requestVariables.TryGetValue("enabled", out string? aprilFoolsModeEnabledStr) || - bool.TryParse(aprilFoolsModeEnabledStr, out bool aprilFoolsModeEnabled)) + !bool.TryParse(aprilFoolsModeEnabledStr, out bool aprilFoolsModeEnabled)) { SendResponse(HttpStatusCode.BadRequest, response); break; From fb0755eb89b4471c7a5b9897546d14aef1e5df9c Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 27 Apr 2024 19:09:12 +0200 Subject: [PATCH 25/45] Use NeedlemanWunsch for string comparison on Mangasee.cs Resolves #132 #167 --- README.md | 1 + Tranga/MangaConnectors/Mangasee.cs | 54 +++++++----------------------- Tranga/Tranga.csproj | 1 + 3 files changed, 14 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 05d6aee..6137396 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ That is why I wanted to create my own project, in a language I understand, and t - Newtonsoft.JSON - [PuppeteerSharp](https://www.puppeteersharp.com/) - [Html Agility Pack (HAP)](https://html-agility-pack.net/) +- [Soenneker.Utils.String.NeedlemanWunsch](https://github.com/soenneker/soenneker.utils.string.needlemanwunsch) - 💙 Blåhaj 🦈

(back to top)

diff --git a/Tranga/MangaConnectors/Mangasee.cs b/Tranga/MangaConnectors/Mangasee.cs index e1ce0c2..067f840 100644 --- a/Tranga/MangaConnectors/Mangasee.cs +++ b/Tranga/MangaConnectors/Mangasee.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using System.Xml.Linq; using HtmlAgilityPack; using Newtonsoft.Json; +using Soenneker.Utils.String.NeedlemanWunsch; using Tranga.Jobs; namespace Tranga.MangaConnectors; @@ -41,14 +42,6 @@ public class Mangasee : MangaConnector SearchResult[] filteredResults = FilteredResults(publicationTitle, searchResults); Log($"Total available manga: {searchResults.Length} Filtered down to: {filteredResults.Length}"); - /* - Dictionary levenshteinRelation = filteredResults.ToDictionary(result => result, - result => - { - Log($"Levenshtein {result.s}"); - return LevenshteinDistance(publicationTitle.Replace(" ", "").ToLower(), result.s.Replace(" ", "").ToLower()); - }); - Log($"After levenshtein: {levenshteinRelation.Count}");*/ string[] urls = filteredResults.Select(result => $"https://mangasee123.com/manga/{result.i}").ToArray(); List searchResultManga = new(); @@ -70,42 +63,19 @@ public class Mangasee : MangaConnector private SearchResult[] FilteredResults(string publicationTitle, SearchResult[] unfilteredSearchResults) { - string[] bannedStrings = {"a", "the", "of", "as", "to", "no", "for", "on", "with", "be", "and", "in", "wa", "at"}; - string[] cleanSplitPublicationTitle = publicationTitle.Split(' ') - .Where(part => part.Length > 0 && !bannedStrings.Contains(part.ToLower())).ToArray(); - - return unfilteredSearchResults.Where(usr => + Dictionary similarity = new(); + foreach (SearchResult sr in unfilteredSearchResults) { - string cleanSearchResultString = string.Join(' ', usr.s.Split(' ').Where(part => part.Length > 0 && !bannedStrings.Contains(part.ToLower()))); - foreach(string splitPublicationTitlePart in cleanSplitPublicationTitle) - if (cleanSearchResultString.Contains(splitPublicationTitlePart, StringComparison.InvariantCultureIgnoreCase) || - cleanSearchResultString.Contains(splitPublicationTitlePart, StringComparison.InvariantCultureIgnoreCase)) - return true; - return false; - }).ToArray(); - } - - private int LevenshteinDistance(string a, string b) - { - if (b.Length == 0) - return a.Length; - if (a.Length == 0) - return b.Length; - if (a[0] == b[0]) - return LevenshteinDistance(a[1..], b[1..]); - - int case1 = LevenshteinDistance(a, b[1..]); - int case2 = LevenshteinDistance(a[1..], b[1..]); - int case3 = LevenshteinDistance(a[1..], b); - - if (case1 < case2) - { - return 1 + (case1 < case3 ? case1 : case3); - } - else - { - return 1 + (case2 < case3 ? case2 : case3); + List scores = new(); + foreach (string se in sr.a) + scores.Add(NeedlemanWunschStringUtil.CalculateSimilarity(se.ToLower(), publicationTitle.ToLower())); + scores.Add(NeedlemanWunschStringUtil.CalculateSimilarity(sr.s.ToLower(), publicationTitle.ToLower())); + similarity.Add(sr, scores.Sum() / scores.Count); } + + SearchResult[] similarity90 = similarity.Where(s => s.Value < 10).Select(s => s.Key).ToArray(); + + return similarity90; } public override Manga? GetMangaFromId(string publicationId) diff --git a/Tranga/Tranga.csproj b/Tranga/Tranga.csproj index 2e4242b..47b765f 100644 --- a/Tranga/Tranga.csproj +++ b/Tranga/Tranga.csproj @@ -11,6 +11,7 @@ + From 1d44b6d9c6216ab0ffffe02b8a61464cc684d641 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 18:10:29 +0200 Subject: [PATCH 26/45] Log added Jobs during Startup --- Tranga/Jobs/JobBoss.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 51fdece..275864b 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -150,15 +150,23 @@ public class JobBoss : GlobalBase //Load json-job-files foreach (FileInfo file in new DirectoryInfo(settings.jobsFolderPath).EnumerateFiles().Where(fileInfo => idRex.IsMatch(fileInfo.Name))) { + Log($"Adding {file.Name}"); Job job = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors)))!; + Log($"Adding Job {job}"); this.jobs.Add(job); } //Connect jobs to parent-jobs and add Publications to cache foreach (Job job in this.jobs) { - this.jobs.FirstOrDefault(jjob => jjob.id == job.parentJobId)?.AddSubJob(job); + Log($"Loading Job {job}"); + Job? parentJob = this.jobs.FirstOrDefault(jjob => jjob.id == job.parentJobId); + if (parentJob is not null) + { + parentJob.AddSubJob(job); + Log($"Parent Job {parentJob}"); + } if (job is DownloadNewChapters dncJob) AddMangaToCache(dncJob.manga); } From e05684d5d13335e5c0d869430a0f46639f42b45c Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 18:22:51 +0200 Subject: [PATCH 27/45] Fix loading file results in "null"-job and crashes. --- Tranga/Jobs/JobBoss.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 275864b..c8cd28b 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -151,10 +151,19 @@ public class JobBoss : GlobalBase foreach (FileInfo file in new DirectoryInfo(settings.jobsFolderPath).EnumerateFiles().Where(fileInfo => idRex.IsMatch(fileInfo.Name))) { Log($"Adding {file.Name}"); - Job job = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), - new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors)))!; - Log($"Adding Job {job}"); - this.jobs.Add(job); + Job? job = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), + new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors))); + if (job is null) + { + string newName = file.FullName + ".failed"; + Log($"Failed loading file {file.Name}.\nMoving to {newName}"); + File.Move(file.FullName, newName); + } + else + { + Log($"Adding Job {job}"); + this.jobs.Add(job); + } } //Connect jobs to parent-jobs and add Publications to cache From f4996659efdfe5330dd3eb11f7e4919aba8bbc33 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 18:22:51 +0200 Subject: [PATCH 28/45] Fix loading file results in "null"-job and crashes. --- Tranga/Jobs/JobBoss.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index 275864b..c8cd28b 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -151,10 +151,19 @@ public class JobBoss : GlobalBase foreach (FileInfo file in new DirectoryInfo(settings.jobsFolderPath).EnumerateFiles().Where(fileInfo => idRex.IsMatch(fileInfo.Name))) { Log($"Adding {file.Name}"); - Job job = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), - new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors)))!; - Log($"Adding Job {job}"); - this.jobs.Add(job); + Job? job = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), + new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors))); + if (job is null) + { + string newName = file.FullName + ".failed"; + Log($"Failed loading file {file.Name}.\nMoving to {newName}"); + File.Move(file.FullName, newName); + } + else + { + Log($"Adding Job {job}"); + this.jobs.Add(job); + } } //Connect jobs to parent-jobs and add Publications to cache From 98bd8a983baae0f1752d7bb9e2b10cb185825ff0 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 18:34:45 +0200 Subject: [PATCH 29/45] Possible Fix #182 --- Tranga/MangaConnectors/MangaConnector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index fdbec1c..7038715 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -287,8 +287,9 @@ public abstract class MangaConnector : GlobalBase protected string SaveCoverImageToCache(string url, RequestType requestType) { - string filetype = url.Split('/')[^1].Split('?')[0].Split('.')[^1]; - string filename = $"{DateTime.Now.Ticks.ToString()}.{filetype}"; + Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9]+\.)+[a-zA-Z]+)\/(?:.+\/)*(.+\.[a-zA-Z]+)"); + Match match = urlRex.Match(url); + string filename = $"{match.Groups[1].Value}-{match.Groups[2].Value}"; string saveImagePath = Path.Join(settings.coverImageCache, filename); if (File.Exists(saveImagePath)) From e3211b95e2c5906b31be6b88d096b9e821d64880 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 18:46:40 +0200 Subject: [PATCH 30/45] #182 Remove covers that have no asssociated Manga --- Tranga/Jobs/JobBoss.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Tranga/Jobs/JobBoss.cs b/Tranga/Jobs/JobBoss.cs index c8cd28b..c5324d0 100644 --- a/Tranga/Jobs/JobBoss.cs +++ b/Tranga/Jobs/JobBoss.cs @@ -180,12 +180,9 @@ public class JobBoss : GlobalBase AddMangaToCache(dncJob.manga); } - HashSet coverFileNames = GetAllCachedManga().Select(manga => manga.coverFileNameInCache!).ToHashSet(); - foreach (string fileName in Directory.GetFiles(settings.coverImageCache)) //Cleanup Unused Covers - { - if(!coverFileNames.Any(existingManga => fileName.Contains(existingManga))) + string[] coverFiles = Directory.GetFiles(settings.coverImageCache); + foreach(string fileName in coverFiles.Where(fileName => !GetAllCachedManga().Any(manga => manga.coverFileNameInCache == fileName))) File.Delete(fileName); - } } internal void UpdateJobFile(Job job, string? oldFile = null) From 57f1e037efa6228ed59b4f0fdd0d9862efcf8f62 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 22:45:39 +0200 Subject: [PATCH 31/45] Corrected check for if cover exists --- Tranga/MangaConnectors/MangaConnector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 7038715..a1de35b 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -175,8 +175,8 @@ public abstract class MangaConnector : GlobalBase return; } - string fileInCache = manga.coverFileNameInCache ?? Path.Join(settings.coverImageCache, manga.coverFileNameInCache); - if (!File.Exists(fileInCache)) + string? fileInCache = manga.coverFileNameInCache; + if (fileInCache is null || !File.Exists(fileInCache)) { Log($"Cloning cover failed: File missing {fileInCache}."); if (retries > 0 && manga.coverUrl is not null) From 7ceb9cd4cb599523b55665514b2f60ac5f4a854f Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 22:51:46 +0200 Subject: [PATCH 32/45] #182 Changed filename to instead of remote filename have the format server-internalId.fileFormat --- Tranga/MangaConnectors/Bato.cs | 2 +- Tranga/MangaConnectors/MangaConnector.cs | 8 ++++---- Tranga/MangaConnectors/MangaDex.cs | 2 +- Tranga/MangaConnectors/MangaKatana.cs | 2 +- Tranga/MangaConnectors/MangaLife.cs | 2 +- Tranga/MangaConnectors/Manganato.cs | 2 +- Tranga/MangaConnectors/Mangasee.cs | 2 +- Tranga/MangaConnectors/Mangaworld.cs | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Tranga/MangaConnectors/Bato.cs b/Tranga/MangaConnectors/Bato.cs index f077ea9..d50081b 100644 --- a/Tranga/MangaConnectors/Bato.cs +++ b/Tranga/MangaConnectors/Bato.cs @@ -86,7 +86,7 @@ public class Bato : MangaConnector string posterUrl = document.DocumentNode.SelectNodes("//img") .First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&", "&"); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); List genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList(); string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray(); diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index a1de35b..9318ee4 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -182,7 +182,7 @@ public abstract class MangaConnector : GlobalBase if (retries > 0 && manga.coverUrl is not null) { Log($"Trying {retries} more times"); - SaveCoverImageToCache(manga.coverUrl, 0); + SaveCoverImageToCache(manga.coverUrl, manga.internalId, 0); CopyCoverFromCacheToDownloadLocation(manga, --retries); } @@ -285,11 +285,11 @@ public abstract class MangaConnector : GlobalBase return HttpStatusCode.OK; } - protected string SaveCoverImageToCache(string url, RequestType requestType) + protected string SaveCoverImageToCache(string url, string mangaInternalId, RequestType requestType) { - Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9]+\.)+[a-zA-Z]+)\/(?:.+\/)*(.+\.[a-zA-Z]+)"); + Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9]+\.)+[a-zA-Z]+)\/(?:.+\/)*(.+\.([a-zA-Z]+))"); Match match = urlRex.Match(url); - string filename = $"{match.Groups[1].Value}-{match.Groups[2].Value}"; + string filename = $"{match.Groups[1].Value}-{mangaInternalId}.{match.Groups[3].Value}"; string saveImagePath = Path.Join(settings.coverImageCache, filename); if (File.Exists(saveImagePath)) diff --git a/Tranga/MangaConnectors/MangaDex.cs b/Tranga/MangaConnectors/MangaDex.cs index 7b3766a..60f05fd 100644 --- a/Tranga/MangaConnectors/MangaDex.cs +++ b/Tranga/MangaConnectors/MangaDex.cs @@ -160,7 +160,7 @@ public class MangaDex : MangaConnector return null; string fileName = coverNode["attributes"]!["fileName"]!.GetValue(); string coverUrl = $"https://uploads.mangadex.org/covers/{publicationId}/{fileName}"; - string coverCacheName = SaveCoverImageToCache(coverUrl, RequestType.MangaCover); + string coverCacheName = SaveCoverImageToCache(coverUrl, publicationId, RequestType.MangaCover); List authors = new(); JsonNode?[] authorNodes = relationshipsNode.AsArray() diff --git a/Tranga/MangaConnectors/MangaKatana.cs b/Tranga/MangaConnectors/MangaKatana.cs index 4976d28..e2284c4 100644 --- a/Tranga/MangaConnectors/MangaKatana.cs +++ b/Tranga/MangaConnectors/MangaKatana.cs @@ -126,7 +126,7 @@ public class MangaKatana : MangaConnector string posterUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First() .GetAttributes().First(a => a.Name == "src").Value; - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); string description = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[3]/p").InnerText; while (description.StartsWith('\n')) diff --git a/Tranga/MangaConnectors/MangaLife.cs b/Tranga/MangaConnectors/MangaLife.cs index b99e0e9..43ca2e0 100644 --- a/Tranga/MangaConnectors/MangaLife.cs +++ b/Tranga/MangaConnectors/MangaLife.cs @@ -78,7 +78,7 @@ public class MangaLife : MangaConnector HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img"); string posterUrl = posterNode.GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1"); string sortName = titleNode.InnerText; diff --git a/Tranga/MangaConnectors/Manganato.cs b/Tranga/MangaConnectors/Manganato.cs index 7fc5bec..1f3a0e5 100644 --- a/Tranga/MangaConnectors/Manganato.cs +++ b/Tranga/MangaConnectors/Manganato.cs @@ -120,7 +120,7 @@ public class Manganato : MangaConnector string posterUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First() .GetAttributes().First(a => a.Name == "src").Value; - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description")) .InnerText.Replace("Description :", ""); diff --git a/Tranga/MangaConnectors/Mangasee.cs b/Tranga/MangaConnectors/Mangasee.cs index 067f840..2de2b2f 100644 --- a/Tranga/MangaConnectors/Mangasee.cs +++ b/Tranga/MangaConnectors/Mangasee.cs @@ -103,7 +103,7 @@ public class Mangasee : MangaConnector HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img"); string posterUrl = posterNode.GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1"); string sortName = titleNode.InnerText; diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index b5c1dc3..186a7c1 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -111,7 +111,7 @@ public class Mangaworld: MangaConnector string posterUrl = document.DocumentNode.SelectSingleNode("//img[@class='rounded']").GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); string description = document.DocumentNode.SelectSingleNode("//div[@id='noidungm']").InnerText; From 811ddd903f8708e60b564a82f217c0d02fc7614d Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 26 May 2024 23:04:16 +0200 Subject: [PATCH 33/45] fix missing minus-sign from domain namers in coverimages --- Tranga/MangaConnectors/MangaConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 9318ee4..21418b8 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -287,7 +287,7 @@ public abstract class MangaConnector : GlobalBase protected string SaveCoverImageToCache(string url, string mangaInternalId, RequestType requestType) { - Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9]+\.)+[a-zA-Z]+)\/(?:.+\/)*(.+\.([a-zA-Z]+))"); + Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9-]+\.)+[a-zA-Z0-9]+)\/(?:.+\/)*(.+\.([a-zA-Z]+))"); Match match = urlRex.Match(url); string filename = $"{match.Groups[1].Value}-{mangaInternalId}.{match.Groups[3].Value}"; string saveImagePath = Path.Join(settings.coverImageCache, filename); From ebe3012c694ea1006b2fae995007f90287e87c8c Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 1 Jun 2024 22:08:59 +0200 Subject: [PATCH 34/45] NTFY check endpoint URI and add optional custom topic #183 --- Tranga/NotificationConnectors/Ntfy.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Tranga/NotificationConnectors/Ntfy.cs b/Tranga/NotificationConnectors/Ntfy.cs index fef0f90..c725b8a 100644 --- a/Tranga/NotificationConnectors/Ntfy.cs +++ b/Tranga/NotificationConnectors/Ntfy.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Text.RegularExpressions; using Newtonsoft.Json; namespace Tranga.NotificationConnectors; @@ -8,7 +9,7 @@ public class Ntfy : NotificationConnector // ReSharper disable twice MemberCanBePrivate.Global public string endpoint { get; init; } public string auth { get; init; } - private const string Topic = "tranga"; + private readonly string _topic = "tranga"; private readonly HttpClient _client = new(); [JsonConstructor] @@ -16,19 +17,25 @@ public class Ntfy : NotificationConnector { if (!baseUrlRex.IsMatch(endpoint)) throw new ArgumentException("endpoint does not match pattern"); - this.endpoint = endpoint; + Regex rootUriRex = new(@"(https?:\/\/[a-zA-Z0-9-\.]+\.[a-zA-Z0-9]+)(?:\/([a-zA-Z0-9-\.]+))?.*"); + Match match = rootUriRex.Match(endpoint); + if(!match.Success) + Log($"Error getting URI from provided endpoint-URI: {endpoint}"); + this.endpoint = match.Groups[1].Value; + if (match.Groups[2].Success) + _topic = match.Groups[2].Value; this.auth = auth; } public override string ToString() { - return $"Ntfy {endpoint} {Topic}"; + return $"Ntfy {endpoint} {_topic}"; } public override void SendNotification(string title, string notificationText) { Log($"Sending notification: {title} - {notificationText}"); - MessageData message = new(title, notificationText); + MessageData message = new(title, _topic, notificationText); HttpRequestMessage request = new(HttpMethod.Post, $"{this.endpoint}?auth={this.auth}"); request.Content = new StringContent(JsonConvert.SerializeObject(message, Formatting.None), Encoding.UTF8, "application/json"); HttpResponseMessage response = _client.Send(request); @@ -47,9 +54,9 @@ public class Ntfy : NotificationConnector public string message { get; } public int priority { get; } - public MessageData(string title, string message) + public MessageData(string title, string topic, string message) { - this.topic = Topic; + this.topic = topic; this.title = title; this.message = message; this.priority = 3; From 7b9e935db7565b2d93e65405da0b08008f717fdc Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 1 Jun 2024 22:10:09 +0200 Subject: [PATCH 35/45] Commented optional second level only domains for cover-image-names --- Tranga/MangaConnectors/MangaConnector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 21418b8..809cc3b 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -288,6 +288,7 @@ public abstract class MangaConnector : GlobalBase protected string SaveCoverImageToCache(string url, string mangaInternalId, RequestType requestType) { Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9-]+\.)+[a-zA-Z0-9]+)\/(?:.+\/)*(.+\.([a-zA-Z]+))"); + //https?:\/\/[a-zA-Z0-9-]+\.([a-zA-Z0-9-]+\.[a-zA-Z0-9]+)\/(?:.+\/)*(.+\.([a-zA-Z]+)) for only second level domains Match match = urlRex.Match(url); string filename = $"{match.Groups[1].Value}-{mangaInternalId}.{match.Groups[3].Value}"; string saveImagePath = Path.Join(settings.coverImageCache, filename); From aea4c0c61b0c5623fb3b801582536b5b82e90557 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 2 Jun 2024 00:09:03 +0200 Subject: [PATCH 36/45] Add GlaxArguments to fetch Runtime-Args --- Tranga/Tranga.csproj | 1 + Tranga/TrangaArgs.cs | 132 ++++++++++--------------------------------- 2 files changed, 31 insertions(+), 102 deletions(-) diff --git a/Tranga/Tranga.csproj b/Tranga/Tranga.csproj index 47b765f..58cb6de 100644 --- a/Tranga/Tranga.csproj +++ b/Tranga/Tranga.csproj @@ -8,6 +8,7 @@ + diff --git a/Tranga/TrangaArgs.cs b/Tranga/TrangaArgs.cs index a4cf2ec..e95ad91 100644 --- a/Tranga/TrangaArgs.cs +++ b/Tranga/TrangaArgs.cs @@ -1,4 +1,5 @@ using Logging; +using GlaxArguments; namespace Tranga; @@ -7,46 +8,53 @@ public partial class Tranga : GlobalBase public static void Main(string[] args) { - Console.WriteLine(string.Join(' ', args)); - string[]? help = GetArg(args, ArgEnum.Help); - if (help is not null) - { - PrintHelp(); - return; - } + Argument downloadLocation = new (new[] { "-d", "--downloadLocation" }, 1, "Directory to which downloaded Manga are saved"); + Argument workingDirectory = new (new[] { "-w", "--workingDirectory" }, 1, "Directory in which application-data is saved"); + Argument consoleLogger = new (new []{"-c", "--consoleLogger"}, 0, "Enables the consoleLogger"); + Argument fileLogger = new (new []{"-f", "--fileLogger"}, 0, "Enables the fileLogger"); + Argument fPath = new (new []{"-l", "--fPath"}, 1, "Log Folder Path"); - string[]? consoleLogger = GetArg(args, ArgEnum.ConsoleLogger); - string[]? fileLogger = GetArg(args, ArgEnum.FileLogger); - string? directoryPath = GetArg(args, ArgEnum.FileLoggerPath)?[0]; + Argument[] arguments = new[] + { + downloadLocation, + workingDirectory, + consoleLogger, + fileLogger, + fPath + }; + ArgumentFetcher fetcher = new (arguments); + Dictionary fetched = fetcher.Fetch(args); + + string? directoryPath = fetched.TryGetValue(fPath, out string[]? path) ? path[0] : null; if (directoryPath is not null && !Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath); List enabledLoggers = new(); - if(consoleLogger is not null) + if(fetched.ContainsKey(consoleLogger)) enabledLoggers.Add(Logger.LoggerType.ConsoleLogger); - if (fileLogger is not null) + if (fetched.ContainsKey(fileLogger)) enabledLoggers.Add(Logger.LoggerType.FileLogger); Logger logger = new(enabledLoggers.ToArray(), Console.Out, Console.OutputEncoding, directoryPath); TrangaSettings? settings = null; - string[]? downloadLocationPath = GetArg(args, ArgEnum.DownloadLocation); - string[]? workingDirectory = GetArg(args, ArgEnum.WorkingDirectory); + bool dlp = fetched.TryGetValue(downloadLocation, out string[]? downloadLocationPath); + bool wdp = fetched.TryGetValue(downloadLocation, out string[]? workingDirectoryPath); - if (downloadLocationPath is not null && workingDirectory is not null) + if (dlp && wdp) { - settings = new TrangaSettings(downloadLocationPath[0], workingDirectory[0]); - }else if (downloadLocationPath is not null) + settings = new TrangaSettings(downloadLocationPath![0], workingDirectoryPath![0]); + }else if (dlp) { if (settings is null) - settings = new TrangaSettings(downloadLocation: downloadLocationPath[0]); + settings = new TrangaSettings(downloadLocation: downloadLocationPath![0]); else - settings = new TrangaSettings(downloadLocation: downloadLocationPath[0], settings.workingDirectory); - }else if (workingDirectory is not null) + settings = new TrangaSettings(downloadLocation: downloadLocationPath![0], settings.workingDirectory); + }else if (wdp) { if (settings is null) - settings = new TrangaSettings(downloadLocation: workingDirectory[0]); + settings = new TrangaSettings(downloadLocation: workingDirectoryPath![0]); else - settings = new TrangaSettings(settings.downloadLocation, workingDirectory[0]); + settings = new TrangaSettings(settings.downloadLocation, workingDirectoryPath![0]); } else { @@ -58,84 +66,4 @@ public partial class Tranga : GlobalBase Tranga _ = new (logger, settings); } - - private static void PrintHelp() - { - Console.WriteLine("Tranga-Help:"); - foreach (Argument argument in Arguments.Values) - { - foreach(string name in argument.names) - Console.Write("{0} ", name); - if(argument.parameterCount > 0) - Console.Write($"<{argument.parameterCount}>"); - Console.Write("\r\n {0}\r\n", argument.helpText); - } - } - - /// - /// Returns an array containing the parameters for the argument. - /// - /// List of argument-strings - /// Requested parameter - /// - /// If there are no parameters for an argument, returns an empty array. - /// If the argument is not found returns null. - /// - private static string[]? GetArg(string[] args, ArgEnum arg) - { - List argsList = args.ToList(); - List ret = new(); - foreach (string name in Arguments[arg].names) - { - int argIndex = argsList.IndexOf(name); - if (argIndex != -1) - { - if (Arguments[arg].parameterCount == 0) - return ret.ToArray(); - for (int parameterIndex = 1; parameterIndex <= Arguments[arg].parameterCount; parameterIndex++) - { - if(argIndex + parameterIndex >= argsList.Count || args[argIndex + parameterIndex].Contains('-'))//End of arguments, or no parameter provided, when one is required - Console.WriteLine($"No parameter provided for argument {name}. -h for help."); - ret.Add(args[argIndex + parameterIndex]); - } - } - } - return ret.Any() ? ret.ToArray() : null; - } - - private static readonly Dictionary Arguments = new() - { - { ArgEnum.DownloadLocation, new(new []{"-d", "--downloadLocation"}, 1, "Directory to which downloaded Manga are saved") }, - { ArgEnum.WorkingDirectory, new(new []{"-w", "--workingDirectory"}, 1, "Directory in which application-data is saved") }, - { ArgEnum.ConsoleLogger, new(new []{"-c", "--consoleLogger"}, 0, "Enables the consoleLogger") }, - { ArgEnum.FileLogger, new(new []{"-f", "--fileLogger"}, 0, "Enables the fileLogger") }, - { ArgEnum.FileLoggerPath, new (new []{"-l", "--fPath"}, 1, "Log Folder Path" ) }, - { ArgEnum.Help, new(new []{"-h", "--help"}, 0, "Print this") } - //{ ArgEnum., new(new []{""}, 1, "") } - }; - - internal enum ArgEnum - { - TrangaSettings, - DownloadLocation, - WorkingDirectory, - ConsoleLogger, - FileLogger, - FileLoggerPath, - Help - } - - private struct Argument - { - public string[] names { get; } - public byte parameterCount { get; } - public string helpText { get; } - - public Argument(string[] names, byte parameterCount, string helpText) - { - this.names = names; - this.parameterCount = parameterCount; - this.helpText = helpText; - } - } } \ No newline at end of file From 08dd01942f6ccfc234807f86e86ac04c146ba5ae Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 2 Jun 2024 00:23:16 +0200 Subject: [PATCH 37/45] #183 Fix NTFY not exporting topic to notificationConnectors.json --- Tranga/NotificationConnectors/Ntfy.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Tranga/NotificationConnectors/Ntfy.cs b/Tranga/NotificationConnectors/Ntfy.cs index c725b8a..13d220e 100644 --- a/Tranga/NotificationConnectors/Ntfy.cs +++ b/Tranga/NotificationConnectors/Ntfy.cs @@ -9,10 +9,9 @@ public class Ntfy : NotificationConnector // ReSharper disable twice MemberCanBePrivate.Global public string endpoint { get; init; } public string auth { get; init; } - private readonly string _topic = "tranga"; + public string topic { get; init; } private readonly HttpClient _client = new(); - [JsonConstructor] public Ntfy(GlobalBase clone, string endpoint, string auth) : base(clone, NotificationConnectorType.Ntfy) { if (!baseUrlRex.IsMatch(endpoint)) @@ -23,19 +22,29 @@ public class Ntfy : NotificationConnector Log($"Error getting URI from provided endpoint-URI: {endpoint}"); this.endpoint = match.Groups[1].Value; if (match.Groups[2].Success) - _topic = match.Groups[2].Value; + topic = match.Groups[2].Value; + else + topic = "tranga"; + this.auth = auth; + } + + [JsonConstructor] + public Ntfy(GlobalBase clone, string endpoint, string auth, string topic) : base(clone, NotificationConnectorType.Ntfy) + { + this.endpoint = endpoint; + this.topic = topic.Length > 0 ? topic : "tranga"; this.auth = auth; } public override string ToString() { - return $"Ntfy {endpoint} {_topic}"; + return $"Ntfy {endpoint} {topic}"; } public override void SendNotification(string title, string notificationText) { Log($"Sending notification: {title} - {notificationText}"); - MessageData message = new(title, _topic, notificationText); + MessageData message = new(title, topic, notificationText); HttpRequestMessage request = new(HttpMethod.Post, $"{this.endpoint}?auth={this.auth}"); request.Content = new StringContent(JsonConvert.SerializeObject(message, Formatting.None), Encoding.UTF8, "application/json"); HttpResponseMessage response = _client.Send(request); From 42feea3ad5c9ed9176459afd814fb094e0432e42 Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 2 Jun 2024 01:05:08 +0200 Subject: [PATCH 38/45] Fix covers returning wrong fileLocation if cover already exists. --- Tranga/MangaConnectors/MangaConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 809cc3b..630489b 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -294,7 +294,7 @@ public abstract class MangaConnector : GlobalBase string saveImagePath = Path.Join(settings.coverImageCache, filename); if (File.Exists(saveImagePath)) - return filename; + return saveImagePath; RequestResult coverResult = downloadClient.MakeRequest(url, requestType); using MemoryStream ms = new(); From d52ec8d36f9cdf62fe547bfdb5c947a17e22aa4d Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 15 Jun 2024 21:24:28 +0200 Subject: [PATCH 39/45] NTFY username and password usage instead of auth. --- Tranga/NotificationConnectors/Ntfy.cs | 49 +++++++++++++++++---------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/Tranga/NotificationConnectors/Ntfy.cs b/Tranga/NotificationConnectors/Ntfy.cs index 13d220e..f19fe34 100644 --- a/Tranga/NotificationConnectors/Ntfy.cs +++ b/Tranga/NotificationConnectors/Ntfy.cs @@ -11,30 +11,43 @@ public class Ntfy : NotificationConnector public string auth { get; init; } public string topic { get; init; } private readonly HttpClient _client = new(); - - public Ntfy(GlobalBase clone, string endpoint, string auth) : base(clone, NotificationConnectorType.Ntfy) - { - if (!baseUrlRex.IsMatch(endpoint)) - throw new ArgumentException("endpoint does not match pattern"); - Regex rootUriRex = new(@"(https?:\/\/[a-zA-Z0-9-\.]+\.[a-zA-Z0-9]+)(?:\/([a-zA-Z0-9-\.]+))?.*"); - Match match = rootUriRex.Match(endpoint); - if(!match.Success) - Log($"Error getting URI from provided endpoint-URI: {endpoint}"); - this.endpoint = match.Groups[1].Value; - if (match.Groups[2].Success) - topic = match.Groups[2].Value; - else - topic = "tranga"; - this.auth = auth; - } [JsonConstructor] - public Ntfy(GlobalBase clone, string endpoint, string auth, string topic) : base(clone, NotificationConnectorType.Ntfy) + public Ntfy(GlobalBase clone, string endpoint, string topic, string auth) : base(clone, NotificationConnectorType.Ntfy) { this.endpoint = endpoint; - this.topic = topic.Length > 0 ? topic : "tranga"; + this.topic = topic; this.auth = auth; } + + public Ntfy(GlobalBase clone, string endpoint, string username, string password, string? topic = null) : + this(clone, EndpointAndTopicFromUrl(endpoint)[0], topic??EndpointAndTopicFromUrl(endpoint)[1], AuthFromUsernamePassword(username, password)) + { + + } + + private static string AuthFromUsernamePassword(string username, string password) + { + string authHeader = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); + string authParam = Convert.ToBase64String(Encoding.UTF8.GetBytes(authHeader)).Replace("=",""); + return authParam; + } + + private static string[] EndpointAndTopicFromUrl(string url) + { + string[] ret = new string[2]; + if (!baseUrlRex.IsMatch(url)) + throw new ArgumentException("url does not match pattern"); + Regex rootUriRex = new(@"(https?:\/\/[a-zA-Z0-9-\.]+\.[a-zA-Z0-9]+)(?:\/([a-zA-Z0-9-\.]+))?.*"); + Match match = rootUriRex.Match(url); + if(!match.Success) + throw new ArgumentException($"Error getting URI from provided endpoint-URI: {url}"); + + ret[0] = match.Groups[1].Value; + ret[1] = match.Groups[2].Success && match.Groups[2].Value.Length > 0 ? match.Groups[2].Value : "tranga"; + + return ret; + } public override string ToString() { From 5614729eabedd95f2e8784b0c7b8c9233d4b5e94 Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 15 Jun 2024 21:33:42 +0200 Subject: [PATCH 40/45] #187 Server v1 NTFY username password --- Tranga/Server.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Tranga/Server.cs b/Tranga/Server.cs index 77e2fba..3ea53c7 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -492,12 +492,13 @@ public class Server : GlobalBase }else if (notificationConnectorType is NotificationConnector.NotificationConnectorType.Ntfy) { if (!requestVariables.TryGetValue("ntfyUrl", out string? ntfyUrl) || - !requestVariables.TryGetValue("ntfyAuth", out string? ntfyAuth)) + !requestVariables.TryGetValue("ntfyUser", out string? ntfyUser)|| + !requestVariables.TryGetValue("ntfyPass", out string? ntfyPass)) { SendResponse(HttpStatusCode.BadRequest, response); break; } - AddNotificationConnector(new Ntfy(this, ntfyUrl, ntfyAuth)); + AddNotificationConnector(new Ntfy(this, ntfyUrl, ntfyUser, ntfyPass, null)); SendResponse(HttpStatusCode.Accepted, response); } else @@ -534,12 +535,13 @@ public class Server : GlobalBase }else if (notificationConnectorType is NotificationConnector.NotificationConnectorType.Ntfy) { if (!requestVariables.TryGetValue("ntfyUrl", out string? ntfyUrl) || - !requestVariables.TryGetValue("ntfyAuth", out string? ntfyAuth)) + !requestVariables.TryGetValue("ntfyUser", out string? ntfyUser)|| + !requestVariables.TryGetValue("ntfyPass", out string? ntfyPass)) { SendResponse(HttpStatusCode.BadRequest, response); break; } - notificationConnector = new Ntfy(this, ntfyUrl, ntfyAuth); + notificationConnector = new Ntfy(this, ntfyUrl, ntfyUser, ntfyPass, null); } else { From 9d92069a4be7fb6813cec947afdebe4d3a49670a Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 15 Jun 2024 21:39:53 +0200 Subject: [PATCH 41/45] #187 NTFY JsonConverter --- .../NotificationConnectors/NotificationManagerJsonConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/NotificationConnectors/NotificationManagerJsonConverter.cs b/Tranga/NotificationConnectors/NotificationManagerJsonConverter.cs index c5963c2..6f310e1 100644 --- a/Tranga/NotificationConnectors/NotificationManagerJsonConverter.cs +++ b/Tranga/NotificationConnectors/NotificationManagerJsonConverter.cs @@ -28,7 +28,7 @@ public class NotificationManagerJsonConverter : JsonConverter case (byte)NotificationConnector.NotificationConnectorType.LunaSea: return new LunaSea(this._clone, jo.GetValue("id")!.Value()!); case (byte)NotificationConnector.NotificationConnectorType.Ntfy: - return new Ntfy(this._clone, jo.GetValue("endpoint")!.Value()!, jo.GetValue("auth")!.Value()!); + return new Ntfy(this._clone, jo.GetValue("endpoint")!.Value()!, jo.GetValue("topic")!.Value()!, jo.GetValue("auth")!.Value()!); } throw new Exception(); From 6db46463361b8451583b581d6f64f378cd262d30 Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 29 Jun 2024 19:11:18 +0200 Subject: [PATCH 42/45] Move/rename archives if volume number gets updated. --- Tranga/Chapter.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Tranga/Chapter.cs b/Tranga/Chapter.cs index a699cb5..3c47878 100644 --- a/Tranga/Chapter.cs +++ b/Tranga/Chapter.cs @@ -91,18 +91,22 @@ public readonly struct Chapter : IComparable { if (!Directory.Exists(Path.Join(downloadLocation, parentManga.folderName))) return false; - FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentManga.folderName)).GetFiles(); + FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentManga.folderName)).GetFiles().Where(file => file.Name.Split('.')[^1] == "cbz").ToArray(); Regex volChRex = new(@"(?:Vol(?:ume)?\.([0-9]+)\D*)?Ch(?:apter)?\.([0-9]+(?:\.[0-9]+)*)"); Chapter t = this; - return archives.Select(archive => archive.Name).Any(archiveFileName => + string thisPath = GetArchiveFilePath(downloadLocation); + FileInfo? archive = archives.FirstOrDefault(archive => { - Match m = volChRex.Match(archiveFileName); + Match m = volChRex.Match(archive.Name); string archiveVolNum = m.Groups[1].Success ? m.Groups[1].Value : "0"; string archiveChNum = m.Groups[2].Value; - return archiveVolNum == t.volumeNumber && - archiveChNum == t.chapterNumber; + return archiveVolNum == t.volumeNumber && archiveChNum == t.chapterNumber || + archiveVolNum == "0" && archiveChNum == t.chapterNumber; }); + if(archive is not null && thisPath != archive.FullName) + archive.MoveTo(thisPath); + return archive is not null; } /// /// Creates full file path of chapter-archive From 1dcfecd66f165c9fd6b0763da44a6ed73ea5bab6 Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 29 Jun 2024 19:14:37 +0200 Subject: [PATCH 43/45] Create CoverImageCache when saving coverimages. --- Tranga/MangaConnectors/MangaConnector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 630489b..6dbcb45 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -299,6 +299,7 @@ public abstract class MangaConnector : GlobalBase RequestResult coverResult = downloadClient.MakeRequest(url, requestType); using MemoryStream ms = new(); coverResult.result.CopyTo(ms); + Directory.CreateDirectory(settings.coverImageCache); File.WriteAllBytes(saveImagePath, ms.ToArray()); Log($"Saving cover to {saveImagePath}"); return saveImagePath; From a437fcbca1da81918b693967ec8f1635653a953e Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 29 Jun 2024 19:20:04 +0200 Subject: [PATCH 44/45] Possible fix https://github.com/C9Glax/tranga/issues/185 Mangaworld publication id had invalid path characters. --- Tranga/MangaConnectors/Mangaworld.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index 186a7c1..9558a73 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -111,7 +111,7 @@ public class Mangaworld: MangaConnector string posterUrl = document.DocumentNode.SelectSingleNode("//img[@class='rounded']").GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId, RequestType.MangaCover); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, publicationId.Replace('/', '-'), RequestType.MangaCover); string description = document.DocumentNode.SelectSingleNode("//div[@id='noidungm']").InnerText; From e368c3c98a0ef701a44bad7ba1a2f20031b5f129 Mon Sep 17 00:00:00 2001 From: Glax Date: Sat, 29 Jun 2024 19:37:02 +0200 Subject: [PATCH 45/45] Fix https://github.com/C9Glax/tranga/issues/193 Mangaworld Volume and Chapter number Parsing. --- Tranga/MangaConnectors/Mangaworld.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index 9558a73..d5725d7 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -153,10 +153,13 @@ public class Mangaworld: MangaConnector { foreach (HtmlNode volNode in document.DocumentNode.SelectNodes("//div[contains(concat(' ',normalize-space(@class),' '),'volume-element')]")) { - string volume = volNode.SelectNodes("div").First(node => node.HasClass("volume")).SelectSingleNode("p").InnerText.Split(' ')[^1]; + string volume = Regex.Match(volNode.SelectNodes("div").First(node => node.HasClass("volume")).SelectSingleNode("p").InnerText, + @"[Vv]olume ([0-9]+).*").Groups[1].Value; foreach (HtmlNode chNode in volNode.SelectNodes("div").First(node => node.HasClass("volume-chapters")).SelectNodes("div")) { - string number = chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText.Split(" ")[^1]; + + string number = Regex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText, + @"[Cc]apitolo ([0-9]+).*").Groups[1].Value; string url = chNode.SelectSingleNode("a").GetAttributeValue("href", ""); ret.Add(new Chapter(manga, null, volume, number, url)); }