Video Skill
Process and share videos using ffmpeg, Deepgram Nova 3, and S3/tunnel sharing.
Default: lighter video, same resolution
Always re-encode to a smaller file at the source's own resolution by default. A
compressed, same-resolution copy is almost always what the user wants — faster
uploads, lighter shares, identical viewing experience. Make this the default in
every flow (silent, interactive, and the Gemini dev-video playback encode); only
deviate when the user explicitly says otherwise. Concretely:
- Default optimization =
--resolution keep— re-encode with libx264 + CRF and no scale filter, so the resolution is unchanged and the file just gets smaller. This is the recommended Q1 choice and the silent-mode default. - Only downscale (e.g. to 1080p) when the source is materially larger than 1080p (≥1440p / 4K) or the user asks. Never upscale.
- Only skip the re-encode (use the untouched original / stream-copy) when the user explicitly asks for the original — e.g. "keep the original", "don't re-encode".
- Safety check: after encoding, compare the output size to the source. If it isn't
meaningfully smaller (rare — e.g. an already-low-bitrate source), raise CRF (26–28)
or fall back to the original. Screen recordings compress especially well: CRF 24–26
with
preset slowroutinely cuts 70–80 % while keeping on-screen text crisp. - Download buttons: the page shows a single "Download Video" button by default
(it points at the playback video). Only add a "Download Original" button — i.e.
pass
render_page.py --original-filename <name>— when you actually upload the untouched original into the output folder under that exact name; otherwise the button links to a missing file and 404s. The default compress-keep flow keeps no separate original, so do not pass--original-filename.
Prerequisites
ffmpegandffprobemust be installed- Python 3 must be available
awsCLI must be available (for S3 uploads)- Deepgram API key — for audio transcription/subtitles (default engine)
- OpenRouter API key — for the Gemini analysis engine (
dev-video/transcript-cheapmodes). Optional; only needed when those modes are selected.
Directories
- Scripts:
<skill_dir>/scripts/— all Python scripts and the player.html template - Credentials:
~/.config/video-skill/— persistent across plugin updatesdeepgram_token— single-line Deepgram API keyopenrouter_token— single-line OpenRouter API key (Gemini analysis engine)s3_credentials— key=value format (endpoint, bucket, access_key, secret_key)
- Preferences:
~/.config/video-skill/preferences.json— user choices saved across sessions
Credential Setup
If credentials are missing when needed, ask the user via AskUserQuestion:
Deepgram: Check DEEPGRAM_API_KEY env, then ~/.config/video-skill/deepgram_token file. If neither exists, ask the user and save to the file.
OpenRouter: Check OPENROUTER_TOKEN env, then ~/.config/video-skill/openrouter_token file. Only needed for dev-video / transcript-cheap analysis modes. If a Gemini mode is selected and the token is missing, ask the user and save to the file.
S3: Check env vars (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_ENDPOINT, S3_BUCKET), then ~/.config/video-skill/s3_credentials. If missing, ask the user for endpoint, bucket, access_key, secret_key and save.
Important: Never pass credentials as CLI arguments. Scripts read them from files internally via --credential-dir ~/.config/video-skill.
Preferences
{
"language": "en",
"last_folder": "/Users/example/screencasts",
"share_folder": "/Users/example/screencasts",
"sharing_method": "s3",
"optimization": "compress-keep",
"crf": 23,
"preset": "medium",
"audio": "aac-128k",
"subtitles": "track",
"subtitle_style": "modern",
"subtitle_font": "Helvetica Neue",
"target_language": "ru",
"download_button": true,
"passcode": true,
"mode": "subtitles"
}
optimization: how the playback video is encoded. Default compress-keep =
re-encode lighter at the source resolution (--resolution keep). Other values:
web-1080p (downscale to 1080p — for ≥1440p/4K sources), custom, keep-original
(no re-encode — only on explicit user request). See "Default: lighter video, same
resolution" above.
mode: a single field that decides the transcript/analysis engine. One of four
values (the old analysis_mode + developer_analysis pair collapsed into this):
mode | Engine | Subtitles | Developer analysis | Use when |
|---|---|---|---|---|
subtitles (default) | Deepgram | precise | no | normal share, just want captions |
dev-audio | Deepgram | precise | yes (from transcript text) | tool/bug feedback, want clean captions too |
dev-video | Gemini video | coarse | yes + screenshots | richest tool/UX feedback (reads the screen) |
transcript-cheap | Gemini audio | coarse | no | cheap rough transcript, timing not critical |
Internally: subtitles/dev-audio run process_and_share.py (the latter with
--developer-analysis); dev-video/transcript-cheap run the Gemini engine
(gemini_analyze.py). "Developer analysis" = the in-page analysis block plus
the Analysis Markdown artifact (see that section). Engine guidance (research
2026-06): keep Deepgram for precise subtitles; use Gemini-video for
analysis, Gemini-audio for cheap rough transcripts. For dev-video where
precise captions also matter, additionally run a Deepgram pass for the track.
Saving: After every interactive choice, update preferences.json. Always save last_folder after every run.
Loading: Read at start. Use saved values as "(Recommended)" defaults. In silent mode, use directly.
Scripts Reference
All scripts are in <skill_dir>/scripts/. They accept --credential-dir for credentials and print [PROGRESS] lines for status tracking.
| Script | Purpose | Key Args |
|---|---|---|
process_and_share.py | Main workflow: optimize + transcribe + render + upload | <video> --output-dir --share-folder --share s3|tunnel |
share_existing.py | Share a pre-processed folder | <folder> --share-folder --share s3|tunnel |
upload_s3.py | Upload folder to S3 | <folder> --key <key> --credential-dir |
render_page.py | Generate index.html from template | --output-dir --template --metadata |
manage_registry.py | Registry CRUD (add/remove/list/get/migrate) | <subcommand> --share-folder |
partial_update.py | Re-render + re-upload a shared folder (resolves key from registry, forwards passcode) | <output_folder> [--key] |
font_name.py | Print a TTF's internal family name (for ASS Fontname) | <font.ttf> |
transcribe.py | Deepgram transcription → SRT + VTT | <video> --credential-dir [--language] |
gemini_analyze.py | Gemini engine: audio transcript OR video analysis + screenshots | <video> --mode audio|video --output-dir --credential-dir [--key] |
burn_subtitles.py | Burn subtitles into video | <video> <srt> [--font --fontsize ...] |
share_server.py | Local HTTP server for tunnel sharing | <share_root> [--port] |
Workflow
Step 0: Parse Arguments & Detect Mode
Commands:
/video→ Interactive mode (Steps 1-5)/video process <path>→ Interactive mode for specific file/video share→ Silent mode: process latest + share/video share <path>→ Silent mode: process specific file + share/video share <path> "context"→ Silent mode with extra context for metadata/video start→ Start sharing server + tunnel/video stop→ Stop sharing/video status→ List shared videos/video copy <key_or_name>→ Copy link + passcode to clipboard/video remove <key_or_name>→ Remove from registry
Sharing Commands (handled directly, no processing)
Share registry: <share_folder>/.share_registry.json — managed by manage_registry.py.
start command:
- Read preferences →
share_folder. If not set, ask. - Check
pgrep -f share_server.py. If running, show status. - Start server:
python3 "<scripts>/share_server.py" "<share_folder>" --port 8080 & - Start tunnel:
ssh -p 443 -R0:localhost:<port> -o StrictHostKeyChecking=no a.pinggy.io 2>&1 & - Parse tunnel URL. Show: "Server running at <base_url>"
- List existing videos with full URLs (
<base_url>/v/<key>).
stop command:
pkill -f share_server.py; pkill -f "ssh.*pinggy"; pkill -f "ngrok http"
status command:
- Run
python3 "<scripts>/manage_registry.py" list --share-folder "<share_folder>" - Check
pgrep -f share_server.pyfor tunnel status. - Display table: key, title, S3 URL, tunnel URL, passcode, created.
copy command:
- Run
python3 "<scripts>/manage_registry.py" get --share-folder "<share_folder>" --key "<query>" - Format:
<title>\n<url>\nPasscode: <code>→pbcopy
remove command:
Run python3 "<scripts>/manage_registry.py" remove --share-folder "<share_folder>" --key "<query>"
Silent Mode (/video share)
One confirmation, one script execution, minimal interaction.
Flow:
-
Identify video (argument path or newest in
last_folder/ CWD) -
Create output folder
<share_folder>/<video_name>/ -
Generate passcode (random 6-digit or saved preference)
-
Show confirmation:
Quick share: demo.mov → same resolution · compressed (CRF 23) · AAC 128k → Transcribe + subtitles (track) → Share via S3 (permanent link) → Passcode: 482910 Proceed?Options: Proceed (Recommended) / Switch to interactive mode
If
modeisdev-videoortranscript-cheap, do not runprocess_and_share.py— follow the "Gemini analysis engine" section instead (silently, using saved preferences), then still upload + register the share. -
Run the main workflow script. Pass
--developer-analysiswhenmodeisdev-audio:python3 "<scripts>/process_and_share.py" "<video_path>" \ --output-dir "<output_folder>" \ --share-folder "<share_folder>" \ --credential-dir ~/.config/video-skill \ --resolution keep --crf 23 --preset medium --audio aac-128k \ --subtitles track \ --share s3 \ --passcode "<passcode>" \ [--developer-analysis] \ [--context "<user_context>"]Default
--resolution keep(compress, same resolution). Use--resolution 1080ponly if the source is ≥1440p/4K, orkeep-originalhandling if the user explicitly wants the untouched file. -
Monitor stdout for
METADATA_READY:— when the script prints this marker: a. Read theTRANSCRIPT_PREVIEW:andMETADATA_INFO:that preceded it. b. Generate title, description, and 4-8 chapters from the transcript preview. c. Ifmodeisdev-audio(developer analysis on): build the analysis block and the Analysis Markdown from the full transcript (read the SRT/VTT in the output dir, not the truncated preview). See the "metadata.json reference" and "Analysis Markdown" sections for the exact shape and contract. Match the page language (RU / DE / EN). d. Writemetadata.jsonto the output dir per the metadata.json reference. e. The script detects metadata.json and continues automatically. -
Script finishes. Display result, l
…(truncated)…