Skip to content

Commit

Permalink
Refactor player a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
Quahu committed Aug 16, 2024
1 parent de13d4e commit 5af2e00
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 64 deletions.
145 changes: 83 additions & 62 deletions examples/Voice/BasicVoice/Audio/BasicAudioPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Disqord;
using Disqord.Bot;
Expand Down Expand Up @@ -46,35 +47,29 @@ public BasicAudioPlayer(
_queue = new();
}

private async Task SendNotificationAsync(AudioSource finishedSource, bool wasReplaced, AudioSource? queuedSource)
/// <summary>
/// Invoked when this audio player is stopped.
/// </summary>
/// <param name="exception"> The exception that caused the stop or <see langword="null"/> if no exception occurred. </param>
/// <returns>
/// A <see cref="ValueTask"/> representing the work.
/// </returns>
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<string>(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<AudioPlayerService>();
await playerService.DisposePlayerAsync(GuildId);
}
}
}
Expand All @@ -91,18 +86,10 @@ private async Task SendNotificationAsync(AudioSource finishedSource, bool wasRep
/// </returns>
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;
}

Expand All @@ -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<string>(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);
}
}
}

/// <summary>
/// Enqueues the specified audio source.
/// </summary>
Expand All @@ -148,31 +196,4 @@ public bool Enqueue(AudioSource source)

return false;
}

/// <summary>
/// Invoked when this audio player is stopped.
/// </summary>
/// <param name="exception"> The exception that caused the stop or <see langword="null"/> if no exception occurred. </param>
/// <returns>
/// A <see cref="ValueTask"/> representing the work.
/// </returns>
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<AudioPlayerService>();
await playerService.DisposePlayerAsync(GuildId);
}
}
}
}
18 changes: 16 additions & 2 deletions src/Disqord.Extensions.Voice/Audio/Default/AudioPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 5af2e00

Please sign in to comment.