using MangaReader.Core.Metadata; using MangaReader.Core.Sources.MangaDex.Api; namespace MangaReader.Core.Sources.MangaDex.Metadata; public class MangaDexMetadataProvider(IMangaDexClient mangaDexClient) : IMangaMetadataProvider { public string SourceId => "MangaDex"; public async Task GetMangaAsync(string url, CancellationToken cancellationToken) { Guid mangaGuid = GetSourceMangaGuid(url); MangaDexResponse? mangaDexResponse = await mangaDexClient.GetMangaAsync(mangaGuid, cancellationToken); if (mangaDexResponse == null || mangaDexResponse is not MangaDexEntityResponse mangaDexEntityResponse) return null; if (mangaDexEntityResponse.Data == null || mangaDexEntityResponse.Data is not MangaEntity mangaEntity) return null; if (mangaEntity.Attributes == null) return null; MangaAttributes mangaAttributes = mangaEntity.Attributes; List mangaRelationships = mangaEntity.Relationships; MangaDexResponse? mangaDexFeedResponse = await mangaDexClient.GetFeedAsync(mangaGuid, cancellationToken); return new SourceManga() { Title = GetTitle(mangaAttributes), AlternateTitles = GetAlternateTitles(mangaAttributes), Genres = GetGenres(mangaAttributes), Contributors = GetContributors(mangaRelationships), Chapters = GetChapters(mangaDexFeedResponse) }; } private static Guid GetSourceMangaGuid(string url) { string[] parts = url.Split('/'); if (parts.Length < 5 || Guid.TryParse(parts[4], out Guid mangaGuid) == false) { throw new Exception("Unable to get guid from MangaDex url: " + url); } return mangaGuid; } private static string GetTitle(MangaAttributes attributes) { if (attributes.Title.TryGetValue("en", out string? title)) return title; return string.Empty; } private static List GetAlternateTitles(MangaAttributes attributes) { if (attributes.AltTitles == null || attributes.AltTitles.Count == 0) return []; Dictionary languageIdMap = new() { { "en", SourceMangaLanguage.English }, { "ja", SourceMangaLanguage.Japanese }, { "ja-ro", SourceMangaLanguage.Romanji }, }; List sourceMangaTitles = []; foreach (Dictionary alternateTitle in attributes.AltTitles) { foreach (string alternateTitleKey in alternateTitle.Keys) { if (languageIdMap.TryGetValue(alternateTitleKey, out SourceMangaLanguage language) == false) continue; SourceMangaTitle sourceMangaTitle = new() { Title = alternateTitle[alternateTitleKey], Language = language }; sourceMangaTitles.Add(sourceMangaTitle); } } return sourceMangaTitles; } private static List GetGenres(MangaAttributes attributes) { if (attributes.Tags == null || attributes.Tags.Count == 0) return []; List tags = []; foreach (TagEntity tagEntity in attributes.Tags) { if (tagEntity.Attributes == null) continue; if (tagEntity.Attributes.Name == null || tagEntity.Attributes.Name.Count == 0) continue; tags.Add(tagEntity.Attributes.Name.FirstOrDefault().Value); } return tags; } private static SourceMangaContributor[] GetContributors(List relationships) { List contributors = []; AuthorEntity[] authorEntities = [.. relationships.Where(entity => entity is AuthorEntity).Cast()]; ArtistEntity[] artistEntities = [.. relationships.Where(entity => entity is ArtistEntity).Cast()]; foreach (AuthorEntity authorEntity in authorEntities) { if (authorEntity.Attributes == null) continue; SourceMangaContributor contributor = new() { Name = authorEntity.Attributes.Name, Role = SourceMangaContributorRole.Author }; contributors.Add(contributor); } foreach (ArtistEntity artistEntity in artistEntities) { if (artistEntity.Attributes == null) continue; SourceMangaContributor contributor = new() { Name = artistEntity.Attributes.Name, Role = SourceMangaContributorRole.Artist }; contributors.Add(contributor); } return [.. contributors]; } private static List GetChapters(MangaDexResponse? mangaDexFeedResponse) { if (mangaDexFeedResponse == null || mangaDexFeedResponse is not MangaDexCollectionResponse collectionResponse) return []; //https://mangadex.org/chapter/46084762-855c-46dd-a7b6-66e5cd15604d List chapters = []; ChapterEntity[] chapterEntities = [.. collectionResponse.Data.Where(entity => entity is ChapterEntity).Cast()]; foreach (ChapterEntity chapterEntity in chapterEntities) { if (chapterEntity.Attributes == null || chapterEntity.Attributes.TranslatedLanguage != "en") continue; int? volume = int.TryParse(chapterEntity.Attributes.Volume, out var temp) ? temp : null; if (float.TryParse(chapterEntity.Attributes.Chapter, out float chapterNumber) == false) continue; SourceMangaChapter chapter = new() { Volume = volume, Number = chapterNumber, Title = chapterEntity.Attributes.Title, Url = $"https://mangadex.org/chapter/{chapterEntity.Id}" }; chapters.Add(chapter); } return chapters; } }