@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/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 && player_response.playabilityStatus;
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 || (playability.messages && playability.messages[0]));
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 request = rewriteRequest(url, requestOptions);
185
- requestOptions = request.requestOptions;
186
- url = request.url;
204
+ const rewritten = rewriteRequest(url, requestOptions);
205
+ requestOptions = rewritten.requestOptions || requestOptions;
206
+ url = rewritten.url || url;
187
207
  }
188
208
 
189
- const req = await request(url, requestOptions);
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
- // eslint-disable-next-line max-len
246
- console.warn(
247
- '\x1b[33mWARNING:\x1B[0m @distube/ytdl-core is out of date! Update with "npm install @distube/ytdl-core@latest".',
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 = (exports.getRandomIPv6 = ip => {
267
- // Start with a fast Regex-Check
268
- if (!isIPv6(ip)) throw Error("Invalid IPv6 format");
269
- // Start by splitting and normalizing addr and mask
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
- let base10Mask = parseInt(rawMask);
272
- if (!base10Mask || base10Mask > 128 || base10Mask < 24) throw Error("Invalid IPv6 subnet");
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
- // eslint-disable-next-line max-len
295
- const IPV6_REGEX =
296
- /^(([0-9a-f]{1,4}:)(:[0-9a-f]{1,4}){1,6}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4})|([0-9a-f]{1,4}:){1,7}(([0-9a-f]{1,4})|:))\/(1[0-1]\d|12[0-8]|\d{1,2})$/;
297
- /**
298
- * Quick check for a valid IPv6
299
- * The Regex only accepts a subset of all IPv6 Addresses
300
- *
301
- * @param {string} ip the IPv6 block in CIDR-Notation to test
302
- * @returns {boolean} true if valid
303
- */
304
- const isIPv6 = (exports.isIPv6 = ip => IPV6_REGEX.test(ip));
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
- * Normalise an IP Address
308
- *
309
- * @param {string} ip the IPv6 Addr
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 = (exports.normalizeIP = ip => {
313
- // Split by fill position
314
- const parts = ip.split("::").map(x => x.split(":"));
315
- // Normalize start and end
316
- const partStart = parts[0] || [];
317
- const partEnd = parts[1] || [];
318
- partEnd.reverse();
319
- // Placeholder for full ip
320
- const fullIP = new Array(8).fill(0);
321
- // Fill in start and end parts
322
- for (let i = 0; i < Math.min(partStart.length, 8); i++) {
323
- fullIP[i] = parseInt(partStart[i], 16) || 0;
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/distubejs/ytdl-core#cookies-support)",
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/distubejs/ytdl-core#ip-rotation)",
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/distubejs/ytdl-core#ip-rotation)",
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 = ["WEB_CREATOR", "IOS"];
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 HQ fork of ytdl-core. YouTube video downloader in pure javascript.",
4
- "version": "4.15.1",
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.6",
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": "^4.1.4",
32
- "undici": "five"
29
+ "tough-cookie": "^5.1.0",
30
+ "undici": "^7.3.0"
33
31
  },
34
32
  "devDependencies": {
35
- "@types/node": "^22.8.1",
36
- "prettier": "^3.3.3",
37
- "typescript": "^5.6.3"
33
+ "@types/node": "^22.12.0",
34
+ "prettier": "^3.4.2",
35
+ "typescript": "^5.7.3"
38
36
  },
39
37
  "engines": {
40
- "node": ">=14.0"
38
+ "node": ">=20.18.1"
41
39
  },
42
- "license": "MIT",
43
- "scripts": {
44
- "prettier": "prettier --write \"**/*.{js,json,yml,md,ts}\""
45
- }
40
+ "scripts": {}
46
41
  }
@@ -322,7 +322,7 @@ declare module "tough-cookie" {
322
322
  }
323
323
  }
324
324
 
325
- declare module "@distube/ytdl-core" {
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<"WEB_CREATOR" | "IOS" | "ANDROID" | "WEB">;
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.