diff --git a/API/Migrations/20250111180034_ChapterNumber.Designer.cs b/API/Migrations/20250111180034_ChapterNumber.Designer.cs new file mode 100644 index 0000000..e330827 --- /dev/null +++ b/API/Migrations/20250111180034_ChapterNumber.Designer.cs @@ -0,0 +1,693 @@ +// +using System; +using API.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(PgsqlContext))] + [Migration("20250111180034_ChapterNumber")] + partial class ChapterNumber + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("API.Schema.Author", b => + { + b.Property("AuthorId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("AuthorName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("AuthorId"); + + b.ToTable("Authors"); + }); + + modelBuilder.Entity("API.Schema.Chapter", b => + { + b.Property("ChapterId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ArchiveFileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ChapterNumber") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Downloaded") + .HasColumnType("boolean"); + + b.Property("ParentMangaId") + .IsRequired() + .HasColumnType("character varying(64)"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.Property("VolumeNumber") + .HasColumnType("integer"); + + b.HasKey("ChapterId"); + + b.HasIndex("ParentMangaId"); + + b.ToTable("Chapters"); + }); + + modelBuilder.Entity("API.Schema.Jobs.Job", b => + { + b.Property("JobId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.PrimitiveCollection("DependsOnJobsIds") + .HasMaxLength(64) + .HasColumnType("text[]"); + + b.Property("JobId1") + .HasColumnType("character varying(64)"); + + b.Property("JobType") + .HasColumnType("smallint"); + + b.Property("LastExecution") + .HasColumnType("timestamp with time zone"); + + b.Property("ParentJobId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RecurrenceMs") + .HasColumnType("numeric(20,0)"); + + b.Property("state") + .HasColumnType("integer"); + + b.HasKey("JobId"); + + b.HasIndex("JobId1"); + + b.HasIndex("ParentJobId"); + + b.ToTable("Jobs"); + + b.HasDiscriminator("JobType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Schema.LibraryConnectors.LibraryConnector", b => + { + b.Property("LibraryConnectorId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Auth") + .IsRequired() + .HasColumnType("text"); + + b.Property("BaseUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("LibraryType") + .HasColumnType("smallint"); + + b.HasKey("LibraryConnectorId"); + + b.ToTable("LibraryConnectors"); + + b.HasDiscriminator("LibraryType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Schema.Link", b => + { + b.Property("LinkId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("LinkProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("LinkUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("MangaId") + .HasColumnType("character varying(64)"); + + b.HasKey("LinkId"); + + b.HasIndex("MangaId"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("API.Schema.Manga", b => + { + b.Property("MangaId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConnectorId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CoverFileNameInCache") + .HasColumnType("text"); + + b.Property("CoverUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IgnoreChapterBefore") + .HasColumnType("real"); + + b.Property("MangaConnectorId") + .IsRequired() + .HasColumnType("character varying(32)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OriginalLanguage") + .HasColumnType("text"); + + b.Property("ReleaseStatus") + .HasColumnType("smallint"); + + b.Property("WebsiteUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("bigint"); + + b.HasKey("MangaId"); + + b.HasIndex("MangaConnectorId"); + + b.ToTable("Manga"); + }); + + modelBuilder.Entity("API.Schema.MangaAltTitle", b => + { + b.Property("AltTitleId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("MangaId") + .HasColumnType("character varying(64)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("AltTitleId"); + + b.HasIndex("MangaId"); + + b.ToTable("AltTitles"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.MangaConnector", b => + { + b.Property("Name") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.PrimitiveCollection("BaseUris") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("SupportedLanguages") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Name"); + + b.ToTable("MangaConnectors"); + + b.HasDiscriminator("Name").HasValue("MangaConnector"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Schema.MangaTag", b => + { + b.Property("Tag") + .HasColumnType("text"); + + b.HasKey("Tag"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("API.Schema.Notification", b => + { + b.Property("NotificationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Urgency") + .HasColumnType("smallint"); + + b.HasKey("NotificationId"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("API.Schema.NotificationConnectors.NotificationConnector", b => + { + b.Property("NotificationConnectorId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NotificationConnectorType") + .HasColumnType("smallint"); + + b.HasKey("NotificationConnectorId"); + + b.ToTable("NotificationConnectors"); + + b.HasDiscriminator("NotificationConnectorType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("AuthorManga", b => + { + b.Property("AuthorsAuthorId") + .HasColumnType("character varying(64)"); + + b.Property("MangaId") + .HasColumnType("character varying(64)"); + + b.HasKey("AuthorsAuthorId", "MangaId"); + + b.HasIndex("MangaId"); + + b.ToTable("AuthorManga"); + }); + + modelBuilder.Entity("MangaMangaTag", b => + { + b.Property("MangaId") + .HasColumnType("character varying(64)"); + + b.Property("TagsTag") + .HasColumnType("text"); + + b.HasKey("MangaId", "TagsTag"); + + b.HasIndex("TagsTag"); + + b.ToTable("MangaMangaTag"); + }); + + modelBuilder.Entity("API.Schema.Jobs.DownloadNewChaptersJob", b => + { + b.HasBaseType("API.Schema.Jobs.Job"); + + b.Property("MangaId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasIndex("MangaId"); + + b.HasDiscriminator().HasValue((byte)1); + }); + + modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b => + { + b.HasBaseType("API.Schema.Jobs.Job"); + + b.Property("ChapterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasIndex("ChapterId"); + + b.HasDiscriminator().HasValue((byte)0); + }); + + modelBuilder.Entity("API.Schema.Jobs.MoveFileOrFolderJob", b => + { + b.HasBaseType("API.Schema.Jobs.Job"); + + b.Property("FromLocation") + .IsRequired() + .HasColumnType("text"); + + b.Property("ToLocation") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue((byte)3); + }); + + modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b => + { + b.HasBaseType("API.Schema.Jobs.Job"); + + b.Property("MangaId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasIndex("MangaId"); + + b.ToTable("Jobs", t => + { + t.Property("MangaId") + .HasColumnName("UpdateMetadataJob_MangaId"); + }); + + b.HasDiscriminator().HasValue((byte)2); + }); + + modelBuilder.Entity("API.Schema.LibraryConnectors.Kavita", b => + { + b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector"); + + b.HasDiscriminator().HasValue((byte)1); + }); + + modelBuilder.Entity("API.Schema.LibraryConnectors.Komga", b => + { + b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector"); + + b.HasDiscriminator().HasValue((byte)0); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.AsuraToon", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("AsuraToon"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.Bato", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("Bato"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.MangaDex", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("MangaDex"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.MangaHere", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("MangaHere"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.MangaKatana", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("MangaKatana"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.MangaLife", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("Manga4Life"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.Manganato", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("Manganato"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.Mangasee", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("Mangasee"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.Mangaworld", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("Mangaworld"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.ManhuaPlus", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("ManhuaPlus"); + }); + + modelBuilder.Entity("API.Schema.MangaConnectors.Weebcentral", b => + { + b.HasBaseType("API.Schema.MangaConnectors.MangaConnector"); + + b.HasDiscriminator().HasValue("Weebcentral"); + }); + + modelBuilder.Entity("API.Schema.NotificationConnectors.Gotify", b => + { + b.HasBaseType("API.Schema.NotificationConnectors.NotificationConnector"); + + b.Property("AppToken") + .IsRequired() + .HasColumnType("text"); + + b.Property("Endpoint") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue((byte)0); + }); + + modelBuilder.Entity("API.Schema.NotificationConnectors.Lunasea", b => + { + b.HasBaseType("API.Schema.NotificationConnectors.NotificationConnector"); + + b.Property("Id") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue((byte)1); + }); + + modelBuilder.Entity("API.Schema.NotificationConnectors.Ntfy", b => + { + b.HasBaseType("API.Schema.NotificationConnectors.NotificationConnector"); + + b.Property("Auth") + .IsRequired() + .HasColumnType("text"); + + b.Property("Endpoint") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.ToTable("NotificationConnectors", t => + { + t.Property("Endpoint") + .HasColumnName("Ntfy_Endpoint"); + }); + + b.HasDiscriminator().HasValue((byte)2); + }); + + modelBuilder.Entity("API.Schema.Chapter", b => + { + b.HasOne("API.Schema.Manga", "ParentManga") + .WithMany() + .HasForeignKey("ParentMangaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParentManga"); + }); + + modelBuilder.Entity("API.Schema.Jobs.Job", b => + { + b.HasOne("API.Schema.Jobs.Job", null) + .WithMany("DependsOnJobs") + .HasForeignKey("JobId1"); + + b.HasOne("API.Schema.Jobs.Job", "ParentJob") + .WithMany() + .HasForeignKey("ParentJobId"); + + b.Navigation("ParentJob"); + }); + + modelBuilder.Entity("API.Schema.Link", b => + { + b.HasOne("API.Schema.Manga", null) + .WithMany("Links") + .HasForeignKey("MangaId"); + }); + + modelBuilder.Entity("API.Schema.Manga", b => + { + b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector") + .WithMany() + .HasForeignKey("MangaConnectorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MangaConnector"); + }); + + modelBuilder.Entity("API.Schema.MangaAltTitle", b => + { + b.HasOne("API.Schema.Manga", null) + .WithMany("AltTitles") + .HasForeignKey("MangaId"); + }); + + modelBuilder.Entity("AuthorManga", b => + { + b.HasOne("API.Schema.Author", null) + .WithMany() + .HasForeignKey("AuthorsAuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("API.Schema.Manga", null) + .WithMany() + .HasForeignKey("MangaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MangaMangaTag", b => + { + b.HasOne("API.Schema.Manga", null) + .WithMany() + .HasForeignKey("MangaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("API.Schema.MangaTag", null) + .WithMany() + .HasForeignKey("TagsTag") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("API.Schema.Jobs.DownloadNewChaptersJob", b => + { + b.HasOne("API.Schema.Manga", "Manga") + .WithMany() + .HasForeignKey("MangaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Manga"); + }); + + modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b => + { + b.HasOne("API.Schema.Chapter", "Chapter") + .WithMany() + .HasForeignKey("ChapterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chapter"); + }); + + modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b => + { + b.HasOne("API.Schema.Manga", "Manga") + .WithMany() + .HasForeignKey("MangaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Manga"); + }); + + modelBuilder.Entity("API.Schema.Jobs.Job", b => + { + b.Navigation("DependsOnJobs"); + }); + + modelBuilder.Entity("API.Schema.Manga", b => + { + b.Navigation("AltTitles"); + + b.Navigation("Links"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/API/Migrations/20250111180034_ChapterNumber.cs b/API/Migrations/20250111180034_ChapterNumber.cs new file mode 100644 index 0000000..ef0ba03 --- /dev/null +++ b/API/Migrations/20250111180034_ChapterNumber.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class ChapterNumber : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "VolumeNumber", + table: "Chapters", + type: "integer", + nullable: true, + oldClrType: typeof(float), + oldType: "real", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ChapterNumber", + table: "Chapters", + type: "character varying(10)", + maxLength: 10, + nullable: false, + oldClrType: typeof(float), + oldType: "real"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "VolumeNumber", + table: "Chapters", + type: "real", + nullable: true, + oldClrType: typeof(int), + oldType: "integer", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ChapterNumber", + table: "Chapters", + type: "real", + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(10)", + oldMaxLength: 10); + } + } +} diff --git a/API/Migrations/PgsqlContextModelSnapshot.cs b/API/Migrations/PgsqlContextModelSnapshot.cs index 41a4ae8..a0499ab 100644 --- a/API/Migrations/PgsqlContextModelSnapshot.cs +++ b/API/Migrations/PgsqlContextModelSnapshot.cs @@ -47,8 +47,10 @@ namespace API.Migrations .IsRequired() .HasColumnType("text"); - b.Property("ChapterNumber") - .HasColumnType("real"); + b.Property("ChapterNumber") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); b.Property("Downloaded") .HasColumnType("boolean"); @@ -64,8 +66,8 @@ namespace API.Migrations .IsRequired() .HasColumnType("text"); - b.Property("VolumeNumber") - .HasColumnType("real"); + b.Property("VolumeNumber") + .HasColumnType("integer"); b.HasKey("ChapterId"); diff --git a/API/Schema/Chapter.cs b/API/Schema/Chapter.cs index f236e89..65e5811 100644 --- a/API/Schema/Chapter.cs +++ b/API/Schema/Chapter.cs @@ -11,7 +11,8 @@ public class Chapter : IComparable [MaxLength(64)] public string ChapterId { get; init; } = TokenGen.CreateToken(typeof(Chapter), 64); public int? VolumeNumber { get; private set; } - public ChapterNumber ChapterNumber { get; private set; } + [MaxLength(10)] + public string ChapterNumber { get; private set; } public string Url { get; internal set; } public string? Title { get; private set; } public string ArchiveFileName { get; private set; } @@ -20,13 +21,14 @@ public class Chapter : IComparable public string ParentMangaId { get; internal set; } public Manga? ParentManga { get; init; } - public Chapter(Manga parentManga, string url, ChapterNumber chapterNumber, int? volumeNumber = null, string? title = null) - : this(parentManga.MangaId, url, chapterNumber, volumeNumber, title) + public Chapter(Manga parentManga, string url, string chapterNumberStruct, int? volumeNumber = null, string? title = null) + : this(parentManga.MangaId, url, chapterNumberStruct, volumeNumber, title) { this.ParentManga = parentManga; + this.ArchiveFileName = BuildArchiveFileName(); } - public Chapter(string parentMangaId, string url, ChapterNumber chapterNumber, + public Chapter(string parentMangaId, string url, string chapterNumber, int? volumeNumber = null, string? title = null) { this.ParentMangaId = parentMangaId; @@ -34,10 +36,9 @@ public class Chapter : IComparable this.ChapterNumber = chapterNumber; this.VolumeNumber = volumeNumber; this.Title = title; - this.ArchiveFileName = BuildArchiveFileName(); } - public MoveFileOrFolderJob? UpdateChapterNumber(ChapterNumber chapterNumber) + public MoveFileOrFolderJob? UpdateChapterNumber(string chapterNumber) { this.ChapterNumber = chapterNumber; return UpdateArchiveFileName(); @@ -86,6 +87,26 @@ public class Chapter : IComparable return File.Exists(path); } + private static int CompareChapterNumbers(string ch1, string ch2) + { + var ch1Arr = ch1.Split('.').Select(c => int.Parse(c)).ToArray(); + var ch2Arr = ch2.Split('.').Select(c => int.Parse(c)).ToArray(); + + int i = 0, j = 0; + + while (i < ch1Arr.Length && j < ch2Arr.Length) + { + if (ch1Arr[i] < ch2Arr[j]) + return -1; + if (ch1Arr[i] > ch2Arr[j]) + return 1; + i++; + j++; + } + + return 0; + } + public int CompareTo(Chapter? other) { if(other is not { } otherChapter) @@ -94,7 +115,7 @@ public class Chapter : IComparable { <0 => -1, >0 => 1, - _ => this.ChapterNumber.CompareTo(otherChapter.ChapterNumber) + _ => CompareChapterNumbers(this.ChapterNumber, otherChapter.ChapterNumber) }; } diff --git a/API/Schema/ChapterNumber.cs b/API/Schema/ChapterNumber.cs deleted file mode 100644 index 240ff7c..0000000 --- a/API/Schema/ChapterNumber.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Numerics; -using System.Text.RegularExpressions; - -namespace API.Schema; - -public readonly struct ChapterNumber : INumber -{ - private readonly uint[] _numbers; - private readonly bool _naN; - - private ChapterNumber(uint[] numbers, bool naN = false) - { - this._numbers = numbers; - this._naN = naN; - } - - public ChapterNumber(string number) - { - if (!CanParse(number)) - { - this._numbers = []; - this._naN = true; - } - this._numbers = number.Split('.').Select(uint.Parse).ToArray(); - } - - public ChapterNumber(float number) : this(number.ToString("F")) {} - - public ChapterNumber(double number) : this((float)number) {} - - public ChapterNumber(uint number) - { - this._numbers = [number]; - this._naN = false; - } - - public ChapterNumber(int number) - { - if (int.IsNegative(number)) - { - this._numbers = []; - this._naN = true; - } - this._numbers = [(uint)number]; - this._naN = false; - } - - public int CompareTo(ChapterNumber other) - { - byte index = 0; - do - { - if (this._numbers[index] < other._numbers[index]) - return -1; - else if (this._numbers[index] > other._numbers[index]) - return 1; - }while(index < this._numbers.Length && index < other._numbers.Length); - - if (index >= this._numbers.Length && index >= other._numbers.Length) - return 0; - else if (index >= this._numbers.Length) - return -1; - else if (index >= other._numbers.Length) - return 1; - throw new UnreachableException(); - } - - private static readonly Regex Pattern = new(@"[0-9]+(?:\.[0-9]+)*"); - public static bool CanParse(string? number) => number is not null && Pattern.Match(number).Length == number.Length && number.Length > 0; - - public bool Equals(ChapterNumber other) => CompareTo(other) == 0; - - public string ToString(string? format, IFormatProvider? formatProvider) - { - return string.Join('.', _numbers); - } - - public override bool Equals(object? obj) - { - return obj is ChapterNumber other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(_numbers, _naN); - } - - public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) - { - throw new NotImplementedException(); - } - - public int CompareTo(object? obj) - { - if(obj is ChapterNumber other) - return CompareTo(other); - throw new ArgumentException(); - } - - public static ChapterNumber Parse(string s, IFormatProvider? provider) - { - if(!CanParse(s)) - throw new FormatException($"Invalid ChapterNumber-String: {s}"); - return new ChapterNumber(s); - } - - public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out ChapterNumber result) - { - result = new ChapterNumber([], true);; - if (!CanParse(s)) - return false; - if (s is null) - return false; - result = new ChapterNumber(s); - return true; - } - - public static ChapterNumber Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s.ToString(), provider); - - public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out ChapterNumber result) => TryParse(s.ToString(), provider, out result); - - public static ChapterNumber operator +(ChapterNumber left, ChapterNumber right) - { - if (IsNaN(left) || IsNaN(right)) - return new ChapterNumber([], true); - int size = left._numbers.Length > right._numbers.Length ? left._numbers.Length : right._numbers.Length; - uint[] numbers = new uint[size]; - for (int i = 0; i < size; i++) - { - if(left._numbers.Length < i) - numbers[i] = right._numbers[i]; - else if(right._numbers.Length < i) - numbers[i] = left._numbers[i]; - else - numbers[i] = left._numbers[i] + right._numbers[i]; - } - return new ChapterNumber(numbers); - } - - private static bool BothNotNaN(ChapterNumber left, ChapterNumber right) => !IsNaN(left) && !IsNaN(right); - - public static ChapterNumber AdditiveIdentity => Zero; - - public static bool operator ==(ChapterNumber left, ChapterNumber right) => BothNotNaN(left, right) && left.Equals(right); - - public static bool operator !=(ChapterNumber left, ChapterNumber right) => !(left == right); - - public static bool operator >(ChapterNumber left, ChapterNumber right) => BothNotNaN(left, right) && left.CompareTo(right) > 0; - - public static bool operator >=(ChapterNumber left, ChapterNumber right) => BothNotNaN(left, right) && left.CompareTo(right) >= 0; - - public static bool operator <(ChapterNumber left, ChapterNumber right) => BothNotNaN(left, right) && left.CompareTo(right) < 0; - - public static bool operator <=(ChapterNumber left, ChapterNumber right) => BothNotNaN(left, right) && left.CompareTo(right) <= 0; - - public static ChapterNumber operator %(ChapterNumber left, ChapterNumber right) => throw new ArithmeticException(); - - public static ChapterNumber operator +(ChapterNumber value) => throw new InvalidOperationException(); - - public static ChapterNumber operator --(ChapterNumber value) - { - if (IsNaN(value)) - return value; - uint[] numbers = value._numbers; - numbers[0]--; - return new ChapterNumber(numbers); - } - - public static ChapterNumber operator /(ChapterNumber left, ChapterNumber right) => throw new InvalidOperationException(); - - public static ChapterNumber operator ++(ChapterNumber value) - { - if (IsNaN(value)) - return value; - uint[] numbers = value._numbers; - numbers[0]++; - return new ChapterNumber(numbers); - } - - public static ChapterNumber MultiplicativeIdentity => One; - public static ChapterNumber operator *(ChapterNumber left, ChapterNumber right) => throw new InvalidOperationException(); - - public static ChapterNumber operator -(ChapterNumber left, ChapterNumber right) => throw new InvalidOperationException(); - - public static ChapterNumber operator -(ChapterNumber value) => throw new InvalidOperationException(); - - public static ChapterNumber Abs(ChapterNumber value) => value; - - public static bool IsCanonical(ChapterNumber value) => true; - - public static bool IsComplexNumber(ChapterNumber value) => false; - - public static bool IsEvenInteger(ChapterNumber value) => IsInteger(value) && uint.IsEvenInteger(value._numbers[0]); - - public static bool IsFinite(ChapterNumber value) => true; - - public static bool IsImaginaryNumber(ChapterNumber value) => false; - - public static bool IsInfinity(ChapterNumber value) => false; - - public static bool IsInteger(ChapterNumber value) => !IsNaN(value) && value._numbers.Length == 1; - - public static bool IsNaN(ChapterNumber value) => value._naN; - - public static bool IsNegative(ChapterNumber value) => false; - - public static bool IsNegativeInfinity(ChapterNumber value) => false; - - public static bool IsNormal(ChapterNumber value) => true; - - public static bool IsOddInteger(ChapterNumber value) => false; - - public static bool IsPositive(ChapterNumber value) => true; - - public static bool IsPositiveInfinity(ChapterNumber value) => false; - - public static bool IsRealNumber(ChapterNumber value) => false; - - public static bool IsSubnormal(ChapterNumber value) => false; - - public static bool IsZero(ChapterNumber value) => value._numbers.All(n => n == 0); - - public static ChapterNumber MaxMagnitude(ChapterNumber x, ChapterNumber y) - { - if(IsNaN(x)) - return new ChapterNumber([], true); - if (IsNaN(y)) - return new ChapterNumber([], true); - return x >= y ? x : y; - } - - public static ChapterNumber MaxMagnitudeNumber(ChapterNumber x, ChapterNumber y) - { - if (IsNaN(x)) - return y; - if (IsNaN(y)) - return x; - return x >= y ? x : y; - } - - public static ChapterNumber MinMagnitude(ChapterNumber x, ChapterNumber y) - { - if(IsNaN(x)) - return new ChapterNumber([], true); - if (IsNaN(y)) - return new ChapterNumber([], true); - return x <= y ? x : y; - } - - public static ChapterNumber MinMagnitudeNumber(ChapterNumber x, ChapterNumber y) - { - if (IsNaN(x)) - return y; - if (IsNaN(y)) - return x; - return x <= y ? x : y; - } - - public static ChapterNumber Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => throw new NotImplementedException(); - - public static ChapterNumber Parse(string s, NumberStyles style, IFormatProvider? provider) => throw new NotImplementedException(); - - public static bool TryConvertFromChecked(TOther value, out ChapterNumber result) where TOther : INumberBase - { - throw new NotImplementedException(); - } - - public static bool TryConvertFromSaturating(TOther value, out ChapterNumber result) where TOther : INumberBase - { - throw new NotImplementedException(); - } - - public static bool TryConvertFromTruncating(TOther value, out ChapterNumber result) where TOther : INumberBase - { - throw new NotImplementedException(); - } - - public static bool TryConvertToChecked(ChapterNumber value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase - { - throw new NotImplementedException(); - } - - public static bool TryConvertToSaturating(ChapterNumber value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase - { - throw new NotImplementedException(); - } - - public static bool TryConvertToTruncating(ChapterNumber value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase - { - throw new NotImplementedException(); - } - - public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ChapterNumber result) - => TryParse(s.ToString(), style, provider, out result); - - public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ChapterNumber result) - => TryParse(s, provider, out result); - - public static ChapterNumber One => new(1); - public static int Radix => 10; - public static ChapterNumber Zero => new(0); -} \ No newline at end of file diff --git a/API/Schema/MangaConnectors/AsuraToon.cs b/API/Schema/MangaConnectors/AsuraToon.cs index 4394200..b57db16 100644 --- a/API/Schema/MangaConnectors/AsuraToon.cs +++ b/API/Schema/MangaConnectors/AsuraToon.cs @@ -152,9 +152,7 @@ public class AsuraToon : MangaConnector string chapterUrl = chapterInfo.GetAttributeValue("href", ""); Match match = infoRex.Match(chapterInfo.InnerText); - if(!ChapterNumber.CanParse(match.Groups[1].Value)) - continue; - ChapterNumber chapterNumber = new(match.Groups[1].Value); + string chapterNumber = new(match.Groups[1].Value); string? chapterName = match.Groups[2].Success && match.Groups[2].Length > 1 ? match.Groups[2].Value : null; string url = $"https://asuracomic.net/series/{chapterUrl}"; try diff --git a/API/Schema/MangaConnectors/Bato.cs b/API/Schema/MangaConnectors/Bato.cs index 80e3e03..a2e8dc3 100644 --- a/API/Schema/MangaConnectors/Bato.cs +++ b/API/Schema/MangaConnectors/Bato.cs @@ -159,9 +159,7 @@ public class Bato : MangaConnector Match match = numberRex.Match(chapterUrl); string id = match.Groups[1].Value; int? volumeNumber = match.Groups[2].Success ? int.Parse(match.Groups[2].Value) : null; - if(ChapterNumber.CanParse(match.Groups[3].Value)) - continue; - ChapterNumber chapterNumber = new(match.Groups[3].Value); + string chapterNumber = new(match.Groups[3].Value); string url = $"https://bato.to{chapterUrl}?load=2"; try { diff --git a/API/Schema/MangaConnectors/MangaDex.cs b/API/Schema/MangaConnectors/MangaDex.cs index c4a4b42..a1abede 100644 --- a/API/Schema/MangaConnectors/MangaDex.cs +++ b/API/Schema/MangaConnectors/MangaDex.cs @@ -229,9 +229,7 @@ public class MangaDex : MangaConnector ? attributes["chapter"]!.GetValue() : null; - if(chapterNumStr is null || ChapterNumber.CanParse(chapterNumStr)) - continue; - ChapterNumber chapterNumber = new(chapterNumStr); + string chapterNumber = new(chapterNumStr); if (attributes.ContainsKey("pages") && attributes["pages"] is not null && diff --git a/API/Schema/MangaConnectors/MangaHere.cs b/API/Schema/MangaConnectors/MangaHere.cs index bf3ec40..4c7a6d4 100644 --- a/API/Schema/MangaConnectors/MangaHere.cs +++ b/API/Schema/MangaConnectors/MangaHere.cs @@ -128,9 +128,7 @@ public class MangaHere : MangaConnector Match rexMatch = chapterRex.Match(url); int? volumeNumber = rexMatch.Groups[1].Value == "TBD" ? null : int.Parse(rexMatch.Groups[1].Value); - if(!ChapterNumber.CanParse(rexMatch.Groups[2].Value)) - continue; - ChapterNumber chapterNumber = new(rexMatch.Groups[2].Value); + string chapterNumber = new(rexMatch.Groups[2].Value); string fullUrl = $"https://www.mangahere.cc{url}"; try diff --git a/API/Schema/MangaConnectors/MangaKatana.cs b/API/Schema/MangaConnectors/MangaKatana.cs index 7529988..0905db4 100644 --- a/API/Schema/MangaConnectors/MangaKatana.cs +++ b/API/Schema/MangaConnectors/MangaKatana.cs @@ -185,9 +185,8 @@ public class MangaKatana : MangaConnector .GetAttributeValue("href", ""); int? volumeNumber = volumeRex.IsMatch(url) ? int.Parse(volumeRex.Match(url).Groups[1].Value) : null; - if(!ChapterNumber.CanParse(chapterNumRex.Match(url).Groups[1].Value)) - continue; - ChapterNumber chapterNumber = new(chapterNumRex.Match(url).Groups[1].Value); + + string chapterNumber = new(chapterNumRex.Match(url).Groups[1].Value); string chapterName = chapterNameRex.Match(fullString).Groups[1].Value; try { diff --git a/API/Schema/MangaConnectors/MangaLife.cs b/API/Schema/MangaConnectors/MangaLife.cs index 30d72c6..54bdd1c 100644 --- a/API/Schema/MangaConnectors/MangaLife.cs +++ b/API/Schema/MangaConnectors/MangaLife.cs @@ -151,9 +151,8 @@ public class MangaLife : MangaConnector ? int.Parse(rexMatch.Groups[3].Value) : null; - if(!ChapterNumber.CanParse(rexMatch.Groups[1].Value)) - continue; - ChapterNumber chapterNumber = new(rexMatch.Groups[1].Value); + + string chapterNumber = new(rexMatch.Groups[1].Value); string fullUrl = $"https://manga4life.com{url}"; fullUrl = fullUrl.Replace(Regex.Match(url,"(-page-[0-9])").Value,""); try diff --git a/API/Schema/MangaConnectors/Manganato.cs b/API/Schema/MangaConnectors/Manganato.cs index 054dfc5..d7062ca 100644 --- a/API/Schema/MangaConnectors/Manganato.cs +++ b/API/Schema/MangaConnectors/Manganato.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Net; using System.Text.RegularExpressions; using API.MangaDownloadClients; using HtmlAgilityPack; @@ -10,33 +9,37 @@ public class Manganato : MangaConnector { public Manganato() : base("Manganato", ["en"], ["manganato.com"]) { - this.downloadClient = new HttpDownloadClient(); + downloadClient = new HttpDownloadClient(); } - public override (Manga, List?, List?, List?, List?)[] 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}"; - RequestResult requestResult = + var sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)) + .ToLower(); + var requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}"; + var requestResult = 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 []; - (Manga, List?, List?, List?, List?)[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument); + (Manga, List?, List?, List?, List?)[] publications = + ParsePublicationsFromHtml(requestResult.htmlDocument); return publications; } - private (Manga, List?, List?, List?, List?)[] 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 searchResults = document.DocumentNode.Descendants("div") + .Where(n => n.HasClass("search-story-item")).ToList(); List urls = new(); - foreach (HtmlNode mangaResult in searchResults) - { + foreach (var mangaResult in searchResults) urls.Add(mangaResult.Descendants("a").First(n => n.HasClass("item-title")).GetAttributes() .First(a => a.Name == "href").Value); - } List<(Manga, List?, List?, List?, List?)> ret = new(); - foreach (string url in urls) + foreach (var url in urls) { (Manga, List?, List?, List?, List?)? manga = GetMangaFromUrl(url); if (manga is { } x) @@ -46,54 +49,57 @@ public class Manganato : MangaConnector return ret.ToArray(); } - public override (Manga, List?, List?, List?, List?)? GetMangaFromId(string publicationId) + public override (Manga, List?, List?, List?, List?)? GetMangaFromId( + string publicationId) { return GetMangaFromUrl($"https://chapmanganato.com/{publicationId}"); } - public override (Manga, List?, List?, List?, List?)? GetMangaFromUrl(string url) + public override (Manga, List?, List?, List?, List?)? + GetMangaFromUrl(string url) { - RequestResult requestResult = + var requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; - + if (requestResult.htmlDocument is null) return null; return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url); } - private (Manga, List?, List?, List?, List?) 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; HashSet tags = new(); string[] authorNames = []; - string originalLanguage = ""; - MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased; + var originalLanguage = ""; + var releaseStatus = MangaReleaseStatus.Unreleased; - HtmlNode infoNode = document.DocumentNode.Descendants("div").First(d => d.HasClass("story-info-right")); + var infoNode = document.DocumentNode.Descendants("div").First(d => d.HasClass("story-info-right")); - string sortName = infoNode.Descendants("h1").First().InnerText; + var sortName = infoNode.Descendants("h1").First().InnerText; - HtmlNode infoTable = infoNode.Descendants().First(d => d.Name == "table"); - - foreach (HtmlNode row in infoTable.Descendants("tr")) + var infoTable = infoNode.Descendants().First(d => d.Name == "table"); + + foreach (var row in infoTable.Descendants("tr")) { - string key = row.SelectNodes("td").First().InnerText.ToLower(); - string value = row.SelectNodes("td").Last().InnerText; - string keySanitized = string.Concat(Regex.Matches(key, "[a-z]")); + var key = row.SelectNodes("td").First().InnerText.ToLower(); + var value = row.SelectNodes("td").Last().InnerText; + var keySanitized = string.Concat(Regex.Matches(key, "[a-z]")); switch (keySanitized) { case "alternative": string[] alts = value.Split(" ; "); - for(int i = 0; i < alts.Length; i++) + for (var i = 0; i < alts.Length; i++) altTitlesDict.Add(i.ToString(), alts[i]); break; case "authors": authorNames = value.Split('-'); - for (int i = 0; i < authorNames.Length; i++) + for (var i = 0; i < authorNames.Length; i++) authorNames[i] = authorNames[i].Replace("\r\n", ""); break; case "status": @@ -102,57 +108,62 @@ public class Manganato : MangaConnector case "ongoing": releaseStatus = MangaReleaseStatus.Continuing; break; case "completed": releaseStatus = MangaReleaseStatus.Completed; break; } + break; case "genres": string[] genres = value.Split(" - "); - for (int i = 0; i < genres.Length; i++) + for (var i = 0; i < genres.Length; i++) genres[i] = genres[i].Replace("\r\n", ""); tags = genres.ToHashSet(); break; } } + 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() + var coverUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img") + .First() .GetAttributes().First(a => a.Name == "src").Value; - string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description")) + var description = document.DocumentNode.Descendants("div") + .First(d => d.HasClass("panel-story-info-description")) .InnerText.Replace("Description :", ""); while (description.StartsWith('\n')) description = description.Substring(1); - - string pattern = "MMM dd,yyyy HH:mm"; - HtmlNode? oldestChapter = document.DocumentNode + var pattern = "MMM dd,yyyy HH:mm"; + + var oldestChapter = document.DocumentNode .SelectNodes("//span[contains(concat(' ',normalize-space(@class),' '),' chapter-time ')]").MaxBy( node => DateTime.ParseExact(node.GetAttributeValue("title", "Dec 31 2400, 23:59"), pattern, CultureInfo.InvariantCulture).Millisecond); - uint year = (uint)DateTime.ParseExact(oldestChapter?.GetAttributeValue("title", "Dec 31 2400, 23:59")??"Dec 31 2400, 23:59", pattern, + var year = (uint)DateTime.ParseExact( + oldestChapter?.GetAttributeValue("title", "Dec 31 2400, 23:59") ?? "Dec 31 2400, 23:59", pattern, CultureInfo.InvariantCulture).Year; - - Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year, + + Manga manga = new(publicationId, sortName, description, websiteUrl, coverUrl, null, year, originalLanguage, releaseStatus, -1, - this, - authors, - mangaTags, + this, + authors, + mangaTags, [], mangaAltTitles); - + return (manga, authors, mangaTags, [], mangaAltTitles); } - public override Chapter[] GetChapters(Manga manga, string language="en") + public override Chapter[] GetChapters(Manga manga, string language = "en") { - string requestUrl = $"https://chapmanganato.com/{manga.MangaId}"; - RequestResult requestResult = + var requestUrl = $"https://chapmanganato.com/{manga.MangaId}"; + var requestResult = downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return []; - + //Return Chapters ordered by Chapter-Number if (requestResult.htmlDocument is null) return []; @@ -165,26 +176,25 @@ public class Manganato : MangaConnector { List ret = new(); - HtmlNode chapterList = document.DocumentNode.Descendants("ul").First(l => l.HasClass("row-content-chapter")); + var chapterList = document.DocumentNode.Descendants("ul").First(l => l.HasClass("row-content-chapter")); Regex volRex = new(@"Vol\.([0-9]+).*"); Regex chapterRex = new(@"https:\/\/chapmanganato.[A-z]+\/manga-[A-z0-9]+\/chapter-([0-9\.]+)"); Regex nameRex = new(@"Chapter ([0-9]+(\.[0-9]+)*){1}:? (.*)"); - foreach (HtmlNode chapterInfo in chapterList.Descendants("li")) + foreach (var chapterInfo in chapterList.Descendants("li")) { - string fullString = chapterInfo.Descendants("a").First(d => d.HasClass("chapter-name")).InnerText; + var fullString = chapterInfo.Descendants("a").First(d => d.HasClass("chapter-name")).InnerText; - string url = chapterInfo.Descendants("a").First(d => d.HasClass("chapter-name")) + var url = chapterInfo.Descendants("a").First(d => d.HasClass("chapter-name")) .GetAttributeValue("href", ""); - + int? volumeNumber = volRex.IsMatch(fullString) ? int.Parse(volRex.Match(fullString).Groups[1].Value) : null; - if(!ChapterNumber.CanParse(chapterRex.Match(url).Groups[1].Value)) - continue; - ChapterNumber chapterNumber = new(chapterRex.Match(url).Groups[1].Value); - string chapterName = nameRex.Match(fullString).Groups[3].Value; + + string chapterNumber = new(chapterRex.Match(url).Groups[1].Value); + var chapterName = nameRex.Match(fullString).Groups[3].Value; try { ret.Add(new Chapter(manga, url, chapterNumber, volumeNumber, chapterName)); @@ -193,20 +203,19 @@ public class Manganato : MangaConnector { } } + ret.Reverse(); return ret; } - + internal override string[] GetChapterImageUrls(Chapter chapter) { - string requestUrl = chapter.Url; - RequestResult requestResult = + var requestUrl = chapter.Url; + var requestResult = downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null) - { return []; - } string[] imageUrls = ParseImageUrlsFromHtml(requestResult.htmlDocument); return imageUrls; @@ -216,9 +225,9 @@ public class Manganato : MangaConnector { List ret = new(); - HtmlNode imageContainer = + var imageContainer = document.DocumentNode.Descendants("div").First(i => i.HasClass("container-chapter-reader")); - foreach(HtmlNode imageNode in imageContainer.Descendants("img")) + foreach (var imageNode in imageContainer.Descendants("img")) ret.Add(imageNode.GetAttributeValue("src", "")); return ret.ToArray(); diff --git a/API/Schema/MangaConnectors/Mangasee.cs b/API/Schema/MangaConnectors/Mangasee.cs index 67e2b0f..f83f154 100644 --- a/API/Schema/MangaConnectors/Mangasee.cs +++ b/API/Schema/MangaConnectors/Mangasee.cs @@ -172,9 +172,8 @@ public class Mangasee : MangaConnector string url = chapter.Descendants("link").First().Value; Match m = chVolRex.Match(url); int? volumeNumber = m.Groups[2].Success ? int.Parse(m.Groups[2].Value) : null; - if(!ChapterNumber.CanParse(m.Groups[1].Value)) - continue; - ChapterNumber chapterNumber = new(m.Groups[1].Value); + + string chapterNumber = new(m.Groups[1].Value); string chapterUrl = Regex.Replace(url, @"-page-[0-9]+(\.html)", ".html"); try diff --git a/API/Schema/MangaConnectors/Mangaworld.cs b/API/Schema/MangaConnectors/Mangaworld.cs index cb0a81b..9e563e9 100644 --- a/API/Schema/MangaConnectors/Mangaworld.cs +++ b/API/Schema/MangaConnectors/Mangaworld.cs @@ -158,9 +158,8 @@ public class Mangaworld : MangaConnector { string numberStr = chapterRex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText).Groups[1].Value; - if(!ChapterNumber.CanParse(numberStr)) - continue; - ChapterNumber chapterNumber = new(numberStr); + + string chapterNumber = new(numberStr); string url = chNode.SelectSingleNode("a").GetAttributeValue("href", ""); string id = idRex.Match(chNode.SelectSingleNode("a").GetAttributeValue("href", "")).Groups[1].Value; try @@ -178,9 +177,8 @@ public class Mangaworld : MangaConnector foreach (HtmlNode chNode in chaptersWrapper.SelectNodes("div").Where(node => node.HasClass("chapter"))) { string numberStr = chapterRex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText).Groups[1].Value; - if(!ChapterNumber.CanParse(numberStr)) - continue; - ChapterNumber chapterNumber = new(numberStr); + + string chapterNumber = new(numberStr); string url = chNode.SelectSingleNode("a").GetAttributeValue("href", ""); string id = idRex.Match(chNode.SelectSingleNode("a").GetAttributeValue("href", "")).Groups[1].Value; try diff --git a/API/Schema/MangaConnectors/ManhuaPlus.cs b/API/Schema/MangaConnectors/ManhuaPlus.cs index 0312867..a8efb72 100644 --- a/API/Schema/MangaConnectors/ManhuaPlus.cs +++ b/API/Schema/MangaConnectors/ManhuaPlus.cs @@ -148,9 +148,7 @@ public class ManhuaPlus : MangaConnector { Match rexMatch = urlRex.Match(url); - if(!ChapterNumber.CanParse(rexMatch.Groups[1].Value)) - continue; - ChapterNumber chapterNumber = new(rexMatch.Groups[1].Value); + string chapterNumber = new(rexMatch.Groups[1].Value); string fullUrl = url; try { diff --git a/API/Schema/MangaConnectors/WeebCentral.cs b/API/Schema/MangaConnectors/WeebCentral.cs index 7568188..a329994 100644 --- a/API/Schema/MangaConnectors/WeebCentral.cs +++ b/API/Schema/MangaConnectors/WeebCentral.cs @@ -157,7 +157,7 @@ public class Weebcentral : MangaConnector public override Chapter[] GetChapters(Manga manga, string language = "en") { - var requestUrl = $"{_baseUrl}/series/{manga.MangaId}/full-chapter-list"; + var requestUrl = $"{_baseUrl}/series/{manga.ConnectorId}/full-chapter-list"; var requestResult = downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) @@ -182,7 +182,7 @@ public class Weebcentral : MangaConnector var url = elem.GetAttributeValue("href", "") ?? "Undefined"; if (!url.StartsWith("https://") && !url.StartsWith("http://")) - return new Chapter(manga, "undefined", new ChapterNumber(-1), null, null); + return new Chapter(manga, "undefined", "-1", null, null); var idMatch = idRex.Match(url); var id = idMatch.Success ? idMatch.Groups[1].Value : null; @@ -192,12 +192,13 @@ public class Weebcentral : MangaConnector var chapterNumberMatch = chapterRex.Match(chapterNode); - if(!chapterNumberMatch.Success || !ChapterNumber.CanParse(chapterNumberMatch.Groups[1].Value)) - return new Chapter(manga, "undefined", new ChapterNumber(-1), null, null); - ChapterNumber chapterNumber = new(chapterNumberMatch.Groups[1].Value); + if(!chapterNumberMatch.Success) + return new Chapter(manga, "undefined", "-1", null, null); - return new Chapter(manga, url, chapterNumber, null, null); - }).Where(elem => elem.ChapterNumber < ChapterNumber.Zero && elem.Url != "undefined").ToList(); + string chapterNumber = new(chapterNumberMatch.Groups[1].Value); + var chapter = new Chapter(manga, url, chapterNumber, null, null); + return chapter; + }).Where(elem => elem.ChapterNumber.CompareTo("-1") != 0 && elem.Url != "undefined").ToList(); ret.Reverse(); return ret;