streamify-audio 2.0.1 → 2.0.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/package.json +1 -1
- package/src/discord/Stream.js +20 -5
- package/src/providers/youtube.js +10 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "streamify-audio",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.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/discord/Stream.js
CHANGED
|
@@ -47,8 +47,17 @@ class StreamController {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const isYouTube = source === 'youtube' || source === 'spotify';
|
|
50
|
+
const isLive = this.track.isLive === true || this.track.duration === 0;
|
|
51
|
+
|
|
52
|
+
let formatString;
|
|
53
|
+
if (isLive) {
|
|
54
|
+
formatString = 'bestaudio[ext=webm]/bestaudio/best';
|
|
55
|
+
} else {
|
|
56
|
+
formatString = isYouTube ? '18/22/bestaudio[ext=webm]/bestaudio/best' : 'bestaudio/best';
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
const ytdlpArgs = [
|
|
51
|
-
'-f',
|
|
60
|
+
'-f', formatString,
|
|
52
61
|
'--no-playlist',
|
|
53
62
|
'--no-check-certificates',
|
|
54
63
|
'--no-warnings',
|
|
@@ -58,6 +67,11 @@ class StreamController {
|
|
|
58
67
|
url
|
|
59
68
|
];
|
|
60
69
|
|
|
70
|
+
if (isLive) {
|
|
71
|
+
ytdlpArgs.push('--live-from-start');
|
|
72
|
+
log.info('STREAM', `Live stream detected, using live-compatible format`);
|
|
73
|
+
}
|
|
74
|
+
|
|
61
75
|
if (isYouTube) {
|
|
62
76
|
ytdlpArgs.push('--extractor-args', 'youtube:player_client=web_creator');
|
|
63
77
|
}
|
|
@@ -163,7 +177,7 @@ class StreamController {
|
|
|
163
177
|
}
|
|
164
178
|
});
|
|
165
179
|
|
|
166
|
-
await this._waitForData();
|
|
180
|
+
await this._waitForData(isLive);
|
|
167
181
|
|
|
168
182
|
this.resource = createAudioResource(this.ffmpeg.stdout, {
|
|
169
183
|
inputType: StreamType.OggOpus,
|
|
@@ -177,12 +191,13 @@ class StreamController {
|
|
|
177
191
|
return this.resource;
|
|
178
192
|
}
|
|
179
193
|
|
|
180
|
-
_waitForData() {
|
|
194
|
+
_waitForData(isLive = false) {
|
|
181
195
|
return new Promise((resolve, reject) => {
|
|
196
|
+
const timeoutMs = isLive ? 30000 : 15000;
|
|
182
197
|
const timeout = setTimeout(() => {
|
|
183
|
-
log.warn('STREAM', `Timeout waiting for data, proceeding anyway (received: ${this.bytesReceived})`);
|
|
198
|
+
log.warn('STREAM', `Timeout waiting for data, proceeding anyway (received: ${this.bytesReceived}, isLive: ${isLive})`);
|
|
184
199
|
resolve();
|
|
185
|
-
},
|
|
200
|
+
}, timeoutMs);
|
|
186
201
|
|
|
187
202
|
let resolved = false;
|
|
188
203
|
|
package/src/providers/youtube.js
CHANGED
|
@@ -39,12 +39,13 @@ async function search(query, limit, config) {
|
|
|
39
39
|
const tracks = (data.entries || []).map(entry => ({
|
|
40
40
|
id: entry.id,
|
|
41
41
|
title: entry.title,
|
|
42
|
-
duration: entry.duration,
|
|
42
|
+
duration: entry.duration || 0,
|
|
43
43
|
author: entry.channel || entry.uploader,
|
|
44
44
|
thumbnail: entry.thumbnails?.[0]?.url,
|
|
45
45
|
uri: `https://www.youtube.com/watch?v=${entry.id}`,
|
|
46
46
|
streamUrl: `/youtube/stream/${entry.id}`,
|
|
47
|
-
source: 'youtube'
|
|
47
|
+
source: 'youtube',
|
|
48
|
+
isLive: entry.live_status === 'is_live' || entry.is_live === true || !entry.duration
|
|
48
49
|
}));
|
|
49
50
|
const elapsed = Date.now() - startTime;
|
|
50
51
|
log.info('YOUTUBE', `Found ${tracks.length} results (${elapsed}ms)`);
|
|
@@ -87,15 +88,17 @@ async function getInfo(videoId, config) {
|
|
|
87
88
|
}
|
|
88
89
|
try {
|
|
89
90
|
const data = JSON.parse(stdout);
|
|
91
|
+
const isLive = data.live_status === 'is_live' || data.is_live === true || !data.duration;
|
|
90
92
|
resolve({
|
|
91
93
|
id: data.id,
|
|
92
94
|
title: data.title,
|
|
93
|
-
duration: data.duration,
|
|
95
|
+
duration: data.duration || 0,
|
|
94
96
|
author: data.channel || data.uploader,
|
|
95
97
|
thumbnail: data.thumbnail,
|
|
96
98
|
uri: data.webpage_url,
|
|
97
99
|
streamUrl: `/youtube/stream/${data.id}`,
|
|
98
|
-
source: 'youtube'
|
|
100
|
+
source: 'youtube',
|
|
101
|
+
isLive
|
|
99
102
|
});
|
|
100
103
|
} catch (e) {
|
|
101
104
|
reject(new Error('Failed to parse yt-dlp output'));
|
|
@@ -238,12 +241,13 @@ async function getPlaylist(playlistId, config) {
|
|
|
238
241
|
const tracks = (data.entries || []).map(entry => ({
|
|
239
242
|
id: entry.id,
|
|
240
243
|
title: entry.title,
|
|
241
|
-
duration: entry.duration,
|
|
244
|
+
duration: entry.duration || 0,
|
|
242
245
|
author: entry.channel || entry.uploader,
|
|
243
246
|
thumbnail: entry.thumbnails?.[0]?.url,
|
|
244
247
|
uri: `https://www.youtube.com/watch?v=${entry.id}`,
|
|
245
248
|
streamUrl: `/youtube/stream/${entry.id}`,
|
|
246
|
-
source: 'youtube'
|
|
249
|
+
source: 'youtube',
|
|
250
|
+
isLive: entry.live_status === 'is_live' || entry.is_live === true || !entry.duration
|
|
247
251
|
}));
|
|
248
252
|
log.info('YOUTUBE', `Playlist loaded: ${data.title || playlistId} (${tracks.length} tracks)`);
|
|
249
253
|
resolve({
|