Code Monkey home page Code Monkey logo

lavalink4net's Introduction

High performance Lavalink wrapper for .NET

Important

We now have a FAQ channel for quick questions on the Lavalink4NET Support Server

Lavalink4NET is a Lavalink wrapper with node clustering, caching and custom players for .NET with support for Discord.Net, DSharpPlus, Remora, and NetCord.

Lavalink4NET Support Server Banner

Features

  • ⚖️ Node Clustering / Load Balancing
    Distribute load across nodes for efficient and reliable audio playback (for large scale bots).

  • ✳️ Extensible
    Customize and enhance features using plugins to match your bot's needs.

  • 🎤 Lyrics
    Display song lyrics alongside audio playback to enrich the user experience.

  • 🗳️ Queueing / Voting-System
    Let users queue tracks and vote on the next songs, enhancing collaborative playlists.

  • 🎵 Track Decoding and Encoding
    Lavalink4NET supports high efficient track decoding and encoding of lavaplayer track identifiers.

  • 🔄 Auto-Reconnect and Resuming
    Maintain uninterrupted audio playback during connection disruptions.

  • 🔌 Fully Asynchronous Interface
    Effortlessly communicate with the Lavalink audio server without causing delays in your bot. All actions that can be offloaded are asynchronous and can be canceled at any time if needed.

  • 📝 Logging (optional)
    Enable insights for troubleshooting and debugging.

  • Request Caching (optional)
    Improve performance by reducing redundant requests.

  • ⏱️ Inactivity Tracking (optional)
    Monitor inactive players and disconnect them to save resources.

  • 🖋️ Supports Lavalink plugins
    Expand capabilities by integrating with Lavalink plugins.

  • 🎶 Custom players
    Manage audio playback instances tailored to your bot's requirements.

  • 🖼️ Artwork resolution
    Lavalink4NET allows the user to resolve artwork images for the tracks to display an appealing image to the user.

  • 🎚️ Audio filter support
    Lavalink4NET supports all audio filters provided by lavaplayer and even more when installing the ExtraFilters plugin.

  • 📊 Statistics tracking support
    Lavalink4NET supports tracking and evaluation of node statistics. In clustering, node statistics can be used to evaluate the best node for efficient resource usage.

  • Compatible with DSharpPlus, Discord.Net, Remora, and NetCord.
    Lavalink4NET has an adaptive client API, meaning it can support any discord client. Currently, DSharpPlus, Discord.Net, Remora and NetCord are supported out-of-the-box.

Documentation

Important

You can find the documentation for Lavalink4NET v4 here.

Components

Lavalink4NET offers high flexibility and extensibility by providing an isolated interface. You can extend Lavalink4NET by adding additional packages which add integrations with other services, support for additional lavalink/lavaplayer plugins, or additional client support.

Client Support

  • Lavalink4NET.Discord.Net   NuGet
    Enhance your Discord bots with advanced audio playback using this integration for Lavalink4NET. Designed for end users building Discord.Net-based applications.

  • Lavalink4NET.DSharpPlus   NuGet
    Add powerful audio playback to your DSharpPlus-based applications with this integration for Lavalink4NET. Suitable for end users developing with DSharpPlus.

  • Lavalink4NET.Remora.Discord   NuGet
    Add powerful audio playback to your Remora-based discord bots with this integration for Lavalink4NET. Suitable for end users developing with Remora.

  • Lavalink4NET.NetCord   NuGet
    Add powerful audio playback to your NetCord-based discord bots with this integration for Lavalink4NET. Suitable for end users developing with NetCord.

Clustering

  • Lavalink4NET.Cluster   NuGet
    Scale and improve performance by using multiple Lavalink nodes with this cluster support module. Ideal for handling high-demand music streaming applications.

Integrations

  • Lavalink4NET.Integrations.ExtraFilters   NuGet
    Enhance your audio playback experience with extra filters in Lavalink4NET. Apply additional audio effects and modifications to customize the sound output. Requires the installation of the corresponding plugin on the Lavalink node.

  • Lavalink4NET.Integrations.SponsorBlock   NuGet
    Integrate SponsorBlock functionality into Lavalink4NET. Automatically skip sponsored segments in videos for a seamless and uninterrupted playback experience. Requires the installation of the corresponding plugin on the Lavalink node.

  • Lavalink4NET.Integrations.TextToSpeech   NuGet
    Enable text-to-speech functionality in Lavalink4NET. Convert written text into spoken words, allowing your application to generate and play audio from text inputs. Requires the installation of the corresponding plugin on the Lavalink node.

  • Lavalink4NET.Integrations.LyricsJava   NuGet
    Fetch timed lyrics from youtube or non-timed lyrics from genius. Automatically fetches lyrics for the current track. Requires the installation of the corresponding plugin on the Lavalink node.

Services

  • Lavalink4NET.Lyrics   NuGet
    Fetch and display song lyrics from lyrics.ovh with this lyrics service integrated with Lavalink4NET. Enhance the music experience for your users.

  • Lavalink4NET.Artwork   NuGet
    Artwork resolution service for the Lavalink4NET client library.

  • Lavalink4NET.InactivityTracking   NuGet
    Optimize resource usage by tracking and disconnecting inactive players. Ensure efficient audio playback in your application.

Core Components

  • Lavalink4NET   NuGet
    This core library is used to implement client wrappers. It is not intended for end users. Please use Lavalink4NET.Discord.Net, Lavalink4NET.DSharpPlus, Lavalink4NET.Remora.Discord or Lavalink4NET.NetCord instead.

  • Lavalink4NET.Abstractions   NuGet
    General abstractions and common primitives for the Lavalink4NET client library.

  • Lavalink4NET.Protocol   NuGet
    Protocol implementation for the Lavalink4NET client library used to interact with the Lavalink REST API.

  • Lavalink4NET.Rest   NuGet
    Easily interact with the Lavalink REST API using this REST API client primitives library. Build custom functionalities or integrate Lavalink4NET with other services.

Prerequisites

  • At least one lavalink node
  • At least .NET 6

Getting Started

Lavalink4NET works by using dependency injection to make management of services very easy. You just have to add services.AddLavalink(); to your startup code. We prefer using the host application builder to manage services required by Lavalink4NET.

var builder = new HostApplicationBuilder(args);

builder.Services.AddLavalink(); // Contained in the client support packages

var app = builder.Build();

var audioService = app.Services.GetRequiredService<IAudioService>();

// [...]

(ℹ️) Since Lavalink4NET v4, boilerplate code has been drastically reduced. It is also no longer required to initialize the node.

