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
- Five ways to place descriptions
- Pauses, extend when needed (default): speaks in gaps in the original speech; when a description needs more time than its gap, the picture freezes just long enough for it to finish (a slightly longer video only when needed).
- 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, then use the Admin tab to check the connections and set a password.
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) |
|
ADMIN_FEATURES |
on |
on: Feedback button and Admin tab. off: neither exists, settings come from the environment only |
ADMIN_PASSWORD |
optional fixed password for the Admin tab (otherwise set one in the tab) | |
PLACEMENT_MODE |
hybrid |
hybrid (pauses, extend when needed), 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.
Choosing Gemini or this server
Describe with is on the main page, per browser tab. Google Gemini is greyed out until a key is available: the server's key (KEY_MODE=server), or the visitor's own key typed into the Gemini API key box (KEY_MODE=user). Choosing Gemini pops up a warning that video frames and text go to Google and that charges may apply, with OK / Cancel.
Admin tab
Anything that configures or could break the app lives here, not on the main page: the Whisper, Kokoro and Ollama addresses, the Ollama vision model and frames per description, the Gemini model (with a Load models button), the shared Gemini key (KEY_MODE=server), Save settings / Reset settings, a Test button for each of Whisper, Kokoro, Ollama and Gemini, and the list of feedback sent with the Feedback button.
Set a password under Admin password in the tab (stored only as a salted hash in /config), or fix one with ADMIN_PASSWORD. Until a password is set, anyone who can open the page can use Admin. The server itself reads these settings and ignores what a browser sends for them, so the lock cannot be bypassed from the page or the API.
Security and privacy
- There are no user accounts. Only the Admin tab can be password protected. If you expose AD Studio beyond your home network, set an admin password and put it behind a reverse proxy with authentication, or set
ADMIN_FEATURES=off. - 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 settings.json, the admin password hash, feedback.json and (KEY_MODE=server) the saved Gemini key. 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
on = Feedback button and an Admin tab (service addresses, vision backend and models, Gemini key, feedback list). off = neither exists; settings then come only from these variables.
- Target
- ADMIN_FEATURES
- Default
- on
- Value
- on
Optional. Locks the Admin tab. Leave blank to set or change a password inside the Admin tab instead. Strongly recommended if the app is reachable from the internet.
- Target
- ADMIN_PASSWORD
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