Fix Arrays shall not be added to context

This commit is contained in:
Glax 2024-12-16 22:54:23 +01:00
parent 729f018712
commit 60b128fc30
20 changed files with 249 additions and 288 deletions

View File

@ -1,10 +1,8 @@
using API.Schema; using API.Schema;
using API.Schema.Jobs;
using API.Schema.MangaConnectors; using API.Schema.MangaConnectors;
using Asp.Versioning; using Asp.Versioning;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Soenneker.Utils.String.NeedlemanWunsch;
using static Microsoft.AspNetCore.Http.StatusCodes; using static Microsoft.AspNetCore.Http.StatusCodes;
namespace API.Controllers; namespace API.Controllers;
@ -36,21 +34,24 @@ public class ConnectorController(PgsqlContext context) : Controller
[ProducesResponseType<Manga[]>(Status500InternalServerError)] [ProducesResponseType<Manga[]>(Status500InternalServerError)]
public IActionResult SearchMangaGlobal(string name) public IActionResult SearchMangaGlobal(string name)
{ {
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> allManga = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> allManga = new();
foreach (MangaConnector contextMangaConnector in context.MangaConnectors) foreach (MangaConnector contextMangaConnector in context.MangaConnectors)
allManga.AddRange(contextMangaConnector.GetManga(name)); allManga.AddRange(contextMangaConnector.GetManga(name));
foreach ((Manga? manga, Author[]? authors, MangaTag[]? tags, Link[]? links, MangaAltTitle[]? altTitles) in allManga) List<Manga> retMangas = new();
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in allManga)
{ {
try 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)
{ {
return StatusCode(500, new ProblemResponse("An error occurred while processing your request.")); 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());
} }
/// <summary> /// <summary>
@ -68,31 +69,41 @@ public class ConnectorController(PgsqlContext context) : Controller
MangaConnector? connector = context.MangaConnectors.Find(id); MangaConnector? connector = context.MangaConnectors.Find(id);
if (connector is null) if (connector is null)
return NotFound(new ProblemResponse("Connector not found.")); return NotFound(new ProblemResponse("Connector not found."));
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] mangas = connector.GetManga(name); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] mangas = connector.GetManga(name);
foreach ((Manga? manga, Author[]? authors, MangaTag[]? tags, Link[]? links, MangaAltTitle[]? altTitles) in mangas) List<Manga> retMangas = new();
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in mangas)
{ {
try 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, private Manga? AddMangaToContext(Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links,
MangaAltTitle[]? altTitles) List<MangaAltTitle>? altTitles)
{ {
if (manga is null) if (manga is null)
return; return null;
Manga? existing = context.Manga.FirstOrDefault(m => m.ConnectorId == manga.ConnectorId);
if (tags is not null) if (tags is not null)
{ {
IEnumerable<MangaTag> newTags = tags.Where(mt => context.Tags.All(t => !t.Tag.Equals(mt.Tag))); IEnumerable<MangaTag> mergedTags = tags.Select(mt =>
{
MangaTag? inDb = context.Tags.FirstOrDefault(t => t.Equals(mt));
return inDb ?? mt;
});
manga.Tags = mergedTags.ToList();
IEnumerable<MangaTag> newTags = manga.Tags.Where(mt => !context.Tags.Any(t => t.Tag.Equals(mt.Tag)));
context.Tags.AddRange(newTags); context.Tags.AddRange(newTags);
} }
@ -100,22 +111,50 @@ public class ConnectorController(PgsqlContext context) : Controller
{ {
IEnumerable<Author> mergedAuthors = authors.Select(ma => IEnumerable<Author> 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; return inDb ?? ma;
}); });
manga.Authors = mergedAuthors.ToArray(); manga.Authors = mergedAuthors.ToList();
IEnumerable<Author> newAuthors = authors.Where(ma => context.Authors.All(a => !a.Equals(ma))); IEnumerable<Author> newAuthors = manga.Authors.Where(ma => !context.Authors.Any(a =>
a.AuthorName == ma.AuthorName));
context.Authors.AddRange(newAuthors); context.Authors.AddRange(newAuthors);
} }
if (links is not null) if (links is not null)
context.Link.AddRange(links); {
IEnumerable<Link> 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<Link> 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) if (altTitles is not null)
context.AltTitles.AddRange(altTitles); {
IEnumerable<MangaAltTitle> 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<MangaAltTitle> newAltTitles = manga.AltTitles.Where(mat =>
!context.AltTitles.Any(at => at.Language == mat.Language && at.Title == mat.Title));
context.AltTitles.AddRange(newAltTitles);
}
context.Manga.Add(manga); existing?.UpdateWithInfo(manga);
if(existing is not null)
context.Manga.Update(existing);
else
context.Manga.Add(manga);
context.SaveChanges(); context.SaveChanges();
return existing ?? manga;
} }
} }

View File

