@@ -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 . VoiceWork Creators)
. Include ( v = > v . VoiceWork Tags)
. 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 . VoiceWork Tags. 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 . VoiceWork Creators. 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 ) ;