Added English localization logic during the regular scan process.
This commit is contained in:
@@ -29,6 +29,7 @@ public sealed record VoiceWorkIngest
|
|||||||
public AIGeneration AI { get; init; }
|
public AIGeneration AI { get; init; }
|
||||||
public VoiceWorkSeries? Series { get; init; }
|
public VoiceWorkSeries? Series { get; init; }
|
||||||
public VoiceWorkTranslation? Translation { get; init; }
|
public VoiceWorkTranslation? Translation { get; init; }
|
||||||
|
public VoiceWorkLocalizationIngest[] Localizations { get; init; } = [];
|
||||||
|
|
||||||
public static VoiceWorkIngest From(DLSiteWork work, VoiceWorkDetails? details, ChobitResult? chobit)
|
public static VoiceWorkIngest From(DLSiteWork work, VoiceWorkDetails? details, ChobitResult? chobit)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using JSMR.Domain.Enums;
|
||||||
|
|
||||||
|
namespace JSMR.Application.Scanning.Contracts;
|
||||||
|
|
||||||
|
public sealed class VoiceWorkLocalizationIngest
|
||||||
|
{
|
||||||
|
public Language Language { get; init; }
|
||||||
|
public string? Title { get; init; }
|
||||||
|
public string? Description { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using JSMR.Application.Scanning.Contracts;
|
||||||
|
|
||||||
|
namespace JSMR.Application.Scanning.Ports;
|
||||||
|
|
||||||
|
public interface IVoiceWorkIngestBuilder
|
||||||
|
{
|
||||||
|
Task<VoiceWorkIngest[]> BuildAsync(VoiceWorkScanResult scanResult, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
using JSMR.Application.Common.Caching;
|
using JSMR.Application.Common.Caching;
|
||||||
using JSMR.Application.Integrations.Chobit.Models;
|
|
||||||
using JSMR.Application.Integrations.Chobit.Ports;
|
|
||||||
using JSMR.Application.Integrations.DLSite.Models;
|
|
||||||
using JSMR.Application.Integrations.DLSite.Models.ReleasedWorks;
|
|
||||||
using JSMR.Application.Integrations.DLSite.Ports;
|
|
||||||
using JSMR.Application.Scanning.Contracts;
|
using JSMR.Application.Scanning.Contracts;
|
||||||
using JSMR.Application.Scanning.Ports;
|
using JSMR.Application.Scanning.Ports;
|
||||||
|
|
||||||
@@ -12,10 +7,8 @@ namespace JSMR.Application.Scanning;
|
|||||||
public sealed class ScanVoiceWorksHandler(
|
public sealed class ScanVoiceWorksHandler(
|
||||||
IVoiceWorkScannerRepository scannerRepository,
|
IVoiceWorkScannerRepository scannerRepository,
|
||||||
IVoiceWorkUpdaterRepository updaterRepository,
|
IVoiceWorkUpdaterRepository updaterRepository,
|
||||||
IDLSiteClient dlsiteClient,
|
|
||||||
IChobitClient chobitClient,
|
|
||||||
ISpamCircleCache spamCircleCache,
|
ISpamCircleCache spamCircleCache,
|
||||||
IReleasedWorksProvider releasedWorksProvider,
|
IVoiceWorkIngestBuilder ingestBuilder,
|
||||||
IVoiceWorkSearchUpdater searchUpdater)
|
IVoiceWorkSearchUpdater searchUpdater)
|
||||||
{
|
{
|
||||||
public async Task<ScanVoiceWorksResponse> HandleAsync(ScanVoiceWorksRequest request, CancellationToken cancellationToken)
|
public async Task<ScanVoiceWorksResponse> HandleAsync(ScanVoiceWorksRequest request, CancellationToken cancellationToken)
|
||||||
@@ -47,19 +40,7 @@ public sealed class ScanVoiceWorksHandler(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] productIds = [.. scanResult.Works.Where(x => !string.IsNullOrWhiteSpace(x.ProductId)).Select(x => x.ProductId!)];
|
VoiceWorkIngest[] ingests = await ingestBuilder.BuildAsync(scanResult, cancellationToken);
|
||||||
VoiceWorkDetailCollection voiceWorkDetails = await dlsiteClient.GetVoiceWorkDetailsAsync(productIds, cancellationToken);
|
|
||||||
ChobitResultCollection chobitResults = await chobitClient.GetSampleInfoAsync(productIds, cancellationToken);
|
|
||||||
ReleasedWorksCollection releasedWorkCollection = await releasedWorksProvider.GetReleasedWorksAsync(scanResult, cancellationToken);
|
|
||||||
|
|
||||||
VoiceWorkIngest[] ingests = [.. scanResult.Works.Select(work =>
|
|
||||||
{
|
|
||||||
voiceWorkDetails.TryGetValue(work.ProductId!, out VoiceWorkDetails? value);
|
|
||||||
chobitResults.TryGetValue(work.ProductId, out ChobitResult? chobit);
|
|
||||||
releasedWorkCollection.TryGetValue(work.ProductId, out ReleasedWork? releasedWork);
|
|
||||||
|
|
||||||
return VoiceWorkIngest.From(work, value, chobit);
|
|
||||||
})];
|
|
||||||
|
|
||||||
VoiceWorkUpsertResult[] upsertResults = await updater.UpsertAsync(ingests, cancellationToken);
|
VoiceWorkUpsertResult[] upsertResults = await updater.UpsertAsync(ingests, cancellationToken);
|
||||||
int[] voiceWorkIds = [.. upsertResults.Where(x => x.VoiceWorkId.HasValue).Select(x => x.VoiceWorkId!.Value)];
|
int[] voiceWorkIds = [.. upsertResults.Where(x => x.VoiceWorkId.HasValue).Select(x => x.VoiceWorkId!.Value)];
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public static class InfrastructureServiceCollectionExtensions
|
|||||||
services.AddScoped<IVoiceWorkScannerRepository, VoiceWorkScannerRepository>();
|
services.AddScoped<IVoiceWorkScannerRepository, VoiceWorkScannerRepository>();
|
||||||
|
|
||||||
services.AddScoped<IReleasedWorksProvider, ReleasedWorksProvider>();
|
services.AddScoped<IReleasedWorksProvider, ReleasedWorksProvider>();
|
||||||
|
services.AddScoped<IVoiceWorkIngestBuilder, VoiceWorkIngestBuilder>();
|
||||||
|
|
||||||
services.AddKeyedScoped<IVoiceWorkUpdater, VoiceWorkUpdater>(Locale.Japanese);
|
services.AddKeyedScoped<IVoiceWorkUpdater, VoiceWorkUpdater>(Locale.Japanese);
|
||||||
services.AddKeyedScoped<IVoiceWorkUpdater, EnglishVoiceWorkUpdater>(Locale.English);
|
services.AddKeyedScoped<IVoiceWorkUpdater, EnglishVoiceWorkUpdater>(Locale.English);
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public class VoiceWorkUpdater(AppDbContext dbContext, ITimeProvider timeProvider
|
|||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
.Include(v => v.Creators)
|
.Include(v => v.Creators)
|
||||||
.Include(v => v.Tags)
|
.Include(v => v.Tags)
|
||||||
|
.Include(v => v.EnglishVoiceWorks)
|
||||||
.Include(v => v.Localizations)
|
.Include(v => v.Localizations)
|
||||||
.Include(v => v.SupportedLanguages)
|
.Include(v => v.SupportedLanguages)
|
||||||
.ToDictionaryAsync(v => v.ProductId, cancellationToken),
|
.ToDictionaryAsync(v => v.ProductId, cancellationToken),
|
||||||
@@ -161,6 +162,7 @@ public class VoiceWorkUpdater(AppDbContext dbContext, ITimeProvider timeProvider
|
|||||||
UpsertVoiceWorkCreators(ingest, upsertContext);
|
UpsertVoiceWorkCreators(ingest, upsertContext);
|
||||||
UpsertVoiceWorkSupportedLanguages(ingest, upsertContext);
|
UpsertVoiceWorkSupportedLanguages(ingest, upsertContext);
|
||||||
UpsertSeries(ingest, upsertContext);
|
UpsertSeries(ingest, upsertContext);
|
||||||
|
UpsertVoiceWorkLocalizations(ingest, upsertContext);
|
||||||
|
|
||||||
return dbContext.Entry(voiceWork).State switch
|
return dbContext.Entry(voiceWork).State switch
|
||||||
{
|
{
|
||||||
@@ -473,4 +475,39 @@ public class VoiceWorkUpdater(AppDbContext dbContext, ITimeProvider timeProvider
|
|||||||
|
|
||||||
return series;
|
return series;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpsertVoiceWorkLocalizations(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||||
|
{
|
||||||
|
// For now, just adding/updating English voice works
|
||||||
|
foreach (VoiceWorkLocalizationIngest localizationIngest in ingest.Localizations)
|
||||||
|
{
|
||||||
|
if (localizationIngest.Language is Language.English)
|
||||||
|
{
|
||||||
|
EnglishVoiceWork englishVoiceWork = GetOrAddEnglishVoiceWork(ingest, upsertContext);
|
||||||
|
englishVoiceWork.ProductName = localizationIngest.Title ?? string.Empty;
|
||||||
|
englishVoiceWork.Description = localizationIngest.Description ?? string.Empty;
|
||||||
|
englishVoiceWork.IsValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EnglishVoiceWork GetOrAddEnglishVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||||
|
{
|
||||||
|
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
|
||||||
|
EnglishVoiceWork? englishVoiceWork = voiceWork.EnglishVoiceWorks.FirstOrDefault();
|
||||||
|
|
||||||
|
if (englishVoiceWork is null)
|
||||||
|
{
|
||||||
|
englishVoiceWork = new EnglishVoiceWork
|
||||||
|
{
|
||||||
|
VoiceWork = voiceWork,
|
||||||
|
ProductName = string.Empty,
|
||||||
|
Description = string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
dbContext.EnglishVoiceWorks.Add(englishVoiceWork);
|
||||||
|
}
|
||||||
|
|
||||||
|
return englishVoiceWork;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ public class ReleasedWorksProvider(IDLSiteClient dlsiteClient) : IReleasedWorksP
|
|||||||
|
|
||||||
ReleasedWorksRequest releasedWorksRequest = new(
|
ReleasedWorksRequest releasedWorksRequest = new(
|
||||||
Locale: Locale.English,
|
Locale: Locale.English,
|
||||||
Date: requestDate,
|
Date: requestEndDate,
|
||||||
Period: period
|
Period: period
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
94
JSMR.Infrastructure/Scanning/VoiceWorkIngestBuilder.cs
Normal file
94
JSMR.Infrastructure/Scanning/VoiceWorkIngestBuilder.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using JSMR.Application.Integrations.Chobit.Models;
|
||||||
|
using JSMR.Application.Integrations.Chobit.Ports;
|
||||||
|
using JSMR.Application.Integrations.DLSite.Models;
|
||||||
|
using JSMR.Application.Integrations.DLSite.Models.ReleasedWorks;
|
||||||
|
using JSMR.Application.Integrations.DLSite.Ports;
|
||||||
|
using JSMR.Application.Scanning.Contracts;
|
||||||
|
using JSMR.Application.Scanning.Ports;
|
||||||
|
using JSMR.Domain.Enums;
|
||||||
|
using JSMR.Infrastructure.Common.Languages;
|
||||||
|
|
||||||
|
namespace JSMR.Infrastructure.Scanning;
|
||||||
|
|
||||||
|
public class VoiceWorkIngestBuilder(
|
||||||
|
IDLSiteClient dlsiteClient,
|
||||||
|
IChobitClient chobitClient,
|
||||||
|
IReleasedWorksProvider releasedWorksProvider,
|
||||||
|
ILanguageIdentifier languageIdentifier) : IVoiceWorkIngestBuilder
|
||||||
|
{
|
||||||
|
public async Task<VoiceWorkIngest[]> BuildAsync(VoiceWorkScanResult scanResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
string[] productIds = [.. scanResult.Works.Where(x => !string.IsNullOrWhiteSpace(x.ProductId)).Select(x => x.ProductId!)];
|
||||||
|
|
||||||
|
Task<VoiceWorkDetailCollection> detailsTask = dlsiteClient.GetVoiceWorkDetailsAsync(productIds, cancellationToken);
|
||||||
|
Task<ChobitResultCollection> chobitTask = chobitClient.GetSampleInfoAsync(productIds, cancellationToken);
|
||||||
|
Task<ReleasedWorksCollection> releasedTask = releasedWorksProvider.GetReleasedWorksAsync(scanResult, cancellationToken);
|
||||||
|
|
||||||
|
await Task.WhenAll(detailsTask, chobitTask, releasedTask);
|
||||||
|
|
||||||
|
VoiceWorkDetailCollection voiceWorkDetails = await detailsTask;
|
||||||
|
ChobitResultCollection chobitResults = await chobitTask;
|
||||||
|
ReleasedWorksCollection releasedWorkCollection = await releasedTask;
|
||||||
|
|
||||||
|
List<VoiceWorkIngest> ingests = [];
|
||||||
|
|
||||||
|
foreach (DLSiteWork work in scanResult.Works)
|
||||||
|
{
|
||||||
|
voiceWorkDetails.TryGetValue(work.ProductId!, out VoiceWorkDetails? details);
|
||||||
|
chobitResults.TryGetValue(work.ProductId, out ChobitResult? chobit);
|
||||||
|
releasedWorkCollection.TryGetValue(work.ProductId, out ReleasedWork? releasedWork);
|
||||||
|
|
||||||
|
VoiceWorkIngest ingest = new()
|
||||||
|
{
|
||||||
|
MakerId = work.MakerId,
|
||||||
|
MakerName = work.Maker,
|
||||||
|
ProductId = work.ProductId,
|
||||||
|
Title = details?.Title ?? work.ProductName,
|
||||||
|
Description = work.Description ?? string.Empty,
|
||||||
|
Tags = work.Tags,
|
||||||
|
Creators = work.Creators,
|
||||||
|
WishlistCount = details?.WishlistCount ?? 0,
|
||||||
|
Downloads = Math.Max(work.Downloads, details?.DownloadCount ?? 0),
|
||||||
|
HasTrial = work.HasTrial || (details?.HasTrial ?? false),
|
||||||
|
HasChobit = chobit?.Count > 0,
|
||||||
|
StarRating = work.StarRating,
|
||||||
|
Votes = work.Votes,
|
||||||
|
AgeRating = details?.AgeRating ?? work.AgeRating,
|
||||||
|
HasImage = !string.IsNullOrEmpty(work.ImageUrl) && !work.ImageUrl.Contains("no_img", StringComparison.OrdinalIgnoreCase),
|
||||||
|
SupportedLanguages = details?.SupportedLanguages ?? [],
|
||||||
|
ExpectedDate = work.ExpectedDate,
|
||||||
|
SalesDate = work.SalesDate,
|
||||||
|
RegistrationDate = details?.RegistrationDate,
|
||||||
|
AI = details?.AI ?? AIGeneration.None,
|
||||||
|
Series = details?.Series,
|
||||||
|
Translation = details?.Translation,
|
||||||
|
Localizations = GetLocalizationIngests(releasedWork)
|
||||||
|
};
|
||||||
|
|
||||||
|
ingests.Add(ingest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. ingests];
|
||||||
|
}
|
||||||
|
|
||||||
|
private VoiceWorkLocalizationIngest[] GetLocalizationIngests(ReleasedWork? releasedWork)
|
||||||
|
{
|
||||||
|
if (releasedWork is null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
Language titleLanguage = languageIdentifier.GetLanguage(releasedWork.Title);
|
||||||
|
Language descriptionLanguage = languageIdentifier.GetLanguage(releasedWork.Description);
|
||||||
|
|
||||||
|
if (titleLanguage is not Language.English && descriptionLanguage is not Language.English)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
VoiceWorkLocalizationIngest localizationIngest = new()
|
||||||
|
{
|
||||||
|
Title = releasedWork.Title,
|
||||||
|
Description = releasedWork.Description,
|
||||||
|
Language = Language.English
|
||||||
|
};
|
||||||
|
|
||||||
|
return [localizationIngest];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="8.0.0">
|
<PackageReference Include="coverlet.collector" Version="8.0.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -10,14 +10,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Bit.BlazorUI" Version="10.4.2" />
|
<PackageReference Include="Bit.BlazorUI" Version="10.4.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.5" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.5" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.9" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.5" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.5" />
|
||||||
<PackageReference Include="MudBlazor" Version="9.1.0" />
|
<PackageReference Include="MudBlazor" Version="9.2.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="Radzen.Blazor" Version="10.0.2" />
|
<PackageReference Include="Radzen.Blazor" Version="10.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -16,6 +16,14 @@
|
|||||||
},
|
},
|
||||||
"workingDirectory": ""
|
"workingDirectory": ""
|
||||||
},
|
},
|
||||||
|
"Scan (JP, 10 pages)": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "scan --locale Japanese --start 1 --end 10 --pageSize 100",
|
||||||
|
"environmentVariables": {
|
||||||
|
"DOTNET_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"workingDirectory": ""
|
||||||
|
},
|
||||||
"Scan (EN, 3 pages)": {
|
"Scan (EN, 3 pages)": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": "scan --locale English --start 1 --end 3 --pageSize 100",
|
"commandLineArgs": "scan --locale English --start 1 --end 3 --pageSize 100",
|
||||||
|
|||||||
Reference in New Issue
Block a user