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
package/index.d.ts
CHANGED
|
@@ -203,6 +203,15 @@ declare module 'streamify-audio' {
|
|
|
203
203
|
enabled?: boolean;
|
|
204
204
|
maxTracks?: number;
|
|
205
205
|
};
|
|
206
|
+
stream?: {
|
|
207
|
+
dataTimeout?: number;
|
|
208
|
+
pipeDataTimeout?: number;
|
|
209
|
+
liveDataTimeout?: number;
|
|
210
|
+
extractTimeout?: number;
|
|
211
|
+
urlCacheTTL?: number;
|
|
212
|
+
bufferSize?: string;
|
|
213
|
+
maxRetries?: number;
|
|
214
|
+
};
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
export interface ManagerStats {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "streamify-audio",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Dual-mode audio library: HTTP streaming proxy + Discord player (Lavalink alternative). Supports YouTube, Spotify, SoundCloud with audio filters.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
package/src/config.js
CHANGED
|
@@ -41,10 +41,16 @@ const defaults = {
|
|
|
41
41
|
},
|
|
42
42
|
stream: {
|
|
43
43
|
dataTimeout: 8000,
|
|
44
|
+
pipeDataTimeout: 20000,
|
|
44
45
|
liveDataTimeout: 15000,
|
|
46
|
+
extractTimeout: 30000,
|
|
45
47
|
urlCacheTTL: 1800,
|
|
46
48
|
bufferSize: '1M',
|
|
47
49
|
maxRetries: 2
|
|
50
|
+
},
|
|
51
|
+
sponsorblock: {
|
|
52
|
+
enabled: false,
|
|
53
|
+
categories: ['sponsor', 'selfpromo']
|
|
48
54
|
}
|
|
49
55
|
};
|
|
50
56
|
|
|
@@ -119,6 +125,11 @@ function load(options = {}) {
|
|
|
119
125
|
...defaults.stream,
|
|
120
126
|
...fileConfig.stream,
|
|
121
127
|
...options.stream
|
|
128
|
+
},
|
|
129
|
+
sponsorblock: {
|
|
130
|
+
...defaults.sponsorblock,
|
|
131
|
+
...fileConfig.sponsorblock,
|
|
132
|
+
...options.sponsorblock
|
|
122
133
|
}
|
|
123
134
|
};
|
|
124
135
|
|
package/src/discord/Manager.js
CHANGED
|
@@ -403,7 +403,7 @@ class Manager extends EventEmitter {
|
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
async _resolveSpotifyToYouTube(track) {
|
|
406
|
-
const videoId = await spotify.resolveToYouTube(track.id, this.config);
|
|
406
|
+
const videoId = await spotify.resolveToYouTube(track.id, this.config, track);
|
|
407
407
|
return videoId;
|
|
408
408
|
}
|
|
409
409
|
|
package/src/discord/Stream.js
CHANGED
|
@@ -95,7 +95,7 @@ class StreamController {
|
|
|
95
95
|
log.info('STREAM', `Resolving Spotify track to YouTube: ${this.track.title}`);
|
|
96
96
|
try {
|
|
97
97
|
const spotify = require('../providers/spotify');
|
|
98
|
-
videoId = await spotify.resolveToYouTube(this.track.id, this.config);
|
|
98
|
+
videoId = await spotify.resolveToYouTube(this.track.id, this.config, this.track);
|
|
99
99
|
this.track._resolvedId = videoId;
|
|
100
100
|
this.metrics.metadata = Date.now() - this.startTime;
|
|
101
101
|
} catch (error) {
|
|
@@ -238,7 +238,7 @@ class StreamController {
|
|
|
238
238
|
ytdlpArgs.unshift('--cookies', this.config.cookiesPath);
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
if (this.config.sponsorblock?.enabled
|
|
241
|
+
if (this.config.sponsorblock?.enabled === true && isYouTube) {
|
|
242
242
|
const categories = this.config.sponsorblock?.categories || ['sponsor', 'selfpromo'];
|
|
243
243
|
ytdlpArgs.push('--sponsorblock-remove', categories.join(','));
|
|
244
244
|
}
|
|
@@ -365,11 +365,9 @@ class StreamController {
|
|
|
365
365
|
const url = `https://www.youtube.com/watch?v=${videoId}`;
|
|
366
366
|
const args = [
|
|
367
367
|
'--get-url',
|
|
368
|
-
'-f',
|
|
368
|
+
'-f', 'bestaudio/best',
|
|
369
369
|
'--no-playlist',
|
|
370
|
-
'--no-check-certificates',
|
|
371
370
|
'--no-warnings',
|
|
372
|
-
'--no-cache-dir',
|
|
373
371
|
url
|
|
374
372
|
];
|
|
375
373
|
|
|
@@ -381,20 +379,16 @@ class StreamController {
|
|
|
381
379
|
args.unshift('--cookies', config.cookiesPath);
|
|
382
380
|
}
|
|
383
381
|
|
|
384
|
-
if (config.sponsorblock?.enabled !== false && isYouTube) {
|
|
385
|
-
const categories = config.sponsorblock?.categories || ['sponsor', 'selfpromo'];
|
|
386
|
-
args.push('--sponsorblock-remove', categories.join(','));
|
|
387
|
-
}
|
|
388
|
-
|
|
389
382
|
const env = { ...process.env, PATH: '/usr/local/bin:/root/.deno/bin:' + process.env.PATH };
|
|
390
383
|
const proc = spawn(config.ytdlpPath, args, { env });
|
|
391
384
|
|
|
392
385
|
let stdout = '';
|
|
393
386
|
let stderr = '';
|
|
387
|
+
const extractTimeout = config.stream?.extractTimeout || 30000;
|
|
394
388
|
const timeout = setTimeout(() => {
|
|
395
389
|
proc.kill('SIGKILL');
|
|
396
390
|
reject(new Error('URL extraction timed out'));
|
|
397
|
-
},
|
|
391
|
+
}, extractTimeout);
|
|
398
392
|
|
|
399
393
|
proc.stdout.on('data', (data) => { stdout += data; });
|
|
400
394
|
proc.stderr.on('data', (data) => { stderr += data; });
|
|
@@ -422,9 +416,12 @@ class StreamController {
|
|
|
422
416
|
const ffmpeg = this.ffmpeg;
|
|
423
417
|
const ytdlp = this.ytdlp;
|
|
424
418
|
const isLive = this.track.isLive === true || this.track.duration === 0;
|
|
419
|
+
const isPipe = !!this.ytdlp;
|
|
425
420
|
const timeoutMs = isLive
|
|
426
421
|
? (this.config.stream?.liveDataTimeout || 15000)
|
|
427
|
-
:
|
|
422
|
+
: isPipe
|
|
423
|
+
? (this.config.stream?.pipeDataTimeout || 20000)
|
|
424
|
+
: (this.config.stream?.dataTimeout || 8000);
|
|
428
425
|
|
|
429
426
|
return new Promise((resolve, reject) => {
|
|
430
427
|
if (!ffmpeg) return resolve();
|
package/src/providers/spotify.js
CHANGED
|
@@ -104,14 +104,14 @@ async function getInfo(trackId, config) {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
async function resolveToYouTube(trackId, config) {
|
|
107
|
+
async function resolveToYouTube(trackId, config, trackInfo = null) {
|
|
108
108
|
const cached = youtubeIdCache.get(trackId);
|
|
109
109
|
if (cached && Date.now() < cached.expires) {
|
|
110
110
|
log.info('SPOTIFY', `Using cached YouTube ID: ${cached.videoId}`);
|
|
111
111
|
return cached.videoId;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
const track = await getInfo(trackId, config);
|
|
114
|
+
const track = trackInfo || await getInfo(trackId, config);
|
|
115
115
|
const searchQuery = `${track.author} - ${track.title}`;
|
|
116
116
|
log.info('SPOTIFY', `Searching YouTube: "${searchQuery}"`);
|
|
117
117
|
|
package/docs/README.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Streamify Documentation
|
|
2
|
-
|
|
3
|
-
## Getting Started
|
|
4
|
-
|
|
5
|
-
- [Quick Start](./quick-start.md) — Get up and running in 5 minutes
|
|
6
|
-
- [Configuration](./configuration.md) — All configuration options
|
|
7
|
-
|
|
8
|
-
## Discord Player Mode
|
|
9
|
-
|
|
10
|
-
- [Manager](./discord/manager.md) — Create and manage players
|
|
11
|
-
- [Player](./discord/player.md) — Playback control and state
|
|
12
|
-
- [Queue](./discord/queue.md) — Queue management
|
|
13
|
-
- [Events](./discord/events.md) — All player events
|
|
14
|
-
|
|
15
|
-
## HTTP Server Mode
|
|
16
|
-
|
|
17
|
-
- [Server Setup](./http/server.md) — Start the HTTP server
|
|
18
|
-
- [Endpoints](./http/endpoints.md) — API reference
|
|
19
|
-
|
|
20
|
-
## Features
|
|
21
|
-
|
|
22
|
-
- [Filters](./filters.md) — Audio filters and presets
|
|
23
|
-
- [Sources](./sources.md) — YouTube, Spotify, SoundCloud
|
|
24
|
-
- [Automation](./automation.md) — Auto-pause, auto-leave, autoplay
|
|
25
|
-
- [Sponsorblock](./sponsorblock.md) — Skip YouTube sponsors
|
|
26
|
-
|
|
27
|
-
## Examples
|
|
28
|
-
|
|
29
|
-
- [Basic Discord Bot](./examples/basic-bot.md)
|
|
30
|
-
- [Advanced Bot with Queue](./examples/advanced-bot.md)
|
|
31
|
-
- [HTTP Server with Lavalink](./examples/lavalink.md)
|
package/docs/automation.md
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# Automation
|
|
2
|
-
|
|
3
|
-
Streamify includes built-in automation features for common scenarios.
|
|
4
|
-
|
|
5
|
-
## Auto-Pause
|
|
6
|
-
|
|
7
|
-
Automatically pauses playback when users leave the voice channel and resumes when they return.
|
|
8
|
-
|
|
9
|
-
### Configuration
|
|
10
|
-
|
|
11
|
-
```javascript
|
|
12
|
-
const manager = new Streamify.Manager(client, {
|
|
13
|
-
autoPause: {
|
|
14
|
-
enabled: true,
|
|
15
|
-
minUsers: 1 // Pause when users drop below this
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### Behavior
|
|
21
|
-
|
|
22
|
-
1. User count drops below `minUsers`
|
|
23
|
-
2. Playback pauses immediately
|
|
24
|
-
3. Stream is destroyed (saves resources)
|
|
25
|
-
4. Position is saved
|
|
26
|
-
5. When users rejoin and count reaches `minUsers`:
|
|
27
|
-
6. Stream recreates with seek
|
|
28
|
-
7. Playback resumes
|
|
29
|
-
|
|
30
|
-
### Events
|
|
31
|
-
|
|
32
|
-
```javascript
|
|
33
|
-
player.on('autoPause', (userCount) => {
|
|
34
|
-
channel.send(`Paused (${userCount} users in channel)`);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
player.on('autoResume', (userCount) => {
|
|
38
|
-
channel.send(`Resumed (${userCount} users now)`);
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Runtime Toggle
|
|
43
|
-
|
|
44
|
-
```javascript
|
|
45
|
-
player.setAutoPause(false); // Disable
|
|
46
|
-
player.setAutoPause(true); // Enable
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Auto-Leave
|
|
50
|
-
|
|
51
|
-
Automatically leaves the voice channel after a period of inactivity.
|
|
52
|
-
|
|
53
|
-
### Configuration
|
|
54
|
-
|
|
55
|
-
```javascript
|
|
56
|
-
const manager = new Streamify.Manager(client, {
|
|
57
|
-
autoLeave: {
|
|
58
|
-
enabled: true,
|
|
59
|
-
emptyDelay: 30000, // Leave 30s after channel empty
|
|
60
|
-
inactivityTimeout: 300000 // Leave after 5min of not playing
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Behavior
|
|
66
|
-
|
|
67
|
-
**Empty Channel:**
|
|
68
|
-
1. All users leave
|
|
69
|
-
2. 30 second countdown starts
|
|
70
|
-
3. If no one rejoins, player destroys
|
|
71
|
-
|
|
72
|
-
**Inactivity:**
|
|
73
|
-
1. Queue ends, nothing playing
|
|
74
|
-
2. 5 minute countdown starts
|
|
75
|
-
3. If nothing plays, player destroys
|
|
76
|
-
|
|
77
|
-
### Events
|
|
78
|
-
|
|
79
|
-
```javascript
|
|
80
|
-
player.on('channelEmpty', () => {
|
|
81
|
-
channel.send('Everyone left. Leaving in 30 seconds...');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
player.on('destroy', () => {
|
|
85
|
-
channel.send('Disconnected due to inactivity.');
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Autoplay
|
|
90
|
-
|
|
91
|
-
Automatically plays related tracks when the queue ends.
|
|
92
|
-
|
|
93
|
-
### Configuration
|
|
94
|
-
|
|
95
|
-
```javascript
|
|
96
|
-
const manager = new Streamify.Manager(client, {
|
|
97
|
-
autoplay: {
|
|
98
|
-
enabled: false, // Off by default
|
|
99
|
-
maxTracks: 5 // How many related tracks to fetch
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Behavior
|
|
105
|
-
|
|
106
|
-
1. Queue ends (last track finishes)
|
|
107
|
-
2. Fetch related tracks from YouTube Mix or Spotify Recommendations
|
|
108
|
-
3. Add to queue and start playing
|
|
109
|
-
4. Repeat when those tracks end
|
|
110
|
-
|
|
111
|
-
### Events
|
|
112
|
-
|
|
113
|
-
```javascript
|
|
114
|
-
player.on('autoplayStart', (lastTrack) => {
|
|
115
|
-
console.log(`Finding tracks like: ${lastTrack.title}`);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
player.on('autoplayAdd', (tracks) => {
|
|
119
|
-
channel.send(`Autoplay added ${tracks.length} tracks`);
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Runtime Toggle
|
|
124
|
-
|
|
125
|
-
```javascript
|
|
126
|
-
player.setAutoplay(true); // Enable
|
|
127
|
-
player.setAutoplay(false); // Disable
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Track Identification
|
|
131
|
-
|
|
132
|
-
Autoplay tracks are marked:
|
|
133
|
-
|
|
134
|
-
```javascript
|
|
135
|
-
player.on('trackStart', (track) => {
|
|
136
|
-
if (track.isAutoplay) {
|
|
137
|
-
channel.send(`Autoplay: **${track.title}**`);
|
|
138
|
-
} else {
|
|
139
|
-
channel.send(`Now playing: **${track.title}**`);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## Combining Features
|
|
145
|
-
|
|
146
|
-
All automation features work together:
|
|
147
|
-
|
|
148
|
-
```javascript
|
|
149
|
-
const manager = new Streamify.Manager(client, {
|
|
150
|
-
autoPause: {
|
|
151
|
-
enabled: true,
|
|
152
|
-
minUsers: 1
|
|
153
|
-
},
|
|
154
|
-
autoLeave: {
|
|
155
|
-
enabled: true,
|
|
156
|
-
emptyDelay: 30000,
|
|
157
|
-
inactivityTimeout: 300000
|
|
158
|
-
},
|
|
159
|
-
autoplay: {
|
|
160
|
-
enabled: true,
|
|
161
|
-
maxTracks: 5
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Scenario: User leaves during playback**
|
|
167
|
-
1. `autoPause` triggers → pauses playback
|
|
168
|
-
2. `autoLeave` starts 30s countdown
|
|
169
|
-
3. User rejoins within 30s
|
|
170
|
-
4. `autoResume` triggers → playback continues
|
|
171
|
-
5. Countdown cancelled
|
|
172
|
-
|
|
173
|
-
**Scenario: Queue ends**
|
|
174
|
-
1. `autoplay` fetches related tracks
|
|
175
|
-
2. Playback continues with recommendations
|
|
176
|
-
3. If autoplay disabled, `autoLeave` inactivity timeout starts
|
|
177
|
-
|
|
178
|
-
## Disabling All Automation
|
|
179
|
-
|
|
180
|
-
```javascript
|
|
181
|
-
const manager = new Streamify.Manager(client, {
|
|
182
|
-
autoPause: { enabled: false },
|
|
183
|
-
autoLeave: { enabled: false },
|
|
184
|
-
autoplay: { enabled: false }
|
|
185
|
-
});
|
|
186
|
-
```
|
package/docs/configuration.md
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
# Configuration
|
|
2
|
-
|
|
3
|
-
## Discord Player Mode
|
|
4
|
-
|
|
5
|
-
```javascript
|
|
6
|
-
const manager = new Streamify.Manager(client, {
|
|
7
|
-
// Paths (required)
|
|
8
|
-
ytdlpPath: '/usr/local/bin/yt-dlp',
|
|
9
|
-
ffmpegPath: '/usr/bin/ffmpeg',
|
|
10
|
-
|
|
11
|
-
// YouTube cookies (optional)
|
|
12
|
-
cookiesPath: './cookies.txt',
|
|
13
|
-
|
|
14
|
-
// Providers (optional - all enabled by default)
|
|
15
|
-
providers: {
|
|
16
|
-
youtube: { enabled: true },
|
|
17
|
-
spotify: { enabled: true },
|
|
18
|
-
soundcloud: { enabled: true }
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
// Spotify credentials (optional)
|
|
22
|
-
spotify: {
|
|
23
|
-
clientId: process.env.SPOTIFY_CLIENT_ID,
|
|
24
|
-
clientSecret: process.env.SPOTIFY_CLIENT_SECRET
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
// Audio settings
|
|
28
|
-
audio: {
|
|
29
|
-
bitrate: '128k', // Audio bitrate
|
|
30
|
-
format: 'opus', // opus, mp3, aac
|
|
31
|
-
vbr: true, // Enable Variable Bitrate (Opus only)
|
|
32
|
-
compressionLevel: 10, // Opus compression level (0-10)
|
|
33
|
-
application: 'audio' // Opus application (audio, voip, lowdelay)
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
// yt-dlp settings
|
|
37
|
-
ytdlp: {
|
|
38
|
-
format: 'bestaudio/best', // Custom yt-dlp format string
|
|
39
|
-
additionalArgs: [] // Extra arguments for yt-dlp (e.g. proxy)
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
// Defaults
|
|
43
|
-
defaultVolume: 80, // 0-200
|
|
44
|
-
maxPreviousTracks: 25, // History size
|
|
45
|
-
|
|
46
|
-
// Sponsorblock
|
|
47
|
-
sponsorblock: {
|
|
48
|
-
enabled: true,
|
|
49
|
-
categories: ['sponsor', 'selfpromo', 'intro', 'outro']
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
// Auto-leave
|
|
53
|
-
autoLeave: {
|
|
54
|
-
enabled: true,
|
|
55
|
-
emptyDelay: 30000, // Leave 30s after channel empty
|
|
56
|
-
inactivityTimeout: 300000 // Leave after 5min idle
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
// Auto-pause
|
|
60
|
-
autoPause: {
|
|
61
|
-
enabled: true,
|
|
62
|
-
minUsers: 1 // Pause when users drop below this
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
// Autoplay
|
|
66
|
-
autoplay: {
|
|
67
|
-
enabled: false,
|
|
68
|
-
maxTracks: 5 // Related tracks to fetch
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// Voice Channel Status
|
|
72
|
-
voiceChannelStatus: {
|
|
73
|
-
enabled: true, // Show "Now Playing" in VC description
|
|
74
|
-
template: '🎶 Now Playing: {title} - {artist}' // Custom template
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Voice Channel Status Template Variables:**
|
|
80
|
-
- `{title}` - Track title
|
|
81
|
-
- `{artist}` - Track artist/author
|
|
82
|
-
- `{requester}` - Username of the requester (if provided)
|
|
83
|
-
|
|
84
|
-
## HTTP Server Mode
|
|
85
|
-
|
|
86
|
-
```javascript
|
|
87
|
-
const streamify = new Streamify({
|
|
88
|
-
// Server
|
|
89
|
-
port: 8787,
|
|
90
|
-
host: '0.0.0.0',
|
|
91
|
-
|
|
92
|
-
// Paths
|
|
93
|
-
ytdlpPath: '/usr/local/bin/yt-dlp',
|
|
94
|
-
ffmpegPath: '/usr/bin/ffmpeg',
|
|
95
|
-
|
|
96
|
-
// YouTube cookies
|
|
97
|
-
cookiesPath: './cookies.txt',
|
|
98
|
-
// Or direct string:
|
|
99
|
-
cookies: '# Netscape HTTP Cookie File\n...',
|
|
100
|
-
|
|
101
|
-
// Providers (optional - all enabled by default)
|
|
102
|
-
providers: {
|
|
103
|
-
youtube: { enabled: true },
|
|
104
|
-
spotify: { enabled: true },
|
|
105
|
-
soundcloud: { enabled: true }
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
// Spotify
|
|
109
|
-
spotify: {
|
|
110
|
-
clientId: 'xxx',
|
|
111
|
-
clientSecret: 'xxx'
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
// Audio
|
|
115
|
-
audio: {
|
|
116
|
-
bitrate: '128k',
|
|
117
|
-
format: 'opus',
|
|
118
|
-
vbr: true,
|
|
119
|
-
compressionLevel: 10,
|
|
120
|
-
application: 'audio'
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
// yt-dlp
|
|
124
|
-
ytdlp: {
|
|
125
|
-
format: 'bestaudio/best',
|
|
126
|
-
additionalArgs: []
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
// Logging
|
|
130
|
-
logLevel: 'info', // none, error, warn, info, debug
|
|
131
|
-
silent: false,
|
|
132
|
-
colors: true
|
|
133
|
-
});
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Environment Variables
|
|
137
|
-
|
|
138
|
-
| Variable | Description |
|
|
139
|
-
|----------|-------------|
|
|
140
|
-
| `SPOTIFY_CLIENT_ID` | Spotify API client ID |
|
|
141
|
-
| `SPOTIFY_CLIENT_SECRET` | Spotify API client secret |
|
|
142
|
-
| `YTDLP_PATH` | Path to yt-dlp binary |
|
|
143
|
-
| `FFMPEG_PATH` | Path to ffmpeg binary |
|
|
144
|
-
| `COOKIES_PATH` | Path to YouTube cookies file |
|
|
145
|
-
| `PORT` | HTTP server port |
|
|
146
|
-
|
|
147
|
-
## YouTube Cookies
|
|
148
|
-
|
|
149
|
-
For age-restricted or region-locked videos:
|
|
150
|
-
|
|
151
|
-
1. Install browser extension "Get cookies.txt"
|
|
152
|
-
2. Visit youtube.com while logged in
|
|
153
|
-
3. Export cookies in Netscape format
|
|
154
|
-
4. Save as `cookies.txt`
|
|
155
|
-
|
|
156
|
-
```javascript
|
|
157
|
-
// File path
|
|
158
|
-
cookiesPath: './cookies.txt'
|
|
159
|
-
|
|
160
|
-
// Or direct string
|
|
161
|
-
cookies: `# Netscape HTTP Cookie File
|
|
162
|
-
.youtube.com TRUE / TRUE 0 COOKIE_NAME COOKIE_VALUE`
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Spotify Setup
|
|
166
|
-
|
|
167
|
-
1. Go to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard)
|
|
168
|
-
2. Create an app
|
|
169
|
-
3. Copy Client ID and Client Secret
|
|
170
|
-
|
|
171
|
-
```javascript
|
|
172
|
-
spotify: {
|
|
173
|
-
clientId: process.env.SPOTIFY_CLIENT_ID,
|
|
174
|
-
clientSecret: process.env.SPOTIFY_CLIENT_SECRET
|
|
175
|
-
}
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Spotify tracks are resolved to YouTube for playback.
|
|
179
|
-
|
|
180
|
-
## Providers
|
|
181
|
-
|
|
182
|
-
Enable or disable individual providers:
|
|
183
|
-
|
|
184
|
-
```javascript
|
|
185
|
-
providers: {
|
|
186
|
-
youtube: { enabled: true },
|
|
187
|
-
spotify: { enabled: true },
|
|
188
|
-
soundcloud: { enabled: false } // Disabled
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
All providers are enabled by default. When a disabled provider is accessed, an error is thrown:
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
Error: SoundCloud provider is disabled
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
This is useful if you only want to support specific platforms.
|