using MangaReader.Core.Search; using MangaReader.Core.Sources.MangaDex.Api; using System.Text.RegularExpressions; namespace MangaReader.Core.Sources.MangaDex.Search; public partial class MangaDexSearchProvider(IMangaDexClient mangaDexClient) : IMangaSearchProvider, IMangaSourceComponent { [GeneratedRegex(@"[^a-z0-9\s-]")] private static partial Regex InvalidSlugCharactersRegex(); [GeneratedRegex(@"\s+")] private static partial Regex WhitespaceRegex(); public string SourceId => "MangaDex"; public async Task SearchAsync(string keyword, CancellationToken cancellationToken) { MangaDexResponse? response = await mangaDexClient.SearchMangaByTitleAsync(keyword, cancellationToken); if (response == null || (response is not MangaDexCollectionResponse collectionResponse)) return []; List mangaSearchResults = []; foreach (MangaDexEntity entity in collectionResponse.Data) { MangaSearchResult? mangaSearchResult = GetMangaSearchResult(entity); if (mangaSearchResult == null) continue; mangaSearchResults.Add(mangaSearchResult); } return [.. mangaSearchResults]; } private static MangaSearchResult? GetMangaSearchResult(MangaDexEntity entity) { if (entity is not MangaEntity mangaEntity) return null; if (mangaEntity.Attributes == null) return null; string title = mangaEntity.Attributes.Title.FirstOrDefault().Value; string slug = GenerateSlug(title); MangaSearchResult mangaSearchResult = new() { Title = title, Url = $"https://mangadex.org/title/{mangaEntity.Id}/{slug}", Thumbnail = GetThumbnail(mangaEntity) }; return mangaSearchResult; } public static string GenerateSlug(string title) { // title.ToLowerInvariant().Normalize(NormalizationForm.FormD); title = title.ToLowerInvariant(); //title = InvalidSlugCharactersRegex().Replace(title, ""); // remove invalid chars title = InvalidSlugCharactersRegex().Replace(title, "-"); // replace invalid chars with dash title = WhitespaceRegex().Replace(title, "-"); // replace spaces with dash return title.Trim('-'); } private static string? GetThumbnail(MangaDexEntity mangaDexEntity) { CoverArtEntity? coverArtEntity = (CoverArtEntity?)mangaDexEntity.Relationships.FirstOrDefault(entity => entity is CoverArtEntity); if (coverArtEntity == null || string.IsNullOrWhiteSpace(coverArtEntity.Attributes?.FileName)) return null; string? fileName = coverArtEntity.Attributes?.FileName; if (string.IsNullOrWhiteSpace(coverArtEntity.Attributes?.FileName)) return null; return $"https://mangadex.org/covers/{mangaDexEntity.Id}/{fileName}"; } }