@@ -5,10 +5,12 @@ import { type RecordingFormData } from '#app/components/calls/recording-form.tsx
55import {
66 deleteAudioObject ,
77 getAudioBuffer ,
8+ parseBase64DataUrl ,
89 putCallAudioFromDataUrl ,
10+ putEpisodeDraftResponseAudioFromBuffer ,
911} from '#app/utils/call-kent-audio-storage.server.ts'
1012import { startCallKentCallerTranscriptProcessing } from '#app/utils/call-kent-caller-transcript.server.ts'
11- import { startCallKentEpisodeDraftProcessing } from '#app/utils/call-kent-episode-draft .server.ts'
13+ import { requestCallKentEpisodeAudioGeneration } from '#app/utils/call-kent-audio-processor .server.ts'
1214import { getPublishedCallKentEpisodeEmail } from '#app/utils/call-kent-published-email.ts'
1315import {
1416 getErrorForAudio ,
@@ -355,9 +357,13 @@ async function publishCall({
355357 }
356358
357359 // Best-effort cleanup of stored audio blobs after publish.
358- const keysToDelete = [ call . audioKey , draft . episodeAudioKey ] . filter (
359- ( k ) : k is string => typeof k === 'string' && k . length > 0 ,
360- )
360+ const keysToDelete = [
361+ call . audioKey ,
362+ draft . episodeAudioKey ,
363+ draft . responseAudioKey ,
364+ draft . callerSegmentAudioKey ,
365+ draft . responseSegmentAudioKey ,
366+ ] . filter ( ( k ) : k is string => typeof k === 'string' && k . length > 0 )
361367 await Promise . all (
362368 keysToDelete . map ( async ( key ) =>
363369 deleteAudioObject ( { key } ) . catch ( ( ) => { } ) ,
@@ -434,11 +440,24 @@ async function createEpisodeDraft({
434440 // If we're replacing a draft, clean up the old stored audio blob.
435441 const existingDraft = await prisma . callKentEpisodeDraft . findFirst ( {
436442 where : { callId } ,
437- select : { episodeAudioKey : true } ,
443+ select : {
444+ episodeAudioKey : true ,
445+ responseAudioKey : true ,
446+ callerSegmentAudioKey : true ,
447+ responseSegmentAudioKey : true ,
448+ } ,
438449 } )
439- if ( existingDraft ?. episodeAudioKey ) {
440- await deleteAudioObject ( { key : existingDraft . episodeAudioKey } ) . catch (
441- ( ) => { } ,
450+ const existingKeysToDelete = [
451+ existingDraft ?. episodeAudioKey ,
452+ existingDraft ?. responseAudioKey ,
453+ existingDraft ?. callerSegmentAudioKey ,
454+ existingDraft ?. responseSegmentAudioKey ,
455+ ] . filter ( ( key ) : key is string => typeof key === 'string' && key . length > 0 )
456+ if ( existingKeysToDelete . length ) {
457+ await Promise . all (
458+ existingKeysToDelete . map ( async ( key ) =>
459+ deleteAudioObject ( { key } ) . catch ( ( ) => { } ) ,
460+ ) ,
442461 )
443462 }
444463
@@ -452,9 +471,47 @@ async function createEpisodeDraft({
452471 } ) ,
453472 ] )
454473
455- void startCallKentEpisodeDraftProcessing ( draft . id , {
456- responseBase64 : responseAudio ! ,
457- } )
474+ try {
475+ if ( ! call . audioKey ) {
476+ throw new Error ( 'Call audio is missing (audioKey is null).' )
477+ }
478+ const parsedResponseAudio = parseBase64DataUrl ( responseAudio ! )
479+ const storedResponseAudio = await putEpisodeDraftResponseAudioFromBuffer ( {
480+ draftId : draft . id ,
481+ audio : parsedResponseAudio . buffer ,
482+ contentType : parsedResponseAudio . contentType ,
483+ } )
484+ await prisma . callKentEpisodeDraft . updateMany ( {
485+ where : { id : draft . id , status : 'PROCESSING' } ,
486+ data : {
487+ responseAudioKey : storedResponseAudio . key ,
488+ responseAudioContentType : storedResponseAudio . contentType ,
489+ responseAudioSize : storedResponseAudio . size ,
490+ step : 'GENERATING_AUDIO' ,
491+ errorMessage : null ,
492+ } ,
493+ } )
494+ await requestCallKentEpisodeAudioGeneration ( {
495+ draftId : draft . id ,
496+ callAudioKey : call . audioKey ,
497+ responseAudioKey : storedResponseAudio . key ,
498+ } )
499+ } catch ( error : unknown ) {
500+ await prisma . callKentEpisodeDraft . updateMany ( {
501+ where : { id : draft . id , status : 'PROCESSING' } ,
502+ data : {
503+ status : 'ERROR' ,
504+ errorMessage : getErrorMessage ( error ) ,
505+ step : 'DONE' ,
506+ } ,
507+ } )
508+ const searchParams = new URLSearchParams ( )
509+ searchParams . set (
510+ 'error' ,
511+ `Unable to start draft audio generation: ${ getErrorMessage ( error ) } ` ,
512+ )
513+ return redirect ( `/calls/admin/${ callId } ?${ searchParams . toString ( ) } ` )
514+ }
458515
459516 return redirect ( `/calls/admin/${ callId } ` )
460517}
@@ -547,14 +604,26 @@ async function undoEpisodeDraft({
547604
548605 const drafts = await prisma . callKentEpisodeDraft . findMany ( {
549606 where : { callId } ,
550- select : { episodeAudioKey : true } ,
607+ select : {
608+ episodeAudioKey : true ,
609+ responseAudioKey : true ,
610+ callerSegmentAudioKey : true ,
611+ responseSegmentAudioKey : true ,
612+ } ,
551613 } )
552- if ( drafts . some ( ( d ) => d . episodeAudioKey ) ) {
614+ const keysToDelete = drafts
615+ . flatMap ( ( draft ) => [
616+ draft . episodeAudioKey ,
617+ draft . responseAudioKey ,
618+ draft . callerSegmentAudioKey ,
619+ draft . responseSegmentAudioKey ,
620+ ] )
621+ . filter ( ( k ) : k is string => typeof k === 'string' && k . length > 0 )
622+ if ( keysToDelete . length ) {
553623 await Promise . all (
554- drafts
555- . map ( ( d ) => d . episodeAudioKey )
556- . filter ( ( k ) : k is string => typeof k === 'string' && k . length > 0 )
557- . map ( async ( key ) => deleteAudioObject ( { key } ) . catch ( ( ) => { } ) ) ,
624+ keysToDelete . map ( async ( key ) =>
625+ deleteAudioObject ( { key } ) . catch ( ( ) => { } ) ,
626+ ) ,
558627 )
559628 }
560629 await prisma . callKentEpisodeDraft . deleteMany ( { where : { callId } } )
@@ -652,7 +721,14 @@ async function deleteCall({
652721 select : {
653722 id : true ,
654723 audioKey : true ,
655- episodeDraft : { select : { episodeAudioKey : true } } ,
724+ episodeDraft : {
725+ select : {
726+ episodeAudioKey : true ,
727+ responseAudioKey : true ,
728+ callerSegmentAudioKey : true ,
729+ responseSegmentAudioKey : true ,
730+ } ,
731+ } ,
656732 } ,
657733 } )
658734 if ( ! call ) {
@@ -663,6 +739,9 @@ async function deleteCall({
663739 const keysToDelete = [
664740 call . audioKey ,
665741 call . episodeDraft ?. episodeAudioKey ,
742+ call . episodeDraft ?. responseAudioKey ,
743+ call . episodeDraft ?. callerSegmentAudioKey ,
744+ call . episodeDraft ?. responseSegmentAudioKey ,
666745 ] . filter ( ( k ) : k is string => typeof k === 'string' && k . length > 0 )
667746 if ( keysToDelete . length ) {
668747 await Promise . all (
0 commit comments