diff --git a/API/Controllers/ConnectorController.cs b/API/Controllers/ConnectorController.cs index 4a1662d..5411db3 100644 --- a/API/Controllers/ConnectorController.cs +++ b/API/Controllers/ConnectorController.cs @@ -1,10 +1,8 @@ using API.Schema; -using API.Schema.Jobs; using API.Schema.MangaConnectors; using Asp.Versioning; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using Soenneker.Utils.String.NeedlemanWunsch; using static Microsoft.AspNetCore.Http.StatusCodes; namespace API.Controllers; @@ -36,21 +34,24 @@ public class ConnectorController(PgsqlContext context) : Controller [ProducesResponseType(Status500InternalServerError)] public IActionResult SearchMangaGlobal(string name) { - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> allManga = new(); + List<(Manga, List?, List?, List?, List?)> allManga = new(); foreach (MangaConnector contextMangaConnector in context.MangaConnectors) allManga.AddRange(contextMangaConnector.GetManga(name)); - foreach ((Manga? manga, Author[]? authors, MangaTag[]? tags, Link[]? links, MangaAltTitle[]? altTitles) in allManga) + List retMangas = new(); + foreach ((Manga? manga, List? authors, List? tags, List? links, List? altTitles) in allManga) { try { - AddMangaToContext(manga, authors, tags, links, altTitles); + Manga? add = AddMangaToContext(manga, authors, tags, links, altTitles); + if(add is not null) + retMangas.Add(add); } catch (DbUpdateException) { return StatusCode(500, new ProblemResponse("An error occurred while processing your request.")); } } - return Ok(allManga.Select(m => context.Manga.Find(m.Item1.MangaId)).ToArray()); + return Ok(retMangas.ToArray()); } /// @@ -68,31 +69,41 @@ public class ConnectorController(PgsqlContext context) : Controller MangaConnector? connector = context.MangaConnectors.Find(id); if (connector is null) return NotFound(new ProblemResponse("Connector not found.")); - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] mangas = connector.GetManga(name); - foreach ((Manga? manga, Author[]? authors, MangaTag[]? tags, Link[]? links, MangaAltTitle[]? altTitles) in mangas) + (Manga, List?, List?, List?, List?)[] mangas = connector.GetManga(name); + List retMangas = new(); + foreach ((Manga? manga, List? authors, List? tags, List? links, List? altTitles) in mangas) { try { - AddMangaToContext(manga, authors, tags, links, altTitles); + Manga? add = AddMangaToContext(manga, authors, tags, links, altTitles); + if(add is not null) + retMangas.Add(add); } - catch (DbUpdateException) + catch (DbUpdateException e) { - return StatusCode(500, new ProblemResponse("An error occurred while processing your request.")); + return StatusCode(500, new ProblemResponse("An error occurred while processing your request.", e.Message)); } } - return Ok(mangas.Select(m => context.Manga.Find(m.Item1.MangaId)).ToArray()); + return Ok(retMangas.ToArray()); } - private void AddMangaToContext(Manga? manga, Author[]? authors, MangaTag[]? tags, Link[]? links, - MangaAltTitle[]? altTitles) + private Manga? AddMangaToContext(Manga? manga, List? authors, List? tags, List? links, + List? altTitles) { if (manga is null) - return; - + return null; + Manga? existing = context.Manga.FirstOrDefault(m => m.ConnectorId == manga.ConnectorId); + if (tags is not null) { - IEnumerable newTags = tags.Where(mt => context.Tags.All(t => !t.Tag.Equals(mt.Tag))); + IEnumerable mergedTags = tags.Select(mt => + { + MangaTag? inDb = context.Tags.FirstOrDefault(t => t.Equals(mt)); + return inDb ?? mt; + }); + manga.Tags = mergedTags.ToList(); + IEnumerable newTags = manga.Tags.Where(mt => !context.Tags.Any(t => t.Tag.Equals(mt.Tag))); context.Tags.AddRange(newTags); } @@ -100,22 +111,50 @@ public class ConnectorController(PgsqlContext context) : Controller { IEnumerable mergedAuthors = authors.Select(ma => { - Author? inDb = context.Authors.FirstOrDefault(a => a.Equals(ma)); + Author? inDb = context.Authors.FirstOrDefault(a => a.AuthorName == ma.AuthorName); return inDb ?? ma; }); - manga.Authors = mergedAuthors.ToArray(); - IEnumerable newAuthors = authors.Where(ma => context.Authors.All(a => !a.Equals(ma))); + manga.Authors = mergedAuthors.ToList(); + IEnumerable newAuthors = manga.Authors.Where(ma => !context.Authors.Any(a => + a.AuthorName == ma.AuthorName)); context.Authors.AddRange(newAuthors); } if (links is not null) - context.Link.AddRange(links); + { + IEnumerable mergedLinks = links.Select(ml => + { + Link? inDb = context.Link.FirstOrDefault(l => + l.LinkProvider == ml.LinkProvider && l.LinkUrl == ml.LinkUrl); + return inDb ?? ml; + }); + manga.Links = mergedLinks.ToList(); + IEnumerable newLinks = manga.Links.Where(ml => !context.Link.Any(l => + l.LinkProvider == ml.LinkProvider && l.LinkUrl == ml.LinkUrl)); + context.Link.AddRange(newLinks); + } + + if (altTitles is not null) + { + IEnumerable mergedAltTitles = altTitles.Select(mat => + { + MangaAltTitle? inDb = context.AltTitles.FirstOrDefault(at => + at.Language == mat.Language && at.Title == mat.Title); + return inDb ?? mat; + }); + manga.AltTitles = mergedAltTitles.ToList(); + IEnumerable newAltTitles = manga.AltTitles.Where(mat => + !context.AltTitles.Any(at => at.Language == mat.Language && at.Title == mat.Title)); + context.AltTitles.AddRange(newAltTitles); + } - if(altTitles is not null) - context.AltTitles.AddRange(altTitles); - - context.Manga.Add(manga); + existing?.UpdateWithInfo(manga); + if(existing is not null) + context.Manga.Update(existing); + else + context.Manga.Add(manga); context.SaveChanges(); + return existing ?? manga; } } \ No newline at end of file diff --git a/API/Controllers/JobController.cs b/API/Controllers/JobController.cs index b6c249f..94b0e8a 100644 --- a/API/Controllers/JobController.cs +++ b/API/Controllers/JobController.cs @@ -19,25 +19,12 @@ public class JobController(PgsqlContext context) : Controller /// Array of Jobs [HttpPost("WithIDs")] [ProducesResponseType(Status200OK)] - public IActionResult GetJobs([FromBody] string[] ids) + public IActionResult GetJobs([FromBody]string[] ids) { Job[] ret = context.Jobs.Where(job => ids.Contains(job.JobId)).ToArray(); return Ok(ret); } - /// - /// Get all due Jobs (NextExecution > CurrentTime) - /// - /// Array of Jobs - [HttpGet("Due")] - [ProducesResponseType(Status200OK)] - public IActionResult GetDueJobs() - { - DateTime now = DateTime.Now.ToUniversalTime(); - Job[] dueJobs = context.Jobs.Where(job => job.NextExecution < now && job.state < JobState.Running).ToArray(); - return Ok(dueJobs); - } - /// /// Get all Jobs in requested State /// @@ -56,7 +43,7 @@ public class JobController(PgsqlContext context) : Controller /// /// Requested Job-Type /// Array of Jobs - [HttpPost("Type/{type}")] + [HttpGet("Type/{type}")] [ProducesResponseType(Status200OK)] public IActionResult GetJobsOfType(JobType type) { @@ -83,46 +70,71 @@ public class JobController(PgsqlContext context) : Controller } /// - /// Updates the State of a Job + /// Create a new CreateNewDownloadChapterJob /// - /// Job-ID - /// New State + /// ID of the Manga, and how often we check again /// Nothing - [HttpPatch("{id}/Status")] - [ProducesResponseType(Status200OK)] - [ProducesResponseType(Status404NotFound)] - [ProducesResponseType(Status500InternalServerError)] - public IActionResult UpdateJobStatus(string id, [FromBody]JobState state) + [HttpPut("NewDownloadChapterJob/{mangaId}")] + [ProducesResponseType(Status201Created)] + [ProducesResponseType(Status500InternalServerError)] + public IActionResult CreateNewDownloadChapterJob(string mangaId, [FromBody]ulong recurrenceTime) { + Job job = new DownloadNewChaptersJob(recurrenceTime, mangaId); + return AddJob(job); + } + + /// + /// Create a new DownloadSingleChapterJob + /// + /// ID of the Chapter + /// Nothing + [HttpPut("DownloadSingleChapterJob/{chapterId}")] + [ProducesResponseType(Status201Created)] + [ProducesResponseType(Status500InternalServerError)] + public IActionResult CreateNewDownloadChapterJob(string chapterId) + { + Job job = new DownloadSingleChapterJob(chapterId); + return AddJob(job); + } + + /// + /// Create a new UpdateMetadataJob + /// + /// ID of the Manga + /// Nothing + [HttpPut("UpdateMetadataJob/{mangaId}")] + [ProducesResponseType(Status201Created)] + [ProducesResponseType(Status500InternalServerError)] + public IActionResult CreateUpdateMetadataJob(string mangaId) + { + Job job = new UpdateMetadataJob(0, mangaId); + return AddJob(job); + } + + /// + /// Create a new UpdateMetadataJob for all Manga + /// + /// Nothing + [HttpPut("UpdateMetadataJob")] + [ProducesResponseType(Status201Created)] + [ProducesResponseType(Status500InternalServerError)] + public IActionResult CreateUpdateAllMetadataJob() + { + List ids = context.Manga.Select(m => m.MangaId).ToList(); + List jobs = ids.Select(id => new UpdateMetadataJob(0, id)).ToList(); try { - Job? ret = context.Jobs.Find(id); - switch (ret is not null) - { - case true: - ret.state = state; - context.Update(ret); - context.SaveChanges(); - return Ok(); - case false: return NotFound(); - } + context.Jobs.AddRange(jobs); + context.SaveChanges(); + return Created(); } catch (Exception e) { return StatusCode(500, e.Message); } - } - - /// - /// Create a new Job - /// - /// Job - /// Nothing - [HttpPut] - [ProducesResponseType(Status201Created)] - [ProducesResponseType(Status500InternalServerError)] - public IActionResult CreateJob([FromBody]Job job) + + private IActionResult AddJob(Job job) { try { diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs index a4162fc..6bffad8 100644 --- a/API/Controllers/MangaController.cs +++ b/API/Controllers/MangaController.cs @@ -71,59 +71,6 @@ public class MangaController(PgsqlContext context) : Controller } } - /// - /// Create new Manga - /// - /// Manga - /// Nothing - [HttpPut] - [ProducesResponseType(Status200OK)] - [ProducesResponseType(Status500InternalServerError)] - public IActionResult CreateManga([FromBody] Manga manga) - { - try - { - context.Manga.Add(manga); - context.SaveChanges(); - return Ok(); - } - catch (Exception e) - { - return StatusCode(500, e.Message); - } - } - - /// - /// Update Manga MetaData - /// - /// Manga-ID - /// New Manga-Info - /// Nothing - [HttpPatch("{id}")] - [ProducesResponseType(Status200OK)] - [ProducesResponseType(Status404NotFound)] - [ProducesResponseType(Status500InternalServerError)] - public IActionResult UpdateMangaMetadata(string id, [FromBody]Manga manga) - { - try - { - Manga? ret = context.Manga.Find(id); - switch (ret is not null) - { - case true: - ret.UpdateWithInfo(manga); - context.Update(ret); - context.SaveChanges(); - return Ok(); - case false: return NotFound(); - } - } - catch (Exception e) - { - return StatusCode(500, e.Message); - } - } - /// /// Returns URL of Cover of Manga /// @@ -152,37 +99,6 @@ public class MangaController(PgsqlContext context) : Controller Chapter[] ret = context.Chapters.Where(c => c.ParentManga.MangaId == m.MangaId).ToArray(); return Ok(ret); } - - /// - /// Adds/Creates new Chapter for Manga - /// - /// Manga-ID - /// Array of Chapters - /// Manga-ID and all Chapters have to be the same - /// Nothing - [HttpPut("{id}")] - [ProducesResponseType(Status200OK)] - [ProducesResponseType(Status404NotFound)] - [ProducesResponseType(Status500InternalServerError)] - public IActionResult CreateChapters(string id, [FromBody]Chapter[] chapters) - { - try - { - Manga? ret = context.Manga.Find(id); - if(ret is null) - return NotFound("Manga could not be found"); - if(chapters.All(c => c.ParentManga.MangaId == ret.MangaId)) - return BadRequest("Chapters belong to different Manga."); - - context.Chapters.AddRange(chapters); - context.SaveChanges(); - return Ok(); - } - catch (Exception e) - { - return StatusCode(500, e.Message); - } - } /// /// Returns the latest Chapter of requested Manga diff --git a/API/Schema/Author.cs b/API/Schema/Author.cs index 233e6c6..7b64e66 100644 --- a/API/Schema/Author.cs +++ b/API/Schema/Author.cs @@ -9,11 +9,4 @@ public class Author(string authorName) [MaxLength(64)] public string AuthorId { get; init; } = TokenGen.CreateToken(typeof(Author), 64); public string AuthorName { get; init; } = authorName; - - public override bool Equals(object? obj) - { - if (obj is not Author other) - return false; - return other.AuthorName == AuthorName; - } } \ No newline at end of file diff --git a/API/Schema/Chapter.cs b/API/Schema/Chapter.cs index 2de4f19..6c03c97 100644 --- a/API/Schema/Chapter.cs +++ b/API/Schema/Chapter.cs @@ -1,5 +1,4 @@ using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Linq; using API.Schema.Jobs; using Microsoft.EntityFrameworkCore; diff --git a/API/Schema/Link.cs b/API/Schema/Link.cs index af7dc7a..9a439dd 100644 --- a/API/Schema/Link.cs +++ b/API/Schema/Link.cs @@ -10,4 +10,11 @@ public class Link(string linkProvider, string linkUrl) public string LinkId { get; init; } = TokenGen.CreateToken(typeof(Link), 64); public string LinkProvider { get; init; } = linkProvider; public string LinkUrl { get; init; } = linkUrl; + + public override bool Equals(object? obj) + { + if (obj is not Link other) + return false; + return other.LinkProvider == LinkProvider && other.LinkUrl == LinkUrl; + } } \ No newline at end of file diff --git a/API/Schema/Manga.cs b/API/Schema/Manga.cs index d5afb4c..5a4cfb7 100644 --- a/API/Schema/Manga.cs +++ b/API/Schema/Manga.cs @@ -25,10 +25,10 @@ public class Manga( Chapter? latestChapterDownloaded, Chapter? latestChapterAvailable, MangaConnector mangaConnector, - Author[] authors, - MangaTag[] tags, - Link[] links, - MangaAltTitle[] altTitles) + ICollection authors, + ICollection tags, + ICollection links, + ICollection altTitles) { [MaxLength(64)] public string MangaId { get; init; } = TokenGen.CreateToken(typeof(Manga), 64); @@ -57,13 +57,11 @@ public class Manga( public ICollection Authors { get; internal set; } = authors; - public ICollection Tags { get; private set; } = tags; + public ICollection Tags { get; internal set; } = tags; - [ForeignKey("LinkIds")] - public ICollection Links { get; private set; } = links; + public ICollection Links { get; internal set; } = links; - [ForeignKey("AltTitleIds")] - public ICollection AltTitles { get; private set; } = altTitles; + public ICollection AltTitles { get; internal set; } = altTitles; public Manga(string connectorId, string name, string description, string websiteUrl, string coverUrl, string? coverFileNameInCache, diff --git a/API/Schema/MangaConnectors/AsuraToon.cs b/API/Schema/MangaConnectors/AsuraToon.cs index 4e3c5ee..2e1682c 100644 --- a/API/Schema/MangaConnectors/AsuraToon.cs +++ b/API/Schema/MangaConnectors/AsuraToon.cs @@ -12,7 +12,7 @@ public class AsuraToon : MangaConnector this.downloadClient = new ChromiumDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string requestUrl = $"https://asuracomic.net/series?name={sanitizedTitle}"; @@ -26,16 +26,16 @@ public class AsuraToon : MangaConnector return []; } - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://asuracomic.net/series/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) @@ -47,7 +47,7 @@ public class AsuraToon : MangaConnector return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { HtmlNodeCollection mangaList = document.DocumentNode.SelectNodes("//a[starts-with(@href,'series')]"); if (mangaList is null || mangaList.Count < 1) @@ -55,10 +55,10 @@ public class AsuraToon : MangaConnector IEnumerable urls = mangaList.Select(a => $"https://asuracomic.net/{a.GetAttributeValue("href", "")}"); - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + List<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -66,14 +66,14 @@ public class AsuraToon : MangaConnector return ret.ToArray(); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string? originalLanguage = null; Dictionary altTitles = new(), links = new(); HtmlNodeCollection genreNodes = document.DocumentNode.SelectNodes("//h3[text()='Genres']/../div/button"); string[] tags = genreNodes.Select(b => b.InnerText).ToArray(); - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); HtmlNode statusNode = document.DocumentNode.SelectSingleNode("//h3[text()='Status']/../h3[2]"); MangaReleaseStatus releaseStatus = statusNode.InnerText.ToLower() switch @@ -104,7 +104,7 @@ public class AsuraToon : MangaConnector IEnumerable authorNames = authorNodes is null ? [] : authorNodes.Select(a => a.InnerText); IEnumerable artistNames = artistNodes is null ? [] : artistNodes.Select(a => a.InnerText); List authorStrings = authorNames.Concat(artistNames).ToList(); - Author[] authors = authorStrings.Select(author => new Author(author)).ToArray(); + List authors = authorStrings.Select(author => new Author(author)).ToList(); HtmlNode? firstChapterNode = document.DocumentNode.SelectSingleNode("//a[contains(@href, 'chapter/1')]/../following-sibling::h3"); uint year = uint.Parse(firstChapterNode?.InnerText.Split(' ')[^1] ?? "2000"); diff --git a/API/Schema/MangaConnectors/Bato.cs b/API/Schema/MangaConnectors/Bato.cs index 5f4d08f..f5122dc 100644 --- a/API/Schema/MangaConnectors/Bato.cs +++ b/API/Schema/MangaConnectors/Bato.cs @@ -13,7 +13,7 @@ public class Bato : MangaConnector this.downloadClient = new HttpDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string requestUrl = $"https://bato.to/v3x-search?word={sanitizedTitle}&lang=en"; @@ -27,16 +27,16 @@ public class Bato : MangaConnector return []; } - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://bato.to/title/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) @@ -48,7 +48,7 @@ public class Bato : MangaConnector return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { HtmlNode mangaList = document.DocumentNode.SelectSingleNode("//div[@data-hk='0-0-2']"); if (!mangaList.ChildNodes.Any(node => node.Name == "div")) @@ -57,10 +57,10 @@ public class Bato : MangaConnector List urls = mangaList.ChildNodes .Select(node => $"https://bato.to{node.Descendants("div").First().FirstChild.GetAttributeValue("href", "")}").ToList(); - HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + HashSet<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -68,7 +68,7 @@ public class Bato : MangaConnector return ret.ToArray(); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { HtmlNode infoNode = document.DocumentNode.SelectSingleNode("/html/body/div/main/div[1]/div[2]"); @@ -78,18 +78,18 @@ public class Bato : MangaConnector string[] altTitlesList = infoNode.ChildNodes[1].ChildNodes[2].InnerText.Split('/'); int i = 0; - MangaAltTitle[] altTitles = altTitlesList.Select(a => new MangaAltTitle(i++.ToString(), a)).ToArray(); + List altTitles = altTitlesList.Select(a => new MangaAltTitle(i++.ToString(), a)).ToList(); string coverUrl = document.DocumentNode.SelectNodes("//img") .First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&", "&"); List genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList(); string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray(); - MangaTag[] mangaTags = tags.Select(s => new MangaTag(s)).ToArray(); + List mangaTags = tags.Select(s => new MangaTag(s)).ToList(); List authorsNodes = infoNode.ChildNodes[1].ChildNodes[3].Descendants("a").ToList(); List authorNames = authorsNodes.Select(node => node.InnerText.Replace("amp;", "")).ToList(); - Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); + List authors = authorNames.Select(n => new Author(n)).ToList(); HtmlNode? originalLanguageNode = document.DocumentNode.SelectSingleNode("//span[text()='Tr From']/.."); string originalLanguage = originalLanguageNode is not null ? originalLanguageNode.LastChild.InnerText : ""; diff --git a/API/Schema/MangaConnectors/MangaConnector.cs b/API/Schema/MangaConnectors/MangaConnector.cs index bed247d..2e52177 100644 --- a/API/Schema/MangaConnectors/MangaConnector.cs +++ b/API/Schema/MangaConnectors/MangaConnector.cs @@ -14,11 +14,11 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s public string[] SupportedLanguages { get; init; } = supportedLanguages; public string[] BaseUris { get; init; } = baseUris; - public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = ""); + public abstract (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = ""); - public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url); + public abstract (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url); - public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId); + public abstract (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId); public abstract Chapter[] GetChapters(Manga manga, string language="en"); diff --git a/API/Schema/MangaConnectors/MangaDex.cs b/API/Schema/MangaConnectors/MangaDex.cs index f7b9ce8..4a841dc 100644 --- a/API/Schema/MangaConnectors/MangaDex.cs +++ b/API/Schema/MangaConnectors/MangaDex.cs @@ -16,12 +16,12 @@ public class MangaDex : MangaConnector this.downloadClient = new HttpDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { const int limit = 100; //How many values we want returned at once int offset = 0; //"Page" int total = int.MaxValue; //How many total results are there, is updated on first request - HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> retManga = new(); + HashSet<(Manga, List?, List?, List?, List?)> retManga = new(); int loadedPublicationData = 0; List results = new(); @@ -59,7 +59,7 @@ public class MangaDex : MangaConnector return retManga.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { RequestResult requestResult = downloadClient.MakeRequest($"https://api.mangadex.org/manga/{publicationId}?includes%5B%5D=manga&includes%5B%5D=cover_art&includes%5B%5D=author&includes%5B%5D=artist&includes%5B%5D=tag", RequestType.MangaInfo); @@ -71,14 +71,14 @@ public class MangaDex : MangaConnector return null; } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { Regex idRex = new (@"https:\/\/mangadex.org\/title\/([A-z0-9-]*)\/.*"); string id = idRex.Match(url).Groups[1].Value; return GetMangaFromId(id); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? MangaFromJsonObject(JsonObject manga) + private (Manga, List?, List?, List?, List?)? MangaFromJsonObject(JsonObject manga) { if (!manga.TryGetPropertyValue("id", out JsonNode? idNode)) return null; @@ -105,7 +105,7 @@ public class MangaDex : MangaConnector altTitlesDict.TryAdd(altTitleNodeObject.First().Key, altTitleNodeObject.First().Value!.GetValue()); } } - MangaAltTitle[] altTitles = altTitlesDict.Select(t => new MangaAltTitle(t.Key, t.Value)).ToArray(); + List altTitles = altTitlesDict.Select(t => new MangaAltTitle(t.Key, t.Value)).ToList(); if (!attributes.TryGetPropertyValue("description", out JsonNode? descriptionNode)) return null; @@ -119,7 +119,7 @@ public class MangaDex : MangaConnector if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode) && linksNode is not null) foreach (KeyValuePair linkKv in linksNode!.AsObject()) linksDict.TryAdd(linkKv.Key, linkKv.Value.GetValue()); - Link[] links = linksDict.Select(x => new Link(x.Key, x.Value)).ToArray(); + List links = linksDict.Select(x => new Link(x.Key, x.Value)).ToList(); string? originalLanguage = attributes.TryGetPropertyValue("originalLanguage", out JsonNode? originalLanguageNode) switch @@ -151,7 +151,7 @@ public class MangaDex : MangaConnector if (attributes.TryGetPropertyValue("tags", out JsonNode? tagsNode)) foreach (JsonNode? tagNode in tagsNode!.AsArray()) tags.Add(tagNode!["attributes"]!["name"]!["en"]!.GetValue()); - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); if (!manga.TryGetPropertyValue("relationships", out JsonNode? relationshipsNode)) return null; @@ -172,7 +172,7 @@ public class MangaDex : MangaConnector if(!authorNames.Contains(authorName)) authorNames.Add(authorName); } - Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); + List authors = authorNames.Select(a => new Author(a)).ToList(); Manga pub = new (publicationId, sortName, description, $"https://mangadex.org/title/{publicationId}", coverUrl, null, year, originalLanguage, releaseStatus, -1, null, null, diff --git a/API/Schema/MangaConnectors/MangaHere.cs b/API/Schema/MangaConnectors/MangaHere.cs index cf37048..7320d2c 100644 --- a/API/Schema/MangaConnectors/MangaHere.cs +++ b/API/Schema/MangaConnectors/MangaHere.cs @@ -11,7 +11,7 @@ public class MangaHere : MangaConnector this.downloadClient = new ChromiumDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join('+', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string requestUrl = $"https://www.mangahere.cc/search?title={sanitizedTitle}"; @@ -20,11 +20,11 @@ public class MangaHere : MangaConnector if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null) return []; - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { if (document.DocumentNode.SelectNodes("//div[contains(concat(' ',normalize-space(@class),' '),' container ')]").Any(node => node.ChildNodes.Any(cNode => cNode.HasClass("search-keywords")))) return []; @@ -33,10 +33,10 @@ public class MangaHere : MangaConnector .SelectNodes("//a[contains(@href, '/manga/') and not(contains(@href, '.html'))]") .Select(thumb => $"https://www.mangahere.cc{thumb.GetAttributeValue("href", "")}").Distinct().ToList(); - HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + HashSet<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -44,12 +44,12 @@ public class MangaHere : MangaConnector return ret.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://www.mangahere.cc/manga/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); @@ -61,7 +61,7 @@ public class MangaHere : MangaConnector return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string originalLanguage = "", status = ""; Dictionary altTitles = new(), links = new(); @@ -77,13 +77,13 @@ public class MangaHere : MangaConnector .SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-say ')]/a") .Select(node => node.InnerText) .ToList(); - Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); + List authors = authorNames.Select(n => new Author(n)).ToList(); HashSet tags = document.DocumentNode .SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-tag-list ')]/a") .Select(node => node.InnerText) .ToHashSet(); - MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray(); + List mangaTags = tags.Select(n => new MangaTag(n)).ToList(); status = document.DocumentNode.SelectSingleNode("//span[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-title-tip ')]").InnerText; switch (status.ToLower()) diff --git a/API/Schema/MangaConnectors/MangaKatana.cs b/API/Schema/MangaConnectors/MangaKatana.cs index a4065c7..a4b38ad 100644 --- a/API/Schema/MangaConnectors/MangaKatana.cs +++ b/API/Schema/MangaConnectors/MangaKatana.cs @@ -11,7 +11,7 @@ public class MangaKatana : MangaConnector this.downloadClient = new HttpDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join("%20", Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name"; @@ -29,16 +29,16 @@ public class MangaKatana : MangaConnector return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1], requestResult.redirectedToUrl) }; } - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.result); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.result); return publications; } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://mangakatana.com/manga/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); @@ -47,7 +47,7 @@ public class MangaKatana : MangaConnector return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1], url); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(Stream html) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(Stream html) { StreamReader reader = new(html); string htmlString = reader.ReadToEnd(); @@ -63,10 +63,10 @@ public class MangaKatana : MangaConnector .First(a => a.Name == "href").Value); } - HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + HashSet<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -74,7 +74,7 @@ public class MangaKatana : MangaConnector return ret.ToArray(); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl) { StreamReader reader = new(html); string htmlString = reader.ReadToEnd(); @@ -135,9 +135,9 @@ public class MangaKatana : MangaConnector { year = uint.Parse(yearString); } - Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); - MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray(); - MangaAltTitle[] altTitles = altTitlesDict.Select(x => new MangaAltTitle(x.Key, x.Value)).ToArray(); + List authors = authorNames.Select(n => new Author(n)).ToList(); + List mangaTags = tags.Select(n => new MangaTag(n)).ToList(); + List altTitles = altTitlesDict.Select(x => new MangaAltTitle(x.Key, x.Value)).ToList(); Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year, originalLanguage, releaseStatus, -1, null, null, diff --git a/API/Schema/MangaConnectors/MangaLife.cs b/API/Schema/MangaConnectors/MangaLife.cs index 57770f8..ccbc6c2 100644 --- a/API/Schema/MangaConnectors/MangaLife.cs +++ b/API/Schema/MangaConnectors/MangaLife.cs @@ -12,7 +12,7 @@ public class MangaLife : MangaConnector this.downloadClient = new ChromiumDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = WebUtility.UrlEncode(publicationTitle); string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}"; @@ -23,16 +23,16 @@ public class MangaLife : MangaConnector if (requestResult.htmlDocument is null) return []; - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://manga4life.com/manga/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*"); string publicationId = publicationIdRex.Match(url).Groups[2].Value; @@ -43,7 +43,7 @@ public class MangaLife : MangaConnector return null; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { HtmlNode resultsNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']/div[last()]/div[1]/div"); if (resultsNode.Descendants("div").Count() == 1 && resultsNode.Descendants("div").First().HasClass("NoResults")) @@ -51,12 +51,12 @@ public class MangaLife : MangaConnector return []; } - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + List<(Manga, List?, List?, List?, List?)> ret = new(); foreach (HtmlNode resultNode in resultsNode.SelectNodes("div")) { string url = resultNode.Descendants().First(d => d.HasClass("SeriesName")).GetAttributeValue("href", ""); - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl($"https://manga4life.com{url}"); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl($"https://manga4life.com{url}"); if (manga is { } x) ret.Add(x); } @@ -65,7 +65,7 @@ public class MangaLife : MangaConnector } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string originalLanguage = "", status = ""; Dictionary altTitles = new(), links = new(); @@ -84,14 +84,14 @@ public class MangaLife : MangaConnector List authorNames = new(); foreach (HtmlNode authorNode in authorsNodes) authorNames.Add(authorNode.InnerText); - Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); + List authors = authorNames.Select(a => new Author(a)).ToList(); HtmlNode[] genreNodes = document.DocumentNode .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a") .ToArray(); foreach (HtmlNode genreNode in genreNodes) tags.Add(genreNode.InnerText); - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); HtmlNode yearNode = document.DocumentNode .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a") diff --git a/API/Schema/MangaConnectors/Manganato.cs b/API/Schema/MangaConnectors/Manganato.cs index 4078d55..22db030 100644 --- a/API/Schema/MangaConnectors/Manganato.cs +++ b/API/Schema/MangaConnectors/Manganato.cs @@ -13,7 +13,7 @@ public class Manganato : MangaConnector this.downloadClient = new HttpDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}"; @@ -21,11 +21,11 @@ public class Manganato : MangaConnector downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 ||requestResult.htmlDocument is null) return []; - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { List searchResults = document.DocumentNode.Descendants("div").Where(n => n.HasClass("search-story-item")).ToList(); List urls = new(); @@ -35,10 +35,10 @@ public class Manganato : MangaConnector .First(a => a.Name == "href").Value); } - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + List<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -46,12 +46,12 @@ public class Manganato : MangaConnector return ret.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://chapmanganato.com/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); @@ -63,7 +63,7 @@ public class Manganato : MangaConnector return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { Dictionary altTitlesDict = new(); Dictionary? links = null; @@ -111,9 +111,9 @@ public class Manganato : MangaConnector break; } } - Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); - MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray(); - MangaAltTitle[] mangaAltTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray(); + List authors = authorNames.Select(n => new Author(n)).ToList(); + List mangaTags = tags.Select(n => new MangaTag(n)).ToList(); + List mangaAltTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToList(); string coverUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First() .GetAttributes().First(a => a.Name == "src").Value; diff --git a/API/Schema/MangaConnectors/Mangasee.cs b/API/Schema/MangaConnectors/Mangasee.cs index ac0de54..2e29920 100644 --- a/API/Schema/MangaConnectors/Mangasee.cs +++ b/API/Schema/MangaConnectors/Mangasee.cs @@ -23,7 +23,7 @@ public class Mangasee : MangaConnector public string[] a { get; set; } } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string requestUrl = "https://mangasee123.com/_search.php"; RequestResult requestResult = @@ -41,10 +41,10 @@ public class Mangasee : MangaConnector string[] urls = filteredResults.Select(result => $"https://mangasee123.com/manga/{result.i}").ToArray(); - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> searchResultManga = new(); + List<(Manga, List?, List?, List?, List?)> searchResultManga = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? newManga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? newManga = GetMangaFromUrl(url); if(newManga is { } manga) searchResultManga.Add(manga); } @@ -79,12 +79,12 @@ public class Mangasee : MangaConnector return ret.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://mangasee123.com/manga/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*"); string publicationId = publicationIdRex.Match(url).Groups[1].Value; @@ -95,7 +95,7 @@ public class Mangasee : MangaConnector return null; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string originalLanguage = "", status = ""; Dictionary altTitles = new(), links = new(); @@ -114,14 +114,14 @@ public class Mangasee : MangaConnector List authorNames = new(); foreach (HtmlNode authorNode in authorsNodes) authorNames.Add(authorNode.InnerText); - Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); + List authors = authorNames.Select(a => new Author(a)).ToList(); HtmlNode[] genreNodes = document.DocumentNode .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a") .ToArray(); foreach (HtmlNode genreNode in genreNodes) tags.Add(genreNode.InnerText); - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); HtmlNode yearNode = document.DocumentNode .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a") diff --git a/API/Schema/MangaConnectors/Mangaworld.cs b/API/Schema/MangaConnectors/Mangaworld.cs index 762faea..3f33b4d 100644 --- a/API/Schema/MangaConnectors/Mangaworld.cs +++ b/API/Schema/MangaConnectors/Mangaworld.cs @@ -12,7 +12,7 @@ public class Mangaworld : MangaConnector this.downloadClient = new HttpDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string requestUrl = $"https://www.mangaworld.ac/archive?keyword={sanitizedTitle}"; @@ -23,11 +23,11 @@ public class Mangaworld : MangaConnector if (requestResult.htmlDocument is null) return []; - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { if (!document.DocumentNode.SelectSingleNode("//div[@class='comics-grid']").ChildNodes .Any(node => node.HasClass("entry"))) @@ -38,10 +38,10 @@ public class Mangaworld : MangaConnector "//div[@class='comics-grid']//div[@class='entry']//a[contains(concat(' ',normalize-space(@class),' '),'thumb')]") .Select(thumb => thumb.GetAttributeValue("href", "")).ToList(); - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + List<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -49,12 +49,12 @@ public class Mangaworld : MangaConnector return ret.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://www.mangaworld.ac/manga/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); @@ -69,10 +69,9 @@ public class Mangaworld : MangaConnector return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url); } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { Dictionary altTitlesDict = new(); - Dictionary? links = null; string originalLanguage = ""; MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased; @@ -86,17 +85,17 @@ public class Mangaworld : MangaConnector string[] alts = altTitlesNode.InnerText.Split(", "); for(int i = 0; i < alts.Length; i++) altTitlesDict.Add(i.ToString(), alts[i]); - MangaAltTitle[] altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray(); + List altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToList(); HtmlNode genresNode = metadata.SelectSingleNode("//span[text()='Generi: ' or text()='Genero: ']/.."); HashSet tags = genresNode.SelectNodes("a").Select(node => node.InnerText).ToHashSet(); - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); HtmlNode authorsNode = metadata.SelectSingleNode("//span[text()='Autore: ' or text()='Autori: ']/.."); string[] authorNames = authorsNode.SelectNodes("a").Select(node => node.InnerText).ToArray(); - Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); + List authors = authorNames.Select(n => new Author(n)).ToList(); string status = metadata.SelectSingleNode("//span[text()='Stato: ']/..").SelectNodes("a").First().InnerText; // ReSharper disable 5 times StringLiteralTypo diff --git a/API/Schema/MangaConnectors/ManhuaPlus.cs b/API/Schema/MangaConnectors/ManhuaPlus.cs index dbace83..be1fcb6 100644 --- a/API/Schema/MangaConnectors/ManhuaPlus.cs +++ b/API/Schema/MangaConnectors/ManhuaPlus.cs @@ -1,5 +1,4 @@ -using System.Net; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using API.MangaDownloadClients; using HtmlAgilityPack; @@ -12,7 +11,7 @@ public class ManhuaPlus : MangaConnector this.downloadClient = new ChromiumDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string requestUrl = $"https://manhuaplus.org/search?keyword={sanitizedTitle}"; @@ -21,11 +20,11 @@ public class ManhuaPlus : MangaConnector if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null) return []; - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { if (document.DocumentNode.SelectSingleNode("//h1/../..").ChildNodes//I already want to not. .Any(node => node.InnerText.Contains("No manga found"))) @@ -35,10 +34,10 @@ public class ManhuaPlus : MangaConnector .SelectNodes("//h1/../..//a[contains(@href, 'https://manhuaplus.org/manga/') and contains(concat(' ',normalize-space(@class),' '),' clamp ') and not(contains(@href, '/chapter'))]") .Select(mangaNode => mangaNode.GetAttributeValue("href", "")).ToList(); - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + List<(Manga, List?, List?, List?, List?)> ret = new(); foreach (string url in urls) { - (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); + (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) ret.Add(x); } @@ -46,12 +45,12 @@ public class ManhuaPlus : MangaConnector return ret.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://manhuaplus.org/manga/{publicationId}"); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { Regex publicationIdRex = new(@"https:\/\/manhuaplus.org\/manga\/(.*)(\/.*)*"); string publicationId = publicationIdRex.Match(url).Groups[1].Value; @@ -62,7 +61,7 @@ public class ManhuaPlus : MangaConnector return null; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { string originalLanguage = "", status = ""; Dictionary altTitles = new(), links = new(); @@ -88,7 +87,7 @@ public class ManhuaPlus : MangaConnector catch (ArgumentNullException e) { } - Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); + List authors = authorNames.Select(a => new Author(a)).ToList(); try { @@ -100,7 +99,7 @@ public class ManhuaPlus : MangaConnector catch (ArgumentNullException e) { } - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); Regex yearRex = new(@"(?:[0-9]{1,2}\/){2}([0-9]{2,4}) [0-9]{1,2}:[0-9]{1,2}"); HtmlNode yearNode = document.DocumentNode.SelectSingleNode("//aside//i[contains(concat(' ',normalize-space(@class),' '),' fa-clock ')]/../span"); diff --git a/API/Schema/MangaConnectors/WeebCentral.cs b/API/Schema/MangaConnectors/WeebCentral.cs index 067df88..74c4a5c 100644 --- a/API/Schema/MangaConnectors/WeebCentral.cs +++ b/API/Schema/MangaConnectors/WeebCentral.cs @@ -17,7 +17,7 @@ public class Weebcentral : MangaConnector downloadClient = new ChromiumDownloadClient(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") + public override (Manga, List?, List?, List?, List?)[] GetManga(string publicationTitle = "") { const int limit = 32; //How many values we want returned at once var offset = 0; //"Page" @@ -36,7 +36,7 @@ public class Weebcentral : MangaConnector return publications; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) + private (Manga, List?, List?, List?, List?)[] ParsePublicationsFromHtml(HtmlDocument document) { if (document.DocumentNode.SelectNodes("//article") == null) return []; @@ -44,7 +44,7 @@ public class Weebcentral : MangaConnector var urls = document.DocumentNode.SelectNodes("/html/body/article/a[@class='link link-hover']") .Select(elem => elem.GetAttributeValue("href", "")).ToList(); - List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); + List<(Manga, List?, List?, List?, List?)> ret = new(); foreach (var url in urls) { var manga = GetMangaFromUrl(url); @@ -55,7 +55,7 @@ public class Weebcentral : MangaConnector return ret.ToArray(); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) { Regex publicationIdRex = new(@"https:\/\/weebcentral\.com\/series\/(\w*)\/(.*)"); var publicationId = publicationIdRex.Match(url).Groups[1].Value; @@ -67,7 +67,7 @@ public class Weebcentral : MangaConnector return null; } - private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) + private (Manga, List?, List?, List?, List?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) { var posterNode = document.DocumentNode.SelectSingleNode("//section[@class='flex items-center justify-center']/picture/img"); @@ -79,12 +79,12 @@ public class Weebcentral : MangaConnector HtmlNode[] authorsNodes = document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Author(s): ']/span")?.ToArray() ?? []; var authorNames = authorsNodes.Select(n => n.InnerText).ToList(); - Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); + List authors = authorNames.Select(n => new Author(n)).ToList(); HtmlNode[] genreNodes = document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Tags(s): ']/span")?.ToArray() ?? []; HashSet tags = genreNodes.Select(n => n.InnerText).ToHashSet(); - MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); + List mangaTags = tags.Select(t => new MangaTag(t)).ToList(); var statusNode = document.DocumentNode.SelectSingleNode("//ul/li[strong/text() = 'Status: ']/a"); var status = statusNode?.InnerText ?? ""; @@ -108,7 +108,7 @@ public class Weebcentral : MangaConnector Dictionary altTitlesDict = new(), links = new(); for (var i = 0; i < altTitleNodes.Length; i++) altTitlesDict.Add(i.ToString(), altTitleNodes[i].InnerText); - MangaAltTitle[] altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray(); + List altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToList(); var originalLanguage = ""; @@ -123,7 +123,7 @@ public class Weebcentral : MangaConnector return (manga, authors, mangaTags, [], altTitles); } - public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) { return GetMangaFromUrl($"https://weebcentral.com/series/{publicationId}"); } diff --git a/API/Schema/MangaTag.cs b/API/Schema/MangaTag.cs index 3964f4c..0569106 100644 --- a/API/Schema/MangaTag.cs +++ b/API/Schema/MangaTag.cs @@ -1,5 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace API.Schema;