streamify-audio 2.3.0 → 2.3.2
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/index.d.ts +9 -0
- package/package.json +1 -1
- package/src/config.js +11 -0
- package/src/discord/Manager.js +1 -1
- package/src/discord/Stream.js +9 -12
- package/src/providers/spotify.js +2 -2
- package/docs/README.md +0 -31
- package/docs/automation.md +0 -186
- package/docs/configuration.md +0 -198
- package/docs/discord/events.md +0 -206
- package/docs/discord/manager.md +0 -195
- package/docs/discord/player.md +0 -263
- package/docs/discord/queue.md +0 -197
- package/docs/examples/advanced-bot.md +0 -391
- package/docs/examples/basic-bot.md +0 -182
- package/docs/examples/lavalink.md +0 -156
- package/docs/filters.md +0 -347
- package/docs/http/endpoints.md +0 -224
- package/docs/http/server.md +0 -174
- package/docs/plans/2026-02-22-stream-revamp-design.md +0 -88
- package/docs/plans/2026-02-22-stream-revamp-plan.md +0 -814
- package/docs/quick-start.md +0 -92
- package/docs/sources.md +0 -189
- package/docs/sponsorblock.md +0 -95
- package/tests/cache.test.js +0 -234
- package/tests/config.test.js +0 -44
- package/tests/error-handling.test.js +0 -318
- package/tests/ffmpeg.test.js +0 -66
- package/tests/filters-edge.test.js +0 -333
- package/tests/http.test.js +0 -24
- package/tests/integration.test.js +0 -325
- package/tests/local.test.js +0 -37
- package/tests/queue.test.js +0 -94
- package/tests/spotify.test.js +0 -238
- package/tests/stream.test.js +0 -217
- package/tests/twitch.test.js +0 -42
- package/tests/utils.test.js +0 -60
- package/tests/youtube.test.js +0 -219
- package/youtube-cookies.txt +0 -26
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
# Basic Discord Bot
|
|
2
|
-
|
|
3
|
-
A simple music bot with play, skip, and stop commands.
|
|
4
|
-
|
|
5
|
-
```javascript
|
|
6
|
-
const { Client, GatewayIntentBits } = require('discord.js');
|
|
7
|
-
const Streamify = require('streamify-audio');
|
|
8
|
-
|
|
9
|
-
const client = new Client({
|
|
10
|
-
intents: [
|
|
11
|
-
GatewayIntentBits.Guilds,
|
|
12
|
-
GatewayIntentBits.GuildVoiceStates,
|
|
13
|
-
GatewayIntentBits.GuildMessages,
|
|
14
|
-
GatewayIntentBits.MessageContent
|
|
15
|
-
]
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const manager = new Streamify.Manager(client, {
|
|
19
|
-
ytdlpPath: '/usr/local/bin/yt-dlp',
|
|
20
|
-
ffmpegPath: '/usr/bin/ffmpeg',
|
|
21
|
-
cookiesPath: './cookies.txt',
|
|
22
|
-
spotify: {
|
|
23
|
-
clientId: process.env.SPOTIFY_CLIENT_ID,
|
|
24
|
-
clientSecret: process.env.SPOTIFY_CLIENT_SECRET
|
|
25
|
-
},
|
|
26
|
-
defaultVolume: 80
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
client.on('ready', () => {
|
|
30
|
-
console.log(`Logged in as ${client.user.tag}`);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
client.on('messageCreate', async (message) => {
|
|
34
|
-
if (message.author.bot) return;
|
|
35
|
-
if (!message.content.startsWith('!')) return;
|
|
36
|
-
|
|
37
|
-
const [command, ...args] = message.content.slice(1).split(' ');
|
|
38
|
-
const query = args.join(' ');
|
|
39
|
-
|
|
40
|
-
const player = manager.get(message.guild.id);
|
|
41
|
-
|
|
42
|
-
switch (command) {
|
|
43
|
-
case 'play':
|
|
44
|
-
case 'p': {
|
|
45
|
-
if (!query) {
|
|
46
|
-
return message.reply('Please provide a search query or URL.');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const voiceChannel = message.member.voice.channel;
|
|
50
|
-
if (!voiceChannel) {
|
|
51
|
-
return message.reply('You need to be in a voice channel.');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const result = await manager.resolve(query);
|
|
56
|
-
|
|
57
|
-
if (!result.tracks.length) {
|
|
58
|
-
return message.reply('No results found.');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const track = result.tracks[0];
|
|
62
|
-
track.requestedBy = message.author;
|
|
63
|
-
|
|
64
|
-
let p = player;
|
|
65
|
-
if (!p) {
|
|
66
|
-
p = await manager.create(
|
|
67
|
-
message.guild.id,
|
|
68
|
-
voiceChannel.id,
|
|
69
|
-
message.channel.id
|
|
70
|
-
);
|
|
71
|
-
setupEvents(p, message.channel);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
await p.play(track);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
console.error(error);
|
|
77
|
-
message.reply(`Error: ${error.message}`);
|
|
78
|
-
}
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
case 'skip':
|
|
83
|
-
case 's': {
|
|
84
|
-
if (!player?.playing) {
|
|
85
|
-
return message.reply('Nothing is playing.');
|
|
86
|
-
}
|
|
87
|
-
await player.skip();
|
|
88
|
-
message.react('⏭️');
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
case 'stop': {
|
|
93
|
-
if (!player) {
|
|
94
|
-
return message.reply('No active player.');
|
|
95
|
-
}
|
|
96
|
-
player.stop();
|
|
97
|
-
message.react('⏹️');
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
case 'pause': {
|
|
102
|
-
if (!player?.playing) {
|
|
103
|
-
return message.reply('Nothing is playing.');
|
|
104
|
-
}
|
|
105
|
-
player.pause();
|
|
106
|
-
message.react('⏸️');
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
case 'resume': {
|
|
111
|
-
if (!player?.paused) {
|
|
112
|
-
return message.reply('Not paused.');
|
|
113
|
-
}
|
|
114
|
-
await player.resume();
|
|
115
|
-
message.react('▶️');
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
case 'leave':
|
|
120
|
-
case 'disconnect':
|
|
121
|
-
case 'dc': {
|
|
122
|
-
if (!player) {
|
|
123
|
-
return message.reply('Not in a voice channel.');
|
|
124
|
-
}
|
|
125
|
-
player.destroy();
|
|
126
|
-
message.react('👋');
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
case 'np':
|
|
131
|
-
case 'nowplaying': {
|
|
132
|
-
if (!player?.queue.current) {
|
|
133
|
-
return message.reply('Nothing is playing.');
|
|
134
|
-
}
|
|
135
|
-
const track = player.queue.current;
|
|
136
|
-
const position = Math.floor(player.position / 1000);
|
|
137
|
-
message.reply(`Now playing: **${track.title}** [${position}s / ${track.duration}s]`);
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
function setupEvents(player, channel) {
|
|
144
|
-
player.on('trackStart', (track) => {
|
|
145
|
-
channel.send(`🎵 Now playing: **${track.title}**`);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
player.on('trackError', (track, error) => {
|
|
149
|
-
channel.send(`❌ Error playing **${track.title}**: ${error.message}`);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
player.on('queueEnd', () => {
|
|
153
|
-
channel.send('📭 Queue finished.');
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
client.login(process.env.DISCORD_TOKEN);
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Running
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
# Set environment variables
|
|
164
|
-
export DISCORD_TOKEN=your_token
|
|
165
|
-
export SPOTIFY_CLIENT_ID=your_id
|
|
166
|
-
export SPOTIFY_CLIENT_SECRET=your_secret
|
|
167
|
-
|
|
168
|
-
# Run
|
|
169
|
-
node bot.js
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Commands
|
|
173
|
-
|
|
174
|
-
| Command | Description |
|
|
175
|
-
|---------|-------------|
|
|
176
|
-
| `!play <query>` | Play a song |
|
|
177
|
-
| `!skip` | Skip current song |
|
|
178
|
-
| `!stop` | Stop and clear queue |
|
|
179
|
-
| `!pause` | Pause playback |
|
|
180
|
-
| `!resume` | Resume playback |
|
|
181
|
-
| `!leave` | Disconnect |
|
|
182
|
-
| `!np` | Now playing |
|
|
@@ -1,156 +0,0 @@
|
|
|
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.
|
package/docs/filters.md
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
# Audio Filters
|
|
2
|
-
|
|
3
|
-
Filters are applied in real-time via ffmpeg. When you change a filter during playback, Streamify uses **Seamless Transitions** to prepare the new stream in the background and swap it instantly once ready, ensuring no audio gaps.
|
|
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
|
-
## Effect Presets (Stacked)
|
|
291
|
-
|
|
292
|
-
Streamify supports "Effect Presets" which can be stacked on top of each other. These are more powerful than standard filters because they can combine multiple FFmpeg settings.
|
|
293
|
-
|
|
294
|
-
### Available Effect Presets
|
|
295
|
-
|
|
296
|
-
| Preset | Description |
|
|
297
|
-
|--------|-------------|
|
|
298
|
-
| `bassboost` | Strong low-end boost |
|
|
299
|
-
| `subboost` | Extreme sub-woofer boost |
|
|
300
|
-
| `nightcore` | High speed and pitch |
|
|
301
|
-
| `vaporwave` | Low speed and pitch |
|
|
302
|
-
| `reverb` | Adds room acoustics / echo |
|
|
303
|
-
| `surround` | Virtual surround sound mapping |
|
|
304
|
-
| `boost` | General volume and clarity boost |
|
|
305
|
-
| `karaoke` | Vocal reduction |
|
|
306
|
-
| `8d` | Fast circular panning |
|
|
307
|
-
|
|
308
|
-
### Usage
|
|
309
|
-
|
|
310
|
-
```javascript
|
|
311
|
-
// Set a single effect with intensity (0.0 to 2.0)
|
|
312
|
-
await player.setEffectPresets({ name: 'reverb', intensity: 0.8 });
|
|
313
|
-
|
|
314
|
-
// Stack multiple effects
|
|
315
|
-
await player.setEffectPresets([
|
|
316
|
-
{ name: 'bassboost', intensity: 1.2 },
|
|
317
|
-
{ name: 'nightcore', intensity: 1.0 }
|
|
318
|
-
]);
|
|
319
|
-
|
|
320
|
-
// Clear all effects
|
|
321
|
-
await player.clearEffectPresets();
|
|
322
|
-
|
|
323
|
-
// Get active effects
|
|
324
|
-
const active = player.getActiveEffectPresets();
|
|
325
|
-
// [{ name: 'reverb', intensity: 0.8 }]
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
## Additional Effects
|
|
329
|
-
|
|
330
|
-
| Effect | Description |
|
|
331
|
-
|--------|-------------|
|
|
332
|
-
| `flanger` | Sweeping comb filter effect |
|
|
333
|
-
| `phaser` | Phase-shifting effect |
|
|
334
|
-
| `chorus` | Thickens sound with slight detuning |
|
|
335
|
-
| `compressor` | Dynamic range compression |
|
|
336
|
-
| `normalizer` | Loudness normalization |
|
|
337
|
-
| `mono` | Convert stereo to mono |
|
|
338
|
-
| `surround` | Surround sound effect |
|
|
339
|
-
|
|
340
|
-
```javascript
|
|
341
|
-
await player.setFilter('flanger', true);
|
|
342
|
-
await player.setFilter('phaser', true);
|
|
343
|
-
await player.setFilter('chorus', true);
|
|
344
|
-
await player.setFilter('compressor', true);
|
|
345
|
-
await player.setFilter('normalizer', true);
|
|
346
|
-
await player.setFilter('mono', true);
|
|
347
|
-
```
|