@ -19,25 +19,12 @@ public class JobController(PgsqlContext context) : Controller
/// <returns>Array of Jobs</returns> /// <returns>Array of Jobs</returns>
[HttpPost("WithIDs")] [HttpPost("WithIDs")]
[ProducesResponseType<Job[]>(Status200OK)] [ProducesResponseType<Job[]>(Status200OK)]
public IActionResult GetJobs([FromBody] string[] ids) public IActionResult GetJobs([FromBody]string[] ids)
{ {
Job[] ret = context.Jobs.Where(job => ids.Contains(job.JobId)).ToArray(); Job[] ret = context.Jobs.Where(job => ids.Contains(job.JobId)).ToArray();
return Ok(ret); return Ok(ret);
} }
/// <summary>
/// Get all due Jobs (NextExecution > CurrentTime)
/// </summary>
/// <returns>Array of Jobs</returns>
[HttpGet("Due")]
[ProducesResponseType<Job[]>(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);
}
/// <summary> /// <summary>
/// Get all Jobs in requested State /// Get all Jobs in requested State
/// </summary> /// </summary>
@ -56,7 +43,7 @@ public class JobController(PgsqlContext context) : Controller
/// </summary> /// </summary>
/// <param name="type">Requested Job-Type</param> /// <param name="type">Requested Job-Type</param>
/// <returns>Array of Jobs</returns> /// <returns>Array of Jobs</returns>
[HttpPost("Type/{type}")] [HttpGet("Type/{type}")]
[ProducesResponseType<Job[]>(Status200OK)] [ProducesResponseType<Job[]>(Status200OK)]
public IActionResult GetJobsOfType(JobType type) public IActionResult GetJobsOfType(JobType type)
{ {
@ -83,46 +70,71 @@ public class JobController(PgsqlContext context) : Controller
} }
/// <summary> /// <summary>
/// Updates the State of a Job /// Create a new CreateNewDownloadChapterJob
/// </summary> /// </summary>
/// <param name="id">Job-ID</param> /// <param name="request">ID of the Manga, and how often we check again</param>
/// <param name="state">New State</param>
/// <returns>Nothing</returns> /// <returns>Nothing</returns>
[HttpPatch("{id}/Status")] [HttpPut("NewDownloadChapterJob/{mangaId}")]
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status201Created)]
[ProducesResponseType<string>(Status404NotFound)] [ProducesResponseType<string>(Status500InternalServerError)]
[ProducesResponseType(Status500InternalServerError)] public IActionResult CreateNewDownloadChapterJob(string mangaId, [FromBody]ulong recurrenceTime)
public IActionResult UpdateJobStatus(string id, [FromBody]JobState state)
{ {
Job job = new DownloadNewChaptersJob(recurrenceTime, mangaId);
return AddJob(job);
}
/// <summary>
/// Create a new DownloadSingleChapterJob
/// </summary>
/// <param name="chapterId">ID of the Chapter</param>
/// <returns>Nothing</returns>
[HttpPut("DownloadSingleChapterJob/{chapterId}")]
[ProducesResponseType(Status201Created)]
[ProducesResponseType<string>(Status500InternalServerError)]
public IActionResult CreateNewDownloadChapterJob(string chapterId)
{
Job job = new DownloadSingleChapterJob(chapterId);
return AddJob(job);
}
/// <summary>
/// Create a new UpdateMetadataJob
/// </summary>
/// <param name="mangaId">ID of the Manga</param>
/// <returns>Nothing</returns>
[HttpPut("UpdateMetadataJob/{mangaId}")]
[ProducesResponseType(Status201Created)]
[ProducesResponseType<string>(Status500InternalServerError)]
public IActionResult CreateUpdateMetadataJob(string mangaId)
{
Job job = new UpdateMetadataJob(0, mangaId);
return AddJob(job);
}
/// <summary>
/// Create a new UpdateMetadataJob for all Manga
/// </summary>
/// <returns>Nothing</returns>
[HttpPut("UpdateMetadataJob")]
[ProducesResponseType(Status201Created)]
[ProducesResponseType<string>(Status500InternalServerError)]
public IActionResult CreateUpdateAllMetadataJob()
{
List<string> ids = context.Manga.Select(m => m.MangaId).ToList();
List<UpdateMetadataJob> jobs = ids.Select(id => new UpdateMetadataJob(0, id)).ToList();
try try
{ {
Job? ret = context.Jobs.Find(id); context.Jobs.AddRange(jobs);
switch (ret is not null) context.SaveChanges();
{ return Created();
case true:
ret.state = state;
context.Update(ret);
context.SaveChanges();
return Ok();
case false: return NotFound();
}
} }
catch (Exception e) catch (Exception e)
{ {
return StatusCode(500, e.Message); return StatusCode(500, e.Message);
} }
} }
/// <summary> private IActionResult AddJob(Job job)
/// Create a new Job
/// </summary>
/// <param name="job">Job</param>
/// <returns>Nothing</returns>
[HttpPut]
[ProducesResponseType(Status201Created)]
[ProducesResponseType<string>(Status500InternalServerError)]
public IActionResult CreateJob([FromBody]Job job)
{ {
try try
{ {

View File

@ -71,59 +71,6 @@ public class MangaController(PgsqlContext context) : Controller
} }
} }
/// <summary>
/// Create new Manga
/// </summary>
/// <param name="manga">Manga</param>
/// <returns>Nothing</returns>
[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);
}
}
/// <summary>
/// Update Manga MetaData
/// </summary>
/// <param name="id">Manga-ID</param>
/// <param name="manga">New Manga-Info</param>
/// <returns>Nothing</returns>
[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);
}
}
/// <summary> /// <summary>
/// Returns URL of Cover of Manga /// Returns URL of Cover of Manga
/// </summary> /// </summary>
@ -153,37 +100,6 @@ public class MangaController(PgsqlContext context) : Controller
return Ok(ret); return Ok(ret);
} }
/// <summary>
/// Adds/Creates new Chapter for Manga
/// </summary>
/// <param name="id">Manga-ID</param>
/// <param name="chapters">Array of Chapters</param>
/// <remarks>Manga-ID and all Chapters have to be the same</remarks>
/// <returns>Nothing</returns>
[HttpPut("{id}")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType<string>(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);
}
}
/// <summary> /// <summary>
/// Returns the latest Chapter of requested Manga /// Returns the latest Chapter of requested Manga
/// </summary> /// </summary>

View File

@ -9,11 +9,4 @@ public class Author(string authorName)
[MaxLength(64)] [MaxLength(64)]
public string AuthorId { get; init; } = TokenGen.CreateToken(typeof(Author), 64); public string AuthorId { get; init; } = TokenGen.CreateToken(typeof(Author), 64);
public string AuthorName { get; init; } = authorName; public string AuthorName { get; init; } = authorName;
public override bool Equals(object? obj)
{
if (obj is not Author other)
return false;
return other.AuthorName == AuthorName;
}
} }

View File

@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Xml.Linq; using System.Xml.Linq;
using API.Schema.Jobs; using API.Schema.Jobs;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;

View File

