using Microsoft.Extensions.Options; namespace MeetingAssistant.Speakers; public sealed class ResemblyzerWarmupHostedService : BackgroundService { private readonly IResemblyzerVoiceEncoder encoder; private readonly bool speakerIdentificationEnabled; private readonly ResemblyzerSpeakerRecognitionOptions options; private readonly ILogger logger; public ResemblyzerWarmupHostedService( IResemblyzerVoiceEncoder encoder, IOptions options, ILogger logger) { this.encoder = encoder; speakerIdentificationEnabled = options.Value.SpeakerIdentification.Enabled; this.options = options.Value.SpeakerIdentification.Resemblyzer; this.logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (!speakerIdentificationEnabled || !options.Enabled) { return; } await Task.Yield(); try { logger.LogInformation( "Starting Resemblyzer warm-up in runtime folder {RuntimeFolder} for model {ModelId}", VaultPath.Resolve(options.RuntimeFolder), options.ModelId); await encoder.WarmUpAsync(stoppingToken); logger.LogInformation( "Finished Resemblyzer warm-up in runtime folder {RuntimeFolder} for model {ModelId}", VaultPath.Resolve(options.RuntimeFolder), options.ModelId); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { logger.LogInformation("Resemblyzer warm-up was cancelled during application shutdown"); } catch (Exception exception) { logger.LogError( exception, "Resemblyzer warm-up failed in runtime folder {RuntimeFolder}; voice encoding can still retry on demand", VaultPath.Resolve(options.RuntimeFolder)); } } }