mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 20:47:21 +00:00
Added transcription of audio and video files to local data sources (#1081)
Co-authored-by: Paul Koudelka <106623909+PaulKoudelka@users.noreply.github.com>
This commit is contained in:
1 parent
8642ce2d81
commit
752f7d828a
82 files changed
+3231
-302
No files matched your search
@@ -63,7 +63,7 @@ public sealed class MediaTranscriptionService(
|
||||
}
|
||||
|
||||
/// <summary>Gets whether the currently configured transcription provider can be used.</summary>
|
||||
public bool HasUsableTranscriptionProvider => this.ResolveProvider() is not null;
|
||||
public bool HasUsableTranscriptionProvider => this.GetUsableProvider() is not null;
|
||||
|
||||
/// <summary>Gets the last retained state for one owner.</summary>
|
||||
public MediaImportSnapshot? GetSnapshot(MediaImportOwner owner)
|
||||
@@ -411,14 +411,31 @@ public sealed class MediaTranscriptionService(
|
||||
/// <param name="mediaPath">Audio or video file path.</param>
|
||||
/// <param name="token">Caller cancellation token.</param>
|
||||
/// <returns>A typed terminal result.</returns>
|
||||
public async Task<MediaTranscriptionResult> TranscribeAsync(string mediaPath, CancellationToken token = default)
|
||||
public Task<MediaTranscriptionResult> TranscribeAsync(string mediaPath, CancellationToken token = default) => this.TranscribeWithoutImportAsync(mediaPath, null, token);
|
||||
|
||||
/// <summary>
|
||||
/// Transcribes an audio or video file with exactly the given provider, without starting a visible import operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For callers which checked the provider against rules of their own, such as the confidence
|
||||
/// level of a data source. The provider is used as given and not resolved from the settings
|
||||
/// again, so a change of the settings between that check and the upload cannot send the media
|
||||
/// to another provider.
|
||||
/// </remarks>
|
||||
/// <param name="mediaPath">Audio or video file path.</param>
|
||||
/// <param name="provider">The transcription provider the caller checked.</param>
|
||||
/// <param name="token">Caller cancellation token.</param>
|
||||
/// <returns>A typed terminal result.</returns>
|
||||
public Task<MediaTranscriptionResult> TranscribeAsync(string mediaPath, TranscriptionProvider provider, CancellationToken token = default) => this.TranscribeWithoutImportAsync(mediaPath, provider, token);
|
||||
|
||||
private async Task<MediaTranscriptionResult> TranscribeWithoutImportAsync(string mediaPath, TranscriptionProvider? provider, CancellationToken token)
|
||||
{
|
||||
this.ThrowIfDisposed();
|
||||
var operation = this.CreateOperation(null, token);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
return await this.TranscribeCoreAsync(mediaPath, operation, updateImportState: false);
|
||||
return await this.TranscribeCoreAsync(mediaPath, operation, updateImportState: false, provider);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -458,8 +475,9 @@ public sealed class MediaTranscriptionService(
|
||||
/// <param name="mediaPath">Source media path.</param>
|
||||
/// <param name="operation">Operation-specific cancellation and Rust-job state.</param>
|
||||
/// <param name="updateImportState">Whether progress belongs to the visible import lane.</param>
|
||||
/// <param name="checkedProvider">The provider a caller checked itself, or null for the configured one.</param>
|
||||
/// <returns>A typed terminal result.</returns>
|
||||
private async Task<MediaTranscriptionResult> TranscribeCoreAsync(string mediaPath, MediaOperation operation, bool updateImportState)
|
||||
private async Task<MediaTranscriptionResult> TranscribeCoreAsync(string mediaPath, MediaOperation operation, bool updateImportState, TranscriptionProvider? checkedProvider = null)
|
||||
{
|
||||
var normalizedPath = Path.Combine(Path.GetTempPath(), "mindwork-ai-studio-media", $"{operation.Id:N}.webm");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(normalizedPath)!);
|
||||
@@ -497,7 +515,7 @@ public sealed class MediaTranscriptionService(
|
||||
return MediaTranscriptionResult.NoAudibleSignal(TB("The audio track contains no audible signal, so there is nothing to transcribe."));
|
||||
}
|
||||
|
||||
var providerSettings = this.ResolveProvider();
|
||||
var providerSettings = checkedProvider ?? this.GetUsableProvider();
|
||||
if (providerSettings is null)
|
||||
return MediaTranscriptionResult.Failed(TB("No usable transcription provider is configured."));
|
||||
|
||||
@@ -530,16 +548,20 @@ public sealed class MediaTranscriptionService(
|
||||
"The transcription provider failed for '{FileName}' (operation {OperationId}): {Diagnostic}",
|
||||
fileName,
|
||||
operation.Id,
|
||||
providerResult.ErrorMessage);
|
||||
string.IsNullOrWhiteSpace(providerResult.ErrorMessage) ? "<empty>" : providerResult.ErrorMessage);
|
||||
|
||||
//
|
||||
// When the provider told us why it failed, the user gets to read it. Only that
|
||||
// message says whether to wait, to check the API key, or to ask the provider for
|
||||
// a format it can read:
|
||||
//
|
||||
return MediaTranscriptionResult.Failed(string.IsNullOrWhiteSpace(providerResult.ErrorMessage)
|
||||
var failureMessage = string.IsNullOrWhiteSpace(providerResult.ErrorMessage)
|
||||
? TB("The transcription provider could not transcribe the media file.")
|
||||
: providerResult.ErrorMessage);
|
||||
: providerResult.ErrorMessage;
|
||||
|
||||
return providerResult.FileRejected
|
||||
? MediaTranscriptionResult.Rejected(failureMessage)
|
||||
: MediaTranscriptionResult.Failed(failureMessage);
|
||||
}
|
||||
|
||||
return MediaTranscriptionResult.Succeeded(providerResult.Text.Trim());
|
||||
@@ -719,7 +741,12 @@ public sealed class MediaTranscriptionService(
|
||||
}
|
||||
|
||||
/// <summary>Resolves the configured provider after confidence validation.</summary>
|
||||
private TranscriptionProvider? ResolveProvider()
|
||||
/// <remarks>
|
||||
/// Checks the global minimum confidence only. A caller with rules of its own, such as the
|
||||
/// confidence level of a data source, checks the provider against them as well.
|
||||
/// </remarks>
|
||||
/// <returns>The configured provider, or null when none is configured or it falls short of the global minimum.</returns>
|
||||
public TranscriptionProvider? GetUsableProvider()
|
||||
{
|
||||
var providerId = settingsManager.ConfigurationData.App.UseTranscriptionProvider;
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
@@ -735,6 +762,19 @@ public sealed class MediaTranscriptionService(
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The configured provider, when it may also hear the audio and video files of the given data source.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">The data source, with the confidence level it asks for.</param>
|
||||
/// <returns>The provider, or null when none is configured or it falls short of the global minimum or of the data source.</returns>
|
||||
public TranscriptionProvider? GetUsableProvider(IIndexedDataSource dataSource)
|
||||
{
|
||||
var provider = this.GetUsableProvider();
|
||||
return provider is not null && DataSourceEmbeddingService.AllowsTranscription(dataSource, provider.GetConfidenceLevel(settingsManager))
|
||||
? provider
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>Creates and registers operation-owned cancellation state.</summary>
|
||||
/// <param name="target">Optional visible media import target.</param>
|
||||
/// <param name="token">Caller token linked to the operation.</param>
|
||||
|
||||
Reference in new issue
Block a user