kazagumo-bun 3.4.0-b
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/.gitattributes +2 -0
- package/.prettierrc +5 -0
- package/LICENSE +21 -0
- package/LICENSE.md +22 -0
- package/README.md +267 -0
- package/SECURITY.md +21 -0
- package/appveyor.yml +24 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/hierarchy.js +1 -0
- package/docs/assets/highlight.css +99 -0
- package/docs/assets/icons.js +18 -0
- package/docs/assets/icons.svg +1 -0
- package/docs/assets/main.js +60 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1633 -0
- package/docs/classes/Kazagumo.Kazagumo.html +307 -0
- package/docs/classes/Managers_KazagumoPlayer.KazagumoPlayer.html +81 -0
- package/docs/classes/Managers_Supports_KazagumoQueue.KazagumoQueue.html +222 -0
- package/docs/classes/Managers_Supports_KazagumoTrack.KazagumoTrack.html +43 -0
- package/docs/classes/Modules_Interfaces.KazagumoError.html +33 -0
- package/docs/classes/Modules_Interfaces.KazagumoPlugin.html +4 -0
- package/docs/classes/Modules_Utils.KazagumoUtils.html +3 -0
- package/docs/classes/Plugins_PlayerMoved.KazagumoPlugin.html +13 -0
- package/docs/enums/Modules_Interfaces.PlayerState.html +7 -0
- package/docs/functions/Modules_Interfaces.escapeRegExp.html +1 -0
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +208 -0
- package/docs/interfaces/Kazagumo.KazagumoEvents.html +40 -0
- package/docs/interfaces/Modules_Interfaces.CreatePlayerOptions.html +21 -0
- package/docs/interfaces/Modules_Interfaces.KazagumoOptions.html +17 -0
- package/docs/interfaces/Modules_Interfaces.KazagumoPlayerOptions.html +9 -0
- package/docs/interfaces/Modules_Interfaces.KazagumoSearchOptions.html +5 -0
- package/docs/interfaces/Modules_Interfaces.KazagumoSearchResult.html +4 -0
- package/docs/interfaces/Modules_Interfaces.Payload.html +4 -0
- package/docs/interfaces/Modules_Interfaces.PlayOptions.html +6 -0
- package/docs/interfaces/Modules_Interfaces.PlayerMovedChannels.html +3 -0
- package/docs/interfaces/Modules_Interfaces.RawTrack.html +4 -0
- package/docs/interfaces/Modules_Interfaces.ResolveOptions.html +4 -0
- package/docs/modules/Index.html +1 -0
- package/docs/modules/Kazagumo.html +1 -0
- package/docs/modules/Managers_KazagumoPlayer.html +1 -0
- package/docs/modules/Managers_Supports_KazagumoQueue.html +1 -0
- package/docs/modules/Managers_Supports_KazagumoTrack.html +1 -0
- package/docs/modules/Modules_Interfaces.html +1 -0
- package/docs/modules/Modules_Plugins.html +1 -0
- package/docs/modules/Modules_Utils.html +1 -0
- package/docs/modules/Plugins_PlayerMoved.html +1 -0
- package/docs/modules.html +1 -0
- package/docs/types/Modules_Interfaces.PlayerMovedState.html +1 -0
- package/docs/types/Modules_Interfaces.SearchEngines.html +1 -0
- package/docs/types/Modules_Interfaces.SearchResultTypes.html +1 -0
- package/docs/types/Modules_Interfaces.YoutubeThumbnail.html +1 -0
- package/docs/types/Modules_Utils.Constructor.html +1 -0
- package/docs/variables/Index.version.html +1 -0
- package/docs/variables/Modules_Interfaces.Events.html +1 -0
- package/docs/variables/Modules_Interfaces.SourceIDs.html +1 -0
- package/docs/variables/Modules_Interfaces.SupportedSources.html +1 -0
- package/docs/variables/Modules_Plugins.default.html +1 -0
- package/eslint.config.mjs +15 -0
- package/package.json +26 -0
- package/src/Index.ts +14 -0
- package/src/Kazagumo.ts +396 -0
- package/src/Managers/KazagumoPlayer.ts +470 -0
- package/src/Managers/Supports/KazagumoQueue.ts +89 -0
- package/src/Managers/Supports/KazagumoTrack.ts +211 -0
- package/src/Modules/EventEmitter.ts +53 -0
- package/src/Modules/Interfaces.ts +204 -0
- package/src/Modules/Plugins.ts +5 -0
- package/src/Modules/Utils.ts +26 -0
- package/src/Plugins/PlayerMoved.ts +54 -0
- package/tsconfig.json +23 -0
- package/tslint.json +3 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { Kazagumo } from '../Kazagumo';
|
|
2
|
+
import { KazagumoQueue } from './Supports/KazagumoQueue';
|
|
3
|
+
import {
|
|
4
|
+
FilterOptions,
|
|
5
|
+
Node,
|
|
6
|
+
Player,
|
|
7
|
+
PlayerUpdate,
|
|
8
|
+
TrackExceptionEvent,
|
|
9
|
+
TrackStuckEvent,
|
|
10
|
+
WebSocketClosedEvent,
|
|
11
|
+
} from 'shoukaku';
|
|
12
|
+
import {
|
|
13
|
+
Events,
|
|
14
|
+
KazagumoError,
|
|
15
|
+
KazagumoPlayerOptions,
|
|
16
|
+
KazagumoSearchOptions,
|
|
17
|
+
KazagumoSearchResult,
|
|
18
|
+
PlayerState,
|
|
19
|
+
PlayOptions,
|
|
20
|
+
} from '../Modules/Interfaces';
|
|
21
|
+
import { KazagumoTrack } from './Supports/KazagumoTrack';
|
|
22
|
+
|
|
23
|
+
export class KazagumoPlayer {
|
|
24
|
+
/**
|
|
25
|
+
* Kazagumo options
|
|
26
|
+
*/
|
|
27
|
+
private options: KazagumoPlayerOptions;
|
|
28
|
+
/**
|
|
29
|
+
* Kazagumo Instance
|
|
30
|
+
*/
|
|
31
|
+
public readonly kazagumo: Kazagumo;
|
|
32
|
+
/**
|
|
33
|
+
* Shoukaku's Player instance
|
|
34
|
+
*/
|
|
35
|
+
public shoukaku: Player;
|
|
36
|
+
/**
|
|
37
|
+
* The guild ID of the player
|
|
38
|
+
*/
|
|
39
|
+
public readonly guildId: string;
|
|
40
|
+
/**
|
|
41
|
+
* The voice channel ID of the player
|
|
42
|
+
*/
|
|
43
|
+
public voiceId: string | null;
|
|
44
|
+
/**
|
|
45
|
+
* The text channel ID of the player
|
|
46
|
+
*/
|
|
47
|
+
public textId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Player's queue
|
|
50
|
+
*/
|
|
51
|
+
public readonly queue: KazagumoQueue;
|
|
52
|
+
/**
|
|
53
|
+
* Get the current state of the player
|
|
54
|
+
*/
|
|
55
|
+
public state: PlayerState = PlayerState.CONNECTING;
|
|
56
|
+
/**
|
|
57
|
+
* Paused state of the player
|
|
58
|
+
*/
|
|
59
|
+
public paused: boolean = false;
|
|
60
|
+
/**
|
|
61
|
+
* Whether the player is playing or not
|
|
62
|
+
*/
|
|
63
|
+
public playing: boolean = false;
|
|
64
|
+
/**
|
|
65
|
+
* Loop status
|
|
66
|
+
*/
|
|
67
|
+
public loop: 'none' | 'queue' | 'track' = 'none';
|
|
68
|
+
/**
|
|
69
|
+
* Search track/s
|
|
70
|
+
*/
|
|
71
|
+
public search: (query: string, options?: KazagumoSearchOptions) => Promise<KazagumoSearchResult>;
|
|
72
|
+
/**
|
|
73
|
+
* Player's volume in percentage (default 100%)
|
|
74
|
+
*/
|
|
75
|
+
public volume: number = 100;
|
|
76
|
+
/**
|
|
77
|
+
* Player's custom data
|
|
78
|
+
*/
|
|
79
|
+
public readonly data: Map<string, any> = new Map();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initialize the player
|
|
83
|
+
* @param kazagumo Kazagumo instance
|
|
84
|
+
* @param player Shoukaku's Player instance
|
|
85
|
+
* @param options Kazagumo options
|
|
86
|
+
* @param customData private readonly customData
|
|
87
|
+
*/
|
|
88
|
+
constructor(
|
|
89
|
+
kazagumo: Kazagumo,
|
|
90
|
+
player: Player,
|
|
91
|
+
options: KazagumoPlayerOptions,
|
|
92
|
+
private readonly customData: unknown,
|
|
93
|
+
) {
|
|
94
|
+
this.options = options;
|
|
95
|
+
this.kazagumo = kazagumo;
|
|
96
|
+
this.shoukaku = player;
|
|
97
|
+
this.guildId = options.guildId;
|
|
98
|
+
this.voiceId = options.voiceId;
|
|
99
|
+
this.textId = options.textId;
|
|
100
|
+
this.queue = new (this.options.extends?.queue ?? KazagumoQueue)(this);
|
|
101
|
+
|
|
102
|
+
if (options.volume !== 100) this.setVolume(options.volume);
|
|
103
|
+
|
|
104
|
+
this.search = (typeof this.options.searchWithSameNode === 'boolean' ? this.options.searchWithSameNode : true)
|
|
105
|
+
? (query: string, opt?: KazagumoSearchOptions) =>
|
|
106
|
+
kazagumo.search.bind(kazagumo)(query, opt ? { ...opt, nodeName: this.shoukaku.node.name } : undefined)
|
|
107
|
+
: kazagumo.search.bind(kazagumo);
|
|
108
|
+
|
|
109
|
+
this.shoukaku.on('start', () => {
|
|
110
|
+
this.playing = true;
|
|
111
|
+
this.emit(Events.PlayerStart, this, this.queue.current);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
this.shoukaku.on('end', (data) => {
|
|
115
|
+
// This event emits STOPPED reason when destroying, so return to prevent double emit
|
|
116
|
+
if (this.state === PlayerState.DESTROYING || this.state === PlayerState.DESTROYED)
|
|
117
|
+
return this.emit(Events.Debug, `Player ${this.guildId} destroyed from end event`);
|
|
118
|
+
|
|
119
|
+
if (data.reason === 'replaced') return this.emit(Events.PlayerEnd, this);
|
|
120
|
+
if (['loadFailed', 'cleanup'].includes(data.reason)) {
|
|
121
|
+
if (
|
|
122
|
+
this.queue.current &&
|
|
123
|
+
!this.queue.previous.find(
|
|
124
|
+
(x) => x.identifier === this.queue.current?.identifier && x.title === this.queue.current?.title,
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
this.queue.previous = [this.queue.current].concat(this.queue.previous);
|
|
128
|
+
this.emit(Events.PlayerEnd, this, this.queue.current);
|
|
129
|
+
this.queue.current = null;
|
|
130
|
+
this.playing = false;
|
|
131
|
+
if (!this.queue.length) return this.emit(Events.PlayerEmpty, this);
|
|
132
|
+
return this.play();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this.loop === 'track' && this.queue.current) this.queue.unshift(this.queue.current);
|
|
136
|
+
if (this.loop === 'queue' && this.queue.current) this.queue.push(this.queue.current);
|
|
137
|
+
|
|
138
|
+
if (
|
|
139
|
+
this.queue.current &&
|
|
140
|
+
!this.queue.previous.find(
|
|
141
|
+
(x) => x.identifier === this.queue.current?.identifier && x.title === this.queue.current?.title,
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
this.queue.previous = [this.queue.current].concat(this.queue.previous);
|
|
145
|
+
|
|
146
|
+
const currentSong = this.queue.current;
|
|
147
|
+
this.emit(Events.PlayerEnd, this, currentSong);
|
|
148
|
+
this.queue.current = null;
|
|
149
|
+
|
|
150
|
+
if (!this.queue.length) {
|
|
151
|
+
this.playing = false;
|
|
152
|
+
return this.emit(Events.PlayerEmpty, this);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return this.play();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
this.shoukaku.on('closed', (data: WebSocketClosedEvent) => {
|
|
159
|
+
this.playing = false;
|
|
160
|
+
this.emit(Events.PlayerClosed, this, data);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
this.shoukaku.on('exception', (data: TrackExceptionEvent) => {
|
|
164
|
+
this.playing = false;
|
|
165
|
+
this.emit(Events.PlayerException, this, data);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
this.shoukaku.on('update', (data: PlayerUpdate) => this.emit(Events.PlayerUpdate, this, data));
|
|
169
|
+
this.shoukaku.on('stuck', (data: TrackStuckEvent) => this.emit(Events.PlayerStuck, this, data));
|
|
170
|
+
this.shoukaku.on('resumed', () => this.emit(Events.PlayerResumed, this));
|
|
171
|
+
// @ts-ignore
|
|
172
|
+
this.shoukaku.on(Events.QueueUpdate, (referencePlayer: KazagumoPlayer, queue: KazagumoQueue) =>
|
|
173
|
+
this.kazagumo.emit(Events.QueueUpdate, referencePlayer, queue),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// /**
|
|
178
|
+
// * Get volume
|
|
179
|
+
// */
|
|
180
|
+
// public get volume(): number {
|
|
181
|
+
// return this.shoukaku.filters.volume || 1;
|
|
182
|
+
// }
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get player position
|
|
186
|
+
*/
|
|
187
|
+
public get position(): number {
|
|
188
|
+
return this.shoukaku.position;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get filters
|
|
193
|
+
*/
|
|
194
|
+
public get filters(): FilterOptions {
|
|
195
|
+
return this.shoukaku.filters;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private get node(): Node {
|
|
199
|
+
return this.shoukaku.node;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Pause the player
|
|
204
|
+
* @param pause Whether to pause or not
|
|
205
|
+
* @returns KazagumoPlayer
|
|
206
|
+
*/
|
|
207
|
+
public pause(pause: boolean): KazagumoPlayer {
|
|
208
|
+
if (typeof pause !== 'boolean') throw new KazagumoError(1, 'pause must be a boolean');
|
|
209
|
+
|
|
210
|
+
if (this.paused === pause || !this.queue.totalSize) return this;
|
|
211
|
+
this.paused = pause;
|
|
212
|
+
this.playing = !pause;
|
|
213
|
+
this.shoukaku.setPaused(pause);
|
|
214
|
+
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Set text channel
|
|
220
|
+
* @param textId Text channel ID
|
|
221
|
+
* @returns KazagumoPlayer
|
|
222
|
+
*/
|
|
223
|
+
public setTextChannel(textId: string): KazagumoPlayer {
|
|
224
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
225
|
+
|
|
226
|
+
this.textId = textId;
|
|
227
|
+
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Set voice channel and move the player to the voice channel
|
|
233
|
+
* @param voiceId Voice channel ID
|
|
234
|
+
* @returns KazagumoPlayer
|
|
235
|
+
*/
|
|
236
|
+
public setVoiceChannel(voiceId: string): KazagumoPlayer {
|
|
237
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
238
|
+
this.state = PlayerState.CONNECTING;
|
|
239
|
+
|
|
240
|
+
this.voiceId = voiceId;
|
|
241
|
+
this.kazagumo.KazagumoOptions.send(this.guildId, {
|
|
242
|
+
op: 4,
|
|
243
|
+
d: {
|
|
244
|
+
guild_id: this.guildId,
|
|
245
|
+
channel_id: this.voiceId,
|
|
246
|
+
self_mute: false,
|
|
247
|
+
self_deaf: this.options.deaf,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
this.emit(Events.Debug, `Player ${this.guildId} moved to voice channel ${voiceId}`);
|
|
252
|
+
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get the previous track from the queue
|
|
258
|
+
* @param remove Whether to remove the track from the previous list or not. Best to set to true if you want to play it
|
|
259
|
+
*/
|
|
260
|
+
public getPrevious(remove: boolean = false): KazagumoTrack | undefined {
|
|
261
|
+
if (remove) return this.queue.previous.shift();
|
|
262
|
+
return this.queue.previous[0];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Set loop mode
|
|
267
|
+
* @param [loop] Loop mode
|
|
268
|
+
* @returns KazagumoPlayer
|
|
269
|
+
*/
|
|
270
|
+
public setLoop(loop?: 'none' | 'queue' | 'track'): KazagumoPlayer {
|
|
271
|
+
if (loop === undefined) {
|
|
272
|
+
if (this.loop === 'none') this.loop = 'queue';
|
|
273
|
+
else if (this.loop === 'queue') this.loop = 'track';
|
|
274
|
+
else if (this.loop === 'track') this.loop = 'none';
|
|
275
|
+
return this;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (loop === 'none' || loop === 'queue' || loop === 'track') {
|
|
279
|
+
this.loop = loop;
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
throw new KazagumoError(1, "loop must be one of 'none', 'queue', 'track'");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Play a track
|
|
288
|
+
* @param track Track to play
|
|
289
|
+
* @param options Play options
|
|
290
|
+
* @returns KazagumoPlayer
|
|
291
|
+
*/
|
|
292
|
+
public async play(track?: KazagumoTrack, options?: PlayOptions): Promise<KazagumoPlayer> {
|
|
293
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
294
|
+
|
|
295
|
+
if (track && !(track instanceof KazagumoTrack)) throw new KazagumoError(1, 'track must be a KazagumoTrack');
|
|
296
|
+
|
|
297
|
+
if (!track && !this.queue.totalSize) throw new KazagumoError(1, 'No track is available to play');
|
|
298
|
+
|
|
299
|
+
if (!options || typeof options.replaceCurrent !== 'boolean') options = { ...options, replaceCurrent: false };
|
|
300
|
+
|
|
301
|
+
if (track) {
|
|
302
|
+
if (!options.replaceCurrent && this.queue.current) this.queue.unshift(this.queue.current);
|
|
303
|
+
this.queue.current = track;
|
|
304
|
+
} else if (!this.queue.current) this.queue.current = this.queue.shift();
|
|
305
|
+
|
|
306
|
+
if (!this.queue.current) throw new KazagumoError(1, 'No track is available to play');
|
|
307
|
+
|
|
308
|
+
const current = this.queue.current;
|
|
309
|
+
current.setKazagumo(this.kazagumo);
|
|
310
|
+
|
|
311
|
+
let errorMessage: string | undefined;
|
|
312
|
+
|
|
313
|
+
const resolveResult = await current.resolve({ player: this as KazagumoPlayer }).catch((e) => {
|
|
314
|
+
errorMessage = e.message;
|
|
315
|
+
return null;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
if (!resolveResult) {
|
|
319
|
+
this.emit(Events.PlayerResolveError, this, current, errorMessage);
|
|
320
|
+
this.emit(Events.Debug, `Player ${this.guildId} resolve error: ${errorMessage}`);
|
|
321
|
+
this.queue.current = null;
|
|
322
|
+
this.queue.size ? await this.play() : this.emit(Events.PlayerEmpty, this);
|
|
323
|
+
return this;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
let playOptions = { track: { encoded: current.track, userData: current.requester ?? {} } };
|
|
327
|
+
if (options) playOptions = { ...playOptions, ...options };
|
|
328
|
+
|
|
329
|
+
await this.shoukaku.playTrack(playOptions);
|
|
330
|
+
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Skip the current track
|
|
336
|
+
* @returns KazagumoPlayer
|
|
337
|
+
*/
|
|
338
|
+
public skip(): KazagumoPlayer {
|
|
339
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
340
|
+
|
|
341
|
+
this.shoukaku.stopTrack();
|
|
342
|
+
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Seek to a position
|
|
348
|
+
* @param position Position in seconds
|
|
349
|
+
* @returns KazagumoPlayer
|
|
350
|
+
*/
|
|
351
|
+
public async seek(position: number): Promise<KazagumoPlayer> {
|
|
352
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
353
|
+
if (!this.queue.current) throw new KazagumoError(1, "Player has no current track in it's queue");
|
|
354
|
+
if (!this.queue.current.isSeekable) throw new KazagumoError(1, "The current track isn't seekable");
|
|
355
|
+
|
|
356
|
+
position = Number(position);
|
|
357
|
+
|
|
358
|
+
if (isNaN(position)) throw new KazagumoError(1, 'position must be a number');
|
|
359
|
+
if (position < 0 || position > (this.queue.current.length ?? 0))
|
|
360
|
+
position = Math.max(Math.min(position, this.queue.current.length ?? 0), 0);
|
|
361
|
+
|
|
362
|
+
this.queue.current.position = position;
|
|
363
|
+
await this.shoukaku.seekTo(position);
|
|
364
|
+
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Set the volume in percentage (default 100%)
|
|
370
|
+
* @param volume Volume
|
|
371
|
+
* @returns KazagumoPlayer
|
|
372
|
+
*/
|
|
373
|
+
public async setVolume(volume: number): Promise<KazagumoPlayer> {
|
|
374
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
375
|
+
if (isNaN(volume)) throw new KazagumoError(1, 'volume must be a number');
|
|
376
|
+
|
|
377
|
+
await this.node.rest.updatePlayer({
|
|
378
|
+
guildId: this.guildId,
|
|
379
|
+
playerOptions: {
|
|
380
|
+
volume,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
this.volume = volume;
|
|
385
|
+
|
|
386
|
+
return this;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Connect to the voice channel
|
|
391
|
+
* @returns KazagumoPlayer
|
|
392
|
+
*/
|
|
393
|
+
public connect(): KazagumoPlayer {
|
|
394
|
+
if (this.state === PlayerState.DESTROYED) throw new KazagumoError(1, 'Player is already destroyed');
|
|
395
|
+
if (this.state === PlayerState.CONNECTED || !!this.voiceId)
|
|
396
|
+
throw new KazagumoError(1, 'Player is already connected');
|
|
397
|
+
this.state = PlayerState.CONNECTING;
|
|
398
|
+
|
|
399
|
+
this.kazagumo.KazagumoOptions.send(this.guildId, {
|
|
400
|
+
op: 4,
|
|
401
|
+
d: {
|
|
402
|
+
guild_id: this.guildId,
|
|
403
|
+
channel_id: this.voiceId,
|
|
404
|
+
self_mute: false,
|
|
405
|
+
self_deaf: this.options.deaf,
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
this.state = PlayerState.CONNECTED;
|
|
410
|
+
|
|
411
|
+
this.emit(Events.Debug, `Player ${this.guildId} connected`);
|
|
412
|
+
|
|
413
|
+
return this;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Disconnect from the voice channel
|
|
418
|
+
* @returns KazagumoPlayer
|
|
419
|
+
*/
|
|
420
|
+
public disconnect(): KazagumoPlayer {
|
|
421
|
+
if (this.state === PlayerState.DISCONNECTED || !this.voiceId)
|
|
422
|
+
throw new KazagumoError(1, 'Player is already disconnected');
|
|
423
|
+
this.state = PlayerState.DISCONNECTING;
|
|
424
|
+
|
|
425
|
+
this.pause(true);
|
|
426
|
+
this.kazagumo.KazagumoOptions.send(this.guildId, {
|
|
427
|
+
op: 4,
|
|
428
|
+
d: {
|
|
429
|
+
guild_id: this.guildId,
|
|
430
|
+
channel_id: null,
|
|
431
|
+
self_mute: false,
|
|
432
|
+
self_deaf: false,
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
this.voiceId = null;
|
|
437
|
+
this.state = PlayerState.DISCONNECTED;
|
|
438
|
+
|
|
439
|
+
this.emit(Events.Debug, `Player disconnected; Guild id: ${this.guildId}`);
|
|
440
|
+
|
|
441
|
+
return this;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Destroy the player
|
|
446
|
+
* @returns KazagumoPlayer
|
|
447
|
+
*/
|
|
448
|
+
async destroy(): Promise<KazagumoPlayer> {
|
|
449
|
+
if (this.state === PlayerState.DESTROYING || this.state === PlayerState.DESTROYED)
|
|
450
|
+
throw new KazagumoError(1, 'Player is already destroyed');
|
|
451
|
+
|
|
452
|
+
this.disconnect();
|
|
453
|
+
this.state = PlayerState.DESTROYING;
|
|
454
|
+
this.shoukaku.clean();
|
|
455
|
+
await this.kazagumo.shoukaku.leaveVoiceChannel(this.guildId);
|
|
456
|
+
await this.shoukaku.destroy();
|
|
457
|
+
this.shoukaku.removeAllListeners();
|
|
458
|
+
this.kazagumo.players.delete(this.guildId);
|
|
459
|
+
this.state = PlayerState.DESTROYED;
|
|
460
|
+
|
|
461
|
+
this.emit(Events.PlayerDestroy, this);
|
|
462
|
+
this.emit(Events.Debug, `Player destroyed; Guild id: ${this.guildId}`);
|
|
463
|
+
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
private emit(event: string, ...args: any): void {
|
|
468
|
+
this.kazagumo.emit(event, ...args);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { KazagumoTrack } from './KazagumoTrack';
|
|
2
|
+
import { Events, KazagumoError } from '../../Modules/Interfaces';
|
|
3
|
+
import { KazagumoPlayer } from '../KazagumoPlayer';
|
|
4
|
+
|
|
5
|
+
export class KazagumoQueue extends Array<KazagumoTrack> {
|
|
6
|
+
constructor(private readonly kazagumoPlayer: KazagumoPlayer) {
|
|
7
|
+
super();
|
|
8
|
+
}
|
|
9
|
+
/** Get the size of queue */
|
|
10
|
+
public get size() {
|
|
11
|
+
return this.length;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Get the size of queue including current */
|
|
15
|
+
public get totalSize(): number {
|
|
16
|
+
return this.length + (this.current ? 1 : 0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Check if the queue is empty or not */
|
|
20
|
+
public get isEmpty() {
|
|
21
|
+
return this.length === 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Get the queue's duration */
|
|
25
|
+
public get durationLength() {
|
|
26
|
+
return this.reduce((acc, cur) => acc + (cur.length || 0), 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Current playing track */
|
|
30
|
+
public current: KazagumoTrack | undefined | null = null;
|
|
31
|
+
/** Previous playing tracks */
|
|
32
|
+
public previous: KazagumoTrack[] = [];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Add track(s) to the queue
|
|
36
|
+
* @param track KazagumoTrack to add
|
|
37
|
+
* @returns KazagumoQueue
|
|
38
|
+
*/
|
|
39
|
+
public add(track: KazagumoTrack | KazagumoTrack[]): KazagumoQueue {
|
|
40
|
+
if (!Array.isArray(track)) track = [track];
|
|
41
|
+
|
|
42
|
+
if (!this.current) {
|
|
43
|
+
if (Array.isArray(track)) this.current = track.shift();
|
|
44
|
+
else {
|
|
45
|
+
this.current = track;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (Array.isArray(track)) for (const t of track) this.push(t);
|
|
51
|
+
else this.push(track);
|
|
52
|
+
this.emitChanges();
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Remove track from the queue
|
|
58
|
+
* @param position Position of the track
|
|
59
|
+
* @returns KazagumoQueue
|
|
60
|
+
*/
|
|
61
|
+
public remove(position: number): KazagumoQueue {
|
|
62
|
+
if (position < 0 || position >= this.length)
|
|
63
|
+
throw new KazagumoError(1, 'Position must be between 0 and ' + (this.length - 1));
|
|
64
|
+
this.splice(position, 1);
|
|
65
|
+
this.emitChanges();
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Shuffle the queue */
|
|
70
|
+
public shuffle(): KazagumoQueue {
|
|
71
|
+
for (let i = this.length - 1; i > 0; i--) {
|
|
72
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
73
|
+
[this[i], this[j]] = [this[j], this[i]];
|
|
74
|
+
}
|
|
75
|
+
this.emitChanges();
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Clear the queue */
|
|
80
|
+
public clear(): KazagumoQueue {
|
|
81
|
+
this.splice(0, this.length);
|
|
82
|
+
this.emitChanges();
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private emitChanges(): void {
|
|
87
|
+
(this.kazagumoPlayer.shoukaku as any).emit(Events.QueueUpdate, this.kazagumoPlayer, this);
|
|
88
|
+
}
|
|
89
|
+
}
|