Recapit is a Rust CLI for turning slide decks, lecture handouts, PDFs, YouTube videos, and standalone images into cleaned Markdown or LaTeX using Google Gemini models. It bundles asset discovery, ffmpeg/yt-dlp normalization, quota-aware retries, and prompt preambles into one binary. The default model is gemini-3-pro-preview, with full support for the generally available Gemini 2.5 family (gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite).
./install
export GEMINI_API_KEY="your-key"
recapit input.pdfThe command above installs the CLI, sets your API key, and transcribes input.pdf to Markdown beside the source file. Add --format latex for LaTeX output or --export srt vtt for subtitles.
- Rust 1.79+ and Cargo.
- Google Gemini access and a
GEMINI_API_KEYwith permissions forgemini-3-pro-preview(default) or the GA Gemini 2.5 family (gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite). - Poppler (
pdftoppm,pdfinfo) and FFmpeg are required, and yt-dlp is required for YouTube URLs.
Clone the repository, then run the helper script to install (or update) the CLI into your Cargo bin directory:
./installThe installer also drops man/recapit.1 into ${MANPREFIX:-~/.local/share/man}/man1, so man recapit works after installation (set MANPREFIX to change the target).
The script ensures cargo is available, warns if external tools such as ffmpeg, yt-dlp, or Poppler’s pdftoppm/pdfinfo are missing, and finally executes cargo install --path . --locked --force. Any extra flags you pass to ./install are forwarded to cargo install.
Prefer to invoke Cargo directly? Run:
cargo install --path . --locked --forceOr run directly without installing:
cargo run -- transcribe input.pdf --export srtYou must supply a Gemini API key via GEMINI_API_KEY. Google provides a free tier for Gemini 2.5 Flash and Flash‑Lite (input/output tokens are “Free of charge” up to published limits). You can use that by creating a standalone key in Google AI Studio and not attaching it to a Cloud project with billing. Keys linked to a billed Cloud project are charged after free limits. See the official pricing page for details and current limits.
Even on the free tier, this tool still shows token counts and estimated dollar cost in its summary, and that estimate reflects list pricing and doesn’t change your free/paid status.
Environment variables:
| Setting | Description |
|---|---|
GEMINI_API_KEY |
Required. API key consumed by the CLI via AppConfig::load. |
RECAPIT_DEFAULT_MODEL |
Optional. Override the default transcription model (defaults to gemini-3-pro-preview). |
RECAPIT_OUTPUT_DIR |
Optional. Override the base output directory (defaults to each input's parent directory). |
RECAPIT_PDF_DPI |
Optional. DPI to use when rasterizing PDFs to PNGs (defaults to 200). |
RECAPIT_TEMPLATES_DIR |
Optional. Point to an alternate prompt template directory. |
RECAPIT_SAVE_FULL_RESPONSE |
Optional. Set to 1/true to also write raw model text under full-response/. |
RECAPIT_SAVE_INTERMEDIATES |
Optional. Set to 1/true to retain normalized videos, chunk MP4s, and manifests for debugging/re-use. |
RECAPIT_MAX_WORKERS |
Optional. Control the maximum number of parallel document/image workers (defaults to 4). |
RECAPIT_MAX_VIDEO_WORKERS |
Optional. Control the maximum number of parallel video chunk workers (defaults to 3). |
RECAPIT_TOKENS_PER_SECOND |
Optional. Override the effective tokens-per-second budget used to slice video/audio inputs. |
RECAPIT_VIDEO_MAX_CHUNK_SECONDS |
Optional. Cap per-chunk duration when planning video segments (defaults to 7200). |
RECAPIT_VIDEO_MAX_CHUNK_BYTES |
Optional. Cap per-chunk size in bytes (defaults to 524288000). |
RECAPIT_VIDEO_MEDIA_RESOLUTION |
Optional. Force Gemini media resolution hints: default, low, medium, high, unspecified. |
RECAPIT_VIDEO_ENCODER |
Optional. Override the encoder used for video normalization (auto, cpu, nvenc, videotoolbox, qsv, amf). auto probes available FFmpeg hardware encoders and prefers GPU paths when they work. |
Environment variables prefixed with LECTURE_SUMMARIZER_ remain supported for compatibility with older configurations, but new setups should prefer the RECAPIT_ variants.
All prompt and preamble files are optional: the app ships with reasonable built-in defaults. Drop files into templates/ when you want to override them (e.g., document-template.txt, document-prompt.txt). The auto classifier inspects filenames and the first-page aspect ratio to decide between slide-, lecture-, or document-style prompts. For ambiguous cases, force a mode with --kind slides|lecture|document.
Prefer configuration files? Create recapit.yaml in the repo root to store defaults for default_model, output_dir, exports, video chunk parameters, and per-preset overrides. CLI flags override environment variables, and environment variables override the YAML file, giving you explicit precedence of CLI > ENV > YAML.
After installation the recapit command becomes available. Export GEMINI_API_KEY first, then explore the commands below.
| Command | Purpose | Highlights |
|---|---|---|
recapit [SOURCE] |
Default transcribe workflow | Honors presets/config, supports exports (srt, vtt, markdown, json), YouTube URLs, directory recursion |
recapit [SOURCE] --dry-run [--json] |
Preview ingestion + normalization only | No Gemini calls; shows assets/chunks; --json for machine-readable output |
recapit [SOURCE] --to markdown|json [--from auto|latex|markdown] |
Batch-convert existing LaTeX/Markdown to Markdown or JSON via Gemini | Supports --file-pattern, --recursive, --skip-existing |
recapit report cost |
Summarize token/cost telemetry from a previous run | Works on run-summary.json or directories |
recapit cleanup cache|downloads |
Remove cached downloads or normalized artifacts | Safe-by-default; pass --yes to apply |
All commands support --config to point at an alternate YAML file. Presets from recapit.yaml automatically merge with CLI flags.
export GEMINI_API_KEY="..."
# Inspect how an asset will be processed (no API calls)
recapit input/video.mp4 --dry-run
recapit https://example.com/report.pdf --dry-run --json
# Transcribe a deck with the “speed” preset, keeping raw responses, JSON exports, and LaTeX output
RECAPIT_SAVE_FULL_RESPONSE=1 recapit slides/deck.pdf \
--preset speed \
--format latex \
--export json \
--output-dir output/decks
# Batch multiple sources in one run (each gets its own slugged output dir)
recapit slides/deck.pdf notes/lecture01.pdf images/scan.png
# Transcribe a YouTube lecture, keeping intermediates for reuse and forcing low-res media hints
RECAPIT_SAVE_INTERMEDIATES=1 recapit "https://www.youtube.com/watch?v=dQw4w9WgXcQ" \
--preset quality \
--media-resolution low \
--export srt vtt
# Post-processing helpers powered by the conversion utilities
# Convert legacy LaTeX transcripts to Markdown
recapit output/course-notes --to markdown --file-pattern "*.tex" --recursive
# Convert freshly-generated Markdown into JSON tables
recapit output/course-notes --to json --file-pattern "*.md" --skip-existing
# Review the cost of a prior run
recapit report cost output/course-notes/run-summary.json
# Periodically prune caches (dry-run by default)
recapit cleanup cache
recapit cleanup downloads --yesrecapit transcribe (and the shorthand recapit <SOURCE>) accept the standard --kind/--pdf-mode overrides, plus:
--preset <name>to preload overrides fromrecapit.yaml(e.g., select models, exports, concurrency).--pages <range>to process only selected PDF pages (1-based). Examples:1-3,5,10-or-2. Pass once to apply to all sources, or once per source when supplying multiple inputs.--format markdown|latexto choose the primary transcript format (defaults to Markdown).--export srt|vtt|markdown|jsonto emit additional artifacts. Markdown is already the default output (the flag is retained for compatibility), and JSON exports use the new conversion pipeline under the hood.- Save toggles (
save_full_response,save_intermediates) follow precedenceCLI preset > config file > environment. SetRECAPIT_SAVE_FULL_RESPONSE=1orRECAPIT_SAVE_INTERMEDIATES=1(or edit the preset) to turn them on for a run. --media-resolution default|low|medium|high|unspecifiedforwards Gemini media hints, matching preset/environment behaviour.
Every run writes:
<slug>/<slug>-transcribed.md|tex– primary transcript (Markdown by default, LaTeX when you use--format latex).run-summary.json– totals, estimated spend, and a list of output artifacts.run-events.ndjson– per-request telemetry (one JSON object per API call).chunks.json– manifest for normalized video assets (video inputs only). Manifests include hashes and chunk response paths so reruns with--skip-existinghonor prior work.- Optional
.srt/.vttsubtitle files or.jsonexports when--exportis provided. - Optional
full-response/artifacts and chunk intermediates when the corresponding save toggles are enabled.
Use --hide-summary, --detailed-costs, and --summary-path to adjust the console summary behaviour.
Output layout depends on what you asked for:
- Transcript only (default, no exports/metadata/full-response/intermediates): write a single file (
<stem>-transcribed.md|tex) alongside the input (or inside--output-dirif provided). - Any extras (exports,
--save-metadata,--save-full-response,--save-intermediates): create a slugified folder next to the input (or under--output-dir) named<stem>-transcribed/and put all artifacts there.
Example foldered layout when extras are enabled:
path/to/slides/
lecture01/
page-images/
Lecture01-transcribed-0.png
...
Lecture01-transcribed.md
Switching to --format latex replaces the primary artifact with Lecture01-transcribed.tex while keeping the same directory structure.
If RECAPIT_SAVE_FULL_RESPONSE (or its LECTURE_SUMMARIZER_SAVE_FULL_RESPONSE alias) is enabled, you'll also see full-response/lecture01-transcribed.txt alongside the cleaned transcript.
JSON (*.json) exports are written beside the primary transcript when you enable the export hooks.
Video inputs produce chunk-aware transcripts. With Markdown you get headings such as ## Chunk N (HH:MM:SS–HH:MM:SS) inside <stem>-transcribed.md, and with LaTeX the sections mirror the same structure inside <stem>-transcribed.tex. When the save_full_response toggle is enabled (via presets, recapit.yaml, or environment variables), every raw chunk response is also captured under full-response/chunks/. Intermediates such as normalized MP4s and chunk slices are discarded by default unless you enable save_intermediates (e.g., RECAPIT_SAVE_INTERMEDIATES=1 or LECTURE_SUMMARIZER_SAVE_INTERMEDIATES=1). Concurrency is bounded by max_video_workers so you can align ffmpeg load with your hardware budget.
Every CLI run additionally writes a JSON telemetry report (default run-summary.json). The report contains:
- Aggregate token counts (input/output/total) and request durations.
- Per-model breakdowns covering requests, tokens, and estimated cost.
- A flag noting whether any costs were estimated (e.g., when the API omits token usage and the tool infers values from video duration).
- Follow the workflow documented in CONTRIBUTING.md.
- Linting & formatting: use
python -m compileallfor quick syntax checks, and run any project-specific linters/tests added in the future. - Preferred package tooling:
uvfor dependency management,pnpmfor any JS tooling,cargo/justfor Rust integrations.
- Add resumable job metadata for long-running transcripts.
- Expose streaming progress events for upstream integrations.
- Ship optional Markdown/JSON schema validators.
Released under the GNU General Public License v3.0.