streamify-audio 2.0.1 → 2.0.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "streamify-audio",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
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",
@@ -198,6 +198,11 @@ class Manager extends EventEmitter {
198
198
  }
199
199
 
200
200
  async search(query, options = {}) {
201
+ const isUrl = query.startsWith('http://') || query.startsWith('https://');
202
+ if (isUrl) {
203
+ return this.resolve(query);
204
+ }
205
+
201
206
  const source = options.source || this._detectSource(query) || 'youtube';
202
207
  const limit = options.limit || 10;
203
208
 
@@ -313,6 +318,10 @@ class Manager extends EventEmitter {
313
318
  }
314
319
  }
315
320
 
321
+ const isUrl = query.startsWith('http://') || query.startsWith('https://');
322
+ if (isUrl) {
323
+ return { loadType: 'empty', tracks: [] };
324
+ }
316
325
  return this.search(query);
317
326
  }
318
327
 
@@ -332,7 +341,8 @@ class Manager extends EventEmitter {
332
341
  youtube: [
333
342
  /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/,
334
343
  /(?:youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/,
335
- /(?:music\.youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/
344
+ /(?:music\.youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/,
345
+ /(?:youtube\.com\/live\/)([a-zA-Z0-9_-]{11})/
336
346
  ],
337
347
  spotify: [
338
348
  /open\.spotify\.com\/track\/([a-zA-Z0-9]+)/,
@@ -357,7 +367,7 @@ class Manager extends EventEmitter {
357
367
 
358
368
  _extractId(input, source) {
359
369
  const patterns = {
360
- youtube: /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/|music\.youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/,
370
+ youtube: /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/|music\.youtube\.com\/watch\?v=|youtube\.com\/live\/)([a-zA-Z0-9_-]{11})/,
361
371
  youtube_playlist: /youtube\.com\/playlist\?list=([a-zA-Z0-9_-]+)/,
362
372
  spotify: /(?:open\.spotify\.com\/track\/|spotify:track:)([a-zA-Z0-9]+)/,
363
373
  spotify_playlist: /open\.spotify\.com\/playlist\/([a-zA-Z0-9]+)/,
@@ -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*/best';
55
+ } else {
56
+ formatString = isYouTube ? '18/22/bestaudio[ext=webm]/bestaudio/best' : 'bestaudio/best';
57
+ }
58
+
50
59
  const ytdlpArgs = [
51
- '-f', isYouTube ? '18/22/bestaudio[ext=webm]/bestaudio/best' : 'bestaudio/best',
60
+ '-f', formatString,
52
61
  '--no-playlist',
53
62
  '--no-check-certificates',
54
63
  '--no-warnings',
@@ -58,7 +67,10 @@ class StreamController {
58
67
  url
59
68
  ];
60
69
 
61
- if (isYouTube) {
70
+ if (isLive) {
71
+ ytdlpArgs.push('--no-live-from-start');
72
+ log.info('STREAM', `Live stream detected, using live-compatible format`);
73
+ } else if (isYouTube) {
62
74
  ytdlpArgs.push('--extractor-args', 'youtube:player_client=web_creator');
63
75
  }
64
76
 
@@ -163,7 +175,7 @@ class StreamController {
163
175
  }
164
176
  });
165
177
 
166
- await this._waitForData();
178
+ await this._waitForData(isLive);
167
179
 
168
180
  this.resource = createAudioResource(this.ffmpeg.stdout, {
169
181
  inputType: StreamType.OggOpus,
@@ -177,12 +189,13 @@ class StreamController {
177
189
  return this.resource;
178
190
  }
179
191
 
180
- _waitForData() {
192
+ _waitForData(isLive = false) {
181
193
  return new Promise((resolve, reject) => {
194
+ const timeoutMs = isLive ? 30000 : 15000;
182
195
  const timeout = setTimeout(() => {
183
- log.warn('STREAM', `Timeout waiting for data, proceeding anyway (received: ${this.bytesReceived})`);
196
+ log.warn('STREAM', `Timeout waiting for data, proceeding anyway (received: ${this.bytesReceived}, isLive: ${isLive})`);
184
197
  resolve();
185
- }, 15000);
198
+ }, timeoutMs);
186
199
 
187
200
  let resolved = false;
188
201
 
@@ -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({