Manga and Chapters are shared across Connectors

This commit is contained in:
2025-06-30 22:01:10 +02:00
parent ea73d03b8f
commit 7e9ba7090a
49 changed files with 3192 additions and 795 deletions

View File

@ -9,15 +9,11 @@ using Newtonsoft.Json;
namespace API.Schema;
[PrimaryKey("ChapterId")]
public class Chapter : IComparable<Chapter>
[PrimaryKey("Key")]
public class Chapter : Identifiable, IComparable<Chapter>
{
[StringLength(64)] [Required] public string ChapterId { get; init; }
[StringLength(256)]public string? IdOnConnectorSite { get; init; }
[StringLength(64)] [Required] public string ParentMangaId { get; init; } = null!;
private Manga? _parentManga = null!;
private Manga? _parentManga;
[JsonIgnore]
public Manga ParentManga
@ -25,41 +21,43 @@ public class Chapter : IComparable<Chapter>
get => _lazyLoader.Load(this, ref _parentManga) ?? throw new InvalidOperationException();
init
{
ParentMangaId = value.MangaId;
ParentMangaId = value.Key;
_parentManga = value;
}
}
private MangaConnectorMangaEntry? _mangaConnectorMangaEntry = null!;
[NotMapped]
public Dictionary<string, string> IdsOnMangaConnectors =>
MangaConnectorIds.ToDictionary(id => id.MangaConnectorName, id => id.IdOnConnectorSite);
private ICollection<MangaConnectorId<Chapter>>? _mangaConnectorIds;
[JsonIgnore]
public MangaConnectorMangaEntry MangaConnectorMangaEntry
public ICollection<MangaConnectorId<Chapter>> MangaConnectorIds
{
get => _lazyLoader.Load(this, ref _mangaConnectorMangaEntry) ?? throw new InvalidOperationException();
init => _mangaConnectorMangaEntry = value;
get => _lazyLoader.Load(this, ref _mangaConnectorIds) ?? throw new InvalidOperationException();
init => _mangaConnectorIds = value;
}
public int? VolumeNumber { get; private set; }
[StringLength(10)] [Required] public string ChapterNumber { get; private set; }
[StringLength(2048)] [Required] [Url] public string Url { get; internal set; }
[StringLength(256)] public string? Title { get; private set; }
[StringLength(256)] [Required] public string FileName { get; private set; }
[Required] public bool Downloaded { get; internal set; }
[NotMapped] public string FullArchiveFilePath => Path.Join(MangaConnectorMangaEntry.Manga.FullDirectoryPath, FileName);
[NotMapped] public string FullArchiveFilePath => Path.Join(ParentManga.FullDirectoryPath, FileName);
private readonly ILazyLoader _lazyLoader = null!;
public Chapter(MangaConnectorMangaEntry mangaConnectorMangaEntry, string url, string chapterNumber, int? volumeNumber = null, string? idOnConnectorSite = null, string? title = null)
public Chapter(Manga parentManga, string chapterNumber,
int? volumeNumber, string? title = null)
: base(TokenGen.CreateToken(typeof(Chapter), parentManga.Key, chapterNumber))
{
this.ChapterId = TokenGen.CreateToken(typeof(Chapter), mangaConnectorMangaEntry.MangaId, chapterNumber);
this.MangaConnectorMangaEntry = mangaConnectorMangaEntry;
this.IdOnConnectorSite = idOnConnectorSite;
this.ParentManga = parentManga;
this.MangaConnectorIds = [];
this.VolumeNumber = volumeNumber;
this.ChapterNumber = chapterNumber;
this.Url = url;
this.Title = title;
this.FileName = GetArchiveFilePath();
this.Downloaded = false;
@ -68,14 +66,12 @@ public class Chapter : IComparable<Chapter>
/// <summary>
/// EF ONLY!!!
/// </summary>
internal Chapter(ILazyLoader lazyLoader, string chapterId, int? volumeNumber, string chapterNumber, string url, string? idOnConnectorSite, string? title, string fileName, bool downloaded)
internal Chapter(ILazyLoader lazyLoader, string key, int? volumeNumber, string chapterNumber, string? title, string fileName, bool downloaded)
: base(key)
{
this._lazyLoader = lazyLoader;
this.ChapterId = chapterId;
this.IdOnConnectorSite = idOnConnectorSite;
this.VolumeNumber = volumeNumber;
this.ChapterNumber = chapterNumber;
this.Url = url;
this.Title = title;
this.FileName = fileName;
this.Downloaded = downloaded;
@ -100,14 +96,14 @@ public class Chapter : IComparable<Chapter>
public bool CheckDownloaded() => File.Exists(FullArchiveFilePath);
/// Placeholders:
/// %M Manga Name
/// %M Obj Name
/// %V Volume
/// %C Chapter
/// %T Title
/// %A Author (first in list)
/// %I Chapter Internal ID
/// %i Manga Internal ID
/// %Y Year (Manga)
/// %i Obj Internal ID
/// %Y Year (Obj)
private static readonly Regex NullableRex = new(@"\?([a-zA-Z])\(([^\)]*)\)|(.+?)");
private static readonly Regex ReplaceRexx = new(@"%([a-zA-Z])|(.+?)");
private string GetArchiveFilePath()
@ -125,14 +121,12 @@ public class Chapter : IComparable<Chapter>
char placeholder = nullable.Groups[1].Value[0];
bool isNull = placeholder switch
{
'M' => MangaConnectorMangaEntry.Manga?.Name is null,
'M' => ParentManga?.Name is null,
'V' => VolumeNumber is null,
'C' => ChapterNumber is null,
'T' => Title is null,
'A' => MangaConnectorMangaEntry.Manga?.Authors?.FirstOrDefault()?.AuthorName is null,
'I' => ChapterId is null,
'i' => MangaConnectorMangaEntry.Manga?.MangaId is null,
'Y' => MangaConnectorMangaEntry.Manga?.Year is null,
'A' => ParentManga?.Authors?.FirstOrDefault()?.AuthorName is null,
'Y' => ParentManga?.Year is null,
_ => true
};
if(!isNull)
@ -153,14 +147,12 @@ public class Chapter : IComparable<Chapter>
char placeholder = replace.Groups[1].Value[0];
string? value = placeholder switch
{
'M' => MangaConnectorMangaEntry.Manga?.Name,
'M' => ParentManga?.Name,
'V' => VolumeNumber?.ToString(),
'C' => ChapterNumber,
'T' => Title,
'A' => MangaConnectorMangaEntry.Manga?.Authors?.FirstOrDefault()?.AuthorName,
'I' => ChapterId,
'i' => MangaConnectorMangaEntry.Manga?.MangaId,
'Y' => MangaConnectorMangaEntry.Manga?.Year.ToString(),
'A' => ParentManga?.Authors?.FirstOrDefault()?.AuthorName,
'Y' => ParentManga?.Year.ToString(),
_ => null
};
stringBuilder.Append(value);
@ -201,21 +193,18 @@ public class Chapter : IComparable<Chapter>
);
if(Title is not null)
comicInfo.Add(new XElement("Title", Title));
if(MangaConnectorMangaEntry.Manga.MangaTags.Count > 0)
comicInfo.Add(new XElement("Tags", string.Join(',', MangaConnectorMangaEntry.Manga.MangaTags.Select(tag => tag.Tag))));
if(ParentManga.MangaTags.Count > 0)
comicInfo.Add(new XElement("Tags", string.Join(',', ParentManga.MangaTags.Select(tag => tag.Tag))));
if(VolumeNumber is not null)
comicInfo.Add(new XElement("Volume", VolumeNumber));
if(MangaConnectorMangaEntry.Manga.Authors.Count > 0)
comicInfo.Add(new XElement("Writer", string.Join(',', MangaConnectorMangaEntry.Manga.Authors.Select(author => author.AuthorName))));
if(MangaConnectorMangaEntry.Manga.OriginalLanguage is not null)
comicInfo.Add(new XElement("LanguageISO", MangaConnectorMangaEntry.Manga.OriginalLanguage));
if(MangaConnectorMangaEntry.Manga.Description != string.Empty)
comicInfo.Add(new XElement("Summary", MangaConnectorMangaEntry.Manga.Description));
if(ParentManga.Authors.Count > 0)
comicInfo.Add(new XElement("Writer", string.Join(',', ParentManga.Authors.Select(author => author.AuthorName))));
if(ParentManga.OriginalLanguage is not null)
comicInfo.Add(new XElement("LanguageISO", ParentManga.OriginalLanguage));
if(ParentManga.Description != string.Empty)
comicInfo.Add(new XElement("Summary", ParentManga.Description));
return comicInfo.ToString();
}
public override string ToString()
{
return $"{ChapterId} Vol.{VolumeNumber} Ch.{ChapterNumber} - {Title}";
}
public override string ToString() => $"{base.ToString()} Vol.{VolumeNumber} Ch.{ChapterNumber} - {Title}";
}