veodl 1.8.1
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/AGENTS.md +11 -0
- package/CHANGELOG.md +264 -0
- package/LICENSE +21 -0
- package/README.md +725 -0
- package/bin/veo.js +4 -0
- package/docs/AGENT_GUIDE.md +66 -0
- package/package.json +59 -0
- package/src/backend-update.js +191 -0
- package/src/backend.js +520 -0
- package/src/cli.js +454 -0
- package/src/compatibility.js +39 -0
- package/src/config-clipboard.js +67 -0
- package/src/config-diagnostics.js +144 -0
- package/src/config-editor.js +281 -0
- package/src/config-errors.js +64 -0
- package/src/config-reset.js +39 -0
- package/src/config-template.js +171 -0
- package/src/config.js +213 -0
- package/src/disk-space.js +68 -0
- package/src/doctor.js +302 -0
- package/src/download-cache.js +31 -0
- package/src/downloader.js +600 -0
- package/src/execution.js +68 -0
- package/src/flush.js +66 -0
- package/src/history.js +173 -0
- package/src/inspect-media.js +168 -0
- package/src/interactive.js +82 -0
- package/src/jobs.js +172 -0
- package/src/legacy-config-comments.js +106 -0
- package/src/naming.js +50 -0
- package/src/open-file.js +15 -0
- package/src/output.js +61 -0
- package/src/paths.js +26 -0
- package/src/playlist.js +31 -0
- package/src/progress.js +152 -0
- package/src/run-archive.js +148 -0
- package/src/runs.js +333 -0
- package/src/state.js +27 -0
- package/src/stats.js +63 -0
- package/src/terminal-title.js +19 -0
- package/src/tool-setup.js +151 -0
- package/src/updater.js +227 -0
- package/src/utils.js +208 -0
- package/src/version.js +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
# veo
|
|
2
|
+
|
|
3
|
+
A small, clean video-downloading CLI powered by [yt-dlp](https://github.com/yt-dlp/yt-dlp). No banners, just progress and the saved file path.
|
|
4
|
+
|
|
5
|
+
Automating with an agent? Start with the [agent guide](docs/AGENT_GUIDE.md) for JSON output, run inspection and audio verification.
|
|
6
|
+
|
|
7
|
+
**Only download content you own or are authorized or legally permitted to download.** Respect copyright, website terms, and access restrictions. veo does not bypass DRM, private-content access controls, or paywalls.
|
|
8
|
+
|
|
9
|
+
## Install and run
|
|
10
|
+
|
|
11
|
+
Requires **Node.js 22+** and npm. Internet access is needed for installation and first-use backend setup.
|
|
12
|
+
|
|
13
|
+
The npm package is `veodl`; the installed commands are `veo`, `veod`, and `veodl`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx veodl "https://example.com/video.mp4"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install globally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g veodl
|
|
23
|
+
veo "https://example.com/video.mp4"
|
|
24
|
+
# or: veod / veodl "https://example.com/video.mp4"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The first download automatically prepares missing yt-dlp, FFmpeg and FFprobe.
|
|
28
|
+
On Android/Termux this includes installing the native packages and yt-dlp's
|
|
29
|
+
JavaScript support with `pkg install -y`. On desktop systems, missing media tools
|
|
30
|
+
are downloaded into veo's own cache. Run `veo doctor fix` to prepare everything
|
|
31
|
+
before downloading. Plain `veo doctor`, help and version do not install tools.
|
|
32
|
+
|
|
33
|
+
From this checkout, without publishing:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install
|
|
37
|
+
node bin/veo.js --help
|
|
38
|
+
npm link
|
|
39
|
+
veo "https://example.com/video.mp4"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`package.json` maps `"veo": "./bin/veo.js"`; npm creates the executable/shim automatically. No custom command prefix is needed. Use only trusted URLs, and quote URLs to protect query strings from your shell.
|
|
43
|
+
|
|
44
|
+
If something does not work, run `veo doctor` first: it inspects the whole local setup and prints one line per check.
|
|
45
|
+
|
|
46
|
+
Run `veo` without arguments in an interactive terminal for a guided download: choose a
|
|
47
|
+
profile, enter a link, select video or audio, choose available resolution and output
|
|
48
|
+
directory, and start. The wizard checks the link and asks about playlist mode only when
|
|
49
|
+
it finds a collection; configured playlist and entry selection values are preselected.
|
|
50
|
+
Playlist mode also lists entries for selection. The wizard enables
|
|
51
|
+
resume and offers skipping previously downloaded files. With redirected input, use the
|
|
52
|
+
regular command arguments; veo never starts a prompt in a script.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
veo <url> [<url>...] [options]
|
|
58
|
+
|
|
59
|
+
Options:
|
|
60
|
+
-q, --quality <quality> best, 2160p, 1440p, 1080p, 720p, 480p, 360p
|
|
61
|
+
-o, --output <path> Output directory (default: current working directory)
|
|
62
|
+
-r, --rename <name> Filename without extension; * inserts the original title
|
|
63
|
+
--closest-quality Nearest available resolution instead of an upper bound
|
|
64
|
+
--open Open the saved file with your default app
|
|
65
|
+
--audio Audio only (MP3 by default)
|
|
66
|
+
--format <format> Video: mp4, mkv, webm, mov
|
|
67
|
+
Audio: mp3, m4a, aac, opus, flac, wav
|
|
68
|
+
--recode Explicit video conversion (requires --format)
|
|
69
|
+
--playlist-concurrency <n> Parallel playlist entries, 1-4 (default: 2)
|
|
70
|
+
--playlist Download every entry of a playlist or channel URL
|
|
71
|
+
-N, --concurrent-fragments <n> Parallel fragments, 1-16 (default: 8)
|
|
72
|
+
--subs Download subtitles (default languages: en)
|
|
73
|
+
--sub-langs <langs> Subtitle languages, e.g. "de,en" (implies --subs)
|
|
74
|
+
--embed-subs Embed subtitles into the video file
|
|
75
|
+
--embed-metadata Embed title, date and other metadata
|
|
76
|
+
--embed-thumbnail Embed the thumbnail
|
|
77
|
+
--sponsorblock-remove <categories> e.g. "sponsor,selfpromo"
|
|
78
|
+
--section <range> Only a time range, e.g. "*10:00-12:00"
|
|
79
|
+
--cookies <file> Netscape cookie file, for content you may access
|
|
80
|
+
--cookies-from-browser <browser[:profile]>
|
|
81
|
+
--resume Keep partial data and continue an interrupted download
|
|
82
|
+
--list-formats Show available formats and exit
|
|
83
|
+
--dry-run Show what would be downloaded and exit
|
|
84
|
+
--json One JSON object per URL instead of prose
|
|
85
|
+
--profile <name> Use a named profile from the config file
|
|
86
|
+
--batch-file <file> Read one URL per line (blank lines and # comments ignored)
|
|
87
|
+
--retry-failed <file> Retry failed/unfinished downloads from a saved job
|
|
88
|
+
--playlist-items <list> Select entries, e.g. 1,3-5 (implies --playlist)
|
|
89
|
+
--skip-existing Skip matching downloads that are still on disk
|
|
90
|
+
--no-<boolean-option> Disable a default, e.g. --no-open or --no-audio
|
|
91
|
+
-v, --version Show installed version (also: veo version)
|
|
92
|
+
-h, --help Show help
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
veo "https://youtube.com/watch?v=VIDEO_ID" -q 1080p
|
|
97
|
+
veo "https://x.com/USER/status/STATUS_ID" -q best
|
|
98
|
+
veo "https://example.com/video.mp4" --audio
|
|
99
|
+
veo "https://example.com/video.mp4" --audio --format flac
|
|
100
|
+
veo "https://example.com/video.mp4" --format webm --recode -o ./videos
|
|
101
|
+
veo "https://example.com/video.mp4" -r "My Video" --format mp4 --open
|
|
102
|
+
veo "https://example.com/video.mp4" --audio -r "My Music"
|
|
103
|
+
veo <url1> <url2> <url3> --embed-metadata --subs
|
|
104
|
+
veo "https://youtube.com/playlist?list=LIST_ID" --playlist -q 720p
|
|
105
|
+
veo "https://example.com/video.mp4" --resume
|
|
106
|
+
npx veodl "https://example.com/video.mp4" --output ./downloads
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Several URLs and playlists
|
|
110
|
+
|
|
111
|
+
Playlist downloads use two concurrent entries by default; use `--playlist-concurrency 1` for sequential downloads. Each entry uses up to eight concurrent DASH/HLS fragments by default (`-N 1` disables fragment parallelism). More connections help only when the source and connection have spare capacity. Video format changes preserve the encoded streams unless `--recode` is explicitly enabled; audio extraction and precise section cuts retain their existing conversion behavior.
|
|
112
|
+
|
|
113
|
+
- Pass any number of URLs; up to two run concurrently by default. Use
|
|
114
|
+
`--concurrent-downloads 1` for sequential processing. A failure is reported
|
|
115
|
+
with its URL and does not stop the remaining URLs; the exit status is `1` if anything
|
|
116
|
+
failed.
|
|
117
|
+
- Playlists, channels and other collections are refused by default with a hint. Add
|
|
118
|
+
`--playlist` to download every entry. Each entry keeps its own title; `--rename` applies
|
|
119
|
+
a name prefix per entry (`My Name - 001`), or substitutes each title with `movie_*`.
|
|
120
|
+
- Use `--playlist-items 1,3-5` to select entries by their original, one-based index.
|
|
121
|
+
Playlist entries run with bounded parallelism: a failed entry does not discard successful
|
|
122
|
+
files or stop the remaining entries. Before downloading, veo displays the selected count
|
|
123
|
+
and a size estimate when the source supplies sizes. Unknown sizes are labeled explicitly;
|
|
124
|
+
metadata estimates do not predict conversion size.
|
|
125
|
+
- `--batch-file links.txt` accepts a UTF-8 URL list, optionally alongside URLs on the command
|
|
126
|
+
line. A final summary counts saved, skipped and failed videos. Successful files are opened
|
|
127
|
+
with `--open` even if another URL or playlist entry fails.
|
|
128
|
+
- Failed or cancelled jobs print a ready-to-use `veo --retry-failed "<job.json>"` command.
|
|
129
|
+
- Use `veo retry --last` to retry the newest failed or unfinished job without copying its path. Active runs are ignored; use `veo runs` to inspect them.
|
|
130
|
+
Jobs are stored under the per-user veo cache's `jobs` directory. Retries retain resolved
|
|
131
|
+
output directories and settings, even from a different working directory, and explicit
|
|
132
|
+
flags can override them. Completed playlist jobs retry only failed indices; interrupted
|
|
133
|
+
playlists revisit their selection and skip completed entries through resume history.
|
|
134
|
+
Playlist indices refer to the playlist's current order, so changes to that order can
|
|
135
|
+
change what an index selects. Cookie paths and browser-session settings are not saved in
|
|
136
|
+
retry jobs; pass those flags again if needed. Job files contain URLs, settings and local
|
|
137
|
+
file paths and can be deleted when no longer needed.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
veo --batch-file links.txt --profile archive
|
|
141
|
+
veo "https://example.com/playlist" --playlist-items 1,3-5 --resume
|
|
142
|
+
veo "https://example.com/playlist" --playlist --skip-existing
|
|
143
|
+
veo --retry-failed "C:\path\to\job.json"
|
|
144
|
+
veo retry --last
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Quality
|
|
148
|
+
|
|
149
|
+
- Numeric qualities are an **upper bound**: `-q 720p` downloads the best resolution at or
|
|
150
|
+
below 720p, so a metered connection can never accidentally receive a 2160p file. If a
|
|
151
|
+
source offers nothing at or below the request, veo stops before downloading and lists
|
|
152
|
+
the resolutions that do exist.
|
|
153
|
+
- `--closest-quality` restores the former "nearest available resolution" rule, which may
|
|
154
|
+
pick a resolution *above* the request. It needs a numeric `--quality`.
|
|
155
|
+
- Sources that report no resolution metadata fall back to the best available stream.
|
|
156
|
+
Collections resolve and enforce the quality limit separately for each entry.
|
|
157
|
+
- Other positive numeric resolutions, such as `-q 540p`, are also accepted. The interactive
|
|
158
|
+
wizard offers the resolutions reported by the source; without resolution metadata it
|
|
159
|
+
offers `best` rather than inventing available streams.
|
|
160
|
+
- `--quality` does not apply to audio.
|
|
161
|
+
- MP4-compatible codecs are preferred at the selected resolution; merged video prefers MP4
|
|
162
|
+
with MKV fallback. A single-file source may retain its original container. Use
|
|
163
|
+
`--format mp4` to require MP4 through lossless remuxing. Incompatible codecs cause an error.
|
|
164
|
+
- `--format` on video remuxes without re-encoding. Add `--recode` to explicitly allow conversion, which may be slow or lossy. Audio formats require `--audio`.
|
|
165
|
+
|
|
166
|
+
### Playback compatibility
|
|
167
|
+
|
|
168
|
+
H.264/AAC sources are preferred at the selected quality, including explicit MP4 output.
|
|
169
|
+
Original codecs are retained by default; a playback note identifies media needing extra
|
|
170
|
+
player codecs. MP4 is a container, not a promise of a particular codec.
|
|
171
|
+
|
|
172
|
+
Use `veo URL --compatible` to ensure MP4 with H.264 8-bit 4:2:0 and AAC.
|
|
173
|
+
Already compatible streams are copied; only incompatible streams are encoded (H.264 CRF 18,
|
|
174
|
+
AAC 192 kbit/s), which takes time and may lose quality. HDR requiring video conversion
|
|
175
|
+
is refused rather than silently losing correct colors. Embedded subtitles/thumbnails are
|
|
176
|
+
not supported in this mode; separate subtitle files remain supported.
|
|
177
|
+
New configs include a `kompatibel` profile; existing configs receive a commented profile
|
|
178
|
+
example through `veo config edit`. Enable it with `veo URL --profile kompatibel`.
|
|
179
|
+
|
|
180
|
+
### Download controls and diagnostics
|
|
181
|
+
|
|
182
|
+
- URL lists and batch files use `--concurrent-downloads 2` by default (range 1–4).
|
|
183
|
+
Each playlist independently uses `--playlist-concurrency 2` (range 1–4).
|
|
184
|
+
The limits multiply when several playlists run at once. JSON emits one complete
|
|
185
|
+
object per URL in completion order; the `url` field identifies each result.
|
|
186
|
+
- Adaptive concurrency is on by default. After the backend's own retries are exhausted,
|
|
187
|
+
HTTP 429/5xx and temporary connection failures trigger at most two further attempts,
|
|
188
|
+
after 2 and 4 seconds. Fragment parallelism and future URL/playlist workers are reduced
|
|
189
|
+
for the rest of the run. Active downloads finish normally; format and quality stay fixed.
|
|
190
|
+
Partials are continued on retry. Use `--no-adaptive-concurrency` to disable this.
|
|
191
|
+
- `--check-space` checks cache and destination free space before media transfer. Known
|
|
192
|
+
sizes include conservative room for merge output and a destination copy, plus space
|
|
193
|
+
reserved by concurrent downloads in the same process. Unknown sizes are reported as
|
|
194
|
+
unknown; conversion sizes and other processes' disk usage cannot be predicted exactly.
|
|
195
|
+
Use `--no-check-space` to disable this conservative check.
|
|
196
|
+
- `--timings` shows elapsed time for setup, metadata, download/backend, processing,
|
|
197
|
+
saving and retry waits. Download/backend includes the backend's own startup and
|
|
198
|
+
extraction overhead; these are elapsed timings, not a diagnosis of network speed.
|
|
199
|
+
JSON results include `timings` in milliseconds, or `entryTimings` for playlists.
|
|
200
|
+
Use `--no-timings` to hide/omit them.
|
|
201
|
+
- Terminal details are gray, titles bold, saves green and errors red. The same styling
|
|
202
|
+
applies to stats, history, runs/stop, doctor, flush, updates, config prose, help and
|
|
203
|
+
interactive prompts. For example, `veo stats --no-color` disables it for one command. Redirected output
|
|
204
|
+
and JSON have no color escapes. `--no-color`, `"color": false`, `NO_COLOR`,
|
|
205
|
+
and `TERM=dumb` disable colors.
|
|
206
|
+
This can be set independently per profile, for example
|
|
207
|
+
`"profiles": { "default": { "color": true }, "plain": { "color": false } }`.
|
|
208
|
+
Use `veo stats --profile plain` or `veo URL --profile plain`; `--color` or
|
|
209
|
+
`--no-color` overrides the profile for one invocation (except `NO_COLOR`).
|
|
210
|
+
External npm/editor output and Node runtime warnings keep their own formatting.
|
|
211
|
+
|
|
212
|
+
### Filename templates and folders
|
|
213
|
+
|
|
214
|
+
`--filename-template` controls the name without extension; `--folder-template`
|
|
215
|
+
creates relative subfolders under `--output`. Both support `{title}`, `{id}`,
|
|
216
|
+
`{channel}`, `{year}`, `{playlist}` and `{index}` (three-digit playlist index).
|
|
217
|
+
Missing metadata uses readable fallbacks. `{year}` is the upload year when available.
|
|
218
|
+
Subtitles follow the media name. Existing files are never overwritten. Templates cannot
|
|
219
|
+
escape the output folder, and symlink/junction subfolders are refused. Use forward slashes
|
|
220
|
+
for folders. `--rename` and `--filename-template` are mutually exclusive.
|
|
221
|
+
|
|
222
|
+
```powershell
|
|
223
|
+
veo "VIDEO-URL" --folder-template "{channel}/{year}" --filename-template "{title} - {id}"
|
|
224
|
+
veo "PLAYLIST-URL" --playlist --folder-template "{playlist}" --filename-template "{index} - {title}"
|
|
225
|
+
veo config check
|
|
226
|
+
veo config check --profile fast
|
|
227
|
+
veo config show --profile fast
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Config checks validate every effective profile (or the selected one), including unknown
|
|
231
|
+
settings, ranges and conflicting options. Show prints merged built-in, global and profile
|
|
232
|
+
settings as JSON, with credential paths/browser profiles redacted. Neither command contacts
|
|
233
|
+
video sites. All new options are also documented as commented examples in `veo config edit`.
|
|
234
|
+
|
|
235
|
+
### Files, names and resume
|
|
236
|
+
|
|
237
|
+
- Original titles are preserved by default. Use `-r "My Video"` or `--rename "My Video"`
|
|
238
|
+
for video or audio. Supply the name **without an extension**; the actual media extension
|
|
239
|
+
is appended automatically. Use `-o` for the directory. Invalid filename characters are
|
|
240
|
+
sanitized and overly long names shortened. Existing files are never intentionally
|
|
241
|
+
overwritten: duplicates get ` (1)`, ` (2)`, etc.
|
|
242
|
+
- Use `-r "movie_*"` or `"rename": "movie_*"` in your config/profile to insert the
|
|
243
|
+
original title: `My Film` becomes `movie_My Film.mp4`. Every `*` is substituted.
|
|
244
|
+
Quote the pattern in your shell. Patterns also work with URL batches and playlists.
|
|
245
|
+
- Subtitles and thumbnails are saved beside the media file under the same base name
|
|
246
|
+
(`My Video.mp4` → `My Video.en.vtt`).
|
|
247
|
+
- Downloads and media processing first finish in the local per-user veo cache, under
|
|
248
|
+
`downloads` (`%LOCALAPPDATA%\veo\downloads` on Windows). The destination is only written
|
|
249
|
+
after the media is ready. Saving uses a hard link when supported or an exclusive copy
|
|
250
|
+
across drives/cloud mounts; copied file sizes are checked. Existing files are never
|
|
251
|
+
overwritten. The local original is removed only after files and history are saved.
|
|
252
|
+
Local disk space is therefore needed for the complete download and processing files.
|
|
253
|
+
- If saving fails or is cancelled after media processing completes, the local original and
|
|
254
|
+
sidecars are kept for **15 minutes**, even without `--resume`. Repeat the same command or
|
|
255
|
+
use the printed `--retry-failed` command to retry the transfer. Individual completed
|
|
256
|
+
downloads can be recovered without contacting their source again. The same source,
|
|
257
|
+
media settings and destination identify a cached transfer; changing those starts a new
|
|
258
|
+
download. Expired copies are removed on the next veo invocation, not by a background
|
|
259
|
+
timer while veo is closed. Active transfers are never expired. Another failed transfer
|
|
260
|
+
starts a fresh 15-minute retention period.
|
|
261
|
+
- `--resume` also keeps unfinished downloads in local `.veo-part-<request-hash>` folders
|
|
262
|
+
until resumed; unfinished data has no automatic expiry. Without `--resume`, unfinished
|
|
263
|
+
downloads are discarded on failure or cancellation. The hash separates source URLs,
|
|
264
|
+
playlist entries, destinations, quality, media type, format, sections and subtitle/metadata
|
|
265
|
+
options. A manifest records backend-confirmed
|
|
266
|
+
completion and saved files, so an unprocessed file is not mistaken for a finished video.
|
|
267
|
+
Each playlist entry has its own state. Completed entries survive later failures and are
|
|
268
|
+
skipped when resuming the same selection. Partial downloads are never resumed without
|
|
269
|
+
`--resume`. Legacy partial folders in the output directory are left untouched; the new
|
|
270
|
+
local cache does not automatically migrate them.
|
|
271
|
+
- A lock prevents two resume processes from using the same partial folder. Normal failures
|
|
272
|
+
and Ctrl+C release it. After a force kill, remove the named `.lock` file only after making
|
|
273
|
+
sure no veo process still uses that folder.
|
|
274
|
+
- Successful downloads record their source, output settings and saved paths in
|
|
275
|
+
`.veo-history` inside the output directory. `--skip-existing` uses these records and checks
|
|
276
|
+
that the files still exist. A different quality/format or deleted output is downloaded
|
|
277
|
+
again. Files downloaded before this history existed are not recognized automatically.
|
|
278
|
+
Deleting history removes duplicate detection, not downloaded media. Records whose files
|
|
279
|
+
no longer exist are removed on the next download into that folder, and `.veo-history`
|
|
280
|
+
itself is removed with its last record; `veo doctor` reports stale records.
|
|
281
|
+
|
|
282
|
+
### Metadata and subtitles
|
|
283
|
+
|
|
284
|
+
- `--subs` writes subtitle files, `--sub-langs de,en` selects languages, and `--embed-subs`
|
|
285
|
+
embeds them into the video container instead.
|
|
286
|
+
- `--embed-metadata` and `--embed-thumbnail` use FFmpeg to write metadata and cover art.
|
|
287
|
+
- `--sponsorblock-remove sponsor,selfpromo` cuts sponsor segments (YouTube) and requires
|
|
288
|
+
FFmpeg; the cut re-encodes the affected parts.
|
|
289
|
+
- `--section "*10:00-12:00"` downloads only a time range.
|
|
290
|
+
- `-N 8` downloads several fragments in parallel, which is noticeably faster on HLS/DASH
|
|
291
|
+
sources and heavier on the network.
|
|
292
|
+
|
|
293
|
+
### Access and credentials
|
|
294
|
+
|
|
295
|
+
veo downloads publicly accessible content. It does not bypass access controls, and it does
|
|
296
|
+
not expose options that defeat them. If you need your own session for content you are
|
|
297
|
+
authorized to view:
|
|
298
|
+
|
|
299
|
+
- `--cookies ./cookies.txt` passes a Netscape-format cookie file. The file is validated
|
|
300
|
+
before any network work, and on Unix a file readable by other users produces a warning.
|
|
301
|
+
- `--cookies-from-browser firefox:Work` reads cookies from an installed browser: `brave`,
|
|
302
|
+
`chrome`, `chromium`, `edge`, `firefox`, `opera`, `safari`, `vivaldi`, `whale`, optionally
|
|
303
|
+
with `+gnomekeyring`/`+kwallet`/`+basic` and a profile or container.
|
|
304
|
+
|
|
305
|
+
Credentials are used for that single invocation only and are never stored by veo. When a
|
|
306
|
+
download fails because a login is required, the error message points at these flags.
|
|
307
|
+
|
|
308
|
+
### Inspection and scripting
|
|
309
|
+
|
|
310
|
+
- `--list-formats` prints the backend's own format table for one URL and exits.
|
|
311
|
+
- `--dry-run` prints the title, the resolved quality and the destination path that would be
|
|
312
|
+
used — without creating the output directory or downloading anything.
|
|
313
|
+
- `--json` prints one JSON object per line on stdout:
|
|
314
|
+
`{"url":…,"status":"saved","title":…,"files":[…]}` or
|
|
315
|
+
`{"url":…,"status":"failed","error":…}`. Progress and status still go to stderr. Without
|
|
316
|
+
`--json`, every saved file is printed as `Saved: <path>` on stdout.
|
|
317
|
+
Duplicate detection can return `status: "skipped"`; cancellation returns `"cancelled"`.
|
|
318
|
+
Playlist results also include `saved`, `skipped` and `failures` (with original indices).
|
|
319
|
+
A failed or cancelled playlist can still report files saved before the failure.
|
|
320
|
+
- `veo runs [id] --json` reports active run metadata and per-item progress. Finished attempts
|
|
321
|
+
remain available through `veo history --json`.
|
|
322
|
+
- `veo inspect <file> --json` reports local container and stream metadata. Add
|
|
323
|
+
`--check-audio` to decode all audio tracks and measure whether any peak exceeds
|
|
324
|
+
-60 dBFS. A track can exist without a detectable signal. The check reads the full
|
|
325
|
+
media file and uses only local FFmpeg/FFprobe tools.
|
|
326
|
+
- Download results and history retain a `runId`. `veo inspect run <id> --json` reads
|
|
327
|
+
a finished run and checks every saved file before probing it. Renamed media is
|
|
328
|
+
found by file identity, size and a sampled SHA-256 fingerprint within the original output tree;
|
|
329
|
+
add `--search <directory>` after moving it elsewhere. Ambiguous or missing files
|
|
330
|
+
are reported without probing another file. Old runs lack this persistent record.
|
|
331
|
+
Finished run records live in the current device's veo cache. On Windows, macOS,
|
|
332
|
+
Linux and Termux, `inspect` uses bundled media tools where supported and otherwise
|
|
333
|
+
uses local `ffmpeg` and `ffprobe` (or `VEO_FFMPEG_PATH`); it does not install them.
|
|
334
|
+
Copying only the media to another device does not copy its run ID record.
|
|
335
|
+
|
|
336
|
+
### Config file
|
|
337
|
+
|
|
338
|
+
Defaults can be stored in a config file, so a long list of flags is not needed for every
|
|
339
|
+
call. `veo doctor` prints the exact path; `VEO_CONFIG` overrides it.
|
|
340
|
+
The file accepts JSON with `//` line comments and `/* ... */` block comments. Strings
|
|
341
|
+
(including URLs and Windows paths) keep their normal JSON escaping rules. Trailing commas
|
|
342
|
+
are not allowed.
|
|
343
|
+
|
|
344
|
+
- Windows: `%APPDATA%\veo\config.json`
|
|
345
|
+
- macOS: `~/Library/Application Support/veo/config.json`
|
|
346
|
+
- Linux: `$XDG_CONFIG_HOME/veo/config.json` (or `~/.config/veo/config.json`)
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"output": "D:\\Videos",
|
|
351
|
+
"quality": "1080p",
|
|
352
|
+
"embedMetadata": true,
|
|
353
|
+
"subLangs": "de,en",
|
|
354
|
+
"concurrentFragments": 4
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Supported keys: `output`, `quality`, `format`, `rename`, `audio`, `open`, `resume`,
|
|
359
|
+
`closestQuality`, `cookies`, `cookiesFromBrowser`, `playlist`, `concurrentFragments`, `playlistConcurrency`, `recode`, `compatible`, `concurrentDownloads`, `adaptiveConcurrency`,
|
|
360
|
+
`filenameTemplate`, `folderTemplate`, `checkSpace`, `timings`, `color`,
|
|
361
|
+
`subs`, `subLangs`, `embedSubs`, `embedMetadata`, `embedThumbnail`, `sponsorblockRemove`,
|
|
362
|
+
`section`, `json`. An explicit command-line flag always wins over a stored default. An
|
|
363
|
+
unknown key produces a warning; invalid JSON or a wrong value type is an error, because
|
|
364
|
+
silently ignoring a typo would be worse.
|
|
365
|
+
|
|
366
|
+
Additional defaults are `skipExisting` and `playlistItems`. Boolean defaults can be disabled
|
|
367
|
+
with `--no-open`, `--no-audio`, `--no-resume`, `--no-embed-metadata`, etc. `--no-subs` also
|
|
368
|
+
disables stored subtitle languages and subtitle embedding for that invocation.
|
|
369
|
+
|
|
370
|
+
### Named profiles
|
|
371
|
+
|
|
372
|
+
If no profile is selected, `profiles.default` is applied automatically. You can also select
|
|
373
|
+
it explicitly with `--profile default`. Other named profiles use global defaults rather
|
|
374
|
+
than inheriting `default`. The wizard preselects `default` when it exists.
|
|
375
|
+
`veo config edit` adds an empty `default` profile to existing configurations if missing,
|
|
376
|
+
preserving existing settings. An empty profile does not change download behavior.
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{
|
|
380
|
+
"output": "D:\\Videos",
|
|
381
|
+
"profiles": {
|
|
382
|
+
"default": { "quality": "1080p", "resume": true },
|
|
383
|
+
"music": { "audio": true, "format": "mp3", "output": "D:\\Music" },
|
|
384
|
+
"archive": { "quality": "1080p", "embedMetadata": true, "subLangs": "de,en" }
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`veo <url> --profile music` merges global defaults, then the selected profile, then explicit
|
|
390
|
+
CLI flags. `veo config profiles` lists profile names; `veo config path` prints the file path.
|
|
391
|
+
`veo config edit` fills new or empty files with a commented template explaining common
|
|
392
|
+
options and example profiles. Existing files receive a commented reference guide once;
|
|
393
|
+
their settings remain intact. In an interactive terminal, it opens the built-in editor
|
|
394
|
+
unless `VISUAL` or `EDITOR` is configured (an executable path, without shell arguments).
|
|
395
|
+
The editor provides syntax colors, line numbers and live validation. Syntax errors mark
|
|
396
|
+
the affected line; unknown properties and invalid values are highlighted directly, with
|
|
397
|
+
spelling suggestions, expected types and allowed values in the status area. Validation uses
|
|
398
|
+
the same rules as the CLI, including profile overrides and audio/video formats.
|
|
399
|
+
Press F2 on a property or value to open suggestions, use Up/Down to choose, Enter to apply
|
|
400
|
+
or Esc to cancel. Suggestions require valid JSON syntax; free-text values and custom numeric
|
|
401
|
+
resolutions such as `900p` remain supported. Ctrl+S validates and saves;
|
|
402
|
+
Esc or Ctrl+Q exits, asking before discarding changes. Use arrows, Home/End and PageUp/PageDown
|
|
403
|
+
to navigate. Invalid configurations cannot be saved. `--no-color` disables syntax colors.
|
|
404
|
+
In terminals supporting SGR mouse reporting, left-click positions the cursor and dragging
|
|
405
|
+
selects text, including across lines. Backspace/Delete removes the selection; typing replaces
|
|
406
|
+
it. The mouse wheel scrolls without changing the editing position; Ctrl+Up/Down also scrolls.
|
|
407
|
+
Ctrl+C copies selected text to the system clipboard, and Ctrl+V pastes clipboard text at the
|
|
408
|
+
cursor or replaces the selection. Ctrl+Q or Esc exits the editor. The editor disables mouse
|
|
409
|
+
reporting again when it exits. Keyboard editing remains available in terminals without mouse
|
|
410
|
+
support.
|
|
411
|
+
Clipboard access uses the system clipboard on Windows, `pbcopy`/`pbpaste` on macOS, and
|
|
412
|
+
`wl-clipboard` (Wayland), `xclip` or `xsel` (X11) on Linux. Linux requires one of those tools
|
|
413
|
+
and access to a graphical session. The editor reports when no clipboard is available; terminal
|
|
414
|
+
paste shortcuts can still insert text directly when supported by the terminal.
|
|
415
|
+
On Termux, editor clipboard shortcuts use `termux-clipboard-set`/`termux-clipboard-get`
|
|
416
|
+
when the Termux:API app and `termux-api` package are installed.
|
|
417
|
+
|
|
418
|
+
Use `veo config edit --external` to use `VISUAL`, then `EDITOR`, then Notepad on Windows
|
|
419
|
+
or `vi` elsewhere. `veo config edit --terminal` explicitly selects the built-in editor.
|
|
420
|
+
Set `VEO_CONFIG_EDITOR=external` to disable the built-in editor by default (`auto` restores
|
|
421
|
+
automatic selection, `terminal` forces it). For a persistent Windows preference:
|
|
422
|
+
|
|
423
|
+
```powershell
|
|
424
|
+
[Environment]::SetEnvironmentVariable('VEO_CONFIG_EDITOR', 'external', 'User')
|
|
425
|
+
$env:VEO_CONFIG_EDITOR = 'external'
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Non-interactive sessions use the external editor unless `--terminal` is explicitly selected,
|
|
429
|
+
in which case an interactive-terminal error is reported. The wizard also offers configured profiles.
|
|
430
|
+
Generated templates and app messages are in English. Previously generated German template
|
|
431
|
+
comments are translated the next time you run `veo config edit`; existing profile names,
|
|
432
|
+
paths and custom comments are preserved.
|
|
433
|
+
|
|
434
|
+
### Terminal output
|
|
435
|
+
|
|
436
|
+
- During a download, the terminal/tab title shows `veo | 50% | My Video` (original title or
|
|
437
|
+
your `-r` name), plus setup/processing status. It ends with `Done`, `Failed`, or
|
|
438
|
+
`Cancelled`; the shell may replace it at the next prompt. This uses the native console
|
|
439
|
+
title on Windows (including PowerShell/Windows Terminal) and OSC title sequences on
|
|
440
|
+
compatible Linux/macOS terminals. Titles are not changed when stderr is redirected or
|
|
441
|
+
`TERM=dumb`. Terminal settings that enforce a fixed tab title can override this feature.
|
|
442
|
+
- Progress shows percentage, speed, downloaded/total size and ETA on stderr; unknown values
|
|
443
|
+
appear as `?`. Separate audio/video streams each have their own progress. Non-interactive
|
|
444
|
+
output is throttled.
|
|
445
|
+
- Progress labels include the current item (`[3/12]`), source title, video/audio/media stream,
|
|
446
|
+
postprocessing (merging, conversion, subtitles or metadata) and saving. Stream percentages
|
|
447
|
+
describe that stream, not the entire multi-step job. Sources without codec metadata use
|
|
448
|
+
the neutral `Media` label.
|
|
449
|
+
- `--open` launches the completed file in your default app, including renamed files and
|
|
450
|
+
audio. With several URLs, the primary file of each URL is opened. Uses `explorer.exe` on
|
|
451
|
+
Windows, `open` on macOS, and `xdg-open` on Linux (requires a graphical desktop,
|
|
452
|
+
xdg-utils and a file association). The CLI does not wait for the player to close. If the
|
|
453
|
+
opener cannot be launched, a warning is printed and the successful download still exits
|
|
454
|
+
with `0`; later errors inside the detached opener/player are not monitored.
|
|
455
|
+
- After each successful download, veo checks the npm registry at most once per day for a
|
|
456
|
+
newer version and prints a one-line notice on stderr (never on failure). Disable it with
|
|
457
|
+
`VEO_NO_UPDATE_CHECK=1`; `VEO_REGISTRY`/`npm_config_registry` are respected.
|
|
458
|
+
- Exit status is `0` on success, `1` on errors, and `130` on cancellation. With several
|
|
459
|
+
URLs, `1` means at least one URL failed.
|
|
460
|
+
|
|
461
|
+
## Commands
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
veo version # installed version (also: veo --version or veo -v)
|
|
465
|
+
veo # interactive download wizard (terminal only)
|
|
466
|
+
veo config edit # create/open config, including example profiles
|
|
467
|
+
veo config reset # confirm with y; back up config and restore the current template
|
|
468
|
+
veo config profiles # list available profiles
|
|
469
|
+
veo config path # show the config file location
|
|
470
|
+
veo doctor # diagnose the local setup; exit 1 if a check fails
|
|
471
|
+
veo doctor fix # restore missing or damaged managed tools
|
|
472
|
+
veo doctor --offline # skip the network checks
|
|
473
|
+
veo stats # persistent download totals; --json for scripting
|
|
474
|
+
veo history # the last 5 downloads; --json for scripting
|
|
475
|
+
veo history --failed --limit 20 # recent failed/cancelled attempts
|
|
476
|
+
veo retry --last # retry the newest failed or unfinished job
|
|
477
|
+
veo runs # active runs with their id; veo runs <id> for details
|
|
478
|
+
veo runs --json # machine-readable active run metadata
|
|
479
|
+
veo inspect FILE --check-audio --json # track metadata and audio signal
|
|
480
|
+
veo inspect run ID --check-audio --json # finished run and saved media
|
|
481
|
+
veo stop [id] # stop one run, or every active run
|
|
482
|
+
veo flush # stop runs, clear temporary downloads and retry jobs
|
|
483
|
+
veo flush --stats # the same, and reset the statistics
|
|
484
|
+
veo update # install the latest veo with npm
|
|
485
|
+
veo update --check # only check for a newer veo
|
|
486
|
+
veo upgrade # alias for veo update
|
|
487
|
+
veo check update # alias for veo update --check
|
|
488
|
+
veo backend update # install the newest yt-dlp release
|
|
489
|
+
veo backend reset # back to the release pinned in this veo version
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
`veo update` runs `npm install -g veodl@latest` and then removes yt-dlp backend
|
|
493
|
+
caches from older pinned releases, keeping an explicitly installed backend release. It
|
|
494
|
+
never uses a shell on Linux/macOS, passes fixed arguments only, and prints the manual npm
|
|
495
|
+
command on any failure. The registry can be overridden with `VEO_REGISTRY` (or npm's
|
|
496
|
+
`npm_config_registry`) for mirrors and proxies.
|
|
497
|
+
|
|
498
|
+
### `veo flush`
|
|
499
|
+
|
|
500
|
+
Run `veo flush` to stop active veo runs started with this version, then remove
|
|
501
|
+
temporary local downloads, including the 15-minute retained files and unfinished
|
|
502
|
+
resume data, and cached retry job JSON files. Those jobs can no longer be retried.
|
|
503
|
+
Saved media, output history, config/profiles, the `veo history` list, finished-run
|
|
504
|
+
records and backend binaries are kept.
|
|
505
|
+
Cleanup waits for cancellation; if a run cannot stop, it fails without deleting
|
|
506
|
+
download or job files. Locked folders from older or interrupted processes are
|
|
507
|
+
skipped and reported. Only veo's own per-user cache is cleaned. Statistics are
|
|
508
|
+
preserved unless `--stats` is given: `veo flush --stats` resets them too.
|
|
509
|
+
To end runs without removing their downloads and jobs, use `veo stop [id]` instead.
|
|
510
|
+
|
|
511
|
+
### `veo stats`
|
|
512
|
+
|
|
513
|
+
Shows saved videos/audio, failed attempts, skips, cancellations and total time spent
|
|
514
|
+
on download requests (including preparation, processing and saving). Parallel run
|
|
515
|
+
times are added together. Playlist entries count individually; retries are new
|
|
516
|
+
attempts. Active requests are recorded when they finish. Tracking starts with this
|
|
517
|
+
version; previous downloads are not imported. `veo stats --json` returns the totals
|
|
518
|
+
as JSON. Statistics contain counters and timestamps, not URLs or filenames.
|
|
519
|
+
|
|
520
|
+
### `veo history`
|
|
521
|
+
|
|
522
|
+
Shows the **last 5 download attempts** by default, newest first, with title, status, media type,
|
|
523
|
+
date, duration, URL and the saved files. Saved, skipped, failed and cancelled items are
|
|
524
|
+
recorded, including the reason a failure was reported; playlist entries and retried
|
|
525
|
+
attempts count individually. Active downloads appear once they finish. Long file lists
|
|
526
|
+
are summarized in the text view.
|
|
527
|
+
|
|
528
|
+
Use `veo history --limit N` to show 1-1000 attempts, or `veo history --failed` to
|
|
529
|
+
show only failed and cancelled attempts; the flags can be combined. Failed attempts
|
|
530
|
+
include their exact retry-job command when one was recorded. New attempts also show
|
|
531
|
+
their persistent run ID for `veo inspect run <id>`. `veo history --json`
|
|
532
|
+
prints `{"count":N,"entries":[…]}` for scripting. Each entry has
|
|
533
|
+
`at`, `url`, `title`, `status`, `media` (`video`/`audio`), `quality`, `format`, the
|
|
534
|
+
complete `files` list, `error`, `elapsedMs` and an optional `job` path. Titles and paths are stored without
|
|
535
|
+
terminal control characters.
|
|
536
|
+
|
|
537
|
+
History is one small JSON file per attempt in the per-user veo cache's `history`
|
|
538
|
+
directory, so parallel runs cannot overwrite each other and each record stays small. The
|
|
539
|
+
directory keeps one file per attempt and contains URLs, titles and local file paths;
|
|
540
|
+
delete it to remove those records. `veo flush` keeps the list (only `veo flush --stats`
|
|
541
|
+
resets statistics, not history).
|
|
542
|
+
|
|
543
|
+
### `veo runs` and `veo stop`
|
|
544
|
+
|
|
545
|
+
Every run registers itself while it works, under a **6-character id**, and removes that
|
|
546
|
+
record when it ends:
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
veo runs # active runs: id, PID, state, start time, progress, output directory
|
|
550
|
+
veo runs k3f9qa # one run in detail: URLs, settings, job file, per-item state
|
|
551
|
+
veo stop k3f9qa # ask that run to stop and wait until it exits
|
|
552
|
+
veo stop # stop every active run
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
`veo runs` lists the runs of this user, including those started in another terminal, and a
|
|
556
|
+
run disappears from the list as soon as it finishes. Progress such as
|
|
557
|
+
`1/3 done, 1 running` is read from the run's job file, which is created before the first
|
|
558
|
+
download starts, so a run that is still preparing the backend shows `starting`. `veo stop
|
|
559
|
+
<id>` writes a stop request that the run itself polls, so it needs no signals or PIDs and
|
|
560
|
+
works the same on every platform. A stopped run exits like Ctrl+C (`130`) and keeps its
|
|
561
|
+
partial data and retry job, so the printed `veo --retry-failed` command still works —
|
|
562
|
+
unlike `veo flush`, which also removes that data. If a run does not stop within 15 seconds,
|
|
563
|
+
`veo stop` reports it and exits with `1`.
|
|
564
|
+
|
|
565
|
+
A run that crashed without cleaning up leaves its record behind: `veo runs` marks it
|
|
566
|
+
`stale` and `veo stop` removes it, with or without its id. Records contain the id, process
|
|
567
|
+
id, start time, URLs, output directory and job path — never cookie files, browser sessions
|
|
568
|
+
or other credentials. Unknown or damaged record files are ignored instead of breaking veo,
|
|
569
|
+
because a single stray file in the cache must never stop later runs.
|
|
570
|
+
|
|
571
|
+
### `veo doctor`
|
|
572
|
+
|
|
573
|
+
Prints one line per check: `ok`, `warn` or `fail`. It inspects Node.js, the platform, the
|
|
574
|
+
output directory, the backend cache, yt-dlp (including the SHA-256 of the cached binary and
|
|
575
|
+
the version it reports), FFmpeg/FFprobe, a system FFmpeg fallback, the config file, leftover
|
|
576
|
+
partial downloads, stale duplicate-detection records, the npm registry and the yt-dlp release host. It downloads no backend and
|
|
577
|
+
only creates its own probe files plus the backend cache directory. Exit status is `1` when
|
|
578
|
+
at least one check fails.
|
|
579
|
+
|
|
580
|
+
Run `veo doctor fix` to restore missing or damaged managed tools and check again.
|
|
581
|
+
It stages bundled FFmpeg/FFprobe and downloads verified yt-dlp when needed. Missing
|
|
582
|
+
desktop media tools are installed in veo's cache using npm and the pinned media
|
|
583
|
+
packages. On Termux it installs missing native tools with the package manager.
|
|
584
|
+
With `--offline`, it only uses local binaries; missing tools are reported for an online retry.
|
|
585
|
+
Use `-o PATH` to create and check an output directory. Config errors and invalid overrides
|
|
586
|
+
are reported for manual correction; PATH and config values are never rewritten.
|
|
587
|
+
A missing system FFmpeg is not a warning when the selected media tools work.
|
|
588
|
+
|
|
589
|
+
## Supported sites and backend
|
|
590
|
+
|
|
591
|
+
YouTube, X/Twitter, TikTok, Vimeo, Reddit, Instagram, and [many other yt-dlp sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) are supported **when publicly accessible and technically available**. Support changes with websites, regions, rate limits, and backend versions; it is not a guarantee that every URL will work.
|
|
592
|
+
|
|
593
|
+
Official standalone yt-dlp is acquired on first download and cached outside the package directory; help, version and plain doctor never download the backend. The release is pinned and SHA-256 verified against hashes shipped with this package. FFmpeg and FFprobe are normally supplied by the optional `ffmpeg-static` and `ffprobe-static` dependencies during npm installation. If these are absent and no local pair is available, veo installs `ffmpeg-static@5.3.0` and a platform-specific `@ffprobe-installer` package in a temporary cache project, checks that both programs run, and saves them in its backend cache. Media packages use npm/upstream HTTPS distribution, not the yt-dlp pinned-hash guarantee. These binaries have their own licenses; see their upstream packages. No Python installation is needed on supported standalone platforms.
|
|
594
|
+
|
|
595
|
+
The existing Node executable is explicitly enabled as yt-dlp's JavaScript runtime for YouTube. Local yt-dlp configuration and plugins are disabled for predictable execution. Arguments are passed without a shell.
|
|
596
|
+
|
|
597
|
+
`ffmpeg-static` and `ffprobe-static` publish no Windows ARM64 binaries. veo no longer refuses
|
|
598
|
+
that platform: it uses an FFmpeg/FFprobe pair found on `PATH`, including the usual
|
|
599
|
+
WinGet, Chocolatey and `C:\ffmpeg\bin` locations, or automatically downloads x64
|
|
600
|
+
media tools for Windows 11's x64 emulation.
|
|
601
|
+
|
|
602
|
+
### Android / Termux
|
|
603
|
+
|
|
604
|
+
With Node.js 22+ and npm already installed in Termux:
|
|
605
|
+
|
|
606
|
+
```sh
|
|
607
|
+
npm install -g veodl
|
|
608
|
+
veo "URL"
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
On Android, veo automatically finds `yt-dlp`, `ffmpeg` and `ffprobe` on PATH
|
|
612
|
+
(also checking `$PREFIX/bin`). If tools or the `yt-dlp-ejs` Python module are
|
|
613
|
+
missing, it runs `pkg install -y python-yt-dlp yt-dlp-ejs ffmpeg` automatically,
|
|
614
|
+
then checks the installed programs before continuing the download. Python is
|
|
615
|
+
installed as a dependency. Termux with the pacman package manager uses
|
|
616
|
+
`--noconfirm` instead. This needs a working Termux repository and internet access.
|
|
617
|
+
No root access is needed. Later runs reuse the installed tools.
|
|
618
|
+
|
|
619
|
+
Static FFmpeg dependencies are optional; `--omit=optional` can be added to the
|
|
620
|
+
npm install command to skip attempting those desktop packages entirely.
|
|
621
|
+
The system yt-dlp is maintained by Termux and is
|
|
622
|
+
not pinned or hash-verified by veo. Explicit `VEO_YT_DLP_PATH` and
|
|
623
|
+
`VEO_FFMPEG_PATH` overrides still take precedence.
|
|
624
|
+
|
|
625
|
+
`veo doctor fix` also performs this automatic setup; `veo doctor fix --offline`
|
|
626
|
+
never invokes a package manager. Update Termux packages with `pkg upgrade`;
|
|
627
|
+
`veo backend update` prints this platform's update instructions.
|
|
628
|
+
|
|
629
|
+
To save files in Android's shared Downloads folder, run `termux-setup-storage`,
|
|
630
|
+
grant the storage permission, then use `veo "URL" -o ~/storage/downloads`.
|
|
631
|
+
Keep veo and its tools in Termux's private storage.
|
|
632
|
+
|
|
633
|
+
For existing published veo 1.6.1 installations (before this automatic setup), run:
|
|
634
|
+
|
|
635
|
+
```sh
|
|
636
|
+
pkg install python-yt-dlp yt-dlp-ejs ffmpeg
|
|
637
|
+
export VEO_YT_DLP_PATH="$(command -v yt-dlp)"
|
|
638
|
+
export VEO_FFMPEG_PATH="$PREFIX/bin"
|
|
639
|
+
veo doctor
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
These exports apply to the current shell; add them to your shell startup file
|
|
643
|
+
if you need them on subsequent launches of 1.6.1.
|
|
644
|
+
|
|
645
|
+
### Advanced overrides
|
|
646
|
+
|
|
647
|
+
- `VEO_YT_DLP_PATH`: absolute path to a trusted, current yt-dlp executable. It always takes
|
|
648
|
+
precedence over the managed backend, including one installed by `veo backend update`.
|
|
649
|
+
- `VEO_FFMPEG_PATH`: directory containing both `ffmpeg` and `ffprobe` executables (with
|
|
650
|
+
`.exe` on Windows).
|
|
651
|
+
- `VEO_CONFIG`: path to the config file.
|
|
652
|
+
|
|
653
|
+
Automatic setup covers Windows x64/ia32/ARM64, macOS x64/ARM64, Linux x64/ARM64,
|
|
654
|
+
and Android through Termux. Windows ARM64 media tools require x64 emulation.
|
|
655
|
+
`--open` uses `termux-open` in Termux; another Android app must be available to view the file.
|
|
656
|
+
Other systems should supply trusted native binaries. Node.js 22+, npm, internet
|
|
657
|
+
access, executable private storage and working OS libraries are prerequisites;
|
|
658
|
+
network blocks or unavailable repositories can still prevent setup. Android's
|
|
659
|
+
shared-storage permission must be granted by the user. Maintainers should update
|
|
660
|
+
the pinned yt-dlp release and hashes as websites change,
|
|
661
|
+
or users can install a newer release themselves.
|
|
662
|
+
|
|
663
|
+
### Installing a newer backend
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
veo backend update --check # is a newer yt-dlp release available?
|
|
667
|
+
veo backend update # install it
|
|
668
|
+
veo backend reset # forget it, use the pinned release again
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
The pinned backend is hash-verified against values compiled into this veo version. That
|
|
672
|
+
guarantee cannot cover a release that did not exist when this version was built, so
|
|
673
|
+
`veo backend update` verifies the download against the `SHA2-256SUMS` file published with
|
|
674
|
+
that release over HTTPS: the trust anchor becomes HTTPS and GitHub instead of the npm
|
|
675
|
+
package. It is opt-in, it says so on stderr when it runs, and `veo backend reset` (plus the
|
|
676
|
+
next `veo update`) returns to the pinned release. An installed release is used only while it
|
|
677
|
+
is newer than the pinned one and its bytes still match the recorded hash; anything else
|
|
678
|
+
silently falls back to the pinned release.
|
|
679
|
+
|
|
680
|
+
## Develop and publish
|
|
681
|
+
|
|
682
|
+
```bash
|
|
683
|
+
npm install
|
|
684
|
+
npm test # unit tests, no network
|
|
685
|
+
npm run test:smoke # real downloads: local HTTP server, real yt-dlp, real FFmpeg
|
|
686
|
+
npm run test:open # real download plus a controlled desktop-opener replacement
|
|
687
|
+
npm pack
|
|
688
|
+
npm run check:package # inspect the packed tarball
|
|
689
|
+
npm link
|
|
690
|
+
veo --version
|
|
691
|
+
# Then, authenticated as the account that owns the name:
|
|
692
|
+
npm publish
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
The smoke test generates a two-second synthetic video with FFmpeg, serves it on loopback,
|
|
696
|
+
and runs real yt-dlp downloads. It checks default output, the quality cap, duplicate names,
|
|
697
|
+
audio extraction, conversion, dry-run, format listing, JSON output, metadata embedding,
|
|
698
|
+
resume, batch behaviour, playlist selection and nonzero failures, and verifies that a
|
|
699
|
+
finished download whose destination was unavailable is retried from the local cache
|
|
700
|
+
without contacting the source again. It also confirms that real runs leave `veo history`
|
|
701
|
+
entries, that finished runs clean up their record, and that `veo runs`/`veo stop` report
|
|
702
|
+
nothing left behind. It needs network access once for yt-dlp acquisition; it downloads no
|
|
703
|
+
third-party video. Unit tests require no network.
|
|
704
|
+
|
|
705
|
+
The published tarball only includes `bin/`, `src/`, package metadata, README and LICENSE.
|
|
706
|
+
The lockfile is kept for reproducible development. There is no build step. npm makes the
|
|
707
|
+
shebang-bearing bin executable on installation (on Unix, `chmod +x bin/veo.js` also enables
|
|
708
|
+
direct checkout execution).
|
|
709
|
+
|
|
710
|
+
Continuous integration runs the unit tests on Windows, macOS and Linux for Node 22 and 24,
|
|
711
|
+
runs the end-to-end smoke test on Node 22, and verifies the packed tarball.
|
|
712
|
+
|
|
713
|
+
Modules separate argument parsing (`src/cli.js`), configuration (`src/config.js`,
|
|
714
|
+
`src/config-errors.js`, `src/config-template.js`), backend setup (`src/backend.js`),
|
|
715
|
+
backend updates (`src/backend-update.js`), download orchestration (`src/downloader.js`,
|
|
716
|
+
`src/download-cache.js`), retry jobs (`src/jobs.js`), playlists (`src/playlist.js`),
|
|
717
|
+
statistics, download history, the run registry and cleanup (`src/stats.js`,
|
|
718
|
+
`src/history.js`, `src/runs.js`, `src/flush.js`), diagnostics (`src/doctor.js`),
|
|
719
|
+
progress (`src/progress.js`), and helpers (`src/utils.js`, `src/paths.js`,
|
|
720
|
+
`src/state.js`, `src/version.js`) so new options and providers can be added without
|
|
721
|
+
replacing the CLI.
|
|
722
|
+
|
|
723
|
+
## License
|
|
724
|
+
|
|
725
|
+
MIT for this CLI. yt-dlp and FFmpeg/FFprobe retain their respective upstream licenses.
|