untube 2.5.2 → 2.5.3
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/README.md +129 -98
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,22 @@
|
|
|
9
9
|
|
|
10
10
|
A lightweight, extremely fast YouTube video downloader and metadata scraper for Node.js. Ported from the core extraction logic of [yt-dlp](https://github.com/yt-dlp/yt-dlp).
|
|
11
11
|
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
- [Installation](#installation)
|
|
16
|
+
- [Fetching Metadata (Basic Usage)](#fetching-metadata-basic-usage)
|
|
17
|
+
- [Downloading Videos (Streaming)](#downloading-videos-streaming)
|
|
18
|
+
- [Configuration Options](#configuration-options)
|
|
19
|
+
- [Format Selection & Merging](#format-selection--merging)
|
|
20
|
+
- [Format Utilities](#format-utilities)
|
|
21
|
+
- [YouTube Music Search](#youtube-music-search)
|
|
22
|
+
- [Cookie Handling](#cookie-handling)
|
|
23
|
+
- [API Reference](#api-reference)
|
|
24
|
+
- [Disclaimer & License](#disclaimer--license)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
12
28
|
## Installation
|
|
13
29
|
|
|
14
30
|
**npm**
|
|
@@ -31,12 +47,34 @@ yarn add untube
|
|
|
31
47
|
bun add untube
|
|
32
48
|
```
|
|
33
49
|
|
|
34
|
-
|
|
50
|
+
---
|
|
35
51
|
|
|
36
|
-
|
|
52
|
+
## Fetching Metadata (Basic Usage)
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
If you only need video details without downloading the stream, use `untube.getVideoInfo()`. This is the fastest way to get metadata.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import untube from 'untube';
|
|
58
|
+
|
|
59
|
+
// Basic usage
|
|
60
|
+
const info = await untube.getVideoInfo('dQw4w9WgXcQ');
|
|
61
|
+
|
|
62
|
+
console.log('Title:', info.title);
|
|
63
|
+
console.log('Views:', info.view_count);
|
|
64
|
+
console.log('Duration:', info.duration, 'seconds');
|
|
65
|
+
|
|
66
|
+
// With options (e.g., proxy or cookies)
|
|
67
|
+
const infoWithProxy = await untube.getVideoInfo('dQw4w9WgXcQ', {
|
|
68
|
+
proxy: 'http://user:pass@host:port',
|
|
69
|
+
cookies: './cookies.txt'
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Downloading Videos (Streaming)
|
|
76
|
+
|
|
77
|
+
`untube` provides a readable stream that you can pipe anywhere (e.g., to a file, to `fluent-ffmpeg`, or an HTTP response).
|
|
40
78
|
|
|
41
79
|
```typescript
|
|
42
80
|
import fs from 'node:fs';
|
|
@@ -47,26 +85,19 @@ const controller = new AbortController();
|
|
|
47
85
|
|
|
48
86
|
// Start downloading a video
|
|
49
87
|
const stream = untube('dQw4w9WgXcQ', {
|
|
50
|
-
format: 'highestvideo', // Select
|
|
51
|
-
signal: controller.signal, //
|
|
52
|
-
// cookies: './cookies.txt', // Optional: avoid age-restrictions
|
|
88
|
+
format: 'highestvideo', // Select quality
|
|
89
|
+
signal: controller.signal, // Optional: pass the abort signal
|
|
53
90
|
});
|
|
54
91
|
|
|
55
|
-
//
|
|
92
|
+
// Listen to events
|
|
56
93
|
stream.on('info', (info, format) => {
|
|
57
94
|
console.log(`Downloading: ${info.title}`);
|
|
58
|
-
console.log(`Format: ${format.resolution} (${format.container})`);
|
|
59
|
-
|
|
60
|
-
// You can also access subtitles/captions
|
|
61
|
-
if (info.captions.length > 0) {
|
|
62
|
-
console.log(`Available Subtitles: ${info.captions.map(c => c.language).join(', ')}`);
|
|
63
|
-
}
|
|
95
|
+
console.log(`Selected Format: ${format.resolution} (${format.container})`);
|
|
64
96
|
});
|
|
65
97
|
|
|
66
98
|
stream.on('progress', (progress) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.log(`Progress: ${progress.percent}% (${downloadedMb}MB / ${totalMb}MB)`);
|
|
99
|
+
// Progress event is only available in 'parallel' mode (default)
|
|
100
|
+
console.log(`Progress: ${progress.percent}% (${progress.downloadedBytes} bytes)`);
|
|
70
101
|
});
|
|
71
102
|
|
|
72
103
|
stream.on('error', (err) => {
|
|
@@ -75,144 +106,144 @@ stream.on('error', (err) => {
|
|
|
75
106
|
|
|
76
107
|
// Pipe the stream directly to a file
|
|
77
108
|
stream.pipe(fs.createWriteStream('video.mp4'));
|
|
78
|
-
|
|
79
|
-
// Example: Cancel download after 5 seconds
|
|
80
|
-
// setTimeout(() => controller.abort(), 5000);
|
|
81
109
|
```
|
|
82
110
|
|
|
83
111
|
---
|
|
84
112
|
|
|
85
113
|
## Configuration Options
|
|
86
114
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
115
|
+
### `untube(id, options)`
|
|
116
|
+
| Option | Type | Default | Description |
|
|
117
|
+
| :--- | :--- | :--- | :--- |
|
|
118
|
+
| `format` | `string` | `'highest'` | Quality preset (e.g., `'highestaudio'`, `'1080p'`) or specific `itag`. |
|
|
119
|
+
| `filter` | `string \| function` | `undefined` | Filter formats (e.g., `'audioonly'`, `'videoonly'`). |
|
|
120
|
+
| `mode` | `'parallel' \| 'sequential'` | `'parallel'` | Download strategy. `'parallel'` is faster; `'sequential'` is better for live playback. |
|
|
121
|
+
| `proxy` | `string` | `undefined` | Proxy URL (HTTP/HTTPS). |
|
|
122
|
+
| `cookies` | `string \| RawCookie` | `undefined` | Path to Netscape cookie file or `RawCookie` instance. |
|
|
123
|
+
| `signal` | `AbortSignal` | `undefined` | Signal to abort the download. |
|
|
124
|
+
|
|
125
|
+
### `untube.getVideoInfo(id, options)`
|
|
126
|
+
| Option | Type | Default | Description |
|
|
127
|
+
| :--- | :--- | :--- | :--- |
|
|
128
|
+
| `proxy` | `string` | `undefined` | Proxy URL (HTTP/HTTPS). |
|
|
129
|
+
| `cookies` | `string \| RawCookie` | `undefined` | Path to Netscape cookie file or `RawCookie` instance. |
|
|
95
130
|
|
|
96
|
-
|
|
97
|
-
// Download exactly 1080p MP4 (Video only)
|
|
98
|
-
untube('videoId', { format: '137' });
|
|
99
|
-
|
|
100
|
-
// Download the best audio available
|
|
101
|
-
untube('videoId', { format: 'highestaudio' });
|
|
102
|
-
```
|
|
131
|
+
---
|
|
103
132
|
|
|
104
|
-
|
|
105
|
-
By default, `untube` uses **parallel** downloading to maximize speed.
|
|
133
|
+
## Format Selection & Merging
|
|
106
134
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
135
|
+
> **⚠️ Important Note on Video & Audio:**
|
|
136
|
+
> YouTube separates most video and audio into different streams (DASH formats).
|
|
137
|
+
>
|
|
138
|
+
> - If you choose a video-only format (like `highestvideo`), the resulting file will **not have sound**.
|
|
139
|
+
> - If you want a single file with both video and audio, you must download the video and audio streams separately and merge them yourself using a tool like **ffmpeg**.
|
|
111
140
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
141
|
+
### Common Presets
|
|
142
|
+
- **Presets:** `'highest'`, `'lowest'`, `'highestaudio'`, `'lowestaudio'`, `'highestvideo'`, `'lowestvideo'`.
|
|
143
|
+
- **Resolutions:** `'1080p'`, `'720p'`, etc.
|
|
144
|
+
- **Format ID / itag:** Use specific itags like `'137'` (1080p video-only) or `'140'` (m4a audio).
|
|
116
145
|
|
|
117
146
|
---
|
|
118
147
|
|
|
119
|
-
##
|
|
148
|
+
## Format Utilities
|
|
120
149
|
|
|
121
|
-
|
|
150
|
+
`untube` includes utility functions to help you manage and filter formats from the `info` object.
|
|
122
151
|
|
|
123
152
|
```typescript
|
|
124
|
-
import untube from 'untube';
|
|
125
|
-
|
|
126
153
|
const info = await untube.getVideoInfo('videoId');
|
|
127
154
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Format Utilities
|
|
155
|
+
// 1. Filter formats using presets
|
|
156
|
+
// Available presets: 'audioandvideo', 'video', 'videoonly', 'audio', 'audioonly'
|
|
157
|
+
const audioOnly = untube.filterFormats(info.formats, 'audioonly');
|
|
133
158
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
const info = await untube.getVideoInfo('videoId');
|
|
159
|
+
// 2. Filter using custom logic
|
|
160
|
+
const mp4Only = untube.filterFormats(info.formats, f => f.container === 'mp4');
|
|
138
161
|
|
|
139
|
-
//
|
|
162
|
+
// 3. Choose a specific format manually
|
|
140
163
|
const bestAudio = untube.chooseFormat(info.formats, { quality: 'highestaudio' });
|
|
141
164
|
|
|
142
|
-
// 2. Filter formats custom logic
|
|
143
|
-
const mp4Only = untube.filterFormats(info.formats, format => format.container === 'mp4');
|
|
144
|
-
|
|
145
|
-
// 3. Filter using presets ('video', 'audio', 'audioandvideo', 'videoonly', 'audioonly')
|
|
146
|
-
const videoNoSound = untube.filterFormats(info.formats, 'videoonly');
|
|
147
|
-
|
|
148
165
|
// 4. Sort formats from highest to lowest quality
|
|
149
166
|
const sorted = untube.sortFormats(info.formats);
|
|
150
167
|
```
|
|
151
168
|
|
|
152
169
|
---
|
|
153
170
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
You can search for songs directly from YouTube Music using `untube.ytmusic()`:
|
|
171
|
+
### `untube.ytmusic(query, options)`
|
|
172
|
+
Search for songs or videos directly from YouTube Music.
|
|
157
173
|
|
|
158
174
|
```typescript
|
|
159
175
|
import untube from 'untube';
|
|
160
176
|
|
|
161
|
-
//
|
|
177
|
+
// Basic usage
|
|
162
178
|
const results = await untube.ytmusic('Never gonna give you up');
|
|
163
179
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
// With options (e.g., proxy or cookies)
|
|
181
|
+
const resultsWithProxy = await untube.ytmusic('Never gonna give you up', {
|
|
182
|
+
proxy: 'http://user:pass@host:port',
|
|
183
|
+
cookies: './cookies.txt'
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
console.log('Top Result:', results[0].title);
|
|
187
|
+
console.log('Artist:', results[0].artist);
|
|
188
|
+
console.log('Album:', results[0].album);
|
|
189
|
+
console.log('Duration:', results[0].duration, 'seconds');
|
|
171
190
|
```
|
|
172
191
|
|
|
173
192
|
---
|
|
174
193
|
|
|
175
194
|
## Cookie Handling
|
|
176
195
|
|
|
177
|
-
Using cookies is highly recommended to avoid rate limits, access age-restricted
|
|
196
|
+
Using cookies is highly recommended to avoid rate limits, access age-restricted videos, or bypass regional restrictions.
|
|
178
197
|
|
|
179
198
|
### 1. Using a File (Netscape format)
|
|
180
|
-
|
|
181
|
-
2. Open YouTube and ensure you are logged in.
|
|
182
|
-
3. Export the cookies in **Netscape format** and save it as `cookies.txt`.
|
|
183
|
-
4. Provide the file path:
|
|
184
|
-
|
|
199
|
+
Export cookies in **Netscape format** from your browser (e.g., using "Get cookies.txt LOCALLY" extension) and provide the file path:
|
|
185
200
|
```typescript
|
|
186
201
|
untube('videoId', { cookies: './cookies.txt' });
|
|
187
202
|
```
|
|
188
203
|
|
|
189
|
-
### 2. Advanced: Remote Storage (Database
|
|
190
|
-
|
|
191
|
-
|
|
204
|
+
### 2. Advanced: Remote Storage (Database)
|
|
205
|
+
Use the `RawCookie` class for custom read/write logic (e.g., storing in a database):
|
|
192
206
|
```typescript
|
|
193
|
-
import untube from 'untube';
|
|
194
|
-
|
|
195
207
|
const myRawCookie = new untube.RawCookie(
|
|
196
|
-
async () =>
|
|
197
|
-
|
|
198
|
-
return await fetchCookiesFromDB(); // Must return Netscape format string
|
|
199
|
-
},
|
|
200
|
-
async (newCookies) => {
|
|
201
|
-
// Implement write logic (called when YouTube refreshes cookies)
|
|
202
|
-
await saveCookiesToDB(newCookies);
|
|
203
|
-
}
|
|
208
|
+
async () => await fetchFromDB(), // Must return Netscape string
|
|
209
|
+
async (newCookies) => await saveToDB(newCookies)
|
|
204
210
|
);
|
|
205
|
-
|
|
206
211
|
untube('videoId', { cookies: myRawCookie });
|
|
207
212
|
```
|
|
208
213
|
|
|
209
|
-
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## API Reference
|
|
217
|
+
|
|
218
|
+
### `VideoInfo`
|
|
219
|
+
| Property | Type | Description |
|
|
220
|
+
| :--- | :--- | :--- |
|
|
221
|
+
| `id` | `string` | Video ID. |
|
|
222
|
+
| `title` | `string` | Video title. |
|
|
223
|
+
| `description` | `string` | Video description. |
|
|
224
|
+
| `duration` | `number` | Duration in seconds. |
|
|
225
|
+
| `view_count` | `number` | Total views. |
|
|
226
|
+
| `uploader` | `string` | Channel name. |
|
|
227
|
+
| `thumbnail` | `string` | Highest resolution thumbnail URL. |
|
|
228
|
+
| `formats` | `YouTubeFormat[]` | Array of available formats. |
|
|
229
|
+
| `captions` | `YouTubeCaption[]` | Array of available subtitles. |
|
|
230
|
+
|
|
231
|
+
### `YouTubeFormat`
|
|
232
|
+
| Property | Type | Description |
|
|
233
|
+
| :--- | :--- | :--- |
|
|
234
|
+
| `format_id` | `string` | The itag of the format. |
|
|
235
|
+
| `ext` | `string` | File extension (e.g., `'mp4'`, `'webm'`). |
|
|
236
|
+
| `resolution` | `string` | e.g., `'1080p'`, `'audio only'`. |
|
|
237
|
+
| `vcodec` | `string` | Video codec. `'none'` for audio-only. |
|
|
238
|
+
| `acodec` | `string` | Audio codec. `'none'` for video-only. |
|
|
239
|
+
| `filesize` | `number \| null` | File size in bytes. |
|
|
240
|
+
| `url` | `string` | Direct streaming URL (decrypted). |
|
|
210
241
|
|
|
211
242
|
---
|
|
212
243
|
|
|
213
244
|
## Disclaimer
|
|
214
245
|
|
|
215
|
-
This project is created for educational
|
|
246
|
+
This project is created for educational purposes only. Users are responsible for complying with YouTube's Terms of Service and local copyright laws.
|
|
216
247
|
|
|
217
248
|
## License
|
|
218
249
|
|