@students-dev/audify-js 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 @students-dev/audify-js
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -12,7 +12,7 @@ A lightweight, modern, modular audio engine that provides playback, filters, que
12
12
  - 🎛️ **Audio Filters**: Bassboost, nightcore, vaporwave, 8D rotate, pitch/speed adjustment, reverb
13
13
  - 🔌 **Plugin System**: Extensible architecture with lifecycle hooks
14
14
  - 📡 **Event-Driven**: Comprehensive event system for all operations
15
- - 🌐 **Multi-Source Support**: Local files, remote URLs, YouTube, SoundCloud
15
+ - 🌐 **Multi-Source Support**: Local files, remote URLs, YouTube, SoundCloud, Spotify, Lavalink
16
16
  - 🛠️ **Developer-Friendly**: TypeScript declarations, ESM/CJS builds
17
17
 
18
18
  ## Installation
@@ -76,6 +76,39 @@ The main class that orchestrates all audio functionality.
76
76
  const engine = new AudioEngine(options);
77
77
  ```
78
78
 
79
+ #### Spotify Integration
80
+
81
+ ```javascript
82
+ // Initialize Spotify provider
83
+ engine.initSpotify({
84
+ clientId: 'your_client_id',
85
+ clientSecret: 'your_client_secret'
86
+ });
87
+
88
+ // Search tracks
89
+ const results = await engine.searchSpotifyTracks('query', { token: 'access_token' });
90
+
91
+ // Load track
92
+ const track = await engine.loadSpotifyTrack('track_id', { token: 'access_token' });
93
+ ```
94
+
95
+ #### Lavalink Integration
96
+
97
+ ```javascript
98
+ // Connect to Lavalink server
99
+ await engine.connectLavalink({
100
+ host: 'localhost',
101
+ port: 2333,
102
+ password: 'youshallnotpass'
103
+ });
104
+
105
+ // Load track from Lavalink
106
+ const track = await engine.loadLavalinkTrack('ytsearch:query');
107
+
108
+ // Get Lavalink player for Discord bots
109
+ const player = engine.getLavalinkPlayer('guild_id', 'channel_id');
110
+ ```
111
+
79
112
  #### Methods
80
113
 
81
114
  ##### Playback Controls
@@ -219,7 +252,7 @@ engine.pluginManager.enable('my-plugin');
219
252
  Fetch metadata from different sources.
220
253
 
221
254
  ```javascript
222
- import { YouTubeProvider, SoundCloudProvider, LocalProvider } from '@students-dev/audify-js';
255
+ import { YouTubeProvider, SoundCloudProvider, LocalProvider, SpotifyProvider, LavalinkProvider } from '@students-dev/audify-js';
223
256
 
224
257
  // YouTube
225
258
  const ytInfo = await YouTubeProvider.getInfo('https://youtube.com/watch?v=VIDEO_ID');