@ -10,4 +10,11 @@ public class Link(string linkProvider, string linkUrl)
public string LinkId { get; init; } = TokenGen.CreateToken(typeof(Link), 64); public string LinkId { get; init; } = TokenGen.CreateToken(typeof(Link), 64);
public string LinkProvider { get; init; } = linkProvider; public string LinkProvider { get; init; } = linkProvider;
public string LinkUrl { get; init; } = linkUrl; 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;
}
} }

View File

@ -25,10 +25,10 @@ public class Manga(
Chapter? latestChapterDownloaded, Chapter? latestChapterDownloaded,
Chapter? latestChapterAvailable, Chapter? latestChapterAvailable,
MangaConnector mangaConnector, MangaConnector mangaConnector,
Author[] authors, ICollection<Author> authors,
MangaTag[] tags, ICollection<MangaTag> tags,
Link[] links, ICollection<Link> links,
MangaAltTitle[] altTitles) ICollection<MangaAltTitle> altTitles)
{ {
[MaxLength(64)] [MaxLength(64)]
public string MangaId { get; init; } = TokenGen.CreateToken(typeof(Manga), 64); public string MangaId { get; init; } = TokenGen.CreateToken(typeof(Manga), 64);
@ -57,13 +57,11 @@ public class Manga(
public ICollection<Author> Authors { get; internal set; } = authors; public ICollection<Author> Authors { get; internal set; } = authors;
public ICollection<MangaTag> Tags { get; private set; } = tags; public ICollection<MangaTag> Tags { get; internal set; } = tags;
[ForeignKey("LinkIds")] public ICollection<Link> Links { get; internal set; } = links;
public ICollection<Link> Links { get; private set; } = links;
[ForeignKey("AltTitleIds")] public ICollection<MangaAltTitle> AltTitles { get; internal set; } = altTitles;
public ICollection<MangaAltTitle> AltTitles { get; private set; } = altTitles;
public Manga(string connectorId, string name, string description, string websiteUrl, string coverUrl, public Manga(string connectorId, string name, string description, string websiteUrl, string coverUrl,
string? coverFileNameInCache, string? coverFileNameInCache,

View File

@ -12,7 +12,7 @@ public class AsuraToon : MangaConnector
this.downloadClient = new ChromiumDownloadClient(); this.downloadClient = new ChromiumDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower();
string requestUrl = $"https://asuracomic.net/series?name={sanitizedTitle}"; string requestUrl = $"https://asuracomic.net/series?name={sanitizedTitle}";
@ -26,16 +26,16 @@ public class AsuraToon : MangaConnector
return []; return [];
} }
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://asuracomic.net/series/{publicationId}"); return GetMangaFromUrl($"https://asuracomic.net/series/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) 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); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
HtmlNodeCollection mangaList = document.DocumentNode.SelectNodes("//a[starts-with(@href,'series')]"); HtmlNodeCollection mangaList = document.DocumentNode.SelectNodes("//a[starts-with(@href,'series')]");
if (mangaList is null || mangaList.Count < 1) if (mangaList is null || mangaList.Count < 1)
@ -55,10 +55,10 @@ public class AsuraToon : MangaConnector
IEnumerable<string> urls = mangaList.Select(a => $"https://asuracomic.net/{a.GetAttributeValue("href", "")}"); IEnumerable<string> urls = mangaList.Select(a => $"https://asuracomic.net/{a.GetAttributeValue("href", "")}");
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -66,14 +66,14 @@ public class AsuraToon : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
string? originalLanguage = null; string? originalLanguage = null;
Dictionary<string, string> altTitles = new(), links = new(); Dictionary<string, string> altTitles = new(), links = new();
HtmlNodeCollection genreNodes = document.DocumentNode.SelectNodes("//h3[text()='Genres']/../div/button"); HtmlNodeCollection genreNodes = document.DocumentNode.SelectNodes("//h3[text()='Genres']/../div/button");
string[] tags = genreNodes.Select(b => b.InnerText).ToArray(); string[] tags = genreNodes.Select(b => b.InnerText).ToArray();
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> mangaTags = tags.Select(t => new MangaTag(t)).ToList();
HtmlNode statusNode = document.DocumentNode.SelectSingleNode("//h3[text()='Status']/../h3[2]"); HtmlNode statusNode = document.DocumentNode.SelectSingleNode("//h3[text()='Status']/../h3[2]");
MangaReleaseStatus releaseStatus = statusNode.InnerText.ToLower() switch MangaReleaseStatus releaseStatus = statusNode.InnerText.ToLower() switch
@ -104,7 +104,7 @@ public class AsuraToon : MangaConnector
IEnumerable<string> authorNames = authorNodes is null ? [] : authorNodes.Select(a => a.InnerText); IEnumerable<string> authorNames = authorNodes is null ? [] : authorNodes.Select(a => a.InnerText);
IEnumerable<string> artistNames = artistNodes is null ? [] : artistNodes.Select(a => a.InnerText); IEnumerable<string> artistNames = artistNodes is null ? [] : artistNodes.Select(a => a.InnerText);
List<string> authorStrings = authorNames.Concat(artistNames).ToList(); List<string> authorStrings = authorNames.Concat(artistNames).ToList();
Author[] authors = authorStrings.Select(author => new Author(author)).ToArray(); List<Author> authors = authorStrings.Select(author => new Author(author)).ToList();
HtmlNode? firstChapterNode = document.DocumentNode.SelectSingleNode("//a[contains(@href, 'chapter/1')]/../following-sibling::h3"); HtmlNode? firstChapterNode = document.DocumentNode.SelectSingleNode("//a[contains(@href, 'chapter/1')]/../following-sibling::h3");
uint year = uint.Parse(firstChapterNode?.InnerText.Split(' ')[^1] ?? "2000"); uint year = uint.Parse(firstChapterNode?.InnerText.Split(' ')[^1] ?? "2000");

View File

@ -13,7 +13,7 @@ public class Bato : MangaConnector
this.downloadClient = new HttpDownloadClient(); this.downloadClient = new HttpDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); 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"; string requestUrl = $"https://bato.to/v3x-search?word={sanitizedTitle}&lang=en";
@ -27,16 +27,16 @@ public class Bato : MangaConnector
return []; return [];
} }
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://bato.to/title/{publicationId}"); return GetMangaFromUrl($"https://bato.to/title/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) 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); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
HtmlNode mangaList = document.DocumentNode.SelectSingleNode("//div[@data-hk='0-0-2']"); HtmlNode mangaList = document.DocumentNode.SelectSingleNode("//div[@data-hk='0-0-2']");
if (!mangaList.ChildNodes.Any(node => node.Name == "div")) if (!mangaList.ChildNodes.Any(node => node.Name == "div"))
@ -57,10 +57,10 @@ public class Bato : MangaConnector
List<string> urls = mangaList.ChildNodes List<string> urls = mangaList.ChildNodes
.Select(node => $"https://bato.to{node.Descendants("div").First().FirstChild.GetAttributeValue("href", "")}").ToList(); .Select(node => $"https://bato.to{node.Descendants("div").First().FirstChild.GetAttributeValue("href", "")}").ToList();
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); HashSet<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -68,7 +68,7 @@ public class Bato : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
HtmlNode infoNode = document.DocumentNode.SelectSingleNode("/html/body/div/main/div[1]/div[2]"); 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('/'); string[] altTitlesList = infoNode.ChildNodes[1].ChildNodes[2].InnerText.Split('/');
int i = 0; int i = 0;
MangaAltTitle[] altTitles = altTitlesList.Select(a => new MangaAltTitle(i++.ToString(), a)).ToArray(); List<MangaAltTitle> altTitles = altTitlesList.Select(a => new MangaAltTitle(i++.ToString(), a)).ToList();
string coverUrl = document.DocumentNode.SelectNodes("//img") string coverUrl = document.DocumentNode.SelectNodes("//img")
.First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&amp;", "&"); .First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&amp;", "&");
List<HtmlNode> genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList(); List<HtmlNode> genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList();
string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray(); string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray();
MangaTag[] mangaTags = tags.Select(s => new MangaTag(s)).ToArray(); List<MangaTag> mangaTags = tags.Select(s => new MangaTag(s)).ToList();
List<HtmlNode> authorsNodes = infoNode.ChildNodes[1].ChildNodes[3].Descendants("a").ToList(); List<HtmlNode> authorsNodes = infoNode.ChildNodes[1].ChildNodes[3].Descendants("a").ToList();
List<string> authorNames = authorsNodes.Select(node => node.InnerText.Replace("amp;", "")).ToList(); List<string> authorNames = authorsNodes.Select(node => node.InnerText.Replace("amp;", "")).ToList();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); List<Author> authors = authorNames.Select(n => new Author(n)).ToList();
HtmlNode? originalLanguageNode = document.DocumentNode.SelectSingleNode("//span[text()='Tr From']/.."); HtmlNode? originalLanguageNode = document.DocumentNode.SelectSingleNode("//span[text()='Tr From']/..");
string originalLanguage = originalLanguageNode is not null ? originalLanguageNode.LastChild.InnerText : ""; string originalLanguage = originalLanguageNode is not null ? originalLanguageNode.LastChild.InnerText : "";

