2026-08-11 20:14:21 +02:00
using AIStudio.Tools.Media ;
namespace AIStudio.Assistants.BatchProcessing ;
public partial class AssistantBatchProcessing
{
/// <summary>
/// Loads a document through the Rust content stream or resolves a persistent
/// transcript for an audio or video file.
/// </summary>
private Task < string? > LoadInputContentAsync ( BatchProcessingFileResult fileResult , CancellationToken token )
{
return IsTranscribableMedia ( fileResult . FilePath )
? this . LoadMediaTranscriptAsync ( fileResult , token )
2026-08-23 21:15:37 +02:00
: this . LoadDocumentContentAsync ( fileResult , token );
2026-08-11 20:14:21 +02:00
}
2026-08-23 21:15:37 +02:00
private async Task < string? > LoadDocumentContentAsync ( BatchProcessingFileResult fileResult , CancellationToken token )
2026-08-11 20:14:21 +02:00
{
FileExtractionResult extraction ;
try
{
2026-08-23 21:15:37 +02:00
extraction = await this . RustService . ReadArbitraryFileData ( fileResult . FilePath , int . MaxValue , token : token );
2026-08-11 20:14:21 +02:00
}
catch ( Exception e )
{
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . FAILED , string . Format ( T ( "Was not able to read the file: {0}" ), e . Message ), e );
return null ;
}
2026-08-23 21:15:37 +02:00
//
// The user stopped the batch run while we were reading this file. That says nothing about
// the file, so it gets the same status as a cancelled AI request instead of a failure:
//
if ( extraction . ErrorCode is FileExtractionErrorCode . CANCELLED )
{
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . CANCELED , T ( "The batch run was canceled." ));
return null ;
}
2026-08-11 20:14:21 +02:00
if (! extraction . HasUsableContent )
{
this . Logger . LogError ( "Reading the batch file '{FilePath}' failed: code={ErrorCode}, message='{ErrorMessage}'." , fileResult . FilePath , extraction . ErrorCode , extraction . ErrorMessage );
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . FAILED , extraction . ToUserMessage ( fileResult . FileName ));
return null ;
}
if ( extraction . Outcome is FileExtractionOutcome . PARTIAL )
{
this . Logger . LogWarning ( "Parts of the batch file '{FilePath}' could not be read: pages={FailedPages}." , fileResult . FilePath , string . Join ( ", " , extraction . FailedPages ));
await this . MessageBus . SendWarning ( new ( Icons . Material . Filled . Description , extraction . ToPartialUserMessage ( fileResult . FileName )));
}
if ( extraction . HasExtensionMismatch )
{
this . Logger . LogWarning ( "The batch file '{FilePath}' is actually a '{DetectedFormat}'." , fileResult . FilePath , extraction . DetectedFormat );
await this . MessageBus . SendWarning ( new ( Icons . Material . Filled . RuleFolder , extraction . ToExtensionMismatchUserMessage ( fileResult . FileName )));
}
if (! string . IsNullOrWhiteSpace ( extraction . Content ))
return extraction . Content ;
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . FAILED , T ( "Was not able to extract any text from this file." ));
return null ;
}
private async Task < string? > LoadMediaTranscriptAsync ( BatchProcessingFileResult fileResult , CancellationToken token )
{
2026-10-18 12:06:15 +02:00
var provider = this . MediaTranscriptionService . GetUsableProvider ();
var transcript = await PersistentMediaTranscript . ReadOrCreateAsync ( MediaSidecarTranscripts . INSTANCE , fileResult . FilePath , provider , this . MediaTranscriptionService . TranscribeAsync , this . Logger , token );
if ( transcript . Status is PersistentMediaTranscriptStatus . CANCELLED )
2026-08-11 20:14:21 +02:00
{
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . CANCELED , T ( "The batch run was canceled." ));
return null ;
}
2026-10-18 12:06:15 +02:00
if ( transcript . Status is not PersistentMediaTranscriptStatus . READY )
2026-08-11 20:14:21 +02:00
{
2026-10-18 12:06:15 +02:00
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . FAILED , transcript . UserMessage );
2026-08-11 20:14:21 +02:00
return null ;
}
try
{
2026-10-18 12:06:15 +02:00
return await File . ReadAllTextAsync ( transcript . TranscriptPath , token );
2026-08-11 20:14:21 +02:00
}
2026-10-18 12:06:15 +02:00
catch ( OperationCanceledException )
2026-08-11 20:14:21 +02:00
{
2026-10-18 12:06:15 +02:00
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . CANCELED , T ( "The batch run was canceled." ));
2026-08-11 20:14:21 +02:00
return null ;
}
2026-10-18 12:06:15 +02:00
catch ( Exception e )
2026-08-11 20:14:21 +02:00
{
2026-10-18 12:06:15 +02:00
this . FinishFileResult ( fileResult , BatchProcessingFileStatus . FAILED , string . Format ( T ( "Was not able to read the existing transcript: {0}" ), e . Message ), e );
return null ;
2026-08-11 20:14:21 +02:00
}
}
2026-10-18 12:06:15 +02:00
private static bool IsTranscribableMedia ( string filePath ) => PersistentMediaTranscript . IsMediaPath ( filePath );
2026-08-11 20:14:21 +02:00
2026-10-18 12:06:15 +02:00
private static bool HasReusableTranscript ( string mediaFilePath ) => MediaSidecarTranscripts . HasReusableTranscript ( mediaFilePath );
2026-08-11 20:14:21 +02:00
}