streamify-audio 2.2.14 → 2.3.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,88 @@
1
+ # Stream System Revamp — Design Document
2
+
3
+ ## Problem Statement
4
+
5
+ The current stream system has critical issues:
6
+ 1. `_waitForData()` resolves on timeout (15s), allowing broken/silent streams to start playback
7
+ 2. yt-dlp spawns per-play are slow — format negotiation adds seconds of latency
8
+ 3. No URL caching — direct URLs aren't reused for replays/seeks
9
+ 4. Prefetch only resolves metadata, doesn't prepare the actual stream pipeline
10
+ 5. Error swallowing hides real failures from yt-dlp/ffmpeg stderr
11
+
12
+ ## Architecture: Two-Phase StreamController
13
+
14
+ ### Phase 1: `prepare()` — URL Extraction
15
+ - Check `StreamURLCache` for cached direct URL
16
+ - Cache miss → spawn `yt-dlp --get-url -f bestaudio` to extract direct stream URL
17
+ - Cache the URL (key: `{videoId}:{format}`, TTL: 30 min)
18
+ - For Spotify: resolve to YouTube ID first (existing behavior)
19
+ - For live streams: skip (can't pre-extract live URLs)
20
+
21
+ ### Phase 2: `start()` — Stream Creation
22
+ - Spawn ffmpeg with `-i <directURL>` (no yt-dlp pipe for non-live)
23
+ - Wait for first byte with configurable timeout (default: 8s, live: 15s)
24
+ - **Reject on timeout** — never proceed without data
25
+ - Return AudioResource ready for playback
26
+
27
+ ### Fallback for live/non-YouTube sources
28
+ - Spawn yt-dlp piped to ffmpeg (current behavior)
29
+ - Same timeout/rejection behavior
30
+
31
+ ## StreamURLCache
32
+
33
+ Module-level cache in Stream.js:
34
+ - `Map<string, { url, headers, expiry }>`
35
+ - `get(key)` — returns null if expired
36
+ - `set(key, url, headers, ttl)`
37
+ - `invalidate(key)` — for 403/gone errors
38
+ - Auto-cleanup every 5 minutes
39
+
40
+ ## Player.js — Aggressive Prefetch
41
+
42
+ ### `_prefetchNext()` Rewrite
43
+ - Triggers on `trackStart` (current track begins)
44
+ - Creates full StreamController for queue[0]
45
+ - Runs `prepare()` + `start()` — ffmpeg spawned, audio buffered
46
+ - Stores as `_prefetchedStream` / `_prefetchedTrack`
47
+
48
+ ### `_playTrack()` Changes
49
+ - Check prefetch match → instant play (zero startup)
50
+ - Otherwise: create StreamController, prepare() + start()
51
+ - On error: emit trackError, auto-skip
52
+
53
+ ### Prefetch Lifecycle
54
+ - Clear on: filter/volume change, stop, destroy, queue reorder
55
+ - Ignore failures: log and fall back to normal creation
56
+
57
+ ## Config Additions
58
+
59
+ ```js
60
+ stream: {
61
+ dataTimeout: 8000, // ms to wait for first byte
62
+ liveDataTimeout: 15000, // ms for live streams
63
+ urlCacheTTL: 1800, // 30 min
64
+ bufferSize: '1M', // yt-dlp buffer (up from 256K)
65
+ maxRetries: 2 // extraction retries before skip
66
+ }
67
+ ```
68
+
69
+ ## Error Handling
70
+
71
+ - `_waitForData()`: reject on timeout (not resolve)
72
+ - URL extraction failure: retry once before giving up
73
+ - 403/expired URL: invalidate cache, retry with fresh extraction
74
+ - ffmpeg stderr: parse for actionable errors, log properly
75
+ - yt-dlp stderr: count "Retrying fragment" occurrences, fail after threshold
76
+
77
+ ## Files Modified
78
+
79
+ 1. `src/discord/Stream.js` — Full rewrite (StreamController + StreamURLCache)
80
+ 2. `src/discord/Player.js` — Prefetch rewrite, _playTrack changes
81
+ 3. `src/config.js` — New `stream` config section
82
+
83
+ ## Files NOT Modified
84
+
85
+ - `src/filters/ffmpeg.js` — Unchanged (filter building is solid)
86
+ - `src/providers/youtube.js` — Unchanged (search/info/playlist untouched)
87
+ - `src/discord/Queue.js` — Unchanged
88
+ - `src/discord/Manager.js` — Unchanged