extract-youtube 1.0.2 → 1.0.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.
Files changed (47) hide show
  1. package/README.md +61 -20
  2. package/dist/archiver-web.es-3EQgnnex.js +2 -0
  3. package/dist/archiver-web.es-3EQgnnex.js.map +1 -0
  4. package/dist/archiver-web.es-CO0IfW41.mjs +2 -0
  5. package/dist/archiver-web.es-CO0IfW41.mjs.map +1 -0
  6. package/dist/archiver-web.es-eh2cNrlm.js +3140 -0
  7. package/dist/archiver-web.es-eh2cNrlm.js.map +1 -0
  8. package/dist/cli.js +455 -56
  9. package/dist/cli.js.map +1 -1
  10. package/dist/index-CLlAnStL-BKslXZFs.mjs +2 -0
  11. package/dist/index-CLlAnStL-BKslXZFs.mjs.map +1 -0
  12. package/dist/index-CLlAnStL-BNm8mEFS.js +6524 -0
  13. package/dist/index-CLlAnStL-BNm8mEFS.js.map +1 -0
  14. package/dist/index-CLlAnStL-DDV_Pgc5.js +2 -0
  15. package/dist/index-CLlAnStL-DDV_Pgc5.js.map +1 -0
  16. package/dist/index.cjs +2 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.mjs +2 -0
  19. package/dist/index.mjs.map +1 -0
  20. package/dist/rolldown-runtime-Bd6kNlEP.js +71 -0
  21. package/jest.config.js +0 -0
  22. package/package.json +13 -10
  23. package/src/cli.ts +11 -2
  24. package/src/{errors.ts → errors/index.ts} +1 -1
  25. package/src/fetchers/transcript-list-fetcher.ts +0 -0
  26. package/src/fetchers/youtube-data-extractor.ts +0 -0
  27. package/src/{formatters.ts → formatters/index.ts} +82 -3
  28. package/src/http/fetch-http-client.ts +9 -9
  29. package/src/index.ts +4 -0
  30. package/src/models/fetched-transcript.ts +0 -0
  31. package/src/models/index.ts +0 -0
  32. package/src/models/transcript-list.ts +0 -0
  33. package/src/models/transcript.ts +0 -0
  34. package/src/parsers/transcript-parser.ts +4 -0
  35. package/src/{proxies.ts → proxies/index.ts} +1 -1
  36. package/src/{types.ts → types/index.ts} +0 -2
  37. package/src/utils/http-error-handler.ts +0 -1
  38. package/src/utils/transcript-utils.ts +107 -0
  39. package/src/youtube-transcript-api.ts +0 -0
  40. package/{__tests__ → test}/api.test.ts +0 -0
  41. package/{__tests__/basic-usage.ts → test/basic.test.ts} +0 -0
  42. package/{__tests__ → test}/formatters.test.ts +0 -0
  43. package/{__tests__ → test}/integration.test.ts +0 -0
  44. package/tsconfig.json +5 -5
  45. package/tsconfig.test.json +10 -0
  46. package/vite.config.ts +8 -3
  47. package/CHANGELOG.md +0 -1
package/dist/cli.js CHANGED
@@ -1,45 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- //#region \0rolldown/runtime.js
4
- var __create = Object.create;
5
- var __defProp = Object.defineProperty;
6
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
- var __getOwnPropNames = Object.getOwnPropertyNames;
8
- var __getProtoOf = Object.getPrototypeOf;
9
- var __hasOwnProp = Object.prototype.hasOwnProperty;
10
- var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
11
- var __exportAll = (all, no_symbols) => {
12
- let target = {};
13
- for (var name in all) __defProp(target, name, {
14
- get: all[name],
15
- enumerable: true
16
- });
17
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
18
- return target;
19
- };
20
- var __copyProps = (to, from, except, desc) => {
21
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
22
- key = keys[i];
23
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
24
- get: ((k) => from[k]).bind(null, key),
25
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
26
- });
27
- }
28
- return to;
29
- };
30
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
31
- value: mod,
32
- enumerable: true
33
- }) : target, mod));
34
- var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
- //#endregion
3
+ const require_rolldown_runtime = require("./rolldown-runtime-Bd6kNlEP.js");
36
4
  let html_entities = require("html-entities");
37
5
  let fast_xml_parser = require("fast-xml-parser");
38
- let node_fetch = require("node-fetch");
39
- node_fetch = __toESM(node_fetch);
40
6
  let https_proxy_agent = require("https-proxy-agent");
41
- //#region src/errors.ts
42
- var _CouldNotRetrieveTranscript, _RequestBlocked;
7
+ //#region src/errors/index.ts
8
+ var _CouldNotRetrieveTranscript;
9
+ var _RequestBlocked;
43
10
  var YOUTUBE_WATCH_URL$1 = "https://www.youtube.com/watch?v={video_id}";
44
11
  /**
45
12
  * Base exception class for all YouTube Transcript API errors.
@@ -411,6 +378,7 @@ var TranscriptParser = class TranscriptParser {
411
378
  * @returns {string} The cleaned text
412
379
  */
413
380
  cleanTranscriptText(text) {
381
+ if (typeof text !== "string") text = String(text || "");
414
382
  return (0, html_entities.decode)(text).replace(this.htmlRemovalRegex, "");
415
383
  }