(ℹ️) If you only use Dependency Injection without Microsoft.Extensions.Hosting, you may need to call .StartAsync() on various Lavalink4NET services, like IAudioService, IInactivityTrackingService, ...

// Play a track
var playerOptions = new LavalinkPlayerOptions
{
    InitialTrack = new TrackQueueItem("https://www.youtube.com/watch?v=dQw4w9WgXcQ"),
};

await audioService.Players
    .JoinAsync(<guild id>, <voice channel id>, playerOptions, stoppingToken)
    .ConfigureAwait(false);

You can take a look at the example bots.

lavalink4net's People

Contributors

angelobreuer avatar chantalpaschalidis avatar cxnky avatar dependabot[bot] avatar erwin-j avatar freyacodes avatar giggitybyte avatar hmathieu31 avatar kalebbroo avatar kmen1 avatar koentech avatar korrdyn avatar kubaz2 avatar lulalaby avatar murl-digital avatar oolunar avatar silverdimond avatar skproch avatar thekrush avatar thomasloupe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lavalink4net's Issues

When restarting the bot and leaving Lavalink running it wont reconnect to the old player

Describe the bug
When you try to leave Lavalink running (with a player running) and restart the bot, it won't reconnect to the player that is running rather create a new one. So the old player which is running cant be controlled anymore until it has a timeout.

To Reproduce
Steps to reproduce the behavior:

  1. Start Lavalink
  2. Start bot
  3. Start music playback via Lavalink4NET
  4. Stop bot
  5. Start bot
  6. Try to stop the playback, change volume, ...

Expected behavior
Lavalink4NET reconnects with the old player so that it can be used and controlled again.

  • Lavalink4NET 1.4.11

Logs of Lavalink


2020-04-09 14:37:08.197  INFO 30927 --- [   XNIO-1 I/O-8] lavalink.server.io.SocketServer          : Connection closed from /0:0:0:0:0:0:0:1:35214 with status CloseStatus[code=1006, reason=] -- Session can be resumed within the next 60 seconds with key xxx

2020-04-09 14:37:11.863  INFO 30927 --- [ XNIO-1 task-38] l.server.io.HandshakeInterceptorImpl     : Incoming connection from /0:0:0:0:0:0:0:1:35236

2020-04-09 14:37:11.875  INFO 30927 --- [ XNIO-1 task-38] lavalink.server.io.SocketServer          : Connection successfully established from /0:0:0:0:0:0:0:1:35236

2020-04-09 14:37:11.883  INFO 30927 --- [   XNIO-1 I/O-4] lavalink.server.io.SocketServer          : {"op":"configureResuming","key":"yyy","timeout":60}

Wrong bands comparing in UpdateEqualizerAsync

Describe the bug
UpdateEqualizerAsync is not working properly.
If i have old: (Band - 0, Gain - 0) and new: (Band - 0, Gain - 1), method does nothing, although he should.

(please complete the following information):

  • Lavalink4NET 1.6.1

LavalinkNode.TrackStuck event not dispatched

Tracks appear to get stuck, but the LavalinkNode.TrackStuck event is not triggered.
I'm not sure if the event should be called or not when I think it should, so I'll describe how my track is getting stuck as well.

To reproduce:

  1. Connect a QueuedLavalinkPlayer to a channel.

  2. Leave it connected for a really long time. During this time, Discord reconnects will occur. I'm not sure if this fact is related to the problem.

    I'm using Discord.NET v2.3.0-dev-20200904.2 which is the latest dev release. I'm using this release because it correctly implements discord re-connects instead of failing to re-establish connections. The console outputs "Resumed previous session" instead of "Failed to resume previous session".

  3. Play a track. The TrackStarted event is called. Sometimes, the bot doesn't get that little green ring and it makes no sound. The player's TrackPosition property advances 1 second, but never past that.

    If this occurs, then the bot will consistently have this issue until it is disconnected and re-connected to the channel. While one bot is experiencing this problem, other instances of the same bot connected to the same Lavalink server may not experience this problem.

  4. Wait for one minute. The track finishes with reason CleanUp, and the TrackStuck event was never fired. TrackException is also not fired.

    On the several occasions I have seen this issue occur, the CleanUp event was never fired earlier than 1 minute and a few seconds after the track starts. The latest I've seen it fire was at one minute and 7 seconds after the TrackStarted event was called.

Expected behaviour
I believe that the TrackStuck event should fire because the player's TrackPosition property appears to remain constant. (I didn't test this thoroughly, the only output I have of this value is a value accurate to 1 second which is updated every 10 seconds).

Details

Lavalink4NET v1.5.2
Discord.NET v2.3.0-dev-20200904.2

Console output when this behaviour occurs:

14:50:37 | Debug | Search  | SearchResult for query songo di volare. (19 tracks)
14:50:37 | Debug | Player  | Added to queue in position 0: Christopher Tin - Sogno di Volare ("The Dream of Flight") (Civilization VI Main Theme)
14:50:37 | Debug | Audio   | Track started: Christopher Tin - Sogno di Volare ("The Dream of Flight") (Civilization VI Main Theme)
14:51:41 | Debug | Audio   | Track finished (CleanUp)

Lavalink server output when this behaviour occurs:

2020-09-20 14:50:36.617  INFO 24196 --- [lava-daemon-pool-info-loader-2-thread-24] lavalink.server.player.AudioLoader       : Loaded playlist Search results for: songo di volare
2020-09-20 14:50:37.171  INFO 24196 --- [XNIO-1 I/O-2] lavalink.server.io.SocketServer          : {"op":"play","guildId":"687018691023601726","track":"QAAAvwIAVkNocmlzdG9waGVyIFRpbiAtIFNvZ25vIGRpIFZvbGFyZSAoIlRoZSBEcmVhbSBvZiBGbGlnaHQiKSAoQ2l2aWxpemF0aW9uIFZJIE1haW4gVGhlbWUpAA9DaHJpc3RvcGhlciBUaW4AAAAAAAOlmAALV1FZTjJQM0UwNnMAAQAraHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1XUVlOMlAzRTA2cwAHeW91dHViZQAAAAAAAAAA","startTime":0,"noReplace":false}
2020-09-20 14:50:37.172  INFO 24196 --- [lava-daemon-pool-playback-1-thread-5] c.s.l.e.y.tools.ip.Ipv6Block             : /hidden
2020-09-20 14:50:37.622  INFO 24196 --- [lava-daemon-pool-playback-1-thread-5] c.s.l.e.y.tools.ip.Ipv6Block             : /hidden
2020-09-20 14:56:39.330  INFO 24196 --- [XNIO-1 I/O-2] lavalink.server.io.SocketServer          : {"op":"stop","guildId":"687018691023601726"}

