Crunchy-Downloader/CRD/Utils/AudioPlayer.cs
Elwador ff3e28093e - Added notification service for webhooks
- Added retry delay for rate limit handling
- Added toggle to control whether auto refresh also adds missing episodes to the queue
- Added configurable delay after each dub download
- Changed encoding preset dialog to show a preview of the FFmpeg command
- Changed play sound on queue empty and execute file on completion to be handled by the notification service
- Changed shutdown PC option to disable once triggered
- Fixed crash with queue persistence
- Fixed crash with audio player
- Fixed subscription countdown on the account page
2026-05-14 21:49:57 +02:00

178 lines
5.5 KiB
C#

using System;
using System.IO;
using System.Threading.Tasks;
using NetCoreAudio;
using NAudio.Wave;
namespace CRD.Utils;
public class AudioPlayer{
private readonly Player _player;
private bool _isPlaying;
private WaveOutEvent? _waveOut;
private AudioFileReader? _audioFileReader;
private TaskCompletionSource? _playbackCompleted;
public AudioPlayer(){
_player = new Player();
}
public static (bool IsValid, string ErrorMessage) ValidateSoundFile(string path){
if (string.IsNullOrWhiteSpace(path)){
return (false, "The selected sound file path is empty.");
}
if (!File.Exists(path)){
return (false, "The selected sound file does not exist.");
}
try{
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
if (stream.Length == 0){
return (false, "The selected sound file is empty.");
}
} catch (Exception exception){
return (false, $"The selected sound file could not be opened: {exception.Message}");
}
if (string.IsNullOrWhiteSpace(Path.GetExtension(path))){
return (false, "The selected sound file has no file extension.");
}
return (true, string.Empty);
}
public async Task<(bool IsSuccess, string ErrorMessage)> ValidatePlaybackAsync(string path){
var fileValidation = ValidateSoundFile(path);
if (!fileValidation.IsValid){
return (false, fileValidation.ErrorMessage);
}
if (_isPlaying){
return (false, "Audio playback is already in progress.");
}
if (OperatingSystem.IsWindows()){
try{
_isPlaying = true;
DisposeWindowsPlayback();
_audioFileReader = new AudioFileReader(path);
_waveOut = new WaveOutEvent();
_playbackCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_waveOut.PlaybackStopped += (_, args) => {
if (args.Exception != null){
_playbackCompleted?.TrySetException(args.Exception);
} else{
_playbackCompleted?.TrySetResult();
}
};
_waveOut.Init(_audioFileReader);
_waveOut.Play();
await _playbackCompleted.Task;
return (true, string.Empty);
} catch (Exception exception){
return (false, exception.Message);
} finally{
DisposeWindowsPlayback();
_isPlaying = false;
}
}
try{
_isPlaying = true;
await _player.Play(path);
return (true, string.Empty);
} catch (Exception exception){
return (false, exception.Message);
} finally{
_isPlaying = false;
}
}
public async Task PlayAsync(string path){
var fileValidation = ValidateSoundFile(path);
if (!fileValidation.IsValid){
Console.Error.WriteLine($"Failed to play audio '{path}': {fileValidation.ErrorMessage}");
return;
}
if (_isPlaying){
Console.WriteLine("Audio is already playing, ignoring duplicate request.");
return;
}
if (OperatingSystem.IsWindows()){
try{
_isPlaying = true;
DisposeWindowsPlayback();
_audioFileReader = new AudioFileReader(path);
_waveOut = new WaveOutEvent();
_playbackCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_waveOut.PlaybackStopped += (_, args) => {
if (args.Exception != null){
_playbackCompleted?.TrySetException(args.Exception);
} else{
_playbackCompleted?.TrySetResult();
}
};
_waveOut.Init(_audioFileReader);
_waveOut.Play();
await _playbackCompleted.Task;
} catch (Exception exception){
Console.Error.WriteLine($"Failed to play audio '{path}': {exception}");
} finally{
DisposeWindowsPlayback();
_isPlaying = false;
}
return;
}
try{
_isPlaying = true;
await _player.Play(path);
} catch (Exception exception){
Console.Error.WriteLine($"Failed to play audio '{path}': {exception.Message}");
} finally{
_isPlaying = false;
}
}
public async Task StopAsync(){
if (OperatingSystem.IsWindows()){
try{
_waveOut?.Stop();
} catch (Exception exception){
Console.Error.WriteLine($"Failed to stop audio playback: {exception}");
} finally{
DisposeWindowsPlayback();
_isPlaying = false;
}
return;
}
try{
await _player.Stop();
} catch (Exception exception){
Console.Error.WriteLine($"Failed to stop audio playback: {exception}");
} finally{
_isPlaying = false;
}
}
private void DisposeWindowsPlayback(){
_playbackCompleted = null;
_waveOut?.Dispose();
_waveOut = null;
_audioFileReader?.Dispose();
_audioFileReader = null;
}
}