@mks2508/bundlp 0.1.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.
Files changed (83) hide show
  1. package/README.md +495 -0
  2. package/dist/core/extractor.d.ts +30 -0
  3. package/dist/core/extractor.d.ts.map +1 -0
  4. package/dist/http/client.d.ts +50 -0
  5. package/dist/http/client.d.ts.map +1 -0
  6. package/dist/http/retry.d.ts +22 -0
  7. package/dist/http/retry.d.ts.map +1 -0
  8. package/dist/index.d.ts +20 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +19021 -0
  11. package/dist/innertube/client.d.ts +62 -0
  12. package/dist/innertube/client.d.ts.map +1 -0
  13. package/dist/player/ast/analyzer.d.ts +16 -0
  14. package/dist/player/ast/analyzer.d.ts.map +1 -0
  15. package/dist/player/ast/extractor.d.ts +35 -0
  16. package/dist/player/ast/extractor.d.ts.map +1 -0
  17. package/dist/player/ast/matchers.d.ts +40 -0
  18. package/dist/player/ast/matchers.d.ts.map +1 -0
  19. package/dist/player/cache.d.ts +60 -0
  20. package/dist/player/cache.d.ts.map +1 -0
  21. package/dist/player/player.d.ts +49 -0
  22. package/dist/player/player.d.ts.map +1 -0
  23. package/dist/po-token/botguard/challenge.d.ts +22 -0
  24. package/dist/po-token/botguard/challenge.d.ts.map +1 -0
  25. package/dist/po-token/botguard/client.d.ts +25 -0
  26. package/dist/po-token/botguard/client.d.ts.map +1 -0
  27. package/dist/po-token/cache/token-cache.d.ts +24 -0
  28. package/dist/po-token/cache/token-cache.d.ts.map +1 -0
  29. package/dist/po-token/index.d.ts +14 -0
  30. package/dist/po-token/index.d.ts.map +1 -0
  31. package/dist/po-token/manager.d.ts +34 -0
  32. package/dist/po-token/manager.d.ts.map +1 -0
  33. package/dist/po-token/minter/web-minter.d.ts +20 -0
  34. package/dist/po-token/minter/web-minter.d.ts.map +1 -0
  35. package/dist/po-token/policies.d.ts +18 -0
  36. package/dist/po-token/policies.d.ts.map +1 -0
  37. package/dist/po-token/providers/local.provider.d.ts +26 -0
  38. package/dist/po-token/providers/local.provider.d.ts.map +1 -0
  39. package/dist/po-token/providers/provider.interface.d.ts +15 -0
  40. package/dist/po-token/providers/provider.interface.d.ts.map +1 -0
  41. package/dist/po-token/types.d.ts +160 -0
  42. package/dist/po-token/types.d.ts.map +1 -0
  43. package/dist/result/index.d.ts +6 -0
  44. package/dist/result/index.d.ts.map +1 -0
  45. package/dist/result/result.types.d.ts +14 -0
  46. package/dist/result/result.types.d.ts.map +1 -0
  47. package/dist/result/result.utils.d.ts +32 -0
  48. package/dist/result/result.utils.d.ts.map +1 -0
  49. package/dist/streaming/dash/parser.d.ts +37 -0
  50. package/dist/streaming/dash/parser.d.ts.map +1 -0
  51. package/dist/streaming/dash/segments.d.ts +58 -0
  52. package/dist/streaming/dash/segments.d.ts.map +1 -0
  53. package/dist/streaming/decipher.d.ts +24 -0
  54. package/dist/streaming/decipher.d.ts.map +1 -0
  55. package/dist/streaming/drm.d.ts +26 -0
  56. package/dist/streaming/drm.d.ts.map +1 -0
  57. package/dist/streaming/formats.d.ts +20 -0
  58. package/dist/streaming/formats.d.ts.map +1 -0
  59. package/dist/streaming/hls/parser.d.ts +10 -0
  60. package/dist/streaming/hls/parser.d.ts.map +1 -0
  61. package/dist/streaming/hls/segments.d.ts +37 -0
  62. package/dist/streaming/hls/segments.d.ts.map +1 -0
  63. package/dist/streaming/processor.d.ts +20 -0
  64. package/dist/streaming/processor.d.ts.map +1 -0
  65. package/dist/types/error.types.d.ts +12 -0
  66. package/dist/types/error.types.d.ts.map +1 -0
  67. package/dist/types/index.d.ts +8 -0
  68. package/dist/types/index.d.ts.map +1 -0
  69. package/dist/types/innertube.types.d.ts +155 -0
  70. package/dist/types/innertube.types.d.ts.map +1 -0
  71. package/dist/types/player.types.d.ts +30 -0
  72. package/dist/types/player.types.d.ts.map +1 -0
  73. package/dist/types/video.types.d.ts +112 -0
  74. package/dist/types/video.types.d.ts.map +1 -0
  75. package/dist/utils/constants.d.ts +129 -0
  76. package/dist/utils/constants.d.ts.map +1 -0
  77. package/dist/utils/m3u8.d.ts +31 -0
  78. package/dist/utils/m3u8.d.ts.map +1 -0
  79. package/dist/utils/xml.d.ts +41 -0
  80. package/dist/utils/xml.d.ts.map +1 -0
  81. package/dist/validation/schemas.d.ts +290 -0
  82. package/dist/validation/schemas.d.ts.map +1 -0
  83. package/package.json +72 -0