Console output when a Discord re-connect occurs: (discord.net client output)

14:42:15 | Info  | Gateway | Disconnecting
14:42:15 | Info  | Gateway | Disconnected
14:42:16 | Info  | Gateway | Connecting
14:42:17 | Info  | Gateway | Resumed previous session
14:42:17 | Info  | Gateway | Connected

Similar behaviour also occurs for another person who is writing a music bot in JavaScript (erela.js I believe). They're using the same Lavalink server as me. This is why I'm making an issue about the TrackStuck event not being called, instead of the problem of the tracks themselves getting stuck.

Thank you in advance,
from Daniel.

Can not install Lavalink4NET.MemoryCache to .NET Framework 4.7.2 project.

Hi, I decided to use your library for making music functions for my discord bot, but when I tried to add Lavalink4NET.MemoryCache package from NuGet to my .NET Framework 4..7.2 project it gives me an error like "package is not supported on this platform". I created new .NET Core App project and it worked with it, but I need my project on .NET Framework. Could you please help me, thanks.

Discord.NET NullReferenceException

Bug description
NullReferenceException while using first constructor (which accepts DiscordShardedClient only) of type DiscordClientWrapper (library - Discord.NET), line 94 in file "DiscordClientWrapper.cs". Constructor is placed in this file on line 52.

Reproduce

using Discord.WebSocket;
using Lavalink4NET.DiscordNet;

var Discord = new DiscordShardedClient();

var Wrapper = new DiscordClientWrapper(Discord);

Allow extending LavalinkClusterNode

After recent changes, it became difficult to update my fork library (because a big difference between the codebase has accumulated) and I want to port some of my changes to upstream, which would give up using the fork.

Is your feature request related to a problem? Please describe.
In my bot i need to perform a custom actions when bot is kicked from the channel. Yes, it can be done in the global discordnet handler, but I need to change the behavior of the library when receiving an event from discord.

Describe the solution you'd like

  • Make LavalinkClusterNode non sealed and allow overriding VoiceStateUpdated.
  • Allow creating user defined node implementations in LavalinkCluster (may be via factory defined at LavalinkClusterOptions)

Additional context
May be good idea is a create generic LavalinkCluster for using with a user node implementations.
My implementation here (its not perfect, but worked for me)

[Question] Custom tracks?

Is your feature request related to a problem? Please describe.
Hello, I recently swapped over to lavalink4net from Victoria due to scaling issues and have had zero issues so far. However, I was trying to look into doing a custom track to tack on additional info for the tracks however I don't see an easy way to do this nor do I see anything in the documentation for this. I was able to quickly and easily create a custom player and that works great, however I also have tracks-specific properties I need to implement.

Describe the solution you'd like
I'd like to be able to extend the tracks and use them in a custom player that I already have. Use case would be to show track requester info, source (as I support other sources like vlare, SoundCloud, direct web, etc), and some other misc stuff like dashboard-link IDs. If this is already possible, an example in the documentation would be nice as well for other users.

Describe alternatives you've considered
I could create a custom list of tracks in the custom player and use the tracks as a key, however this is a hacky solution compared to just being able to extend the existing tracks.

Additional context
Another side question I have: is there a public Discord server for lavalink4net where users can discuss and ask questions relating to lavalink4net? If so, could I get an invite to it?

GetTracksAsync query issue

