@ooneex/youtube 0.0.4

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) 2025 Ooneex
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 @@
1
+ # @ooneex/youtube
@@ -0,0 +1,439 @@
1
+ import { ArgsOptions, FormatOptions, PlaylistInfo, QualityOptions, VideoFormat, VideoInfo, VideoProgress } from "ytdlp-nodejs";
2
+ /**
3
+ * Video information returned from YouTube.
4
+ * Contains metadata such as title, description, duration, view count, etc.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const info: YoutubeVideoInfoType = await youtube.getVideoInfo(url);
9
+ * console.log(info.title, info.duration, info.view_count);
10
+ * ```
11
+ */
12
+ type YoutubeVideoInfoType = VideoInfo;
13
+ /**
14
+ * Playlist information returned from YouTube.
15
+ * Contains playlist metadata and list of videos in the playlist.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const playlist: YoutubePlaylistInfoType = await youtube.getPlaylistInfo(url);
20
+ * console.log(playlist.title, playlist.entries.length);
21
+ * ```
22
+ */
23
+ type YoutubePlaylistInfoType = PlaylistInfo;
24
+ /**
25
+ * Video format information including codec, resolution, and bitrate details.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const info = await youtube.getVideoInfo(url);
30
+ * const formats: YoutubeVideoFormatType[] = info.formats;
31
+ * formats.forEach(f => console.log(f.format_id, f.ext, f.resolution));
32
+ * ```
33
+ */
34
+ type YoutubeVideoFormatType = VideoFormat;
35
+ /**
36
+ * Download progress information for tracking video download status.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const progress: YoutubeVideoProgressType = {
41
+ * percent: 50,
42
+ * totalSize: "100MB",
43
+ * currentSpeed: "5MB/s",
44
+ * eta: "10s",
45
+ * };
46
+ * ```
47
+ */
48
+ type YoutubeVideoProgressType = VideoProgress;
49
+ /**
50
+ * Additional arguments options for yt-dlp commands.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const args: YoutubeArgsOptionsType = {
55
+ * noPlaylist: true,
56
+ * extractAudio: true,
57
+ * };
58
+ * ```
59
+ */
60
+ type YoutubeArgsOptionsType = ArgsOptions;
61
+ /**
62
+ * Format options for downloading videos with specific quality settings.
63
+ *
64
+ * @typeParam F - The format keyword type (e.g., "video", "audio", "videoandaudio")
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const options: YoutubeFormatOptionsType<"video"> = {
69
+ * format: { quality: "1080p" },
70
+ * };
71
+ * ```
72
+ */
73
+ type YoutubeFormatOptionsType<F extends YoutubeFormatKeyWordType> = FormatOptions<F>;
74
+ /**
75
+ * Quality options for specifying video/audio quality preferences.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const quality: YoutubeQualityOptionsType = {
80
+ * video: "1080p",
81
+ * audio: "highest",
82
+ * };
83
+ * ```
84
+ */
85
+ type YoutubeQualityOptionsType = QualityOptions;
86
+ /**
87
+ * Format keyword type for specifying download format categories.
88
+ * Can be "video", "audio", or "videoandaudio".
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const format: YoutubeFormatKeyWordType = "videoandaudio";
93
+ * ```
94
+ */
95
+ type YoutubeFormatKeyWordType = keyof QualityOptions;
96
+ /**
97
+ * Video quality presets for download operations.
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const quality: YoutubeVideoQualityType = "1080p";
102
+ *
103
+ * // Available options:
104
+ * // - "2160p" (4K)
105
+ * // - "1440p" (2K)
106
+ * // - "1080p" (Full HD)
107
+ * // - "720p" (HD)
108
+ * // - "480p" (SD)
109
+ * // - "360p", "240p", "144p" (Low quality)
110
+ * // - "highest" (Best available)
111
+ * // - "lowest" (Smallest file size)
112
+ * ```
113
+ */
114
+ type YoutubeVideoQualityType = "2160p" | "1440p" | "1080p" | "720p" | "480p" | "360p" | "240p" | "144p" | "highest" | "lowest";
115
+ /**
116
+ * Audio quality presets for audio download operations.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const quality: YoutubeAudioQualityType = "highest";
121
+ *
122
+ * // Available options:
123
+ * // - "highest" (Best available audio quality)
124
+ * // - "lowest" (Smallest file size)
125
+ * ```
126
+ */
127
+ type YoutubeAudioQualityType = "highest" | "lowest";
128
+ /**
129
+ * Configuration options for the YouTube client.
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const options: YoutubeOptionsType = {
134
+ * binaryPath: "/usr/local/bin/yt-dlp",
135
+ * ffmpegPath: "/usr/local/bin/ffmpeg",
136
+ * };
137
+ * const youtube = new Youtube(options);
138
+ * ```
139
+ */
140
+ type YoutubeOptionsType = {
141
+ /**
142
+ * Path to the yt-dlp binary.
143
+ * Falls back to YOUTUBE_YTDLP_PATH environment variable if not provided.
144
+ */
145
+ binaryPath?: string;
146
+ /**
147
+ * Path to the ffmpeg binary.
148
+ * Falls back to YOUTUBE_FFMPEG_PATH environment variable if not provided.
149
+ */
150
+ ffmpegPath?: string;
151
+ };
152
+ /**
153
+ * Interface defining the YouTube client API.
154
+ * Provides methods for fetching video info, downloading, and managing media.
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * class MyYoutubeClient implements IYoutube {
159
+ * async getVideoInfo(url: string) { ... }
160
+ * async getPlaylistInfo(url: string) { ... }
161
+ * // ... implement all methods
162
+ * }
163
+ * ```
164
+ */
165
+ interface IYoutube {
166
+ /**
167
+ * Retrieves detailed information about a YouTube video.
168
+ * @param url - The YouTube video URL
169
+ * @returns Promise resolving to video metadata
170
+ */
171
+ getVideoInfo(url: string): Promise<YoutubeVideoInfoType>;
172
+ /**
173
+ * Retrieves information about a YouTube playlist.
174
+ * @param url - The YouTube playlist URL
175
+ * @returns Promise resolving to playlist metadata
176
+ */
177
+ getPlaylistInfo(url: string): Promise<YoutubePlaylistInfoType>;
178
+ /**
179
+ * Downloads a video to the local filesystem.
180
+ * @param url - The YouTube video URL
181
+ * @param destination - The destination path for the downloaded file
182
+ * @returns Promise resolving to the downloaded file path
183
+ */
184
+ download(url: string, destination: string): Promise<string>;
185
+ /**
186
+ * Downloads a video and returns it as a File object.
187
+ * @param url - The YouTube video URL
188
+ * @param options - Optional filename and format settings
189
+ * @returns Promise resolving to a File object
190
+ */
191
+ getFile<F extends YoutubeFormatKeyWordType>(url: string, options?: {
192
+ filename?: string;
193
+ format?: YoutubeFormatOptionsType<F>["format"];
194
+ }): Promise<File>;
195
+ /**
196
+ * Extracts the video ID (watch ID) from a YouTube URL.
197
+ * @param url - The YouTube video URL
198
+ * @returns The video ID or null if not found
199
+ */
200
+ getWatchId(url: string): string | null;
201
+ /**
202
+ * Generates an embed URL for a YouTube video.
203
+ * @param urlOrId - The YouTube video URL or video ID
204
+ * @returns The embed URL or null if the video ID cannot be extracted
205
+ */
206
+ getEmbedUrl(urlOrId: string): string | null;
207
+ /**
208
+ * Generates a standard watch URL for a YouTube video.
209
+ * @param urlOrId - The YouTube video URL or video ID
210
+ * @returns The watch URL or null if the video ID cannot be extracted
211
+ */
212
+ getWatchUrl(urlOrId: string): string | null;
213
+ /**
214
+ * Downloads only the audio from a YouTube video.
215
+ * @param url - The YouTube video URL
216
+ * @param destination - The destination path for the downloaded audio file
217
+ * @returns Promise resolving to the downloaded audio file path
218
+ */
219
+ downloadAudio(url: string, destination: string): Promise<string>;
220
+ }
221
+ /**
222
+ * YouTube client for downloading and extracting information from YouTube videos and playlists.
223
+ * Wraps yt-dlp functionality with a clean async API and proper error handling.
224
+ *
225
+ * @example Basic Usage
226
+ * ```typescript
227
+ * import { Youtube } from "@ooneex/youtube";
228
+ *
229
+ * const youtube = new Youtube();
230
+ *
231
+ * // Get video information
232
+ * const info = await youtube.getVideoInfo("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
233
+ * console.log(info.title, info.duration);
234
+ *
235
+ * // Download a video
236
+ * const filePath = await youtube.download("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
237
+ * console.log(`Downloaded to: ${filePath}`);
238
+ * ```
239
+ *
240
+ * @example Download with Quality Options
241
+ * ```typescript
242
+ * const youtube = new Youtube();
243
+ *
244
+ * // Download video in 1080p
245
+ * const path = await youtube.download(url, {
246
+ * format: { video: "1080p" },
247
+ * });
248
+ *
249
+ * // Download audio only
250
+ * const audioPath = await youtube.download(url, {
251
+ * format: { audio: "highest" },
252
+ * });
253
+ * ```
254
+ */
255
+ declare class Youtube implements IYoutube {
256
+ private readonly ytdlp;
257
+ /**
258
+ * Creates a new YouTube client instance.
259
+ *
260
+ * @param options - Optional configuration for binary paths
261
+ * @throws {YoutubeException} If required binary paths are not configured
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * const youtube = new Youtube();
266
+ * ```
267
+ *
268
+ * @example With Custom Paths
269
+ * ```typescript
270
+ * const youtube = new Youtube({
271
+ * binaryPath: "/custom/path/to/yt-dlp",
272
+ * ffmpegPath: "/custom/path/to/ffmpeg",
273
+ * });
274
+ * ```
275
+ */
276
+ constructor(options?: YoutubeOptionsType);
277
+ /**
278
+ * Retrieves detailed metadata for a YouTube video.
279
+ *
280
+ * @param url - The YouTube video URL
281
+ * @returns Promise resolving to video information including title, description,
282
+ * duration, view count, available formats, and more
283
+ * @throws {YoutubeException} If the video cannot be accessed or URL is invalid
284
+ *
285
+ * @example
286
+ * ```typescript
287
+ * const youtube = new Youtube();
288
+ * const info = await youtube.getVideoInfo("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
289
+ *
290
+ * console.log("Title:", info.title);
291
+ * console.log("Duration:", info.duration, "seconds");
292
+ * console.log("Views:", info.view_count);
293
+ * console.log("Description:", info.description);
294
+ * console.log("Available formats:", info.formats.length);
295
+ * ```
296
+ */
297
+ getVideoInfo(url: string): Promise<YoutubeVideoInfoType>;
298
+ /**
299
+ * Retrieves information about a YouTube playlist including all videos.
300
+ *
301
+ * @param url - The YouTube playlist URL
302
+ * @returns Promise resolving to playlist metadata and video entries
303
+ * @throws {YoutubeException} If the playlist cannot be accessed or URL is invalid
304
+ *
305
+ * @example
306
+ * ```typescript
307
+ * const youtube = new Youtube();
308
+ * const playlist = await youtube.getPlaylistInfo(
309
+ * "https://www.youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf"
310
+ * );
311
+ *
312
+ * console.log("Playlist:", playlist.title);
313
+ * console.log("Video count:", playlist.entries.length);
314
+ *
315
+ * // Iterate over videos in the playlist
316
+ * for (const video of playlist.entries) {
317
+ * console.log(`- ${video.title} (${video.duration}s)`);
318
+ * }
319
+ * ```
320
+ */
321
+ getPlaylistInfo(url: string): Promise<YoutubePlaylistInfoType>;
322
+ /**
323
+ * Downloads a video to the local filesystem.
324
+ *
325
+ * @typeParam F - The format keyword type (video, audio, or videoandaudio)
326
+ * @param url - The YouTube video URL
327
+ * @param options - Optional format and quality settings
328
+ * @returns Promise resolving to the path of the downloaded file
329
+ * @throws {YoutubeException} If download fails
330
+ *
331
+ * @example Download with Default Settings
332
+ * ```typescript
333
+ * const youtube = new Youtube();
334
+ * const filePath = await youtube.download("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
335
+ * console.log("Downloaded to:", filePath);
336
+ * ```
337
+ *
338
+ * @example Download with Specific Quality
339
+ * ```typescript
340
+ * const youtube = new Youtube();
341
+ *
342
+ * // Download 1080p video
343
+ * const videoPath = await youtube.download(url, {
344
+ * format: { video: "1080p" },
345
+ * });
346
+ *
347
+ * // Download 720p video with audio
348
+ * const hdPath = await youtube.download(url, {
349
+ * format: { videoandaudio: "720p" },
350
+ * });
351
+ * ```
352
+ *
353
+ * @example Download Audio Only
354
+ * ```typescript
355
+ * const youtube = new Youtube();
356
+ * const audioPath = await youtube.download(url, {
357
+ * format: { audio: "highest" },
358
+ * });
359
+ * ```
360
+ */
361
+ download(url: string, destination: string): Promise<string>;
362
+ /**
363
+ * Downloads only the audio from a YouTube video.
364
+ * Convenience method that wraps download with audio-only format settings.
365
+ *
366
+ * @param url - The YouTube video URL
367
+ * @param quality - Audio quality preference (defaults to "highest")
368
+ * @returns Promise resolving to the path of the downloaded audio file
369
+ * @throws {YoutubeException} If download fails
370
+ *
371
+ * @example Basic Usage
372
+ * ```typescript
373
+ * const youtube = new Youtube();
374
+ * const audioPath = await youtube.downloadAudio("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
375
+ * console.log("Audio downloaded to:", audioPath);
376
+ * ```
377
+ *
378
+ * @example With Quality Option
379
+ * ```typescript
380
+ * const youtube = new Youtube();
381
+ *
382
+ * // Download highest quality audio
383
+ * const hqPath = await youtube.downloadAudio(url, "highest");
384
+ *
385
+ * // Download lowest quality (smaller file)
386
+ * const lqPath = await youtube.downloadAudio(url, "lowest");
387
+ * ```
388
+ */
389
+ downloadAudio(url: string, destination: string): Promise<string>;
390
+ /**
391
+ * Downloads a video and returns it as a File object.
392
+ * Useful for in-memory processing or streaming without saving to disk.
393
+ *
394
+ * @typeParam F - The format keyword type (video, audio, or videoandaudio)
395
+ * @param url - The YouTube video URL
396
+ * @param options - Optional filename and format settings
397
+ * @returns Promise resolving to a File object containing the video data
398
+ * @throws {YoutubeException} If download fails
399
+ *
400
+ * @example Basic Usage
401
+ * ```typescript
402
+ * const youtube = new Youtube();
403
+ * const file = await youtube.getFile("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
404
+ *
405
+ * console.log("File name:", file.name);
406
+ * console.log("File size:", file.size, "bytes");
407
+ * console.log("MIME type:", file.type);
408
+ * ```
409
+ *
410
+ * @example With Custom Filename
411
+ * ```typescript
412
+ * const youtube = new Youtube();
413
+ * const file = await youtube.getFile(url, {
414
+ * filename: "my-video.mp4",
415
+ * });
416
+ * ```
417
+ *
418
+ * @example With Format Options
419
+ * ```typescript
420
+ * const youtube = new Youtube();
421
+ * const audioFile = await youtube.getFile(url, {
422
+ * filename: "audio.mp3",
423
+ * format: { audio: "highest" },
424
+ * });
425
+ * ```
426
+ */
427
+ getFile<F extends YoutubeFormatKeyWordType>(url: string, options?: {
428
+ filename?: string;
429
+ format?: YoutubeFormatOptionsType<F>["format"] | undefined;
430
+ }): Promise<File>;
431
+ getWatchId(url: string): string | null;
432
+ getEmbedUrl(urlOrId: string): string | null;
433
+ getWatchUrl(urlOrId: string): string | null;
434
+ }
435
+ import { Exception } from "@ooneex/exception";
436
+ declare class YoutubeException extends Exception {
437
+ constructor(message: string, data?: Record<string, unknown>);
438
+ }
439
+ export { YoutubeVideoQualityType, YoutubeVideoProgressType, YoutubeVideoInfoType, YoutubeVideoFormatType, YoutubeQualityOptionsType, YoutubePlaylistInfoType, YoutubeOptionsType, YoutubeFormatOptionsType, YoutubeFormatKeyWordType, YoutubeException, YoutubeAudioQualityType, YoutubeArgsOptionsType, Youtube, IYoutube };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ import{YtDlp as B}from"ytdlp-nodejs";import{Exception as z}from"@ooneex/exception";import{HttpStatus as A}from"@ooneex/http-status";class q extends z{constructor(S,C={}){super(S,{status:A.Code.InternalServerError,data:C});this.name="YoutubeException"}}class F{ytdlp;constructor(S={}){let C=S.binaryPath||Bun.env.YOUTUBE_YTDLP_PATH,H=S.ffmpegPath||Bun.env.YOUTUBE_FFMPEG_PATH;if(!C)throw new q("yt-dlp binary path is required. Please provide it through the constructor options or set the YOUTUBE_YTDLP_PATH environment variable.");if(!H)throw new q("ffmpeg binary path is required. Please provide it through the constructor options or set the YOUTUBE_FFMPEG_PATH environment variable.");this.ytdlp=new B({binaryPath:C,ffmpegPath:H})}async getVideoInfo(S){try{return await this.ytdlp.getInfoAsync(S)}catch(C){throw new q(`Failed to get video info: ${C.message}`,{data:{url:S}})}}async getPlaylistInfo(S){try{return await this.ytdlp.getInfoAsync(S)}catch(C){throw new q(`Failed to get playlist info: ${C.message}`,{data:{url:S}})}}async download(S,C){try{return await this.ytdlp.downloadAsync(this.getWatchId(S)||"",{format:{filter:"audioandvideo",type:"mp4"},output:C})}catch(H){throw new q(`Failed to download video: ${H.message}`,{data:{url:S}})}}async downloadAudio(S,C){try{return await this.ytdlp.downloadAsync(this.getWatchId(S)||"",{format:{filter:"audioonly",type:"mp3"},output:C})}catch(H){throw new q(`Failed to download audio: ${H.message}`,{data:{url:S}})}}async getFile(S,C){try{let H=C?{filename:C.filename,format:C.format}:void 0;return await this.ytdlp.getFileAsync(this.getWatchId(S)||"",H)}catch(H){throw new q(`Failed to get file: ${H.message}`,{data:{url:S}})}}getWatchId(S){let C=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/|youtube\.com\/watch\?.*&v=)([^&\n?#]+)/,/youtube\.com\/shorts\/([^&\n?#]+)/];for(let H of C){let x=S.match(H);if(x?.[1])return x[1]}return null}getEmbedUrl(S){let C=this.getWatchId(S)??S;if(!/^[\w-]{10,12}$/.test(C))return null;return`https://www.youtube.com/embed/${C}`}getWatchUrl(S){let C=this.getWatchId(S)??S;if(!/^[\w-]{10,12}$/.test(C))return null;return`https://www.youtube.com/watch?v=${C}`}}export{q as YoutubeException,F as Youtube};
3
+
4
+ //# debugId=D8DC7E8F8C92550864756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/Youtube.ts", "src/YoutubeException.ts"],
4
+ "sourcesContent": [
5
+ "import { YtDlp } from \"ytdlp-nodejs\";\nimport type {\n IYoutube,\n YoutubeFormatKeyWordType,\n YoutubeFormatOptionsType,\n YoutubeOptionsType,\n YoutubePlaylistInfoType,\n YoutubeVideoInfoType,\n} from \"./types\";\nimport { YoutubeException } from \"./YoutubeException\";\n\n/**\n * YouTube client for downloading and extracting information from YouTube videos and playlists.\n * Wraps yt-dlp functionality with a clean async API and proper error handling.\n *\n * @example Basic Usage\n * ```typescript\n * import { Youtube } from \"@ooneex/youtube\";\n *\n * const youtube = new Youtube();\n *\n * // Get video information\n * const info = await youtube.getVideoInfo(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n * console.log(info.title, info.duration);\n *\n * // Download a video\n * const filePath = await youtube.download(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n * console.log(`Downloaded to: ${filePath}`);\n * ```\n *\n * @example Download with Quality Options\n * ```typescript\n * const youtube = new Youtube();\n *\n * // Download video in 1080p\n * const path = await youtube.download(url, {\n * format: { video: \"1080p\" },\n * });\n *\n * // Download audio only\n * const audioPath = await youtube.download(url, {\n * format: { audio: \"highest\" },\n * });\n * ```\n */\nexport class Youtube implements IYoutube {\n private readonly ytdlp: YtDlp;\n\n /**\n * Creates a new YouTube client instance.\n *\n * @param options - Optional configuration for binary paths\n * @throws {YoutubeException} If required binary paths are not configured\n *\n * @example\n * ```typescript\n * const youtube = new Youtube();\n * ```\n *\n * @example With Custom Paths\n * ```typescript\n * const youtube = new Youtube({\n * binaryPath: \"/custom/path/to/yt-dlp\",\n * ffmpegPath: \"/custom/path/to/ffmpeg\",\n * });\n * ```\n */\n constructor(options: YoutubeOptionsType = {}) {\n const binaryPath = options.binaryPath || Bun.env.YOUTUBE_YTDLP_PATH;\n const ffmpegPath = options.ffmpegPath || Bun.env.YOUTUBE_FFMPEG_PATH;\n\n if (!binaryPath) {\n throw new YoutubeException(\n \"yt-dlp binary path is required. Please provide it through the constructor options or set the YOUTUBE_YTDLP_PATH environment variable.\",\n );\n }\n\n if (!ffmpegPath) {\n throw new YoutubeException(\n \"ffmpeg binary path is required. Please provide it through the constructor options or set the YOUTUBE_FFMPEG_PATH environment variable.\",\n );\n }\n\n this.ytdlp = new YtDlp({\n binaryPath,\n ffmpegPath,\n });\n }\n\n /**\n * Retrieves detailed metadata for a YouTube video.\n *\n * @param url - The YouTube video URL\n * @returns Promise resolving to video information including title, description,\n * duration, view count, available formats, and more\n * @throws {YoutubeException} If the video cannot be accessed or URL is invalid\n *\n * @example\n * ```typescript\n * const youtube = new Youtube();\n * const info = await youtube.getVideoInfo(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n *\n * console.log(\"Title:\", info.title);\n * console.log(\"Duration:\", info.duration, \"seconds\");\n * console.log(\"Views:\", info.view_count);\n * console.log(\"Description:\", info.description);\n * console.log(\"Available formats:\", info.formats.length);\n * ```\n */\n public async getVideoInfo(url: string): Promise<YoutubeVideoInfoType> {\n try {\n return await this.ytdlp.getInfoAsync<\"video\">(url);\n } catch (error) {\n throw new YoutubeException(`Failed to get video info: ${(error as Error).message}`, {\n data: { url },\n });\n }\n }\n\n /**\n * Retrieves information about a YouTube playlist including all videos.\n *\n * @param url - The YouTube playlist URL\n * @returns Promise resolving to playlist metadata and video entries\n * @throws {YoutubeException} If the playlist cannot be accessed or URL is invalid\n *\n * @example\n * ```typescript\n * const youtube = new Youtube();\n * const playlist = await youtube.getPlaylistInfo(\n * \"https://www.youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf\"\n * );\n *\n * console.log(\"Playlist:\", playlist.title);\n * console.log(\"Video count:\", playlist.entries.length);\n *\n * // Iterate over videos in the playlist\n * for (const video of playlist.entries) {\n * console.log(`- ${video.title} (${video.duration}s)`);\n * }\n * ```\n */\n public async getPlaylistInfo(url: string): Promise<YoutubePlaylistInfoType> {\n try {\n return await this.ytdlp.getInfoAsync<\"playlist\">(url);\n } catch (error) {\n throw new YoutubeException(`Failed to get playlist info: ${(error as Error).message}`, {\n data: { url },\n });\n }\n }\n\n /**\n * Downloads a video to the local filesystem.\n *\n * @typeParam F - The format keyword type (video, audio, or videoandaudio)\n * @param url - The YouTube video URL\n * @param options - Optional format and quality settings\n * @returns Promise resolving to the path of the downloaded file\n * @throws {YoutubeException} If download fails\n *\n * @example Download with Default Settings\n * ```typescript\n * const youtube = new Youtube();\n * const filePath = await youtube.download(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n * console.log(\"Downloaded to:\", filePath);\n * ```\n *\n * @example Download with Specific Quality\n * ```typescript\n * const youtube = new Youtube();\n *\n * // Download 1080p video\n * const videoPath = await youtube.download(url, {\n * format: { video: \"1080p\" },\n * });\n *\n * // Download 720p video with audio\n * const hdPath = await youtube.download(url, {\n * format: { videoandaudio: \"720p\" },\n * });\n * ```\n *\n * @example Download Audio Only\n * ```typescript\n * const youtube = new Youtube();\n * const audioPath = await youtube.download(url, {\n * format: { audio: \"highest\" },\n * });\n * ```\n */\n public async download(url: string, destination: string): Promise<string> {\n try {\n return await this.ytdlp.downloadAsync(this.getWatchId(url) || \"\", {\n format: { filter: \"audioandvideo\", type: \"mp4\" },\n output: destination,\n });\n } catch (error) {\n throw new YoutubeException(`Failed to download video: ${(error as Error).message}`, {\n data: { url },\n });\n }\n }\n\n /**\n * Downloads only the audio from a YouTube video.\n * Convenience method that wraps download with audio-only format settings.\n *\n * @param url - The YouTube video URL\n * @param quality - Audio quality preference (defaults to \"highest\")\n * @returns Promise resolving to the path of the downloaded audio file\n * @throws {YoutubeException} If download fails\n *\n * @example Basic Usage\n * ```typescript\n * const youtube = new Youtube();\n * const audioPath = await youtube.downloadAudio(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n * console.log(\"Audio downloaded to:\", audioPath);\n * ```\n *\n * @example With Quality Option\n * ```typescript\n * const youtube = new Youtube();\n *\n * // Download highest quality audio\n * const hqPath = await youtube.downloadAudio(url, \"highest\");\n *\n * // Download lowest quality (smaller file)\n * const lqPath = await youtube.downloadAudio(url, \"lowest\");\n * ```\n */\n public async downloadAudio(url: string, destination: string): Promise<string> {\n try {\n return await this.ytdlp.downloadAsync(this.getWatchId(url) || \"\", {\n format: { filter: \"audioonly\", type: \"mp3\" },\n output: destination,\n });\n } catch (error) {\n throw new YoutubeException(`Failed to download audio: ${(error as Error).message}`, {\n data: { url },\n });\n }\n }\n\n /**\n * Downloads a video and returns it as a File object.\n * Useful for in-memory processing or streaming without saving to disk.\n *\n * @typeParam F - The format keyword type (video, audio, or videoandaudio)\n * @param url - The YouTube video URL\n * @param options - Optional filename and format settings\n * @returns Promise resolving to a File object containing the video data\n * @throws {YoutubeException} If download fails\n *\n * @example Basic Usage\n * ```typescript\n * const youtube = new Youtube();\n * const file = await youtube.getFile(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n *\n * console.log(\"File name:\", file.name);\n * console.log(\"File size:\", file.size, \"bytes\");\n * console.log(\"MIME type:\", file.type);\n * ```\n *\n * @example With Custom Filename\n * ```typescript\n * const youtube = new Youtube();\n * const file = await youtube.getFile(url, {\n * filename: \"my-video.mp4\",\n * });\n * ```\n *\n * @example With Format Options\n * ```typescript\n * const youtube = new Youtube();\n * const audioFile = await youtube.getFile(url, {\n * filename: \"audio.mp3\",\n * format: { audio: \"highest\" },\n * });\n * ```\n */\n public async getFile<F extends YoutubeFormatKeyWordType>(\n url: string,\n options?: { filename?: string; format?: YoutubeFormatOptionsType<F>[\"format\"] | undefined },\n ): Promise<File> {\n try {\n const fileOptions = options ? { filename: options.filename, format: options.format } : undefined;\n return await this.ytdlp.getFileAsync(\n this.getWatchId(url) || \"\",\n fileOptions as Parameters<typeof this.ytdlp.getFileAsync<F>>[1],\n );\n } catch (error) {\n throw new YoutubeException(`Failed to get file: ${(error as Error).message}`, {\n data: { url },\n });\n }\n }\n\n public getWatchId(url: string): string | null {\n const patterns = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/|youtube\\.com\\/v\\/|youtube\\.com\\/watch\\?.*&v=)([^&\\n?#]+)/,\n /youtube\\.com\\/shorts\\/([^&\\n?#]+)/,\n ];\n\n for (const pattern of patterns) {\n const match = url.match(pattern);\n if (match?.[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n public getEmbedUrl(urlOrId: string): string | null {\n const videoId = this.getWatchId(urlOrId) ?? urlOrId;\n\n // Validate that it looks like a YouTube video ID (typically 11 characters, alphanumeric with - and _)\n if (!/^[\\w-]{10,12}$/.test(videoId)) {\n return null;\n }\n\n return `https://www.youtube.com/embed/${videoId}`;\n }\n\n public getWatchUrl(urlOrId: string): string | null {\n const videoId = this.getWatchId(urlOrId) ?? urlOrId;\n\n // Validate that it looks like a YouTube video ID (typically 11 characters, alphanumeric with - and _)\n if (!/^[\\w-]{10,12}$/.test(videoId)) {\n return null;\n }\n\n return `https://www.youtube.com/watch?v=${videoId}`;\n }\n}\n",
6
+ "import { Exception } from \"@ooneex/exception\";\nimport { HttpStatus } from \"@ooneex/http-status\";\n\nexport class YoutubeException extends Exception {\n constructor(message: string, data: Record<string, unknown> = {}) {\n super(message, {\n status: HttpStatus.Code.InternalServerError,\n data,\n });\n this.name = \"YoutubeException\";\n }\n}\n"
7
+ ],
8
+ "mappings": ";AAAA,gBAAS,qBCAT,oBAAS,0BACT,qBAAS,4BAEF,MAAM,UAAyB,CAAU,CAC9C,WAAW,CAAC,EAAiB,EAAgC,CAAC,EAAG,CAC/D,MAAM,EAAS,CACb,OAAQ,EAAW,KAAK,oBACxB,MACF,CAAC,EACD,KAAK,KAAO,mBAEhB,CDkCO,MAAM,CAA4B,CACtB,MAqBjB,WAAW,CAAC,EAA8B,CAAC,EAAG,CAC5C,IAAM,EAAa,EAAQ,YAAc,IAAI,IAAI,mBAC3C,EAAa,EAAQ,YAAc,IAAI,IAAI,oBAEjD,GAAI,CAAC,EACH,MAAM,IAAI,EACR,uIACF,EAGF,GAAI,CAAC,EACH,MAAM,IAAI,EACR,wIACF,EAGF,KAAK,MAAQ,IAAI,EAAM,CACrB,aACA,YACF,CAAC,OAuBU,aAAY,CAAC,EAA4C,CACpE,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,aAAsB,CAAG,EACjD,MAAO,EAAO,CACd,MAAM,IAAI,EAAiB,6BAA8B,EAAgB,UAAW,CAClF,KAAM,CAAE,KAAI,CACd,CAAC,QA2BQ,gBAAe,CAAC,EAA+C,CAC1E,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,aAAyB,CAAG,EACpD,MAAO,EAAO,CACd,MAAM,IAAI,EAAiB,gCAAiC,EAAgB,UAAW,CACrF,KAAM,CAAE,KAAI,CACd,CAAC,QA2CQ,SAAQ,CAAC,EAAa,EAAsC,CACvE,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,cAAc,KAAK,WAAW,CAAG,GAAK,GAAI,CAChE,OAAQ,CAAE,OAAQ,gBAAiB,KAAM,KAAM,EAC/C,OAAQ,CACV,CAAC,EACD,MAAO,EAAO,CACd,MAAM,IAAI,EAAiB,6BAA8B,EAAgB,UAAW,CAClF,KAAM,CAAE,KAAI,CACd,CAAC,QA+BQ,cAAa,CAAC,EAAa,EAAsC,CAC5E,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,cAAc,KAAK,WAAW,CAAG,GAAK,GAAI,CAChE,OAAQ,CAAE,OAAQ,YAAa,KAAM,KAAM,EAC3C,OAAQ,CACV,CAAC,EACD,MAAO,EAAO,CACd,MAAM,IAAI,EAAiB,6BAA8B,EAAgB,UAAW,CAClF,KAAM,CAAE,KAAI,CACd,CAAC,QAyCQ,QAA2C,CACtD,EACA,EACe,CACf,GAAI,CACF,IAAM,EAAc,EAAU,CAAE,SAAU,EAAQ,SAAU,OAAQ,EAAQ,MAAO,EAAI,OACvF,OAAO,MAAM,KAAK,MAAM,aACtB,KAAK,WAAW,CAAG,GAAK,GACxB,CACF,EACA,MAAO,EAAO,CACd,MAAM,IAAI,EAAiB,uBAAwB,EAAgB,UAAW,CAC5E,KAAM,CAAE,KAAI,CACd,CAAC,GAIE,UAAU,CAAC,EAA4B,CAC5C,IAAM,EAAW,CACf,wHACA,mCACF,EAEA,QAAW,KAAW,EAAU,CAC9B,IAAM,EAAQ,EAAI,MAAM,CAAO,EAC/B,GAAI,IAAQ,GACV,OAAO,EAAM,GAIjB,OAAO,KAGF,WAAW,CAAC,EAAgC,CACjD,IAAM,EAAU,KAAK,WAAW,CAAO,GAAK,EAG5C,GAAI,CAAC,iBAAiB,KAAK,CAAO,EAChC,OAAO,KAGT,MAAO,iCAAiC,IAGnC,WAAW,CAAC,EAAgC,CACjD,IAAM,EAAU,KAAK,WAAW,CAAO,GAAK,EAG5C,GAAI,CAAC,iBAAiB,KAAK,CAAO,EAChC,OAAO,KAGT,MAAO,mCAAmC,IAE9C",
9
+ "debugId": "D8DC7E8F8C92550864756E2164756E21",
10
+ "names": []
11
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@ooneex/youtube",
3
+ "description": "A YouTube video downloader and utility library",
4
+ "version": "0.0.4",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "LICENSE",
9
+ "README.md",
10
+ "package.json"
11
+ ],
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "license": "MIT",
24
+ "scripts": {
25
+ "test": "bun test tests",
26
+ "build": "bunup",
27
+ "lint": "tsgo --noEmit && bunx biome lint",
28
+ "publish": "bun publish --access public || true"
29
+ },
30
+ "dependencies": {
31
+ "@ooneex/exception": "0.0.1",
32
+ "@ooneex/http-status": "0.0.1",
33
+ "ytdlp-nodejs": "^2.3.5"
34
+ },
35
+ "keywords": [
36
+ "api",
37
+ "bun",
38
+ "downloader",
39
+ "embed",
40
+ "ooneex",
41
+ "typescript",
42
+ "video",
43
+ "youtube",
44
+ "ytdlp"
45
+ ]
46
+ }