yt2text 0.1.0
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/LICENSE +21 -0
- package/README.md +334 -0
- package/dist/asr.js +19 -0
- package/dist/cache.js +28 -0
- package/dist/cli.js +384 -0
- package/dist/config.js +185 -0
- package/dist/cookies.js +121 -0
- package/dist/deps.js +82 -0
- package/dist/doctor.js +140 -0
- package/dist/download-file.js +42 -0
- package/dist/errors.js +68 -0
- package/dist/local-asr.js +218 -0
- package/dist/logger.js +32 -0
- package/dist/media.js +224 -0
- package/dist/output.js +62 -0
- package/dist/platform.js +39 -0
- package/dist/process.js +69 -0
- package/dist/progress.js +79 -0
- package/dist/segments.js +36 -0
- package/dist/system-asr.js +152 -0
- package/dist/types.js +1 -0
- package/helpers/macos-speech-info.plist +21 -0
- package/helpers/macos-transcribe.swift +130 -0
- package/helpers/windows-transcribe.ps1 +45 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lopleec
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# yt2text
|
|
2
|
+
|
|
3
|
+
Download audio from YouTube or another `yt-dlp` supported URL, transcribe it locally or through the platform speech interface, and save the result as `txt`, `json`, or `srt`.
|
|
4
|
+
|
|
5
|
+
Windows support is included but currently untested. macOS and Linux are the primary targets.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Downloads audio only through `yt-dlp`.
|
|
10
|
+
- Uses macOS Speech.framework by default on macOS.
|
|
11
|
+
- Uses local Whisper ASR by default on Linux.
|
|
12
|
+
- Includes a best-effort Windows SAPI helper, marked untested.
|
|
13
|
+
- Supports local multilingual ASR with `--multilingual` or `-l auto`.
|
|
14
|
+
- Optional anonymous speaker diarization with `--diarize`.
|
|
15
|
+
- Browser cookie auto-detection, manual cookies files, proxy, retries, timeouts, and fragment concurrency.
|
|
16
|
+
- Progress output for dependency downloads, media download, conversion, and transcription.
|
|
17
|
+
- JSON config file with CLI flags taking precedence.
|
|
18
|
+
- Output filename templates and optional audio retention.
|
|
19
|
+
|
|
20
|
+
## Requirements
|
|
21
|
+
|
|
22
|
+
- Node.js 20 or newer.
|
|
23
|
+
- Network access on first use to download `yt-dlp` and, when local ASR is enabled, ASR models.
|
|
24
|
+
- macOS system ASR requires Speech Recognition permission and Xcode Command Line Tools for `swiftc`; yt2text automatically opens Apple's `xcode-select --install` installer when `swiftc` is missing.
|
|
25
|
+
- Linux local ASR requires the optional `sherpa-onnx-node` package to install correctly.
|
|
26
|
+
- `ffmpeg` is bundled through `@ffmpeg-installer/ffmpeg`; a system `ffmpeg` is used as fallback.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
Recommended:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g yt2text
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
From this repository:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/lopleec/yt2text.git
|
|
40
|
+
cd yt2text
|
|
41
|
+
npm install
|
|
42
|
+
npm run build
|
|
43
|
+
npm link
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Run a local checkout without linking:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm run dev -- "https://www.youtube.com/watch?v=ROrOGwJDneo" -l en-US
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
macOS default: browser cookies are auto-detected, the system Speech interface runs in offline/on-device mode, and the transcript is saved as `txt` in `~/Downloads`.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
yt2text "https://www.youtube.com/watch?v=ROrOGwJDneo" -l en-US
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Chinese video using the macOS default language:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
yt2text "https://www.youtube.com/watch?v=muAgkhaoLDA"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Mixed-language or unknown-language video:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
yt2text "https://youtu.be/..." --multilingual
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Local file:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
yt2text file ./audio.wav -l en-US
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Interactive terminal mode:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
yt2text
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Check dependencies and effective configuration:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
yt2text doctor --fix --show-config
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`--fix` downloads missing small runtime tools such as `yt-dlp`. On macOS it also opens the Xcode Command Line Tools installer if `swiftc` is missing. That Apple installer still requires the user to finish the system dialog, then rerun yt2text.
|
|
91
|
+
|
|
92
|
+
## Platform Defaults
|
|
93
|
+
|
|
94
|
+
| Platform | Default ASR | Notes |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| macOS | `system` | Uses Speech.framework with `--system-mode offline`; browser cookies default to `auto`. |
|
|
97
|
+
| Linux | `local` | No single cross-distro system speech API is assumed; first local ASR run downloads models. |
|
|
98
|
+
| Windows | `system` | Uses a best-effort offline SAPI helper. This path is included but not tested. |
|
|
99
|
+
|
|
100
|
+
Use `--asr local` to force local ASR, or `--fallback local` to try local ASR if system ASR fails.
|
|
101
|
+
|
|
102
|
+
## Common Commands
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
yt2text "https://youtu.be/..." # direct run
|
|
106
|
+
yt2text "https://youtu.be/..." -l en-US # English locale
|
|
107
|
+
yt2text "https://youtu.be/..." --asr local # force local Whisper ASR
|
|
108
|
+
yt2text "https://youtu.be/..." --multilingual # local ASR with language auto-detect
|
|
109
|
+
yt2text "https://youtu.be/..." --diarize # anonymous speaker labels
|
|
110
|
+
yt2text "https://youtu.be/..." -f srt # subtitle output
|
|
111
|
+
yt2text "https://youtu.be/..." --audio-quality small # smaller download
|
|
112
|
+
yt2text "https://youtu.be/..." --parallel-downloads 8 # faster fragmented downloads
|
|
113
|
+
yt2text "https://youtu.be/..." --keep-original-audio --keep-audio
|
|
114
|
+
yt2text file ./meeting.m4a -l zh-CN
|
|
115
|
+
yt2text config
|
|
116
|
+
yt2text config --print
|
|
117
|
+
yt2text doctor
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Important Options
|
|
121
|
+
|
|
122
|
+
ASR:
|
|
123
|
+
|
|
124
|
+
- `--asr system|local`
|
|
125
|
+
- `--system-mode online|offline|auto`
|
|
126
|
+
- `--fallback local|fail`
|
|
127
|
+
- `-l, --language <locale>` such as `zh-CN`, `zh-TW`, `en-US`, `ja-JP`, or `auto`
|
|
128
|
+
- `--multilingual`
|
|
129
|
+
- `--model tiny|base|small|medium`
|
|
130
|
+
- `--diarize`
|
|
131
|
+
- `--chunk-seconds <seconds>`
|
|
132
|
+
|
|
133
|
+
Output:
|
|
134
|
+
|
|
135
|
+
- `-f, --format txt|json|srt`
|
|
136
|
+
- `-o, --output <dir>`
|
|
137
|
+
- `--filename-template <template>`
|
|
138
|
+
- `--keep-original-audio`
|
|
139
|
+
- `--keep-audio`
|
|
140
|
+
- `--log-file <file>`
|
|
141
|
+
|
|
142
|
+
Filename template tokens:
|
|
143
|
+
|
|
144
|
+
- `{title}`
|
|
145
|
+
- `{date}`
|
|
146
|
+
- `{datetime}`
|
|
147
|
+
- `{language}`
|
|
148
|
+
- `{engine}`
|
|
149
|
+
- `{source}`
|
|
150
|
+
|
|
151
|
+
Download and cookies:
|
|
152
|
+
|
|
153
|
+
- `--cookies <file>`
|
|
154
|
+
- `--cookies-from-browser auto|chrome|safari|firefox|edge|brave|chromium`
|
|
155
|
+
- `--no-browser-cookies`
|
|
156
|
+
- `--audio-quality best|balanced|small`
|
|
157
|
+
- `--parallel-downloads <n>`
|
|
158
|
+
- `--concurrent-fragments <n>`
|
|
159
|
+
- `--retries <n>`
|
|
160
|
+
- `--fragment-retries <n>`
|
|
161
|
+
- `--socket-timeout <seconds>`
|
|
162
|
+
- `--download-timeout <seconds>`
|
|
163
|
+
- `--proxy <url>`
|
|
164
|
+
- `--rate-limit <rate>`
|
|
165
|
+
- `--user-agent <ua>`
|
|
166
|
+
- `--update-ytdlp`
|
|
167
|
+
|
|
168
|
+
Runtime:
|
|
169
|
+
|
|
170
|
+
- `--config <file>`
|
|
171
|
+
- `--cache-dir <dir>`
|
|
172
|
+
- `--offline`
|
|
173
|
+
- `--convert-timeout <seconds>`
|
|
174
|
+
- `--asr-chunk-timeout <seconds>`
|
|
175
|
+
- `-v, --verbose`
|
|
176
|
+
|
|
177
|
+
## Configuration
|
|
178
|
+
|
|
179
|
+
Create a config file:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
yt2text config
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Default paths:
|
|
186
|
+
|
|
187
|
+
- macOS/Linux: `~/.config/yt2text/config.json`
|
|
188
|
+
- Windows: `%APPDATA%\yt2text\config.json`
|
|
189
|
+
|
|
190
|
+
Print the platform-specific default config without writing:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
yt2text config --print
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"asr": "system",
|
|
201
|
+
"systemMode": "offline",
|
|
202
|
+
"fallback": "fail",
|
|
203
|
+
"language": "zh-CN",
|
|
204
|
+
"outputDir": "~/Downloads",
|
|
205
|
+
"format": "txt",
|
|
206
|
+
"cookiesFromBrowser": "auto",
|
|
207
|
+
"browserCookies": true,
|
|
208
|
+
"model": "small",
|
|
209
|
+
"offline": false,
|
|
210
|
+
"diarize": false,
|
|
211
|
+
"chunkSeconds": 55,
|
|
212
|
+
"filenameTemplate": "{date}-{title}",
|
|
213
|
+
"audioQuality": "balanced",
|
|
214
|
+
"keepAudio": false,
|
|
215
|
+
"keepOriginalAudio": false,
|
|
216
|
+
"parallelDownloads": 8,
|
|
217
|
+
"concurrentFragments": 8,
|
|
218
|
+
"retries": 20,
|
|
219
|
+
"fragmentRetries": 20,
|
|
220
|
+
"socketTimeout": 30,
|
|
221
|
+
"downloadTimeoutSeconds": 1800,
|
|
222
|
+
"convertTimeoutSeconds": 600,
|
|
223
|
+
"asrChunkTimeoutSeconds": 180,
|
|
224
|
+
"logFile": "~/Downloads/yt2text.log"
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
CLI flags override config values. Environment variables:
|
|
229
|
+
|
|
230
|
+
- `YT2TEXT_CONFIG`
|
|
231
|
+
- `YT2TEXT_OUTPUT_DIR`
|
|
232
|
+
- `YT2TEXT_LANGUAGE`
|
|
233
|
+
- `YT2TEXT_CACHE_DIR`
|
|
234
|
+
|
|
235
|
+
## Cookies
|
|
236
|
+
|
|
237
|
+
For many YouTube videos, cookies are not needed. When YouTube asks for sign-in or bot verification, use browser cookies:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
yt2text "https://youtu.be/..." --cookies-from-browser chrome
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
macOS defaults to `--cookies-from-browser auto`. You can disable this:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
yt2text "https://youtu.be/..." --no-browser-cookies
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Manual Netscape cookies file:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
yt2text "https://youtu.be/..." --cookies ./cookies.txt
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Local ASR Models
|
|
256
|
+
|
|
257
|
+
Local ASR uses `sherpa-onnx-node` and Whisper-style models from the sherpa-onnx project. First use downloads model files into the cache directory. Larger models usually improve accuracy but cost more disk, memory, and CPU time.
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
yt2text "https://youtu.be/..." --asr local --model tiny
|
|
261
|
+
yt2text "https://youtu.be/..." --asr local --model small
|
|
262
|
+
yt2text "https://youtu.be/..." --multilingual --model medium
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Use `--offline` only after dependencies and models are already cached.
|
|
266
|
+
|
|
267
|
+
## Speaker Labels
|
|
268
|
+
|
|
269
|
+
`--diarize` adds anonymous labels such as `Speaker 1` and `Speaker 2`. It does not identify real people by name. Diarization downloads extra local models on first use.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
yt2text "https://youtu.be/..." --asr local --diarize
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
Run:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
yt2text doctor --fix --show-config
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Common fixes:
|
|
284
|
+
|
|
285
|
+
- `npm` says `package.json` is missing: run commands from the project directory, or install/link the CLI globally first.
|
|
286
|
+
- YouTube requires sign-in: try `--cookies-from-browser chrome`, `safari`, or `firefox`.
|
|
287
|
+
- Browser cookie database is locked: fully quit the browser and retry.
|
|
288
|
+
- 403 or bot verification: use cookies, then try `--update-ytdlp`; if needed, configure `--proxy`.
|
|
289
|
+
- macOS Speech permission denied: allow Speech Recognition for the helper when macOS prompts.
|
|
290
|
+
- `swiftc` is missing: yt2text automatically opens the Xcode Command Line Tools installer on macOS. Finish the system installer and rerun the command; if it did not open, run `xcode-select --install`.
|
|
291
|
+
- Local ASR dependency missing: reinstall without `--no-optional`.
|
|
292
|
+
- Local model extraction fails: make sure `tar` with bzip2 support is available.
|
|
293
|
+
- Long files time out: increase `--download-timeout`, `--convert-timeout`, or `--asr-chunk-timeout`.
|
|
294
|
+
|
|
295
|
+
## Development
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npm install
|
|
299
|
+
npm run check
|
|
300
|
+
npm run build
|
|
301
|
+
npm test
|
|
302
|
+
npm pack --dry-run
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Useful local commands:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
node dist/cli.js doctor --show-config
|
|
309
|
+
node dist/cli.js "https://www.youtube.com/watch?v=ROrOGwJDneo" -l en-US
|
|
310
|
+
node dist/cli.js file ./audio.wav -l en-US -o ./out
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Privacy
|
|
314
|
+
|
|
315
|
+
The CLI stores downloaded tools, ASR models, temporary audio, and optional logs on your machine. Temporary work files are removed after each run. If you pass browser cookies or a manual cookies file, `yt-dlp` reads them locally to access the requested media. Be careful when sharing logs, configs, transcripts, and retained audio files.
|
|
316
|
+
|
|
317
|
+
## Acknowledgements
|
|
318
|
+
|
|
319
|
+
- [`yt-dlp`](https://github.com/yt-dlp/yt-dlp) for media extraction and download support.
|
|
320
|
+
- [`FFmpeg`](https://ffmpeg.org/) and `@ffmpeg-installer/ffmpeg` for audio conversion.
|
|
321
|
+
- [`sherpa-onnx`](https://github.com/k2-fsa/sherpa-onnx) and `sherpa-onnx-node` for local ASR and diarization runtime support.
|
|
322
|
+
- OpenAI Whisper model architecture and the broader open speech recognition ecosystem.
|
|
323
|
+
- Apple Speech.framework and Windows SAPI for platform speech interfaces.
|
|
324
|
+
- [`NotKiwy/trawl`](https://github.com/NotKiwy/trawl) for informing the initial downloader research direction. This project currently shells out to `yt-dlp` directly.
|
|
325
|
+
|
|
326
|
+
## Disclaimer
|
|
327
|
+
|
|
328
|
+
This project is not affiliated with YouTube, Google, Apple, Microsoft, FFmpeg, yt-dlp, sherpa-onnx, OpenAI, or NotKiwy/trawl.
|
|
329
|
+
|
|
330
|
+
Use this tool only for media you are allowed to access, download, and transcribe. You are responsible for complying with copyright law, platform terms of service, privacy rules, consent requirements, and any laws that apply in your jurisdiction. The software is provided as-is under the MIT License, without warranty.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT.
|
package/dist/asr.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { transcribeWithLocal } from "./local-asr.js";
|
|
2
|
+
import { transcribeWithSystem } from "./system-asr.js";
|
|
3
|
+
import { asError } from "./errors.js";
|
|
4
|
+
export async function transcribe(wavPath, paths, options) {
|
|
5
|
+
const provider = options.asr;
|
|
6
|
+
if (provider === "local") {
|
|
7
|
+
return transcribeWithLocal(wavPath, paths, options);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
return await transcribeWithSystem(wavPath, paths, options);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (options.fallback !== "local")
|
|
14
|
+
throw error;
|
|
15
|
+
console.error(`System ASR unavailable: ${asError(error).message}`);
|
|
16
|
+
console.error("Falling back to local ASR.");
|
|
17
|
+
return transcribeWithLocal(wavPath, paths, options);
|
|
18
|
+
}
|
|
19
|
+
}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { homedir, tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
function defaultCacheDir() {
|
|
6
|
+
if (process.env.YT2TEXT_CACHE_DIR)
|
|
7
|
+
return process.env.YT2TEXT_CACHE_DIR;
|
|
8
|
+
if (process.platform === "darwin") {
|
|
9
|
+
return join(homedir(), "Library", "Caches", "yt2text");
|
|
10
|
+
}
|
|
11
|
+
if (process.platform === "win32") {
|
|
12
|
+
return join(process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"), "yt2text");
|
|
13
|
+
}
|
|
14
|
+
return join(process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"), "yt2text");
|
|
15
|
+
}
|
|
16
|
+
export async function createRuntimePaths(options) {
|
|
17
|
+
const cacheDir = options.cacheDir ?? defaultCacheDir();
|
|
18
|
+
const toolsDir = join(cacheDir, "tools");
|
|
19
|
+
const modelsDir = join(cacheDir, "models");
|
|
20
|
+
const workDir = join(tmpdir(), `yt2text-${randomUUID()}`);
|
|
21
|
+
await Promise.all([
|
|
22
|
+
mkdir(toolsDir, { recursive: true }),
|
|
23
|
+
mkdir(modelsDir, { recursive: true }),
|
|
24
|
+
mkdir(workDir, { recursive: true }),
|
|
25
|
+
mkdir(options.outputDir, { recursive: true }),
|
|
26
|
+
]);
|
|
27
|
+
return { cacheDir, toolsDir, modelsDir, workDir };
|
|
28
|
+
}
|