When you set a GetTrack query with several parameters, lavalink receives an invalid GET request.

  1. Create a new LavalinkNode;
  2. Call the GetTracksAsync function for LavalinkNode with the following query https://www.youtube.com/watch?v=videoId&list=listId (for example: https://www.youtube.com/watch?v=7CMyNJXaJmU&list=RD7CMyNJXaJmU&start_radio=1&t=0);
  3. This will create the following request /loadtracks?identifier=https://www.youtube.com/watch?v=videoId&list=listId
    and it will be parsed as {identifier = https: //www.youtube.com/watch?v=videoId, list = listId} and will return only 1 track, not the whole mix.

The identifier parameter must be https: //www.youtube.com/watch?v=videoId&list=listId.

  • Lavalink4NET Version 1.6.2

I think it's worth to encode the request passed to GetTracks Async. At least it helped me.

Speed effect doesn't affect TrackPosition

Describe the bug
When the effect that changes the playback speed is on, the TrackPosition changes at the normal speed.

To Reproduce
Steps to reproduce the behavior:

  1. Apply Timescale effect with changed speed

Expected behavior
TrackPosition must be actual track position.

Volume resets after node change

Describe the bug
When we change a node, the volume for the node is 1, and the player retains the previous volume (from the old node).

To Reproduce
Steps to reproduce the behavior:

  1. Start playing
  2. Change node
  3. See wrong volume level
  • Lavalink4NET Version 1.5.4

In my player implementation i override OnConnectedAsync and call SetVolumeAsync but it does nothing because player volume level already set (but wrong):

I think I need to add a parameter here:

public virtual async Task SetVolumeAsync(float volume = 1f, bool normalize = false, bool force = false)

And change the method accordingly.

Perhaps it is also worth doing something with Volume, but I don't know how to do this better (because if after changing the node you just call SetVolumeAsync (with volume = 1), then this will break the system for saving the volume level for me, for example).

UDP. Perhaps just after changing the node, you need to forcibly send volume change, respectively, with the player's Volume property.

Unable to resolve service for IDiscordClientWrapper while attempting to activate LavalinkCluster.

An exception is thrown when attempting to activate the LavalinkCluster class using dependency injection.

How to reproduce:

  1. Install the latest version (Currently it's 1.7.0).
  2. Replace IDiscordClientWrapper with DiscordClientWrapperBase in a .AddSingletion().
  3. Use LavalinkCluster in another .AddSingletion().
  4. Run the application.

I expected everything would go smoothly but it seems that the LavalinkCluster class implements IDiscordClientWrapper inside its constructor. Maybe replacing IDiscordClientWrapper with the new DiscordClientWrapperBase class and making the appropriate changes will make everything work again.

  • Lavalink4NET Version 1.7.0
  • Exception thrown: Unable to resolve service for type 'Lavalink4NET.IDiscordClientWrapper' while attempting to activate 'Lavalink4NET.Cluster.LavalinkCluster'.

Capture
Capture1

QueuedLavalinkPlayer refuses to play after using GetTracksAsync()

Describe the bug
The title says it all.

To Reproduce
(using DSharpPlus 4.2.0-nightly-1079 with DSharpPlus.SlashCommand)

  • _client.Lavalink is the DI'ed Lavalink client
[SlashCommand("pltest", "Playlist test command")]
public async Task MusicPlTestCmd(InteractionContext ctx) {
  await ctx.DeferAsync();

  await ctx.EditResponseAsync(new DiscordWebhookBuilder()
    .WithContent("Executing"));

  var player = await _client.Lavalink.JoinAsync < QueuedLavalinkPlayer > (ctx.Guild.Id, ctx.Member.VoiceState.Channel.Id, true);

  var tracks = await _client.Lavalink.GetTracksAsync("https://www.youtube.com/playlist?list=PL_h4IoimLDtTBLxUn8vJBq3DVsoHcwsJa");
  player.Queue.AddRange(tracks);
  var track2 = player.Queue.Dequeue();

  await player.PlayAsync(track2);
}

Expected behavior
The queued player should play from begin to end of the queue normally.

Screenshots

  • No play opcode is sent after calling PlayAsync()
    image

(please complete the following information):

  • Lavalink4NET Version: 2.1.1-preview.3
  • Exception thrown? None

Additional context

  • Lavalink still decodes the playlist
    image
    image
  • The queue works fine
    image

Missing implementation/definition of the `TrackStart` event

Is your feature request related to a problem? Please describe.
I won't be able to get my bot to run with a cluster of Lavalink Nodes. There always is an error in the Lavalink console. With a single Lavalink node everything works as expected.

Describe the solution you'd like
A better wiki entry that deals more with the cluster, how to use a cluster and how to start players from a cluser, ...

Additional context
Log of Lavalink:

2020-04-10 12:46:37.615  INFO 26841 --- [  XNIO-1 task-6] l.server.player.AudioLoaderRestHandler   : GET /loadtracks
2020-04-10 12:46:37.834  INFO 26841 --- [   XNIO-1 I/O-1] lavalink.server.io.SocketServer          : {"op":"voiceUpdate","guildId":"`xxx`","sessionId":"xxx","event":{"token":"xxx","guild_id":xxx,"endpoint":"eu-central922.discord.media:80"}}
2020-04-10 12:46:38.549  INFO 26841 --- [ader-2-thread-1] lavalink.server.player.AudioLoader       : Loaded track Unlike Pluto - Everything Black (feat. Mike Taylor)
2020-04-10 12:46:38.592  INFO 26841 --- [   XNIO-1 I/O-1] lavalink.server.io.SocketServer          : {"op":"play","guildId":"xxx","track":"QAAAlQIAM1VubGlrZSBQbHV0byAtIEV2ZXJ5dGhpbmcgQmxhY2sgKGZlYXQuIE1pa2UgVGF5bG9yKQAIQ2xvdWRLaWQAAAAAAAN+iAALQTZQbGU0YUpSeUEAAQAraHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1BNlBsZTRhSlJ5QQAHeW91dHViZQAAAAAAAAAA","startTime":0,"noReplace":false}
2020-04-10 12:46:38.605  INFO 26841 --- [   XNIO-1 I/O-1] lavalink.server.io.SocketServer          : Connection closed from /127.0.0.1:58946 -- CloseStatus[code=1007, reason=]
2020-04-10 12:46:38.606  INFO 26841 --- [   XNIO-1 I/O-1] lavalink.server.io.SocketContext         : Shutting down 1 playing players.
2020-04-10 12:46:38.609 ERROR 26841 --- [     parallel-1] lavalink.server.io.SocketContext         : Error

java.io.IOException: UT002002: Channel is closed
        at io.undertow.websockets.core.WebSocketChannel.send(WebSocketChannel.java:343) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendInternal(WebSockets.java:906) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendInternal(WebSockets.java:900) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendText(WebSockets.java:63) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendText(WebSockets.java:50) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at lavalink.server.io.SocketContext.send(SocketContext.kt:138) ~[classes!/:na]
        at lavalink.server.io.SocketContext.send(SocketContext.kt:127) ~[classes!/:na]
        at lavalink.server.io.SocketContext.handleMagmaEvent(SocketContext.kt:113) ~[classes!/:na]
        at lavalink.server.io.SocketContext.access$handleMagmaEvent(SocketContext.kt:48) ~[classes!/:na]
        at lavalink.server.io.SocketContext$1.accept(SocketContext.kt:82) ~[classes!/:na]
        at lavalink.server.io.SocketContext$1.accept(SocketContext.kt:48) ~[classes!/:na]
        at reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:130) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:793) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxCreate$BufferAsyncSink.next(FluxCreate.java:718) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxCreate$SerializedSink.next(FluxCreate.java:153) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at space.npstr.magma.impl.Magma.lambda$new$2(Magma.java:100) ~[impl-0.12.5.jar!/:na]
        at space.npstr.magma.impl.AudioStackLifecyclePipeline.hookOnNext(AudioStackLifecyclePipeline.java:121) ~[impl-0.12.5.jar!/:na]
        at space.npstr.magma.impl.AudioStackLifecyclePipeline.hookOnNext(AudioStackLifecyclePipeline.java:83) ~[impl-0.12.5.jar!/:na]
        at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:158) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.runAsync(FluxPublishOn.java:398) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.run(FluxPublishOn.java:484) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2020-04-10 12:46:38.609 ERROR 26841 --- [   XNIO-1 I/O-1] lavalink.server.io.SocketContext         : Error

