Fix merging of Manga

Fix ComickIo empty lists
This commit is contained in:
2025-07-22 20:24:53 +02:00
parent 4d5c95b119
commit 32f7a6642a
8 changed files with 89 additions and 57 deletions

View File

@@ -41,15 +41,7 @@ public class MangaController(MangaContext context) : Controller
[ProducesResponseType<Manga[]>(Status200OK, "application/json")] [ProducesResponseType<Manga[]>(Status200OK, "application/json")]
public IActionResult GetManga([FromBody]string[] MangaIds) public IActionResult GetManga([FromBody]string[] MangaIds)
{ {
Manga[] ret = context.Mangas.Where(m => MangaIds.Contains(m.Key)) Manga[] ret = context.MangaIncludeAll().Where(m => MangaIds.Contains(m.Key)).ToArray();
.Include(m => m.Library)
.Include(m => m.Authors)
.Include(m => m.MangaTags)
.Include(m => m.Links)
.Include(m => m.AltTitles)
.Include(m => m.Chapters)
.Include(m => m.MangaConnectorIds)
.ToArray();
return Ok(ret); return Ok(ret);
} }
@@ -64,11 +56,8 @@ public class MangaController(MangaContext context) : Controller
[ProducesResponseType(Status404NotFound)] [ProducesResponseType(Status404NotFound)]
public IActionResult GetManga(string MangaId) public IActionResult GetManga(string MangaId)
{ {
if (context.Mangas.Find(MangaId) is not { } manga) if (context.MangaIncludeAll().FirstOrDefault(m => m.Key == MangaId) is not { } manga)
return NotFound(nameof(MangaId)); return NotFound(nameof(MangaId));
foreach (CollectionEntry collectionEntry in context.Entry(manga).Collections)
collectionEntry.Load();
context.Entry(manga).Navigation(nameof(Manga.Library)).Load();
return Ok(manga); return Ok(manga);
} }

View File

@@ -177,19 +177,19 @@ public class ComickIo : MangaConnector
byte whatever = 0; byte whatever = 0;
List<AltTitle> altTitles = altTitlesArray? List<AltTitle> altTitles = altTitlesArray?
.Select(token => new AltTitle(token.Value<string>("lang")??whatever++.ToString(), token.Value<string>("title")!)) .Select(token => new AltTitle(token.Value<string>("lang")??whatever++.ToString(), token.Value<string>("title")!))
.ToList()!; .ToList()??[];
JArray? authorsArray = json["authors"] as JArray; JArray? authorsArray = json["authors"] as JArray;
JArray? artistsArray = json["artists"] as JArray; JArray? artistsArray = json["artists"] as JArray;
List<Author> authors = authorsArray?.Concat(artistsArray!) List<Author> authors = authorsArray?.Concat(artistsArray!)
.Select(token => new Author(token.Value<string>("name")!)) .Select(token => new Author(token.Value<string>("name")!))
.DistinctBy(a => a.Key) .DistinctBy(a => a.Key)
.ToList()!; .ToList()??[];
JArray? genreArray = json["comic"]?["md_comic_md_genres"] as JArray; JArray? genreArray = json["comic"]?["md_comic_md_genres"] as JArray;
List<MangaTag> tags = genreArray? List<MangaTag> tags = genreArray?
.Select(token => new MangaTag(token["md_genres"]?.Value<string>("name")!)) .Select(token => new MangaTag(token["md_genres"]?.Value<string>("name")!))
.ToList()!; .ToList()??[];
JArray? linksArray = json["comic"]?["links"] as JArray; JArray? linksArray = json["comic"]?["links"] as JArray;
List<Link> links = linksArray? List<Link> links = linksArray?
@@ -221,7 +221,7 @@ public class ComickIo : MangaConnector
_ => kv.Key _ => kv.Key
}; };
return new Link(key, fullUrl); return new Link(key, fullUrl);
}).ToList()!; }).ToList()??[];
if(hid is null) if(hid is null)
throw new Exception("hid is null"); throw new Exception("hid is null");
@@ -232,7 +232,9 @@ public class ComickIo : MangaConnector
Manga manga = new (name, description??"", coverUrl, status, authors, tags, links, altTitles, Manga manga = new (name, description??"", coverUrl, status, authors, tags, links, altTitles,
year: year, originalLanguage: originalLanguage); year: year, originalLanguage: originalLanguage);
return (manga, new MangaConnectorId<Manga>(manga, this, hid, url)); MangaConnectorId<Manga> mcId = new (manga, this, hid, url);
manga.MangaConnectorIds.Add(mcId);
return (manga, mcId);
} }
private List<(Chapter, MangaConnectorId<Chapter>)> ParseChapters(MangaConnectorId<Manga> mcIdManga, JArray chaptersArray) private List<(Chapter, MangaConnectorId<Chapter>)> ParseChapters(MangaConnectorId<Manga> mcIdManga, JArray chaptersArray)
@@ -251,8 +253,9 @@ public class ComickIo : MangaConnector
continue; continue;
Chapter ch = new (mcIdManga.Obj, chapterNum, volumeNum, title); Chapter ch = new (mcIdManga.Obj, chapterNum, volumeNum, title);
MangaConnectorId<Chapter> mcId = new(ch, this, hid, url);
chapters.Add((ch, new (ch, this, hid, url))); ch.MangaConnectorIds.Add(mcId);
chapters.Add((ch, mcId));
} }
return chapters; return chapters;
} }

