extract-youtube 1.0.256 → 1.0.257

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 (70) hide show
  1. package/README.md +137 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +44 -12
  5. package/dist/cli.js.map +1 -1
  6. package/dist/errors/index.d.ts +101 -0
  7. package/dist/errors/index.d.ts.map +1 -0
  8. package/dist/fetchers/transcript-list-fetcher.d.ts +133 -0
  9. package/dist/fetchers/transcript-list-fetcher.d.ts.map +1 -0
  10. package/dist/fetchers/youtube-data-extractor.d.ts +53 -0
  11. package/dist/fetchers/youtube-data-extractor.d.ts.map +1 -0
  12. package/dist/formatters/index.d.ts +85 -0
  13. package/dist/formatters/index.d.ts.map +1 -0
  14. package/dist/http/fetch-http-client.d.ts +57 -0
  15. package/dist/http/fetch-http-client.d.ts.map +1 -0
  16. package/dist/index.cjs +1 -1
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.mjs +1 -1
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/models/fetched-transcript.d.ts +77 -0
  23. package/dist/models/fetched-transcript.d.ts.map +1 -0
  24. package/dist/models/index.d.ts +7 -0
  25. package/dist/models/index.d.ts.map +1 -0
  26. package/dist/models/transcript-list.d.ts +132 -0
  27. package/dist/models/transcript-list.d.ts.map +1 -0
  28. package/dist/models/transcript.d.ts +102 -0
  29. package/dist/models/transcript.d.ts.map +1 -0
  30. package/dist/parsers/transcript-parser.d.ts +76 -0
  31. package/dist/parsers/transcript-parser.d.ts.map +1 -0
  32. package/dist/proxies/index.d.ts +140 -0
  33. package/dist/proxies/index.d.ts.map +1 -0
  34. package/dist/react/YouTubeTranscriptModal.d.ts +49 -0
  35. package/dist/react/YouTubeTranscriptModal.d.ts.map +1 -0
  36. package/dist/react/index.cjs +2 -0
  37. package/dist/react/index.cjs.map +1 -0
  38. package/dist/react/index.d.ts +8 -0
  39. package/dist/react/index.d.ts.map +1 -0
  40. package/dist/react/index.mjs +4 -0
  41. package/dist/react/index.mjs.map +1 -0
  42. package/dist/types/index.d.ts +114 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/utils/extract-video-id.d.ts +22 -0
  45. package/dist/utils/extract-video-id.d.ts.map +1 -0
  46. package/dist/utils/http-error-handler.d.ts +20 -0
  47. package/dist/utils/http-error-handler.d.ts.map +1 -0
  48. package/dist/utils/transcript-utils.d.ts +28 -0
  49. package/dist/utils/transcript-utils.d.ts.map +1 -0
  50. package/dist/youtube-transcript-api.d.ts +208 -0
  51. package/dist/youtube-transcript-api.d.ts.map +1 -0
  52. package/package.json +41 -8
  53. package/src/index.ts +1 -0
  54. package/src/react/YouTubeTranscriptModal.tsx +354 -0
  55. package/src/react/index.ts +8 -0
  56. package/src/utils/extract-video-id.ts +60 -0
  57. package/jest.config.js +0 -19
  58. package/test/api.test.ts +0 -163
  59. package/test/article-formatter.test.ts +0 -107
  60. package/test/errors.test.ts +0 -204
  61. package/test/examples.ts +0 -298
  62. package/test/fetchers.test.ts +0 -283
  63. package/test/formatters.test.ts +0 -239
  64. package/test/integration.test.ts +0 -283
  65. package/test/models.test.ts +0 -232
  66. package/test/transcript-parser.test.ts +0 -96
  67. package/test/transcript-utils.test.ts +0 -132
  68. package/tsconfig.json +0 -20
  69. package/tsconfig.test.json +0 -10
  70. package/vite.config.ts +0 -77
package/README.md CHANGED
@@ -27,6 +27,7 @@
27
27
  <a href="https://codespaces.new/vtempest/qwksearch-research-agent">
28
28
  <img src="https://github.com/codespaces/badge.svg" width="150" height="20" />
29
29
  </a>
30
+ <a href="https://codecov.io/gh/OpenSourceAGI/qwksearch-research-agent"><img src="https://codecov.io/gh/OpenSourceAGI/qwksearch-research-agent/graph/badge.svg?component=package-extract-youtube" alt="Coverage" /></a>
30
31
  </p>