java.io.IOException: UT002002: Channel is closed
        at io.undertow.websockets.core.WebSocketChannel.send(WebSocketChannel.java:343) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendInternal(WebSockets.java:906) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendInternal(WebSockets.java:900) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendText(WebSockets.java:63) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendText(WebSockets.java:50) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at lavalink.server.io.SocketContext.send(SocketContext.kt:138) ~[classes!/:na]
        at lavalink.server.io.SocketContext.send(SocketContext.kt:127) ~[classes!/:na]
        at lavalink.server.player.EventEmitter.onTrackEnd(EventEmitter.java:80) ~[classes!/:na]
        at com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter.onEvent(AudioEventAdapter.java:70) ~[lavaplayer-1.3.38.jar!/:na]
        at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer.dispatchEvent(DefaultAudioPlayer.java:350) ~[lavaplayer-1.3.38.jar!/:na]
        at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer.stopWithReason(DefaultAudioPlayer.java:137) ~[lavaplayer-1.3.38.jar!/:na]
        at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer.stopTrack(DefaultAudioPlayer.java:125) ~[lavaplayer-1.3.38.jar!/:na]
        at lavalink.server.player.Player.stop(Player.java:72) ~[classes!/:na]
        at lavalink.server.io.SocketContext.shutdown$Lavalink_Server(SocketContext.kt:181) ~[classes!/:na]
        at lavalink.server.io.SocketServer.afterConnectionClosed(SocketServer.kt:122) ~[classes!/:na]
        at org.springframework.web.socket.handler.WebSocketHandlerDecorator.afterConnectionClosed(WebSocketHandlerDecorator.java:85) ~[spring-websocket-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
        at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.afterConnectionClosed(LoggingWebSocketHandlerDecorator.java:72) ~[spring-websocket-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
        at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.afterConnectionClosed(ExceptionWebSocketHandlerDecorator.java:78) ~[spring-websocket-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
        at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.onClose(StandardWebSocketHandlerAdapter.java:144) ~[spring-websocket-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
        at io.undertow.websockets.jsr.UndertowSession.closeInternal(UndertowSession.java:235) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.jsr.FrameHandler$1.run(FrameHandler.java:107) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:170) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:167) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) ~[undertow-servlet-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:604) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:594) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.jsr.FrameHandler.onFullCloseMessage(FrameHandler.java:100) ~[undertow-websockets-jsr-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener$1.complete(AbstractReceiveListener.java:136) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener$1.complete(AbstractReceiveListener.java:124) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.BufferedBinaryMessage.read(BufferedBinaryMessage.java:95) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener.readBufferedBinary(AbstractReceiveListener.java:124) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener.bufferFullMessage(AbstractReceiveListener.java:98) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener.onClose(AbstractReceiveListener.java:62) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:50) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:33) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92) ~[xnio-api-3.3.8.Final.jar!/:3.3.8.Final]
        at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:951) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:932) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92) ~[xnio-api-3.3.8.Final.jar!/:3.3.8.Final]
        at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66) ~[xnio-api-3.3.8.Final.jar!/:3.3.8.Final]
        at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88) ~[xnio-nio-3.3.8.Final.jar!/:3.3.8.Final]
        at org.xnio.nio.WorkerThread.run(WorkerThread.java:561) ~[xnio-nio-3.3.8.Final.jar!/:3.3.8.Final]

2020-04-10 12:46:38.612 ERROR 26841 --- [     parallel-2] lavalink.server.io.SocketContext         : Error

java.io.IOException: UT002002: Channel is closed
        at io.undertow.websockets.core.WebSocketChannel.send(WebSocketChannel.java:343) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendInternal(WebSockets.java:906) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendInternal(WebSockets.java:900) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendText(WebSockets.java:63) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at io.undertow.websockets.core.WebSockets.sendText(WebSockets.java:50) ~[undertow-core-2.0.26.Final.jar!/:2.0.26.Final]
        at lavalink.server.io.SocketContext.send(SocketContext.kt:138) ~[classes!/:na]
        at lavalink.server.io.SocketContext.send(SocketContext.kt:127) ~[classes!/:na]
        at lavalink.server.io.SocketContext.handleMagmaEvent(SocketContext.kt:113) ~[classes!/:na]
        at lavalink.server.io.SocketContext.access$handleMagmaEvent(SocketContext.kt:48) ~[classes!/:na]
        at lavalink.server.io.SocketContext$1.accept(SocketContext.kt:82) ~[classes!/:na]
        at lavalink.server.io.SocketContext$1.accept(SocketContext.kt:48) ~[classes!/:na]
        at reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:130) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:793) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxCreate$BufferAsyncSink.next(FluxCreate.java:718) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxCreate$SerializedSink.next(FluxCreate.java:153) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at space.npstr.magma.impl.Magma.lambda$new$2(Magma.java:100) ~[impl-0.12.5.jar!/:na]
        at space.npstr.magma.impl.AudioStack.handleCloseWebSocket(AudioStack.java:168) ~[impl-0.12.5.jar!/:na]
        at space.npstr.magma.impl.AudioStack.hookOnNext(AudioStack.java:120) ~[impl-0.12.5.jar!/:na]
        at space.npstr.magma.impl.AudioStack.hookOnNext(AudioStack.java:54) ~[impl-0.12.5.jar!/:na]
        at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:158) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.runAsync(FluxPublishOn.java:398) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.run(FluxPublishOn.java:484) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

OnTrackEndAsync not fired after reconnect to node

Describe the bug
OnTrackEndAsync not fired after reconnect to node

To Reproduce
Steps to reproduce the behavior:

  1. Start bot
  2. Wait for connecting to lavalink
  3. Shutdown lavalink server
  4. Start lavalink server
  5. Wait for connecting to lavalink

Expected behavior
I expected that after re-joining everything would work as intended.

  • Lavalink4NET Version 1.4.6

Additional context
Instead of steps 4-5, there may simply be any break in the connection of the bot and lavalink

Bad Payload when the Lavalink node has more than 2gb reservable memory

Hello,

I just tried out this library and just got this tiny error. It does not affect my usage of this nice library, but I thought you might want to know:

Received bad payload: {"playingPlayers":0,"op":"stats","memory":{"reservable":4151836672,"used":25529976,"free":15704456,"allocated":41234432},"players":0,"cpu":{"cores":10,"systemLoad":0.07831206290252479,"lavalinkLoad":0.11399947957325007},"uptime":4694308}.
Newtonsoft.Json.JsonReaderException: Could not convert to integer: 4151836672. Path 'memory.reservable'. ---> System.OverflowException: Value was either too large or too small for an Int32.
   at System.Convert.ThrowInt32OverflowException()
   at System.Convert.ToInt32(Int64 value)
   at Newtonsoft.Json.JsonReader.ReadAsInt32()
   --- End of inner exception stack trace ---
   at Newtonsoft.Json.JsonReader.ReadAsInt32()
   at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
   at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType)
   at Lavalink4NET.Payloads.PayloadConverter.ReadPayload(String json)
   at Lavalink4NET.LavalinkSocket.ProcessNextPayload()

[JsonRequired, JsonProperty("reservable")]
public int ReservableMemory { get; internal set; }

I think the problem is that the code allocates an int32 (signed, so the max value is 2147483647, approximately 2 gigabytes). If the Lavalink node always uses 32 bit (so the process should only have 4gb), an unsigned int may suffice, otherwise a larger type may be better.

PlayerState.NotPlaying after server region change

Describe the bug
The player state is reset to NotPlaying after the region at the server changes even if music was playing before

