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/tests/youtube.test.js
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
const { describe, it, beforeEach } = require('node:test');
|
|
2
|
-
const assert = require('node:assert');
|
|
3
|
-
|
|
4
|
-
const youtube = require('../src/providers/youtube');
|
|
5
|
-
|
|
6
|
-
describe('YouTube Provider', () => {
|
|
7
|
-
const mockConfig = {
|
|
8
|
-
ytdlpPath: 'yt-dlp',
|
|
9
|
-
ffmpegPath: 'ffmpeg',
|
|
10
|
-
cookiesPath: null,
|
|
11
|
-
cache: {
|
|
12
|
-
searchTTL: 300,
|
|
13
|
-
infoTTL: 600
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
describe('search', () => {
|
|
18
|
-
it('should return tracks array for valid query', async () => {
|
|
19
|
-
const results = await youtube.search('never gonna give you up', 1, mockConfig);
|
|
20
|
-
|
|
21
|
-
assert(results.tracks, 'Should have tracks array');
|
|
22
|
-
assert(Array.isArray(results.tracks), 'Tracks should be an array');
|
|
23
|
-
assert.strictEqual(results.source, 'youtube');
|
|
24
|
-
assert(typeof results.searchTime === 'number', 'Should have searchTime');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should respect limit parameter', async () => {
|
|
28
|
-
const results = await youtube.search('music', 3, mockConfig);
|
|
29
|
-
|
|
30
|
-
assert(results.tracks.length <= 3, 'Should not exceed limit');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should return track with required fields', async () => {
|
|
34
|
-
const results = await youtube.search('test video', 1, mockConfig);
|
|
35
|
-
|
|
36
|
-
if (results.tracks.length > 0) {
|
|
37
|
-
const track = results.tracks[0];
|
|
38
|
-
assert(track.id, 'Track should have id');
|
|
39
|
-
assert(track.title, 'Track should have title');
|
|
40
|
-
assert(typeof track.duration === 'number', 'Track should have numeric duration');
|
|
41
|
-
assert(track.uri, 'Track should have uri');
|
|
42
|
-
assert(track.streamUrl, 'Track should have streamUrl');
|
|
43
|
-
assert.strictEqual(track.source, 'youtube');
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should handle empty query gracefully', async () => {
|
|
48
|
-
await assert.rejects(
|
|
49
|
-
() => youtube.search('', 1, mockConfig),
|
|
50
|
-
/yt-dlp/
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should handle special characters in query', async () => {
|
|
55
|
-
const results = await youtube.search('test & special <chars> "quotes"', 1, mockConfig);
|
|
56
|
-
assert(results.tracks, 'Should handle special characters');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should handle unicode in query', async () => {
|
|
60
|
-
const results = await youtube.search('日本語 テスト', 1, mockConfig);
|
|
61
|
-
assert(results.tracks, 'Should handle unicode');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should handle very long query', async () => {
|
|
65
|
-
const longQuery = 'a'.repeat(200);
|
|
66
|
-
const results = await youtube.search(longQuery, 1, mockConfig);
|
|
67
|
-
assert(results.tracks, 'Should handle long query');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should filter by type when specified', async () => {
|
|
71
|
-
const results = await youtube.search('music', 3, mockConfig, { type: 'video' });
|
|
72
|
-
assert(results.tracks, 'Should filter by type');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should sort by views when specified', async () => {
|
|
76
|
-
const results = await youtube.search('music', 3, mockConfig, { sort: 'views' });
|
|
77
|
-
assert(results.tracks, 'Should sort by views');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should sort by date when specified', async () => {
|
|
81
|
-
const results = await youtube.search('music', 3, mockConfig, { sort: 'date' });
|
|
82
|
-
assert(results.tracks, 'Should sort by date');
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe('getInfo', () => {
|
|
87
|
-
it('should return info for valid video ID', async () => {
|
|
88
|
-
const info = await youtube.getInfo('dQw4w9WgXcQ', mockConfig);
|
|
89
|
-
|
|
90
|
-
assert(info.id, 'Should have id');
|
|
91
|
-
assert(info.title, 'Should have title');
|
|
92
|
-
assert(typeof info.duration === 'number', 'Should have numeric duration');
|
|
93
|
-
assert(info.uri, 'Should have uri');
|
|
94
|
-
assert.strictEqual(info.source, 'youtube');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should include author/channel info', async () => {
|
|
98
|
-
const info = await youtube.getInfo('dQw4w9WgXcQ', mockConfig);
|
|
99
|
-
assert(info.author, 'Should have author');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should include thumbnail', async () => {
|
|
103
|
-
const info = await youtube.getInfo('dQw4w9WgXcQ', mockConfig);
|
|
104
|
-
assert(info.thumbnail, 'Should have thumbnail');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should throw for invalid video ID', async () => {
|
|
108
|
-
await assert.rejects(
|
|
109
|
-
() => youtube.getInfo('invalidid123456789', mockConfig),
|
|
110
|
-
/yt-dlp/
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should throw for empty video ID', async () => {
|
|
115
|
-
await assert.rejects(
|
|
116
|
-
() => youtube.getInfo('', mockConfig),
|
|
117
|
-
/yt-dlp/
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should detect live streams', async () => {
|
|
122
|
-
const info = await youtube.getInfo('dQw4w9WgXcQ', mockConfig);
|
|
123
|
-
assert(typeof info.isLive === 'boolean', 'Should have isLive flag');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should include streamUrl', async () => {
|
|
127
|
-
const info = await youtube.getInfo('dQw4w9WgXcQ', mockConfig);
|
|
128
|
-
assert(info.streamUrl.includes('/youtube/stream/'), 'Should have correct streamUrl format');
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe('getPlaylist', () => {
|
|
133
|
-
it('should return playlist info for valid playlist ID', async () => {
|
|
134
|
-
const playlist = await youtube.getPlaylist('PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf', mockConfig);
|
|
135
|
-
|
|
136
|
-
assert(playlist.id, 'Should have id');
|
|
137
|
-
assert(playlist.tracks, 'Should have tracks');
|
|
138
|
-
assert(Array.isArray(playlist.tracks), 'Tracks should be array');
|
|
139
|
-
assert.strictEqual(playlist.source, 'youtube');
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should throw for invalid playlist ID', async () => {
|
|
143
|
-
await assert.rejects(
|
|
144
|
-
() => youtube.getPlaylist('invalidplaylist', mockConfig),
|
|
145
|
-
/yt-dlp/
|
|
146
|
-
);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should include track details in playlist', async () => {
|
|
150
|
-
const playlist = await youtube.getPlaylist('PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf', mockConfig);
|
|
151
|
-
|
|
152
|
-
if (playlist.tracks.length > 0) {
|
|
153
|
-
const track = playlist.tracks[0];
|
|
154
|
-
assert(track.id, 'Track should have id');
|
|
155
|
-
assert(track.title, 'Track should have title');
|
|
156
|
-
assert(track.streamUrl, 'Track should have streamUrl');
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe('getRelated', () => {
|
|
162
|
-
it('should return related videos', async () => {
|
|
163
|
-
const related = await youtube.getRelated('dQw4w9WgXcQ', 5, mockConfig);
|
|
164
|
-
|
|
165
|
-
assert(related.tracks, 'Should have tracks');
|
|
166
|
-
assert(Array.isArray(related.tracks), 'Tracks should be array');
|
|
167
|
-
assert.strictEqual(related.source, 'youtube');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should respect limit', async () => {
|
|
171
|
-
const related = await youtube.getRelated('dQw4w9WgXcQ', 3, mockConfig);
|
|
172
|
-
assert(related.tracks.length <= 3, 'Should not exceed limit');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should not include source video in results', async () => {
|
|
176
|
-
const sourceId = 'dQw4w9WgXcQ';
|
|
177
|
-
const related = await youtube.getRelated(sourceId, 5, mockConfig);
|
|
178
|
-
|
|
179
|
-
const hasSourceVideo = related.tracks.some(t => t.id === sourceId);
|
|
180
|
-
assert(!hasSourceVideo, 'Should not include source video');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should mark tracks as autoplay', async () => {
|
|
184
|
-
const related = await youtube.getRelated('dQw4w9WgXcQ', 3, mockConfig);
|
|
185
|
-
|
|
186
|
-
if (related.tracks.length > 0) {
|
|
187
|
-
assert.strictEqual(related.tracks[0].isAutoplay, true, 'Should be marked as autoplay');
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe('edge cases', () => {
|
|
193
|
-
it('should handle age-restricted content', async () => {
|
|
194
|
-
// This test may fail without cookies - that's expected
|
|
195
|
-
try {
|
|
196
|
-
const info = await youtube.getInfo('6kLq3WMV1nU', mockConfig);
|
|
197
|
-
assert(info.id, 'Should get info if cookies available');
|
|
198
|
-
} catch (e) {
|
|
199
|
-
assert(e.message.includes('yt-dlp'), 'Should fail gracefully without cookies');
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should handle deleted/private videos', async () => {
|
|
204
|
-
await assert.rejects(
|
|
205
|
-
() => youtube.getInfo('aaaaaaaaaaa', mockConfig),
|
|
206
|
-
/yt-dlp/
|
|
207
|
-
);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('should handle region-blocked content gracefully', async () => {
|
|
211
|
-
// Most region blocks should produce an error from yt-dlp
|
|
212
|
-
try {
|
|
213
|
-
await youtube.getInfo('_some_blocked_video_', mockConfig);
|
|
214
|
-
} catch (e) {
|
|
215
|
-
assert(e.message, 'Should throw with message');
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
});
|
package/youtube-cookies.txt
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Netscape HTTP Cookie File
|
|
2
|
-
# This file is generated by yt-dlp. Do not edit.
|
|
3
|
-
|
|
4
|
-
.youtube.com TRUE / TRUE 1800398983 __Secure-YENID 12.YTE=UfQjguVvlx3WLiJJHSjg4nPS-N-5O3Xj8RZj-wlLmRdjAvaF3TRdMzSmyUmo82Z4P-tggpd89U5d3hMDN886ZuUZUzi5XdEFEmHjL0YrTPBwPhD6pZx5CJ-3bNb3Eb_7eZuq_YlqQb0OX6Hvu2yYtWfWZPq7dZUjnJarOqJB0UaWu4jlsn27LkAmA9ecZK7Tj0NXQsIiDMR_y8pHe-kOfQ9LhLBeMnUWvR7GXsSOo_NqyfHrNnu4W8YnBd45ZUQIhwQ91X7lhuTkbBj3hYNJsWv0G7yda1NfB8j-nh8ZkordnGPIyv5QlCTd745j2DiGeS1GSa2jP2bClx7SoQxTjQ
|
|
5
|
-
.youtube.com TRUE / FALSE 0 PREF f6=40000000&tz=UTC&hl=en
|
|
6
|
-
.youtube.com TRUE / TRUE 1782082203 NID 527=aqYd85605VNHKMN7IAs-QcA6YfpJm6LGDAjKm929dxg81_ly4EHuc_A0jCgct9sjP-yDkNobeQPSES7fdZYHdzmlnHO_gGQ-oVCIbS3GQvSB89DCDEPB-toK2K62rr6iUVoTg3lcUG5xBhVXwoPPOD6VWWqegOp-IOhJhTb7Ijp_899mbKpj-4Elx8vNwUL0X-lJAe5Th-4Mxv9lDynXDH8axGjFhMHboeuTOHiIXRg10-4BoqyhAGFDhKNohiMwDc66EraGHim03-ZM
|
|
7
|
-
.youtube.com TRUE / TRUE 1800826170 __Secure-1PSIDTS sidts-CjUB7I_69MoGgU9hYcmJtLk5dHx1d0LjE7gLaoN86V6V3M8eyIyUH6LOPPFj2llBVQAVsulbqRAA
|
|
8
|
-
.youtube.com TRUE / TRUE 1800826170 __Secure-3PSIDTS sidts-CjUB7I_69MoGgU9hYcmJtLk5dHx1d0LjE7gLaoN86V6V3M8eyIyUH6LOPPFj2llBVQAVsulbqRAA
|
|
9
|
-
.youtube.com TRUE / FALSE 1803850170 HSID Ab4swI6vjvKUnUkhu
|
|
10
|
-
.youtube.com TRUE / TRUE 1803850170 SSID AaxSz8h17yLe-BmmX
|
|
11
|
-
.youtube.com TRUE / FALSE 1803850170 APISID pQgk6uQUwAqRtThp/Ape45AAtgBDxnzVco
|
|
12
|
-
.youtube.com TRUE / TRUE 1803850170 SAPISID y_RgVkrBXATC2XrL/Avlu8CWS6dwZ7LTEg
|
|
13
|
-
.youtube.com TRUE / TRUE 1803850170 __Secure-1PAPISID y_RgVkrBXATC2XrL/Avlu8CWS6dwZ7LTEg
|
|
14
|
-
.youtube.com TRUE / TRUE 1803850170 __Secure-3PAPISID y_RgVkrBXATC2XrL/Avlu8CWS6dwZ7LTEg
|
|
15
|
-
.youtube.com TRUE / FALSE 1803850170 SID g.a0006AhyolP4RoeyrQXcHsFnRGkuVLVIZ9QQsgpwFaXGd822GTtb98Ev6KQIph7SQPbMD4aekgACgYKAaMSARMSFQHGX2MijLBjWKdOhQm5VmUkh9wlRhoVAUF8yKqE_WWJQO8PgiZpXVV1MiB30076
|
|
16
|
-
.youtube.com TRUE / TRUE 1803850170 __Secure-1PSID g.a0006AhyolP4RoeyrQXcHsFnRGkuVLVIZ9QQsgpwFaXGd822GTtb9khH10B1YSjuMht-R8LNjAACgYKAQ8SARMSFQHGX2MiWX5D8_L5MAZK1TLabPQMghoVAUF8yKoFUiQLoi8zHLuskryhcx1P0076
|
|
17
|
-
.youtube.com TRUE / TRUE 1803850170 __Secure-3PSID g.a0006AhyolP4RoeyrQXcHsFnRGkuVLVIZ9QQsgpwFaXGd822GTtbc5VtVrqvDy9fWzO4vguctgACgYKAUkSARMSFQHGX2MinKYgh3CtvTW5E5T8SwXiERoVAUF8yKqzIJTS4ajNX5uO8nh3r0j10076
|
|
18
|
-
.youtube.com TRUE / TRUE 1803850170 LOGIN_INFO AFmmF2swRQIgH6IrdLYFOhW8xu4opIUHTGxAf33LzI5EXw6pdoqsh_gCIQCxYHQL4VZEyb3nE-rhXM6VU2ua99G4AhzKl4JJlzZBEQ:QUQ3MjNmeFpJOS1rVDgtM2I4cm50Z3M4NUplLTlSbXV4OFl4MTM5eUtHX3FEcVNmT2ltVFlSWmpQdlR2QTVqR29mZlUtTVh4OEhkOGw4Rkd5d2tQZm52NXJmQzdrMFJPVk5uYTFLNFFMZWlGelJhRVk1TkVaMm9Uby0xWlcwR3ozYkwwY2lWT21pV2dpU0lsWW5saFhMektISWxwUkRUcXl3
|
|
19
|
-
.youtube.com TRUE / FALSE 1803305074 SIDCC AKEyXzW6XAqY9tNhDWlFzlv-TcDtmpxQxQjOpTwkeLq7ddP20Ecx5ttbRGa7K7WKJVXYBIsbGTE
|
|
20
|
-
.youtube.com TRUE / TRUE 1803305074 __Secure-1PSIDCC AKEyXzVdeiA5hPJOoWk_iVZXhBC3-3D4ZAlHjwkIdtnQ-y_iHxOeAdYucag2Dv-EMebAtINo-Q
|
|
21
|
-
.youtube.com TRUE / TRUE 1803305074 __Secure-3PSIDCC AKEyXzX9LV_1LP1Z1T6SCow5CKbPxteZg-XPIOt3o2Uu6OlsIn6Z2S3gRZBgUTHpaiRcVbPYbg
|
|
22
|
-
.youtube.com TRUE / TRUE 1787290794 __Secure-YNID 16.YT=uhIB3j65_j5F-nN_AIVu93J6J4tqe4fviGdBQuGk0X5EW7VRESfm4qMadhbgM04D7x1YyYRXKI0UIHJjiRMWqsP5PmT_qrHoTUSn4MOLA-FPeX09ELODYdpGvps9F6B3EFC2jC4MXYiWVY6KGZRiEoYBh_KWkh4Suwgn38LgfqN0E4Xv3YAzCB390qeKtLVkaqlQDUIvhnqGBPrObW-DzawPn00gHQph8VM2Zrklq_BzRYVaZHKHs2WkKlHqLljxwOiNXcsLrIkLqGsCgKR_onVOnlWN2gd0zojNv0VyuwYgfB9GBOLzweTnTWBZmp9IPWRA39DH4zMFerdTND-Yfg
|
|
23
|
-
.youtube.com TRUE / TRUE 0 YSC -LHUdUOZvC4
|
|
24
|
-
.youtube.com TRUE / TRUE 1787321074 VISITOR_INFO1_LIVE AuEVGsoJz2Y
|
|
25
|
-
.youtube.com TRUE / TRUE 1787321074 VISITOR_PRIVACY_METADATA CgJVUxIEGgAgVQ%3D%3D
|
|
26
|
-
.youtube.com TRUE / TRUE 1787290794 __Secure-ROLLOUT_TOKEN CIbdoKq8kOipXhC__cjij6WSAxiZi8-rseySAw%3D%3D
|