A Manifest V3 Chrome extension that takes a YouTube URL, pulls the video’s transcript, and exports it as a formatted PDF — no server, no API key, no third-party service. Everything runs locally in the browser.
watch?v=, youtu.be/, /shorts/, /embed/,
/live/, or a bare 11-character video ID. The current tab’s video is
detected automatically..pdf locally with a built-in, dependency-free
PDF writer.git clone https://github.com/mithudso/youtube-transcript-to-pdf.git
cd youtube-transcript-to-pdf
chrome://extensions.Since 2024, YouTube requires a browser-minted proof-of-origin token on caption
downloads. A plain request to the timedtext endpoint — even one carrying the
user’s cookies — comes back HTTP 200 with an empty body. The extension
therefore tries two strategies in order:
timedtext. Silent and instant when YouTube allows it.Strategy 2 is what works most of the time. It is slower and briefly opens a tab, which is the price of YouTube’s token requirement.
| Path | Role |
|---|---|
manifest.json |
MV3 manifest: permissions, service worker, popup, icons. |
src/background/service-worker.js |
Owns all network and tab work; runs the two strategies. |
src/content/scrape-transcript.js |
Injected on demand; reads the transcript panel. |
src/lib/youtube.js |
URL parsing and watch-page scraping. Pure, unit-tested. |
src/lib/transcript.js |
Caption parsing (json3 + legacy XML) and formatting. Pure. |
src/lib/pdf.js |
Dependency-free PDF writer. Pure. |
src/popup/ |
Popup UI. Builds the PDF and triggers the download. |
src/viewer/ |
Printable page for the Chrome “Save as PDF” path. |
The popup is a thin client: it sends one message and renders the reply, so closing it mid-fetch cannot abort the work. The service worker holds no state in module scope, since MV3 terminates it after roughly 30 seconds idle.
src/lib/ has no chrome.* dependency, which is what makes it testable under
plain Node.
| Permission | Why |
|---|---|
tabs |
Read the active tab’s URL to prefill the input, and find an open tab showing the requested video. |
scripting |
Inject the transcript-panel reader when the direct download is blocked. |
storage |
Remember formatting preferences; pass the transcript to the printable viewer. |
downloads |
Save the generated PDF. |
host_permissions on youtube.com |
Fetch watch pages and caption tracks. Scoped to YouTube only. |
No analytics, no telemetry, no remote code. Transcript data never leaves the
machine. Direct fetches use credentials: 'omit'. The transcript-panel fallback uses a
normal YouTube tab, which can use the browser’s existing YouTube session. The
extension does not read cookies or credentials.
npm test # unit and regression tests over modules and browser control flow
npm run lint:manifest # manifest + icon-dimension validation
npm run lint:docs # documentation index validation
npm run check # syntax, manifest, tests and docs
npm run build # dist/youtube-transcript-to-pdf-<version>.zip
Tests run on the Node built-in test runner — no dependencies to install.
See CONTRIBUTING.md for the reload matrix, debugging notes, and the bar for a pull request.
Download PDF. The built-in writer uses the standard
Helvetica fonts, which are Latin-1 only. Typographic characters are
transliterated (— → --, " → "); anything outside Latin-1 becomes ?.
Use Print… for CJK, Cyrillic, Arabic, and similar — Chrome’s renderer
handles them correctly.ytd-transcript-segment-renderer to transcript-segment-view-model; the
reader handles both and falls back to structural matching, but a future
redesign may still require an update.| Doc | Covers |
|---|---|
| Documentation index | All maintained documentation and audit reports |
| docs/ARCHITECTURE.md | The proof-of-origin constraint, the two strategies, design decisions |
| docs/COMPONENTS.md | Every module, its exports and dependencies |
| docs/DEVELOPMENT.md | Setup, the reload matrix, debugging, troubleshooting |
| docs/INSTALLATION.md | Install, verify, upgrade, uninstall |
| docs/SECURITY.md | Permission justifications and threat model |
| docs/TESTING.md | Automated coverage and the manual test plan |
| docs/known-issues.md | Current limitations and what is not a bug |
| docs/onboarding.md | A first-hour path through the codebase |
| docs/runbooks/ | Fixing a YouTube markup change; cutting a release |
The extension sends no transcripts to a backend or analytics service. Formatting preferences and a temporary print handoff stay in local browser storage. Direct fetches omit cookies; a YouTube tab used for the panel fallback follows that tab’s ordinary session and network behavior. Full policy: PRIVACY.md (published copy).
Issues and pull requests welcome — see CONTRIBUTING.md and the Code of Conduct. Report security issues privately per .github/SECURITY.md.
MIT — see LICENSE.