Automatically generate viral-ready vertical short clips from long-form gameplay footage using AI-powered scene analysis, GPU-accelerated rendering, and optional AI voiceovers.
This is an extended variant of AutoShorts by Divyaprakash Dhurandhar. It keeps the original pipeline and adds the fixes and features needed to run it on current hardware and to configure it without editing files.
AutoShorts analyzes your gameplay videos to identify the most engaging moments—action sequences, funny fails, or highlight achievements—then automatically crops, renders, and adds subtitles or AI voiceovers to create ready-to-upload short-form content.
| Area | Original | This edition |
|---|---|---|
| GPU support | CUDA 12.6 wheels | CUDA 12.8, works on Blackwell (RTX 50xx, sm_120) |
| Docker build | failed (missing pkg-config path, dead clone URL) | builds cleanly |
| Captions | rendered invisible (no fonts in the image) | visible, with word-accurate highlighting |
| Clip audio | dropped for Matroska sources | kept |
| Pauses in clips | left in | dead-air removal with motion protection |
| Caption layout | baked into the template | configurable (lines, length, position) |
| Configuration | .env by hand | web UI served from the container |
| Output | one flat folder | one folder per source video |
If this edition helps you, I'd appreciate a small donation – thank you! ☕
Please also consider supporting Divyaprakash Dhurandhar, who wrote the original AutoShorts this edition builds on – see Credits.
Here are some shorts automatically generated from gameplay footage:
| sample 1 | sample 2 | sample 3 | sample 4 |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
AutoShorts automatically adapts its editing style, captions, and voiceover personality based on the content and target language. Here are some examples generated entirely by the pipeline:
| Content | Style | Language | Video |
|---|---|---|---|
| Fortnite | Story Roast | 🇺🇸 English | Watch Part 1 |
| Indiana Jones | GenZ Slang | 🇺🇸 English | Watch Part 1 |
| Battlefield 6 | Dramatic Story | 🇯🇵 Japanese | Watch Part 1 |
| Indiana Jones | Story News | 🇨🇳 Chinese | Watch Part 1 |
| Fortnite | Story Roast | 🇪🇸 Spanish | Watch Part 1 |
| Fortnite | Story Roast | 🇷🇺 Russian | Watch Part 1 |
| Indiana Jones | Auto Gameplay | 🇧🇷 Portuguese | Watch Part 1 |
local mode with heuristic scoring (no API needed)action — Combat, kills, intense gameplay, close callsfunny — Fails, glitches, unexpected humor, comedic timingclutch — 1vX situations, comebacks, last-second winswtf — Unexpected events, "wait what?" moments, random chaosepic_fail — Embarrassing deaths, tragic blunders, game-losing mistakeshype — Celebrations, "LET'S GO" energy, peak excitementskill — Trick shots, IQ plays, advanced mechanics, impressive techniquesgaming, dramatic, funny, minimalgenz - Slang-heavy reactions ("bruh 💀", "no cap", "finna")story_news - Professional esports broadcasterstory_roast - Sarcastic roasting commentarystory_creepypasta - Horror/tension narrativestory_dramatic - Epic cinematic narrationauto - Auto-match style to detected semantic typehype, retro-gaming, neo-minimaldecord + PyTorch on GPUtorchaudio on GPU for fast RMS and spectral flux calculationdecord for stable motion estimationcupy (CUDA-accelerated NumPy) for blur and transformsAutoShorts is designed to work even when optimal components fail:
| Component | Primary | Fallback |
|---|---|---|
| Video Encoding | NVENC (GPU) | libx264 (CPU) |
| Subtitle Rendering | PyCaps (styled) | FFmpeg burn-in (basic) |
| AI Analysis | OpenAI/Gemini API | Heuristic scoring (local mode) |
| TTS Device | GPU (6GB+ VRAM) | CPU Fallback (slower) |
Everything else — Python, FFmpeg 4.4.2, the CUDA toolchain, the CUDA-enabled decord build and the fonts the caption renderer needs — is inside the image.
AutoShorts runs in Docker. The image carries a verified CUDA 12.8 stack, the CUDA-enabled decord build and the fonts the caption renderer needs — all of which are tedious to get right on bare metal.
The image is published on Docker Hub as
andyxtreme/autoshorts
(linux/amd64 — CUDA and NVENC rule out ARM).
You only need docker-compose.yml, no source code. Put it in a folder, create an empty settings file, and start:
touch .env # PowerShell: New-Item -ItemType File .env
docker compose up -d
The
.envmust exist as a file before the first start. Docker otherwise creates a directory with that name, and the container cannot write its settings.
The web UI is then available at http://<HOST-IP>:8501. gameplay/ and
generated/ are created next to the compose file on first start.
Clone this repository, then build the image under the name the compose file expects:
git clone https://github.com/andyxtreme/autoshorts.git
cd autoshorts
docker build -t andyxtreme/autoshorts:latest .
docker compose up -d
Since the image then already exists locally, Compose uses your build instead of pulling. The build compiles decord against CUDA in a separate stage, so expect it to take a while on the first run.
docker compose run --rm -e MODE=batch autoshorts
Or without Compose:
docker run --rm --gpus all --shm-size=8g \
-e MODE=batch \
-v $(pwd)/gameplay:/app/gameplay \
-v $(pwd)/generated:/app/generated \
-v $(pwd)/.env:/app/.env \
andyxtreme/autoshorts:latest
Note: GPU access (
--gpus all, or thedeploy.resourcesblock in the compose file) is essential for NVENC and CUDA acceleration.
Blackwell GPUs (RTX 50xx): these need CUDA 12.8 or newer. The image is built on a CUDA 12.8 base whose PyTorch carries
sm_120kernels — an unpinnedpip install torchresolves to a build without them and fails withCUDA error: no kernel image is available for execution on the device.
Copy .env.example to .env and configure:
cp .env.example .env
| Category | Variable | Description |
|---|---|---|
| AI Provider | AI_PROVIDER | openai, gemini, or local (heuristic-only, no API) |
VIDEO_TYPE | Content type preset (gaming, podcasts, sports, educational, etc.) used for universal clipping + caption style defaults | |
AI_ANALYSIS_ENABLED | Enable/disable AI scene analysis | |
GEMINI_DEEP_ANALYSIS | Gemini-only: upload full video for smarter scene detection (slower initial upload, better results) | |
OPENAI_MODEL | Model for analysis (e.g., gpt-5-mini) | |
AI_SCORE_WEIGHT | How much to weight AI vs heuristic (0.0-1.0) | |
| Semantic Analysis | SEMANTIC_TYPES | All 7 types analyzed: action, funny, clutch, wtf, epic_fail, hype, skill |
CANDIDATE_CLIP_COUNT | Number of clips to analyze | |
| Subtitles | ENABLE_SUBTITLES | Enable subtitle generation |
SUBTITLE_MODE | speech (Whisper), ai_captions, or none | |
CAPTION_STYLE | Gaming styles + story/genz + universal styles like podcast_quote, sports_playbyplay, educational_explainer, news_breaking, or auto | |
PYCAPS_TEMPLATE | Visual template for captions | |
| TTS Voiceover | ENABLE_TTS | Enable Qwen3-TTS voiceover |
TTS_LANGUAGE | Language code (en, zh, ja, ko, de, fr, ru, pt, es, it) | |
TTS_VOICE_DESCRIPTION | Natural language voice description (auto-generated if empty) | |
TTS_GAME_AUDIO_VOLUME | Game audio volume when TTS plays (0.0-1.0, default 0.3) | |
TTS_VOICEOVER_VOLUME | TTS voiceover volume (0.0-1.0, default 1.0) | |
| Video Output | TARGET_RATIO_W/H | Aspect ratio (default 9:16) |
SCENE_LIMIT | Max clips per source video | |
MIN/MAX_SHORT_LENGTH | Clip duration bounds (seconds) |
See .env.example for the complete list with detailed descriptions.
Which moments become clips. Applied before anything is rendered.
| Variable | UI (Settings →) | Default | Effect |
|---|---|---|---|
SCENE_LIMIT | Core Settings → Scene limit | 4 | Clips per source video. Raise it for more coverage, then discard what you do not need |
ACTION_W_AUDIO | Action Detection → Audio weight | 0.6 | How strongly loudness peaks (gunfire, shouting, impacts) drive selection |
ACTION_W_VIDEO | Action Detection → Motion weight | 0.4 | How strongly frame-to-frame motion drives it. Only the ratio matters — raise it above the audio weight to favour visually busy moments over loud ones |
| Variable | UI (Settings →) | Default | Effect |
|---|---|---|---|
CLIP_LENGTH_MODE | Clip Length → Window length | max | max always takes the longest allowed window and lets dead-air removal tighten it, so runs are reproducible. random draws a length per clip, so reruns end at different points |
MAX_SHORT_LENGTH | Clip Length → Max short length | 59 | Upper bound of the window. The detected scene may cap it earlier |
MIN_SHORT_LENGTH | Clip Length → Min short length | 15 | How much source material is selected at minimum. Also filters out shorter scenes entirely |
Cuts stretches out of a rendered clip where nothing is said and nothing happens, then stitches the rest back together. Runs after rendering and before subtitles, so captions are transcribed from the final timeline.
| Variable | UI (Settings →) | Default | Effect |
|---|---|---|---|
REMOVE_SILENCE | Dead Air → Cut out dead air | true | Master switch |
SILENCE_MIN_GAP | Dead Air → Min gap to cut | 1.0 | Minimum pause length that gets removed. 0.5 gives the hard jump-cut style, 2.0 only strips long lulls |
SILENCE_MOTION_KEEP | Dead Air → Protect motion above | 0.5 | Threshold in standard deviations above the video's average motion. High → only very busy moments are protected, so the clip follows the voice. Low → little motion already counts as protected, so the clip follows the gameplay |
SILENCE_PADDING | Dead Air → Keep around speech | 0.15 | Breathing room kept around each word so cuts do not clip syllables |
SILENCE_MIN_RESULT | Dead Air → Min length after cutting | 8.0 | Floor for the finished clip. Cutting stops here even if more dead air remains |
| Variable | UI (Settings →) | Default | Effect |
|---|---|---|---|
SUBTITLE_MAX_LINES | Caption Layout → Max caption lines | 2 | Maximum number of lines a caption wraps to |
SUBTITLE_MIN_LINES | Caption Layout → Min caption lines | 1 | Reserved lines, so captions do not jump vertically |
SUBTITLE_MAX_CHARS | Caption Layout → Max chars per caption | 15 | Where text is split into the next caption. Small values give the fast word-by-word look |
SUBTITLE_MIN_CHARS | Caption Layout → Min chars per caption | 10 | Avoids very short leftover captions |
SUBTITLE_OVERFLOW | Caption Layout → When text does not fit | exceed_lines | exceed_lines adds another line, so the line limit is only a target. exceed_width keeps the line count and lets the last line run wider |
SUBTITLE_WIDTH_RATIO | Caption Layout → Caption width ratio | 0.85 | How much of the frame width captions may use |
SUBTITLE_VERTICAL_ALIGN | Caption Layout → Vertical position | bottom | bottom, center or top |
SUBTITLE_VERTICAL_OFFSET | Caption Layout → Vertical offset | -0.1 | Nudge away from the chosen edge |
PYCAPS_KEEP_SPLITTERS | Caption Layout → Split long captions | true | Off shows a whole transcript block at once: exact SRT boundaries, but walls of text |
Four pairs are easy to confuse because they sound similar but act at different stages of the pipeline.
ACTION_W_VIDEO vs. SILENCE_MOTION_KEEP — both weigh motion, but at
opposite ends. The action weights decide where in the source clips are
looked for, before rendering. The motion-keep threshold decides what survives
inside a clip, after rendering. Gameplay-heavy shorts want a high
ACTION_W_VIDEO and a low SILENCE_MOTION_KEEP; commentary-driven shorts
want the opposite.
MIN_SHORT_LENGTH vs. SILENCE_MIN_RESULT — the first governs how much
raw material is selected, the second how short the finished clip may end up.
They are deliberately separate: select generously, cut tightly. Lowering
MIN_SHORT_LENGTH to allow shorter finals would also make the pipeline accept
thinner source windows, and it filters scenes as well.
SUBTITLE_MAX_LINES vs. SUBTITLE_OVERFLOW — the line limit alone is only
a target. With the default exceed_lines, a generous SUBTITLE_MAX_CHARS
still spills onto extra lines. Set SUBTITLE_OVERFLOW=exceed_width for a hard
line limit.
MAX_SHORT_LENGTH vs. the detected scene — the maximum is an upper bound,
not a target. If scene detection finds a 27-second scene, that caps the window
regardless of a higher MAX_SHORT_LENGTH.
Place source videos in the gameplay/ directory — either by copying
them in directly, or by uploading them in the dashboard. Files copied in
from outside appear in the queue automatically.
Start the run with Start Processing on the Generate page, or in a terminal:
docker compose run --rm -e MODE=batch autoshorts
Generated clips are saved to generated/<source video name>/
The dashboard is the container's default entrypoint and is served on
http://<HOST-IP>:8501. Every setting documented above is editable there, and
it also manages the input queue, starts jobs and previews the results.
| About | Generate | Browse |
|---|---|---|
![]() | ![]() | ![]() |
| Features | Settings | Roadmap |
|---|---|---|
![]() | ![]() | ![]() |
One folder per source video, so clips from different recordings do not interleave:
generated/
└── video_name/
├── scene-0.mp4 # Rendered short clip
├── scene-0.words.json # Whisper word-level timings
├── scene-0_sub.json # Caption layout data
├── scene-0.ffmpeg.log # Render log
├── scene-1.mp4
└── ...
Rerunning a video clears its folder first, so you always get one consistent set
of clips rather than a mix of runs. Only files the pipeline writes (scene-*)
are removed; anything else you keep in that folder is left alone.
Two source files whose names differ only by extension (
clip.mkvandclip.mp4) share one output folder and overwrite each other.
pip install ruff
ruff check .
pytest -q
Tests mock GPU availability and can run in standard CI environments.
For faster iteration during development, you can skip expensive steps using these environment variables in your .env:
| Variable | Description |
|---|---|
DEBUG_SKIP_ANALYSIS=1 | Skip AI scene analysis (uses cached/heuristic scores) |
DEBUG_SKIP_RENDER=1 | Skip video rendering (useful for testing analysis only) |
DEBUG_RENDERED_CLIPS="path1:category,path2" | Test with specific pre-rendered clips |
Example workflow for testing subtitles only:
# In .env
DEBUG_SKIP_ANALYSIS=1
DEBUG_SKIP_RENDER=1
DEBUG_RENDERED_CLIPS="generated/test_clip.mp4:action"
| Issue | Solution |
|---|---|
| "CUDA not available" | Ensure --gpus all (Docker) or CUDA toolkit is installed |
| NVENC Error | Falls back to libx264 automatically; check GPU driver |
| PyCaps fails | Falls back to FFmpeg burn-in subtitles automatically |
| Decord EOF hang | Increase DECORD_EOF_RETRY_MAX or set DECORD_SKIP_TAIL_FRAMES=300 |
| API rate limits | Switch to gpt-5-mini (10M free tokens/day) or use local provider |
We love contributions! Whether you're fixing a bug, adding a feature, or improving documentation:
This project builds upon the excellent work of:
Support the original author:
Content type
Image
Digest
sha256:3425737b9…
Size
5.3 GB
Last updated
about 1 month ago
docker pull andyxtreme/autoshorts