streamify-audio 2.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.
@@ -0,0 +1,156 @@
1
+ # HTTP Server with Lavalink
2
+
3
+ Use Streamify's HTTP server as a stream source for Lavalink.
4
+
5
+ ## Why?
6
+
7
+ - Lavalink's YouTube plugin can be unreliable
8
+ - Streamify uses yt-dlp which handles YouTube's restrictions better
9
+ - Get audio filters without Lavalink's filter support
10
+
11
+ ## Setup
12
+
13
+ ### 1. Start Streamify Server
14
+
15
+ ```javascript
16
+ // streamify-server.js
17
+ const Streamify = require('streamify-audio');
18
+
19
+ const streamify = new Streamify({
20
+ port: 8787,
21
+ cookiesPath: './cookies.txt',
22
+ spotify: {
23
+ clientId: process.env.SPOTIFY_CLIENT_ID,
24
+ clientSecret: process.env.SPOTIFY_CLIENT_SECRET
25
+ }
26
+ });
27
+
28
+ streamify.start().then(() => {
29
+ console.log('Streamify running at http://127.0.0.1:8787');
30
+ });
31
+ ```
32
+
33
+ ### 2. Configure Lavalink
34
+
35
+ In `application.yml`, allow local addresses:
36
+
37
+ ```yaml
38
+ lavalink:
39
+ server:
40
+ sources:
41
+ http: true
42
+ ```
43
+
44
+ ### 3. Bot Integration
45
+
46
+ ```javascript
47
+ const { Client } = require('discord.js');
48
+ const { Manager } = require('erela.js'); // or your Lavalink client
49
+
50
+ const client = new Client({ /* intents */ });
51
+
52
+ // Initialize Streamify client
53
+ const Streamify = require('streamify-audio');
54
+ const streamify = new Streamify({ port: 8787 });
55
+
56
+ // Initialize Lavalink
57
+ const lavalinkManager = new Manager({
58
+ nodes: [{ host: 'localhost', port: 2333, password: 'youshallnotpass' }],
59
+ send: (id, payload) => {
60
+ const guild = client.guilds.cache.get(id);
61
+ if (guild) guild.shard.send(payload);
62
+ }
63
+ });
64
+
65
+ client.on('ready', async () => {
66
+ await streamify.start();
67
+ lavalinkManager.init(client.user.id);
68
+ });
69
+
70
+ client.on('raw', d => lavalinkManager.updateVoiceState(d));
71
+
72
+ // Play command
73
+ client.on('messageCreate', async (message) => {
74
+ if (!message.content.startsWith('!play')) return;
75
+
76
+ const query = message.content.slice(6);
77
+ const vc = message.member.voice.channel;
78
+
79
+ // Search using Streamify
80
+ const results = await streamify.youtube.search(query);
81
+ if (!results.tracks.length) return message.reply('No results');
82
+
83
+ const track = results.tracks[0];
84
+
85
+ // Get stream URL from Streamify
86
+ const streamUrl = streamify.youtube.getStreamUrl(track.id, {
87
+ bass: 5 // Optional: apply filters
88
+ });
89
+
90
+ // Create Lavalink player
91
+ const player = lavalinkManager.create({
92
+ guild: message.guild.id,
93
+ voiceChannel: vc.id,
94
+ textChannel: message.channel.id
95
+ });
96
+
97
+ if (player.state !== 'CONNECTED') {
98
+ player.connect();
99
+ }
100
+
101
+ // Load Streamify URL in Lavalink
102
+ const res = await lavalinkManager.search(streamUrl, message.author);
103
+ player.queue.add(res.tracks[0]);
104
+
105
+ if (!player.playing) player.play();
106
+
107
+ message.reply(`Playing: **${track.title}**`);
108
+ });
109
+
110
+ client.login(process.env.TOKEN);
111
+ ```
112
+
113
+ ## With Filters
114
+
115
+ ```javascript
116
+ // Apply filters via Streamify URL
117
+ const streamUrl = streamify.youtube.getStreamUrl(trackId, {
118
+ bass: 10,
119
+ nightcore: true,
120
+ volume: 80
121
+ });
122
+
123
+ // Lavalink plays the already-filtered audio
124
+ const res = await lavalinkManager.search(streamUrl, user);
125
+ ```
126
+
127
+ ## Changing Filters Mid-Song
128
+
129
+ ```javascript
130
+ // Get current position from Streamify's stream tracking
131
+ const position = await streamify.getPosition(currentStreamId);
132
+
133
+ // Create new URL with filters + position
134
+ const newUrl = streamify.youtube.getStreamUrl(trackId, {
135
+ bass: 10,
136
+ nightcore: true,
137
+ start: Math.floor(position) // Seek to current position
138
+ });
139
+
140
+ // Tell Lavalink to switch
141
+ const res = await lavalinkManager.search(newUrl, user);
142
+ player.play(res.tracks[0]);
143
+ // Seamless transition with new filters
144
+ ```
145
+
146
+ ## Benefits
147
+
148
+ 1. **Reliable extraction** - yt-dlp handles YouTube's anti-bot
149
+ 2. **Cookie support** - Play age-restricted content
150
+ 3. **Pre-applied filters** - Don't need Lavalink filter support
151
+ 4. **Spotify support** - Automatic YouTube resolution
152
+ 5. **Sponsorblock** - Skip sponsors before Lavalink receives audio
153
+
154
+ ## Note
155
+
156
+ This approach adds one network hop (Lavalink fetches from Streamify). For direct playback without Lavalink, use Streamify's [Discord Player mode](../discord/manager.md) instead.
@@ -0,0 +1,309 @@
1
+ # Audio Filters
2
+
3
+ Filters are applied in real-time via ffmpeg. When you change a filter during playback, Streamify recreates the stream and seeks to the current position.
4
+
5
+ ## Available Filters
6
+
7
+ | Filter | Type | Range | Description |
8
+ |--------|------|-------|-------------|
9
+ | `bass` | number | -20 to 20 | Bass boost/cut in dB |
10
+ | `treble` | number | -20 to 20 | Treble boost/cut in dB |
11
+ | `speed` | number | 0.5 to 2.0 | Playback speed |
12
+ | `pitch` | number | 0.5 to 2.0 | Pitch shift |
13
+ | `volume` | number | 0 to 200 | Volume percentage |
14
+ | `tremolo` | object | see below | Volume oscillation |
15
+ | `vibrato` | object | see below | Pitch oscillation |
16
+ | `rotation` | object | see below | Audio rotation (8D) |
17
+ | `lowpass` | number | 100 to 20000 | Low-pass filter (Hz) |
18
+ | `highpass` | number | 20 to 10000 | High-pass filter (Hz) |
19
+ | `karaoke` | boolean | — | Reduce vocals |
20
+ | `nightcore` | boolean | — | Speed + pitch up preset |
21
+ | `vaporwave` | boolean | — | Speed + pitch down preset |
22
+ | `bassboost` | boolean | — | Strong bass boost preset |
23
+ | `8d` | boolean | — | 8D panning effect |
24
+
25
+ ## Usage (Discord Player)
26
+
27
+ ```javascript
28
+ // Set individual filters
29
+ await player.setFilter('bass', 10);
30
+ await player.setFilter('treble', 5);
31
+ await player.setFilter('speed', 1.25);
32
+ await player.setFilter('volume', 120);
33
+
34
+ // Object filters
35
+ await player.setFilter('tremolo', { frequency: 4, depth: 0.5 });
36
+ await player.setFilter('vibrato', { frequency: 4, depth: 0.5 });
37
+ await player.setFilter('rotation', { speed: 0.125 });
38
+
39
+ // Frequency filters
40
+ await player.setFilter('lowpass', 1000); // Cut above 1000Hz
41
+ await player.setFilter('highpass', 200); // Cut below 200Hz
42
+
43
+ // Presets (boolean)
44
+ await player.setFilter('nightcore', true);
45
+ await player.setFilter('vaporwave', true);
46
+ await player.setFilter('bassboost', true);
47
+ await player.setFilter('karaoke', true);
48
+ await player.setFilter('8d', true);
49
+
50
+ // Clear all filters
51
+ await player.clearFilters();
52
+
53
+ // Get current filters
54
+ console.log(player.filters);
55
+ // { bass: 10, nightcore: true, ... }
56
+ ```
57
+
58
+ ## Usage (HTTP Server)
59
+
60
+ ```javascript
61
+ // Add filters as query parameters
62
+ const url = streamify.youtube.getStreamUrl('dQw4w9WgXcQ', {
63
+ bass: 10,
64
+ speed: 1.25,
65
+ nightcore: true
66
+ });
67
+ // http://127.0.0.1:8787/youtube/stream/dQw4w9WgXcQ?bass=10&speed=1.25&nightcore=true
68
+ ```
69
+
70
+ ## Filter Details
71
+
72
+ ### Tremolo
73
+ Volume oscillation effect.
74
+
75
+ ```javascript
76
+ await player.setFilter('tremolo', {
77
+ frequency: 4, // 0.1 to 20 Hz
78
+ depth: 0.5 // 0 to 1
79
+ });
80
+ ```
81
+
82
+ ### Vibrato
83
+ Pitch oscillation effect.
84
+
85
+ ```javascript
86
+ await player.setFilter('vibrato', {
87
+ frequency: 4, // 0.1 to 14 Hz
88
+ depth: 0.5 // 0 to 1
89
+ });
90
+ ```
91
+
92
+ ### Rotation
93
+ Advanced 8D effect with configurable speed.
94
+
95
+ ```javascript
96
+ await player.setFilter('rotation', {
97
+ speed: 0.125 // 0.01 to 5 rotations per second
98
+ });
99
+ ```
100
+
101
+ ### Karaoke
102
+ Reduces vocals by removing the center channel. Works best on stereo tracks with centered vocals.
103
+
104
+ ```javascript
105
+ await player.setFilter('karaoke', true);
106
+ ```
107
+
108
+ ## Presets
109
+
110
+ ### Nightcore
111
+ Speeds up and raises pitch for that anime soundtrack feel.
112
+
113
+ - Speed: 1.25x
114
+ - Pitch: 1.25x
115
+
116
+ ### Vaporwave
117
+ Slows down and lowers pitch for that aesthetic.
118
+
119
+ - Speed: 0.8x
120
+ - Pitch: 0.8x
121
+
122
+ ### Bassboost
123
+ Strong bass boost (+10dB).
124
+
125
+ ### 8D
126
+ Audio pans around in a circle. Same as `rotation` with speed 0.125.
127
+
128
+ ## Combining Filters
129
+
130
+ Filters can be combined:
131
+
132
+ ```javascript
133
+ await player.setFilter('bass', 10);
134
+ await player.setFilter('speed', 1.25);
135
+ await player.setFilter('8d', true);
136
+ // All three active simultaneously
137
+ ```
138
+
139
+ Note: `nightcore` and `vaporwave` both modify speed/pitch, so using both may produce unexpected results.
140
+
141
+ ## 15-Band Equalizer
142
+
143
+ Fine-grained control over frequency response with 15 bands.
144
+
145
+ ### Bands
146
+
147
+ | Band | Frequency |
148
+ |------|-----------|
149
+ | 0 | 25 Hz |
150
+ | 1 | 40 Hz |
151
+ | 2 | 63 Hz |
152
+ | 3 | 100 Hz |
153
+ | 4 | 160 Hz |
154
+ | 5 | 250 Hz |
155
+ | 6 | 400 Hz |
156
+ | 7 | 630 Hz |
157
+ | 8 | 1000 Hz |
158
+ | 9 | 1600 Hz |
159
+ | 10 | 2500 Hz |
160
+ | 11 | 4000 Hz |
161
+ | 12 | 6300 Hz |
162
+ | 13 | 10000 Hz |
163
+ | 14 | 16000 Hz |
164
+
165
+ ### Usage
166
+
167
+ ```javascript
168
+ // Set custom EQ (15 band values, -0.25 to 1.0)
169
+ await player.setEQ([
170
+ 0.3, // 25 Hz (sub bass)
171
+ 0.25, // 40 Hz
172
+ 0.2, // 63 Hz
173
+ 0.1, // 100 Hz
174
+ 0, // 160 Hz
175
+ -0.1, // 250 Hz
176
+ 0, // 400 Hz
177
+ 0.1, // 630 Hz
178
+ 0.2, // 1000 Hz
179
+ 0.25, // 1600 Hz
180
+ 0.3, // 2500 Hz
181
+ 0.3, // 4000 Hz
182
+ 0.25, // 6300 Hz
183
+ 0.2, // 10000 Hz
184
+ 0.15 // 16000 Hz
185
+ ]);
186
+
187
+ // Clear EQ
188
+ await player.clearEQ();
189
+ ```
190
+
191
+ ## EQ Presets
192
+
193
+ Built-in presets for common genres and use cases.
194
+
195
+ ### Available Presets
196
+
197
+ | Preset | Description |
198
+ |--------|-------------|
199
+ | `flat` | No EQ changes |
200
+ | `rock` | Enhanced mids and highs |
201
+ | `pop` | Balanced with slight bass |
202
+ | `jazz` | Warm, smooth sound |
203
+ | `classical` | Wide, natural sound |
204
+ | `electronic` | Heavy bass, crisp highs |
205
+ | `hiphop` | Deep bass, clear vocals |
206
+ | `acoustic` | Natural, warm |
207
+ | `rnb` | Smooth bass, warm mids |
208
+ | `latin` | Punchy, rhythmic |
209
+ | `loudness` | Overall boost |
210
+ | `piano` | Clear mids |
211
+ | `vocal` | Enhanced vocal range |
212
+ | `bass_heavy` | Maximum bass |
213
+ | `treble_heavy` | Maximum highs |
214
+
215
+ ### Usage
216
+
217
+ ```javascript
218
+ // Apply preset
219
+ await player.setPreset('rock');
220
+ await player.setPreset('electronic');
221
+
222
+ // List available presets
223
+ const presets = player.getPresets();
224
+ // ['flat', 'rock', 'pop', ...]
225
+
226
+ // Clear preset
227
+ await player.clearEQ();
228
+ ```
229
+
230
+ ## Biquad Filters
231
+
232
+ Professional-grade frequency filters.
233
+
234
+ ### Bandpass
235
+
236
+ Only allows frequencies within a range.
237
+
238
+ ```javascript
239
+ await player.setFilter('bandpass', {
240
+ frequency: 1000, // Center frequency (Hz)
241
+ width: 200 // Bandwidth (Hz)
242
+ });
243
+ ```
244
+
245
+ ### Band Reject (Notch)
246
+
247
+ Removes frequencies within a range.
248
+
249
+ ```javascript
250
+ await player.setFilter('bandreject', {
251
+ frequency: 1000,
252
+ width: 200
253
+ });
254
+ ```
255
+
256
+ ### Low Shelf
257
+
258
+ Boosts or cuts frequencies below a point.
259
+
260
+ ```javascript
261
+ await player.setFilter('lowshelf', {
262
+ frequency: 200, // Cutoff frequency (Hz)
263
+ gain: 6 // dB (-20 to 20)
264
+ });
265
+ ```
266
+
267
+ ### High Shelf
268
+
269
+ Boosts or cuts frequencies above a point.
270
+
271
+ ```javascript
272
+ await player.setFilter('highshelf', {
273
+ frequency: 3000,
274
+ gain: 6
275
+ });
276
+ ```
277
+
278
+ ### Peaking EQ
279
+
280
+ Boosts or cuts a specific frequency range.
281
+
282
+ ```javascript
283
+ await player.setFilter('peaking', {
284
+ frequency: 1000, // Center frequency
285
+ gain: 6, // dB
286
+ q: 1 // Width (higher = narrower)
287
+ });
288
+ ```
289
+
290
+ ## Additional Effects
291
+
292
+ | Effect | Description |
293
+ |--------|-------------|
294
+ | `flanger` | Sweeping comb filter effect |
295
+ | `phaser` | Phase-shifting effect |
296
+ | `chorus` | Thickens sound with slight detuning |
297
+ | `compressor` | Dynamic range compression |
298
+ | `normalizer` | Loudness normalization |
299
+ | `mono` | Convert stereo to mono |
300
+ | `surround` | Surround sound effect |
301
+
302
+ ```javascript
303
+ await player.setFilter('flanger', true);
304
+ await player.setFilter('phaser', true);
305
+ await player.setFilter('chorus', true);
306
+ await player.setFilter('compressor', true);
307
+ await player.setFilter('normalizer', true);
308
+ await player.setFilter('mono', true);
309
+ ```
@@ -0,0 +1,199 @@
1
+ # HTTP Endpoints
2
+
3
+ ## YouTube
4
+
5
+ ### Search
6
+
7
+ ```
8
+ GET /youtube/search?q=query&limit=10
9
+ ```
10
+
11
+ **Response:**
12
+ ```json
13
+ {
14
+ "tracks": [
15
+ {
16
+ "id": "dQw4w9WgXcQ",
17
+ "title": "Rick Astley - Never Gonna Give You Up",
18
+ "author": "Rick Astley",
19
+ "duration": 213,
20
+ "thumbnail": "https://...",
21
+ "uri": "https://youtube.com/watch?v=dQw4w9WgXcQ",
22
+ "streamUrl": "/youtube/stream/dQw4w9WgXcQ",
23
+ "source": "youtube"
24
+ }
25
+ ],
26
+ "source": "youtube",
27
+ "searchTime": 1234
28
+ }
29
+ ```
30
+
31
+ ### Get Info
32
+
33
+ ```
34
+ GET /youtube/info/:videoId
35
+ ```
36
+
37
+ **Response:**
38
+ ```json
39
+ {
40
+ "id": "dQw4w9WgXcQ",
41
+ "title": "Rick Astley - Never Gonna Give You Up",
42
+ "author": "Rick Astley",
43
+ "duration": 213,
44
+ "thumbnail": "https://...",
45
+ "uri": "https://youtube.com/watch?v=dQw4w9WgXcQ",
46
+ "streamUrl": "/youtube/stream/dQw4w9WgXcQ",
47
+ "source": "youtube"
48
+ }
49
+ ```
50
+
51
+ ### Stream
52
+
53
+ ```
54
+ GET /youtube/stream/:videoId
55
+ GET /youtube/stream/:videoId?bass=10&nightcore=true&start=30
56
+ ```
57
+
58
+ **Query Parameters:**
59
+ - All [filters](../filters.md) are supported
60
+ - `start` - Seek to position in seconds
61
+
62
+ **Response:** Audio stream (`audio/ogg`)
63
+
64
+ ## Spotify
65
+
66
+ ### Search
67
+
68
+ ```
69
+ GET /spotify/search?q=query&limit=10
70
+ ```
71
+
72
+ ### Get Info
73
+
74
+ ```
75
+ GET /spotify/info/:trackId
76
+ ```
77
+
78
+ ### Stream
79
+
80
+ ```
81
+ GET /spotify/stream/:trackId
82
+ GET /spotify/stream/:trackId?bass=10
83
+ ```
84
+
85
+ Spotify tracks are resolved to YouTube and streamed from there.
86
+
87
+ ## SoundCloud
88
+
89
+ ### Search
90
+
91
+ ```
92
+ GET /soundcloud/search?q=query&limit=10
93
+ ```
94
+
95
+ ### Stream
96
+
97
+ ```
98
+ GET /soundcloud/stream/:trackUrl
99
+ ```
100
+
101
+ Note: `trackUrl` should be URL-encoded.
102
+
103
+ ## Stream Management
104
+
105
+ ### List Active Streams
106
+
107
+ ```
108
+ GET /streams
109
+ ```
110
+
111
+ **Response:**
112
+ ```json
113
+ {
114
+ "streams": [
115
+ {
116
+ "id": "yt-dQw4w9WgXcQ-1234567890",
117
+ "source": "youtube",
118
+ "trackId": "dQw4w9WgXcQ",
119
+ "filters": { "bass": 10 },
120
+ "startTime": 1234567890,
121
+ "bytesReceived": 1048576,
122
+ "bytesSent": 524288
123
+ }
124
+ ],
125
+ "count": 1
126
+ }
127
+ ```
128
+
129
+ ### Get Stream Info
130
+
131
+ ```
132
+ GET /streams/:streamId
133
+ ```
134
+
135
+ **Response:**
136
+ ```json
137
+ {
138
+ "id": "yt-dQw4w9WgXcQ-1234567890",
139
+ "source": "youtube",
140
+ "trackId": "dQw4w9WgXcQ",
141
+ "filters": { "bass": 10 },
142
+ "startTime": 1234567890,
143
+ "elapsed": 45000,
144
+ "bytesReceived": 1048576,
145
+ "bytesSent": 524288
146
+ }
147
+ ```
148
+
149
+ ### Get Stream Position
150
+
151
+ ```
152
+ GET /streams/:streamId/position
153
+ ```
154
+
155
+ **Response:**
156
+ ```json
157
+ {
158
+ "position": 45.5,
159
+ "elapsed": 45000
160
+ }
161
+ ```
162
+
163
+ Position is in seconds, useful for seeking when applying filters.
164
+
165
+ ## Filter Parameters
166
+
167
+ All stream endpoints accept filter query parameters:
168
+
169
+ | Parameter | Type | Description |
170
+ |-----------|------|-------------|
171
+ | `bass` | number | -20 to 20 |
172
+ | `treble` | number | -20 to 20 |
173
+ | `speed` | number | 0.5 to 2.0 |
174
+ | `pitch` | number | 0.5 to 2.0 |
175
+ | `volume` | number | 0 to 200 |
176
+ | `nightcore` | boolean | true |
177
+ | `vaporwave` | boolean | true |
178
+ | `bassboost` | boolean | true |
179
+ | `8d` | boolean | true |
180
+ | `start` | number | Seek seconds |
181
+
182
+ **Example:**
183
+ ```
184
+ GET /youtube/stream/dQw4w9WgXcQ?bass=10&speed=1.25&nightcore=true&start=30
185
+ ```
186
+
187
+ ## Error Responses
188
+
189
+ ```json
190
+ {
191
+ "error": "Track not found",
192
+ "code": 404
193
+ }
194
+ ```
195
+
196
+ Common error codes:
197
+ - `400` - Invalid parameters
198
+ - `404` - Track/stream not found
199
+ - `500` - Internal error (yt-dlp/ffmpeg failure)