To Reproduce
Steps to reproduce the behavior:

  1. Start bot
  2. Start listening music
  3. Change server region / wait for voice server auto change
  4. See error

Expected behavior
I think State should not change

  • Lavalink4NET v1.4.10

Additional context
I think this is the reason:

State = PlayerState.NotPlaying;

This seems to require .NET 2.0 so I can't use it on my project, is it supposed to be for .NET 2.0?

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

(please complete the following information):

  • Lavalink4NET Version
  • Exception thrown? (consider enabling logging)

Additional context
Add any other context about the problem here.

Add DisposeOnDisconnect

Add a DisposeOnDisconnect bool in LavalinkNodeOptions (or another name).
Reason: As I can see if someone disconnects the bot through Discord (by having the permission) the player gets disposed and removed from the players dictionary.
But, I already made my own method to check if the bot was disconnected forcefully, so I can send a message in the music output channel. I can dispose the player then manually. But the event from the library gets triggered first before mine.
Can you add this option and some checks in your VoiceStateUpdated event?

TrackException doesn't fire at all.

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Make a bot
  2. Try to play this song: https://www.youtube.com/watch?v=ptzzU7jFQwo
  3. IAudioService sometimes fire under TrackEnd and doesn't fire TrackException.

Expected behavior
IAudioService should fire a TrackException.

Screenshots
image

(please complete the following information):

  • Lavalink4NET Version : 2.1.0
  • Exception thrown? N/A

ArgumentNullException in inactivity tracking service

Describe the bug
Global looped uncaught exception:

System.ArgumentNullException: Value cannot be null. (Parameter 'player')
   at Lavalink4NET.Tracking.InactivityTrackingService.UntrackPlayerAsync(LavalinkPlayer player)
   at Lavalink4NET.Tracking.InactivityTrackingService.PollAsync()

To Reproduce
Steps to reproduce the behavior:

  1. Setup inactivity tracking service
  2. Use the bot
  3. Wait (and when you're lucky)
  4. See error

Expected behavior
Probably when player = null you need to force remove it from the list or something

(please complete the following information):

  • Lavalink4NET Version 1.4.16

IEnumerable`1<DiscordMember> DiscordChannel.get_Users() returns a method not found bricking Lavalink4nets voting features

Describe the bug
While executing VoteLavalinkPlayer.VoteAsync(UInt64 userId, Single percentage) it tries to run a method that can not be found in the latest dsharpplus version (4.2.0-nightly-00987 and some other newer ones)

To Reproduce
Steps to reproduce the behavior:

  1. Make sure to use a new nightly version of DSharpPlus
  2. Make a VoteLavalinkPlayer
  3. Execute VoteAsync on it
  4. See error

Expected behavior
No error to occur

Screenshots
If applicable, add screenshots to help explain your problem.

(please complete the following information):

  • Lavalink4NET Version
    2.0.1-preview.1
  • Exception thrown? (consider enabling logging)
Method not found: 'System.Collections.Generic.IEnumerable`1<DSharpPlus.Entities.DiscordMember> DSharpPlus.Entities.DiscordChannel.get_Users()'.
Stack trace:
  at Lavalink4NET.DSharpPlus.DiscordClientWrapperBase.GetChannelUsersAsync(UInt64 guildId, UInt64 voiceChannelId)
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
  at Lavalink4NET.DSharpPlus.DiscordClientWrapperBase.GetChannelUsersAsync(UInt64 guildId, UInt64 voiceChannelId)
  at Lavalink4NET.Player.VoteLavalinkPlayer.GetVoteInfoAsync()
  at Lavalink4NET.Player.VoteLavalinkPlayer.VoteAsync(UInt64 userId, Single percentage)

Additional context
Add any other context about the problem here.

Player disposes

Describe the bug
Sometimes the player is simply destroyed for no apparent reason.

To Reproduce
Steps to reproduce the behavior:

  1. Setup lavalink, bot
  2. Start listening
  3. Wait a certain amount of time
  4. Voila

Expected behavior
The player should work fine

  • Lavalink4NET Version 1.5.3

C# side player dispose stacktrace:

at Bot.DiscordRelated.Music.AdvancedLavalinkPlayer.Dispose()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Bot.DiscordRelated.Music.AdvancedLavalinkPlayer.Dispose()
   at Lavalink4NET.LavalinkNode.OnEventReceived(EventPayload payload)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Lavalink4NET.LavalinkNode.OnEventReceived(EventPayload payload)
   at Lavalink4NET.LavalinkNode.OnPayloadReceived(PayloadReceivedEventArgs eventArgs)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Lavalink4NET.LavalinkNode.OnPayloadReceived(PayloadReceivedEventArgs eventArgs)
   at Lavalink4NET.LavalinkSocket.ProcessNextPayload()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
   at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
   at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetResult(TResult result)
   at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
   at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
   at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
   at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, CancellationToken cancellationToken, Boolean throwOnPrematureClosure)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
   at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
   at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
   at System.Net.Http.HttpConnection.RawConnectionStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
   at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
   at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
   at System.Net.Http.HttpConnection.ReadBufferedAsyncCore(Memory`1 destination)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.InvokeContinuation(Action`1 continuation, Object state, Boolean forceAsync, Boolean requiresExecutionContextFlow)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.OnCompleted(SocketAsyncEventArgs _)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pNativeOverlapped)

Note: AdvancedLavalinkPlayer.Dispose() this is an override of LavalinkPlayer Dispose()

Perhaps the problem is not with Lavalink4Net, but to be honest, I was already desperate to find its source or stable workaround.
If it helps, then here are my sources: https://gitlab.com/skprochlab/nJMaLBot

Preferred node for getting music information

Is your feature request related to a problem? Please describe.
I think that it would be nice to be able for the cluster to select a node to which all requests for receiving information about tracks will go priority.

Describe the solution you'd like
Add the property of the preferred node to the cluster, and the developer will independently set the desired node.

Describe alternatives you've considered
It is possible to implement this through a node configuration or something similar.

Additional context
This is due to the fact that I can’t, for example, locate nodes in the USA because from there I can’t get information about Yandex Music (it works only in the CIS).

LavalinkNode exit with an error when using in a hosted console app.

Describe the bug
The application exit with an error when running in a hosted environment

To Reproduce
Steps to reproduce the behavior:

  1. Create a hosted discord bot
  2. Shutdown the bot while it's playing a song

Expected behavior
Closing the app without a crash

(please complete the following information):

  • Lavalink4NET 2.0.1-preview.3.1
  • Exception thrown:
    image

