All apps · 0 apps
ad-studio
Docker app from Argus006's Repository
Overview
Readme
View on GitHubAD Studio
Add audio description to videos: a narrator that says what is on screen, mixed into the original soundtrack.
AD Studio watches your video with a vision model, writes short descriptions, voices them with a text-to-speech engine and mixes them into a descriptive audio track (or a longer video that pauses for them). It is made for lectures, slide decks, screen recordings and everyday videos, so people who are blind or have low vision can follow along.
It runs as one small Docker container (no GPU, no PyTorch) and talks to three services over HTTP. It is built with Unraid in mind (there is a Community Apps template) but runs anywhere Docker does.
Features
- Four ways to place descriptions
- Fit in pauses: only speaks in gaps in the original speech.
- Talk over speaker: speaks over the video and lowers the original audio underneath.
- Extended (pause the video): freezes the picture to fit longer descriptions, producing a longer video.
- Describe screen changes: detects page and slide switches and handwriting, and describes them without pausing.
- Vision backends: local Ollama (default
qwen3-vl:8b-instruct) or Google Gemini (cloud,gemini-2.5-flash). - Edit before you build: a description table you can edit, with a preview player that follows along. Rows that are too long for their gap are flagged.
- Batch mode: drop many videos, review each one's descriptions for a few seconds, and download everything as a
.zip. - Private by design: the server keeps no uploads or results. Everything lives in a per-browser-tab scratch folder that is deleted when the tab closes, on a timer, and at startup. You download your results.
- Download / load descriptions (
.json) to and from your own computer. - Settings can be saved from the UI or preset with environment variables, with Test buttons for each service.
What you need
AD Studio itself only orchestrates. You run these alongside it:
| Service | Used for | Image / project |
|---|---|---|
| Whisper | speech timing (finds the pauses) | onerahmet/openai-whisper-asr-webservice |
| Kokoro-FastAPI | text to speech | ghcr.io/remsky/kokoro-fastapi-cpu |
| Ollama or a Gemini API key | looking at the video | ollama.com / Google AI Studio |
A GPU helps Whisper and Ollama a lot but is not required by AD Studio itself. If you only use Gemini you do not need Ollama.
Install
Unraid (Community Apps)
Search for AD Studio in Community Apps and fill in the three service addresses. Put Scratch on a cache-pool path (not inside docker.img).
To install by hand before it appears in the store, add this template URL under Docker, Add Container, template:
https://raw.githubusercontent.com/Chrisv006/ad-studio/main/unraid/ad-studio.xml
Docker Compose
git clone https://github.com/Chrisv006/ad-studio.git
cd ad-studio
cp .env.example .env # edit the three service URLs
mkdir -p config scratch && sudo chown 99:100 config scratch
docker compose up -d
Open http://localhost:8585.
Docker run
docker run -d --name ad-studio --user 99:100 -p 8585:8585 \
-v /path/to/config:/config -v /path/to/scratch:/scratch \
-e WHISPER_URL=http://SERVER:9000 \
-e OLLAMA_URL=http://SERVER:11434 \
-e TTS_URL=http://SERVER:8880/v1 \
ghcr.io/chrisv006/ad-studio:latest
Both folders must be writable by UID 99 / GID 100 (Unraid's nobody:users).
Configuration
Every setting is an environment variable; blank or unset uses the built-in default. Values saved from the UI (Save settings) override the environment, and Reset settings goes back to it.
| Variable | Default | Meaning |
|---|---|---|
WHISPER_URL |
required | whisper-asr-webservice address |
OLLAMA_URL |
Ollama address | |
TTS_URL |
required | Kokoro-FastAPI address including /v1 |
VISION_BACKEND |
local |
local (Ollama) or gemini |
VISION_MODEL |
qwen3-vl:8b-instruct |
Ollama model |
GEMINI_MODEL |
gemini-2.5-flash |
Gemini model |
KEY_MODE |
user |
user: every visitor enters their own Gemini key, kept only in their browser tab. server: one shared key stored in /config |
GEMINI_API_KEY |
shared key (only with KEY_MODE=server) |
|
PLACEMENT_MODE |
pauses |
pauses, talkover, extended or changes |
MIN_PAUSE_SEC |
1.0 |
shortest silence to use |
TALK_OVER_INTERVAL_SEC |
20 |
how often to describe in talk-over / extended |
MAX_WORDS |
20 |
max words per description |
WORDS_PER_SEC |
2.5 |
speaking rate used to size descriptions |
DUCK_DB |
8 |
how far the original audio is lowered under narration |
AD_GAIN |
2 |
narration volume (dB) |
FRAMES_PER_DESC |
6 |
frames sent to the local model per description |
TTS_VOICE / TTS_SPEED |
af_heart / 1.0 |
Kokoro voice id, speed 0.8 to 3.0 |
CHANGE_SENSITIVITY |
5 |
changes mode, 1 (page switches only) to 10 |
MIN_DESC_SPACING_SEC |
4 |
changes mode, fewest seconds between descriptions |
MAX_DESC_DELAY_SEC |
8 |
changes mode, longest wait for a sentence break |
BATCH_REVIEW_SECS |
10 |
batch review window per video |
SESSION_TTL_HOURS |
6 |
delete idle sessions after this long |
SCRATCH_DIR / CONFIG_DIR |
/scratch / /config |
container paths |
Using it
- Analyze: pick a video and press Analyze. The video is transcribed, a vision model describes it, and the descriptions appear in a table.
- Edit: click a row to jump the video there, fix wording or timing, delete or add rows.
- Build: produces
descriptive.mp3, apreview.mp4and (in Extended mode) the longer video. Download them.
In Batch, drop several files, set a review time and press Analyze & build all. You can pause on any video to edit its descriptions. Closing the tab cancels the batch.
Security and privacy
- No accounts and no login. If you expose AD Studio beyond your home network, put it behind a reverse proxy with authentication.
- Public deployments should keep
KEY_MODE=userso your own Gemini key is never stored or shared. - When Gemini is the backend, video frames and transcript text are sent to Google. With Ollama everything stays on your network.
- Uploads and results exist only in
/scratchwhile your tab is open.
Development
pip install -r requirements.txt
WHISPER_URL=... TTS_URL=... OLLAMA_URL=... python -m ad_studio # needs ffmpeg on PATH
The UI is Gradio (pinned at 6.30.0); the preview window depends on Gradio's table markup, so re-check the preview popup and row highlighting when upgrading. Styles are in ad_studio/assets/app.css, the preview script in ad_studio/assets/preview.js.
License
MIT, see LICENSE.
Related apps
Explore more like this
Explore allDetails
ghcr.io/chrisv006/ad-studio:latestRuntime arguments
- Web UI
http://[IP]:[PORT:8585]/- Network
bridge- Shell
sh- Privileged
- false
- Extra Params
--user 99:100
Template configuration
Port for the web page.
- Target
- 8585
- Default
- 8585
- Value
- 8585
Holds only settings.json (and the saved Gemini key when KEY_MODE=server). Must be writable by nobody:users.
- Target
- /config
- Default
- /mnt/user/appdata/ad-studio
- Value
- /mnt/user/appdata/ad-studio
Temporary working files and uploads, one folder per browser tab, deleted when the tab closes and wiped at startup. Put it on the cache pool, not in docker.img.
- Target
- /scratch
- Default
- /mnt/cache/appdata/ad-studio-scratch
- Value
- /mnt/cache/appdata/ad-studio-scratch
Whisper (whisper-asr-webservice) address, e.g. http://SERVER-IP:9000
- Target
- WHISPER_URL
Ollama address, e.g. http://SERVER-IP:11434 (not needed if you only use Gemini)
- Target
- OLLAMA_URL
Kokoro-FastAPI address including /v1, e.g. http://SERVER-IP:8880/v1
- Target
- TTS_URL
local = Ollama, gemini = Google Gemini (cloud).
- Target
- VISION_BACKEND
- Default
- local
- Value
- local
Ollama vision model.
- Target
- VISION_MODEL
- Default
- qwen3-vl:8b-instruct
- Value
- qwen3-vl:8b-instruct
Used when the backend is Gemini.
- Target
- GEMINI_MODEL
- Default
- gemini-2.5-flash
- Value
- gemini-2.5-flash
Shared Gemini key (only used when Key mode is server).
- Target
- GEMINI_API_KEY
user = each visitor types their own Gemini key, kept only in their browser tab (safest, required if the app is exposed to the internet). server = one shared key stored on this server.
- Target
- KEY_MODE
- Default
- user
- Value
- user
Changes mode: 1 (only page switches) to 10 (almost any change). 5 catches writing and page switches.
- Target
- CHANGE_SENSITIVITY
- Default
- 5
- Value
- 5
Changes mode: fewest seconds between two descriptions (1-60).
- Target
- MIN_DESC_SPACING_SEC
- Default
- 4
- Value
- 4
Changes mode: longest a description may wait to start at the end of a sentence (0-30; 0 = at the change).
- Target
- MAX_DESC_DELAY_SEC
- Default
- 8
- Value
- 8
pauses (fit in pauses), talkover (talk over speaker), extended (pause the video) or changes (describe screen changes, no pausing).
- Target
- PLACEMENT_MODE
- Default
- pauses
- Value
- pauses
Pauses mode: shortest silence to use.
- Target
- MIN_PAUSE_SEC
- Default
- 1.0
- Value
- 1.0
Talk over / extended: about how often to describe.
- Target
- TALK_OVER_INTERVAL_SEC
- Default
- 20
- Value
- 20
Talk over / extended: maximum words per description.
- Target
- MAX_WORDS
- Default
- 20
- Value
- 20
Pauses mode: speaking rate used to size descriptions.
- Target
- WORDS_PER_SEC
- Default
- 2.5
- Value
- 2.5
How far to turn the original audio down under narration.
- Target
- DUCK_DB
- Default
- 8
- Value
- 8
Narration volume.
- Target
- AD_GAIN
- Default
- 2
- Value
- 2
Frames sent to the local vision model per description.
- Target
- FRAMES_PER_DESC
- Default
- 6
- Value
- 6
Kokoro voice id.
- Target
- TTS_VOICE
- Default
- af_heart
- Value
- af_heart
0.8 to 3.0.
- Target
- TTS_SPEED
- Default
- 1.0
- Value
- 1.0
Seconds to review each video's descriptions before it is built.
- Target
- BATCH_REVIEW_SECS
- Default
- 10
- Value
- 10
Safety net: delete sessions idle longer than this.
- Target
- SESSION_TTL_HOURS
- Default
- 6
- Value
- 6