# COOKBOOK
- Practical recipes for driving `youtube-legend-cli` from a shell, a script, or a Python pipeline
- Every recipe below was checked against the `0.4.0` binary
- Languages: [English](COOKBOOK.md) | [Português Brasileiro](COOKBOOK.pt-BR.md)
## Latency Note
- The cache lives under the platform cache directory, at `~/.cache/youtube-legend-cli/` on Linux
- A warm cache serves the body straight from disk, with no network round trip at all
- A cold cache pays the upstream HTTP round trips, and those dominate the wall clock
- No latency number is published here, because a number you cannot reproduce on your own machine is worth nothing
## Default Values Reference
- Every default below is what `youtube-legend-cli --help` prints for version `0.4.0`
- Run that command yourself the moment this table and the binary disagree, because the binary is the authority
| Language | `en` | `--lang` |
| Format | `txt` | `--format` |
| Operation timeout | `300` seconds | `--timeout` |
| Log level | `warn` | `--log-level` |
| Log format | `text` | `--log-format` |
| Color | `auto` | `--color` |
| Cache TTL | `24` hours | `--cache-ttl` |
| Concurrent batch items | `0` | `--jobs` |
| Provider | `auto` | `--provider` |
- `--timeout` is the ceiling of the WHOLE operation, and it is NEVER a per-request HTTP timeout
- `--jobs 0` lets the binary choose the concurrency, and the value only matters under `--batch`
- The remaining flags are switches that start off: `--verbose`, `--quiet`, `--json`, `--batch`, `--resume`, `--offline`, `--no-cache`, `--no-progress`, `--dry-run`, `--yes` and `--no-input`
- The binary reads NO environment variable, so a flag or the `config` TOML file is the only way to change any of this
## How To download subtitles for one video
- PROBLEM: someone hands you a single YouTube URL and you need the plain-text transcript on disk
- SOLUTION: pass the URL on the command line and redirect `stdout` to a file
- The body lands in the file, while logs and progress stay on `stderr`
```bash
youtube-legend-cli "https://youtu.be/dQw4w9WgXcQ" > subtitle.txt
```
- VERIFY: the file exists, carries the transcript, and the terminal showed only `stderr` noise
```bash
wc -l subtitle.txt
```
## How To download in batch from a list
- PROBLEM: you have a file with one URL per line and you need a transcript for every video
- SOLUTION: pass `--batch` and feed the file on `stdin`
- A failure on one line does not abort the lines after it
```bash
youtube-legend-cli --batch < urls.txt > transcripts.txt 2> batch.log
```
- VERIFY: `transcripts.txt` holds the bodies in input order and `batch.log` holds the per-URL status
- MEASURED on 2026-09-04: plain-text batch output carries NO separator line between bodies, so use `--json` when you need to tell one item from the next
- The process exit code is the code of the WORST item, and never the code of the first one
## How To get one JSON object per batch item
- PROBLEM: a pipeline needs to attribute every result to its own URL
- SOLUTION: combine `--batch` with `--json` and read the output as NDJSON, one object per line
```bash
youtube-legend-cli --batch --json < urls.txt > items.ndjson
- VERIFY: every line parses on its own, and `target_source` reads `batch-file` on each of them
## How To parse the JSON envelope in Python
- PROBLEM: a pipeline needs the structured fields without writing a regex
- SOLUTION: pass `--json`, then parse the single-line envelope with `json.loads`
- The success envelope carries `byte_size`, `content`, `delivered_language`, `duration_ms`, `format`, `language`, `provider`, `source_url`, `target_resolved`, `target_source` and `video_id`
- `language` echoes what you ASKED for, and `delivered_language` is the only field that reports the track you actually got
```python
import json
import subprocess
result = subprocess.run(
["youtube-legend-cli", "--json", "https://youtu.be/dQw4w9WgXcQ"],
capture_output=True,
text=True,
check=False,
)
envelope = json.loads(result.stdout)
if envelope.get("error"):
raise SystemExit(f"{envelope['kind']}: {envelope['message']}")
print(envelope["content"])
```
- VERIFY: the script prints the transcript and exits `0`
- On failure it exits non-zero and prints the `kind` the binary chose
## How To read the error envelope
- PROBLEM: a run failed and your script has to branch on the reason rather than on a message string
- SOLUTION: read the FLAT error envelope on `stdout`, whose discriminator is the boolean `error`
- MEASURED on 2026-09-04: the fields sit at the TOP level and are `error`, `code`, `message`, `kind`, `retryable`, `provider`, `video_id`, `target_resolved`, `target_source`, `requested_language`, `available_languages` and `attempts`
- A filter like `.error.kind` FAILS, because you cannot index a boolean
```bash
output=$(youtube-legend-cli --json "https://youtu.be/VIDEO")
if [ "$(printf '%s' "$output" | jaq -r '.error // false')" = "true" ]; then
```
- VERIFY: `kind` reads `provider_rate_limited` when a quota bit, and the process exit code is `69`
- `retryable` tells you whether a second attempt can possibly help, so branch on it instead of guessing
## How To pin a provider for reproducible runs
- PROBLEM: a scripted run needs a deterministic provider so a degraded upstream does not turn into a flake
- SOLUTION: `auto` walks the whole chain in cost order, so pin one provider with `--provider`
- The choices are `provider-decopy` and `provider-noiz`, and there are exactly TWO
- `provider-decopy` serves the native track only and offers no language choice
- `provider-noiz` is capped at five requests per day
- A pinned provider never silently falls back to the other one
```bash
youtube-legend-cli --provider provider-decopy \
"https://youtu.be/dQw4w9WgXcQ" > subtitle.txt
```
- VERIFY: the exit code is `0` on success and `69` when the pinned provider is unavailable
- The `provider` field of the JSON envelope names the provider that actually answered
```bash
youtube-legend-cli --json --provider provider-decopy "https://youtu.be/dQw4w9WgXcQ" \
| jaq -r .provider
```
## How To override cache TTL
- PROBLEM: a long-running job wants a wider cache window so a repeated video costs nothing
- SOLUTION: pass `--cache-ttl` in hours, and the cache layer applies it on every read
```bash
youtube-legend-cli --cache-ttl 168 \
"https://youtu.be/dQw4w9WgXcQ" > subtitle.txt
```
- VERIFY: a second run of the same command on the same video answers from disk and makes no upstream request
- Confirm the absence of network by adding `--offline`, which fails loudly rather than reaching out
## How To work from the cache alone
- PROBLEM: you are on a plane, or you want a run that provably touches no network
- SOLUTION: pass `--offline`, which refuses every outbound request and serves only what the cache already holds
```bash
- VERIFY: a cached video answers with `provider` and `source_url` both reading `cache`
- An uncached video fails instead of fetching, which is the whole point of the flag
- `--dry-run` is the neighbouring flag: it reports what it WOULD fetch, while `--offline` still answers from the cache
## How To debug with verbose logging
- PROBLEM: a download fails and you need the provider chain, the retry attempts and the HTTP timings
- SOLUTION: combine `--verbose` with `--log-level debug` so tracing events land on `stderr` and the body stays clean on `stdout`
- Add `--log-format json` when a machine, and not a person, reads the log
```bash
youtube-legend-cli --verbose --log-level debug --log-format json \
"https://youtu.be/dQw4w9WgXcQ" > subtitle.txt 2> trace.log
```
- VERIFY: `trace.log` carries one JSON object per event, and `subtitle.txt` carries nothing but the transcript
- The `attempts` array of a failed `--json` run tells the same story in a single line, without any log at all
## How To shape the JSON output for an agent
- PROBLEM: an agent pays for every token, and the full envelope carries a whole transcript it does not need
- SOLUTION: reduce the payload with the output-shaping flags before it ever reaches the model
- `--select` keeps only the dotted keys you name, and `--fields` is its alias
- `--filter` keeps elements matching `key=value`, `key!=value` or `key~substring`
- `--limit`, `--sort`, `--dedupe-by` and `--count-only` finish the job
- `--truncate-content` shortens long strings by CHARACTERS, and never by bytes
- `--max-output-bytes` caps the whole envelope by dropping entire elements
```bash
youtube-legend-cli --json --select video_id,provider,byte_size \
"https://youtu.be/dQw4w9WgXcQ"
```
- VERIFY: the envelope carries the three keys you asked for and nothing else
- Ask the binary for the full contract with `--print-schema`, which prints the JSON Schema of every output surface and exits
## How To keep settings without environment variables
- PROBLEM: you want the same defaults on every run without pasting flags into every script
- SOLUTION: use the `config` subcommand, because the binary reads NO environment variable at all
- `config path` prints the absolute path of the configuration file
- `config show` prints every key you have set, in dotted form
- `config get <KEY>` and `config set <KEY> <VALUE>` read and write one key
- `config unset <KEY>` restores the compiled default
- `config list-keys` prints the whole registry with each key, its type and its description
```bash
youtube-legend-cli config path
youtube-legend-cli config list-keys
youtube-legend-cli config set lang pt
youtube-legend-cli config get lang
youtube-legend-cli config show
youtube-legend-cli --yes config unset lang
```
- MEASURED on 2026-09-04: `config unset` on a key that is currently set exits `64` and asks for the global `--yes`, which is why the last line carries it
- VERIFY: `config get lang` echoes `pt`, and a run with no `--lang` now asks for Portuguese
- Point a run at a different file with `--config <PATH>` whenever you need a throwaway profile
## How To install shell completion
- PROBLEM: you type the flags often enough that a typo costs you a run
- SOLUTION: generate the completion script with `completions <SHELL>`, which writes to `stdout`
- The accepted shells are `bash`, `elvish`, `fish`, `powershell` and `zsh`
```bash
youtube-legend-cli completions bash > ~/.local/share/bash-completion/completions/youtube-legend-cli
youtube-legend-cli completions zsh > ~/.zfunc/_youtube-legend-cli
youtube-legend-cli completions fish > ~/.config/fish/completions/youtube-legend-cli.fish
youtube-legend-cli completions elvish > ~/.config/elvish/lib/youtube-legend-cli.elv
youtube-legend-cli completions powershell > youtube-legend-cli.ps1
```
- VERIFY: a new shell completes `youtube-legend-cli --pro` into `--provider`
- `youtube-legend-cli man` prints the section 1 manual page in roff, if you would rather read than guess
## How To read the manual page offline
- PROBLEM: you want the whole flag reference on a machine with no browser and no network
- SOLUTION: run `man`, which prints the section 1 page on `stdout` in roff
- The page derives from the same command tree the binary parses, so it can never describe a flag that does not exist
```bash
youtube-legend-cli man > ~/.local/share/man/man1/youtube-legend-cli.1
man youtube-legend-cli
```
- VERIFY: `man youtube-legend-cli` opens the page and names the same flags `--help` prints