auris-core 1.0.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/dist/index.js ADDED
@@ -0,0 +1,425 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DirectUrlAdapter: () => DirectUrlAdapter,
24
+ JamendoAdapter: () => JamendoAdapter,
25
+ LocalFileAdapter: () => LocalFileAdapter,
26
+ LoopMode: () => LoopMode,
27
+ Player: () => Player,
28
+ SoundCloudAdapter: () => SoundCloudAdapter,
29
+ VoiceConnectionManager: () => VoiceConnectionManager
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/types.ts
34
+ var LoopMode = /* @__PURE__ */ ((LoopMode2) => {
35
+ LoopMode2[LoopMode2["OFF"] = 0] = "OFF";
36
+ LoopMode2[LoopMode2["TRACK"] = 1] = "TRACK";
37
+ LoopMode2[LoopMode2["QUEUE"] = 2] = "QUEUE";
38
+ return LoopMode2;
39
+ })(LoopMode || {});
40
+
41
+ // src/Player.ts
42
+ var import_voice2 = require("@discordjs/voice");
43
+ var import_events = require("events");
44
+
45
+ // src/VoiceConnectionManager.ts
46
+ var import_voice = require("@discordjs/voice");
47
+ var VoiceConnectionManager = class {
48
+ connection = null;
49
+ channelId = null;
50
+ guildId = null;
51
+ timeoutId = null;
52
+ timeoutMs;
53
+ constructor(timeoutMs = 3e4) {
54
+ this.timeoutMs = timeoutMs;
55
+ }
56
+ async connect(channel) {
57
+ if (this.connection && this.channelId === channel.id) {
58
+ return this.connection;
59
+ }
60
+ this.connection = (0, import_voice.joinVoiceChannel)({
61
+ channelId: channel.id,
62
+ guildId: channel.guild.id,
63
+ adapterCreator: channel.guild.voiceAdapterCreator,
64
+ selfMute: false,
65
+ selfDeaf: false
66
+ });
67
+ this.channelId = channel.id;
68
+ this.guildId = channel.guild.id;
69
+ try {
70
+ await (0, import_voice.entersState)(this.connection, import_voice.VoiceConnectionStatus.Ready, 2e4);
71
+ } catch (error) {
72
+ this.connection.destroy();
73
+ this.connection = null;
74
+ throw new Error("Failed to connect to voice channel.");
75
+ }
76
+ this.connection.on(import_voice.VoiceConnectionStatus.Disconnected, async () => {
77
+ try {
78
+ await Promise.race([
79
+ (0, import_voice.entersState)(this.connection, import_voice.VoiceConnectionStatus.Signalling, 5e3),
80
+ (0, import_voice.entersState)(this.connection, import_voice.VoiceConnectionStatus.Connecting, 5e3)
81
+ ]);
82
+ } catch (error) {
83
+ this.connection?.destroy();
84
+ this.connection = null;
85
+ }
86
+ });
87
+ return this.connection;
88
+ }
89
+ subscribe(player) {
90
+ if (this.connection) {
91
+ this.connection.subscribe(player);
92
+ }
93
+ }
94
+ destroy() {
95
+ if (this.connection) {
96
+ this.connection.destroy();
97
+ this.connection = null;
98
+ }
99
+ this.channelId = null;
100
+ this.guildId = null;
101
+ this.stopAloneTimeout();
102
+ }
103
+ startAloneTimeout(onTimeout) {
104
+ if (this.timeoutId) return;
105
+ this.timeoutId = setTimeout(() => {
106
+ onTimeout();
107
+ this.destroy();
108
+ }, this.timeoutMs);
109
+ }
110
+ stopAloneTimeout() {
111
+ if (this.timeoutId) {
112
+ clearTimeout(this.timeoutId);
113
+ this.timeoutId = null;
114
+ }
115
+ }
116
+ };
117
+
118
+ // src/Player.ts
119
+ var Player = class extends import_events.EventEmitter {
120
+ queue = [];
121
+ loopMode = 0 /* OFF */;
122
+ volume = 100;
123
+ isPlaying = false;
124
+ audioPlayer;
125
+ currentResource = null;
126
+ connectionManager;
127
+ adapters = /* @__PURE__ */ new Map();
128
+ currentTrack = null;
129
+ debugInterval = null;
130
+ constructor(timeoutMs = 3e4) {
131
+ super();
132
+ this.connectionManager = new VoiceConnectionManager(timeoutMs);
133
+ this.audioPlayer = (0, import_voice2.createAudioPlayer)({
134
+ behaviors: {
135
+ noSubscriber: import_voice2.NoSubscriberBehavior.Pause
136
+ }
137
+ });
138
+ this.audioPlayer.on("stateChange", (oldState, newState) => {
139
+ console.log(`[DEBUG] AudioPlayer state transition: ${oldState.status} -> ${newState.status}`);
140
+ });
141
+ this.audioPlayer.on(import_voice2.AudioPlayerStatus.Idle, () => {
142
+ this.isPlaying = false;
143
+ if (this.currentTrack) {
144
+ this.emit("trackEnd", this.currentTrack);
145
+ }
146
+ this.processQueue();
147
+ });
148
+ this.audioPlayer.on("error", (error) => {
149
+ console.error(`[DEBUG] AudioPlayer Error emitted:`, error);
150
+ this.emit("error", error);
151
+ this.processQueue();
152
+ });
153
+ }
154
+ registerAdapter(adapter) {
155
+ this.adapters.set(adapter.name, adapter);
156
+ }
157
+ async connect(channel) {
158
+ await this.connectionManager.connect(channel);
159
+ this.connectionManager.subscribe(this.audioPlayer);
160
+ }
161
+ disconnect() {
162
+ this.connectionManager.destroy();
163
+ this.audioPlayer.stop();
164
+ this.queue = [];
165
+ this.currentTrack = null;
166
+ this.isPlaying = false;
167
+ this.emit("disconnect");
168
+ }
169
+ async play(trackOrTracks) {
170
+ const tracks = Array.isArray(trackOrTracks) ? trackOrTracks : [trackOrTracks];
171
+ this.queue.push(...tracks);
172
+ if (!this.isPlaying && this.audioPlayer.state.status !== import_voice2.AudioPlayerStatus.Playing) {
173
+ this.processQueue();
174
+ }
175
+ }
176
+ async processQueue() {
177
+ if (this.loopMode === 1 /* TRACK */ && this.currentTrack) {
178
+ this.queue.unshift(this.currentTrack);
179
+ } else if (this.loopMode === 2 /* QUEUE */ && this.currentTrack) {
180
+ this.queue.push(this.currentTrack);
181
+ }
182
+ if (this.queue.length === 0) {
183
+ this.currentTrack = null;
184
+ this.emit("queueEnd");
185
+ this.connectionManager.startAloneTimeout(() => this.disconnect());
186
+ return;
187
+ }
188
+ this.connectionManager.stopAloneTimeout();
189
+ const track = this.queue.shift();
190
+ this.currentTrack = track;
191
+ const adapter = this.adapters.get(track.adapterKey);
192
+ if (!adapter) {
193
+ this.emit("error", new Error(`Adapter ${track.adapterKey} not found.`));
194
+ return this.processQueue();
195
+ }
196
+ try {
197
+ const stream = await adapter.getStream(track);
198
+ let oggStream;
199
+ const ffmpegArgs = [
200
+ "-i",
201
+ typeof stream === "string" ? stream : "pipe:0",
202
+ "-c:a",
203
+ "libopus",
204
+ "-b:a",
205
+ "96k",
206
+ "-f",
207
+ "opus",
208
+ "-ar",
209
+ "48000",
210
+ "-ac",
211
+ "2",
212
+ "-filter:a",
213
+ `volume=${this.volume / 100}`,
214
+ "pipe:1"
215
+ ];
216
+ const ffProcess = require("child_process").spawn("ffmpeg", ffmpegArgs);
217
+ if (typeof stream !== "string") {
218
+ stream.pipe(ffProcess.stdin);
219
+ }
220
+ oggStream = ffProcess.stdout;
221
+ ffProcess.on("error", (err) => console.error(`[FFMPEG ERROR]`, err));
222
+ this.currentResource = require("@discordjs/voice").createAudioResource(oggStream, {
223
+ inputType: require("@discordjs/voice").StreamType.OggOpus
224
+ });
225
+ this.audioPlayer.play(this.currentResource);
226
+ this.isPlaying = true;
227
+ this.emit("trackStart", track);
228
+ } catch (error) {
229
+ this.emit("error", error);
230
+ this.processQueue();
231
+ }
232
+ }
233
+ pause() {
234
+ this.audioPlayer.pause();
235
+ this.isPlaying = false;
236
+ }
237
+ resume() {
238
+ this.audioPlayer.unpause();
239
+ this.isPlaying = true;
240
+ }
241
+ skip() {
242
+ this.audioPlayer.stop();
243
+ }
244
+ stop() {
245
+ this.queue = [];
246
+ this.loopMode = 0 /* OFF */;
247
+ this.audioPlayer.stop();
248
+ }
249
+ setVolume(volume) {
250
+ this.volume = Math.max(0, Math.min(200, volume));
251
+ if (this.currentResource?.volume) {
252
+ this.currentResource.volume.setVolume(this.volume / 100);
253
+ }
254
+ }
255
+ setLoopMode(mode) {
256
+ this.loopMode = mode;
257
+ }
258
+ getQueue() {
259
+ return this.queue;
260
+ }
261
+ getCurrentTrack() {
262
+ return this.currentTrack;
263
+ }
264
+ };
265
+
266
+ // src/adapters/LocalFileAdapter.ts
267
+ var import_fs = require("fs");
268
+ var import_path = require("path");
269
+ var LocalFileAdapter = class {
270
+ name = "LocalFileAdapter";
271
+ async getTrack(query) {
272
+ const absolutePath = (0, import_path.resolve)(query);
273
+ return {
274
+ title: (0, import_path.basename)(absolutePath),
275
+ url: absolutePath,
276
+ source: "local",
277
+ adapterKey: this.name
278
+ };
279
+ }
280
+ async getStream(track) {
281
+ return (0, import_fs.createReadStream)(track.url);
282
+ }
283
+ };
284
+
285
+ // src/adapters/DirectUrlAdapter.ts
286
+ var DirectUrlAdapter = class {
287
+ name = "DirectUrlAdapter";
288
+ async getTrack(query) {
289
+ if (!query.startsWith("http://") && !query.startsWith("https://")) {
290
+ return null;
291
+ }
292
+ return {
293
+ title: query.split("/").pop() || "Unknown Direct Audio",
294
+ url: query,
295
+ source: "direct",
296
+ adapterKey: this.name
297
+ };
298
+ }
299
+ async getStream(track) {
300
+ return track.url;
301
+ }
302
+ };
303
+
304
+ // src/adapters/JamendoAdapter.ts
305
+ var JamendoAdapter = class {
306
+ name = "JamendoAdapter";
307
+ clientId;
308
+ constructor(clientId) {
309
+ this.clientId = clientId;
310
+ }
311
+ async getTrack(query) {
312
+ if (/^\d+$/.test(query)) {
313
+ return this.fetchTrackById(query);
314
+ }
315
+ return this.searchTrack(query);
316
+ }
317
+ async fetchTrackById(id) {
318
+ const url = `https://api.jamendo.com/v3.0/tracks/?client_id=${this.clientId}&format=json&id=${id}`;
319
+ const response = await fetch(url);
320
+ const data = await response.json();
321
+ if (!data.results || data.results.length === 0) return null;
322
+ const trackInfo = data.results[0];
323
+ return {
324
+ title: trackInfo.name,
325
+ url: trackInfo.audio,
326
+ duration: parseInt(trackInfo.duration, 10),
327
+ source: "jamendo",
328
+ author: trackInfo.artist_name,
329
+ thumbnail: trackInfo.image,
330
+ adapterKey: "JamendoAdapter"
331
+ };
332
+ }
333
+ async searchTrack(query) {
334
+ const url = `https://api.jamendo.com/v3.0/tracks/?client_id=${this.clientId}&format=json&search=${encodeURIComponent(query)}&limit=1`;
335
+ const response = await fetch(url);
336
+ const data = await response.json();
337
+ if (!data.results || data.results.length === 0) return null;
338
+ const trackInfo = data.results[0];
339
+ return {
340
+ title: trackInfo.name,
341
+ url: trackInfo.audio,
342
+ duration: parseInt(trackInfo.duration, 10),
343
+ source: "jamendo",
344
+ author: trackInfo.artist_name,
345
+ thumbnail: trackInfo.image,
346
+ adapterKey: "JamendoAdapter"
347
+ };
348
+ }
349
+ async getStream(track) {
350
+ return track.url;
351
+ }
352
+ };
353
+
354
+ // src/adapters/SoundCloudAdapter.ts
355
+ var SoundCloudAdapter = class {
356
+ name = "SoundCloudAdapter";
357
+ clientId;
358
+ constructor(clientId) {
359
+ this.clientId = clientId;
360
+ }
361
+ async getTrack(query) {
362
+ let url = query;
363
+ if (query.includes("soundcloud.com")) {
364
+ const resolveUrl = `https://api-v2.soundcloud.com/resolve?url=${encodeURIComponent(query)}&client_id=${this.clientId}`;
365
+ try {
366
+ const res = await fetch(resolveUrl);
367
+ const data = await res.json();
368
+ if (data && data.kind === "track") {
369
+ return this.mapTrack(data);
370
+ }
371
+ } catch (e) {
372
+ return null;
373
+ }
374
+ } else {
375
+ const searchUrl = `https://api-v2.soundcloud.com/search/tracks?q=${encodeURIComponent(query)}&client_id=${this.clientId}&limit=1`;
376
+ try {
377
+ const res = await fetch(searchUrl);
378
+ const data = await res.json();
379
+ if (data.collection && data.collection.length > 0) {
380
+ return this.mapTrack(data.collection[0]);
381
+ }
382
+ } catch (e) {
383
+ return null;
384
+ }
385
+ }
386
+ return null;
387
+ }
388
+ mapTrack(data) {
389
+ let streamUrl = "";
390
+ if (data.media && data.media.transcodings) {
391
+ const transcoding = data.media.transcodings.find((t) => t.format.protocol === "progressive") || data.media.transcodings[0];
392
+ if (transcoding) {
393
+ streamUrl = transcoding.url;
394
+ }
395
+ }
396
+ return {
397
+ title: data.title,
398
+ url: streamUrl,
399
+ duration: Math.floor(data.duration / 1e3),
400
+ source: "soundcloud",
401
+ author: data.user?.username,
402
+ thumbnail: data.artwork_url,
403
+ adapterKey: "SoundCloudAdapter"
404
+ };
405
+ }
406
+ async getStream(track) {
407
+ if (track.url.includes("api-v2.soundcloud.com")) {
408
+ const res = await fetch(`${track.url}?client_id=${this.clientId}`);
409
+ const data = await res.json();
410
+ return data.url;
411
+ }
412
+ return track.url;
413
+ }
414
+ };
415
+ // Annotate the CommonJS export names for ESM import in node:
416
+ 0 && (module.exports = {
417
+ DirectUrlAdapter,
418
+ JamendoAdapter,
419
+ LocalFileAdapter,
420
+ LoopMode,
421
+ Player,
422
+ SoundCloudAdapter,
423
+ VoiceConnectionManager
424
+ });
425
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/Player.ts","../src/VoiceConnectionManager.ts","../src/adapters/LocalFileAdapter.ts","../src/adapters/DirectUrlAdapter.ts","../src/adapters/JamendoAdapter.ts","../src/adapters/SoundCloudAdapter.ts"],"sourcesContent":["export * from './types';\nexport * from './Player';\nexport * from './VoiceConnectionManager';\nexport * from './adapters/LocalFileAdapter';\nexport * from './adapters/DirectUrlAdapter';\nexport * from './adapters/JamendoAdapter';\nexport * from './adapters/SoundCloudAdapter';\n","import { AudioPlayer, VoiceConnection as DjsVoiceConnection } from '@discordjs/voice';\nimport { VoiceBasedChannel } from 'discord.js';\n\nexport interface Track {\n title: string;\n url: string;\n duration?: number;\n source: string;\n thumbnail?: string;\n author?: string;\n // Internal reference for the adapter to use when fetching the stream\n adapterKey: string;\n}\n\nexport interface AudioSource {\n name: string;\n getTrack(query: string): Promise<Track | Track[] | null>;\n getStream(track: Track): Promise<string | import('stream').Readable>;\n}\n\nexport enum LoopMode {\n OFF = 0,\n TRACK = 1,\n QUEUE = 2,\n}\n\nexport interface PlayerEvents {\n trackStart: (track: Track) => void;\n trackEnd: (track: Track) => void;\n queueEnd: () => void;\n error: (error: Error) => void;\n disconnect: () => void;\n}\n","import { \n AudioPlayer, \n AudioPlayerStatus, \n createAudioPlayer, \n createAudioResource, \n NoSubscriberBehavior, \n AudioResource \n} from '@discordjs/voice';\nimport { EventEmitter } from 'events';\nimport { VoiceBasedChannel } from 'discord.js';\nimport { Track, LoopMode, PlayerEvents, AudioSource } from './types';\nimport { VoiceConnectionManager } from './VoiceConnectionManager';\n\nexport class Player extends EventEmitter {\n public queue: Track[] = [];\n public loopMode: LoopMode = LoopMode.OFF;\n public volume: number = 100;\n public isPlaying: boolean = false;\n\n public audioPlayer: AudioPlayer;\n public currentResource: AudioResource | null = null;\n public connectionManager: VoiceConnectionManager;\n private adapters: Map<string, AudioSource> = new Map();\n private currentTrack: Track | null = null;\n private debugInterval: NodeJS.Timeout | null = null;\n\n constructor(timeoutMs: number = 30000) {\n super();\n this.connectionManager = new VoiceConnectionManager(timeoutMs);\n this.audioPlayer = createAudioPlayer({\n behaviors: {\n noSubscriber: NoSubscriberBehavior.Pause,\n },\n });\n\n this.audioPlayer.on('stateChange', (oldState, newState) => {\n console.log(`[DEBUG] AudioPlayer state transition: ${oldState.status} -> ${newState.status}`);\n });\n\n this.audioPlayer.on(AudioPlayerStatus.Idle, () => {\n this.isPlaying = false;\n if (this.currentTrack) {\n this.emit('trackEnd', this.currentTrack);\n }\n this.processQueue();\n });\n\n this.audioPlayer.on('error', (error) => {\n console.error(`[DEBUG] AudioPlayer Error emitted:`, error);\n this.emit('error', error);\n this.processQueue(); // skip to next on error\n });\n }\n\n public registerAdapter(adapter: AudioSource) {\n this.adapters.set(adapter.name, adapter);\n }\n\n public async connect(channel: VoiceBasedChannel) {\n await this.connectionManager.connect(channel);\n this.connectionManager.subscribe(this.audioPlayer);\n }\n\n public disconnect() {\n this.connectionManager.destroy();\n this.audioPlayer.stop();\n this.queue = [];\n this.currentTrack = null;\n this.isPlaying = false;\n this.emit('disconnect');\n }\n\n public async play(trackOrTracks: Track | Track[]) {\n const tracks = Array.isArray(trackOrTracks) ? trackOrTracks : [trackOrTracks];\n this.queue.push(...tracks);\n\n if (!this.isPlaying && this.audioPlayer.state.status !== AudioPlayerStatus.Playing) {\n this.processQueue();\n }\n }\n\n private async processQueue(): Promise<void> {\n if (this.loopMode === LoopMode.TRACK && this.currentTrack) {\n this.queue.unshift(this.currentTrack);\n } else if (this.loopMode === LoopMode.QUEUE && this.currentTrack) {\n this.queue.push(this.currentTrack);\n }\n\n if (this.queue.length === 0) {\n this.currentTrack = null;\n this.emit('queueEnd');\n this.connectionManager.startAloneTimeout(() => this.disconnect());\n return;\n }\n\n this.connectionManager.stopAloneTimeout();\n\n const track = this.queue.shift()!;\n this.currentTrack = track;\n\n const adapter = this.adapters.get(track.adapterKey);\n if (!adapter) {\n this.emit('error', new Error(`Adapter ${track.adapterKey} not found.`));\n return this.processQueue();\n }\n\n try {\n const stream = await adapter.getStream(track) as any;\n\n let oggStream;\n const ffmpegArgs = [\n '-i', typeof stream === 'string' ? stream : 'pipe:0',\n '-c:a', 'libopus',\n '-b:a', '96k',\n '-f', 'opus',\n '-ar', '48000',\n '-ac', '2',\n '-filter:a', `volume=${this.volume / 100}`,\n 'pipe:1'\n ];\n \n const ffProcess = require('child_process').spawn('ffmpeg', ffmpegArgs);\n \n if (typeof stream !== 'string') {\n stream.pipe(ffProcess.stdin);\n }\n \n oggStream = ffProcess.stdout;\n \n ffProcess.on('error', (err: any) => console.error(`[FFMPEG ERROR]`, err));\n\n this.currentResource = require('@discordjs/voice').createAudioResource(oggStream, {\n inputType: require('@discordjs/voice').StreamType.OggOpus\n });\n\n this.audioPlayer.play(this.currentResource!);\n\n this.isPlaying = true;\n this.emit('trackStart', track);\n } catch (error) {\n this.emit('error', error as Error);\n this.processQueue();\n }\n }\n\n public pause() {\n this.audioPlayer.pause();\n this.isPlaying = false;\n }\n\n public resume() {\n this.audioPlayer.unpause();\n this.isPlaying = true;\n }\n\n public skip() {\n this.audioPlayer.stop(); \n }\n\n public stop() {\n this.queue = [];\n this.loopMode = LoopMode.OFF;\n this.audioPlayer.stop();\n }\n\n public setVolume(volume: number) {\n this.volume = Math.max(0, Math.min(200, volume));\n if (this.currentResource?.volume) {\n this.currentResource.volume.setVolume(this.volume / 100);\n }\n }\n\n public setLoopMode(mode: LoopMode) {\n this.loopMode = mode;\n }\n\n public getQueue(): Track[] {\n return this.queue;\n }\n\n public getCurrentTrack(): Track | null {\n return this.currentTrack;\n }\n}\n\n// Override EventEmitter methods for types\nexport interface Player {\n on<K extends keyof PlayerEvents>(event: K, listener: PlayerEvents[K]): this;\n once<K extends keyof PlayerEvents>(event: K, listener: PlayerEvents[K]): this;\n emit<K extends keyof PlayerEvents>(event: K, ...args: Parameters<PlayerEvents[K]>): boolean;\n}\n","import { \n joinVoiceChannel, \n VoiceConnection, \n VoiceConnectionStatus, \n entersState,\n AudioPlayer\n} from '@discordjs/voice';\nimport { VoiceBasedChannel } from 'discord.js';\n\nexport class VoiceConnectionManager {\n public connection: VoiceConnection | null = null;\n public channelId: string | null = null;\n public guildId: string | null = null;\n\n private timeoutId: NodeJS.Timeout | null = null;\n private readonly timeoutMs: number;\n\n constructor(timeoutMs: number = 30000) {\n this.timeoutMs = timeoutMs;\n }\n\n public async connect(channel: VoiceBasedChannel): Promise<VoiceConnection> {\n if (this.connection && this.channelId === channel.id) {\n return this.connection;\n }\n\n this.connection = joinVoiceChannel({\n channelId: channel.id,\n guildId: channel.guild.id,\n adapterCreator: channel.guild.voiceAdapterCreator as any,\n selfMute: false,\n selfDeaf: false,\n });\n\n this.channelId = channel.id;\n this.guildId = channel.guild.id;\n\n try {\n await entersState(this.connection, VoiceConnectionStatus.Ready, 20_000);\n } catch (error) {\n this.connection.destroy();\n this.connection = null;\n throw new Error('Failed to connect to voice channel.');\n }\n\n this.connection.on(VoiceConnectionStatus.Disconnected, async () => {\n try {\n await Promise.race([\n entersState(this.connection!, VoiceConnectionStatus.Signalling, 5_000),\n entersState(this.connection!, VoiceConnectionStatus.Connecting, 5_000),\n ]);\n } catch (error) {\n this.connection?.destroy();\n this.connection = null;\n }\n });\n\n return this.connection;\n }\n\n public subscribe(player: AudioPlayer) {\n if (this.connection) {\n this.connection.subscribe(player);\n }\n }\n\n public destroy() {\n if (this.connection) {\n this.connection.destroy();\n this.connection = null;\n }\n this.channelId = null;\n this.guildId = null;\n this.stopAloneTimeout();\n }\n\n public startAloneTimeout(onTimeout: () => void) {\n if (this.timeoutId) return;\n this.timeoutId = setTimeout(() => {\n onTimeout();\n this.destroy();\n }, this.timeoutMs);\n }\n\n public stopAloneTimeout() {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId);\n this.timeoutId = null;\n }\n }\n}\n","import { AudioSource, Track } from '../types';\nimport { createReadStream } from 'fs';\nimport { resolve, basename } from 'path';\n\nexport class LocalFileAdapter implements AudioSource {\n public name = 'LocalFileAdapter';\n\n public async getTrack(query: string): Promise<Track | null> {\n const absolutePath = resolve(query);\n return {\n title: basename(absolutePath),\n url: absolutePath,\n source: 'local',\n adapterKey: this.name,\n };\n }\n\n public async getStream(track: Track) {\n return createReadStream(track.url);\n }\n}\n","import { AudioSource, Track } from '../types';\n\nexport class DirectUrlAdapter implements AudioSource {\n public name = 'DirectUrlAdapter';\n\n public async getTrack(query: string): Promise<Track | null> {\n if (!query.startsWith('http://') && !query.startsWith('https://')) {\n return null;\n }\n \n return {\n title: query.split('/').pop() || 'Unknown Direct Audio',\n url: query,\n source: 'direct',\n adapterKey: this.name,\n };\n }\n\n public async getStream(track: Track) {\n return track.url;\n }\n}\n","import { AudioSource, Track } from '../types';\n\nexport class JamendoAdapter implements AudioSource {\n public name = 'JamendoAdapter';\n private clientId: string;\n\n constructor(clientId: string) {\n this.clientId = clientId;\n }\n\n public async getTrack(query: string): Promise<Track | Track[] | null> {\n if (/^\\d+$/.test(query)) {\n return this.fetchTrackById(query);\n }\n return this.searchTrack(query);\n }\n\n private async fetchTrackById(id: string): Promise<Track | null> {\n const url = `https://api.jamendo.com/v3.0/tracks/?client_id=${this.clientId}&format=json&id=${id}`;\n const response = await fetch(url);\n const data = await response.json();\n\n if (!data.results || data.results.length === 0) return null;\n\n const trackInfo = data.results[0];\n return {\n title: trackInfo.name,\n url: trackInfo.audio,\n duration: parseInt(trackInfo.duration, 10),\n source: 'jamendo',\n author: trackInfo.artist_name,\n thumbnail: trackInfo.image,\n adapterKey: 'JamendoAdapter',\n };\n }\n\n private async searchTrack(query: string): Promise<Track | null> {\n const url = `https://api.jamendo.com/v3.0/tracks/?client_id=${this.clientId}&format=json&search=${encodeURIComponent(query)}&limit=1`;\n const response = await fetch(url);\n const data = await response.json();\n\n if (!data.results || data.results.length === 0) return null;\n\n const trackInfo = data.results[0];\n return {\n title: trackInfo.name,\n url: trackInfo.audio,\n duration: parseInt(trackInfo.duration, 10),\n source: 'jamendo',\n author: trackInfo.artist_name,\n thumbnail: trackInfo.image,\n adapterKey: 'JamendoAdapter',\n };\n }\n\n public async getStream(track: Track) {\n return track.url;\n }\n}\n","import { AudioSource, Track } from '../types';\n\nexport class SoundCloudAdapter implements AudioSource {\n public name = 'SoundCloudAdapter';\n private clientId: string;\n\n constructor(clientId: string) {\n this.clientId = clientId;\n }\n\n public async getTrack(query: string): Promise<Track | null> {\n let url = query;\n if (query.includes('soundcloud.com')) {\n const resolveUrl = `https://api-v2.soundcloud.com/resolve?url=${encodeURIComponent(query)}&client_id=${this.clientId}`;\n try {\n const res = await fetch(resolveUrl);\n const data = await res.json();\n \n if (data && data.kind === 'track') {\n return this.mapTrack(data);\n }\n } catch (e) {\n return null;\n }\n } else {\n const searchUrl = `https://api-v2.soundcloud.com/search/tracks?q=${encodeURIComponent(query)}&client_id=${this.clientId}&limit=1`;\n try {\n const res = await fetch(searchUrl);\n const data = await res.json();\n \n if (data.collection && data.collection.length > 0) {\n return this.mapTrack(data.collection[0]);\n }\n } catch (e) {\n return null;\n }\n }\n\n return null;\n }\n\n private mapTrack(data: any): Track {\n let streamUrl = '';\n \n if (data.media && data.media.transcodings) {\n const transcoding = data.media.transcodings.find((t: any) => t.format.protocol === 'progressive') \n || data.media.transcodings[0];\n if (transcoding) {\n streamUrl = transcoding.url;\n }\n }\n\n return {\n title: data.title,\n url: streamUrl, \n duration: Math.floor(data.duration / 1000),\n source: 'soundcloud',\n author: data.user?.username,\n thumbnail: data.artwork_url,\n adapterKey: 'SoundCloudAdapter',\n };\n }\n\n public async getStream(track: Track) {\n if (track.url.includes('api-v2.soundcloud.com')) {\n const res = await fetch(`${track.url}?client_id=${this.clientId}`);\n const data = await res.json();\n return data.url;\n }\n return track.url;\n }\n}\n"],"mappings":"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","names":["LoopMode","import_voice"]}