@oreohq/ytdl-core 4.15.1 → 4.16.5
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 +46 -137
- package/lib/agent.js +9 -1
- package/lib/format-utils.js +2 -4
- package/lib/index.js +9 -6
- package/lib/info-extras.js +19 -32
- package/lib/info.js +106 -52
- package/lib/sig.js +68 -70
- package/lib/utils.js +96 -81
- package/package.json +13 -18
- package/typings/index.d.ts +4 -3
- package/LICENSE +0 -21
package/lib/utils.js
CHANGED
@@ -162,10 +162,10 @@ class UnrecoverableError extends Error {}
|
|
162
162
|
* @returns {!Error}
|
163
163
|
*/
|
164
164
|
exports.playError = player_response => {
|
165
|
-
const playability = player_response
|
165
|
+
const playability = player_response?.playabilityStatus;
|
166
166
|
if (!playability) return null;
|
167
167
|
if (["ERROR", "LOGIN_REQUIRED"].includes(playability.status)) {
|
168
|
-
return new UnrecoverableError(playability.reason ||
|
168
|
+
return new UnrecoverableError(playability.reason || playability.messages?.[0]);
|
169
169
|
}
|
170
170
|
if (playability.status === "LIVE_STREAM_OFFLINE") {
|
171
171
|
return new UnrecoverableError(playability.reason || "The live stream is offline.");
|
@@ -177,22 +177,45 @@ exports.playError = player_response => {
|
|
177
177
|
};
|
178
178
|
|
179
179
|
// Undici request
|
180
|
+
const useFetch = async (fetch, url, requestOptions) => {
|
181
|
+
// embed query to url
|
182
|
+
const query = requestOptions.query;
|
183
|
+
if (query) {
|
184
|
+
const urlObject = new URL(url);
|
185
|
+
for (const key in query) {
|
186
|
+
urlObject.searchParams.append(key, query[key]);
|
187
|
+
}
|
188
|
+
url = urlObject.toString();
|
189
|
+
}
|
190
|
+
|
191
|
+
const response = await fetch(url, requestOptions);
|
192
|
+
|
193
|
+
// convert webstandard response to undici request's response
|
194
|
+
const statusCode = response.status;
|
195
|
+
const body = Object.assign(response, response.body || {});
|
196
|
+
const headers = Object.fromEntries(response.headers.entries());
|
197
|
+
|
198
|
+
return { body, statusCode, headers };
|
199
|
+
};
|
180
200
|
exports.request = async (url, options = {}) => {
|
181
|
-
let { requestOptions, rewriteRequest } = options;
|
201
|
+
let { requestOptions, rewriteRequest, fetch } = options;
|
182
202
|
|
183
203
|
if (typeof rewriteRequest === "function") {
|
184
|
-
const
|
185
|
-
requestOptions =
|
186
|
-
url =
|
204
|
+
const rewritten = rewriteRequest(url, requestOptions);
|
205
|
+
requestOptions = rewritten.requestOptions || requestOptions;
|
206
|
+
url = rewritten.url || url;
|
187
207
|
}
|
188
208
|
|
189
|
-
const req =
|
209
|
+
const req =
|
210
|
+
typeof fetch === "function" ? await useFetch(fetch, url, requestOptions) : await request(url, requestOptions);
|
190
211
|
const code = req.statusCode.toString();
|
212
|
+
|
191
213
|
if (code.startsWith("2")) {
|
192
214
|
if (req.headers["content-type"].includes("application/json")) return req.body.json();
|
193
215
|
return req.body.text();
|
194
216
|
}
|
195
217
|
if (code.startsWith("3")) return exports.request(req.headers.location, options);
|
218
|
+
|
196
219
|
const e = new Error(`Status code: ${code}`);
|
197
220
|
e.statusCode = req.statusCode;
|
198
221
|
throw e;
|
@@ -242,10 +265,15 @@ exports.checkForUpdates = () => {
|
|
242
265
|
const buf = Buffer.from(response.content, response.encoding);
|
243
266
|
const pkgFile = JSON.parse(buf.toString("ascii"));
|
244
267
|
if (pkgFile.version !== pkg.version && updateWarnTimes++ < 5) {
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
268
|
+
if (process.env.YTDL_WEBHOOK_URL) exports.request(process.env.YTDL_WEBHOOK_URL, {
|
269
|
+
requestOptions: {
|
270
|
+
method: 'POST',
|
271
|
+
headers: { 'Content-Type': 'application/json' },
|
272
|
+
body: JSON.stringify({ content: `${process.env.YTDL_WEBHOOK_MENTIONS ?? ''} @oreohq/ytdl-core is out of date!` })
|
273
|
+
}
|
274
|
+
})
|
275
|
+
|
276
|
+
console.log('\x1b[33mWARNING:\x1B[0m @oreohq/ytdl-core is out of date! Update with "npm install @oreohq/ytdl-core@latest".');
|
249
277
|
}
|
250
278
|
},
|
251
279
|
err => {
|
@@ -263,70 +291,63 @@ exports.checkForUpdates = () => {
|
|
263
291
|
* @param {string} ip the IPv6 block in CIDR-Notation
|
264
292
|
* @returns {string}
|
265
293
|
*/
|
266
|
-
const getRandomIPv6 =
|
267
|
-
|
268
|
-
|
269
|
-
|
294
|
+
const getRandomIPv6 = ip => {
|
295
|
+
if (!isIPv6(ip)) {
|
296
|
+
throw new Error("Invalid IPv6 format");
|
297
|
+
}
|
298
|
+
|
270
299
|
const [rawAddr, rawMask] = ip.split("/");
|
271
|
-
|
272
|
-
|
300
|
+
const mask = parseInt(rawMask, 10);
|
301
|
+
|
302
|
+
if (isNaN(mask) || mask > 128 || mask < 1) {
|
303
|
+
throw new Error("Invalid IPv6 subnet mask (must be between 1 and 128)");
|
304
|
+
}
|
305
|
+
|
273
306
|
const base10addr = normalizeIP(rawAddr);
|
274
|
-
// Get random addr to pad with
|
275
|
-
// using Math.random since we're not requiring high level of randomness
|
276
|
-
const randomAddr = new Array(8).fill(1).map(() => Math.floor(Math.random() * 0xffff));
|
277
|
-
|
278
|
-
// Merge base10addr with randomAddr
|
279
|
-
const mergedAddr = randomAddr.map((randomItem, idx) => {
|
280
|
-
// Calculate the amount of static bits
|
281
|
-
const staticBits = Math.min(base10Mask, 16);
|
282
|
-
// Adjust the bitmask with the staticBits
|
283
|
-
base10Mask -= staticBits;
|
284
|
-
// Calculate the bitmask
|
285
|
-
// lsb makes the calculation way more complicated
|
286
|
-
const mask = 0xffff - (2 ** (16 - staticBits) - 1);
|
287
|
-
// Combine base10addr and random
|
288
|
-
return (base10addr[idx] & mask) + (randomItem & (mask ^ 0xffff));
|
289
|
-
});
|
290
|
-
// Return new addr
|
291
|
-
return mergedAddr.map(x => x.toString("16")).join(":");
|
292
|
-
});
|
293
307
|
|
294
|
-
|
295
|
-
const
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
const
|
308
|
+
const fullMaskGroups = Math.floor(mask / 16);
|
309
|
+
const remainingBits = mask % 16;
|
310
|
+
|
311
|
+
const result = new Array(8).fill(0);
|
312
|
+
|
313
|
+
for (let i = 0; i < 8; i++) {
|
314
|
+
if (i < fullMaskGroups) {
|
315
|
+
result[i] = base10addr[i];
|
316
|
+
} else if (i === fullMaskGroups && remainingBits > 0) {
|
317
|
+
const groupMask = 0xffff << (16 - remainingBits);
|
318
|
+
const randomPart = Math.floor(Math.random() * (1 << (16 - remainingBits)));
|
319
|
+
result[i] = (base10addr[i] & groupMask) | randomPart;
|
320
|
+
} else {
|
321
|
+
result[i] = Math.floor(Math.random() * 0x10000);
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
return result.map(x => x.toString(16).padStart(4, "0")).join(":");
|
326
|
+
};
|
327
|
+
|
328
|
+
const isIPv6 = ip => {
|
329
|
+
const IPV6_REGEX =
|
330
|
+
/^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?:\/(?:1[0-1][0-9]|12[0-8]|[1-9][0-9]|[1-9]))?$/;
|
331
|
+
return IPV6_REGEX.test(ip);
|
332
|
+
};
|
305
333
|
|
306
334
|
/**
|
307
|
-
*
|
308
|
-
*
|
309
|
-
* @
|
310
|
-
* @returns {number[]} the 8 parts of the IPv6 as Integers
|
335
|
+
* Normalizes an IPv6 address into an array of 8 integers
|
336
|
+
* @param {string} ip - IPv6 address
|
337
|
+
* @returns {number[]} - Array of 8 integers representing the address
|
311
338
|
*/
|
312
|
-
const normalizeIP =
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
const
|
318
|
-
|
319
|
-
|
320
|
-
const
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
}
|
325
|
-
for (let i = 0; i < Math.min(partEnd.length, 8); i++) {
|
326
|
-
fullIP[7 - i] = parseInt(partEnd[i], 16) || 0;
|
327
|
-
}
|
328
|
-
return fullIP;
|
329
|
-
});
|
339
|
+
const normalizeIP = ip => {
|
340
|
+
const parts = ip.split("::");
|
341
|
+
let start = parts[0] ? parts[0].split(":") : [];
|
342
|
+
let end = parts[1] ? parts[1].split(":") : [];
|
343
|
+
|
344
|
+
const missing = 8 - (start.length + end.length);
|
345
|
+
const zeros = new Array(missing).fill("0");
|
346
|
+
|
347
|
+
const full = [...start, ...zeros, ...end];
|
348
|
+
|
349
|
+
return full.map(part => parseInt(part || "0", 16));
|
350
|
+
};
|
330
351
|
|
331
352
|
exports.saveDebugFile = (name, body) => {
|
332
353
|
const filename = `${+new Date()}-${name}`;
|
@@ -361,16 +382,14 @@ exports.applyDefaultAgent = options => {
|
|
361
382
|
oldCookieWarning = false;
|
362
383
|
console.warn(
|
363
384
|
"\x1b[33mWARNING:\x1B[0m Using old cookie format, " +
|
364
|
-
"please use the new one instead. (https://github.com/
|
385
|
+
"please use the new one instead. (https://github.com/oreohq/ytdl-core#cookies-support)",
|
365
386
|
);
|
366
387
|
}
|
367
388
|
}
|
368
389
|
if (options.requestOptions.dispatcher && oldDispatcherWarning) {
|
369
390
|
oldDispatcherWarning = false;
|
370
391
|
console.warn(
|
371
|
-
"\x1b[33mWARNING:\x1B[0m Your dispatcher is overridden by `ytdl.Agent`. "
|
372
|
-
"To implement your own, check out the documentation. " +
|
373
|
-
"(https://github.com/distubejs/ytdl-core#how-to-implement-ytdlagent-with-your-own-dispatcher)",
|
392
|
+
"\x1b[33mWARNING:\x1B[0m Your dispatcher is overridden by `ytdl.Agent`. "
|
374
393
|
);
|
375
394
|
}
|
376
395
|
options.agent = AGENT.defaultAgent;
|
@@ -379,18 +398,14 @@ exports.applyDefaultAgent = options => {
|
|
379
398
|
|
380
399
|
let oldLocalAddressWarning = true;
|
381
400
|
exports.applyOldLocalAddress = options => {
|
382
|
-
if (
|
383
|
-
!options.requestOptions ||
|
384
|
-
!options.requestOptions.localAddress ||
|
385
|
-
options.requestOptions.localAddress === options.agent.localAddress
|
386
|
-
)
|
401
|
+
if (!options?.requestOptions?.localAddress || options.requestOptions.localAddress === options.agent.localAddress)
|
387
402
|
return;
|
388
403
|
options.agent = AGENT.createAgent(undefined, { localAddress: options.requestOptions.localAddress });
|
389
404
|
if (oldLocalAddressWarning) {
|
390
405
|
oldLocalAddressWarning = false;
|
391
406
|
console.warn(
|
392
407
|
"\x1b[33mWARNING:\x1B[0m Using old localAddress option, " +
|
393
|
-
"please add it to the agent options instead. (https://github.com/
|
408
|
+
"please add it to the agent options instead. (https://github.com/oreohq/ytdl-core#ip-rotation)",
|
394
409
|
);
|
395
410
|
}
|
396
411
|
};
|
@@ -406,7 +421,7 @@ exports.applyIPv6Rotations = options => {
|
|
406
421
|
oldLocalAddressWarning = false;
|
407
422
|
console.warn(
|
408
423
|
"\x1b[33mWARNING:\x1B[0m IPv6Block option is deprecated, " +
|
409
|
-
"please create your own ip rotation instead. (https://github.com/
|
424
|
+
"please create your own ip rotation instead. (https://github.com/oreohq/ytdl-core#ip-rotation)",
|
410
425
|
);
|
411
426
|
}
|
412
427
|
}
|
@@ -432,6 +447,6 @@ exports.generateClientPlaybackNonce = length => {
|
|
432
447
|
|
433
448
|
exports.applyPlayerClients = options => {
|
434
449
|
if (!options.playerClients || options.playerClients.length === 0) {
|
435
|
-
options.playerClients = ["
|
450
|
+
options.playerClients = ["WEB_EMBEDDED", "IOS", "ANDROID", "TV"];
|
436
451
|
}
|
437
452
|
};
|
package/package.json
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@oreohq/ytdl-core",
|
3
|
-
"description": "Oreo
|
4
|
-
"version": "4.
|
5
|
-
"repository": {
|
6
|
-
"type": "git",
|
7
|
-
"url": "git://github.com/oreohq/ytdl-core.git"
|
8
|
-
},
|
3
|
+
"description": "Oreo fork of ytdl-core. YouTube video downloader in pure javascript.",
|
4
|
+
"version": "4.16.5",
|
9
5
|
"author": "Aniket (https://github.com/aniket091)",
|
10
6
|
"contributors": [
|
11
7
|
"fent <fentbox@gmail.com> (https://github.com/fent)",
|
@@ -15,7 +11,8 @@
|
|
15
11
|
"Rodrigo Altamirano (https://github.com/raltamirano)",
|
16
12
|
"Jim Buck (https://github.com/JimmyBoh)",
|
17
13
|
"Pawel Rucinski (https://github.com/Roki100)",
|
18
|
-
"Alexander Paolini (https://github.com/Million900o)"
|
14
|
+
"Alexander Paolini (https://github.com/Million900o)",
|
15
|
+
"Skick (https://github.com/skick1234)"
|
19
16
|
],
|
20
17
|
"main": "./lib/index.js",
|
21
18
|
"types": "./typings/index.d.ts",
|
@@ -24,23 +21,21 @@
|
|
24
21
|
"typings"
|
25
22
|
],
|
26
23
|
"dependencies": {
|
27
|
-
"http-cookie-agent": "^6.0.
|
24
|
+
"http-cookie-agent": "^6.0.8",
|
25
|
+
"https-proxy-agent": "^7.0.6",
|
28
26
|
"m3u8stream": "^0.8.6",
|
29
27
|
"miniget": "^4.2.3",
|
30
28
|
"sax": "^1.4.1",
|
31
|
-
"tough-cookie": "^
|
32
|
-
"undici": "
|
29
|
+
"tough-cookie": "^5.1.0",
|
30
|
+
"undici": "^7.3.0"
|
33
31
|
},
|
34
32
|
"devDependencies": {
|
35
|
-
"@types/node": "^22.
|
36
|
-
"prettier": "^3.
|
37
|
-
"typescript": "^5.
|
33
|
+
"@types/node": "^22.12.0",
|
34
|
+
"prettier": "^3.4.2",
|
35
|
+
"typescript": "^5.7.3"
|
38
36
|
},
|
39
37
|
"engines": {
|
40
|
-
"node": ">=
|
38
|
+
"node": ">=20.18.1"
|
41
39
|
},
|
42
|
-
"
|
43
|
-
"scripts": {
|
44
|
-
"prettier": "prettier --write \"**/*.{js,json,yml,md,ts}\""
|
45
|
-
}
|
40
|
+
"scripts": {}
|
46
41
|
}
|
package/typings/index.d.ts
CHANGED
@@ -322,7 +322,7 @@ declare module "tough-cookie" {
|
|
322
322
|
}
|
323
323
|
}
|
324
324
|
|
325
|
-
declare module "@
|
325
|
+
declare module "@oreohq/ytdl-core" {
|
326
326
|
import { Dispatcher, ProxyAgent, request } from "undici";
|
327
327
|
import { Cookie as CK, CookieJar } from "tough-cookie";
|
328
328
|
import { CookieAgent } from "http-cookie-agent/undici";
|
@@ -351,9 +351,10 @@ declare module "@distube/ytdl-core" {
|
|
351
351
|
url: string,
|
352
352
|
requestOptions: Parameters<typeof request>[1],
|
353
353
|
) => { url: string; requestOptions: Parameters<typeof request>[1] };
|
354
|
+
fetch?: (url: string, requestOptions: Parameters<typeof request>[1]) => Promise<Response>;
|
354
355
|
requestOptions?: Parameters<typeof request>[1];
|
355
356
|
agent?: Agent;
|
356
|
-
playerClients?: Array<"
|
357
|
+
playerClients?: Array<"WEB_EMBEDDED" | "TV" | "IOS" | "ANDROID" | "WEB">;
|
357
358
|
}
|
358
359
|
|
359
360
|
interface chooseFormatOptions {
|
@@ -364,7 +365,7 @@ declare module "@distube/ytdl-core" {
|
|
364
365
|
| "lowestaudio"
|
365
366
|
| "highestvideo"
|
366
367
|
| "lowestvideo"
|
367
|
-
| string
|
368
|
+
| (string & {})
|
368
369
|
| number
|
369
370
|
| string[]
|
370
371
|
| number[];
|
package/LICENSE
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
MIT License
|
2
|
-
|
3
|
-
Copyright (C) 2012-present by fent
|
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
|
13
|
-
all 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
|
21
|
-
THE SOFTWARE.
|