View File

@ -14,11 +14,11 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s
public string[] SupportedLanguages { get; init; } = supportedLanguages; public string[] SupportedLanguages { get; init; } = supportedLanguages;
public string[] BaseUris { get; init; } = baseUris; public string[] BaseUris { get; init; } = baseUris;
public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = ""); public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "");
public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url); public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url);
public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId); public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId);
public abstract Chapter[] GetChapters(Manga manga, string language="en"); public abstract Chapter[] GetChapters(Manga manga, string language="en");

View File

@ -16,12 +16,12 @@ public class MangaDex : MangaConnector
this.downloadClient = new HttpDownloadClient(); this.downloadClient = new HttpDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
const int limit = 100; //How many values we want returned at once const int limit = 100; //How many values we want returned at once
int offset = 0; //"Page" int offset = 0; //"Page"
int total = int.MaxValue; //How many total results are there, is updated on first request 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<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> retManga = new();
int loadedPublicationData = 0; int loadedPublicationData = 0;
List<JsonNode> results = new(); List<JsonNode> results = new();
@ -59,7 +59,7 @@ public class MangaDex : MangaConnector
return retManga.ToArray(); return retManga.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
RequestResult requestResult = 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); 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; return null;
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
Regex idRex = new (@"https:\/\/mangadex.org\/title\/([A-z0-9-]*)\/.*"); Regex idRex = new (@"https:\/\/mangadex.org\/title\/([A-z0-9-]*)\/.*");
string id = idRex.Match(url).Groups[1].Value; string id = idRex.Match(url).Groups[1].Value;
return GetMangaFromId(id); return GetMangaFromId(id);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? MangaFromJsonObject(JsonObject manga) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? MangaFromJsonObject(JsonObject manga)
{ {
if (!manga.TryGetPropertyValue("id", out JsonNode? idNode)) if (!manga.TryGetPropertyValue("id", out JsonNode? idNode))
return null; return null;
@ -105,7 +105,7 @@ public class MangaDex : MangaConnector
altTitlesDict.TryAdd(altTitleNodeObject.First().Key, altTitleNodeObject.First().Value!.GetValue<string>()); altTitlesDict.TryAdd(altTitleNodeObject.First().Key, altTitleNodeObject.First().Value!.GetValue<string>());
} }
} }
MangaAltTitle[] altTitles = altTitlesDict.Select(t => new MangaAltTitle(t.Key, t.Value)).ToArray(); List<MangaAltTitle> altTitles = altTitlesDict.Select(t => new MangaAltTitle(t.Key, t.Value)).ToList();
if (!attributes.TryGetPropertyValue("description", out JsonNode? descriptionNode)) if (!attributes.TryGetPropertyValue("description", out JsonNode? descriptionNode))
return null; return null;
@ -119,7 +119,7 @@ public class MangaDex : MangaConnector
if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode) && linksNode is not null) if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode) && linksNode is not null)
foreach (KeyValuePair<string, JsonNode?> linkKv in linksNode!.AsObject()) foreach (KeyValuePair<string, JsonNode?> linkKv in linksNode!.AsObject())
linksDict.TryAdd(linkKv.Key, linkKv.Value.GetValue<string>()); linksDict.TryAdd(linkKv.Key, linkKv.Value.GetValue<string>());
Link[] links = linksDict.Select(x => new Link(x.Key, x.Value)).ToArray(); List<Link> links = linksDict.Select(x => new Link(x.Key, x.Value)).ToList();
string? originalLanguage = string? originalLanguage =
attributes.TryGetPropertyValue("originalLanguage", out JsonNode? originalLanguageNode) switch attributes.TryGetPropertyValue("originalLanguage", out JsonNode? originalLanguageNode) switch
@ -151,7 +151,7 @@ public class MangaDex : MangaConnector
if (attributes.TryGetPropertyValue("tags", out JsonNode? tagsNode)) if (attributes.TryGetPropertyValue("tags", out JsonNode? tagsNode))
foreach (JsonNode? tagNode in tagsNode!.AsArray()) foreach (JsonNode? tagNode in tagsNode!.AsArray())
tags.Add(tagNode!["attributes"]!["name"]!["en"]!.GetValue<string>()); tags.Add(tagNode!["attributes"]!["name"]!["en"]!.GetValue<string>());
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> mangaTags = tags.Select(t => new MangaTag(t)).ToList();
if (!manga.TryGetPropertyValue("relationships", out JsonNode? relationshipsNode)) if (!manga.TryGetPropertyValue("relationships", out JsonNode? relationshipsNode))
return null; return null;
@ -172,7 +172,7 @@ public class MangaDex : MangaConnector
if(!authorNames.Contains(authorName)) if(!authorNames.Contains(authorName))
authorNames.Add(authorName); authorNames.Add(authorName);
} }
Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); List<Author> authors = authorNames.Select(a => new Author(a)).ToList();
Manga pub = new (publicationId, sortName, description, $"https://mangadex.org/title/{publicationId}", coverUrl, null, year, Manga pub = new (publicationId, sortName, description, $"https://mangadex.org/title/{publicationId}", coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null, originalLanguage, releaseStatus, -1, null, null,

View File

@ -11,7 +11,7 @@ public class MangaHere : MangaConnector
this.downloadClient = new ChromiumDownloadClient(); this.downloadClient = new ChromiumDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join('+', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string sanitizedTitle = string.Join('+', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://www.mangahere.cc/search?title={sanitizedTitle}"; 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null)
return []; return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
if (document.DocumentNode.SelectNodes("//div[contains(concat(' ',normalize-space(@class),' '),' container ')]").Any(node => node.ChildNodes.Any(cNode => cNode.HasClass("search-keywords")))) if (document.DocumentNode.SelectNodes("//div[contains(concat(' ',normalize-space(@class),' '),' container ')]").Any(node => node.ChildNodes.Any(cNode => cNode.HasClass("search-keywords"))))
return []; return [];
@ -33,10 +33,10 @@ public class MangaHere : MangaConnector
.SelectNodes("//a[contains(@href, '/manga/') and not(contains(@href, '.html'))]") .SelectNodes("//a[contains(@href, '/manga/') and not(contains(@href, '.html'))]")
.Select(thumb => $"https://www.mangahere.cc{thumb.GetAttributeValue("href", "")}").Distinct().ToList(); .Select(thumb => $"https://www.mangahere.cc{thumb.GetAttributeValue("href", "")}").Distinct().ToList();
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); HashSet<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -44,12 +44,12 @@ public class MangaHere : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://www.mangahere.cc/manga/{publicationId}"); return GetMangaFromUrl($"https://www.mangahere.cc/manga/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo); downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -61,7 +61,7 @@ public class MangaHere : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
string originalLanguage = "", status = ""; string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new(); Dictionary<string, string> altTitles = new(), links = new();
@ -77,13 +77,13 @@ public class MangaHere : MangaConnector
.SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-say ')]/a") .SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-say ')]/a")
.Select(node => node.InnerText) .Select(node => node.InnerText)
.ToList(); .ToList();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); List<Author> authors = authorNames.Select(n => new Author(n)).ToList();
HashSet<string> tags = document.DocumentNode HashSet<string> tags = document.DocumentNode
.SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-tag-list ')]/a") .SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-tag-list ')]/a")
.Select(node => node.InnerText) .Select(node => node.InnerText)
.ToHashSet(); .ToHashSet();
MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray(); List<MangaTag> mangaTags = tags.Select(n => new MangaTag(n)).ToList();
status = document.DocumentNode.SelectSingleNode("//span[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-title-tip ')]").InnerText; status = document.DocumentNode.SelectSingleNode("//span[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-title-tip ')]").InnerText;
switch (status.ToLower()) switch (status.ToLower())

