soundcord 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shuzo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # soundcord
2
+
3
+ YouTube audio streaming for Discord bots. Simple API, no bloat.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install soundcord
9
+ ```
10
+
11
+ ### Dependencies
12
+
13
+ You need `yt-dlp` and `ffmpeg` installed. Run the setup helper:
14
+
15
+ ```bash
16
+ npx soundcord-setup
17
+ ```
18
+
19
+ Or install manually:
20
+ - [yt-dlp](https://github.com/yt-dlp/yt-dlp)
21
+ - [ffmpeg](https://ffmpeg.org/download.html)
22
+
23
+ ## Usage
24
+
25
+ ```typescript
26
+ import { YouTube } from 'soundcord';
27
+
28
+ const yt = new YouTube();
29
+
30
+ // Search videos
31
+ const results = await yt.search('never gonna give you up');
32
+ console.log(results[0].title);
33
+
34
+ // Get video info
35
+ const info = await yt.getInfo('dQw4w9WgXcQ');
36
+ console.log(info.title, info.duration);
37
+
38
+ // Get audio stream URL (for discord.js voice)
39
+ const stream = await yt.getStream('dQw4w9WgXcQ');
40
+ console.log(stream.url);
41
+ ```
42
+
43
+ ### With discord.js
44
+
45
+ ```typescript
46
+ import { YouTube } from 'soundcord';
47
+ import { createAudioResource, createAudioPlayer, joinVoiceChannel } from '@discordjs/voice';
48
+
49
+ const yt = new YouTube();
50
+
51
+ // Join voice channel
52
+ const connection = joinVoiceChannel({
53
+ channelId: voiceChannel.id,
54
+ guildId: guild.id,
55
+ adapterCreator: guild.voiceAdapterCreator,
56
+ });
57
+
58
+ // Play audio
59
+ const { url } = await yt.getStream('dQw4w9WgXcQ');
60
+ const resource = createAudioResource(url);
61
+ const player = createAudioPlayer();
62
+
63
+ player.play(resource);
64
+ connection.subscribe(player);
65
+ ```
66
+
67
+ ## API
68
+
69
+ ### `new YouTube(options?)`
70
+
71
+ | Option | Type | Default | Description |
72
+ |--------|------|---------|-------------|
73
+ | `timeout` | number | 15000 | Request timeout in ms |
74
+ | `minDuration` | number | 0 | Min video duration to include in search |
75
+ | `cacheTTL` | number | 300000 | Stream URL cache duration (5min) |
76
+ | `ytdlpPath` | string | auto | Custom path to yt-dlp binary |
77
+
78
+ ### Methods
79
+
80
+ #### `search(query, limit?): Promise<SearchResult[]>`
81
+ Search YouTube videos. Returns up to `limit` results (default 10).
82
+
83
+ #### `getInfo(urlOrId): Promise<VideoInfo>`
84
+ Get video metadata by URL or ID.
85
+
86
+ #### `getStream(urlOrId): Promise<StreamInfo>`
87
+ Get audio stream URL for playback.
88
+
89
+ #### `isValidUrl(url): boolean`
90
+ Check if string is a valid YouTube URL.
91
+
92
+ #### `extractId(url): string | null`
93
+ Extract video ID from URL.
94
+
95
+ ## Types
96
+
97
+ ```typescript
98
+ interface SearchResult {
99
+ id: string;
100
+ title: string;
101
+ author: string;
102
+ duration: number;
103
+ thumbnail: string;
104
+ }
105
+
106
+ interface VideoInfo {
107
+ id: string;
108
+ title: string;
109
+ author: string;
110
+ duration: number;
111
+ thumbnail: string;
112
+ url: string;
113
+ }
114
+
115
+ interface StreamInfo {
116
+ url: string;
117
+ mimeType: string;
118
+ bitrate: number;
119
+ }
120
+ ```
121
+
122
+ ## Error Handling
123
+
124
+ ```typescript
125
+ import { YouTube, YouTubeError } from 'soundcord';
126
+
127
+ const yt = new YouTube();
128
+
129
+ try {
130
+ await yt.getStream('invalid');
131
+ } catch (err) {
132
+ if (err instanceof YouTubeError) {
133
+ console.log(err.code); // 'NOT_FOUND' | 'NETWORK_ERROR' | 'YTDLP_MISSING' | etc.
134
+ }
135
+ }
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,4 @@
1
+ export { YouTube } from './youtube';
2
+ export { VideoInfo, SearchResult, StreamInfo, YouTubeOptions, YouTubeError } from './types';
3
+ export { YouTube as DiscordYT } from './youtube';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DiscordYT = exports.YouTubeError = exports.YouTube = void 0;
4
+ var youtube_1 = require("./youtube");
5
+ Object.defineProperty(exports, "YouTube", { enumerable: true, get: function () { return youtube_1.YouTube; } });
6
+ var types_1 = require("./types");
7
+ Object.defineProperty(exports, "YouTubeError", { enumerable: true, get: function () { return types_1.YouTubeError; } });
8
+ var youtube_2 = require("./youtube");
9
+ Object.defineProperty(exports, "DiscordYT", { enumerable: true, get: function () { return youtube_2.YouTube; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAAoC;AAA3B,kGAAA,OAAO,OAAA;AAChB,iCAA4F;AAA9B,qGAAA,YAAY,OAAA;AAC1E,qCAAiD;AAAxC,oGAAA,OAAO,OAAa"}
@@ -0,0 +1,12 @@
1
+ import { VideoInfo, SearchResult, StreamInfo } from './types';
2
+ export declare class Invidious {
3
+ private instances;
4
+ private currentIndex;
5
+ private timeout;
6
+ constructor(customInstances?: string[], timeout?: number);
7
+ private fetch;
8
+ search(query: string, limit?: number): Promise<SearchResult[]>;
9
+ getVideoInfo(videoId: string): Promise<VideoInfo>;
10
+ getAudioStream(videoId: string): Promise<StreamInfo>;
11
+ }
12
+ //# sourceMappingURL=invidious.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invidious.d.ts","sourceRoot":"","sources":["../src/invidious.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAqB,MAAM,SAAS,CAAC;AAMjF,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,OAAO,CAAS;gBAEZ,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAc;YAMjD,KAAK;IA0Cb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAYlE,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAajD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAuB3D"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Invidious = void 0;
4
+ const DEFAULT_INSTANCES = [
5
+ 'https://yewtu.be'
6
+ ];
7
+ class Invidious {
8
+ constructor(customInstances, timeout = 10000) {
9
+ this.currentIndex = 0;
10
+ const urls = customInstances?.length ? customInstances : DEFAULT_INSTANCES;
11
+ this.instances = urls.map(url => ({ url: url.replace(/\/$/, ''), healthy: true }));
12
+ this.timeout = timeout;
13
+ }
14
+ async fetch(endpoint) {
15
+ let lastError = null;
16
+ for (let i = 0; i < this.instances.length; i++) {
17
+ const instance = this.instances[(this.currentIndex + i) % this.instances.length];
18
+ if (!instance.healthy)
19
+ continue;
20
+ try {
21
+ const controller = new AbortController();
22
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
23
+ const response = await fetch(`${instance.url}${endpoint}`, {
24
+ signal: controller.signal,
25
+ headers: {
26
+ 'Accept': 'application/json',
27
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
28
+ }
29
+ });
30
+ clearTimeout(timeoutId);
31
+ if (!response.ok) {
32
+ throw new Error(`HTTP ${response.status}`);
33
+ }
34
+ this.currentIndex = (this.currentIndex + i) % this.instances.length;
35
+ return await response.json();
36
+ }
37
+ catch (error) {
38
+ console.log(`❌ Instance ${instance.url} failed:`, error.message);
39
+ instance.healthy = false;
40
+ lastError = error;
41
+ // Réactive l'instance après 5 minutes
42
+ setTimeout(() => { instance.healthy = true; }, 5 * 60 * 1000);
43
+ }
44
+ }
45
+ throw new Error(`Toutes les instances ont échoué: ${lastError?.message}`);
46
+ }
47
+ async search(query, limit = 10) {
48
+ const data = await this.fetch(`/api/v1/search?q=${encodeURIComponent(query)}&type=video`);
49
+ return data.slice(0, limit).map(item => ({
50
+ id: item.videoId,
51
+ title: item.title,
52
+ author: item.author,
53
+ duration: item.lengthSeconds,
54
+ thumbnail: item.videoThumbnails?.[0]?.url || ''
55
+ }));
56
+ }
57
+ async getVideoInfo(videoId) {
58
+ const data = await this.fetch(`/api/v1/videos/${videoId}`);
59
+ return {
60
+ id: data.videoId,
61
+ title: data.title,
62
+ author: data.author,
63
+ duration: data.lengthSeconds,
64
+ thumbnail: data.videoThumbnails?.[0]?.url || '',
65
+ url: `https://www.youtube.com/watch?v=${data.videoId}`
66
+ };
67
+ }
68
+ async getAudioStream(videoId) {
69
+ const data = await this.fetch(`/api/v1/videos/${videoId}`);
70
+ // Cherche le meilleur stream audio
71
+ const audioFormats = data.adaptiveFormats?.filter((f) => f.type?.startsWith('audio/')) || [];
72
+ if (!audioFormats.length) {
73
+ throw new Error('Aucun stream audio disponible');
74
+ }
75
+ // Trie par bitrate décroissant
76
+ audioFormats.sort((a, b) => (b.bitrate || 0) - (a.bitrate || 0));
77
+ const best = audioFormats[0];
78
+ return {
79
+ url: best.url,
80
+ mimeType: best.type,
81
+ bitrate: best.bitrate || 0
82
+ };
83
+ }
84
+ }
85
+ exports.Invidious = Invidious;
86
+ //# sourceMappingURL=invidious.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invidious.js","sourceRoot":"","sources":["../src/invidious.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":""}
package/dist/setup.js ADDED
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const child_process_1 = require("child_process");
38
+ const readline = __importStar(require("readline"));
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ const isWin = process.platform === 'win32';
43
+ const isMac = process.platform === 'darwin';
44
+ // configurable install paths
45
+ const config = {
46
+ win: {
47
+ installDir: process.env.DISCORD_YT_INSTALL_DIR || path.join(os.homedir(), 'soundcord-deps'),
48
+ },
49
+ unix: {
50
+ installDir: process.env.DISCORD_YT_INSTALL_DIR || '/usr/local/bin',
51
+ }
52
+ };
53
+ const installDir = isWin ? config.win.installDir : config.unix.installDir;
54
+ // terminal colors
55
+ const c = {
56
+ reset: '\x1b[0m',
57
+ bold: '\x1b[1m',
58
+ dim: '\x1b[2m',
59
+ cyan: '\x1b[36m',
60
+ green: '\x1b[32m',
61
+ red: '\x1b[31m',
62
+ yellow: '\x1b[33m',
63
+ magenta: '\x1b[35m',
64
+ };
65
+ const frames = ['|', '/', '-', '\\'];
66
+ let spinnerInterval = null;
67
+ let spinnerFrame = 0;
68
+ function startSpinner(text) {
69
+ process.stdout.write('\x1b[?25l'); // hide cursor
70
+ spinnerInterval = setInterval(() => {
71
+ process.stdout.write(`\r${c.cyan}${frames[spinnerFrame]}${c.reset} ${text}`);
72
+ spinnerFrame = (spinnerFrame + 1) % frames.length;
73
+ }, 100);
74
+ }
75
+ function stopSpinner(success, text) {
76
+ if (spinnerInterval)
77
+ clearInterval(spinnerInterval);
78
+ process.stdout.write('\x1b[?25h'); // show cursor
79
+ const icon = success ? `${c.green}+${c.reset}` : `${c.red}x${c.reset}`;
80
+ process.stdout.write(`\r${icon} ${text}\n`);
81
+ }
82
+ function banner() {
83
+ console.log();
84
+ console.log(` ${c.cyan}${c.bold}soundcord setup${c.reset}`);
85
+ console.log(` ${c.dim}YouTube streaming for Discord bots${c.reset}`);
86
+ console.log();
87
+ }
88
+ function printBox(title, lines) {
89
+ console.log();
90
+ console.log(` ${c.cyan}${title}${c.reset}`);
91
+ for (const line of lines) {
92
+ console.log(` ${line}`);
93
+ }
94
+ console.log();
95
+ }
96
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
97
+ function ask(question) {
98
+ return new Promise(resolve => {
99
+ rl.question(` ${c.magenta}?${c.reset} ${question} ${c.dim}(y/n)${c.reset} `, answer => {
100
+ resolve(answer.toLowerCase().startsWith('y'));
101
+ });
102
+ });
103
+ }
104
+ function hasCmd(cmd) {
105
+ try {
106
+ (0, child_process_1.execSync)(isWin ? `where ${cmd}` : `which ${cmd}`, { stdio: 'ignore' });
107
+ return true;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ }
113
+ function detectPackageManager() {
114
+ if (isWin) {
115
+ if (hasCmd('choco'))
116
+ return 'choco';
117
+ return null;
118
+ }
119
+ if (isMac) {
120
+ if (hasCmd('brew'))
121
+ return 'brew';
122
+ return null;
123
+ }
124
+ // linux
125
+ if (hasCmd('apt'))
126
+ return 'apt';
127
+ if (hasCmd('dnf'))
128
+ return 'dnf';
129
+ if (hasCmd('pacman'))
130
+ return 'pacman';
131
+ if (hasCmd('brew'))
132
+ return 'brew'; // linuxbrew
133
+ return null;
134
+ }
135
+ async function downloadFile(url, dest) {
136
+ return new Promise((resolve, reject) => {
137
+ if (isWin) {
138
+ (0, child_process_1.exec)(`powershell -Command "Invoke-WebRequest -Uri '${url}' -OutFile '${dest}'"`, { timeout: 120000 }, (err) => err ? reject(err) : resolve());
139
+ }
140
+ else {
141
+ (0, child_process_1.exec)(`curl -fsSL "${url}" -o "${dest}"`, { timeout: 120000 }, (err) => err ? reject(err) : resolve());
142
+ }
143
+ });
144
+ }
145
+ async function installYtdlp() {
146
+ const pm = detectPackageManager();
147
+ // try package manager first
148
+ if (pm) {
149
+ startSpinner(`Installing yt-dlp via ${pm}...`);
150
+ try {
151
+ const cmds = {
152
+ apt: 'sudo apt update && sudo apt install -y yt-dlp',
153
+ dnf: 'sudo dnf install -y yt-dlp',
154
+ pacman: 'sudo pacman -S --noconfirm yt-dlp',
155
+ brew: 'brew install yt-dlp',
156
+ choco: 'choco install yt-dlp -y',
157
+ };
158
+ (0, child_process_1.execSync)(cmds[pm], { stdio: 'ignore', timeout: 300000 });
159
+ stopSpinner(true, 'yt-dlp installed');
160
+ return true;
161
+ }
162
+ catch (err) {
163
+ stopSpinner(false, `${pm} install failed, trying direct download...`);
164
+ }
165
+ }
166
+ // fallback: direct download
167
+ startSpinner('Downloading yt-dlp...');
168
+ fs.mkdirSync(installDir, { recursive: true });
169
+ const filename = isWin ? 'yt-dlp.exe' : 'yt-dlp';
170
+ const dest = path.join(installDir, filename);
171
+ const url = isWin
172
+ ? 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe'
173
+ : 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp';
174
+ try {
175
+ await downloadFile(url, dest);
176
+ if (!isWin) {
177
+ fs.chmodSync(dest, 0o755);
178
+ }
179
+ stopSpinner(true, `yt-dlp installed to ${dest}`);
180
+ return true;
181
+ }
182
+ catch (err) {
183
+ stopSpinner(false, `Download failed: ${err.message}`);
184
+ console.log(` ${c.dim}Manual install: pip install yt-dlp${c.reset}`);
185
+ return false;
186
+ }
187
+ }
188
+ async function installFfmpeg() {
189
+ const pm = detectPackageManager();
190
+ if (pm && pm !== 'choco') {
191
+ startSpinner(`Installing ffmpeg via ${pm}...`);
192
+ try {
193
+ const cmds = {
194
+ apt: 'sudo apt update && sudo apt install -y ffmpeg',
195
+ dnf: 'sudo dnf install -y ffmpeg',
196
+ pacman: 'sudo pacman -S --noconfirm ffmpeg',
197
+ brew: 'brew install ffmpeg',
198
+ };
199
+ if (cmds[pm]) {
200
+ (0, child_process_1.execSync)(cmds[pm], { stdio: 'ignore', timeout: 600000 });
201
+ stopSpinner(true, 'ffmpeg installed');
202
+ return true;
203
+ }
204
+ }
205
+ catch (err) {
206
+ stopSpinner(false, `${pm} install failed`);
207
+ }
208
+ }
209
+ if (isWin) {
210
+ // windows: download ffmpeg essentials
211
+ const tmp = path.join(os.tmpdir(), 'ffmpeg.zip');
212
+ const extract = path.join(os.tmpdir(), 'ffmpeg_extract');
213
+ try {
214
+ startSpinner('Downloading ffmpeg (this may take a while)...');
215
+ await downloadFile('https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip', tmp);
216
+ stopSpinner(true, 'Download complete');
217
+ startSpinner('Extracting...');
218
+ (0, child_process_1.execSync)(`powershell -Command "Expand-Archive -Path '${tmp}' -DestinationPath '${extract}' -Force"`, { stdio: 'ignore' });
219
+ fs.mkdirSync(installDir, { recursive: true });
220
+ const folders = fs.readdirSync(extract).filter(f => f.startsWith('ffmpeg-'));
221
+ if (folders.length > 0) {
222
+ const binSrc = path.join(extract, folders[0], 'bin');
223
+ for (const f of fs.readdirSync(binSrc)) {
224
+ fs.copyFileSync(path.join(binSrc, f), path.join(installDir, f));
225
+ }
226
+ }
227
+ // cleanup
228
+ try {
229
+ fs.rmSync(tmp, { force: true });
230
+ fs.rmSync(extract, { recursive: true, force: true });
231
+ }
232
+ catch {
233
+ // ignore cleanup errors
234
+ }
235
+ stopSpinner(true, `ffmpeg installed to ${installDir}`);
236
+ return true;
237
+ }
238
+ catch (err) {
239
+ stopSpinner(false, `Failed: ${err.message}`);
240
+ console.log(` ${c.dim}Manual install: https://ffmpeg.org/download.html${c.reset}`);
241
+ return false;
242
+ }
243
+ }
244
+ // unix without package manager
245
+ console.log(` ${c.yellow}!${c.reset} No supported package manager found`);
246
+ console.log(` ${c.dim}Install ffmpeg manually: https://ffmpeg.org/download.html${c.reset}`);
247
+ return false;
248
+ }
249
+ async function main() {
250
+ console.clear();
251
+ banner();
252
+ console.log(` ${c.bold}Checking dependencies...${c.reset}\n`);
253
+ const ytdlpPaths = isWin
254
+ ? ['yt-dlp', path.join(installDir, 'yt-dlp.exe')]
255
+ : ['yt-dlp', path.join(installDir, 'yt-dlp')];
256
+ const ffmpegPaths = isWin
257
+ ? ['ffmpeg', path.join(installDir, 'ffmpeg.exe')]
258
+ : ['ffmpeg'];
259
+ const hasYtdlp = hasCmd('yt-dlp') || ytdlpPaths.some(p => fs.existsSync(p));
260
+ const hasFfmpeg = hasCmd('ffmpeg') || ffmpegPaths.some(p => fs.existsSync(p));
261
+ console.log(` ${hasYtdlp ? c.green + '+' : c.red + 'x'}${c.reset} yt-dlp`);
262
+ console.log(` ${hasFfmpeg ? c.green + '+' : c.red + 'x'}${c.reset} ffmpeg`);
263
+ console.log();
264
+ if (hasYtdlp && hasFfmpeg) {
265
+ printBox('Ready!', [
266
+ `${c.green}All dependencies installed${c.reset}`,
267
+ '',
268
+ `${c.dim}import { YouTube } from 'soundcord'${c.reset}`,
269
+ ]);
270
+ rl.close();
271
+ return;
272
+ }
273
+ const missing = [];
274
+ if (!hasYtdlp)
275
+ missing.push('yt-dlp');
276
+ if (!hasFfmpeg)
277
+ missing.push('ffmpeg');
278
+ console.log(` ${c.yellow}!${c.reset} Missing: ${c.bold}${missing.join(', ')}${c.reset}\n`);
279
+ if (!hasYtdlp && await ask('Install yt-dlp?')) {
280
+ console.log();
281
+ await installYtdlp();
282
+ }
283
+ if (!hasFfmpeg && await ask('Install ffmpeg?')) {
284
+ console.log();
285
+ await installFfmpeg();
286
+ }
287
+ // show PATH instructions if needed
288
+ if (isWin && installDir !== 'C:\\Windows\\System32') {
289
+ const inPath = (process.env.PATH || '').toLowerCase().includes(installDir.toLowerCase());
290
+ if (!inPath) {
291
+ printBox('Add to PATH', [
292
+ `Add this folder to your system PATH:`,
293
+ `${c.cyan}${installDir}${c.reset}`,
294
+ '',
295
+ `Then restart your terminal.`,
296
+ ]);
297
+ }
298
+ }
299
+ printBox('Usage', [
300
+ `${c.dim}npm install soundcord${c.reset}`,
301
+ '',
302
+ `${c.cyan}import { YouTube } from 'soundcord'${c.reset}`,
303
+ `${c.cyan}const yt = new YouTube()${c.reset}`,
304
+ ]);
305
+ rl.close();
306
+ }
307
+ main().catch(err => {
308
+ console.error(`\n ${c.red}Error:${c.reset} ${err.message}\n`);
309
+ process.exit(1);
310
+ });
311
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ interface DecipherFunction {
2
+ (signature: string): string;
3
+ }
4
+ export declare function getDecipherFunction(html: string): Promise<DecipherFunction>;
5
+ export declare function decodeSignatureCipher(signatureCipher: string, decipher: DecipherFunction): string;
6
+ export {};
7
+ //# sourceMappingURL=signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB;IACxB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAKD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8BjF;AAsHD,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAYjG"}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDecipherFunction = getDecipherFunction;
4
+ exports.decodeSignatureCipher = decodeSignatureCipher;
5
+ const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
6
+ let cachedDecipher = null;
7
+ let cachedPlayerUrl = null;
8
+ async function getDecipherFunction(html) {
9
+ // Extraire l'URL du player JS
10
+ const playerUrlMatch = html.match(/\/s\/player\/[^"]+\/player_ias\.vflset\/[^"]+\/base\.js/);
11
+ if (!playerUrlMatch) {
12
+ throw new Error('Impossible de trouver le player JS');
13
+ }
14
+ const playerUrl = 'https://www.youtube.com' + playerUrlMatch[0];
15
+ // Utiliser le cache si même player
16
+ if (cachedDecipher && cachedPlayerUrl === playerUrl) {
17
+ return cachedDecipher;
18
+ }
19
+ // Télécharger le player JS
20
+ const response = await fetch(playerUrl, {
21
+ headers: { 'User-Agent': USER_AGENT }
22
+ });
23
+ if (!response.ok) {
24
+ throw new Error(`Erreur téléchargement player: HTTP ${response.status}`);
25
+ }
26
+ const playerJs = await response.text();
27
+ // Extraire la fonction de déchiffrement
28
+ cachedDecipher = extractDecipherFunction(playerJs);
29
+ cachedPlayerUrl = playerUrl;
30
+ return cachedDecipher;
31
+ }
32
+ function extractDecipherFunction(playerJs) {
33
+ // Trouver le nom de la fonction principale
34
+ const funcNameMatch = playerJs.match(/\b([a-zA-Z0-9$]+)\s*=\s*function\(\s*a\s*\)\s*\{\s*a\s*=\s*a\.split\(\s*""\s*\)/);
35
+ if (!funcNameMatch) {
36
+ throw new Error('Impossible de trouver la fonction de déchiffrement');
37
+ }
38
+ const mainFuncName = funcNameMatch[1];
39
+ // Extraire le corps de la fonction principale
40
+ const mainFuncMatch = playerJs.match(new RegExp(escapeRegex(mainFuncName) + '\\s*=\\s*function\\([^)]*\\)\\s*\\{([^}]+)\\}'));
41
+ if (!mainFuncMatch) {
42
+ throw new Error('Impossible d\'extraire la fonction principale');
43
+ }
44
+ const mainFuncBody = mainFuncMatch[1];
45
+ // Trouver l'objet helper utilisé (ex: Xo, $n, etc.)
46
+ const helperMatch = mainFuncBody.match(/([a-zA-Z0-9$]+)\.[a-zA-Z0-9$]+\(/);
47
+ if (!helperMatch) {
48
+ throw new Error('Impossible de trouver l\'objet helper');
49
+ }
50
+ const helperName = helperMatch[1];
51
+ // Extraire l'objet helper
52
+ const helperMatch2 = playerJs.match(new RegExp('var\\s+' + escapeRegex(helperName) + '\\s*=\\s*\\{([\\s\\S]*?)\\};'));
53
+ if (!helperMatch2) {
54
+ throw new Error('Impossible d\'extraire l\'objet helper');
55
+ }
56
+ const helperBody = helperMatch2[1];
57
+ // Parser les méthodes helper
58
+ const helpers = parseHelperMethods(helperBody);
59
+ // Créer la fonction de déchiffrement
60
+ return createDecipherFunction(mainFuncBody, helperName, helpers);
61
+ }
62
+ function parseHelperMethods(helperBody) {
63
+ const helpers = {};
64
+ // Reverse: fonction qui inverse le tableau
65
+ const reverseMatch = helperBody.match(/([a-zA-Z0-9$]+)\s*:\s*function\s*\(\s*a\s*\)\s*\{\s*a\.reverse\(\)/);
66
+ if (reverseMatch) {
67
+ helpers[reverseMatch[1]] = (arr) => { arr.reverse(); };
68
+ }
69
+ // Splice: fonction qui supprime le premier élément n fois
70
+ const spliceMatch = helperBody.match(/([a-zA-Z0-9$]+)\s*:\s*function\s*\(\s*a\s*,\s*b\s*\)\s*\{\s*a\.splice\(\s*0\s*,\s*b\s*\)/);
71
+ if (spliceMatch) {
72
+ helpers[spliceMatch[1]] = (arr, b) => { arr.splice(0, b); };
73
+ }
74
+ // Swap: fonction qui échange des éléments
75
+ const swapMatch = helperBody.match(/([a-zA-Z0-9$]+)\s*:\s*function\s*\(\s*a\s*,\s*b\s*\)\s*\{\s*var\s+c\s*=\s*a\s*\[\s*0\s*\]/);
76
+ if (swapMatch) {
77
+ helpers[swapMatch[1]] = (arr, b) => {
78
+ const c = arr[0];
79
+ arr[0] = arr[b % arr.length];
80
+ arr[b % arr.length] = c;
81
+ };
82
+ }
83
+ return helpers;
84
+ }
85
+ function createDecipherFunction(funcBody, helperName, helpers) {
86
+ // Parser les opérations
87
+ const operations = [];
88
+ const regex = new RegExp(escapeRegex(helperName) + '\\.([a-zA-Z0-9$]+)\\(a,(\\d+)\\)', 'g');
89
+ let match;
90
+ while ((match = regex.exec(funcBody)) !== null) {
91
+ operations.push({
92
+ method: match[1],
93
+ arg: parseInt(match[2])
94
+ });
95
+ }
96
+ // Aussi chercher les appels sans argument (reverse)
97
+ const regex2 = new RegExp(escapeRegex(helperName) + '\\.([a-zA-Z0-9$]+)\\(a\\)', 'g');
98
+ // Reset lastIndex for the new regex
99
+ funcBody.replace(regex2, (_, method) => {
100
+ // Insérer au bon endroit - simplifié ici
101
+ return _;
102
+ });
103
+ return (signature) => {
104
+ const arr = signature.split('');
105
+ for (const op of operations) {
106
+ const helper = helpers[op.method];
107
+ if (helper) {
108
+ helper(arr, op.arg || 0);
109
+ }
110
+ }
111
+ return arr.join('');
112
+ };
113
+ }
114
+ function escapeRegex(str) {
115
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
116
+ }
117
+ function decodeSignatureCipher(signatureCipher, decipher) {
118
+ const params = new URLSearchParams(signatureCipher);
119
+ const url = params.get('url');
120
+ const s = params.get('s');
121
+ const sp = params.get('sp') || 'signature';
122
+ if (!url || !s) {
123
+ throw new Error('signatureCipher invalide');
124
+ }
125
+ const decodedSig = decipher(decodeURIComponent(s));
126
+ return `${decodeURIComponent(url)}&${sp}=${encodeURIComponent(decodedSig)}`;
127
+ }
128
+ //# sourceMappingURL=signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.js","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhF,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAU;gBAEb,OAAO,CAAC,EAAE,gBAAgB;IAIhC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIlE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAI1C,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIlC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAG7C"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":";;;AAAA,uCAAoC;AAGpC,MAAa,SAAS;IAGpB,YAAY,OAA0B;QACpC,IAAI,CAAC,OAAO,GAAG,IAAI,iBAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE;QAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAa;QACzB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAa;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3C,OAAO,MAAM,CAAC,GAAG,CAAC;IACpB,CAAC;IAED,UAAU,CAAC,KAAa;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;CACF;AA/BD,8BA+BC"}
@@ -0,0 +1,36 @@
1
+ export interface VideoInfo {
2
+ id: string;
3
+ title: string;
4
+ author: string;
5
+ duration: number;
6
+ thumbnail: string;
7
+ url: string;
8
+ }
9
+ export interface SearchResult {
10
+ id: string;
11
+ title: string;
12
+ author: string;
13
+ duration: number;
14
+ thumbnail: string;
15
+ }
16
+ export interface StreamInfo {
17
+ url: string;
18
+ mimeType: string;
19
+ bitrate: number;
20
+ }
21
+ export interface YouTubeOptions {
22
+ /** request timeout in ms (default: 15000) */
23
+ timeout?: number;
24
+ /** minimum video duration in seconds to include in search results (default: 0) */
25
+ minDuration?: number;
26
+ /** how long to cache stream URLs in ms (default: 300000 = 5min) */
27
+ cacheTTL?: number;
28
+ /** custom path to yt-dlp binary */
29
+ ytdlpPath?: string;
30
+ }
31
+ export declare class YouTubeError extends Error {
32
+ code: 'PARSE_ERROR' | 'NETWORK_ERROR' | 'NOT_FOUND' | 'YTDLP_MISSING' | 'RATE_LIMITED';
33
+ cause?: Error | undefined;
34
+ constructor(message: string, code: 'PARSE_ERROR' | 'NETWORK_ERROR' | 'NOT_FOUND' | 'YTDLP_MISSING' | 'RATE_LIMITED', cause?: Error | undefined);
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAa,SAAQ,KAAK;IAG5B,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc;IACtF,KAAK,CAAC,EAAE,KAAK;gBAFpB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc,EACtF,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB"}
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.YouTubeError = void 0;
4
+ class YouTubeError extends Error {
5
+ constructor(message, code, cause) {
6
+ super(message);
7
+ this.code = code;
8
+ this.cause = cause;
9
+ this.name = 'YouTubeError';
10
+ }
11
+ }
12
+ exports.YouTubeError = YouTubeError;
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAkCA,MAAa,YAAa,SAAQ,KAAK;IACrC,YACE,OAAe,EACR,IAAsF,EACtF,KAAa;QAEpB,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,SAAI,GAAJ,IAAI,CAAkF;QACtF,UAAK,GAAL,KAAK,CAAQ;QAGpB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AATD,oCASC"}
@@ -0,0 +1,21 @@
1
+ import { VideoInfo, SearchResult, StreamInfo, YouTubeOptions } from './types';
2
+ export declare class YouTube {
3
+ private timeout;
4
+ private minDuration;
5
+ private cacheTTL;
6
+ private ytdlp;
7
+ private ytdlpReady;
8
+ constructor(options?: YouTubeOptions);
9
+ private findYtdlp;
10
+ search(query: string, limit?: number): Promise<SearchResult[]>;
11
+ private webSearch;
12
+ private ytdlpSearch;
13
+ getInfo(input: string): Promise<VideoInfo>;
14
+ private webInfo;
15
+ private ytdlpInfo;
16
+ getStream(input: string): Promise<StreamInfo>;
17
+ isValidUrl(url: string): boolean;
18
+ extractId(input: string): string | null;
19
+ private parseDur;
20
+ }
21
+ //# sourceMappingURL=youtube.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"youtube.d.ts","sourceRoot":"","sources":["../src/youtube.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,SAAS,CAAC;AAsC5F,qBAAa,OAAO;IAClB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,UAAU,CAAgB;gBAEtB,OAAO,GAAE,cAAmB;YAc1B,SAAS;IAgBjB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YA8BlD,SAAS;YAqDT,WAAW;IAkBnB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;YA8BlC,OAAO;YA4CP,SAAS;IAgBjB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA+BnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIvC,OAAO,CAAC,QAAQ;CAMjB"}
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.YouTube = void 0;
4
+ const types_1 = require("./types");
5
+ const child_process_1 = require("child_process");
6
+ const util_1 = require("util");
7
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
8
+ // rotate user agents to reduce detection
9
+ const USER_AGENTS = [
10
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
11
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
12
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
13
+ ];
14
+ const streamCache = new Map();
15
+ // basic rate limit: track last request time
16
+ let lastRequestTime = 0;
17
+ const MIN_REQUEST_INTERVAL = 500; // ms between requests
18
+ function getRandomUA() {
19
+ return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
20
+ }
21
+ // sanitize input for shell commands to prevent injection
22
+ function escapeShellArg(arg) {
23
+ // remove any characters that could be dangerous
24
+ return arg.replace(/[;&|`$(){}[\]<>\\!*?#~'"]/g, '');
25
+ }
26
+ async function rateLimit() {
27
+ const now = Date.now();
28
+ const elapsed = now - lastRequestTime;
29
+ if (elapsed < MIN_REQUEST_INTERVAL) {
30
+ await new Promise(r => setTimeout(r, MIN_REQUEST_INTERVAL - elapsed));
31
+ }
32
+ lastRequestTime = Date.now();
33
+ }
34
+ class YouTube {
35
+ constructor(options = {}) {
36
+ this.ytdlp = null;
37
+ this.timeout = options.timeout ?? 15000;
38
+ this.minDuration = options.minDuration ?? 0;
39
+ this.cacheTTL = options.cacheTTL ?? 5 * 60 * 1000;
40
+ // allow custom ytdlp path or auto-detect
41
+ if (options.ytdlpPath) {
42
+ this.ytdlp = options.ytdlpPath;
43
+ this.ytdlpReady = Promise.resolve();
44
+ }
45
+ else {
46
+ this.ytdlpReady = this.findYtdlp();
47
+ }
48
+ }
49
+ async findYtdlp() {
50
+ const paths = process.platform === 'win32'
51
+ ? ['yt-dlp', 'C:\\ffmpeg\\bin\\yt-dlp.exe', './yt-dlp.exe']
52
+ : ['yt-dlp', '/usr/local/bin/yt-dlp', './yt-dlp'];
53
+ for (const p of paths) {
54
+ try {
55
+ await execAsync(`"${p}" --version`);
56
+ this.ytdlp = p;
57
+ return;
58
+ }
59
+ catch (err) {
60
+ // try next path
61
+ }
62
+ }
63
+ }
64
+ async search(query, limit = 10) {
65
+ await this.ytdlpReady;
66
+ let webError;
67
+ try {
68
+ return await this.webSearch(query, limit);
69
+ }
70
+ catch (err) {
71
+ webError = err;
72
+ }
73
+ // fallback to yt-dlp
74
+ if (this.ytdlp) {
75
+ try {
76
+ return await this.ytdlpSearch(query, limit);
77
+ }
78
+ catch (err) {
79
+ throw new types_1.YouTubeError(`search failed: web (${webError?.message}), yt-dlp (${err.message})`, 'NETWORK_ERROR', err);
80
+ }
81
+ }
82
+ throw new types_1.YouTubeError('search failed and yt-dlp not available', 'YTDLP_MISSING', webError);
83
+ }
84
+ async webSearch(query, limit) {
85
+ await rateLimit();
86
+ const ctrl = new AbortController();
87
+ const timer = setTimeout(() => ctrl.abort(), this.timeout);
88
+ try {
89
+ const res = await fetch(`https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`, {
90
+ signal: ctrl.signal,
91
+ headers: { 'User-Agent': getRandomUA(), 'Accept-Language': 'en-US,en;q=0.9' }
92
+ });
93
+ clearTimeout(timer);
94
+ if (res.status === 429) {
95
+ throw new types_1.YouTubeError('rate limited by YouTube', 'RATE_LIMITED');
96
+ }
97
+ if (!res.ok) {
98
+ throw new types_1.YouTubeError(`http ${res.status}`, 'NETWORK_ERROR');
99
+ }
100
+ const html = await res.text();
101
+ const m = html.match(/var ytInitialData = (.+?);<\/script>/);
102
+ if (!m) {
103
+ throw new types_1.YouTubeError('failed to parse YouTube response', 'PARSE_ERROR');
104
+ }
105
+ const data = JSON.parse(m[1]);
106
+ const contents = data?.contents?.twoColumnSearchResultsRenderer?.primaryContents
107
+ ?.sectionListRenderer?.contents?.[0]?.itemSectionRenderer?.contents || [];
108
+ const results = [];
109
+ for (const item of contents) {
110
+ if (results.length >= limit)
111
+ break;
112
+ const v = item?.videoRenderer;
113
+ if (!v?.lengthText?.simpleText)
114
+ continue;
115
+ const dur = this.parseDur(v.lengthText.simpleText);
116
+ if (dur < this.minDuration)
117
+ continue;
118
+ results.push({
119
+ id: v.videoId,
120
+ title: v.title?.runs?.[0]?.text || '',
121
+ author: v.ownerText?.runs?.[0]?.text || '',
122
+ duration: dur,
123
+ thumbnail: v.thumbnail?.thumbnails?.[0]?.url || ''
124
+ });
125
+ }
126
+ return results;
127
+ }
128
+ finally {
129
+ clearTimeout(timer);
130
+ }
131
+ }
132
+ async ytdlpSearch(query, limit) {
133
+ const safeQuery = escapeShellArg(query);
134
+ const { stdout } = await execAsync(`"${this.ytdlp}" "ytsearch${limit}:${safeQuery}" --flat-playlist --no-warnings --print "%(id)s|%(title)s|%(channel)s|%(duration)s|%(thumbnail)s"`, { maxBuffer: 10 * 1024 * 1024 });
135
+ const results = [];
136
+ for (const line of stdout.trim().split('\n')) {
137
+ if (!line)
138
+ continue;
139
+ const [id, title, author, dur, thumb] = line.split('|');
140
+ const duration = parseInt(dur) || 0;
141
+ if (duration < this.minDuration)
142
+ continue;
143
+ results.push({ id, title, author: author || '', duration, thumbnail: thumb || '' });
144
+ }
145
+ return results;
146
+ }
147
+ async getInfo(input) {
148
+ await this.ytdlpReady;
149
+ const id = this.extractId(input) || input;
150
+ let webError;
151
+ try {
152
+ return await this.webInfo(id);
153
+ }
154
+ catch (err) {
155
+ webError = err;
156
+ }
157
+ if (this.ytdlp) {
158
+ try {
159
+ return await this.ytdlpInfo(id);
160
+ }
161
+ catch (err) {
162
+ throw new types_1.YouTubeError(`getInfo failed for ${id}`, 'NETWORK_ERROR', err);
163
+ }
164
+ }
165
+ throw new types_1.YouTubeError(`getInfo failed for ${id}: ${webError?.message}`, 'NOT_FOUND', webError);
166
+ }
167
+ async webInfo(id) {
168
+ await rateLimit();
169
+ const ctrl = new AbortController();
170
+ const timer = setTimeout(() => ctrl.abort(), this.timeout);
171
+ try {
172
+ const res = await fetch(`https://www.youtube.com/watch?v=${id}`, {
173
+ signal: ctrl.signal,
174
+ headers: { 'User-Agent': getRandomUA(), 'Accept-Language': 'en-US,en;q=0.9' }
175
+ });
176
+ clearTimeout(timer);
177
+ if (res.status === 429) {
178
+ throw new types_1.YouTubeError('rate limited by YouTube', 'RATE_LIMITED');
179
+ }
180
+ if (!res.ok) {
181
+ throw new types_1.YouTubeError(`http ${res.status}`, 'NETWORK_ERROR');
182
+ }
183
+ const html = await res.text();
184
+ const m = html.match(/var ytInitialPlayerResponse = ({.+?});(?:var|<\/script>)/s);
185
+ if (!m) {
186
+ throw new types_1.YouTubeError('failed to parse video page', 'PARSE_ERROR');
187
+ }
188
+ const d = JSON.parse(m[1])?.videoDetails;
189
+ if (!d) {
190
+ throw new types_1.YouTubeError('video not found or unavailable', 'NOT_FOUND');
191
+ }
192
+ return {
193
+ id,
194
+ title: d.title || '',
195
+ author: d.author || '',
196
+ duration: parseInt(d.lengthSeconds || '0'),
197
+ thumbnail: d.thumbnail?.thumbnails?.[0]?.url || '',
198
+ url: `https://www.youtube.com/watch?v=${id}`
199
+ };
200
+ }
201
+ finally {
202
+ clearTimeout(timer);
203
+ }
204
+ }
205
+ async ytdlpInfo(id) {
206
+ const { stdout } = await execAsync(`"${this.ytdlp}" "https://www.youtube.com/watch?v=${id}" --no-playlist --no-warnings --print "%(title)s|%(channel)s|%(duration)s|%(thumbnail)s"`, { maxBuffer: 1024 * 1024 });
207
+ const [title, author, dur, thumb] = stdout.trim().split('|');
208
+ return {
209
+ id,
210
+ title,
211
+ author: author || '',
212
+ duration: parseInt(dur) || 0,
213
+ thumbnail: thumb || '',
214
+ url: `https://www.youtube.com/watch?v=${id}`
215
+ };
216
+ }
217
+ async getStream(input) {
218
+ await this.ytdlpReady;
219
+ const id = this.extractId(input) || input;
220
+ // check cache
221
+ const cached = streamCache.get(id);
222
+ if (cached && Date.now() - cached.ts < this.cacheTTL) {
223
+ return { url: cached.url, mimeType: 'audio/webm', bitrate: 128000 };
224
+ }
225
+ if (!this.ytdlp) {
226
+ throw new types_1.YouTubeError('yt-dlp required for streaming - install from https://github.com/yt-dlp/yt-dlp', 'YTDLP_MISSING');
227
+ }
228
+ const { stdout } = await execAsync(`"${this.ytdlp}" "https://www.youtube.com/watch?v=${id}" -f bestaudio/best -g --no-playlist --no-warnings`, { maxBuffer: 1024 * 1024 });
229
+ const url = stdout.trim();
230
+ if (!url) {
231
+ throw new types_1.YouTubeError('no stream url returned', 'NOT_FOUND');
232
+ }
233
+ streamCache.set(id, { url, ts: Date.now() });
234
+ return { url, mimeType: 'audio/webm', bitrate: 128000 };
235
+ }
236
+ isValidUrl(url) {
237
+ return /(?:youtube\.com\/watch\?v=|youtu\.be\/)[\w-]{11}/.test(url);
238
+ }
239
+ extractId(input) {
240
+ return input.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]{11})/)?.[1] || null;
241
+ }
242
+ parseDur(str) {
243
+ const p = str.split(':').map(Number);
244
+ if (p.length === 2)
245
+ return p[0] * 60 + p[1];
246
+ if (p.length === 3)
247
+ return p[0] * 3600 + p[1] * 60 + p[2];
248
+ return 0;
249
+ }
250
+ }
251
+ exports.YouTube = YouTube;
252
+ //# sourceMappingURL=youtube.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"youtube.js","sourceRoot":"","sources":["../src/youtube.ts"],"names":[],"mappings":";;;AAAA,mCAA4F;AAC5F,iDAAqC;AACrC,+BAAiC;AAEjC,MAAM,SAAS,GAAG,IAAA,gBAAS,EAAC,oBAAI,CAAC,CAAC;AAElC,yCAAyC;AACzC,MAAM,WAAW,GAAG;IAClB,iHAAiH;IACjH,uHAAuH;IACvH,uGAAuG;CACxG,CAAC;AAEF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuC,CAAC;AAEnE,4CAA4C;AAC5C,IAAI,eAAe,GAAG,CAAC,CAAC;AACxB,MAAM,oBAAoB,GAAG,GAAG,CAAC,CAAC,sBAAsB;AAExD,SAAS,WAAW;IAClB,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,yDAAyD;AACzD,SAAS,cAAc,CAAC,GAAW;IACjC,gDAAgD;IAChD,OAAO,GAAG,CAAC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,OAAO,GAAG,GAAG,GAAG,eAAe,CAAC;IACtC,IAAI,OAAO,GAAG,oBAAoB,EAAE,CAAC;QACnC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAC/B,CAAC;AAED,MAAa,OAAO;IAOlB,YAAY,UAA0B,EAAE;QAHhC,UAAK,GAAkB,IAAI,CAAC;QAIlC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;QACxC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAElD,yCAAyC;QACzC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO;YACxC,CAAC,CAAC,CAAC,QAAQ,EAAE,6BAA6B,EAAE,cAAc,CAAC;YAC3D,CAAC,CAAC,CAAC,QAAQ,EAAE,uBAAuB,EAAE,UAAU,CAAC,CAAC;QAEpD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACpC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;gBACf,OAAO;YACT,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,gBAAgB;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAK,GAAG,EAAE;QACpC,MAAM,IAAI,CAAC,UAAU,CAAC;QAEtB,IAAI,QAA2B,CAAC;QAChC,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,GAAG,GAAY,CAAC;QAC1B,CAAC;QAED,qBAAqB;QACrB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,oBAAY,CACpB,uBAAuB,QAAQ,EAAE,OAAO,cAAe,GAAa,CAAC,OAAO,GAAG,EAC/E,eAAe,EACf,GAAY,CACb,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,IAAI,oBAAY,CACpB,wCAAwC,EACxC,eAAe,EACf,QAAQ,CACT,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,KAAa;QAClD,MAAM,SAAS,EAAE,CAAC;QAElB,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,gDAAgD,kBAAkB,CAAC,KAAK,CAAC,EAAE,EAAE;gBACnG,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,iBAAiB,EAAE,gBAAgB,EAAE;aAC9E,CAAC,CAAC;YACH,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,MAAM,IAAI,oBAAY,CAAC,yBAAyB,EAAE,cAAc,CAAC,CAAC;YACpE,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,oBAAY,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;YAChE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;YAC7D,IAAI,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM,IAAI,oBAAY,CAAC,kCAAkC,EAAE,aAAa,CAAC,CAAC;YAC5E,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,EAAE,8BAA8B,EAAE,eAAe;gBAC9E,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,QAAQ,IAAI,EAAE,CAAC;YAE5E,MAAM,OAAO,GAAmB,EAAE,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;gBACnC,MAAM,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC;gBAC9B,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU;oBAAE,SAAS;gBAEzC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;gBACnD,IAAI,GAAG,GAAG,IAAI,CAAC,WAAW;oBAAE,SAAS;gBAErC,OAAO,CAAC,IAAI,CAAC;oBACX,EAAE,EAAE,CAAC,CAAC,OAAO;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE;oBACrC,MAAM,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE;oBAC1C,QAAQ,EAAE,GAAG;oBACb,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE;iBACnD,CAAC,CAAC;YACL,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,KAAa,EAAE,KAAa;QACpD,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,IAAI,IAAI,CAAC,KAAK,cAAc,KAAK,IAAI,SAAS,mGAAmG,EACjJ,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAChC,CAAC;QAEF,MAAM,OAAO,GAAmB,EAAE,CAAC;QACnC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,QAAQ,GAAG,IAAI,CAAC,WAAW;gBAAE,SAAS;YAC1C,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAa;QACzB,MAAM,IAAI,CAAC,UAAU,CAAC;QACtB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;QAE1C,IAAI,QAA2B,CAAC;QAChC,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,GAAG,GAAY,CAAC;QAC1B,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,oBAAY,CACpB,sBAAsB,EAAE,EAAE,EAC1B,eAAe,EACf,GAAY,CACb,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,IAAI,oBAAY,CACpB,sBAAsB,EAAE,KAAK,QAAQ,EAAE,OAAO,EAAE,EAChD,WAAW,EACX,QAAQ,CACT,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,EAAU;QAC9B,MAAM,SAAS,EAAE,CAAC;QAElB,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,mCAAmC,EAAE,EAAE,EAAE;gBAC/D,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,iBAAiB,EAAE,gBAAgB,EAAE;aAC9E,CAAC,CAAC;YACH,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,MAAM,IAAI,oBAAY,CAAC,yBAAyB,EAAE,cAAc,CAAC,CAAC;YACpE,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,oBAAY,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;YAChE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAClF,IAAI,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM,IAAI,oBAAY,CAAC,4BAA4B,EAAE,aAAa,CAAC,CAAC;YACtE,CAAC;YAED,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC;YACzC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM,IAAI,oBAAY,CAAC,gCAAgC,EAAE,WAAW,CAAC,CAAC;YACxE,CAAC;YAED,OAAO;gBACL,EAAE;gBACF,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;gBACpB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;gBACtB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,IAAI,GAAG,CAAC;gBAC1C,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE;gBAClD,GAAG,EAAE,mCAAmC,EAAE,EAAE;aAC7C,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,EAAU;QAChC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,IAAI,IAAI,CAAC,KAAK,sCAAsC,EAAE,0FAA0F,EAChJ,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,CAC3B,CAAC;QACF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO;YACL,EAAE;YACF,KAAK;YACL,MAAM,EAAE,MAAM,IAAI,EAAE;YACpB,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YAC5B,SAAS,EAAE,KAAK,IAAI,EAAE;YACtB,GAAG,EAAE,mCAAmC,EAAE,EAAE;SAC7C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,MAAM,IAAI,CAAC,UAAU,CAAC;QACtB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;QAE1C,cAAc;QACd,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrD,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,oBAAY,CACpB,+EAA+E,EAC/E,eAAe,CAChB,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,IAAI,IAAI,CAAC,KAAK,sCAAsC,EAAE,oDAAoD,EAC1G,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,CAC3B,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,oBAAY,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;QAChE,CAAC;QAED,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC1D,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,OAAO,kDAAkD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,OAAO,KAAK,CAAC,KAAK,CAAC,oDAAoD,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACxF,CAAC;IAEO,QAAQ,CAAC,GAAW;QAC1B,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,CAAC,CAAC;IACX,CAAC;CACF;AAjRD,0BAiRC"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "soundcord",
3
+ "version": "1.0.1",
4
+ "description": "Simple YouTube audio streaming for Discord bots",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "soundcord-setup": "dist/setup.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "discord",
20
+ "youtube",
21
+ "music",
22
+ "audio",
23
+ "stream",
24
+ "bot"
25
+ ],
26
+ "author": "Shuzo",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "@types/node": "^25.0.10",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }