@jubbio/voice 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) 2024 Jubbio Team
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,257 @@
1
+ <p align="center">
2
+ <img src="https://jubbio.com/logo.png" alt="Jubbio" width="120" />
3
+ </p>
4
+
5
+ <h1 align="center">@jubbio/voice</h1>
6
+
7
+ <p align="center">
8
+ <strong>Voice library for Jubbio bots</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@jubbio/voice"><img src="https://img.shields.io/npm/v/@jubbio/voice?color=blue" alt="npm"></a>
13
+ <a href="https://github.com/jubbio/jubbio.js/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
14
+ <img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen" alt="Node.js">
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @jubbio/voice
23
+ ```
24
+
25
+ ## Features
26
+
27
+ - 🎵 **Audio Playback** - Play local files or stream from URLs
28
+ - 📺 **YouTube Support** - Built-in yt-dlp integration
29
+ - 🔊 **LiveKit Backend** - High-quality, low-latency audio
30
+ - ⏯️ **Playback Controls** - Play, pause, stop, volume
31
+ - 📊 **Easy Queue Management** - Build music bots easily
32
+
33
+ ## Quick Start
34
+
35
+ ```typescript
36
+ import { Client, GatewayIntentBits } from '@jubbio/core';
37
+ import {
38
+ joinVoiceChannel,
39
+ createAudioPlayer,
40
+ createAudioResourceFromUrl,
41
+ AudioPlayerStatus
42
+ } from '@jubbio/voice';
43
+
44
+ const client = new Client({
45
+ intents: [
46
+ GatewayIntentBits.Guilds,
47
+ GatewayIntentBits.GuildVoiceStates
48
+ ]
49
+ });
50
+
51
+ client.on('interactionCreate', async (interaction) => {
52
+ if (!interaction.isCommand()) return;
53
+
54
+ if (interaction.commandName === 'play') {
55
+ const url = interaction.options.getString('url', true);
56
+ const voiceChannel = interaction.member?.voice?.channelId;
57
+
58
+ if (!voiceChannel) {
59
+ return interaction.reply('❌ Join a voice channel first!');
60
+ }
61
+
62
+ // Join voice channel
63
+ const connection = joinVoiceChannel({
64
+ channelId: voiceChannel,
65
+ guildId: interaction.guildId!,
66
+ adapterCreator: client.voice.adapters.get(interaction.guildId!)!
67
+ });
68
+
69
+ // Create player and play
70
+ const player = createAudioPlayer();
71
+ const resource = createAudioResourceFromUrl(url);
72
+
73
+ connection.subscribe(player);
74
+ player.play(resource);
75
+
76
+ // Handle playback end
77
+ player.on('stateChange', (oldState, newState) => {
78
+ if (newState.status === AudioPlayerStatus.Idle) {
79
+ console.log('Playback finished!');
80
+ }
81
+ });
82
+
83
+ await interaction.reply('🎵 Now playing!');
84
+ }
85
+ });
86
+
87
+ client.login(process.env.BOT_TOKEN);
88
+ ```
89
+
90
+ ## API Reference
91
+
92
+ ### joinVoiceChannel(options)
93
+
94
+ Join a voice channel and return a `VoiceConnection`.
95
+
96
+ ```typescript
97
+ const connection = joinVoiceChannel({
98
+ channelId: '123456789', // Voice channel ID
99
+ guildId: '987654321', // Guild ID
100
+ adapterCreator: adapter, // From client.voice.adapters
101
+ selfMute: false, // Join muted (default: false)
102
+ selfDeaf: false // Join deafened (default: false)
103
+ });
104
+ ```
105
+
106
+ ### createAudioPlayer(options?)
107
+
108
+ Create an `AudioPlayer` instance.
109
+
110
+ ```typescript
111
+ const player = createAudioPlayer({
112
+ behaviors: {
113
+ noSubscriber: 'pause' // 'pause' | 'play' | 'stop'
114
+ }
115
+ });
116
+ ```
117
+
118
+ ### createAudioResource(input, options?)
119
+
120
+ Create an `AudioResource` from a file path.
121
+
122
+ ```typescript
123
+ const resource = createAudioResource('/path/to/audio.mp3', {
124
+ metadata: { title: 'My Song' }
125
+ });
126
+ ```
127
+
128
+ ### createAudioResourceFromUrl(url, options?)
129
+
130
+ Create an `AudioResource` from a URL (YouTube, SoundCloud, etc.).
131
+
132
+ ```typescript
133
+ const resource = createAudioResourceFromUrl('https://youtube.com/watch?v=...', {
134
+ metadata: { title: 'YouTube Video' }
135
+ });
136
+ ```
137
+
138
+ ### probeAudioInfo(url)
139
+
140
+ Get information about an audio URL.
141
+
142
+ ```typescript
143
+ const info = await probeAudioInfo('https://youtube.com/watch?v=...');
144
+ console.log(info.title); // Video title
145
+ console.log(info.duration); // Duration in seconds
146
+ console.log(info.thumbnail); // Thumbnail URL
147
+ ```
148
+
149
+ ## Player Controls
150
+
151
+ ```typescript
152
+ // Play
153
+ player.play(resource);
154
+
155
+ // Pause
156
+ player.pause();
157
+
158
+ // Resume
159
+ player.unpause();
160
+
161
+ // Stop
162
+ player.stop();
163
+
164
+ // Check status
165
+ if (player.state.status === AudioPlayerStatus.Playing) {
166
+ console.log('Currently playing!');
167
+ }
168
+ ```
169
+
170
+ ## Connection Controls
171
+
172
+ ```typescript
173
+ // Disconnect
174
+ connection.disconnect();
175
+
176
+ // Destroy (cleanup)
177
+ connection.destroy();
178
+
179
+ // Check status
180
+ if (connection.state.status === VoiceConnectionStatus.Ready) {
181
+ console.log('Connected!');
182
+ }
183
+ ```
184
+
185
+ ## Events
186
+
187
+ ### AudioPlayer Events
188
+
189
+ ```typescript
190
+ player.on('stateChange', (oldState, newState) => {
191
+ console.log(`${oldState.status} -> ${newState.status}`);
192
+ });
193
+
194
+ player.on('error', (error) => {
195
+ console.error('Player error:', error);
196
+ });
197
+ ```
198
+
199
+ ### VoiceConnection Events
200
+
201
+ ```typescript
202
+ connection.on('stateChange', (oldState, newState) => {
203
+ if (newState.status === VoiceConnectionStatus.Disconnected) {
204
+ console.log('Disconnected from voice channel');
205
+ }
206
+ });
207
+
208
+ connection.on('error', (error) => {
209
+ console.error('Connection error:', error);
210
+ });
211
+ ```
212
+
213
+ ## Status Enums
214
+
215
+ ### AudioPlayerStatus
216
+
217
+ | Status | Description |
218
+ |--------|-------------|
219
+ | `Idle` | Not playing anything |
220
+ | `Buffering` | Loading audio |
221
+ | `Playing` | Currently playing |
222
+ | `Paused` | Playback paused |
223
+ | `AutoPaused` | Paused (no subscribers) |
224
+
225
+ ### VoiceConnectionStatus
226
+
227
+ | Status | Description |
228
+ |--------|-------------|
229
+ | `Connecting` | Establishing connection |
230
+ | `Ready` | Connected and ready |
231
+ | `Disconnected` | Disconnected |
232
+ | `Destroyed` | Connection destroyed |
233
+ | `Signalling` | Exchanging connection info |
234
+
235
+ ## Requirements
236
+
237
+ - **Node.js** 18.0.0 or higher
238
+ - **FFmpeg** installed and in PATH
239
+ - **yt-dlp** installed (for YouTube support)
240
+
241
+ ### Installing Dependencies
242
+
243
+ ```bash
244
+ # Ubuntu/Debian
245
+ sudo apt install ffmpeg
246
+ pip install yt-dlp
247
+
248
+ # macOS
249
+ brew install ffmpeg yt-dlp
250
+
251
+ # Windows
252
+ # Download from ffmpeg.org and yt-dlp GitHub releases
253
+ ```
254
+
255
+ ## License
256
+
257
+ MIT © [Jubbio Team](https://jubbio.com)
@@ -0,0 +1,84 @@
1
+ import { EventEmitter } from 'events';
2
+ import { CreateAudioPlayerOptions, AudioPlayerState } from './types';
3
+ import { AudioResource } from './AudioResource';
4
+ import { VoiceConnection } from './VoiceConnection';
5
+ /**
6
+ * Audio player for playing audio resources
7
+ */
8
+ export declare class AudioPlayer extends EventEmitter {
9
+ /** Current player state */
10
+ state: AudioPlayerState;
11
+ /** Player options */
12
+ private options;
13
+ /** Subscribed voice connections */
14
+ private subscriptions;
15
+ /** Current audio resource */
16
+ private currentResource;
17
+ /** FFmpeg process */
18
+ private ffmpegProcess;
19
+ /** LiveKit audio source and track */
20
+ private audioSource;
21
+ private audioTrack;
22
+ /** Frame queue and playback state */
23
+ private frameQueue;
24
+ private playbackTimeout;
25
+ private leftoverBuffer;
26
+ private isPublished;
27
+ /** High-resolution timing */
28
+ private nextFrameTime;
29
+ private isPlaybackLoopRunning;
30
+ private ffmpegDone;
31
+ /** Buffer statistics */
32
+ private bufferUnderruns;
33
+ private framesPlayed;
34
+ constructor(options?: CreateAudioPlayerOptions);
35
+ /**
36
+ * Play an audio resource
37
+ */
38
+ play(resource: AudioResource): void;
39
+ /**
40
+ * Pause playback
41
+ */
42
+ pause(): boolean;
43
+ /**
44
+ * Unpause playback
45
+ */
46
+ unpause(): boolean;
47
+ /**
48
+ * Stop playback
49
+ */
50
+ stop(force?: boolean): boolean;
51
+ /**
52
+ * Subscribe a voice connection to this player
53
+ * @internal
54
+ */
55
+ subscribe(connection: VoiceConnection): void;
56
+ /**
57
+ * Unsubscribe a voice connection from this player
58
+ * @internal
59
+ */
60
+ unsubscribe(connection: VoiceConnection): void;
61
+ /**
62
+ * Called when a connection becomes ready
63
+ * @internal
64
+ */
65
+ onConnectionReady(connection: VoiceConnection): void;
66
+ private startPlayback;
67
+ private setupAudioTrack;
68
+ private startFFmpeg;
69
+ /**
70
+ * High-resolution frame scheduling using hrtime
71
+ * This provides much more accurate timing than setInterval
72
+ */
73
+ private scheduleNextFrame;
74
+ /**
75
+ * Process and send a single audio frame
76
+ */
77
+ private processFrame;
78
+ private cleanup;
79
+ private setState;
80
+ }
81
+ /**
82
+ * Create an audio player
83
+ */
84
+ export declare function createAudioPlayer(options?: CreateAudioPlayerOptions): AudioPlayer;