219 lines
6.1 KiB
C#
219 lines
6.1 KiB
C#
using MangaReader.Core.Data;
|
|
using MangaReader.Core.Metadata;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace MangaReader.Core.Pipeline;
|
|
|
|
public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|
{
|
|
enum TitleType
|
|
{
|
|
Primary,
|
|
Secondary
|
|
}
|
|
|
|
public async Task RunAsync(MangaPipelineRequest request)
|
|
{
|
|
string sourceName = request.SourceName;
|
|
string sourceUrl = request.SourceUrl;
|
|
SourceManga sourceManga = request.SourceManga;
|
|
|
|
Source source = await GetOrAddSourceAsync(sourceName);
|
|
Manga manga = await GetOrAddMangaAsync(sourceManga);
|
|
|
|
await AddMangaSourceAsync(sourceUrl, manga, source);
|
|
await AddTitleAsync(manga, sourceManga.Title, TitleType.Primary);
|
|
await AddDescriptionAsync(manga, sourceManga.Description);
|
|
|
|
foreach (SourceMangaTitle alternateTitle in sourceManga.AlternateTitles)
|
|
{
|
|
await AddTitleAsync(manga, alternateTitle, TitleType.Secondary);
|
|
}
|
|
|
|
foreach (string genre in sourceManga.Genres)
|
|
{
|
|
await LinkGenreAsync(manga, genre);
|
|
}
|
|
|
|
foreach (SourceMangaChapter chapter in sourceManga.Chapters)
|
|
{
|
|
await AddChapterAsync(manga, chapter);
|
|
}
|
|
|
|
context.SaveChanges();
|
|
}
|
|
|
|
private async Task<Source> GetOrAddSourceAsync(string sourceName)
|
|
{
|
|
Source? source = await context.Sources.FirstOrDefaultAsync(s => s.Name == sourceName);
|
|
|
|
if (source != null)
|
|
return source;
|
|
|
|
source = new()
|
|
{
|
|
Name = sourceName
|
|
};
|
|
|
|
context.Sources.Add(source);
|
|
|
|
return source;
|
|
}
|
|
|
|
private async Task<Manga> GetOrAddMangaAsync(SourceManga sourceManga)
|
|
{
|
|
Manga? manga = await context.Mangas.FirstOrDefaultAsync(manga =>
|
|
manga.Titles.Any(mangaTitle => mangaTitle.Name == sourceManga.Title.Name));
|
|
|
|
if (manga != null)
|
|
return manga;
|
|
|
|
manga = new()
|
|
{
|
|
Slug = GenerateSlug(sourceManga.Title.Name),
|
|
};
|
|
|
|
context.Add(manga);
|
|
|
|
return manga;
|
|
}
|
|
|
|
private static string GenerateSlug(string title)
|
|
{
|
|
title = title.ToLowerInvariant();
|
|
title = RemoveInvalidCharsRegex().Replace(title, ""); // remove invalid chars
|
|
title = RemoveSpacesWithDashRegex().Replace(title, "-"); // replace spaces with dash
|
|
|
|
return title.Trim('-');
|
|
}
|
|
|
|
[GeneratedRegex(@"[^a-z0-9\s-]")]
|
|
private static partial Regex RemoveInvalidCharsRegex();
|
|
|
|
[GeneratedRegex(@"\s+")]
|
|
private static partial Regex RemoveSpacesWithDashRegex();
|
|
|
|
private async Task AddMangaSourceAsync(string sourceUrl, Manga manga, Source source)
|
|
{
|
|
MangaSource? mangaSource = await context.MangaSources.FirstOrDefaultAsync(ms =>
|
|
ms.Manga == manga && ms.Source == source && ms.Url == sourceUrl);
|
|
|
|
if (mangaSource != null)
|
|
return;
|
|
|
|
mangaSource = new()
|
|
{
|
|
Manga = manga,
|
|
Source = source,
|
|
Url = sourceUrl
|
|
};
|
|
|
|
context.MangaSources.Add(mangaSource);
|
|
}
|
|
|
|
private async Task AddTitleAsync(Manga manga, SourceMangaTitle sourceMangaTitle, TitleType titleType)
|
|
{
|
|
MangaTitle? mangaTitle = await context.MangaTitles.FirstOrDefaultAsync(mt =>
|
|
mt.Manga == manga && mt.Name == sourceMangaTitle.Name);
|
|
|
|
if (mangaTitle != null)
|
|
return;
|
|
|
|
mangaTitle = new()
|
|
{
|
|
Manga = manga,
|
|
Name = sourceMangaTitle.Name,
|
|
Language = sourceMangaTitle.Language,
|
|
IsPrimary = titleType == TitleType.Primary
|
|
};
|
|
|
|
context.MangaTitles.Add(mangaTitle);
|
|
}
|
|
|
|
private async Task AddDescriptionAsync(Manga manga, SourceMangaDescription? sourceMangaDescription)
|
|
{
|
|
if (sourceMangaDescription == null)
|
|
return;
|
|
|
|
MangaDescription? mangaDescription = await context.MangaDescriptions.FirstOrDefaultAsync(md =>
|
|
md.Manga == manga && md.Name == sourceMangaDescription.Name);
|
|
|
|
if (mangaDescription != null)
|
|
return;
|
|
|
|
mangaDescription = new()
|
|
{
|
|
Manga = manga,
|
|
Name = sourceMangaDescription.Name,
|
|
Language = sourceMangaDescription.Language
|
|
};
|
|
|
|
context.MangaDescriptions.Add(mangaDescription);
|
|
}
|
|
|
|
private async Task LinkGenreAsync(Manga manga, string genreName)
|
|
{
|
|
Genre genre = await GetOrAddGenreAsync(genreName);
|
|
|
|
MangaGenre? mangaGenre = await context.MangaGenres.FirstOrDefaultAsync(x => x.Manga == manga && x.Genre == genre);
|
|
|
|
if (mangaGenre != null)
|
|
return;
|
|
|
|
mangaGenre = new()
|
|
{
|
|
Manga = manga,
|
|
Genre = genre
|
|
};
|
|
|
|
context.MangaGenres.Add(mangaGenre);
|
|
}
|
|
|
|
private async Task<Genre> GetOrAddGenreAsync(string genreName)
|
|
{
|
|
Genre? genre = await context.Genres.FirstOrDefaultAsync(x => x.Name == genreName);
|
|
|
|
if (genre != null)
|
|
return genre;
|
|
|
|
genre = new()
|
|
{
|
|
Name = genreName,
|
|
};
|
|
|
|
await context.Genres.AddAsync(genre);
|
|
|
|
return genre;
|
|
}
|
|
|
|
private async Task AddChapterAsync(Manga manga, SourceMangaChapter sourceMangaChapter)
|
|
{
|
|
MangaChapter mangaChapter = await context.MangaChapters.FirstOrDefaultAsync(x => x.ChapterNumber == sourceMangaChapter.Number)
|
|
?? AddMangaChapter(manga, sourceMangaChapter);
|
|
|
|
if (mangaChapter.VolumeNumber is null && sourceMangaChapter.Volume is not null)
|
|
{
|
|
mangaChapter.VolumeNumber = sourceMangaChapter.Volume;
|
|
}
|
|
|
|
if (mangaChapter.Title is null && sourceMangaChapter.Title is not null)
|
|
{
|
|
mangaChapter.Title = sourceMangaChapter.Title;
|
|
}
|
|
}
|
|
|
|
private MangaChapter AddMangaChapter(Manga manga, SourceMangaChapter sourceMangaChapter)
|
|
{
|
|
MangaChapter mangaChapter = new()
|
|
{
|
|
Manga = manga,
|
|
ChapterNumber = sourceMangaChapter.Number
|
|
};
|
|
|
|
context.MangaChapters.Add(mangaChapter);
|
|
|
|
return mangaChapter;
|
|
}
|
|
}
|