Manga search provider updates.
This commit is contained in:
25
MangaReader.Core/Extensions/ServiceCollectionExtensions.cs
Normal file
25
MangaReader.Core/Extensions/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using MangaReader.Core.Search;
|
||||||
|
using MangaReader.Core.Search.MangaDex;
|
||||||
|
using MangaReader.Core.Search.NatoManga;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#pragma warning disable IDE0130 // Namespace does not match folder structure
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
|
#pragma warning restore IDE0130 // Namespace does not match folder structure
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddMangaReader(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IMangaSearchProvider, NatoMangaSearchProvider>();
|
||||||
|
services.AddScoped<IMangaSearchProvider, MangaDexSearchProvider>();
|
||||||
|
services.AddScoped<IMangaSearchCoordinator, MangaSearchCoordinator>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
MangaReader.Core/Search/IMangaSearchCoordinator.cs
Normal file
6
MangaReader.Core/Search/IMangaSearchCoordinator.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace MangaReader.Core.Search;
|
||||||
|
|
||||||
|
public interface IMangaSearchCoordinator
|
||||||
|
{
|
||||||
|
Task<MangaSearchResult[]> SearchAsync(string keyword, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
namespace MangaReader.Core.Search;
|
namespace MangaReader.Core.Search;
|
||||||
|
|
||||||
public interface IMangaSearchProvider<T>
|
public interface IMangaSearchProvider
|
||||||
{
|
{
|
||||||
Task<MangaSearchResult[]> SearchAsync(string keyword);
|
Task<MangaSearchResult[]> SearchAsync(string keyword, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
//public class MangaDexWebSearch : IMangaWebSearch
|
|
||||||
//{
|
|
||||||
// // https://api.mangadex.org/manga?title=gal can't be&limit=5
|
|
||||||
//}
|
|
||||||
@@ -4,8 +4,14 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace MangaReader.Core.Search.MangaDex;
|
namespace MangaReader.Core.Search.MangaDex;
|
||||||
|
|
||||||
public class MangaDexSearchProvider(IHttpService httpService) : MangaSearchProviderBase<MangaDexSearchResult>(httpService)
|
public partial class MangaDexSearchProvider(IHttpService httpService) : MangaSearchProviderBase<MangaDexSearchResult>(httpService)
|
||||||
{
|
{
|
||||||
|
[GeneratedRegex(@"[^a-z0-9\s-]")]
|
||||||
|
private static partial Regex InvalidSlugCharactersRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"\s+")]
|
||||||
|
private static partial Regex WhitespaceRegex();
|
||||||
|
|
||||||
protected override string GetSearchUrl(string keyword)
|
protected override string GetSearchUrl(string keyword)
|
||||||
{
|
{
|
||||||
string normalizedKeyword = keyword.ToLowerInvariant().Normalize(NormalizationForm.FormD);
|
string normalizedKeyword = keyword.ToLowerInvariant().Normalize(NormalizationForm.FormD);
|
||||||
@@ -41,9 +47,9 @@ public class MangaDexSearchProvider(IHttpService httpService) : MangaSearchProvi
|
|||||||
// title.ToLowerInvariant().Normalize(NormalizationForm.FormD);
|
// title.ToLowerInvariant().Normalize(NormalizationForm.FormD);
|
||||||
|
|
||||||
title = title.ToLowerInvariant();
|
title = title.ToLowerInvariant();
|
||||||
//title = Regex.Replace(title, @"[^a-z0-9\s-]", ""); // remove invalid chars
|
//title = InvalidSlugCharactersRegex().Replace(title, ""); // remove invalid chars
|
||||||
title = Regex.Replace(title, @"[^a-z0-9\s-]", "-"); // replace invalid chars with dash
|
title = InvalidSlugCharactersRegex().Replace(title, "-"); // replace invalid chars with dash
|
||||||
title = Regex.Replace(title, @"\s+", "-"); // replace spaces with dash
|
title = WhitespaceRegex().Replace(title, "-"); // replace spaces with dash
|
||||||
|
|
||||||
return title.Trim('-');
|
return title.Trim('-');
|
||||||
}
|
}
|
||||||
|
|||||||
12
MangaReader.Core/Search/MangaSearchCoordinator.cs
Normal file
12
MangaReader.Core/Search/MangaSearchCoordinator.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace MangaReader.Core.Search;
|
||||||
|
|
||||||
|
public class MangaSearchCoordinator(IEnumerable<IMangaSearchProvider> searchProviders) : IMangaSearchCoordinator
|
||||||
|
{
|
||||||
|
public async Task<MangaSearchResult[]> SearchAsync(string keyword, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var searchTasks = searchProviders.Select(searchProvider => searchProvider.SearchAsync(keyword, cancellationToken));
|
||||||
|
var allResults = await Task.WhenAll(searchTasks);
|
||||||
|
|
||||||
|
return [.. allResults.SelectMany(mangaSearchResults => mangaSearchResults)];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,14 +3,14 @@ using System.Text.Json;
|
|||||||
|
|
||||||
namespace MangaReader.Core.Search;
|
namespace MangaReader.Core.Search;
|
||||||
|
|
||||||
public abstract class MangaSearchProviderBase<T>(IHttpService httpService) : IMangaSearchProvider<T>
|
public abstract class MangaSearchProviderBase<T>(IHttpService httpService) : IMangaSearchProvider
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public async Task<MangaSearchResult[]> SearchAsync(string keyword)
|
public async Task<MangaSearchResult[]> SearchAsync(string keyword, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
T? searchResult = await GetSearchResultAsync(keyword);
|
T? searchResult = await GetSearchResultAsync(keyword);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class MangaDexSearchTests
|
|||||||
.Returns(Task.FromResult(searchResultJson));
|
.Returns(Task.FromResult(searchResultJson));
|
||||||
|
|
||||||
MangaDexSearchProvider searchProvider = new(httpService);
|
MangaDexSearchProvider searchProvider = new(httpService);
|
||||||
MangaSearchResult[] searchResult = await searchProvider.SearchAsync("Gal Can't Be Kind");
|
MangaSearchResult[] searchResult = await searchProvider.SearchAsync("Gal Can't Be Kind", CancellationToken.None);
|
||||||
|
|
||||||
searchResult.Length.ShouldBe(3);
|
searchResult.Length.ShouldBe(3);
|
||||||
searchResult[0].Title.ShouldBe("Gals Can’t Be Kind to Otaku!?");
|
searchResult[0].Title.ShouldBe("Gals Can’t Be Kind to Otaku!?");
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class NatoMangaWebSearchTests
|
|||||||
.Returns(Task.FromResult(searchResultJson));
|
.Returns(Task.FromResult(searchResultJson));
|
||||||
|
|
||||||
NatoMangaSearchProvider searchProvider = new(httpService);
|
NatoMangaSearchProvider searchProvider = new(httpService);
|
||||||
MangaSearchResult[] searchResult = await searchProvider.SearchAsync("Gal Can't Be Kind");
|
MangaSearchResult[] searchResult = await searchProvider.SearchAsync("Gal Can't Be Kind", CancellationToken.None);
|
||||||
|
|
||||||
searchResult.Length.ShouldBe(2);
|
searchResult.Length.ShouldBe(2);
|
||||||
searchResult[0].Title.ShouldBe("Gal Can't Be Kind to Otaku!");
|
searchResult[0].Title.ShouldBe("Gal Can't Be Kind to Otaku!");
|
||||||
|
|||||||
Reference in New Issue
Block a user