View File

@@ -313,9 +313,11 @@ public class MangaDex : MangaConnector
string websiteUrl = $"https://mangadex.org/title/{id}"; string websiteUrl = $"https://mangadex.org/title/{id}";
string coverUrl = $"https://uploads.mangadex.org/covers/{id}/{coverFileName}"; string coverUrl = $"https://uploads.mangadex.org/covers/{id}/{coverFileName}";
Manga manga = new Manga(name, description, coverUrl, releaseStatus, authors, tags, links,altTitles, Manga manga = new (name, description, coverUrl, releaseStatus, authors, tags, links,altTitles,
null, 0f, year, originalLanguage); null, 0f, year, originalLanguage);
return (manga, new MangaConnectorId<Manga>(manga, this, id, websiteUrl)); MangaConnectorId<Manga> mcId = new (manga, this, id, websiteUrl);
manga.MangaConnectorIds.Add(mcId);
return (manga, mcId);
} }
private (Chapter chapter, MangaConnectorId<Chapter> id) ParseChapterFromJToken(MangaConnectorId<Manga> mcIdManga, JToken jToken) private (Chapter chapter, MangaConnectorId<Chapter> id) ParseChapterFromJToken(MangaConnectorId<Manga> mcIdManga, JToken jToken)
@@ -334,6 +336,8 @@ public class MangaDex : MangaConnector
string websiteUrl = $"https://mangadex.org/chapter/{id}"; string websiteUrl = $"https://mangadex.org/chapter/{id}";
Chapter chapter = new (mcIdManga.Obj, chapterStr, volumeNumber, title); Chapter chapter = new (mcIdManga.Obj, chapterStr, volumeNumber, title);
return (chapter, new MangaConnectorId<Chapter>(chapter, this, id, websiteUrl)); MangaConnectorId<Chapter> mcId = new(chapter, this, id, websiteUrl);
chapter.MangaConnectorIds.Add(mcId);
return (chapter, mcId);
} }
} }

View File

@@ -39,6 +39,7 @@ public class Chapter : Identifiable, IComparable<Chapter>
this.Title = title; this.Title = title;
this.FileName = GetArchiveFilePath(); this.FileName = GetArchiveFilePath();
this.Downloaded = false; this.Downloaded = false;
this.MangaConnectorIds = [];
} }
/// <summary> /// <summary>

View File

@@ -61,6 +61,7 @@ public class Manga : Identifiable
this.Year = year; this.Year = year;
this.OriginalLanguage = originalLanguage; this.OriginalLanguage = originalLanguage;
this.Chapters = []; this.Chapters = [];
this.MangaConnectorIds = [];
} }
/// <summary> /// <summary>

View File

@@ -8,7 +8,7 @@ namespace API.Schema.MangaContext;
[PrimaryKey("Key")] [PrimaryKey("Key")]
public class MangaConnectorId<T> : Identifiable where T : Identifiable public class MangaConnectorId<T> : Identifiable where T : Identifiable
{ {
[StringLength(64)] [Required] public string ObjId { get; private set; } [StringLength(64)] [Required] public string ObjId { get; internal set; }
[JsonIgnore] public T Obj = null!; [JsonIgnore] public T Obj = null!;
[StringLength(32)] [Required] public string MangaConnectorName { get; private set; } [StringLength(32)] [Required] public string MangaConnectorName { get; private set; }

View File

@@ -1,6 +1,7 @@
using API.MangaConnectors; using API.MangaConnectors;
using API.Schema.MangaContext.MetadataFetchers; using API.Schema.MangaContext.MetadataFetchers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
namespace API.Schema.MangaContext; namespace API.Schema.MangaContext;
@@ -102,4 +103,23 @@ public class MangaContext(DbContextOptions<MangaContext> options) : TrangaBaseCo
.WithMany() .WithMany()
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
} }
public Manga? FindMangaLike(Manga other)
{
if (MangaIncludeAll().FirstOrDefault(m => m.Key == other.Key) is { } f)
return f;
return MangaIncludeAll()
.FirstOrDefault(m => m.Links.Any(l => l.Key == other.Key) ||
m.AltTitles.Any(t => other.AltTitles.Select(ot => ot.Title)
.Any(s => s.Equals(t.Title))));
}
public IIncludableQueryable<Manga, ICollection<MangaConnectorId<Manga>>> MangaIncludeAll() => Mangas.Include(m => m.Library)
.Include(m => m.Authors)
.Include(m => m.MangaTags)
.Include(m => m.Links)
.Include(m => m.AltTitles)
.Include(m => m.Chapters)
.Include(m => m.MangaConnectorIds);
} }

View File

@@ -9,7 +9,7 @@ using API.Workers;
using API.Workers.MaintenanceWorkers; using API.Workers.MaintenanceWorkers;
using log4net; using log4net;
using log4net.Config; using log4net.Config;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore;
namespace API; namespace API;
@@ -162,14 +162,25 @@ public static class Tranga
internal static bool AddMangaToContext(Manga addManga, MangaConnectorId<Manga> addMcId, MangaContext context, [NotNullWhen(true)]out Manga? manga) internal static bool AddMangaToContext(Manga addManga, MangaConnectorId<Manga> addMcId, MangaContext context, [NotNullWhen(true)]out Manga? manga)
{ {
manga = context.Mangas.Find(addManga.Key) ?? addManga; context.ChangeTracker.Clear();
MangaConnectorId<Manga> mcId = context.MangaConnectorToManga.Find(addMcId.Key) ?? addMcId; manga = context.FindMangaLike(addManga);
if (manga is not null)
{
foreach (MangaConnectorId<Manga> mcId in addManga.MangaConnectorIds)
{
mcId.Obj = manga; mcId.Obj = manga;
mcId.ObjId = manga.Key;
foreach (CollectionEntry collectionEntry in context.Entry(manga).Collections) }
collectionEntry.Load(); manga.MangaTags = manga.MangaTags.UnionBy(addManga.MangaTags, tag => tag.Tag).ToList();
context.Entry(manga).Navigation(nameof(Manga.Library)).Load(); manga.Authors = manga.Authors.UnionBy(addManga.Authors, author => author.Key).ToList();
manga.Links = manga.Links.UnionBy(addManga.Links, link => link.Key).ToList();
manga.AltTitles = manga.AltTitles.UnionBy(addManga.AltTitles, altTitle => altTitle.Key).ToList();
manga.Chapters = manga.Chapters.UnionBy(addManga.Chapters, chapter => chapter.Key).ToList();
manga.MangaConnectorIds = manga.MangaConnectorIds.UnionBy(addManga.MangaConnectorIds, id => id.MangaConnectorName).ToList();
}
else
{
manga = addManga;
IEnumerable<MangaTag> mergedTags = manga.MangaTags.Select(mt => IEnumerable<MangaTag> mergedTags = manga.MangaTags.Select(mt =>
{ {
MangaTag? inDb = context.Tags.Find(mt.Tag); MangaTag? inDb = context.Tags.Find(mt.Tag);
@@ -184,8 +195,8 @@ public static class Tranga
}); });
manga.Authors = mergedAuthors.ToList(); manga.Authors = mergedAuthors.ToList();
if(context.MangaConnectorToManga.Find(addMcId.Key) is null) context.Mangas.Add(manga);
context.MangaConnectorToManga.Add(mcId); }
if (context.Sync() is { success: false }) if (context.Sync() is { success: false })
return false; return false;
@@ -201,16 +212,19 @@ public static class Tranga
internal static bool AddChapterToContext(Chapter addChapter, MangaConnectorId<Chapter> addChId, MangaContext context, [NotNullWhen(true)] out Chapter? chapter) internal static bool AddChapterToContext(Chapter addChapter, MangaConnectorId<Chapter> addChId, MangaContext context, [NotNullWhen(true)] out Chapter? chapter)
{ {
chapter = context.Chapters.Find(addChapter.Key) ?? addChapter; chapter = context.Chapters.Where(ch => ch.Key == addChapter.Key)
MangaConnectorId<Chapter> chId = context.MangaConnectorToChapter.Find(addChId.Key) ?? addChId; .Include(ch => ch.ParentManga)
chId.Obj = chapter; .Include(ch => ch.MangaConnectorIds)
.FirstOrDefault();
foreach (CollectionEntry collectionEntry in context.Entry(chapter).Collections) if (chapter is not null)
collectionEntry.Load(); {
context.Entry(chapter).Navigation(nameof(Chapter.ParentManga)).Load(); chapter.MangaConnectorIds = chapter.MangaConnectorIds.UnionBy(addChapter.MangaConnectorIds, id => id.Key).ToList();
}
if(context.MangaConnectorToChapter.Find(chId.Key) is null) else
context.MangaConnectorToChapter.Add(chId); {
context.Chapters.Add(addChapter);
chapter = addChapter;
}
if (context.Sync() is { success: false }) if (context.Sync() is { success: false })
return false; return false;