package/README.md ADDED
@@ -0,0 +1,495 @@
1
+ # bundlp
2
+
3
+ > Bun-native TypeScript library for extracting YouTube video and audio streams
4
+
5
+ [![E2E Tests](https://img.shields.io/badge/E2E%20Tests-100%25%20PASS-brightgreen)]()
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)]()
7
+ [![Bun](https://img.shields.io/badge/Bun-1.0+-black)]()
8
+ [![License](https://img.shields.io/badge/License-MIT-yellow)]()
9
+
10
+ ## Features
11
+
12
+ - **Complete Extraction**: Video, audio, and combined formats with full metadata
13
+ - **Multi-Client Support**: ANDROID_SDKLESS, TV, WEB, IOS with automatic fallback
14
+ - **PO Token System**: Superior implementation with SQLite caching (12h TTL)
15
+ - **AST-Based Cipher**: Signature decryption using meriyah parser
16
+ - **Result Pattern**: Type-safe error handling with `Result<T, E>`
17
+ - **ArkType Validation**: Runtime type validation for API responses
18
+ - **HLS/DASH Support**: Manifest parsing for streaming formats
19
+ - **Zero Python**: Pure TypeScript, optimized for Bun runtime
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ bun add bundlp
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```typescript
30
+ import { YouTubeExtractor, isOk } from 'bundlp';
31
+
32
+ const extractor = new YouTubeExtractor();
33
+
34
+ // Extract from URL
35
+ const result = await extractor.extract('https://youtube.com/watch?v=dQw4w9WgXcQ');
36
+
37
+ if (isOk(result)) {
38
+ const video = result.value;
39
+
40
+ console.log('Title:', video.title);
41
+ console.log('Duration:', video.duration, 'seconds');
42
+ console.log('Channel:', video.channel.name);
43
+
44
+ // Get best audio
45
+ const bestAudio = video.formats.audio[0];
46
+ console.log('Best Audio URL:', bestAudio.url);
47
+ console.log('Audio Quality:', bestAudio.audioQuality);
48
+ console.log('Sample Rate:', bestAudio.audioSampleRate);
49
+
50
+ // Get best video
51
+ const bestVideo = video.formats.video[0];
52
+ console.log('Best Video URL:', bestVideo.url);
53
+ console.log('Resolution:', bestVideo.qualityLabel);
54
+ }
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### YouTubeExtractor
60
+
61
+ Main class for extracting video information.
62
+
63
+ ```typescript
64
+ import { YouTubeExtractor } from 'bundlp';
65
+
66
+ const extractor = new YouTubeExtractor({
67
+ cacheDir: '.cache', // Optional: cache directory for player.js
68
+ preferredClient: 'ANDROID_SDKLESS', // Optional: preferred InnerTube client
69
+ poToken: 'your-token' // Optional: static PO token
70
+ });
71
+ ```
72
+
73
+ #### Methods
74
+
75
+ ##### `extract(url: string): Promise<Result<VideoInfo, BundlpError>>`
76
+
77
+ Extracts complete video information from a YouTube URL or video ID.
78
+
79
+ **Supported URL formats:**
80
+ - `https://youtube.com/watch?v=VIDEO_ID`
81
+ - `https://youtu.be/VIDEO_ID`
82
+ - `https://youtube.com/shorts/VIDEO_ID`
83
+ - `https://youtube.com/embed/VIDEO_ID`
84
+ - `https://youtube.com/live/VIDEO_ID`
85
+ - `https://music.youtube.com/watch?v=VIDEO_ID`
86
+ - Direct video ID: `dQw4w9WgXcQ`
87
+
88
+ ### VideoInfo
89
+
90
+ Complete video information returned by `extract()`.
91
+
92
+ ```typescript
93
+ interface VideoInfo {
94
+ id: string; // Video ID
95
+ title: string; // Video title
96
+ description: string; // Video description
97
+ duration: number; // Duration in seconds
98
+ uploadDate?: string; // ISO date string
99
+ channel: ChannelInfo; // Channel information
100
+ viewCount: number; // View count
101
+ thumbnails: Thumbnail[]; // Available thumbnails
102
+ formats: FormatCollection; // All available formats
103
+ subtitles: Map<string, Subtitle[]>; // Subtitles by language
104
+ isLive: boolean; // Is live stream
105
+ isPrivate: boolean; // Is private video
106
+ }
107
+ ```
108
+
109
+ ### FormatCollection
110
+
111
+ Categorized formats for easy access.
112
+
113
+ ```typescript
114
+ interface FormatCollection {
115
+ combined: Format[]; // Video+Audio (progressive downloads)
116
+ video: Format[]; // Video-only (adaptive streaming)
117
+ audio: Format[]; // Audio-only (adaptive streaming)
118
+ hls?: HlsInfo; // HLS manifest info
119
+ dash?: DashInfo; // DASH manifest info
120
+ }
121
+ ```
122
+
123
+ ### Format
124
+
125
+ Individual format details.
126
+
127
+ ```typescript
128
+ interface Format {
129
+ itag: number; // YouTube format identifier
130
+ url: string; // Direct playback URL
131
+ mimeType: string; // MIME type (e.g., 'audio/webm')
132
+ codecs: string[]; // Codec list ['opus']
133
+ bitrate?: number; // Bitrate in bps
134
+
135
+ // Video properties
136
+ width?: number; // Video width
137
+ height?: number; // Video height
138
+ fps?: number; // Frames per second
139
+ qualityLabel?: string; // e.g., '1080p60'
140
+
141
+ // Audio properties
142
+ audioQuality?: string; // 'AUDIO_QUALITY_LOW/MEDIUM/HIGH'
143
+ audioSampleRate?: number; // Sample rate in Hz
144
+ audioChannels?: number; // Number of audio channels
145
+
146
+ // Metadata
147
+ contentLength?: number; // File size in bytes
148
+ approxDurationMs?: number; // Duration in milliseconds
149
+ hasDrm: boolean; // Has DRM protection
150
+ isAdaptive: boolean; // Is adaptive format
151
+ }
152
+ ```
153
+
154
+ ## Usage Examples
155
+
156
+ ### Get Best Audio URL
157
+
158
+ ```typescript
159
+ import { YouTubeExtractor, isOk } from 'bundlp';
160
+
161
+ async function getBestAudioUrl(videoUrl: string): Promise<string | null> {
162
+ const extractor = new YouTubeExtractor();
163
+ const result = await extractor.extract(videoUrl);
164
+
165
+ if (!isOk(result)) {
166
+ console.error('Extraction failed:', result.error.message);
167
+ return null;
168
+ }
169
+
170
+ const { audio } = result.value.formats;
171
+
172
+ if (audio.length === 0) {
173
+ console.error('No audio formats available');
174
+ return null;
175
+ }
176
+
177
+ // Formats are sorted by quality (best first)
178
+ return audio[0].url;
179
+ }
180
+
181
+ const audioUrl = await getBestAudioUrl('https://youtube.com/watch?v=dQw4w9WgXcQ');
182
+ console.log('Audio URL:', audioUrl);
183
+ ```
184
+
185
+ ### Get All Audio Formats with Details
186
+
187
+ ```typescript
188
+ import { YouTubeExtractor, isOk } from 'bundlp';
189
+
190
+ async function getAudioFormats(videoUrl: string) {
191
+ const extractor = new YouTubeExtractor();
192
+ const result = await extractor.extract(videoUrl);
193
+
194
+ if (!isOk(result)) return [];
195
+
196
+ return result.value.formats.audio.map(format => ({
197
+ itag: format.itag,
198
+ url: format.url,
199
+ mimeType: format.mimeType,
200
+ codecs: format.codecs.join(', '),
201
+ bitrate: format.bitrate ? `${Math.round(format.bitrate / 1000)} kbps` : 'unknown',
202
+ sampleRate: format.audioSampleRate ? `${format.audioSampleRate} Hz` : 'unknown',
203
+ channels: format.audioChannels || 2,
204
+ quality: format.audioQuality || 'unknown',
205
+ size: format.contentLength
206
+ ? `${(format.contentLength / 1024 / 1024).toFixed(2)} MB`
207
+ : 'unknown'
208
+ }));
209
+ }
210
+
211
+ const formats = await getAudioFormats('https://youtube.com/watch?v=dQw4w9WgXcQ');
212
+ console.table(formats);
213
+ ```
214
+
215
+ ### Get Video Metadata
216
+
217
+ ```typescript
218
+ import { YouTubeExtractor, isOk } from 'bundlp';
219
+
220
+ async function getVideoMetadata(videoUrl: string) {
221
+ const extractor = new YouTubeExtractor();
222
+ const result = await extractor.extract(videoUrl);
223
+
224
+ if (!isOk(result)) {
225
+ throw new Error(result.error.message);
226
+ }
227
+
228
+ const video = result.value;
229
+
230
+ return {
231
+ id: video.id,
232
+ title: video.title,
233
+ description: video.description,
234
+ duration: {
235
+ seconds: video.duration,
236
+ formatted: formatDuration(video.duration)
237
+ },
238
+ channel: {
239
+ name: video.channel.name,
240
+ id: video.channel.id,
241
+ url: video.channel.url
242
+ },
243
+ statistics: {
244
+ views: video.viewCount,
245
+ viewsFormatted: formatNumber(video.viewCount)
246
+ },
247
+ thumbnails: video.thumbnails.map(t => ({
248
+ url: t.url,
249
+ resolution: `${t.width}x${t.height}`
250
+ })),
251
+ uploadDate: video.uploadDate,
252
+ isLive: video.isLive,
253
+ availableFormats: {
254
+ video: video.formats.video.length,
255
+ audio: video.formats.audio.length,
256
+ combined: video.formats.combined.length
257
+ }
258
+ };
259
+ }
260
+
261
+ function formatDuration(seconds: number): string {
262
+ const h = Math.floor(seconds / 3600);
263
+ const m = Math.floor((seconds % 3600) / 60);
264
+ const s = seconds % 60;
265
+ if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
266
+ return `${m}:${s.toString().padStart(2, '0')}`;
267
+ }
268
+
269
+ function formatNumber(num: number): string {
270
+ if (num >= 1e9) return `${(num / 1e9).toFixed(1)}B`;
271
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
272
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
273
+ return num.toString();
274
+ }
275
+
276
+ const metadata = await getVideoMetadata('https://youtube.com/watch?v=dQw4w9WgXcQ');
277
+ console.log(JSON.stringify(metadata, null, 2));
278
+ ```
279
+
280
+ ### Download Best Quality Audio
281
+
282
+ ```typescript
283
+ import { YouTubeExtractor, isOk } from 'bundlp';
284
+
285
+ async function downloadAudio(videoUrl: string, outputPath: string) {
286
+ const extractor = new YouTubeExtractor();
287
+ const result = await extractor.extract(videoUrl);
288
+
289
+ if (!isOk(result)) {
290
+ throw new Error(`Extraction failed: ${result.error.message}`);
291
+ }
292
+
293
+ const bestAudio = result.value.formats.audio[0];
294
+
295
+ if (!bestAudio) {
296
+ throw new Error('No audio formats available');
297
+ }
298
+
299
+ console.log(`Downloading: ${result.value.title}`);
300
+ console.log(`Format: ${bestAudio.mimeType} (${bestAudio.codecs.join(', ')})`);
301
+ console.log(`Bitrate: ${Math.round((bestAudio.bitrate || 0) / 1000)} kbps`);
302
+
303
+ const response = await fetch(bestAudio.url);
304
+ const buffer = await response.arrayBuffer();
305
+
306
+ await Bun.write(outputPath, buffer);
307
+ console.log(`Saved to: ${outputPath}`);
308
+ }
309
+
310
+ await downloadAudio(
311
+ 'https://youtube.com/watch?v=dQw4w9WgXcQ',
312
+ 'audio.webm'
313
+ );
314
+ ```
315
+
316
+ ### Get HLS/DASH Streaming URLs
317
+
318
+ ```typescript
319
+ import { YouTubeExtractor, isOk } from 'bundlp';
320
+
321
+ async function getStreamingManifests(videoUrl: string) {
322
+ const extractor = new YouTubeExtractor();
323
+ const result = await extractor.extract(videoUrl);
324
+
325
+ if (!isOk(result)) return null;
326
+
327
+ const { hls, dash } = result.value.formats;
328
+
329
+ return {
330
+ hls: hls ? {
331
+ manifestUrl: hls.manifestUrl,
332
+ variants: hls.variants.map(v => ({
333
+ url: v.url,
334
+ bandwidth: `${Math.round(v.bandwidth / 1000)} kbps`,
335
+ resolution: v.resolution,
336
+ codecs: v.codecs
337
+ }))
338
+ } : null,
339
+ dash: dash ? {
340
+ manifestUrl: dash.manifestUrl,
341
+ duration: dash.duration
342
+ } : null
343
+ };
344
+ }
345
+
346
+ const manifests = await getStreamingManifests('https://youtube.com/watch?v=dQw4w9WgXcQ');
347
+ console.log(JSON.stringify(manifests, null, 2));
348
+ ```
349
+
350
+ ### Filter Formats by Criteria
351
+
352
+ ```typescript
353
+ import { YouTubeExtractor, isOk, type Format } from 'bundlp';
354
+
355
+ async function getFormatsFiltered(videoUrl: string, options: {
356
+ maxBitrate?: number;
357
+ codec?: string;
358
+ minQuality?: string;
359
+ }) {
360
+ const extractor = new YouTubeExtractor();
361
+ const result = await extractor.extract(videoUrl);
362
+
363
+ if (!isOk(result)) return { audio: [], video: [] };
364
+
365
+ const { audio, video } = result.value.formats;
366
+
367
+ const filterFormat = (f: Format) => {
368
+ if (options.maxBitrate && f.bitrate && f.bitrate > options.maxBitrate) return false;
369
+ if (options.codec && !f.codecs.some(c => c.includes(options.codec!))) return false;
370
+ return true;
371
+ };
372
+
373
+ return {
374
+ audio: audio.filter(filterFormat),
375
+ video: video.filter(filterFormat)
376
+ };
377
+ }
378
+
379
+ // Get only opus audio under 128kbps
380
+ const formats = await getFormatsFiltered('https://youtube.com/watch?v=dQw4w9WgXcQ', {
381
+ codec: 'opus',
382
+ maxBitrate: 128000
383
+ });
384
+ ```
385
+
386
+ ## Result Pattern
387
+
388
+ bundlp uses the Result pattern for type-safe error handling.
389
+
390
+ ```typescript
391
+ import { isOk, isErr, match, unwrapOr } from 'bundlp';
392
+
393
+ // Check result type
394
+ if (isOk(result)) {
395
+ console.log(result.value);
396
+ } else {
397
+ console.error(result.error);
398
+ }
399
+
400
+ // Pattern matching
401
+ match(result, {
402
+ ok: (video) => console.log('Success:', video.title),
403
+ err: (error) => console.error('Error:', error.message)
404
+ });
405
+
406
+ // Default value on error
407
+ const video = unwrapOr(result, defaultVideoInfo);
408
+ ```
409
+
410
+ ## Error Codes
411
+
412
+ | Code | Description |
413
+ |------|-------------|
414
+ | `INVALID_URL` | Could not parse video ID from URL |
415
+ | `VIDEO_UNAVAILABLE` | Video is unavailable or deleted |
416
+ | `NETWORK_ERROR` | Network request failed |
417
+ | `PARSE_ERROR` | Failed to parse response |
418
+ | `CIPHER_ERROR` | Signature decryption failed |
419
+ | `ALL_CLIENTS_FAILED` | All InnerTube clients failed |
420
+
421
+ ## CLI
422
+
423
+ bundlp includes a CLI for testing and debugging.
424
+
425
+ ```bash
426
+ # Extract video info
427
+ bun run cli extract https://youtube.com/watch?v=dQw4w9WgXcQ
428
+
429
+ # List formats
430
+ bun run cli formats https://youtube.com/watch?v=dQw4w9WgXcQ
431
+
432
+ # Debug extraction
433
+ bun run cli debug https://youtube.com/watch?v=dQw4w9WgXcQ
434
+
435
+ # Run benchmarks
436
+ bun run cli benchmark
437
+ ```
438
+
439
+ ## Performance
440
+
441
+ | Operation | Time (cached) | Time (fresh) |
442
+ |-----------|---------------|--------------|
443
+ | Full Extraction | ~130ms | ~5s |
444
+ | Player.js Parse | ~20ms | ~5s |
445
+ | PO Token | ~10ms | ~800ms |
446
+ | InnerTube Request | ~120ms | ~120ms |
447
+
448
+ ## Architecture
449
+
450
+ ```
451
+ src/
452
+ ├── core/ # YouTubeExtractor
453
+ ├── innertube/ # InnerTube API client
454
+ ├── player/ # Player.js + AST cipher extraction
455
+ ├── streaming/ # HLS/DASH processing
456
+ ├── po-token/ # PO Token system
457
+ ├── http/ # HTTP client with cookies
458
+ ├── result/ # Result<T,E> pattern
459
+ ├── types/ # TypeScript types
460
+ ├── validation/ # ArkType schemas
461
+ └── utils/ # Constants, parsers
462
+ ```
463
+
464
+ ## Documentation
465
+
466
+ - [API Reference](./docs/API-REFERENCE.md) - Complete API documentation
467
+ - [Examples](./docs/EXAMPLES.md) - More usage examples
468
+ - [Architecture](./ARCHITECTURE.md) - Technical architecture
469
+ - [Status](./docs/STATUS.md) - Current status and comparisons
470
+
471
+ ## Development
472
+
473
+ ```bash
474
+ # Install dependencies
475
+ bun install
476
+
477
+ # Run E2E tests
478
+ bun test:e2e
479
+
480
+ # Run unit tests
481
+ bun test
482
+
483
+ # Type check
484
+ bunx tsc --noEmit
485
+
486
+ # Lint
487
+ bun run lint
488
+
489
+ # CLI
490
+ bun run cli
491
+ ```
492
+
493
+ ## License
494
+
495
+ MIT
@@ -0,0 +1,30 @@
1
+ import { type Result } from '../result';
2
+ import type { VideoInfo } from '../types/video.types';
3
+ import { type BundlpError } from '../types/error.types';
4
+ import type { ClientName } from '../utils/constants';
5
+ export interface ExtractorConfig {
6
+ poToken?: string;
7
+ cacheDir?: string;
8
+ preferredClient?: ClientName;
9
+ }
10
+ /**
11
+ * Main YouTube video extractor class.
12
+ * Handles video information extraction using InnerTube API.
13
+ */
14
+ export declare class YouTubeExtractor {
15
+ private client;
16
+ private player;
17
+ private config;
18
+ constructor(config?: ExtractorConfig);
19
+ /**
20
+ * Extracts video information from a YouTube URL or video ID.
21
+ * @param url - YouTube URL or video ID
22
+ * @returns Result containing VideoInfo or BundlpError
23
+ */
24
+ extract(url: string): Promise<Result<VideoInfo, BundlpError>>;
25
+ private parseVideoId;
26
+ private realExtract;
27
+ private processFormats;
28
+ private assembleVideoInfo;
29
+ }
30
+ //# sourceMappingURL=extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/core/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,MAAM,EAAS,MAAM,WAAW,CAAC;AAIxD,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,UAAU,CAAC;CAChC;AAaD;;;GAGG;AACH,qBAAa,gBAAgB;IACzB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,GAAE,eAAoB;IAMxC;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAgBnE,OAAO,CAAC,YAAY;YAcN,WAAW;YAyBX,cAAc;IAK5B,OAAO,CAAC,iBAAiB;CA2B5B"}
@@ -0,0 +1,50 @@
1
+ import { type Result } from '../result';
2
+ import type { BundlpError } from '../types/error.types';
3
+ export interface HttpClientOptions extends RequestInit {
4
+ timeout?: number;
5
+ maxRedirects?: number;
6
+ manualRedirects?: boolean;
7
+ }
8
+ interface Cookie {
9
+ name: string;
10
+ value: string;
11
+ domain?: string;
12
+ path?: string;
13
+ expires?: Date;
14
+ secure?: boolean;
15
+ httpOnly?: boolean;
16
+ }
17
+ /**
18
+ * HTTP client with cookie jar and controlled redirect handling.
19
+ * Superior to YouTube.js: tracks cookies across redirects, prevents loops.
20
+ */
21
+ export declare class HttpClient {
22
+ private cookies;
23
+ /**
24
+ * Performs a fetch request with automatic cookie and redirect handling.
25
+ * @param url - The URL to fetch
26
+ * @param options - Fetch options. Use manualRedirects=true for controlled redirects
27
+ * @returns Result containing the Response or a BundlpError
28
+ */
29
+ request(url: string, options?: HttpClientOptions): Promise<Result<Response, BundlpError>>;
30
+ clearCookies(): void;
31
+ getCookies(): Map<string, Cookie>;
32
+ /**
33
+ * Performs a GET request.
34
+ * @param url - The URL to fetch
35
+ * @param options - Optional fetch options
36
+ * @returns Result containing the Response or a BundlpError
37
+ */
38
+ get(url: string, options?: HttpClientOptions): Promise<Result<Response, BundlpError>>;
39
+ /**
40
+ * Performs a POST request with JSON body.
41
+ * @param url - The URL to post to
42
+ * @param body - The request body (will be JSON stringified)
43
+ * @param options - Optional fetch options
44
+ * @returns Result containing the Response or a BundlpError
45
+ */
46
+ post(url: string, body: unknown, options?: HttpClientOptions): Promise<Result<Response, BundlpError>>;
47
+ }
48
+ export declare const httpClient: HttpClient;
49
+ export {};
50
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/http/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,MAAM;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AA8BD;;;GAGG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,OAAO,CAAkC;IAEjD;;;;;OAKG;IACG,OAAO,CACT,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAkFzC,YAAY,IAAI,IAAI;IAIpB,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIjC;;;;;OAKG;IACG,GAAG,CACL,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAIzC;;;;;;OAMG;IACG,IAAI,CACN,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;CAW5C;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Retry utilities with exponential backoff.
3
+ * Provides automatic retry logic for transient failures.
4
+ * @module http/retry
5
+ */
6
+ export interface RetryOptions {
7
+ retries?: number;
8
+ delay?: number;
9
+ backoff?: number;
10
+ maxDelay?: number;
11
+ shouldRetry?: (error: Error) => boolean;
12
+ onRetry?: (error: Error, attempt: number) => void;
13
+ }
14
+ /**
15
+ * Executes a function with automatic retry on failure.
16
+ * @param fn - The async function to execute
17
+ * @param options - Retry configuration options
18
+ * @returns The result of the function if successful
19
+ * @throws The last error if all retries are exhausted
20
+ */
21
+ export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
22
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/http/retry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,YAAY;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAID;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC7B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,YAAiB,GAC3B,OAAO,CAAC,CAAC,CAAC,CA2BZ"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * bundlp - Bun-native YouTube video/music resolver
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { YouTubeExtractor, isOk } from 'bundlp';
7
+ *
8
+ * const extractor = new YouTubeExtractor();
9
+ * const result = await extractor.extract('https://youtube.com/watch?v=dQw4w9WgXcQ');
10
+ *
11
+ * if (isOk(result)) {
12
+ * console.log(result.value.title);
13
+ * console.log(result.value.formats.video);
14
+ * }
15
+ * ```
16
+ */
17
+ export { type Result, type ResultOk, type ResultErr, ok, err, isOk, isErr, match, map, mapErr, andThen, unwrapOr, unwrap, tryCatch, tryCatchAsync, } from './result';
18
+ export type { VideoInfo, ChannelInfo, Thumbnail, FormatCollection, Format, AudioFormat, VideoFormat, HlsInfo, HlsVariant, DashInfo, Subtitle, Chapter, Storyboard, ErrorCode, BundlpError, ClientName, ClientConfig, InnerTubeContext, PlayerResponse, StreamingData, PlayerInfo, SignatureCipher, } from './types';
19
+ export { YouTubeExtractor, type ExtractorConfig } from './core/extractor';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EACL,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,EAAE,EACF,GAAG,EACH,IAAI,EACJ,KAAK,EACL,KAAK,EACL,GAAG,EACH,MAAM,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,aAAa,GACd,MAAM,UAAU,CAAC;AAGlB,YAAY,EAEV,SAAS,EACT,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,WAAW,EACX,WAAW,EACX,OAAO,EACP,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,UAAU,EAEV,SAAS,EACT,WAAW,EAEX,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,aAAa,EAEb,UAAU,EACV,eAAe,GAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC"}