31
32
 
32
33
  # Extract YouTube Transcript
@@ -244,6 +245,142 @@ extract-youtube --help
244
245
  - `--webshare-user <username>` - Webshare proxy username
245
246
  - `--webshare-pass <password>` - Webshare proxy password
246
247
 
248
+ ## React Popout Modal (Video + Synced Subtitles)
249
+
250
+ `extract-youtube/react` ships a self-contained popout modal component:
251
+ click a trigger, and it opens a YouTube player next to a transcript panel
252
+ that scrolls and highlights in sync with playback — click any line to seek
253
+ the video there. It has no dependency on any particular design system (it
254
+ injects its own minimal, scoped styles), so it drops into any React app.
255
+ It was ported from the video transcript modal used in production on
256
+ [debate-ai.com](https://debate-ai.com). The layout: player on the left,
257
+ a "Transcript" panel on the right (stacks below the player on narrow
258
+ screens) — see the `demo/` app below to try it live.
259
+
260
+ ### Why it's a separate entry point
261
+
262
+ `extract-youtube` (the main entry) fetches captions server-side with no
263
+ browser dependency at all — that's the whole point of the package.
264
+ `extract-youtube/react` is a second, independent entry point that only
265
+ exports UI: it never imports the transcript-fetching code, and the main
266
+ entry never imports React. Import only the one you need and the other
267
+ never ends up in your bundle. React, ReactDOM, and `lucide-react` (used
268
+ for the modal's icons) are peer dependencies — install them yourself if
269
+ you don't already have them:
270
+
271
+ ```bash
272
+ npm install extract-youtube react react-dom lucide-react
273
+ ```
274
+
275
+ ### Setup: video captions still have to be fetched server-side
276
+
277
+ The modal itself never talks to YouTube's caption endpoints directly —
278
+ browsers can't (no CORS, and it would leak this package's whole fetching
279
+ strategy client-side for no benefit). Instead, you expose one small
280
+ backend endpoint that calls this package's `YouTubeTranscriptApi`, and
281
+ point the modal at it with `transcriptUrl`.
282
+
283
+ **1. Add a backend endpoint** (any framework works — this is a Next.js
284
+ route handler, following the same pattern as the Vercel Edge example
285
+ above):
286
+
287
+ ```typescript
288
+ // app/api/transcript/route.ts
289
+ import { NextRequest, NextResponse } from 'next/server';
290
+ import { YouTubeTranscriptApi, extractVideoId } from 'extract-youtube';
291
+
292
+ const api = new YouTubeTranscriptApi();
293
+
294
+ export async function GET(req: NextRequest) {
295
+ const raw = req.nextUrl.searchParams.get('videoId');
296
+ const videoId = raw ? extractVideoId(raw) : null;
297
+ if (!videoId) {
298
+ return NextResponse.json({ error: 'Missing videoId' }, { status: 400 });
299
+ }
300
+
301
+ try {
302
+ const transcript = await api.fetchTranscript(videoId, { languages: ['en'] });
303
+ return NextResponse.json({ videoId, snippets: transcript.toRawData() });
304
+ } catch (error) {
305
+ // 200 + an `error` field, not a 4xx/5xx — "no captions for this video"
306
+ // isn't a server failure, and the modal checks this field either way.
307
+ return NextResponse.json({
308
+ videoId,
309
+ snippets: [],
310
+ error: error instanceof Error ? error.message : 'Failed to fetch transcript',
311
+ });
312
+ }
313
+ }
314
+ ```
315
+
316
+ **2. Render the modal**, pointing it at that endpoint:
317
+
318
+ ```tsx
319
+ import { YouTubeTranscriptModal } from 'extract-youtube/react';
320
+ import { extractVideoId } from 'extract-youtube';
321
+
322
+ function VideoCard({ url, title }: { url: string; title: string }) {
323
+ const videoId = extractVideoId(url); // handles watch/youtu.be/embed/shorts/live URLs, or a bare ID
324
+ if (!videoId) return null;
325
+
326
+ return (
327
+ <YouTubeTranscriptModal
328
+ videoId={videoId}
329
+ title={title}
330
+ transcriptUrl="/api/transcript"
331
+ />
332
+ );
333
+ }
334
+ ```
335
+
336
+ That's the whole setup — one endpoint, one component. `extractVideoId`
337
+ (exported from the main entry, since it runs fine in Node) also accepts
338
+ any of the URL shapes YouTube uses, so you can pass what a user pastes in
339
+ straight through.
340
+
341
+ ### Props
342
+
343
+ | Prop | Type | Description |
344
+ | --- | --- | --- |
345
+ | `videoId` | `string` | **Required.** The YouTube video ID (not a full URL — run it through `extractVideoId` first if needed). |
346
+ | `title` | `string` | Shown in the modal header and as the iframe's accessible title. |
347
+ | `transcriptUrl` | `string` | URL of your backend endpoint (see above). Fetched with `fetch()` when the modal opens. The `videoId` query param is appended automatically if not already present. |
348
+ | `fetchTranscript` | `(videoId: string) => Promise<{ snippets, error? }>` | Use instead of `transcriptUrl` if you want to load the transcript some other way (e.g. from a React Query cache). |
349
+ | `snippets` | `TranscriptSnippet[]` | Pass transcript data directly to skip fetching entirely — e.g. if you already loaded it server-side. |
350
+ | `trigger` | `ReactNode` | Custom element that opens the modal on click. Defaults to a small captions-icon button. |
351
+ | `onOpenChange` | `(open: boolean) => void` | Called whenever the modal opens or closes. |
352
+
353
+ ### Demo: video player + subtitle sidebar, for any video
354
+
355
+ The `demo/` folder is a small standalone app — an Express server for the
356
+ transcript endpoint plus a Vite/React page with a "paste any YouTube URL"
357
+ input — that puts the whole flow together end to end.
358
+
359
+ ```bash
360
+ cd packages/extract-youtube
361
+ npm run build # builds dist/ (main lib + dist/react), which the demo depends on
362
+ npm run demo # installs the demo's own deps and starts it
363
+ ```
364
+
365
+ `npm run demo` runs `cd demo && npm install && npm run dev`, which starts
366
+ both the Express transcript API (port 8787) and the Vite dev server
367
+ (port 5173, proxying `/api` to 8787) together. Open
368
+ **http://localhost:5173**, paste any YouTube URL (or leave the default),
369
+ click **Load**, then **▶ Open video + subtitles** — the player opens with
370
+ the transcript loading in the side panel, synced to playback.
371
+
372
+ Run the two halves separately if you'd rather:
373
+
374
+ ```bash
375
+ cd packages/extract-youtube/demo
376
+ npm install
377
+ npm run server # Express API on :8787
378
+ npm run dev # in another terminal — Vite dev server on :5173
379
+ ```
380
+
381
+ See `demo/server.js` for the Express endpoint and `demo/src/App.jsx` for
382
+ the page that wires the input box up to the modal.
383
+
247
384
  ## Features
248
385
 
249
386
  ### Core Functionality
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview CLI for extracting YouTube transcripts
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;GAEG"}
package/dist/cli.js CHANGED
@@ -1994,42 +1994,55 @@ var package_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
1994
1994
  devDependencies: () => devDependencies,
1995
1995
  engines: () => engines,
1996
1996
  exports: () => exports$1,
1997
+ files: () => files,
1997
1998
  keywords: () => keywords,
1998
1999
  license: () => license,
1999
2000
  main: () => main$1,
2000
2001
  module: () => module$1,
2001
2002
  name: () => name,
2003
+ peerDependencies: () => peerDependencies,
2004
+ peerDependenciesMeta: () => peerDependenciesMeta,
2002
2005
  repository: () => repository,
2003
2006
  scripts: () => scripts,
2004
2007
  types: () => types,
2005
2008
  version: () => version
2006
- }), name, version, description, main$1, module$1, types, exports$1, bin, scripts, keywords, license, repository, dependencies, devDependencies, engines, package_default;
2009
+ }), name, version, description, main$1, module$1, types, exports$1, bin, files, scripts, keywords, license, repository, dependencies, peerDependencies, peerDependenciesMeta, devDependencies, engines, package_default;
2007
2010
  var init_package = require_rolldown_runtime.__esmMin((() => {
2008
2011
  name = "extract-youtube";
2009
- version = "1.0.253";
2012
+ version = "1.0.256";
2010
2013
  description = "Fast, no-browser serverless-optimized YouTube transcript extractor";
2011
2014
  main$1 = "dist/index.cjs";
2012
2015
  module$1 = "dist/index.mjs";
2013
2016
  types = "dist/index.d.ts";
2014
- exports$1 = { ".": {
2015
- "import": "./dist/index.mjs",
2016
- "require": "./dist/index.cjs",
2017
- "types": "./dist/index.d.ts"
2018
- } };
2017
+ exports$1 = {
2018
+ ".": {
2019
+ "types": "./dist/index.d.ts",
2020
+ "import": "./dist/index.mjs",
2021
+ "require": "./dist/index.cjs"
2022
+ },
2023
+ "./react": {
2024
+ "types": "./dist/react/index.d.ts",
2025
+ "import": "./dist/react/index.mjs",
2026
+ "require": "./dist/react/index.cjs"
2027
+ }
2028
+ };
2019
2029
  bin = "dist/cli.js";
2030
+ files = ["dist", "src"];
2020
2031
  scripts = {
2021
- "build": "npm run build:types && npm run build:lib && npm run build:cli",
2032
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
2033
+ "build": "npm run clean && npm run build:lib && npm run build:cli && npm run build:react && npm run build:types",
2022
2034
  "build:types": "tsc --emitDeclarationOnly --declaration --declarationMap",
2023
2035
  "build:lib": "vite build",
2024
2036
  "build:cli": "BUILD_CLI=true vite build",
2025
- "build:bundle": "npm run build:lib && npm run build:cli",
2037
+ "build:react": "BUILD_REACT=true vite build",
2038
+ "build:bundle": "npm run build:lib && npm run build:cli && npm run build:react",
2026
2039
  "build:legacy": "tsc",
2027
- "prepare": "npm run build",
2028
- "ship": "npx standard-version --release-as patch; npm publish",
2040
+ "ship": "npm run build && npx standard-version --release-as patch; npm publish",
2029
2041
  "test": "jest",
2030
2042
  "test:coverage": "jest --coverage",
2031
2043
  "test:speeds": "bun src/cli.ts dQw4w9WgXcQ -f speeds",
2032
- "lint": "eslint src --ext .ts"
2044
+ "lint": "eslint src --ext .ts",
2045
+ "demo": "cd demo && npm install && npm run dev"
2033
2046
  };
2034
2047
  keywords = [
2035
2048
  "youtube",
@@ -2065,10 +2078,25 @@ var init_package = require_rolldown_runtime.__esmMin((() => {
2065
2078
  "grab-url": "^1.6.19",
2066
2079
  "youtube-transcript-api": "^3.0.6"
2067
2080
  };
2081
+ peerDependencies = {
2082
+ "react": ">=18",
2083
+ "react-dom": ">=18",
2084
+ "lucide-react": "^0.300.0 || ^1.0.0"
2085
+ };
2086
+ peerDependenciesMeta = {
2087
+ "react": { "optional": true },
2088
+ "react-dom": { "optional": true },
2089
+ "lucide-react": { "optional": true }
2090
+ };
2068
2091
  devDependencies = {
2069
2092
  "@types/jest": "^29.5.14",
2070
2093
  "@types/node": ">=22.12.0",
2094
+ "@types/react": "^19.2.17",
2095
+ "@types/react-dom": "^19.2.3",
2071
2096
  "jest": "^29.5.0",
2097
+ "lucide-react": "^1.17.0",
2098
+ "react": "^19.2.7",
2099
+ "react-dom": "^19.2.7",
2072
2100
  "terser": "^5.46.0",
2073
2101
  "ts-jest": "^29.1.0",
2074
2102
  "typescript": "^5.0.0",
@@ -2084,12 +2112,16 @@ var init_package = require_rolldown_runtime.__esmMin((() => {
2084
2112
  types,
2085
2113
  exports: exports$1,
2086
2114
  bin,
2115
+ "//files": "Publish only what consumers resolve. Without this the tarball also carries test/, jest.config.js, tsconfig*.json and vite.config.ts. Matches extract-webpage and extract-pdf.",
2116
+ files,
2087
2117
  scripts,
2088
2118
  keywords,
2089
2119
  author: "",
2090
2120
  license,
2091
2121
  repository,
2092
2122
  dependencies,
2123
+ peerDependencies,
2124
+ peerDependenciesMeta,
2093
2125
  devDependencies,
2094
2126
  engines
2095
2127
  };