Public Access
Implement meeting assistant v1
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using NAudio.Wave;
|
||||
|
||||
namespace MeetingAssistant.Recording;
|
||||
|
||||
public sealed class TemporaryRecordedAudioStore : IRecordedAudioStore
|
||||
{
|
||||
private readonly MeetingAssistantOptions options;
|
||||
private readonly ILogger<TemporaryRecordedAudioStore> logger;
|
||||
|
||||
public TemporaryRecordedAudioStore(
|
||||
IOptions<MeetingAssistantOptions> options,
|
||||
ILogger<TemporaryRecordedAudioStore> logger)
|
||||
{
|
||||
this.options = options.Value;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public Task<IRecordedAudioSink> CreateSessionAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var folder = GetTemporaryRecordingsFolder();
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
var path = Path.Combine(folder, $"{DateTimeOffset.Now:yyyyMMdd-HHmmss}-{Guid.NewGuid():N}-recording.wav");
|
||||
var format = new WaveFormat(options.Recording.SampleRate, 16, options.Recording.Channels);
|
||||
logger.LogInformation("Created temporary meeting recording file {RecordingPath}", path);
|
||||
|
||||
return Task.FromResult<IRecordedAudioSink>(new TemporaryRecordedAudioSink(path, format, logger));
|
||||
}
|
||||
|
||||
public Task DeleteStaleRecordingsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var folder = GetTemporaryRecordingsFolder();
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
foreach (var path in Directory.EnumerateFiles(folder, "*.wav", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
DeleteFile(path);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private string GetTemporaryRecordingsFolder()
|
||||
{
|
||||
return VaultPath.Resolve(options.Recording.TemporaryRecordingsFolder);
|
||||
}
|
||||
|
||||
private void DeleteFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(path);
|
||||
logger.LogInformation("Deleted temporary meeting recording file {RecordingPath}", path);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
logger.LogWarning(exception, "Could not delete temporary meeting recording file {RecordingPath}", path);
|
||||
}
|
||||
catch (UnauthorizedAccessException exception)
|
||||
{
|
||||
logger.LogWarning(exception, "Could not delete temporary meeting recording file {RecordingPath}", path);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TemporaryRecordedAudioSink : IRecordedAudioSink
|
||||
{
|
||||
private readonly WaveFileWriter writer;
|
||||
private readonly ILogger logger;
|
||||
private bool completed;
|
||||
private bool disposed;
|
||||
|
||||
public TemporaryRecordedAudioSink(string audioPath, WaveFormat format, ILogger logger)
|
||||
{
|
||||
AudioPath = audioPath;
|
||||
this.logger = logger;
|
||||
writer = new WaveFileWriter(audioPath, format);
|
||||
}
|
||||
|
||||
public string AudioPath { get; }
|
||||
|
||||
public Task AppendAsync(AudioChunk chunk, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
writer.Write(chunk.Pcm, 0, chunk.Pcm.Length);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CompleteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
writer.Flush();
|
||||
completed = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
writer.Dispose();
|
||||
disposed = true;
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await DisposeAsync();
|
||||
try
|
||||
{
|
||||
File.Delete(AudioPath);
|
||||
logger.LogInformation("Deleted temporary meeting recording file {RecordingPath}", AudioPath);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user