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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rishi Bhardwaj / Omnikon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ <div align="center">
2
+ <h1>Auris</h1>
3
+ <p><strong>A self-hosted, legally-clean music bot library for discord.js.</strong></p>
4
+ <p><i>Code. Create. Conquer. — Brought to you by Omnikon</i></p>
5
+
6
+ <p>
7
+ <a href="https://github.com/Omnikon-Org/Auris/actions"><img src="https://img.shields.io/github/actions/workflow/status/Omnikon-Org/Auris/ci.yml?branch=main" alt="Build Status" /></a>
8
+ <a href="https://www.npmjs.com/package/@omnikon/auris"><img src="https://img.shields.io/npm/v/@omnikon/auris.svg" alt="npm version" /></a>
9
+ <a href="https://www.npmjs.com/package/@omnikon/auris"><img src="https://img.shields.io/npm/dt/@omnikon/auris.svg" alt="npm downloads" /></a>
10
+ <a href="https://github.com/Omnikon-Org/Auris/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@omnikon/auris.svg" alt="License" /></a>
11
+ <img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg" alt="TypeScript Ready" />
12
+ </p>
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## 🛑 The Problem: Why not YouTube?
18
+
19
+ Most Discord music bots scrape YouTube audio, violating YouTube's Terms of Service. This gets bots DMCA'd, IP-blocked, or banned completely. Existing alternatives are either abandoned or rely on fragile scraper libraries like `ytdl-core`.
20
+
21
+ **`Auris`** solves this by providing a robust, legal-only audio pipeline. Built from the ground up without YouTube scraping, this library guarantees peace of mind while delivering a high-quality, feature-rich audio experience for your users.
22
+
23
+ ---
24
+
25
+ ## ✨ Features
26
+
27
+ - 🎵 **Legally Safe Audio Sources**: Native support for safe, authorized streaming.
28
+ - **Local Files**: Play MP3, OGG, WAV, and FLAC directly from your server.
29
+ - **Direct URLs**: Stream from any directly hosted audio file.
30
+ - **Jamendo API**: Access thousands of royalty-free / Creative Commons tracks.
31
+ - **SoundCloud API**: Stream officially permitted tracks from SoundCloud.
32
+ - 🔌 **BYO Adapter System**: Easily integrate your own licensed catalog or corporate library using our simple 2-method interface.
33
+ - 🔁 **Robust Playback Engine**: Complete queue management with Loop (Track/Queue), Skip, Pause/Resume, and granular Volume control.
34
+ - ⚙️ **Smart Voice Management**: Automatically reconnects on network drops and gracefully disconnects when inactive or alone.
35
+ - ⚡ **Modern Architecture**: Written in strict TypeScript, exporting both ESM and CommonJS for seamless integration in any Node environment.
36
+
37
+ ---
38
+
39
+ ## 🛠 Installation
40
+
41
+ ### 1. Prerequisites
42
+ - **Node.js**: v18.0.0 or higher.
43
+ - **FFmpeg**: *Required for audio processing.*
44
+ - **Ubuntu/Debian:** `sudo apt install ffmpeg`
45
+ - **MacOS:** `brew install ffmpeg`
46
+ - **Windows:** Download from [ffmpeg.org](https://ffmpeg.org/download.html) and add to PATH.
47
+
48
+ ### 2. Install Package
49
+ ```bash
50
+ npm install @omnikon/auris discord.js @discordjs/voice libsodium-wrappers
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 🚀 Quickstart (Under 10 Lines)
56
+
57
+ Integrate music into your existing `discord.js` bot instantly:
58
+
59
+ ```typescript
60
+ import { Client, GatewayIntentBits } from 'discord.js';
61
+ import { Player, LocalFileAdapter } from '@omnikon/auris';
62
+
63
+ const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates] });
64
+ const player = new Player();
65
+ const localAdapter = new LocalFileAdapter();
66
+
67
+ player.registerAdapter(localAdapter);
68
+
69
+ // Inside your command execution block:
70
+ await player.connect(interaction.member.voice.channel);
71
+ const track = await localAdapter.getTrack('./assets/music/track.mp3');
72
+ await player.play(track);
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 🔌 Supported Adapters
78
+
79
+ | Adapter | Description | Setup |
80
+ |---------|-------------|-------|
81
+ | **LocalFileAdapter** | Plays audio from your local filesystem. | `new LocalFileAdapter()` |
82
+ | **DirectUrlAdapter** | Streams audio directly from an HTTP URL. | `new DirectUrlAdapter()` |
83
+ | **JamendoAdapter** | Searches and streams from Jamendo API. | `new JamendoAdapter('YOUR_CLIENT_ID')` |
84
+ | **SoundCloudAdapter** | Streams permitted tracks from SoundCloud. | `new SoundCloudAdapter('YOUR_CLIENT_ID')` |
85
+
86
+ ---
87
+
88
+ ## 📖 API Reference
89
+
90
+ ### `Player` Class
91
+ The core queue and playback manager.
92
+
93
+ #### Connection & Playback
94
+ - **`connect(channel: VoiceBasedChannel)`**: Joins the voice channel and prepares playback.
95
+ - **`disconnect()`**: Gracefully leaves the voice channel and flushes the queue.
96
+ - **`play(track: Track | Track[])`**: Appends track(s) to the queue and starts playing if idle.
97
+ - **`pause()` / `resume()`**: Toggles playback state.
98
+ - **`skip()`**: Skips the currently playing track.
99
+ - **`stop()`**: Halts playback entirely and clears the queue.
100
+
101
+ #### Controls
102
+ - **`setVolume(level: number)`**: Adjusts the volume between `0` and `200`.
103
+ - **`setLoopMode(mode: LoopMode)`**: Sets looping behavior.
104
+ - `LoopMode.OFF`
105
+ - `LoopMode.TRACK`
106
+ - `LoopMode.QUEUE`
107
+
108
+ #### State & Events
109
+ - **`getQueue(): Track[]`**: Returns the pending tracks.
110
+ - **`getCurrentTrack(): Track | null`**: Returns the active track.
111
+
112
+ The `Player` emits standard events you can hook into for logging or sending Discord embeds:
113
+ ```typescript
114
+ player.on('trackStart', (track) => console.log(`Now playing: ${track.title}`));
115
+ player.on('trackEnd', (track) => console.log(`Finished: ${track.title}`));
116
+ player.on('queueEnd', () => console.log(`Queue empty!`));
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 🧑‍💻 Running the Example Bot
122
+
123
+ We've provided a fully functioning Discord bot showcasing all slash commands (`/play`, `/pause`, `/skip`, `/loop`, `/queue`, `/volume`).
124
+
125
+ 1. Clone the repository.
126
+ 2. Run `npm install`.
127
+ 3. Copy `examples/bot/.env.example` to `examples/bot/.env`.
128
+ 4. Configure your `.env` file:
129
+
130
+ **Required:**
131
+ - `DISCORD_TOKEN`: Your Discord bot token.
132
+
133
+ **Optional:**
134
+ - `JAMENDO_CLIENT_ID`: Enables the Jamendo adapter.
135
+ - `SOUNDCLOUD_CLIENT_ID`: Enables the SoundCloud adapter.
136
+
137
+ *Note: Without these optional credentials, the bot still fully supports all core playback features (Local Files, Direct URLs, Queueing, Pausing, etc.). Jamendo and SoundCloud will simply be disabled gracefully.*
138
+
139
+ 5. Start the bot:
140
+ ```bash
141
+ npm run dev:example
142
+ ```
143
+ 6. Invite the bot to your server and type `/play` in any channel!
144
+
145
+ ---
146
+
147
+ <div align="center">
148
+ <p><strong>Part of the Omnikon Ecosystem</strong></p>
149
+ <p>
150
+ <a href="https://github.com/Omnikon-Org/schema-cast">schema-cast</a> •
151
+ <a href="https://github.com/Omnikon-Org/PackVault">PackVault</a> •
152
+ <a href="https://github.com/Omnikon-Org/IssueSwipe">IssueSwipe</a> •
153
+ <a href="https://github.com/Omnikon-Org/Abyss">Abyss</a>
154
+ </p>
155
+ </div>
@@ -0,0 +1,111 @@
1
+ import * as stream from 'stream';
2
+ import { VoiceConnection, AudioPlayer, AudioResource } from '@discordjs/voice';
3
+ import { EventEmitter } from 'events';
4
+ import { VoiceBasedChannel } from 'discord.js';
5
+ import * as fs from 'fs';
6
+
7
+ interface Track {
8
+ title: string;
9
+ url: string;
10
+ duration?: number;
11
+ source: string;
12
+ thumbnail?: string;
13
+ author?: string;
14
+ adapterKey: string;
15
+ }
16
+ interface AudioSource {
17
+ name: string;
18
+ getTrack(query: string): Promise<Track | Track[] | null>;
19
+ getStream(track: Track): Promise<string | stream.Readable>;
20
+ }
21
+ declare enum LoopMode {
22
+ OFF = 0,
23
+ TRACK = 1,
24
+ QUEUE = 2
25
+ }
26
+ interface PlayerEvents {
27
+ trackStart: (track: Track) => void;
28
+ trackEnd: (track: Track) => void;
29
+ queueEnd: () => void;
30
+ error: (error: Error) => void;
31
+ disconnect: () => void;
32
+ }
33
+
34
+ declare class VoiceConnectionManager {
35
+ connection: VoiceConnection | null;
36
+ channelId: string | null;
37
+ guildId: string | null;
38
+ private timeoutId;
39
+ private readonly timeoutMs;
40
+ constructor(timeoutMs?: number);
41
+ connect(channel: VoiceBasedChannel): Promise<VoiceConnection>;
42
+ subscribe(player: AudioPlayer): void;
43
+ destroy(): void;
44
+ startAloneTimeout(onTimeout: () => void): void;
45
+ stopAloneTimeout(): void;
46
+ }
47
+
48
+ declare class Player extends EventEmitter {
49
+ queue: Track[];
50
+ loopMode: LoopMode;
51
+ volume: number;
52
+ isPlaying: boolean;
53
+ audioPlayer: AudioPlayer;
54
+ currentResource: AudioResource | null;
55
+ connectionManager: VoiceConnectionManager;
56
+ private adapters;
57
+ private currentTrack;
58
+ private debugInterval;
59
+ constructor(timeoutMs?: number);
60
+ registerAdapter(adapter: AudioSource): void;
61
+ connect(channel: VoiceBasedChannel): Promise<void>;
62
+ disconnect(): void;
63
+ play(trackOrTracks: Track | Track[]): Promise<void>;
64
+ private processQueue;
65
+ pause(): void;
66
+ resume(): void;
67
+ skip(): void;
68
+ stop(): void;
69
+ setVolume(volume: number): void;
70
+ setLoopMode(mode: LoopMode): void;
71
+ getQueue(): Track[];
72
+ getCurrentTrack(): Track | null;
73
+ }
74
+ interface Player {
75
+ on<K extends keyof PlayerEvents>(event: K, listener: PlayerEvents[K]): this;
76
+ once<K extends keyof PlayerEvents>(event: K, listener: PlayerEvents[K]): this;
77
+ emit<K extends keyof PlayerEvents>(event: K, ...args: Parameters<PlayerEvents[K]>): boolean;
78
+ }
79
+
80
+ declare class LocalFileAdapter implements AudioSource {
81
+ name: string;
82
+ getTrack(query: string): Promise<Track | null>;
83
+ getStream(track: Track): Promise<fs.ReadStream>;
84
+ }
85
+
86
+ declare class DirectUrlAdapter implements AudioSource {
87
+ name: string;
88
+ getTrack(query: string): Promise<Track | null>;
89
+ getStream(track: Track): Promise<string>;
90
+ }
91
+
92
+ declare class JamendoAdapter implements AudioSource {
93
+ name: string;
94
+ private clientId;
95
+ constructor(clientId: string);
96
+ getTrack(query: string): Promise<Track | Track[] | null>;
97
+ private fetchTrackById;
98
+ private searchTrack;
99
+ getStream(track: Track): Promise<string>;
100
+ }
101
+
102
+ declare class SoundCloudAdapter implements AudioSource {
103
+ name: string;
104
+ private clientId;
105
+ constructor(clientId: string);
106
+ getTrack(query: string): Promise<Track | null>;
107
+ private mapTrack;
108
+ getStream(track: Track): Promise<any>;
109
+ }
110
+
111
+ export { type AudioSource, DirectUrlAdapter, JamendoAdapter, LocalFileAdapter, LoopMode, Player, type PlayerEvents, SoundCloudAdapter, type Track, VoiceConnectionManager };
@@ -0,0 +1,111 @@
1
+ import * as stream from 'stream';
2
+ import { VoiceConnection, AudioPlayer, AudioResource } from '@discordjs/voice';
3
+ import { EventEmitter } from 'events';
4
+ import { VoiceBasedChannel } from 'discord.js';
5
+ import * as fs from 'fs';
6
+
7
+ interface Track {
8
+ title: string;
9
+ url: string;
10
+ duration?: number;
11
+ source: string;
12
+ thumbnail?: string;
13
+ author?: string;
14
+ adapterKey: string;
15
+ }
16
+ interface AudioSource {
17
+ name: string;
18
+ getTrack(query: string): Promise<Track | Track[] | null>;
19
+ getStream(track: Track): Promise<string | stream.Readable>;
20
+ }
21
+ declare enum LoopMode {
22
+ OFF = 0,
23
+ TRACK = 1,
24
+ QUEUE = 2
25
+ }
26
+ interface PlayerEvents {
27
+ trackStart: (track: Track) => void;
28
+ trackEnd: (track: Track) => void;
29
+ queueEnd: () => void;
30
+ error: (error: Error) => void;
31
+ disconnect: () => void;
32
+ }
33
+
34
+ declare class VoiceConnectionManager {
35
+ connection: VoiceConnection | null;
36
+ channelId: string | null;
37
+ guildId: string | null;
38
+ private timeoutId;
39
+ private readonly timeoutMs;
40
+ constructor(timeoutMs?: number);
41
+ connect(channel: VoiceBasedChannel): Promise<VoiceConnection>;
42
+ subscribe(player: AudioPlayer): void;
43
+ destroy(): void;
44
+ startAloneTimeout(onTimeout: () => void): void;
45
+ stopAloneTimeout(): void;
46
+ }
47
+
48
+ declare class Player extends EventEmitter {
49
+ queue: Track[];
50
+ loopMode: LoopMode;
51
+ volume: number;
52
+ isPlaying: boolean;
53
+ audioPlayer: AudioPlayer;
54
+ currentResource: AudioResource | null;
55
+ connectionManager: VoiceConnectionManager;
56
+ private adapters;
57
+ private currentTrack;
58
+ private debugInterval;
59
+ constructor(timeoutMs?: number);
60
+ registerAdapter(adapter: AudioSource): void;
61
+ connect(channel: VoiceBasedChannel): Promise<void>;
62
+ disconnect(): void;
63
+ play(trackOrTracks: Track | Track[]): Promise<void>;
64
+ private processQueue;
65
+ pause(): void;
66
+ resume(): void;
67
+ skip(): void;
68
+ stop(): void;
69
+ setVolume(volume: number): void;
70
+ setLoopMode(mode: LoopMode): void;
71
+ getQueue(): Track[];
72
+ getCurrentTrack(): Track | null;
73
+ }
74
+ interface Player {
75
+ on<K extends keyof PlayerEvents>(event: K, listener: PlayerEvents[K]): this;
76
+ once<K extends keyof PlayerEvents>(event: K, listener: PlayerEvents[K]): this;
77
+ emit<K extends keyof PlayerEvents>(event: K, ...args: Parameters<PlayerEvents[K]>): boolean;
78
+ }
79
+
80
+ declare class LocalFileAdapter implements AudioSource {
81
+ name: string;
82
+ getTrack(query: string): Promise<Track | null>;
83
+ getStream(track: Track): Promise<fs.ReadStream>;
84
+ }
85
+
86
+ declare class DirectUrlAdapter implements AudioSource {
87
+ name: string;
88
+ getTrack(query: string): Promise<Track | null>;
89
+ getStream(track: Track): Promise<string>;
90
+ }
91
+
92
+ declare class JamendoAdapter implements AudioSource {
93
+ name: string;
94
+ private clientId;
95
+ constructor(clientId: string);
96
+ getTrack(query: string): Promise<Track | Track[] | null>;
97
+ private fetchTrackById;
98
+ private searchTrack;
99
+ getStream(track: Track): Promise<string>;
100
+ }
101
+
102
+ declare class SoundCloudAdapter implements AudioSource {
103
+ name: string;
104
+ private clientId;
105
+ constructor(clientId: string);
106
+ getTrack(query: string): Promise<Track | null>;
107
+ private mapTrack;
108
+ getStream(track: Track): Promise<any>;
109
+ }
110
+
111
+ export { type AudioSource, DirectUrlAdapter, JamendoAdapter, LocalFileAdapter, LoopMode, Player, type PlayerEvents, SoundCloudAdapter, type Track, VoiceConnectionManager };