View File

@ -11,7 +11,7 @@ public class MangaKatana : MangaConnector
this.downloadClient = new HttpDownloadClient(); this.downloadClient = new HttpDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join("%20", Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string sanitizedTitle = string.Join("%20", Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower();
string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name"; string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name";
@ -29,16 +29,16 @@ public class MangaKatana : MangaConnector
return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1], requestResult.redirectedToUrl) }; return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1], requestResult.redirectedToUrl) };
} }
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.result); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.result);
return publications; return publications;
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://mangakatana.com/manga/{publicationId}"); return GetMangaFromUrl($"https://mangakatana.com/manga/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo); downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -47,7 +47,7 @@ public class MangaKatana : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1], url); return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1], url);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(Stream html) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(Stream html)
{ {
StreamReader reader = new(html); StreamReader reader = new(html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -63,10 +63,10 @@ public class MangaKatana : MangaConnector
.First(a => a.Name == "href").Value); .First(a => a.Name == "href").Value);
} }
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); HashSet<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -74,7 +74,7 @@ public class MangaKatana : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl)
{ {
StreamReader reader = new(html); StreamReader reader = new(html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -135,9 +135,9 @@ public class MangaKatana : MangaConnector
{ {
year = uint.Parse(yearString); year = uint.Parse(yearString);
} }
Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); List<Author> authors = authorNames.Select(n => new Author(n)).ToList();
MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray(); List<MangaTag> mangaTags = tags.Select(n => new MangaTag(n)).ToList();
MangaAltTitle[] altTitles = altTitlesDict.Select(x => new MangaAltTitle(x.Key, x.Value)).ToArray(); List<MangaAltTitle> altTitles = altTitlesDict.Select(x => new MangaAltTitle(x.Key, x.Value)).ToList();
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year, Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null, originalLanguage, releaseStatus, -1, null, null,

View File

@ -12,7 +12,7 @@ public class MangaLife : MangaConnector
this.downloadClient = new ChromiumDownloadClient(); this.downloadClient = new ChromiumDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = WebUtility.UrlEncode(publicationTitle); string sanitizedTitle = WebUtility.UrlEncode(publicationTitle);
string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}"; string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}";
@ -23,16 +23,16 @@ public class MangaLife : MangaConnector
if (requestResult.htmlDocument is null) if (requestResult.htmlDocument is null)
return []; return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://manga4life.com/manga/{publicationId}"); return GetMangaFromUrl($"https://manga4life.com/manga/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*"); Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[2].Value; string publicationId = publicationIdRex.Match(url).Groups[2].Value;
@ -43,7 +43,7 @@ public class MangaLife : MangaConnector
return null; return null;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
HtmlNode resultsNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']/div[last()]/div[1]/div"); 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")) if (resultsNode.Descendants("div").Count() == 1 && resultsNode.Descendants("div").First().HasClass("NoResults"))
@ -51,12 +51,12 @@ public class MangaLife : MangaConnector
return []; return [];
} }
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (HtmlNode resultNode in resultsNode.SelectNodes("div")) foreach (HtmlNode resultNode in resultsNode.SelectNodes("div"))
{ {
string url = resultNode.Descendants().First(d => d.HasClass("SeriesName")).GetAttributeValue("href", ""); string url = resultNode.Descendants().First(d => d.HasClass("SeriesName")).GetAttributeValue("href", "");
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl($"https://manga4life.com{url}"); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl($"https://manga4life.com{url}");
if (manga is { } x) if (manga is { } x)
ret.Add(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<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
string originalLanguage = "", status = ""; string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new(); Dictionary<string, string> altTitles = new(), links = new();
@ -84,14 +84,14 @@ public class MangaLife : MangaConnector
List<string> authorNames = new(); List<string> authorNames = new();
foreach (HtmlNode authorNode in authorsNodes) foreach (HtmlNode authorNode in authorsNodes)
authorNames.Add(authorNode.InnerText); authorNames.Add(authorNode.InnerText);
Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); List<Author> authors = authorNames.Select(a => new Author(a)).ToList();
HtmlNode[] genreNodes = document.DocumentNode HtmlNode[] genreNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a") .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a")
.ToArray(); .ToArray();
foreach (HtmlNode genreNode in genreNodes) foreach (HtmlNode genreNode in genreNodes)
tags.Add(genreNode.InnerText); tags.Add(genreNode.InnerText);
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> mangaTags = tags.Select(t => new MangaTag(t)).ToList();
HtmlNode yearNode = document.DocumentNode HtmlNode yearNode = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a") .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a")

View File

@ -13,7 +13,7 @@ public class Manganato : MangaConnector
this.downloadClient = new HttpDownloadClient(); this.downloadClient = new HttpDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}"; string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}";
@ -21,11 +21,11 @@ public class Manganato : MangaConnector
downloadClient.MakeRequest(requestUrl, RequestType.Default); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 ||requestResult.htmlDocument is null) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 ||requestResult.htmlDocument is null)
return []; return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
List<HtmlNode> searchResults = document.DocumentNode.Descendants("div").Where(n => n.HasClass("search-story-item")).ToList(); List<HtmlNode> searchResults = document.DocumentNode.Descendants("div").Where(n => n.HasClass("search-story-item")).ToList();
List<string> urls = new(); List<string> urls = new();
@ -35,10 +35,10 @@ public class Manganato : MangaConnector
.First(a => a.Name == "href").Value); .First(a => a.Name == "href").Value);
} }
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -46,12 +46,12 @@ public class Manganato : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://chapmanganato.com/{publicationId}"); return GetMangaFromUrl($"https://chapmanganato.com/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo); downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -63,7 +63,7 @@ public class Manganato : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
Dictionary<string, string> altTitlesDict = new(); Dictionary<string, string> altTitlesDict = new();
Dictionary<string, string>? links = null; Dictionary<string, string>? links = null;
@ -111,9 +111,9 @@ public class Manganato : MangaConnector
break; break;
} }
} }
Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); List<Author> authors = authorNames.Select(n => new Author(n)).ToList();
MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray(); List<MangaTag> mangaTags = tags.Select(n => new MangaTag(n)).ToList();
MangaAltTitle[] mangaAltTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray(); List<MangaAltTitle> 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() string coverUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First()
.GetAttributes().First(a => a.Name == "src").Value; .GetAttributes().First(a => a.Name == "src").Value;

View File

