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.
- package/README.md +137 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +44 -12
- package/dist/cli.js.map +1 -1
- package/dist/errors/index.d.ts +101 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/fetchers/transcript-list-fetcher.d.ts +133 -0
- package/dist/fetchers/transcript-list-fetcher.d.ts.map +1 -0
- package/dist/fetchers/youtube-data-extractor.d.ts +53 -0
- package/dist/fetchers/youtube-data-extractor.d.ts.map +1 -0
- package/dist/formatters/index.d.ts +85 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/http/fetch-http-client.d.ts +57 -0
- package/dist/http/fetch-http-client.d.ts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/models/fetched-transcript.d.ts +77 -0
- package/dist/models/fetched-transcript.d.ts.map +1 -0
- package/dist/models/index.d.ts +7 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/transcript-list.d.ts +132 -0
- package/dist/models/transcript-list.d.ts.map +1 -0
- package/dist/models/transcript.d.ts +102 -0
- package/dist/models/transcript.d.ts.map +1 -0
- package/dist/parsers/transcript-parser.d.ts +76 -0
- package/dist/parsers/transcript-parser.d.ts.map +1 -0
- package/dist/proxies/index.d.ts +140 -0
- package/dist/proxies/index.d.ts.map +1 -0
- package/dist/react/YouTubeTranscriptModal.d.ts +49 -0
- package/dist/react/YouTubeTranscriptModal.d.ts.map +1 -0
- package/dist/react/index.cjs +2 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.mjs +4 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/types/index.d.ts +114 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/extract-video-id.d.ts +22 -0
- package/dist/utils/extract-video-id.d.ts.map +1 -0
- package/dist/utils/http-error-handler.d.ts +20 -0
- package/dist/utils/http-error-handler.d.ts.map +1 -0
- package/dist/utils/transcript-utils.d.ts +28 -0
- package/dist/utils/transcript-utils.d.ts.map +1 -0
- package/dist/youtube-transcript-api.d.ts +208 -0
- package/dist/youtube-transcript-api.d.ts.map +1 -0
- package/package.json +41 -8
- package/src/index.ts +1 -0
- package/src/react/YouTubeTranscriptModal.tsx +354 -0
- package/src/react/index.ts +8 -0
- package/src/utils/extract-video-id.ts +60 -0
- package/jest.config.js +0 -19
- package/test/api.test.ts +0 -163
- package/test/article-formatter.test.ts +0 -107
- package/test/errors.test.ts +0 -204
- package/test/examples.ts +0 -298
- package/test/fetchers.test.ts +0 -283
- package/test/formatters.test.ts +0 -239
- package/test/integration.test.ts +0 -283
- package/test/models.test.ts +0 -232
- package/test/transcript-parser.test.ts +0 -96
- package/test/transcript-utils.test.ts +0 -132
- package/tsconfig.json +0 -20
- package/tsconfig.test.json +0 -10
- 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 @@
|
|
|
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.
|
|
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
|
-
"
|
|
2016
|
-
|
|
2017
|
-
|
|
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
|
-
"
|
|
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:
|
|
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
|
-
"
|
|
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
|
};
|