Added additional voice work update logic.

This commit is contained in:
2025-10-11 17:28:39 -04:00
parent db0c3349a2
commit 278b6df650
38 changed files with 56745 additions and 64 deletions

View File

@@ -11,9 +11,9 @@ public class VoiceWorkSearchUpdater(AppDbContext dbContext) : IVoiceWorkSearchUp
{
List<VoiceWork> batch = await dbContext.VoiceWorks
.Include(vw => vw.Circle)
.Include(vw => vw.VoiceWorkTags)
.Include(vw => vw.Tags)
.ThenInclude(vwt => vwt.Tag)
.Include(vw => vw.VoiceWorkCreators)
.Include(vw => vw.Creators)
.ThenInclude(vwc => vwc.Creator)
.Include(vw => vw.EnglishVoiceWorks)
.Where(vw => voiceWorkIds.Contains(vw.VoiceWorkId))
@@ -71,7 +71,7 @@ public class VoiceWorkSearchUpdater(AppDbContext dbContext) : IVoiceWorkSearchUp
AppendRaw(sb, voiceWork.Description);
AppendRaw(sb, voiceWork.Circle?.Name);
foreach (var tag in voiceWork.VoiceWorkTags.Select(vwt => vwt.Tag))
foreach (var tag in voiceWork.Tags.Select(vwt => vwt.Tag))
{
if (tag is null)
continue;
@@ -86,7 +86,7 @@ public class VoiceWorkSearchUpdater(AppDbContext dbContext) : IVoiceWorkSearchUp
AppendRaw(sb, englishTag?.Name);
}
foreach (var creator in voiceWork.VoiceWorkCreators.Select(vwc => vwc.Creator))
foreach (var creator in voiceWork.Creators.Select(vwc => vwc.Creator))
{
if (creator is null)
continue;

View File

@@ -3,6 +3,8 @@
namespace JSMR.Infrastructure.Data.Repositories.VoiceWorks;
public record VoiceWorkUpsertContext(
DateTimeOffset CurrentScanAnchor,
DateTimeOffset PreviousScanAnchor,
Dictionary<string, Circle> Circles,
Dictionary<string, VoiceWork> VoiceWorks,
Dictionary<string, Tag> Tags,

View File

@@ -1,12 +1,14 @@
using JSMR.Application.Scanning.Contracts;
using JSMR.Application.Common;
using JSMR.Application.Scanning.Contracts;
using JSMR.Application.VoiceWorks.Commands.SetFavorite;
using JSMR.Application.VoiceWorks.Ports;
using JSMR.Domain.Entities;
using JSMR.Infrastructure.Common.Time;
using Microsoft.EntityFrameworkCore;
namespace JSMR.Infrastructure.Data.Repositories.VoiceWorks;
public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
public class VoiceWorkWriter(AppDbContext dbContext, ITimeProvider timeProvider) : IVoiceWorkWriter
{
public async Task<int[]> UpsertAsync(IReadOnlyCollection<VoiceWorkIngest> ingests, CancellationToken cancellationToken)
{
@@ -29,14 +31,21 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
string[] tagNames = [.. ingests.SelectMany(i => i.Tags).Distinct()];
string[] creatorNames = [.. ingests.SelectMany(i => i.Creators).Distinct()];
DateTimeOffset currentScanAnchor = GetCurrentScanAnchor();
DateTimeOffset previousScanAnchor = PreviousScanAnchor(currentScanAnchor);
VoiceWorkUpsertContext upsertContext = new(
CurrentScanAnchor: currentScanAnchor,
PreviousScanAnchor: previousScanAnchor,
Circles: await dbContext.Circles
.Where(c => makerIds.Contains(c.MakerId))
.ToDictionaryAsync(c => c.MakerId, cancellationToken),
VoiceWorks: await dbContext.VoiceWorks
.Where(v => productIds.Contains(v.ProductId))
.Include(v => v.VoiceWorkCreators)
.Include(v => v.VoiceWorkTags)
.Include(v => v.Creators)
.Include(v => v.Tags)
.Include(v => v.Localizations)
.Include(v => v.SupportedLanguages)
.ToDictionaryAsync(v => v.ProductId, cancellationToken),
Tags: await dbContext.Tags
.Where(t => tagNames.Contains(t.Name))
@@ -49,6 +58,25 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
return upsertContext;
}
private DateTimeOffset GetCurrentScanAnchor()
{
DateTimeOffset now = timeProvider.Now();
DateTimeOffset midnight = timeProvider.Local(now.Year, now.Month, now.Day, 0);
DateTimeOffset fourPm = timeProvider.Local(now.Year, now.Month, now.Day, 16);
return now >= fourPm ? fourPm : midnight;
}
private DateTimeOffset PreviousScanAnchor(DateTimeOffset scanAnchorTokyo)
{
// Normalize to Tokyo (no-op if already)
var a = timeProvider.Local(scanAnchorTokyo);
return a.Hour == 16
? timeProvider.Local(a.Year, a.Month, a.Day, 0)
: timeProvider.Local(a.AddDays(-1).Year, a.AddDays(-1).Month, a.AddDays(-1).Day, 16);
}
private void Upsert(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
{
UpsertCircle(ingest, upsertContext);
@@ -57,6 +85,7 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
UpsertVoiceWorkTags(ingest, upsertContext);
UpsertCreators(ingest, upsertContext);
UpsertVoiceWorkCreators(ingest, upsertContext);
UpsertVoiceWorkSupportedLanguages(ingest, upsertContext);
}
private void UpsertCircle(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
@@ -85,23 +114,40 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
private void UpsertVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
{
VoiceWork voiceWork = GetOrAddVoiceWork(ingest, upsertContext);
bool isAdded = dbContext.Entry(voiceWork).State == EntityState.Added;
bool isWithinCurrentScanAnchor = voiceWork.LastScannedDate == upsertContext.CurrentScanAnchor;
bool isNewOnSale = voiceWork.SalesDate is null && ingest.SalesDate is not null;
bool isNew = isAdded || isWithinCurrentScanAnchor || isNewOnSale;
voiceWork.Circle = upsertContext.Circles[ingest.MakerId];
voiceWork.ProductName = ingest.Title;
voiceWork.Description = ingest.Description;
voiceWork.HasImage = ingest.HasImage;
voiceWork.Rating = (int)ingest.AgeRating;
voiceWork.Downloads = ingest.Downloads;
voiceWork.WishlistCount = ingest.WishlistCount;
voiceWork.HasTrial = ingest.HasTrial;
voiceWork.HasChobit = ingest.HasDLPlay;
voiceWork.StarRating = ingest.StarRating;
voiceWork.Votes = ingest.Votes;
voiceWork.IsValid = true;
//var est = i.EstimatedDate?.ToDateTime(new TimeOnly(0, 0));
//if (vw2.ExpectedDate != est) { vw2.ExpectedDate = est; changed = true; }
//var sales = i.SalesDate?.ToDateTime(new TimeOnly(0, 0));
//if (vw2.SalesDate != sales) { vw2.SalesDate = sales; changed = true; }
if (ingest.SalesDate.HasValue)
{
voiceWork.SalesDate = ingest.SalesDate.Value.ToDateTime(new TimeOnly(0, 0));
voiceWork.ExpectedDate = null;
voiceWork.PlannedReleaseDate = null;
voiceWork.Status = isNew ? (byte)VoiceWorkStatus.NewRelease : (byte)VoiceWorkStatus.Available;
}
else
{
voiceWork.SalesDate = null;
voiceWork.ExpectedDate = ingest.ExpectedDate?.ToDateTime(new TimeOnly(0, 0));
voiceWork.PlannedReleaseDate = ingest.RegistrationDate > upsertContext.CurrentScanAnchor ? ingest.RegistrationDate : null;
voiceWork.Status = isNew ? (byte)VoiceWorkStatus.NewAndUpcoming : (byte)VoiceWorkStatus.Upcoming;
}
}
private VoiceWork GetOrAddVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
@@ -144,17 +190,17 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
return tag;
}
private void UpsertCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext something)
private void UpsertCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
{
foreach (string creatorName in ingest.Creators)
{
GetOrAddCreator(creatorName, something);
GetOrAddCreator(creatorName, upsertContext);
}
}
private Creator GetOrAddCreator(string creatorName, VoiceWorkUpsertContext something)
private Creator GetOrAddCreator(string creatorName, VoiceWorkUpsertContext upsertContext)
{
if (!something.Creators.TryGetValue(creatorName, out Creator? creator))
if (!upsertContext.Creators.TryGetValue(creatorName, out Creator? creator))
{
creator = new Creator
{
@@ -162,22 +208,22 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
};
dbContext.Creators.Add(creator);
something.Creators[creatorName] = creator;
upsertContext.Creators[creatorName] = creator;
}
return creator;
}
private void UpsertVoiceWorkTags(VoiceWorkIngest ingest, VoiceWorkUpsertContext something)
private void UpsertVoiceWorkTags(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
{
VoiceWork voiceWork = something.VoiceWorks[ingest.ProductId];
Dictionary<int, VoiceWorkTag> existingTagLinks = voiceWork.VoiceWorkTags.ToDictionary(x => x.TagId);
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
Dictionary<int, VoiceWorkTag> existingTagLinks = voiceWork.Tags.ToDictionary(x => x.TagId);
int position = 1;
foreach (string tagName in ingest.Tags)
{
Tag tag = something.Tags[tagName];
Tag tag = upsertContext.Tags[tagName];
if (!existingTagLinks.TryGetValue(tag.TagId, out VoiceWorkTag? voiceWorkTag))
{
@@ -195,16 +241,16 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
}
}
private void UpsertVoiceWorkCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext something)
private void UpsertVoiceWorkCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
{
VoiceWork voiceWork = something.VoiceWorks[ingest.ProductId];
Dictionary<int, VoiceWorkCreator> existingCreatorLinks = voiceWork.VoiceWorkCreators.ToDictionary(x => x.CreatorId);
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
Dictionary<int, VoiceWorkCreator> existingCreatorLinks = voiceWork.Creators.ToDictionary(x => x.CreatorId);
int position = 1;
foreach (string creatorName in ingest.Creators)
{
Creator creator = something.Creators[creatorName];
Creator creator = upsertContext.Creators[creatorName];
if (!existingCreatorLinks.TryGetValue(creator.CreatorId, out VoiceWorkCreator? voiceWorkCreator))
{
@@ -222,6 +268,26 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
}
}
private void UpsertVoiceWorkSupportedLanguages(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
{
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
Dictionary<string, VoiceWorkSupportedLanguage> existingLanguageLinks = voiceWork.SupportedLanguages.ToDictionary(x => x.Language);
foreach (ISupportedLanguage supportedLanguage in ingest.SupportedLanguages)
{
if (!existingLanguageLinks.TryGetValue(supportedLanguage.Code, out VoiceWorkSupportedLanguage? voiceWorkSupportedLanguage))
{
voiceWorkSupportedLanguage = new VoiceWorkSupportedLanguage
{
VoiceWork = voiceWork,
Language = supportedLanguage.Code
};
dbContext.VoiceWorkSupportedLanguages.Add(voiceWorkSupportedLanguage);
}
}
}
public async Task<SetVoiceWorkFavoriteResponse> SetFavoriteAsync(SetVoiceWorkFavoriteRequest request, CancellationToken cancellationToken)
{
VoiceWork voiceWork = await GetVoiceWorkAsync(request.VoiceWorkId, cancellationToken);