@ -23,7 +23,7 @@ public class Mangasee : MangaConnector
public string[] a { get; set; } public string[] a { get; set; }
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string requestUrl = "https://mangasee123.com/_search.php"; string requestUrl = "https://mangasee123.com/_search.php";
RequestResult requestResult = RequestResult requestResult =
@ -41,10 +41,10 @@ public class Mangasee : MangaConnector
string[] urls = filteredResults.Select(result => $"https://mangasee123.com/manga/{result.i}").ToArray(); string[] urls = filteredResults.Select(result => $"https://mangasee123.com/manga/{result.i}").ToArray();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> searchResultManga = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> searchResultManga = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? newManga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? newManga = GetMangaFromUrl(url);
if(newManga is { } manga) if(newManga is { } manga)
searchResultManga.Add(manga); searchResultManga.Add(manga);
} }
@ -79,12 +79,12 @@ public class Mangasee : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://mangasee123.com/manga/{publicationId}"); return GetMangaFromUrl($"https://mangasee123.com/manga/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*"); Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[1].Value; string publicationId = publicationIdRex.Match(url).Groups[1].Value;
@ -95,7 +95,7 @@ public class Mangasee : MangaConnector
return null; return null;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
string originalLanguage = "", status = ""; string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new(); Dictionary<string, string> altTitles = new(), links = new();
@ -114,14 +114,14 @@ public class Mangasee : MangaConnector
List<string> authorNames = new(); List<string> authorNames = new();
foreach (HtmlNode authorNode in authorsNodes) foreach (HtmlNode authorNode in authorsNodes)
authorNames.Add(authorNode.InnerText); authorNames.Add(authorNode.InnerText);
Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); List<Author> authors = authorNames.Select(a => new Author(a)).ToList();
HtmlNode[] genreNodes = document.DocumentNode HtmlNode[] genreNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a") .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a")
.ToArray(); .ToArray();
foreach (HtmlNode genreNode in genreNodes) foreach (HtmlNode genreNode in genreNodes)
tags.Add(genreNode.InnerText); tags.Add(genreNode.InnerText);
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> mangaTags = tags.Select(t => new MangaTag(t)).ToList();
HtmlNode yearNode = document.DocumentNode HtmlNode yearNode = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a") .SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a")

View File

@ -12,7 +12,7 @@ public class Mangaworld : MangaConnector
this.downloadClient = new HttpDownloadClient(); this.downloadClient = new HttpDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://www.mangaworld.ac/archive?keyword={sanitizedTitle}"; string requestUrl = $"https://www.mangaworld.ac/archive?keyword={sanitizedTitle}";
@ -23,11 +23,11 @@ public class Mangaworld : MangaConnector
if (requestResult.htmlDocument is null) if (requestResult.htmlDocument is null)
return []; return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
if (!document.DocumentNode.SelectSingleNode("//div[@class='comics-grid']").ChildNodes if (!document.DocumentNode.SelectSingleNode("//div[@class='comics-grid']").ChildNodes
.Any(node => node.HasClass("entry"))) .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')]") "//div[@class='comics-grid']//div[@class='entry']//a[contains(concat(' ',normalize-space(@class),' '),'thumb')]")
.Select(thumb => thumb.GetAttributeValue("href", "")).ToList(); .Select(thumb => thumb.GetAttributeValue("href", "")).ToList();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -49,12 +49,12 @@ public class Mangaworld : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://www.mangaworld.ac/manga/{publicationId}"); return GetMangaFromUrl($"https://www.mangaworld.ac/manga/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo); downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -69,10 +69,9 @@ public class Mangaworld : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url);
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
Dictionary<string, string> altTitlesDict = new(); Dictionary<string, string> altTitlesDict = new();
Dictionary<string, string>? links = null;
string originalLanguage = ""; string originalLanguage = "";
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased; MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
@ -86,17 +85,17 @@ public class Mangaworld : MangaConnector
string[] alts = altTitlesNode.InnerText.Split(", "); string[] alts = altTitlesNode.InnerText.Split(", ");
for(int i = 0; i < alts.Length; i++) for(int i = 0; i < alts.Length; i++)
altTitlesDict.Add(i.ToString(), alts[i]); altTitlesDict.Add(i.ToString(), alts[i]);
MangaAltTitle[] altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray(); List<MangaAltTitle> altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToList();
HtmlNode genresNode = HtmlNode genresNode =
metadata.SelectSingleNode("//span[text()='Generi: ' or text()='Genero: ']/.."); metadata.SelectSingleNode("//span[text()='Generi: ' or text()='Genero: ']/..");
HashSet<string> tags = genresNode.SelectNodes("a").Select(node => node.InnerText).ToHashSet(); HashSet<string> tags = genresNode.SelectNodes("a").Select(node => node.InnerText).ToHashSet();
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> mangaTags = tags.Select(t => new MangaTag(t)).ToList();
HtmlNode authorsNode = HtmlNode authorsNode =
metadata.SelectSingleNode("//span[text()='Autore: ' or text()='Autori: ']/.."); metadata.SelectSingleNode("//span[text()='Autore: ' or text()='Autori: ']/..");
string[] authorNames = authorsNode.SelectNodes("a").Select(node => node.InnerText).ToArray(); string[] authorNames = authorsNode.SelectNodes("a").Select(node => node.InnerText).ToArray();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); List<Author> authors = authorNames.Select(n => new Author(n)).ToList();
string status = metadata.SelectSingleNode("//span[text()='Stato: ']/..").SelectNodes("a").First().InnerText; string status = metadata.SelectSingleNode("//span[text()='Stato: ']/..").SelectNodes("a").First().InnerText;
// ReSharper disable 5 times StringLiteralTypo // ReSharper disable 5 times StringLiteralTypo

View File