@@ -229,6 +262,16 @@ const scInfo = await SoundCloudProvider.getInfo('https://soundcloud.com/artist/t
229
262
 
230
263
  // Local file (Node.js only)
231
264
  const localInfo = await LocalProvider.getInfo('/path/to/file.mp3');
265
+
266
+ // Spotify (requires access token)
267
+ const spotify = new SpotifyProvider({ clientId: 'your_client_id' });
268
+ spotify.setAccessToken('access_token');
269
+ const trackInfo = await spotify.getTrack('track_id');
270
+
271
+ // Lavalink (requires Lavalink server)
272
+ const lavalink = new LavalinkProvider({ host: 'localhost', port: 2333, password: 'password' });
273
+ await lavalink.connect();
274
+ const lavalinkTrack = await lavalink.loadTrack('ytsearch:query');
232
275
  ```
233
276
 
234
277
  ### Utils
@@ -250,6 +293,17 @@ const probe = await ProbeUtils.probe(audioBuffer);
250
293
 
251
294
  ## Examples
252
295
 
296
+ The `examples/` directory contains comprehensive examples demonstrating various features:
297
+
298
+ ### 📁 Available Examples
299
+
300
+ - **`browser-example.html`** - Interactive browser demo with UI controls
301
+ - **`nodejs-example.js`** - Node.js usage with event handling
302
+ - **`queue-example.js`** - Queue management operations
303
+ - **`plugin-examples.js`** - Custom plugin implementations
304
+ - **`spotify-example.js`** - Spotify API integration
305
+ - **`lavalink-example.js`** - Lavalink server integration
306
+
253
307
  ### Basic Playback
254
308
 
255
309
  ```javascript
@@ -291,15 +345,46 @@ engine.add([
291
345
  { url: 'song2.mp3', title: 'Song Two' }
292
346
  ]);
293
347
 
294
- // Skip to next
348
+ // Navigation
295
349
  engine.next();
350
+ engine.previous();
351
+ engine.jump(2); // Jump to track at index 2
296
352
 
297
- // Shuffle queue
353
+ // Modify queue
298
354
  engine.shuffle();
299
-
300
- // Clear and add new tracks
301
355
  engine.clear();
302
- engine.add('new-song.mp3');
356
+
357
+ // Remove specific track
358
+ engine.remove(0); // Remove by index
359
+ engine.remove('track-id'); // Remove by ID
360
+ ```
361
+
362
+ ### Audio Filters
363
+
364
+ ```javascript
365
+ // Bass boost
366
+ engine.applyFilter('bassboost', { gain: 1.5 });
367
+
368
+ // Nightcore effect
369
+ engine.applyFilter('nightcore', { rate: 1.2 });
370
+
371
+ // Vaporwave effect
372
+ engine.applyFilter('vaporwave', { rate: 0.8 });
373
+
374
+ // 8D Audio
375
+ engine.applyFilter('8d');
376
+
377
+ // Pitch adjustment
378
+ engine.applyFilter('pitch', { pitch: 1.1 });
379
+
380
+ // Speed adjustment
381
+ engine.applyFilter('speed', { speed: 1.25 });
382
+
383
+ // Reverb
384
+ engine.applyFilter('reverb', { preset: 'hall' });
385
+
386
+ // Remove filter
387
+ engine.removeFilter('bassboost');
303
388
  ```
304
389
 
305
390
  ### Custom Plugin
@@ -331,6 +416,46 @@ engine.pluginManager.load(loggerPlugin);
331
416
  engine.pluginManager.enable('logger');
332
417
  ```
333
418
 
419
+ ### Browser Usage
420
+
421
+ ```html
422
+ <!DOCTYPE html>
423
+ <html>
424
+ <head>
425
+ <title>audify-js Demo</title>
426
+ </head>
427
+ <body>
428
+ <button id="playBtn">Play</button>
429
+ <button id="bassBtn">Bass Boost</button>
430
+
431
+ <script type="module">
432
+ import { AudioEngine } from '@students-dev/audify-js';
433
+
434
+ const engine = new AudioEngine();
435
+
436
+ engine.on('ready', () => {
437
+ engine.add('audio.mp3');
438
+
439
+ document.getElementById('playBtn').onclick = () => engine.play();
440
+ document.getElementById('bassBtn').onclick = () =>
441
+ engine.applyFilter('bassboost');
442
+ });
443
+ </script>
444
+ </body>
445
+ </html>
446
+ ```
447
+
448
+ ### Running Examples
449
+
450
+ ```bash
451
+ # Browser example
452
+ # Open examples/browser-example.html in your browser
453
+
454
+ # Node.js examples
455
+ node examples/nodejs-example.js
456
+ node examples/queue-example.js
457
+ ```
458
+
334
459
  ## Browser Compatibility
335
460
 
336
461
  - Chrome 14+
@@ -385,6 +510,13 @@ Some filters require AudioWorklet support in modern browsers. Check browser comp
385
510
 
386
511
  ## Changelog
387
512
 
513
+ ### v1.1.0
514
+ - Added Spotify integration with client-side API support
515
+ - Added Lavalink integration for server-based audio streaming
516
+ - Updated dependencies for better performance and security
517
+ - Enhanced TypeScript type definitions
518
+ - Added comprehensive examples for new integrations
519
+
388
520
  ### v1.0.0
389
521
  - Initial release
390
522
  - Core audio engine
package/dist/cjs/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ var SpotifyWebApi = require('spotify-web-api-node');
4
+ var lavalinkClient = require('lavalink-client');
3
5
  var fs = require('fs');
4
6
  var path = require('path');
5
7
 
6
8
  /**
7
9
  * Loop modes for playback
8
10
  */
9
- const LOOP_MODES$1 = {
11
+ const LOOP_MODES = {
10
12
  OFF: 'off',
11
13
  TRACK: 'track',
12
14
  QUEUE: 'queue'
@@ -15,7 +17,7 @@ const LOOP_MODES$1 = {
15
17
  /**
16
18
  * Repeat modes (alias for loop modes)
17
19
  */
18
- const REPEAT_MODES = LOOP_MODES$1;
20
+ const REPEAT_MODES = LOOP_MODES;
19
21
 
20
22
  /**
21
23
  * Filter types
@@ -126,7 +128,7 @@ class Player {
126
128
  this.currentTime = 0;
127
129
  this.duration = 0;
128
130
  this.volume = 1;
129
- this.loopMode = LOOP_MODES$1.OFF;
131
+ this.loopMode = LOOP_MODES.OFF;
130
132
  this.eventBus = new EventBus();
131
133
  }
132
134
 
@@ -239,14 +241,14 @@ class Player {
239
241
  */
240
242
  handleTrackEnd() {
241
243
  switch (this.loopMode) {
242
- case LOOP_MODES$1.TRACK:
244
+ case LOOP_MODES.TRACK:
243
245
  // Replay current track
244
246
  break;
245
- case LOOP_MODES$1.QUEUE:
247
+ case LOOP_MODES.QUEUE:
246
248
  // Play next in queue
247
249
  this.audioEngine.queue.getNext();
248
250
  break;
249
- case LOOP_MODES$1.OFF:
251
+ case LOOP_MODES.OFF:
250
252
  }
251
253
  }
252
254
 
@@ -740,6 +742,276 @@ class Queue {
740
742
  }
741
743
  }
742
744
 
745
+ /**
746
+ * Spotify provider for client-side API integration
747
+ */
748
+ class SpotifyProvider {
749
+ constructor(options = {}) {
750
+ this.spotifyApi = new SpotifyWebApi({
751
+ clientId: options.clientId,
752
+ clientSecret: options.clientSecret,
753
+ redirectUri: options.redirectUri,
754
+ accessToken: options.accessToken,
755
+ refreshToken: options.refreshToken
756
+ });
757
+ }
758
+
759
+ /**
760
+ * Set access token
761
+ * @param {string} token - OAuth access token
762
+ */
763
+ setAccessToken(token) {
764
+ this.spotifyApi.setAccessToken(token);
765
+ }
766
+
767
+ /**
768
+ * Set refresh token
769
+ * @param {string} token - OAuth refresh token
770
+ */
771
+ setRefreshToken(token) {
772
+ this.spotifyApi.setRefreshToken(token);
773
+ }
774
+
775
+ /**
776
+ * Refresh access token
777
+ * @returns {Promise<Object>} Token response
778
+ */
779
+ async refreshAccessToken() {
780
+ try {
781
+ const data = await this.spotifyApi.refreshAccessToken();
782
+ this.spotifyApi.setAccessToken(data.body.access_token);
783
+ return data.body;
784
+ } catch (error) {
785
+ throw new Error(`Failed to refresh token: ${error.message}`);
786
+ }
787
+ }
788
+
789
+ /**
790
+ * Search tracks
791
+ * @param {string} query - Search query
792
+ * @param {Object} options - Search options
793
+ * @returns {Promise<Array>} Array of track objects
794
+ */
795
+ async searchTracks(query, options = {}) {
796
+ try {
797
+ const data = await this.spotifyApi.searchTracks(query, {
798
+ limit: options.limit || 20,
799
+ offset: options.offset || 0
800
+ });
801
+
802
+ return data.body.tracks.items.map(track => this._formatTrack(track));
803
+ } catch (error) {
804
+ throw new Error(`Failed to search tracks: ${error.message}`);
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Get track by ID
810
+ * @param {string} trackId - Spotify track ID
811
+ * @returns {Promise<Object>} Track object
812
+ */
813
+ async getTrack(trackId) {
814
+ try {
815
+ const data = await this.spotifyApi.getTrack(trackId);
816
+ return this._formatTrack(data.body);
817
+ } catch (error) {
818
+ throw new Error(`Failed to get track: ${error.message}`);
819
+ }
820
+ }
821
+
822
+ /**
823
+ * Get tracks by IDs
824
+ * @param {Array<string>} trackIds - Array of Spotify track IDs
825
+ * @returns {Promise<Array>} Array of track objects
826
+ */
827
+ async getTracks(trackIds) {
828
+ try {
829
+ const data = await this.spotifyApi.getTracks(trackIds);
830
+ return data.body.tracks.map(track => this._formatTrack(track));
831
+ } catch (error) {
832
+ throw new Error(`Failed to get tracks: ${error.message}`);
833
+ }
834
+ }
835
+
836
+ /**
837
+ * Get audio features for track
838
+ * @param {string} trackId - Spotify track ID
839
+ * @returns {Promise<Object>} Audio features
840
+ */
841
+ async getAudioFeatures(trackId) {
842
+ try {
843
+ const data = await this.spotifyApi.getAudioFeaturesForTrack(trackId);
844
+ return data.body;
845
+ } catch (error) {
846
+ throw new Error(`Failed to get audio features: ${error.message}`);
847
+ }
848
+ }
849
+
850
+ /**
851
+ * Format Spotify track to internal format
852
+ * @param {Object} spotifyTrack - Spotify track object
853
+ * @returns {Object} Formatted track
854
+ * @private
855
+ */
856
+ _formatTrack(spotifyTrack) {
857
+ return {
858
+ id: spotifyTrack.id,
859
+ title: spotifyTrack.name,
860
+ artist: spotifyTrack.artists.map(artist => artist.name).join(', '),
861
+ duration: Math.floor(spotifyTrack.duration_ms / 1000),
862
+ thumbnail: spotifyTrack.album.images[0]?.url,
863
+ url: spotifyTrack.external_urls.spotify,
864
+ source: 'spotify',
865
+ album: spotifyTrack.album.name,
866
+ popularity: spotifyTrack.popularity,
867
+ preview_url: spotifyTrack.preview_url,
868
+ metadata: {
869
+ spotifyId: spotifyTrack.id,
870
+ artists: spotifyTrack.artists,
871
+ album: spotifyTrack.album
872
+ }
873
+ };
874
+ }
875
+ }
876
+
877
+ /**
878
+ * Lavalink provider for connecting to Lavalink server
879
+ */
880
+ class LavalinkProvider {
881
+ constructor(options = {}) {
882
+ this.host = options.host || 'localhost';
883
+ this.port = options.port || 2333;
884
+ this.password = options.password || 'youshallnotpass';
885
+ this.secure = options.secure || false;
886
+ this.manager = null;
887
+ this.node = null;
888
+ this.isConnected = false;
889
+ }
890
+
891
+ /**
892
+ * Connect to Lavalink server
893
+ * @returns {Promise<void>}
894
+ */
895
+ async connect() {
896
+ try {
897
+ this.manager = new lavalinkClient.LavalinkManager({
898
+ nodes: [{
899
+ host: this.host,
900
+ port: this.port,
901
+ password: this.password,
902
+ secure: this.secure,
903
+ id: 'main'
904
+ }],
905
+ sendToShard: (guildId, payload) => {
906
+ // This would need to be implemented to send to Discord gateway
907
+ // For now, this is a placeholder
908
+ console.log('Send to shard:', guildId, payload);
909
+ }
910
+ });
911
+
912
+ await this.manager.connect();
913
+ this.node = this.manager.nodes.get('main');
914
+ this.isConnected = true;
915
+ } catch (error) {
916
+ throw new Error(`Failed to connect to Lavalink: ${error.message}`);
917
+ }
918
+ }
919
+
920
+ /**
921
+ * Disconnect from Lavalink server
922
+ */
923
+ disconnect() {
924
+ if (this.manager) {
925
+ this.manager.destroy();
926
+ this.isConnected = false;
927
+ }
928
+ }
929
+
930
+ /**
931
+ * Create a player for a guild/channel
932
+ * @param {string} guildId - Guild ID
933
+ * @param {string} channelId - Voice channel ID
934
+ * @returns {Object} Player instance
935
+ */
936
+ createPlayer(guildId, channelId) {
937
+ if (!this.isConnected) {
938
+ throw new Error('Not connected to Lavalink');
939
+ }
940
+
941
+ return this.manager.createPlayer({
942
+ guildId,
943
+ voiceChannelId: channelId,
944
+ textChannelId: channelId, // Optional
945
+ selfDeaf: false,
946
+ selfMute: false
947
+ });
948
+ }
949
+
950
+ /**
951
+ * Load track from Lavalink
952
+ * @param {string} identifier - Track identifier (URL or search query)
953
+ * @returns {Promise<Object>} Track info
954
+ */
955
+ async loadTrack(identifier) {
956
+ if (!this.isConnected) {
957
+ throw new Error('Not connected to Lavalink');
958
+ }
959
+
960
+ try {
961
+ const result = await this.node.rest.loadTracks(identifier);
962
+
963
+ if (result.loadType === 'TRACK_LOADED') {
964
+ return this._formatTrack(result.tracks[0]);
965
+ } else if (result.loadType === 'PLAYLIST_LOADED') {
966
+ return result.tracks.map(track => this._formatTrack(track));
967
+ } else if (result.loadType === 'SEARCH_RESULT') {
968
+ return result.tracks.map(track => this._formatTrack(track));
969
+ } else {
970
+ throw new Error('No tracks found');
971
+ }
972
+ } catch (error) {
973
+ throw new Error(`Failed to load track: ${error.message}`);
974
+ }
975
+ }
976
+
977
+ /**
978
+ * Format Lavalink track to internal format
979
+ * @param {Object} lavalinkTrack - Lavalink track object
980
+ * @returns {Object} Formatted track
981
+ * @private
982
+ */
983
+ _formatTrack(lavalinkTrack) {
984
+ const info = lavalinkTrack.info;
985
+ return {
986
+ id: lavalinkTrack.track,
987
+ title: info.title,
988
+ artist: info.author,
989
+ duration: Math.floor(info.length / 1000),
990
+ thumbnail: info.artworkUrl,
991
+ url: info.uri,
992
+ source: 'lavalink',
993
+ isrc: info.isrc,
994
+ metadata: {
995
+ lavalinkTrack: lavalinkTrack.track,
996
+ identifier: info.identifier,
997
+ sourceName: info.sourceName
998
+ }
999
+ };
1000
+ }
1001
+
1002
+ /**
1003
+ * Get node stats
1004
+ * @returns {Promise<Object>} Node stats
1005
+ */
1006
+ async getStats() {
1007
+ if (!this.isConnected) {
1008
+ throw new Error('Not connected to Lavalink');
1009
+ }
1010
+
1011
+ return await this.node.rest.getStats();
1012
+ }
1013
+ }
1014
+
743
1015
  /**
744
1016
  * Main audio engine class
745
1017
  */
@@ -751,6 +1023,8 @@ class AudioEngine {
751
1023
  this.filters = null;
752
1024
  this.queue = new Queue();
753
1025
  this.eventBus = new EventBus();
1026
+ this.spotifyProvider = null;
1027
+ this.lavalinkProvider = null;
754
1028
  this.isReady = false;
755
1029
 
756
1030
  this.initialize();
@@ -933,6 +1207,100 @@ class AudioEngine {
933
1207
  };
934
1208
  }
935
1209
 
1210
+ /**
1211
+ * Initialize Spotify provider
1212
+ * @param {Object} options - Spotify options
1213
+ */
1214
+ initSpotify(options = {}) {
1215
+ this.spotifyProvider = new SpotifyProvider(options);
1216
+ }
1217
+
1218
+ /**
1219
+ * Load Spotify track and add to queue
1220
+ * @param {string} trackId - Spotify track ID
1221
+ * @param {Object} options - Options including token
1222
+ * @returns {Promise<Track>} Added track
1223
+ */
1224
+ async loadSpotifyTrack(trackId, options = {}) {
1225
+ if (!this.spotifyProvider) {
1226
+ throw new Error('Spotify provider not initialized');
1227
+ }
1228
+
1229
+ if (options.token) {
1230
+ this.spotifyProvider.setAccessToken(options.token);
1231
+ }
1232
+
1233
+ const trackData = await this.spotifyProvider.getTrack(trackId);
1234
+ const track = new Track(trackData.url, trackData);
1235
+ this.add(track);
1236
+ return track;
1237
+ }
1238
+
1239
+ /**
1240
+ * Search Spotify tracks
1241
+ * @param {string} query - Search query
1242
+ * @param {Object} options - Search options
1243
+ * @returns {Promise<Array>} Search results
1244
+ */
1245
+ async searchSpotifyTracks(query, options = {}) {
1246
+ if (!this.spotifyProvider) {
1247
+ throw new Error('Spotify provider not initialized');
1248
+ }
1249
+
1250
+ if (options.token) {
1251
+ this.spotifyProvider.setAccessToken(options.token);
1252
+ }
1253
+
1254
+ return await this.spotifyProvider.searchTracks(query, options);
1255
+ }
1256
+
1257
+ /**
1258
+ * Connect to Lavalink server
1259
+ * @param {Object} options - Lavalink connection options
1260
+ * @returns {Promise<void>}
1261
+ */
1262
+ async connectLavalink(options = {}) {
1263
+ this.lavalinkProvider = new LavalinkProvider(options);
1264
+ await this.lavalinkProvider.connect();
1265
+ }
1266
+
1267
+ /**
1268
+ * Load Lavalink track and add to queue
1269
+ * @param {string} identifier - Track identifier
1270
+ * @returns {Promise<Track|Array<Track>>} Added track(s)
1271
+ */
1272
+ async loadLavalinkTrack(identifier) {
1273
+ if (!this.lavalinkProvider) {
1274
+ throw new Error('Lavalink provider not connected');
1275
+ }
1276
+
1277
+ const trackData = await this.lavalinkProvider.loadTrack(identifier);
1278
+
1279
+ if (Array.isArray(trackData)) {
1280
+ const tracks = trackData.map(data => new Track(data.url, data));
1281
+ this.add(tracks);
1282
+ return tracks;
1283
+ } else {
1284
+ const track = new Track(trackData.url, trackData);
1285
+ this.add(track);
1286
+ return track;
1287
+ }
1288
+ }
1289
+
1290
+ /**
1291
+ * Get Lavalink player for guild/channel
1292
+ * @param {string} guildId - Guild ID
1293
+ * @param {string} channelId - Voice channel ID
1294
+ * @returns {Object} Lavalink player
1295
+ */
1296
+ getLavalinkPlayer(guildId, channelId) {
1297
+ if (!this.lavalinkProvider) {
1298
+ throw new Error('Lavalink provider not connected');
1299
+ }
1300
+
1301
+ return this.lavalinkProvider.createPlayer(guildId, channelId);
1302
+ }
1303
+
936
1304
  /**
937
1305
  * Destroy the engine
938
1306
  */
@@ -942,6 +1310,9 @@ class AudioEngine {
942
1310
  }
943
1311
  this.filters.clear();
944
1312
  this.player.stop();
1313
+ if (this.lavalinkProvider) {
1314
+ this.lavalinkProvider.disconnect();
1315
+ }
945
1316
  }
946
1317
  }
947
1318
 
@@ -1479,7 +1850,8 @@ exports.AudioEngine = AudioEngine;
1479
1850
  exports.EVENTS = EVENTS;
1480
1851
  exports.EventBus = EventBus;
1481
1852
  exports.FILTER_TYPES = FILTER_TYPES;
1482
- exports.LOOP_MODES = LOOP_MODES$1;
1853
+ exports.LOOP_MODES = LOOP_MODES;
1854
+ exports.LavalinkProvider = LavalinkProvider;
1483
1855
  exports.LocalProvider = LocalProvider;
1484
1856
  exports.Logger = Logger;
1485
1857
  exports.MetadataUtils = MetadataUtils;
@@ -1488,6 +1860,7 @@ exports.ProbeUtils = ProbeUtils;
1488
1860
  exports.Queue = Queue;
1489
1861
  exports.REPEAT_MODES = REPEAT_MODES;
1490
1862
  exports.SoundCloudProvider = SoundCloudProvider;
1863
+ exports.SpotifyProvider = SpotifyProvider;
1491
1864
  exports.TimeUtils = TimeUtils;
1492
1865
  exports.Track = Track;
1493
1866
  exports.YouTubeProvider = YouTubeProvider;