discord-player 5.3.2-dev.3 → 5.4.0

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/README.md CHANGED
@@ -32,6 +32,7 @@ $ npm install --save @discordjs/opus
32
32
  - Simple & easy to use 🤘
33
33
  - Beginner friendly 😱
34
34
  - Audio filters 🎸
35
+ - Lavalink compatible 15 band equalizer 🎚️
35
36
  - Lightweight ☁️
36
37
  - Custom extractors support 🌌
37
38
  - Multiple sources support ✌
@@ -163,6 +164,7 @@ These bots are made by the community, they can help you build your own!
163
164
  * **[Discord Music Bot](https://github.com/Androz2091/discord-music-bot)** by [Androz2091](https://github.com/Androz2091)
164
165
  * [Dodong](https://github.com/nizeic/Dodong) by [nizeic](https://github.com/nizeic)
165
166
  * [Musico](https://github.com/Whirl21/Musico) by [Whirl21](https://github.com/Whirl21)
167
+ * [Melody](https://github.com/NerdyTechy/Melody) by [NerdyTechy](https://github.com/NerdyTechy)
166
168
  * [Eyesense-Music-Bot](https://github.com/naseif/Eyesense-Music-Bot) by [naseif](https://github.com/naseif)
167
169
  * [Music-bot](https://github.com/ZerioDev/Music-bot) by [ZerioDev](https://github.com/ZerioDev)
168
170
  * [AtlantaBot](https://github.com/Androz2091/AtlantaBot) by [Androz2091](https://github.com/Androz2091) (**outdated**)
package/dist/Player.js CHANGED
@@ -95,6 +95,7 @@ class Player extends tiny_typed_emitter_1.TypedEmitter {
95
95
  const queue = this.getQueue(oldState.guild.id);
96
96
  if (!queue || !queue.connection)
97
97
  return;
98
+ this.emit("voiceStateUpdate", queue, oldState, newState);
98
99
  if (oldState.channelId && !newState.channelId && newState.member.id === newState.guild.members.me.id) {
99
100
  try {
100
101
  queue.destroy();
@@ -105,11 +106,11 @@ class Player extends tiny_typed_emitter_1.TypedEmitter {
105
106
  return void this.emit("botDisconnect", queue);
106
107
  }
107
108
  if (!oldState.channelId && newState.channelId && newState.member.id === newState.guild.members.me.id) {
108
- if (!oldState.serverMute && newState.serverMute) {
109
+ if (oldState.serverMute !== newState.serverMute) {
109
110
  // state.serverMute can be null
110
111
  queue.setPaused(!!newState.serverMute);
111
112
  }
112
- else if (!oldState.suppress && newState.suppress) {
113
+ else if (oldState.suppress !== newState.suppress) {
113
114
  // state.suppress can be null
114
115
  queue.setPaused(!!newState.suppress);
115
116
  if (newState.suppress) {
@@ -117,20 +118,7 @@ class Player extends tiny_typed_emitter_1.TypedEmitter {
117
118
  }
118
119
  }
119
120
  }
120
- if (oldState.channelId === newState.channelId && newState.member.id === newState.guild.members.me.id) {
121
- if (!oldState.serverMute && newState.serverMute) {
122
- // state.serverMute can be null
123
- queue.setPaused(!!newState.serverMute);
124
- }
125
- else if (!oldState.suppress && newState.suppress) {
126
- // state.suppress can be null
127
- queue.setPaused(!!newState.suppress);
128
- if (newState.suppress) {
129
- newState.guild.members.me.voice.setRequestToSpeak(true).catch(Util_1.Util.noop);
130
- }
131
- }
132
- }
133
- if (queue.connection && !newState.channelId && oldState.channelId === queue.connection.channel.id) {
121
+ if (!newState.channelId && oldState.channelId === queue.connection.channel.id) {
134
122
  if (!Util_1.Util.isVoiceEmpty(queue.connection.channel))
135
123
  return;
136
124
  const timeout = setTimeout(() => {
@@ -144,7 +132,7 @@ class Player extends tiny_typed_emitter_1.TypedEmitter {
144
132
  }, queue.options.leaveOnEmptyCooldown || 0).unref();
145
133
  queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
146
134
  }
147
- if (queue.connection && newState.channelId && newState.channelId === queue.connection.channel.id) {
135
+ if (newState.channelId && newState.channelId === queue.connection.channel.id) {
148
136
  const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
149
137
  const channelEmpty = Util_1.Util.isVoiceEmpty(queue.connection.channel);
150
138
  if (!channelEmpty && emptyTimeout) {
@@ -152,26 +140,54 @@ class Player extends tiny_typed_emitter_1.TypedEmitter {
152
140
  queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
153
141
  }
154
142
  }
155
- if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId && newState.member.id === newState.guild.members.me.id) {
156
- if (queue.connection && newState.member.id === newState.guild.members.me.id)
157
- queue.connection.channel = newState.channel;
158
- const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
159
- const channelEmpty = Util_1.Util.isVoiceEmpty(queue.connection.channel);
160
- if (!channelEmpty && emptyTimeout) {
161
- clearTimeout(emptyTimeout);
162
- queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
143
+ if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) {
144
+ if (newState.member.id === newState.guild.members.me.id) {
145
+ if (queue.connection && newState.member.id === newState.guild.members.me.id)
146
+ queue.connection.channel = newState.channel;
147
+ const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
148
+ const channelEmpty = Util_1.Util.isVoiceEmpty(queue.connection.channel);
149
+ if (!channelEmpty && emptyTimeout) {
150
+ clearTimeout(emptyTimeout);
151
+ queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
152
+ }
153
+ else {
154
+ const timeout = setTimeout(() => {
155
+ if (queue.connection && !Util_1.Util.isVoiceEmpty(queue.connection.channel))
156
+ return;
157
+ if (!this.queues.has(queue.guild.id))
158
+ return;
159
+ if (queue.options.leaveOnEmpty)
160
+ queue.destroy(true);
161
+ this.emit("channelEmpty", queue);
162
+ }, queue.options.leaveOnEmptyCooldown || 0).unref();
163
+ queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
164
+ }
163
165
  }
164
166
  else {
165
- const timeout = setTimeout(() => {
166
- if (queue.connection && !Util_1.Util.isVoiceEmpty(queue.connection.channel))
167
+ if (newState.channelId !== queue.connection.channel.id) {
168
+ if (!Util_1.Util.isVoiceEmpty(queue.connection.channel))
167
169
  return;
168
- if (!this.queues.has(queue.guild.id))
170
+ if (queue._cooldownsTimeout.has(`empty_${oldState.guild.id}`))
169
171
  return;
170
- if (queue.options.leaveOnEmpty)
171
- queue.destroy(true);
172
- this.emit("channelEmpty", queue);
173
- }, queue.options.leaveOnEmptyCooldown || 0).unref();
174
- queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
172
+ const timeout = setTimeout(() => {
173
+ if (!Util_1.Util.isVoiceEmpty(queue.connection.channel))
174
+ return;
175
+ if (!this.queues.has(queue.guild.id))
176
+ return;
177
+ if (queue.options.leaveOnEmpty)
178
+ queue.destroy(true);
179
+ this.emit("channelEmpty", queue);
180
+ }, queue.options.leaveOnEmptyCooldown || 0).unref();
181
+ queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
182
+ }
183
+ else {
184
+ const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
185
+ const channelEmpty = Util_1.Util.isVoiceEmpty(queue.connection.channel);
186
+ if (!channelEmpty && emptyTimeout) {
187
+ clearTimeout(emptyTimeout);
188
+ queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
189
+ }
190
+ }
175
191
  }
176
192
  }
177
193
  }
@@ -429,14 +445,14 @@ class Player extends tiny_typed_emitter_1.TypedEmitter {
429
445
  }
430
446
  else {
431
447
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
432
- playlist.tracks = spotifyPlaylist.tracks.items.map((m) => {
448
+ playlist.tracks = spotifyPlaylist.trackList.map((m) => {
433
449
  const data = new Track_1.default(this, {
434
- title: m.track.name ?? "",
435
- description: m.track.description ?? "",
436
- author: m.track.artists?.[0]?.name ?? "Unknown Artist",
437
- url: m.track.external_urls?.spotify ?? query,
438
- thumbnail: m.track.album?.images?.[0]?.url ?? "https://www.scdn.co/i/_global/twitter_card-default.jpg",
439
- duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(m.track.duration_ms)),
450
+ title: m.title ?? "",
451
+ description: m.description ?? "",
452
+ author: m.subtitle ?? "Unknown Artist",
453
+ url: m.external_urls?.spotify ?? query,
454
+ thumbnail: m.album?.images?.[0]?.url ?? "https://www.scdn.co/i/_global/twitter_card-default.jpg",
455
+ duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(m.duration)),
440
456
  views: 0,
441
457
  requestedBy: options.requestedBy,
442
458
  playlist,
@@ -34,6 +34,7 @@ class Queue {
34
34
  this._cooldownsTimeout = new discord_js_1.Collection();
35
35
  this._activeFilters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
36
36
  this._filtersUpdate = false;
37
+ this._lastEQBands = [];
37
38
  _Queue_destroyed.set(this, false);
38
39
  this.onBeforeCreateStream = null;
39
40
  /**
@@ -96,12 +97,98 @@ class Queue {
96
97
  initialVolume: 100,
97
98
  bufferingTimeout: 3000,
98
99
  spotifyBridge: true,
99
- disableVolume: false
100
+ disableVolume: false,
101
+ disableEqualizer: false,
102
+ equalizerBands: []
100
103
  }, options);
104
+ if (Array.isArray(options.equalizerBands))
105
+ this._lastEQBands = options.equalizerBands;
101
106
  if ("onBeforeCreateStream" in this.options)
102
107
  this.onBeforeCreateStream = this.options.onBeforeCreateStream;
103
108
  this.player.emit("debug", this, `Queue initialized:\n\n${this.player.scanDeps()}`);
104
109
  }
110
+ /**
111
+ * Set equalizer bands
112
+ * @param bands Equalizer band multiplier array
113
+ */
114
+ setEqualizer(bands) {
115
+ if (!this.connection.equalizer)
116
+ return false;
117
+ if (!Array.isArray(bands) || !bands.length) {
118
+ this.connection.equalizer.resetEQ();
119
+ this._lastEQBands = this.getEqualizer();
120
+ }
121
+ else {
122
+ this.connection.equalizer.setEQ(bands);
123
+ this._lastEQBands = this.getEqualizer();
124
+ }
125
+ return true;
126
+ }
127
+ /**
128
+ * Set particular equalizer band multiplier
129
+ * @param band The band to update
130
+ * @param gain The gain
131
+ */
132
+ setEqualizerBand(band, gain) {
133
+ if (!this.connection.equalizer)
134
+ return null;
135
+ this.connection.equalizer.equalizer.setGain(band, gain);
136
+ this._lastEQBands = this.getEqualizer();
137
+ return true;
138
+ }
139
+ /**
140
+ * Returns gain value of specific equalizer band
141
+ * @param band The band to get value of
142
+ */
143
+ getEqualizerBand(band) {
144
+ if (!this.connection.equalizer)
145
+ return null;
146
+ return this.connection.equalizer.equalizer.getGain(band);
147
+ }
148
+ /**
149
+ * Returns entire equalizer bands
150
+ */
151
+ getEqualizer() {
152
+ if (!this.connection.equalizer)
153
+ return [];
154
+ return this.connection.equalizer.getEQ();
155
+ }
156
+ /**
157
+ * Check if equalizer is enabled
158
+ */
159
+ isEqualizerEnabled() {
160
+ return !this.connection.equalizer?.disabled;
161
+ }
162
+ /**
163
+ * Toggles equalizer on/off
164
+ */
165
+ toggleEqualizer() {
166
+ const eq = this.connection.equalizer;
167
+ if (!eq)
168
+ return false;
169
+ eq.toggle();
170
+ return !eq.disabled;
171
+ }
172
+ /**
173
+ * Enables equalizer
174
+ */
175
+ enableEqualizer() {
176
+ const eq = this.connection.equalizer;
177
+ if (!eq)
178
+ return false;
179
+ eq.enable();
180
+ return !eq.disabled;
181
+ }
182
+ /**
183
+ * Disables equalizer
184
+ */
185
+ disableEqualizer() {
186
+ const eq = this.connection.equalizer;
187
+ if (!eq)
188
+ return false;
189
+ eq.disable();
190
+ return eq.disabled;
191
+ }
105
192
  /**
106
193
  * Forces next play
107
194
  * @returns {Promise<void>}
@@ -185,8 +272,6 @@ class Queue {
185
272
  if (this._filtersUpdate)
186
273
  return;
187
274
  this._streamTime = 0;
188
- if (resource?.metadata)
189
- this.previousTracks.push(resource.metadata);
190
275
  this.player.emit("trackEnd", this, resource.metadata);
191
276
  if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.OFF) {
192
277
  this.emitEnd();
@@ -730,7 +815,9 @@ class Queue {
730
815
  const resource = this.connection.createStream(ffmpegStream, {
731
816
  type: voice_1.StreamType.Raw,
732
817
  data: track,
733
- disableVolume: Boolean(this.options.disableVolume)
818
+ disableVolume: Boolean(this.options.disableVolume),
819
+ disableEqualizer: Boolean(this.options.disableEqualizer),
820
+ eq: this._lastEQBands
734
821
  });
735
822
  if (options.seek)
736
823
  this._streamTime = options.seek;
@@ -5,6 +5,7 @@ const voice_1 = require("@discordjs/voice");
5
5
  const tiny_typed_emitter_1 = require("tiny-typed-emitter");
6
6
  const Util_1 = require("../utils/Util");
7
7
  const PlayerError_1 = require("../Structures/PlayerError");
8
+ const equalizer_1 = require("@discord-player/equalizer");
8
9
  class StreamDispatcher extends tiny_typed_emitter_1.TypedEmitter {
9
10
  /**
10
11
  * Creates new connection object
@@ -16,6 +17,7 @@ class StreamDispatcher extends tiny_typed_emitter_1.TypedEmitter {
16
17
  super();
17
18
  this.connectionTimeout = connectionTimeout;
18
19
  this.readyLock = false;
20
+ this.equalizer = null;
19
21
  /**
20
22
  * The voice connection
21
23
  * @type {VoiceConnection}
@@ -95,6 +97,10 @@ class StreamDispatcher extends tiny_typed_emitter_1.TypedEmitter {
95
97
  else if (newState.status === voice_1.AudioPlayerStatus.Idle && oldState.status !== voice_1.AudioPlayerStatus.Idle) {
96
98
  if (!this.paused) {
97
99
  void this.emit("finish", this.audioResource);
100
+ if (this.equalizer) {
101
+ this.equalizer.destroy();
102
+ this.equalizer = null;
103
+ }
98
104
  this.audioResource = null;
99
105
  }
100
106
  }
@@ -111,11 +117,17 @@ class StreamDispatcher extends tiny_typed_emitter_1.TypedEmitter {
111
117
  */
112
118
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
119
  createStream(src, ops) {
114
- this.audioResource = (0, voice_1.createAudioResource)(src, {
120
+ if (!ops.disableEqualizer)
121
+ this.equalizer = new equalizer_1.EqualizerStream({
122
+ channels: 1,
123
+ disabled: false,
124
+ bandMultiplier: ops.eq || []
125
+ });
126
+ const stream = this.equalizer && typeof src !== "string" ? src.pipe(this.equalizer) : src;
127
+ this.audioResource = (0, voice_1.createAudioResource)(stream, {
115
128
  inputType: ops?.type ?? voice_1.StreamType.Arbitrary,
116
129
  metadata: ops?.data,
117
- // eslint-disable-next-line no-extra-boolean-cast
118
- inlineVolume: !Boolean(ops?.disableVolume)
130
+ inlineVolume: !ops?.disableVolume
119
131
  });
120
132
  return this.audioResource;
121
133
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  /// <reference types="node" />
2
- import { User, VoiceChannel, StageChannel, Collection, Snowflake, Client, GuildResolvable, Guild, GuildChannelResolvable, UserResolvable } from 'discord.js';
2
+ import { User, VoiceChannel, StageChannel, Collection, Snowflake, Client, GuildResolvable, Guild, GuildChannelResolvable, VoiceState, UserResolvable } from 'discord.js';
3
3
  import { Readable, Duplex, TransformOptions, Transform } from 'stream';
4
4
  import { TypedEmitter } from 'tiny-typed-emitter';
5
5
  import { AudioPlayerError, AudioResource, VoiceConnection, AudioPlayer, StreamType, AudioPlayerStatus } from '@discordjs/voice';
6
+ import { EqualizerStream, EqualizerBand } from '@discord-player/equalizer';
6
7
  import { downloadOptions } from 'ytdl-core';
7
8
 
8
9
  declare class Playlist {
@@ -96,6 +97,7 @@ declare class StreamDispatcher extends TypedEmitter<VoiceEvents> {
96
97
  audioResource?: AudioResource<Track>;
97
98
  private readyLock;
98
99
  paused: boolean;
100
+ equalizer: EqualizerStream | null;
99
101
  /**
100
102
  * Creates new connection object
101
103
  * @param {VoiceConnection} connection The connection
@@ -113,6 +115,8 @@ declare class StreamDispatcher extends TypedEmitter<VoiceEvents> {
113
115
  type?: StreamType;
114
116
  data?: any;
115
117
  disableVolume?: boolean;
118
+ disableEqualizer?: boolean;
119
+ eq?: EqualizerBand[];
116
120
  }): AudioResource<Track>;
117
121
  /**
118
122
  * The player status
@@ -367,6 +371,7 @@ declare class Queue<T = unknown> {
367
371
  _cooldownsTimeout: Collection<string, NodeJS.Timeout>;
368
372
  private _activeFilters;
369
373
  private _filtersUpdate;
374
+ private _lastEQBands;
370
375
  onBeforeCreateStream: (track: Track, source: TrackSource, queue: Queue) => Promise<Readable | undefined>;
371
376
  /**
372
377
  * Queue constructor
@@ -375,6 +380,42 @@ declare class Queue<T = unknown> {
375
380
  * @param {PlayerOptions} [options] Player options for the queue
376
381
  */
377
382
  constructor(player: Player, guild: Guild, options?: PlayerOptions);
383
+ /**
384
+ * Set equalizer bands
385
+ * @param bands Equalizer band multiplier array
386
+ */
387
+ setEqualizer(bands?: EqualizerBand[]): boolean;
388
+ /**
389
+ * Set particular equalizer band multiplier
390
+ * @param band The band to update
391
+ * @param gain The gain
392
+ */
393
+ setEqualizerBand(band: number, gain: number): boolean;
394
+ /**
395
+ * Returns gain value of specific equalizer band
396
+ * @param band The band to get value of
397
+ */
398
+ getEqualizerBand(band: number): number;
399
+ /**
400
+ * Returns entire equalizer bands
401
+ */
402
+ getEqualizer(): EqualizerBand[];
403
+ /**
404
+ * Check if equalizer is enabled
405
+ */
406
+ isEqualizerEnabled(): boolean;
407
+ /**
408
+ * Toggles equalizer on/off
409
+ */
410
+ toggleEqualizer(): boolean;
411
+ /**
412
+ * Enables equalizer
413
+ */
414
+ enableEqualizer(): boolean;
415
+ /**
416
+ * Disables equalizer
417
+ */
418
+ disableEqualizer(): boolean;
378
419
  /**
379
420
  * Forces next play
380
421
  * @returns {Promise<void>}
@@ -744,8 +785,10 @@ interface PlayerProgressbarOptions {
744
785
  * @property {number} [bufferingTimeout=3000] Buffering timeout for the stream
745
786
  * @property {boolean} [spotifyBridge=true] If player should bridge spotify source to youtube
746
787
  * @property {boolean} [disableVolume=false] If player should disable inline volume
788
+ * @property {boolean} [disableEqualizer=false] If player should disable equalizer
747
789
  * @property {number} [volumeSmoothness=0] The volume transition smoothness between volume changes (lower the value to get better result)
748
790
  * Setting this or leaving this empty will disable this effect. Example: `volumeSmoothness: 0.1`
791
+ * @property {EqualizerBand[]} [equalizerBands] The equalizer bands array for 15 band equalizer.
749
792
  * @property {Function} [onBeforeCreateStream] Runs before creating stream
750
793
  */
751
794
  interface PlayerOptions {
@@ -760,7 +803,9 @@ interface PlayerOptions {
760
803
  bufferingTimeout?: number;
761
804
  spotifyBridge?: boolean;
762
805
  disableVolume?: boolean;
806
+ disableEqualizer?: boolean;
763
807
  volumeSmoothness?: number;
808
+ equalizerBands?: EqualizerBand[];
764
809
  onBeforeCreateStream?: (track: Track, source: TrackSource, queue: Queue) => Promise<Readable>;
765
810
  }
766
811
  /**
@@ -923,6 +968,13 @@ declare enum QueryType {
923
968
  * @param {Queue} queue The queue
924
969
  * @param {Track} track The track
925
970
  */
971
+ /**
972
+ * Emitted when a track ends
973
+ * @event Player#voiceStateUpdate
974
+ * @param {Queue} queue The queue that this update belongs to
975
+ * @param {VoiceState} oldState The old voice state
976
+ * @param {VoiceState} newState The new voice state
977
+ */
926
978
  interface PlayerEvents {
927
979
  botDisconnect: (queue: Queue) => any;
928
980
  channelEmpty: (queue: Queue) => any;
@@ -935,6 +987,7 @@ interface PlayerEvents {
935
987
  tracksAdd: (queue: Queue, track: Track[]) => any;
936
988
  trackStart: (queue: Queue, track: Track) => any;
937
989
  trackEnd: (queue: Queue, track: Track) => any;
990
+ voiceStateUpdate: (queue: Queue, oldState: VoiceState, newState: VoiceState) => any;
938
991
  }
939
992
  /**
940
993
  * @typedef {object} PlayOptions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discord-player",
3
- "version": "5.3.2-dev.3",
3
+ "version": "5.4.0",
4
4
  "description": "Complete framework to facilitate music commands using discord.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -65,6 +65,7 @@
65
65
  },
66
66
  "homepage": "https://discord-player.js.org",
67
67
  "dependencies": {
68
+ "@discord-player/equalizer": "^0.1.2",
68
69
  "@discordjs/voice": "^0.11.0",
69
70
  "libsodium-wrappers": "^0.7.10",
70
71
  "tiny-typed-emitter": "^2.1.0",