diff --git a/examples/Voice/BasicVoice/Audio/BasicAudioPlayer.cs b/examples/Voice/BasicVoice/Audio/BasicAudioPlayer.cs
index 09025ffb6..e6e48dc21 100644
--- a/examples/Voice/BasicVoice/Audio/BasicAudioPlayer.cs
+++ b/examples/Voice/BasicVoice/Audio/BasicAudioPlayer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Disqord;
using Disqord.Bot;
@@ -46,35 +47,29 @@ public BasicAudioPlayer(
_queue = new();
}
- private async Task SendNotificationAsync(AudioSource finishedSource, bool wasReplaced, AudioSource? queuedSource)
+ ///
+ /// Invoked when this audio player is stopped.
+ ///
+ /// The exception that caused the stop or if no exception occurred.
+ ///
+ /// A representing the work.
+ ///
+ protected override async ValueTask OnStopped(Exception? exception)
{
- // Yield, so the code below runs in background.
- await Task.Yield();
-
- // Below we access metadata on the audio sources.
- // This metadata is set in AudioModule.
- string? notification = null;
- if (finishedSource.TryGetMetadata(AudioMetadataKeys.Title, out var title))
+ if (exception == null)
{
- notification = $"{(wasReplaced ? "Skipped" : "Finished")} playing {Markdown.Code(title)}.\n";
- }
-
- if (queuedSource != null && queuedSource.TryGetMetadata(AudioMetadataKeys.Title, out title))
- {
- notification += $"Now playing {Markdown.Code(title)}.";
- }
+ // If an exception occurred, we'll log it.
+ Bot.Logger.LogError(exception, "An exception occurred in the audio player for guild ID {GuildId}.", GuildId);
- if (notification != null)
- {
- try
- {
- var message = new LocalMessage().WithContent(notification.TrimStart());
- await Bot.SendMessageAsync(NotificationsChannelId, message);
- }
- catch (Exception ex)
+ // Here you can add different handling for different exceptions that might occur
+ // but for VoiceConnectionException the logic should always be basically the same.
+ // VoiceConnectionException indicates that the connection object was rendered unusable
+ // because, for example, the bot was disconnected from the voice channel.
+ // We dispose of this audio player allowing for a new one to be created.
+ if (exception is VoiceConnectionException)
{
- // If an exception occurred, we'll log it.
- Bot.Logger.LogError(ex, "An exception occurred while sending the notification in the audio player for guild ID {GuildId}.", GuildId);
+ var playerService = Bot.Services.GetRequiredService();
+ await playerService.DisposePlayerAsync(GuildId);
}
}
}
@@ -91,18 +86,10 @@ private async Task SendNotificationAsync(AudioSource finishedSource, bool wasRep
///
protected override ValueTask OnSourceFinished(AudioSource source, bool wasReplaced)
{
- AudioSource? queuedSource;
- lock (_queueLock)
- {
- if (_queue.TryDequeue(out queuedSource))
- {
- // If there's a queued source we start playing it.
- TrySetSource(queuedSource);
- }
- }
+ var nextSource = PlayNextSource();
// Not awaited so that we don't block the audio playback.
- _ = SendNotificationAsync(source, wasReplaced, queuedSource);
+ _ = SendNotificationAsync(source, wasReplaced, exception: null, nextSource);
return default;
}
@@ -123,9 +110,70 @@ protected override ValueTask OnSourceErrored(AudioSource source, Exception excep
Bot.Logger.LogError(exception, "An exception occurred in the audio source '{Title}' ({AudioSourceType}) "
+ "in the audio player for guild ID {GuildId}.", title ?? "unknown", source.GetType().Name, GuildId);
+ var nextSource = PlayNextSource();
+
+ // Not awaited so that we don't block the audio playback.
+ _ = SendNotificationAsync(source, wasReplaced: false, exception, nextSource);
return default;
}
+ private AudioSource? PlayNextSource()
+ {
+ lock (_queueLock)
+ {
+ if (_queue.TryDequeue(out var queuedSource))
+ {
+ // If there's a queued source we start playing it.
+ if (TrySetSource(queuedSource))
+ {
+ return queuedSource;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private async Task SendNotificationAsync(AudioSource finishedSource, bool wasReplaced, Exception? exception, AudioSource? nextSource)
+ {
+ // Yield, so the code below runs in background.
+ await Task.Yield();
+
+ // Below we access metadata on the audio sources.
+ // This metadata is set in AudioModule.
+ string? notification = null;
+ if (finishedSource.TryGetMetadata(AudioMetadataKeys.Title, out var title))
+ {
+ if (exception != null)
+ {
+ notification += $"An error occurred while playing {Markdown.Code(title)}.\n";
+ }
+ else
+ {
+ notification += $"{(wasReplaced ? "Skipped" : "Finished")} playing {Markdown.Code(title)}.\n";
+ }
+ }
+
+ if (nextSource != null && nextSource.TryGetMetadata(AudioMetadataKeys.Title, out title))
+ {
+ notification += $"Now playing {Markdown.Code(title)}.";
+ }
+
+ if (notification != null)
+ {
+ try
+ {
+ var message = new LocalMessage().WithContent(notification.TrimStart());
+ await Bot.SendMessageAsync(NotificationsChannelId, message);
+ }
+ catch (Exception ex)
+ {
+ // If an exception occurred, we'll log it.
+ Bot.Logger.LogError(ex, "An exception occurred while sending the notification in the audio player for guild ID {GuildId}.", GuildId);
+ }
+ }
+ }
+
///
/// Enqueues the specified audio source.
///
@@ -148,31 +196,4 @@ public bool Enqueue(AudioSource source)
return false;
}
-
- ///
- /// Invoked when this audio player is stopped.
- ///
- /// The exception that caused the stop or if no exception occurred.
- ///
- /// A representing the work.
- ///
- protected override async ValueTask OnStopped(Exception? exception)
- {
- if (exception != null)
- {
- // If an exception occurred, we'll log it.
- Bot.Logger.LogError(exception, "An exception occurred in the audio player for guild ID {GuildId}.", GuildId);
-
- // Here you can add different handling for different exceptions that might occur
- // but for VoiceConnectionException the logic should always be basically the same.
- // VoiceConnectionException indicates that the connection object was rendered unusable
- // because, for example, the bot was disconnected from the voice channel.
- // We dispose of this audio player allowing for a new one to be created.
- if (exception is VoiceConnectionException)
- {
- var playerService = Bot.Services.GetRequiredService();
- await playerService.DisposePlayerAsync(GuildId);
- }
- }
- }
}
diff --git a/src/Disqord.Extensions.Voice/Audio/Default/AudioPlayer.cs b/src/Disqord.Extensions.Voice/Audio/Default/AudioPlayer.cs
index 8ca5ed981..1d3791007 100644
--- a/src/Disqord.Extensions.Voice/Audio/Default/AudioPlayer.cs
+++ b/src/Disqord.Extensions.Voice/Audio/Default/AudioPlayer.cs
@@ -344,7 +344,14 @@ private async Task WaitIfPausedAsync(CancellationToken cancellationToken)
await connection.SetSpeakingFlagsAsync(SpeakingFlags.None, cancellationToken).ConfigureAwait(false);
await OnPaused().ConfigureAwait(false);
- await SendSilenceAsync(connection, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ await SendSilenceAsync(connection, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex) when (ex is not OperationCanceledException)
+ {
+ // TODO: handle exception
+ }
await pauseTask.ConfigureAwait(false);
@@ -444,7 +451,14 @@ private async Task ExecuteAsync(CancellationToken cancellationToken)
break;
}
- await connection.SendPacketAsync(packet, linkedCancellationToken).ConfigureAwait(false);
+ try
+ {
+ await connection.SendPacketAsync(packet, linkedCancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex) when (ex is not OperationCanceledException)
+ {
+ // TODO: handle exception
+ }
}
if (continueOuter)