Additional context
I fixed the issue by making my own LavalinkNode and Players?.Clear();

    public class HostingLavalinkNode : LavalinkNode
    {
        private readonly IDiscordClientWrapper _discordClient;

        private bool _disposed;

        public HostingLavalinkNode(LavalinkNodeOptions options, IDiscordClientWrapper client, IDiscordClientWrapper discordClient, ILogger logger = null, ILavalinkCache cache = null) : base(options, client, logger, cache)
        {
            _discordClient = discordClient;
        }

        public override void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;
            _discordClient.VoiceServerUpdated -= VoiceServerUpdated;
            _discordClient.VoiceStateUpdated -= VoiceStateUpdated;
            foreach (var player in Players)
            {
                player.Value.Dispose();
            }

            Players?.Clear();
            base.Dispose();
            GC.SuppressFinalize(this);
        }
    }

Nodes stop trying to reconnect after reconnecting once

Describe the bug
A node stops trying to reconnect when it already reconnected once

To Reproduce
Steps to reproduce the behavior:

  1. Start Lavalink
  2. Connect to it using Lavalink4Net
  3. Stop Lavalink
  4. Start Lavalink
  5. Stop Lavalink
  6. Start Lavalink
  7. See error

Expected behavior
A node should try to reconnect till it gets canceled via a event.

Screenshots
If applicable, add screenshots to help explain your problem.

(please complete the following information):

  • Lavalink4NET Version
    1.4.7
  • Exception thrown? (consider enabling logging)
    No its caused by a logic misstake.

Additional context
Bug is caused by

if (IsConnected)
{
// reconnection successful
return;
}
being return not break

Moving player to new node

Describe the bug
After the node falls, the player moves to another node. This is done through the

private async Task MovePlayerInternalAsync(LavalinkPlayer player, LavalinkNode node)
{
EnsureNotDisposed();
var wasPlaying = player.State == PlayerState.Playing;
var trackPosition = player.TrackPosition;
// destroy (NOT DISCONNECT) the player
await player.DestroyAsync();
// update the communication node
player.LavalinkSocket = node;
// resend voice update to the new node
await player.UpdateAsync();
// play track if one is playing
if (wasPlaying)
{
// restart track
await player.PlayAsync(player.CurrentTrack, trackPosition);
}
// add player to the new node
node.Players[player.GuildId] = player;
}

The player is first destroyed, and then the UpdateAsync method is called, which does not restore the state of the player from Destroyed
internal async Task UpdateAsync()
{
if (_voiceServer is null || _voiceState is null)
{
// voice state or server is missing
return;
}
// send voice update payload
await LavalinkSocket.SendPayloadAsync(new VoiceUpdatePayload(_voiceState.GuildId,
_voiceState.VoiceSessionId, new VoiceServerUpdateEvent(_voiceServer)));
if (State == PlayerState.NotConnected)
{
// set initial player state to connected, if player was not connected,
// see: https://github.com/angelobreuer/Lavalink4NET/issues/28
State = PlayerState.NotPlaying;
}
// trigger event
await OnConnectedAsync(_voiceServer, _voiceState);
}

At the time this line was executed, the player is still destroyed:
await OnConnectedAsync(_voiceServer, _voiceState);

I think there should be something like:

     if (State == PlayerState.NotConnected || State == PlayerState.Destroyed) 
     { 
         State = PlayerState.NotPlaying; 
     } 
  • Lavalink4NET Version 1.4.14

Disconnect on VoiceState change

Description
When you change bot's VoiceState (e.g. server mute, move bot to another voice channel) it simply disconnects without any error (see additional context), i can't find anything in my code that calls Disconnect, so i think there's something with the library. I tried looking into sources, but i didn't find anything that can help with my issue.

To Reproduce
Steps to reproduce the behavior:

  1. Play something
  2. Mute bot on the server

Expected behavior
Bot should not be disconnected from voice channel

Versions
Lavalink4NET 1.5.5
Discord.Net v2.3.0-dev-20201028.4 (no difference in behavior at stable, but dev handles reconnections better)

Additional context
In Lavalink4NET logs:

[Lavalink4NET.LavalinkNode]: Voice WebSocket was closed for player: 66311...13
Close Code: NormalClosure (1000, Reason: , By Remote: No

In Lavalink server logs i see

{"op":"destroy", "guildId":"..."}

and nothing abnormal except the line above.

Docs not showing when using nuget

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
I have to go on a github page to check docs.

Describe the solution you'd like
A clear and concise description of what you want to happen.
Include the documentation on nuget

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
I dont know

Additional context
Add any other context or screenshots about the feature request here.

Docs on github
image

No docs when using rider
image

Processing bot moves through channels

After the server administrator forces the bot to another voice channel, the bot is disconnected after some amount of time due to the inactivity tracker.

However, VoiceChannelId has a private setter and can only be changed using ConnectAsync. However, after calls to this method State is automatically set to NotPlaying, why is this done?

Maybe I'm doing something wrong or missing something?

Implement Lavalink v3.4 features

Is your feature request related to a problem? Please describe.
In a new Lavalink update several features and changes were introduced:

  • Added filters
  • The error string on the TrackExceptionEvent has been deprecated and replaced by the exception object following the same structure as the LOAD_FAILED error on /loadtracks
  • Added the connected boolean to player updates.

I'd love to see support for filters

queue does not matter if different user enqueues track (instantly plays other users track)

Describe the bug
queue does not matter if different user enqueues track (instantly plays other users track)

To Reproduce
Steps to reproduce the behavior:

  1. play track
  2. add another track to queue
  3. other user: add track to queue
  4. other user: track plays without respecting queue

Expected behavior
add other users track to queue without instantly playing the track that got added

Screenshots
second user should have this message "Added to queue: "
image

(please complete the following information):

  • Lavalink4NET Version: 1.4.20
  • Exception thrown: none

Additional context
(also tried the enqueue parameter in the playAsync before)
image

Nodes personalization

I think it would be useful to be able to give the node some parameters (e.g. name)

Describe the solution you'd like
I think this could be added to LavalinkNodeOptions

Track null while moving player to another node

Describe the bug
When we try to change the node for the player, we get an exception, because when the player is destroyed, the current track is nulled.

To Reproduce
Steps to reproduce the behavior:

  1. Start playing something
  2. Switch node
  3. See error
  • Lavalink4NET Version 1.5.3
  • Exception thrown:
System.ArgumentNullException: Value cannot be null. (Parameter 'track')
   at Lavalink4NET.Player.LavalinkPlayer.PlayAsync(LavalinkTrack track, Nullable`1 startTime, Nullable`1 endTime, Boolean noReplace)
   at Lavalink4NET.LavalinkNode.MovePlayerInternalAsync(LavalinkPlayer player, LavalinkNode node)
   at Lavalink4NET.LavalinkNode.MovePlayerAsync(LavalinkPlayer player, LavalinkNode node)

Additional context
CurrentTrack become null after this:

await player.UpdateAsync();

Inactivity Tracking Service requires Lavalink4NET logger (though it should be optional)

Describe the bug
Inactivity Tracking Service requires Lavalink4NET logger (though it should be optional).
I registered the following code:

                .AddSingleton<InactivityTrackingOptions>()
                .AddSingleton<InactivityTrackingService>()

And later used:

            _provider.GetRequiredService<InactivityTrackingService>().BeginTracking();

Then I get this nice error message:

Unhandled Exception: System.InvalidOperationException: Unable to resolve service for type 'Lavalink4NET.ILogger' while attempting to activate 'Lavalink4NET.Tracking.InactivityTrackingService'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.CreateServiceAccessor(Type serviceType)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at MusicBot.Services.StartupService.InitializeLavalink() in /app/Services/StartupService.cs:line 69
   at MusicBot.Services.StartupService.StartAsync() in /app/Services/StartupService.cs:line 64
   at MusicBot.Startup.RunAsync() in /app/Startup.cs:line 47
   at MusicBot.Startup.RunAsync(String[] args) in /app/Startup.cs:line 31
   at MusicBot.Program.<Main>(String[] args)

To Reproduce

  • Use an InactivityTrackingService without registering an Lavalink4NET.ILogger.

Expected behavior
It should run without requiring a logger.

(please complete the following information):

  • Lavalink4NET Version 1.2.0
  • Exception thrown? (consider enabling logging)

Additional context
I'll just solve the issue on my side by adding a Lavalink4NET.ILogger, but the wiki states that logging is optional, so I just wanted to let you know that here may exist an issue.

An additional client wrapper for DSharpPlus' DiscordShardedClient.

I noticed i couldn't use DiscordClientWrapper together with DSharpPlus' DiscordShardedClient, so i tried to make my own wrapper using IDiscordClientWrapper but i ran into problems along the way. In the end, i couldn't make it work and for now i am stuck with using DSharpPlus' DiscordClient.

It would be a good idea to include a sharded client wrapper (For example: DiscordShardedClientWrapper) because it would allow people who use sharded clients for their bots to use this library.

[Question] Player initialization

I have a child class of the player that inherits from LavalinkPlayer.
Previously, the constructor passed GuildId, which was used in my player constructor to load guild information from the database. Now that the factory is being used, the constructor should be empty, but how do I know GuildId then?
When and how should I load guild information now?

Bot will disconnect from the voice channel even after setting DisconnectOnStop to false

Describe the bug
When I stop the player with await player.StopAsync(false); its supposed set the DisconnectOnStop to be false but when browsing through the break point I noticed that after this line, DisconnectOnStop is being true.

Expected behavior
When I do StopAsync(false); the DisconnectOnStop bool supposed to be false

Lavalink4NET version - 1.6.2
image
image

*Note: In the second picture you can see how the debugger already passed the line with the StopAsync(false); but the DisconnectOnStop at the bottom of the page is set to true and the same happens if I write StopAsync(false); or StopAsync(disconnect: false);

Connection to Lavalink Nove "ws://localhost:8080" failed!

I realise that this is most likely a very noob question, but I'm unsure where else to ask this, so I'm sorry for submitting it here. Still, how do I go about solving this?

This is the output of a project I pretty much built using the ExampleBot from the samples directory.

image

QueuedLavalinkPlayer.State property incorrect after skip

Describe the bug
The State property of QueuedLavalinkPlayer is set to NotPlaying after SkipAsync() is called.

To Reproduce
Steps to reproduce the behaviour:

  1. Play a track by adding it to the queue using player.Queue.Add(track) and calling PlayTopAsync(track), where track has been dequeued.
    image

  2. Add another track to the queue by using player.Queue.Add(track), but without calling PlayTopAsync() this time or dequeueing the track.

  3. Skip to the next track using player.SkipAsync();

  4. In a different method a bit later on, get the player using LavalinkNode.GetPlayer() and check the State property. The player is playing music, the track finished event has not been called, and yet player.State is set to NotPlaying.

Expected behaviour
I would expect the player's state property to remain at playing and not change to NotPlaying.

Screenshots
If applicable, add screenshots to help explain your problem.

image

(please complete the following information):

  • Lavalink4NET Version v1.4.21
  • Exception thrown: None

Additional context

In my case I'm finding that State is set to NotPlaying when I try to call my PlayPause method.
The player which has the incorrect value for State still has the correct values for the current track and position.
If I fetch the player immediately after calling SkipAsync(), State is still set to Playing.

Add interface to LavalinkTrack

Is your feature request related to a problem? Please describe.
I am missing the ability to override the track implementation.

Describe the solution you'd like
It would be nice to have ILavalinkTrack or something similar. I also wanted to have a generic player implementation like LavalinkPlayer<T> where T : ILavalinkTrack

Support for lavalink plugins

Is your feature request related to a problem? Please describe.
It would be nice to be able to support third-party plugins for lavalink. For example - https://github.com/Topis-Lavalink-Plugins/Sponsorblock-Plugin

Describe the solution you'd like
Since plugins add their own events to transfer, it would be nice to be able to process all the events, extracting the necessary data from them. It would also be nice if we could also create libraries to support certain plugins.

Additional context
All lavalink plugins - https://github.com/topics/lavalink-plugin
Examples of events - one, two
Also, It is also possible appearance of plugins that will not only send events, but also receive any non-standard messages. Perhaps we should consider supporting this.

After disconnecting a node, it does not try to connect to it back sometimes

Describe the bug
Sometimes after a node disconnects, it does not try to connect back to it.

To Reproduce
Steps to reproduce the behavior:

  1. Launch bot, connect to lavalink
  2. While somebody uses lavalink in your bot, turn off the lavalink server

Expected behavior
I think that even if a node is disconnected while it is playing something, it is necessary to try to connect to it

  • Lavalink4NET Version 1.4.8

Additional context
According to my observations, this happens mainly when at that time the node reproduced something.

Logs
When node is free:

(Stay-Online) Node died! Moving players to a new node...
Trying to resume Lavalink Session ... Key: xxx

When node are busy:

(Stay-Online) Node died! Moving players to a new node...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.