@ -1,5 +1,4 @@
using System.Net; using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
using API.MangaDownloadClients; using API.MangaDownloadClients;
using HtmlAgilityPack; using HtmlAgilityPack;
@ -12,7 +11,7 @@ public class ManhuaPlus : MangaConnector
this.downloadClient = new ChromiumDownloadClient(); this.downloadClient = new ChromiumDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://manhuaplus.org/search?keyword={sanitizedTitle}"; 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null)
return []; return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications; return publications;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
if (document.DocumentNode.SelectSingleNode("//h1/../..").ChildNodes//I already want to not. if (document.DocumentNode.SelectSingleNode("//h1/../..").ChildNodes//I already want to not.
.Any(node => node.InnerText.Contains("No manga found"))) .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'))]") .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(); .Select(mangaNode => mangaNode.GetAttributeValue("href", "")).ToList();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url); (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? manga = GetMangaFromUrl(url);
if (manga is { } x) if (manga is { } x)
ret.Add(x); ret.Add(x);
} }
@ -46,12 +45,12 @@ public class ManhuaPlus : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://manhuaplus.org/manga/{publicationId}"); return GetMangaFromUrl($"https://manhuaplus.org/manga/{publicationId}");
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
Regex publicationIdRex = new(@"https:\/\/manhuaplus.org\/manga\/(.*)(\/.*)*"); Regex publicationIdRex = new(@"https:\/\/manhuaplus.org\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[1].Value; string publicationId = publicationIdRex.Match(url).Groups[1].Value;
@ -62,7 +61,7 @@ public class ManhuaPlus : MangaConnector
return null; return null;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
string originalLanguage = "", status = ""; string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new(); Dictionary<string, string> altTitles = new(), links = new();
@ -88,7 +87,7 @@ public class ManhuaPlus : MangaConnector
catch (ArgumentNullException e) catch (ArgumentNullException e)
{ {
} }
Author[] authors = authorNames.Select(a => new Author(a)).ToArray(); List<Author> authors = authorNames.Select(a => new Author(a)).ToList();
try try
{ {
@ -100,7 +99,7 @@ public class ManhuaPlus : MangaConnector
catch (ArgumentNullException e) catch (ArgumentNullException e)
{ {
} }
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> 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}"); 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"); HtmlNode yearNode = document.DocumentNode.SelectSingleNode("//aside//i[contains(concat(' ',normalize-space(@class),' '),' fa-clock ')]/../span");

View File

@ -17,7 +17,7 @@ public class Weebcentral : MangaConnector
downloadClient = new ChromiumDownloadClient(); downloadClient = new ChromiumDownloadClient();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "") public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "")
{ {
const int limit = 32; //How many values we want returned at once const int limit = 32; //How many values we want returned at once
var offset = 0; //"Page" var offset = 0; //"Page"
@ -36,7 +36,7 @@ public class Weebcentral : MangaConnector
return publications; return publications;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] ParsePublicationsFromHtml(HtmlDocument document)
{ {
if (document.DocumentNode.SelectNodes("//article") == null) if (document.DocumentNode.SelectNodes("//article") == null)
return []; return [];
@ -44,7 +44,7 @@ public class Weebcentral : MangaConnector
var urls = document.DocumentNode.SelectNodes("/html/body/article/a[@class='link link-hover']") var urls = document.DocumentNode.SelectNodes("/html/body/article/a[@class='link link-hover']")
.Select(elem => elem.GetAttributeValue("href", "")).ToList(); .Select(elem => elem.GetAttributeValue("href", "")).ToList();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new(); List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> ret = new();
foreach (var url in urls) foreach (var url in urls)
{ {
var manga = GetMangaFromUrl(url); var manga = GetMangaFromUrl(url);
@ -55,7 +55,7 @@ public class Weebcentral : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url)
{ {
Regex publicationIdRex = new(@"https:\/\/weebcentral\.com\/series\/(\w*)\/(.*)"); Regex publicationIdRex = new(@"https:\/\/weebcentral\.com\/series\/(\w*)\/(.*)");
var publicationId = publicationIdRex.Match(url).Groups[1].Value; var publicationId = publicationIdRex.Match(url).Groups[1].Value;
@ -67,7 +67,7 @@ public class Weebcentral : MangaConnector
return null; return null;
} }
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl) private (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{ {
var posterNode = var posterNode =
document.DocumentNode.SelectSingleNode("//section[@class='flex items-center justify-center']/picture/img"); document.DocumentNode.SelectSingleNode("//section[@class='flex items-center justify-center']/picture/img");
@ -79,12 +79,12 @@ public class Weebcentral : MangaConnector
HtmlNode[] authorsNodes = HtmlNode[] authorsNodes =
document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Author(s): ']/span")?.ToArray() ?? []; document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Author(s): ']/span")?.ToArray() ?? [];
var authorNames = authorsNodes.Select(n => n.InnerText).ToList(); var authorNames = authorsNodes.Select(n => n.InnerText).ToList();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray(); List<Author> authors = authorNames.Select(n => new Author(n)).ToList();
HtmlNode[] genreNodes = HtmlNode[] genreNodes =
document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Tags(s): ']/span")?.ToArray() ?? []; document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Tags(s): ']/span")?.ToArray() ?? [];
HashSet<string> tags = genreNodes.Select(n => n.InnerText).ToHashSet(); HashSet<string> tags = genreNodes.Select(n => n.InnerText).ToHashSet();
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray(); List<MangaTag> mangaTags = tags.Select(t => new MangaTag(t)).ToList();
var statusNode = document.DocumentNode.SelectSingleNode("//ul/li[strong/text() = 'Status: ']/a"); var statusNode = document.DocumentNode.SelectSingleNode("//ul/li[strong/text() = 'Status: ']/a");
var status = statusNode?.InnerText ?? ""; var status = statusNode?.InnerText ?? "";
@ -108,7 +108,7 @@ public class Weebcentral : MangaConnector
Dictionary<string, string> altTitlesDict = new(), links = new(); Dictionary<string, string> altTitlesDict = new(), links = new();
for (var i = 0; i < altTitleNodes.Length; i++) for (var i = 0; i < altTitleNodes.Length; i++)
altTitlesDict.Add(i.ToString(), altTitleNodes[i].InnerText); altTitlesDict.Add(i.ToString(), altTitleNodes[i].InnerText);
MangaAltTitle[] altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray(); List<MangaAltTitle> altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToList();
var originalLanguage = ""; var originalLanguage = "";
@ -123,7 +123,7 @@ public class Weebcentral : MangaConnector
return (manga, authors, mangaTags, [], altTitles); return (manga, authors, mangaTags, [], altTitles);
} }
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId) public override (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromId(string publicationId)
{ {
return GetMangaFromUrl($"https://weebcentral.com/series/{publicationId}"); return GetMangaFromUrl($"https://weebcentral.com/series/{publicationId}");
} }

View File

@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace API.Schema; namespace API.Schema;