416
384
  };
@@ -430,6 +398,9 @@ _TranscriptParser.FORMATTING_TAGS = [
430
398
  //#endregion
431
399
  //#region src/utils/http-error-handler.ts
432
400
  /**
401
+ * @fileoverview HTTP error handling utilities for YouTube API requests
402
+ */
403
+ /**
433
404
  * Checks an HTTP response and throws appropriate errors if the request failed.
434
405
  *
435
406
  * @param {Response} response - The HTTP response to check
@@ -740,7 +711,7 @@ var TranscriptList = class TranscriptList {
740
711
  }
741
712
  };
742
713
  //#endregion
743
- //#region src/types.ts
714
+ //#region src/types/index.ts
744
715
  /**
745
716
  * Playability status enum values
746
717
  */
@@ -1040,12 +1011,375 @@ var TranscriptListFetcher = class {
1040
1011
  }
1041
1012
  };
1042
1013
  //#endregion
1014
+ //#region ../../node_modules/.bun/grab-url@1.6.22/node_modules/grab-url/dist/log.es.js
1015
+ var t = {
1016
+ reset: [0, "000000"],
1017
+ black: [30, "1f2937"],
1018
+ red: [31, "dc2626"],
1019
+ green: [32, "2d6a4f"],
1020
+ yellow: [33, "92400e"],
1021
+ blue: [34, "1a56db"],
1022
+ magenta: [35, "6d28d9"],
1023
+ cyan: [36, "0e7490"],
1024
+ white: [37, "374151"],
1025
+ gray: [90, "6b7280"],
1026
+ brightRed: [91, "ef4444"],
1027
+ brightGreen: [92, "059669"],
1028
+ brightYellow: [93, "d97706"],
1029
+ brightBlue: [94, "3b82f6"],
1030
+ brightMagenta: [95, "8b5cf6"],
1031
+ brightCyan: [96, "06b6d4"],
1032
+ brightWhite: [97, "f9fafb"],
1033
+ bgBlack: [40, "111827"],
1034
+ bgRed: [41, "fca5a5"],
1035
+ bgGreen: [42, "a7f3d0"],
1036
+ bgYellow: [43, "fde68a"],
1037
+ bgBlue: [44, "bfdbfe"],
1038
+ bgMagenta: [45, "ddd6fe"],
1039
+ bgCyan: [46, "a5f3fc"],
1040
+ bgWhite: [47, "f9fafb"],
1041
+ bgGray: [100, "d1d5db"],
1042
+ bgBrightRed: [101, "fee2e2"],
1043
+ bgBrightGreen: [102, "d1fae5"],
1044
+ bgBrightYellow: [103, "fef3c7"],
1045
+ bgBrightBlue: [104, "dbeafe"],
1046
+ bgBrightMagenta: [105, "ede9fe"],
1047
+ bgBrightCyan: [106, "cffafe"],
1048
+ bgBrightWhite: [107, "ffffff"]
1049
+ };
1050
+ function r$1(e = "ansi") {
1051
+ const r = {};
1052
+ for (const [n, [o, i]] of Object.entries(t)) r[n] = "html" === e ? "reset" === n ? "</span>" : `<span style="color:#${i}">` : "\x1B[" + o + "m";
1053
+ return r;
1054
+ }
1055
+ function n$1(e, t = "ansi") {
1056
+ const n = r$1(t);
1057
+ return "string" == typeof e ? n.yellow : "number" == typeof e ? n.cyan : "boolean" == typeof e ? n.magenta : "function" == typeof e ? n.red : null === e ? n.gray : Array.isArray(e) ? n.blue : "object" == typeof e ? n.green : n.white;
1058
+ }
1059
+ function o$1(e) {
1060
+ return "string" == typeof e ? "\"\"" : "number" == typeof e ? "number" : "boolean" == typeof e ? "bool" : "function" == typeof e ? "function" : null === e ? "null" : Array.isArray(e) ? e.length ? "[" + o$1(e[0]) + "]" : "[]" : "object" == typeof e ? "{...}" : typeof e;
1061
+ }
1062
+ function i$2(e, t = 0, l = "ansi") {
1063
+ const g = r$1(l), a = "html" === l, s = " ".repeat(t);
1064
+ if ("object" != typeof e || null === e) return n$1(e, l) + o$1(e) + g.reset;
1065
+ if (Array.isArray(e)) {
1066
+ if (a) {
1067
+ let r = [];
1068
+ e.length && (r = e.every((t) => typeof t == typeof e[0]) ? [i$2(e[0], t + 1, l)] : e.map((e) => i$2(e, t + 1, l)));
1069
+ return `<details open><summary style="cursor:pointer;display:inline">${`${g.blue}[${g.reset}&thinsp;${e.length} × ${o$1(e[0] ?? void 0)}&thinsp;${g.blue}]${g.reset}`}</summary>${r.map((e) => `<div style="margin-left:1.5em">${e}</div>`).join("")}</details>`;
1070
+ }
1071
+ let r = g.blue + "[" + g.reset;
1072
+ return e.length && (r += "\n"), e.every((t) => typeof t == typeof e[0]) ? r += s + " " + i$2(e[0], t + 1, l) + ",\n" : e.forEach((n, o) => {
1073
+ r += s + " " + i$2(n, t + 1, l), o < e.length - 1 && (r += ","), r += "\n";
1074
+ }), r += s + g.blue + "]" + g.reset, r;
1075
+ }
1076
+ const b = Object.keys(e);
1077
+ if (a) {
1078
+ const r = b.slice(0, 4).join(", ") + (b.length > 4 ? "…" : "");
1079
+ return `<details open><summary style="cursor:pointer">${`${g.green}{${g.reset}&nbsp;${r}&nbsp;${g.green}}${g.reset}`}</summary>${b.map((r) => {
1080
+ const a = e[r], s = n$1(a, l);
1081
+ return "object" == typeof a && null !== a ? `<div style="margin-left:1.5em">${s}${r}${g.reset}: ${i$2(a, t + 1, l)}</div>` : `<div style="margin-left:1.5em">${s}${r}: ${o$1(a)}${g.reset}</div>`;
1082
+ }).join("")}</details>`;
1083
+ }
1084
+ let f = g.green + "{" + g.reset;
1085
+ return b.length && (f += "\n"), b.forEach((r, a) => {
1086
+ const y = e[r], c = n$1(y, l);
1087
+ f += s + " ", f += "object" == typeof y && null !== y ? c + r + g.reset + ": " + i$2(y, t + 1, l) : c + r + ": " + o$1(y) + g.reset, a < b.length - 1 && (f += ","), f += "\n";
1088
+ }), f += s + g.green + "}" + g.reset, f;
1089
+ }
1090
+ function l$2(e = "", t = {}) {
1091
+ let { color: n, style: o = "color:rgb(54, 165, 220); font-size: 10pt;", hideInProduction: l, startSpinner: g = !1, stopSpinner: a = !1 } = t;
1092
+ const s = r$1();
1093
+ void 0 === l && (l = "undefined" != typeof window && window?.location.hostname.includes("localhost")), "object" == typeof e && (e = i$2(e) + "\n\n" + JSON.stringify(e, null, 2)), Array.isArray(n) && 1 == n.length && (n = n[0]), n && "undefined" != typeof process && ("string" == typeof e && e.includes("%c") && Array.isArray(n) ? e = e.replace(/%c/g, (e, t) => s[n[t]] || "") : n && "string" == typeof n && (e = (s[n] || "") + e + s.reset));
1094
+ var b = 0;
1095
+ return g ? globalThis.interval = setInterval(() => {
1096
+ process.stdout.write((Array.isArray(n) ? s[n[0]] : s[n] || "") + "\r" + "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏".split("")[b = ++b % 10] + " " + e + s.reset);
1097
+ }, 50) : a ? (clearInterval(globalThis.interval), process.stdout.write("\r" + (e || " ") + " ".repeat("string" == typeof e ? e.length + 20 : 40) + "\n")) : "string" == typeof o ? (1 == o.split(" ").length || n ? o = `color: ${n || o}; font-size: 11pt;` : o.match(/^#[0-9a-fA-F]{6}$/) && (o = `color: ${o}; font-size: 11pt;`), l ? console.debug((o ? "%c" : "") + (e || ""), o) : console.log((o ? "%c" : "") + (e || ""), o)) : "object" == typeof o && console.log(e, ...o), !0;
1098
+ }
1099
+ //#endregion
1100
+ //#region ../../node_modules/.bun/grab-url@1.6.22/node_modules/grab-url/dist/core-BX1uvK6L.js
1101
+ var n = async (e, t) => {
1102
+ let n;
1103
+ return async function(...o) {
1104
+ clearTimeout(n), n = setTimeout(async () => {
1105
+ clearTimeout(n), await e(...o);
1106
+ }, t);
1107
+ };
1108
+ };
1109
+ var o = (e) => new Promise((t) => setTimeout(t, 1e3 * e || 0));
1110
+ var r = (e, t) => {
1111
+ let n = (e) => t?.startsWith(e) || !1, o = e, r = t;
1112
+ return t?.startsWith("http:") || t?.startsWith("https:") ? o = "" : n("/") || o.endsWith("/") ? n("/") && o.endsWith("/") && (r = t.slice(1)) : r = "/" + t, {
1113
+ baseURL: o,
1114
+ path: r
1115
+ };
1116
+ };
1117
+ function s(e) {
1118
+ if ("undefined" == typeof document) return;
1119
+ let t, n = document.getElementById("alert-overlay");
1120
+ if (!n) {
1121
+ n = document.body.appendChild(document.createElement("div")), n.id = "alert-overlay", n.setAttribute("style", "position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center"), n.innerHTML = "<div id=\"alert-box\" style=\"background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:320px;max-width:90vw;max-height:80vh;position:relative;display:flex;flex-direction:column;\">\n <button id=\"close-alert\" style=\"position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;\">&times;</button>\n <div id=\"alert-list\" style=\"overflow:auto;flex:1;font-family:monospace;font-size:13px;\"></div>\n </div>", n.addEventListener("click", (e) => e.target == n && n.remove());
1122
+ const e = document.getElementById("close-alert");
1123
+ e && (e.onclick = () => n.remove());
1124
+ }
1125
+ t = document.getElementById("alert-list"), t.innerHTML += `<div style="border-bottom:1px solid #e5e7eb;margin:0.5em 0;padding-bottom:0.5em">${e}</div>`;
1126
+ }
1127
+ function i$1(e) {
1128
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1129
+ }
1130
+ function a() {
1131
+ "undefined" != typeof document && document.addEventListener("keydown", (t) => {
1132
+ const n = window.grab;
1133
+ if (n && n.log && "i" === t.key && t.ctrlKey && t.altKey) {
1134
+ let t = "";
1135
+ for (const o of n.log) {
1136
+ const n = new Date(o.lastFetchTime).toLocaleString(), r = i$2(o.request, 0, "html"), s = i$2(o.response, 0, "html"), a = i$1(JSON.stringify(o.response, null, 2));
1137
+ t += `\n <details open style="margin-bottom:0.75em">\n <summary style="cursor:pointer;font-weight:bold;padding:4px 0;list-style:none;display:flex;justify-content:space-between">\n <span style="color:#1a56db">${i$1(o.path)}</span>\n <span style="color:#6b7280;font-weight:normal;margin-left:1em">${n}</span>\n </summary>\n <div style="padding-left:1em;margin-top:0.5em">\n <details style="margin-bottom:0.4em">\n <summary style="cursor:pointer;color:#6b7280;font-size:0.9em">Request</summary>\n <div style="padding:0.4em 0 0 1em">${r}</div>\n </details>\n <details open style="margin-bottom:0.4em">\n <summary style="cursor:pointer;color:#6b7280;font-size:0.9em">Response (structure)</summary>\n <div style="padding:0.4em 0 0 1em">${s}</div>\n </details>\n <details style="margin-bottom:0.4em">\n <summary style="cursor:pointer;color:#6b7280;font-size:0.9em">Response (data)</summary>\n <pre style="margin:0.4em 0 0 1em;padding:0.5em;background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;overflow:auto;max-height:300px;font-size:12px">${a}</pre>\n </details>\n </div>\n </details>`;
1138
+ }
1139
+ s(t || "<em>No requests logged yet.</em>");
1140
+ }
1141
+ });
1142
+ }
1143
+ function l$1(o) {
1144
+ return async function i(a, l) {
1145
+ const c = function(e) {
1146
+ return {
1147
+ ..."undefined" != typeof window ? window?.grab?.defaults : globalThis?.grab?.defaults || {},
1148
+ ...e
1149
+ };
1150
+ }(l);
1151
+ let { headers: d, response: u, method: f = c.post ? "POST" : c.put ? "PUT" : c.patch ? "PATCH" : "GET", cache: p, timeout: m = 30, baseURL: y = "undefined" != typeof process && process.env.SERVER_API_URL || "/api/", cancelOngoingIfNew: g, cancelNewIfOngoing: b, rateLimit: w, debug: h, infiniteScroll: v, logger: T = l$2, onRequest: x, onResponse: L, onError: E, onStream: A, unzip: S, dom: O, body: R, post: P, put: k, patch: $, ...j } = c;
1152
+ const q = r(y, a);
1153
+ y = q.baseURL, a = q.path;
1154
+ const F = function(e) {
1155
+ const t = "function" == typeof e ? e : null;
1156
+ return {
1157
+ response: !e || t ? {} : e,
1158
+ resFunction: t
1159
+ };
1160
+ }(u);
1161
+ let N = F.response, D = F.resFunction;
1162
+ const I = ("undefined" != typeof window ? window.grab : globalThis.grab)?.log || [];
1163
+ D ? N = D({
1164
+ ...N,
1165
+ isLoading: !0
1166
+ }) : "object" == typeof N && (N.isLoading = !0);
1167
+ try {
1168
+ const r = await async function(e, t, o, r) {
1169
+ const { debounce: s = 0, repeat: i = 0, repeatEvery: a = null, setDefaults: l = !1 } = o;
1170
+ if (s > 0) return await (await n(async () => {
1171
+ await r(e, {
1172
+ ...t,
1173
+ debounce: 0
1174
+ });
1175
+ }, 1e3 * s))(), o.response || {};
1176
+ if (i > 1) {
1177
+ for (let n = 0; n < i; n++) await r(e, {
1178
+ ...t,
1179
+ repeat: 0
1180
+ });
1181
+ return o.response || {};
1182
+ }
1183
+ if (a) return setInterval(async () => {
1184
+ await r(e, {
1185
+ ...t,
1186
+ repeat: 0,
1187
+ repeatEvery: null
1188
+ });
1189
+ }, 1e3 * a), o.response || {};
1190
+ if (l) {
1191
+ const e = "undefined" != typeof window ? window.grab : globalThis.grab;
1192
+ return e && (e.defaults = {
1193
+ ...t,
1194
+ setDefaults: void 0
1195
+ }), o.response || {};
1196
+ }
1197
+ return null;
1198
+ }(a, l || {}, c, i);
1199
+ if (r) return r;
1200
+ (function(e, t, n, o) {
1201
+ if ("undefined" == typeof window) return;
1202
+ const { regrabOnStale: r, regrabOnFocus: s, regrabOnNetwork: i, cache: a, cacheForTime: l = 60 } = n, c = async () => await o(e, {
1203
+ ...t,
1204
+ cache: !1
1205
+ });
1206
+ r && a && setTimeout(c, 1e3 * l), i && window.addEventListener("online", c), s && (window.addEventListener("focus", c), document.addEventListener("visibilitychange", async () => {
1207
+ "visible" === document.visibilityState && await c();
1208
+ }));
1209
+ })(a, l || {}, c, i);
1210
+ let { params: s, priorRequest: u, response: E, paramsAsText: P } = function(e, t, n, o, r, s) {
1211
+ const { cache: i, cacheForTime: a, infiniteScroll: l } = n, [c, d] = l || [], u = JSON.stringify(c ? {
1212
+ ...t,
1213
+ [c]: void 0
1214
+ } : t);
1215
+ let f = s.find((t) => t.request === u && t.path === e);
1216
+ if (c) {
1217
+ let e = (f?.currentPage || 0) + 1 || t?.[c] || 1;
1218
+ f ? f.currentPage = e : (o[d] = [], e = 1), t = {
1219
+ ...t,
1220
+ [c]: e
1221
+ };
1222
+ } else {
1223
+ for (let e of Object.keys(o)) o[e] = void 0;
1224
+ if (i && f?.response && (!a || f.lastFetchTime > Date.now() - 1e3 * a)) {
1225
+ for (let e of Object.keys(f.response)) o[e] = f.response[e];
1226
+ r && (o = r(o));
1227
+ }
1228
+ }
1229
+ return {
1230
+ params: t,
1231
+ priorRequest: f,
1232
+ response: o,
1233
+ paramsAsText: u
1234
+ };
1235
+ }(a, j, c, N, D, I);
1236
+ if (j = s, N = E, function(e, n, o, r, s) {
1237
+ if ("undefined" == typeof window || !o?.length) return;
1238
+ const [i, , a] = o;
1239
+ if (void 0 === a) return;
1240
+ let l = "string" == typeof a ? document.querySelector(a) : a;
1241
+ l ? (window.scrollListener && "function" == typeof l.removeEventListener && l.removeEventListener("scroll", window.scrollListener), window.scrollListener = (t) => {
1242
+ const o = t.target;
1243
+ localStorage.setItem("scroll", JSON.stringify([
1244
+ o.scrollTop,
1245
+ o.scrollLeft,
1246
+ a
1247
+ ])), o.scrollHeight - o.scrollTop <= o.clientHeight + 200 && s(e, {
1248
+ ...n,
1249
+ cache: !1,
1250
+ [i]: (r?.currentPage || 0) + 1
1251
+ });
1252
+ }, l.addEventListener("scroll", window.scrollListener)) : l$2("paginateDOM not found", { color: "red" });
1253
+ }(a, l, v, u, i), w > 0 && u?.lastFetchTime > Date.now() - 1e3 * w) throw new Error(`Fetch rate limit exceeded for ${a}. Wait ${w}s between requests.`);
1254
+ if (u?.controller) {
1255
+ if (g) u.controller.abort();
1256
+ else if (b) return { isLoading: !0 };
1257
+ }
1258
+ const k = new AbortController();
1259
+ let $ = k.signal;
1260
+ g || "function" != typeof AbortSignal.timeout || ($ = AbortSignal.timeout(1e3 * m)), I.unshift({
1261
+ path: a,
1262
+ request: P,
1263
+ lastFetchTime: Date.now(),
1264
+ controller: k
1265
+ });
1266
+ let { fetchParams: q, paramsGETRequest: F } = function(e, t, n, o, r, s) {
1267
+ const i = [
1268
+ "POST",
1269
+ "PUT",
1270
+ "PATCH"
1271
+ ].includes(e), a = {
1272
+ method: e,
1273
+ headers: {
1274
+ "Content-Type": "application/json",
1275
+ Accept: "application/json",
1276
+ ...t
1277
+ },
1278
+ body: n || (i ? JSON.stringify(o) : null),
1279
+ redirect: "follow",
1280
+ cache: r ? "force-cache" : "no-store",
1281
+ signal: s
1282
+ };
1283
+ let l = "";
1284
+ return i || (l = (Object.keys(o).length ? "?" : "") + new URLSearchParams(o).toString()), {
1285
+ fetchParams: a,
1286
+ paramsGETRequest: l
1287
+ };
1288
+ }(f, d, R, j, !!p, $);
1289
+ if ("function" == typeof x) {
1290
+ const e = x(a, N, j, q);
1291
+ Array.isArray(e) && ([a, N, j, q] = e);
1292
+ }
1293
+ const z = /* @__PURE__ */ new Date(), U = await o(y, a, F, q, j, A, S, O);
1294
+ if (D ? N = D({
1295
+ ...N,
1296
+ isLoading: void 0
1297
+ }) : "object" == typeof N && delete N.isLoading, "function" == typeof L) {
1298
+ const e = L(a, N, j, q);
1299
+ Array.isArray(e) && ([a, N, j, q] = e);
1300
+ }
1301
+ const H = ((Number(/* @__PURE__ */ new Date()) - Number(z)) / 1e3).toFixed(1);
1302
+ h && T(`Path:${y + a + F}\n${JSON.stringify(l, null, 2)}\nTime: ${H}s\nResponse: ${i$2(U)}`);
1303
+ const [, W] = v || [];
1304
+ return N = function(e, t, n, o) {
1305
+ if ("object" == typeof e && null !== e) {
1306
+ for (let n of Object.keys(e)) t[n] = o === n && Array.isArray(t[n]) ? [...t[n], ...e[n]] : e[n];
1307
+ t.data = e;
1308
+ } else n ? t = n({
1309
+ data: e,
1310
+ ...e
1311
+ }) : "object" == typeof t && (t.data = e);
1312
+ return t;
1313
+ }(U, N, D, W), I[0] && (I[0].response = N), D && (N = D(N)), N;
1314
+ } catch (U) {
1315
+ if ("function" == typeof E && E(U.message, y + a, j), c.retryAttempts && c.retryAttempts > 0) return await i(a, {
1316
+ ...l,
1317
+ retryAttempts: --c.retryAttempts
1318
+ });
1319
+ !U.message.includes("signal") && h && (T(`Error: ${U.message}\nPath:${y + a}\n`, { color: "red" }), "undefined" != typeof document && s(U.message)), N = N || {}, N.error = U.message;
1320
+ const e = "function" == typeof u ? u : null;
1321
+ return e ? (N.data = e({
1322
+ isLoading: void 0,
1323
+ error: U.message
1324
+ }), N = N.data) : delete N.isLoading, N;
1325
+ }
1326
+ };
1327
+ }
1328
+ //#endregion
1329
+ //#region ../../node_modules/.bun/grab-url@1.6.22/node_modules/grab-url/dist/grab-api.es.js
1330
+ async function i(t) {
1331
+ const { extract: r } = await Promise.resolve().then(() => require("./archiver-web.es-eh2cNrlm.js")), o = await r({ archiveBuffer: t }), e = {};
1332
+ for (const n of o) e[n.path] = n.content;
1333
+ return e;
1334
+ }
1335
+ async function c(t, r) {
1336
+ const { parseHTML: o } = await Promise.resolve().then(() => require("./index-CLlAnStL-BNm8mEFS.js")), { document: e } = o(t);
1337
+ if (!r || !0 === r) return e;
1338
+ const n = e.querySelector(r);
1339
+ return n ? n.innerHTML ?? n.textContent : null;
1340
+ }
1341
+ var l = l$1(async function(r, o$2, e, n, a, s, l, d) {
1342
+ const u = ("undefined" != typeof window ? window.grab : globalThis.grab)?.mock?.[o$2], f = JSON.stringify(a);
1343
+ if (u && (!u.method || u.method === n.method) && (!u.params || f === JSON.stringify(u.params))) return await o(u.delay || 0), "function" == typeof u.response ? u.response(a) : u.response;
1344
+ const w = await fetch(r + o$2 + e, n).catch((t) => {
1345
+ throw new Error(t.message);
1346
+ });
1347
+ if (!w.ok) throw new Error(`HTTP error: ${w.status} ${w.statusText}`);
1348
+ if (s) return await s(w.body), null;
1349
+ const h = w.headers.get("content-type") ?? "", g = !1 !== l && (h.includes("application/zip") || h.includes("application/x-zip")), m = !1 !== d && ("string" == typeof d || h.includes("text/html"));
1350
+ if (g) return { data: await i(await w.arrayBuffer().catch((t) => {
1351
+ throw new Error("Error reading zip: " + t);
1352
+ })) };
1353
+ if (m) return { data: await c(await w.text().catch((t) => {
1354
+ throw new Error("Error reading html: " + t);
1355
+ }), "string" != typeof d || d) };
1356
+ return await (h.includes("application/json") ? w.json() : h.includes("application/pdf") || h.includes("application/octet-stream") ? w.blob() : h ? w.text() : w.json()).catch((t) => {
1357
+ throw new Error("Error parsing response: " + t);
1358
+ });
1359
+ });
1360
+ var d = l;
1361
+ d.instance = (t = {}) => (r, o = {}) => l(r, {
1362
+ ...t,
1363
+ ...o
1364
+ }), d.log = [], d.mock = {}, d.defaults = {}, "undefined" != typeof window ? (window.log = l$2, window.grab = d, a(), document.addEventListener("DOMContentLoaded", () => {
1365
+ try {
1366
+ const t = localStorage.getItem("scroll");
1367
+ if (!t) return;
1368
+ const [r, o, e] = JSON.parse(t);
1369
+ if (!r || !e) return;
1370
+ const n = document.querySelector(e);
1371
+ n && (n.scrollTop = r, n.scrollLeft = o);
1372
+ } catch (t) {
1373
+ console.warn("Failed to restore scroll position", t);
1374
+ }
1375
+ })) : "undefined" != typeof globalThis && (globalThis.log = l$2, globalThis.grab = d);
1376
+ //#endregion
1043
1377
  //#region src/http/fetch-http-client.ts
1044
1378
  /**
1045
- * @fileoverview HTTP client implementation using node-fetch
1379
+ * @fileoverview HTTP client implementation using grab-url
1046
1380
  */
1047
1381
  /**
1048
- * Simple HTTP client implementation using node-fetch with proxy support.
1382
+ * Simple HTTP client implementation using grab-url with proxy support.
1049
1383
  * This client handles HTTP/HTTPS requests with configurable headers and proxy settings.
1050
1384
  *
1051
1385
  * @internal
@@ -1086,7 +1420,7 @@ var FetchHttpClient = class {
1086
1420
  */
1087
1421
  async get(url, options) {
1088
1422
  const proxyAgent = this.getProxyAgentForUrl(url);
1089
- return (0, node_fetch.default)(url, {
1423
+ return d(url, {
1090
1424
  ...options,
1091
1425
  method: "GET",
1092
1426
  headers: {
@@ -1105,7 +1439,7 @@ var FetchHttpClient = class {
1105
1439
  */
1106
1440
  async post(url, options) {
1107
1441
  const proxyAgent = this.getProxyAgentForUrl(url);
1108
- return (0, node_fetch.default)(url, {
1442
+ return d(url, {
1109
1443
  ...options,
1110
1444
  method: "POST",
1111
1445
  headers: {
@@ -1344,7 +1678,53 @@ var YouTubeTranscriptApi = class {
1344
1678
  }
1345
1679
  };
1346
1680
  //#endregion
1347
- //#region src/proxies.ts
1681
+ //#region src/utils/transcript-utils.ts
1682
+ /**
1683
+ * Encodes speech speed from a fetched transcript into a compact run-length string.
1684
+ * Optionally prepends a YouTube embed iframe with data-timestamps for player sync.
1685
+ * @param transcript - A FetchedTranscript returned by fetchTranscript()
1686
+ * @param addPlayer - If true, prepends an iframe embed with data-timestamps attribute
1687
+ * @returns html content, word count, run-length encoded speed string, and raw timestamps
1688
+ */
1689
+ function encodeTranscriptSpeeds(transcript, addPlayer = false) {
1690
+ let charCount = 0;
1691
+ const timestamps = [];
1692
+ const textParts = [];
1693
+ for (const snippet of transcript.snippets) {
1694
+ textParts.push(snippet.text);
1695
+ charCount += snippet.text.length;
1696
+ if (snippet.start > 0) timestamps.push([charCount, snippet.start]);
1697
+ }
1698
+ const content = textParts.join(" ").replace(/\s+/g, " ");
1699
+ const word_count = content.split(/\s+/).filter(Boolean).length;
1700
+ const speedValues = timestamps.map(([char, time]) => Math.floor(char / time) - 10);
1701
+ if (speedValues.length === 0) return {
1702
+ html: content,
1703
+ word_count,
1704
+ speeds: ""
1705
+ };
1706
+ const compressed = [];
1707
+ const compressedCount = [];
1708
+ let currentNum = speedValues[0];
1709
+ let count = 1;
1710
+ for (let i = 1; i < speedValues.length; i++) if (speedValues[i] === currentNum) count++;
1711
+ else {
1712
+ compressed.push(currentNum);
1713
+ compressedCount.push(count);
1714
+ currentNum = speedValues[i];
1715
+ count = 1;
1716
+ }
1717
+ compressed.push(currentNum);
1718
+ compressedCount.push(count);
1719
+ const speeds = compressed.map((val, i) => `${val}x${compressedCount[i]}`).join(",");
1720
+ return {
1721
+ html: addPlayer ? `<iframe width="100%" height="315px" data-timestamps="${speeds}" src="https://www.youtube.com/embed/${transcript.videoId}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>${content}` : content,
1722
+ word_count,
1723
+ speeds
1724
+ };
1725
+ }
1726
+ //#endregion
1727
+ //#region src/proxies/index.ts
1348
1728
  var _WebshareProxyConfig;
1349
1729
  /**
1350
1730
  * Exception for invalid proxy configurations
@@ -1510,7 +1890,7 @@ _WebshareProxyConfig = WebshareProxyConfig;
1510
1890
  _WebshareProxyConfig.DEFAULT_DOMAIN_NAME = "p.webshare.io";
1511
1891
  _WebshareProxyConfig.DEFAULT_PORT = 80;
1512
1892
  //#endregion
1513
- //#region src/formatters.ts
1893
+ //#region src/formatters/index.ts
1514
1894
  /**
1515
1895
  * Base abstract class for all transcript formatters.
1516
1896
  */
@@ -1604,7 +1984,7 @@ var WebVTTFormatter = class extends SubtitleFormatterBase {
1604
1984
  };
1605
1985
  //#endregion
1606
1986
  //#region package.json
1607
- var package_exports = /* @__PURE__ */ __exportAll({
1987
+ var package_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
1608
1988
  author: () => "",
1609
1989
  bin: () => bin,
1610
1990
  default: () => package_default,
@@ -1618,14 +1998,15 @@ var package_exports = /* @__PURE__ */ __exportAll({
1618
1998
  main: () => main$1,
1619
1999
  module: () => module$1,
1620
2000
  name: () => name,
2001
+ repository: () => repository,
1621
2002
  scripts: () => scripts,
1622
2003
  types: () => types,
1623
2004
  version: () => version
1624
- }), name, version, description, main$1, module$1, types, exports$1, bin, scripts, keywords, dependencies, devDependencies, engines, package_default;
1625
- var init_package = __esmMin((() => {
2005
+ }), name, version, description, main$1, module$1, types, exports$1, bin, scripts, keywords, repository, dependencies, devDependencies, engines, package_default;
2006
+ var init_package = require_rolldown_runtime.__esmMin((() => {
1626
2007
  name = "extract-youtube";
1627
- version = "1.0.2";
1628
- description = "Fast, serverless-optimized YouTube transcript extractor. TypeScript port of the popular Python youtube-transcript-api (100k+ monthly downloads). Works in AWS Lambda, Vercel, Cloudflare Workers. ~7KB gzipped.";
2008
+ version = "1.0.3";
2009
+ description = "Fast, no-browser serverless-optimized YouTube transcript extractor";
1629
2010
  main$1 = "dist/index.cjs";
1630
2011
  module$1 = "dist/index.mjs";
1631
2012
  types = "dist/index.d.ts";
@@ -1634,7 +2015,7 @@ var init_package = __esmMin((() => {
1634
2015
  "require": "./dist/index.cjs",
1635
2016
  "types": "./dist/index.d.ts"
1636
2017
  } };
1637
- bin = { "extract-youtube": "dist/cli.js" };
2018
+ bin = "dist/cli.js";
1638
2019
  scripts = {
1639
2020
  "build": "npm run build:types && npm run build:lib && npm run build:cli",
1640
2021
  "build:types": "tsc --emitDeclarationOnly --declaration --declarationMap",
@@ -1645,6 +2026,7 @@ var init_package = __esmMin((() => {
1645
2026
  "prepare": "npm run build",
1646
2027
  "ship": "npx standard-version --release-as patch; npm publish",
1647
2028
  "test": "jest",
2029
+ "test:speeds": "bun src/cli.ts dQw4w9WgXcQ -f speeds",
1648
2030
  "lint": "eslint src --ext .ts"
1649
2031
  };
1650
2032
  keywords = [
@@ -1668,17 +2050,21 @@ var init_package = __esmMin((() => {
1668
2050
  "extraction",
1669
2051
  "optimized"
1670
2052
  ];
2053
+ repository = {
2054
+ "type": "git",
2055
+ "url": "https://github.com/OpenSourceAGI/qwksearch-research-agent",
2056
+ "directory": "packages/extract-youtube"
2057
+ };
1671
2058
  dependencies = {
1672
2059
  "fast-xml-parser": "^4.2.5",
1673
2060
  "html-entities": "^2.3.3",
1674
- "https-proxy-agent": "^5.0.1",
1675
- "node-fetch": "^2.6.7",
2061
+ "https-proxy-agent": "^7.0.6",
2062
+ "grab-url": "^1.6.19",
1676
2063
  "youtube-transcript-api": "^3.0.6"
1677
2064
  };
1678
2065
  devDependencies = {
1679
2066
  "@types/jest": "^29.5.14",
1680
- "@types/node": "^18.0.0",
1681
- "@types/node-fetch": "^2.6.2",
2067
+ "@types/node": ">=22.12.0",
1682
2068
  "jest": "^29.5.0",
1683
2069
  "terser": "^5.46.0",
1684
2070
  "ts-jest": "^29.1.0",
@@ -1699,6 +2085,7 @@ var init_package = __esmMin((() => {
1699
2085
  keywords,
1700
2086
  author: "",
1701
2087
  license: "MIT",
2088
+ repository,
1702
2089
  dependencies,
1703
2090
  devDependencies,
1704
2091
  engines
@@ -1762,7 +2149,7 @@ OPTIONS:
1762
2149
  -h, --help Show this help message
1763
2150
  -v, --version Show version number
1764
2151
  -l, --languages <codes> Comma-separated language codes (e.g., en,de,fr)
1765
- -f, --format <type> Output format: json, text, srt, webvtt, pretty (default: json)
2152
+ -f, --format <type> Output format: json, text, srt, webvtt, pretty, speeds (default: json)
1766
2153
  -p, --preserve-formatting Preserve text formatting (line breaks, etc.)
1767
2154
  --proxy <url> HTTP/HTTPS proxy URL
1768
2155
  --webshare-user <username> Webshare proxy username
@@ -1786,10 +2173,13 @@ EXAMPLES:
1786
2173
 
1787
2174
  # Extract transcript as plain text with preserved formatting
1788
2175
  extract-youtube jNQXAC9IVRw -f text -p
2176
+
2177
+ # Extract speech speed encoding with joined text and timestamps
2178
+ extract-youtube jNQXAC9IVRw -f speeds
1789
2179
  `);
1790
2180
  }
1791
2181
  function showVersion() {
1792
- const pkg = (init_package(), __toCommonJS(package_exports).default);
2182
+ const pkg = (init_package(), require_rolldown_runtime.__toCommonJS(package_exports).default);
1793
2183
  console.log(`extract-youtube v${pkg.version}`);
1794
2184
  }
1795
2185
  async function main() {
@@ -1836,6 +2226,15 @@ async function main() {
1836
2226
  case "pretty":
1837
2227
  output = new PrettyPrintFormatter().formatTranscript(transcript);
1838
2228
  break;
2229
+ case "speeds": {
2230
+ const { html, word_count, speeds } = encodeTranscriptSpeeds(transcript);
2231
+ output = JSON.stringify({
2232
+ html,
2233
+ word_count,
2234
+ speeds
2235
+ });
2236
+ break;
2237
+ }
1839
2238
  default: throw new Error(`Unknown format: ${format}`);
1840
2239
  }
1841
2240
  console.log(output);