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