Prompt-to-video assembly service powered by Gemini, packaged as an installable server.
Project description
Prompt-to-Video Flask Service
An advanced AI-powered video generation service that transforms text prompts into complete videos with:
- Intelligent scene planning using Google Gemini
- AI-powered image generation (Gemini Imagen 3 or Hugging Face)
- Professional narration with Google Text-to-Speech
- Rich multimedia audio including background music and per-scene sound effects
- Interactive animated subtitles with varied effects
- Scene break transitions with titles and transition audio
- Professional video assembly with MoviePy/ffmpeg
Features
๐ฌ Scene Planning & Narration
- Gemini AI breaks your prompt into optimized scenes
- Each scene includes:
- Title for context
- 2-3 sentence narration (converted to speech)
- Visual prompt for image generation
- Duration calculated to match total target time
- Music mood keywords (for background music)
- SFX keywords (for scene-specific sound effects)
๐จ Visual Generation
- Gemini Imagen 3 or Hugging Face image generation
- Supports both Pixabay stock images/videos and AI-generated visuals
- Automatic image caching by hash to reduce API costs
- Responsive layout for horizontal (16:9) and vertical (9:16) aspect ratios
๐ Multi-Layer Audio System
Background Music:
- Intelligent Freesound API integration
- Uses Gemini-provided mood keywords (e.g., "inspiring, cinematic, light")
- Automatically loops to match video duration
- 30% volume blending with main audio
Per-Scene Sound Effects:
- Context-aware SFX for each scene from Freesound
- Gemini generates specific keywords (e.g., "wind howling", "door opening")
- 40% volume to enhance narration without overwhelming it
Narration:
- Google Cloud Text-to-Speech with customizable voices
- Full-volume primary audio track
Scene Break Audio:
- Transition sounds (e.g., whoosh effects) between scenes
- 50% volume for subtle separation
๐ Interactive Animated Subtitles
- Varied animations that cycle through 4 different effects:
- Pop-in with scale (0.5 โ 1.0)
- Fade-in (0.3 โ 1.0 opacity)
- Bounce effect (0.7 โ 1.2 scale)
- Zoom and fade (0.6 โ 1.0 scale)
- Dynamic font sizing based on aspect ratio:
- Horizontal (16:9): 65px base, alternating between 84px (emphasized) and 55px (supporting)
- Vertical (9:16): 45px base, alternating between 58px and 38px
- Paced text segments to reduce visual crowding
- Customizable styling: font, color, stroke width/color
๐๏ธ Scene Breaks & Transitions
- 2.5-second break clips inserted between scenes
- Features:
- Scene image as background
- Large scene title overlay with fade effects
- Transition sound effects (e.g., whoosh)
- Smooth visual separation between content segments
๐ฅ Video Assembly & Effects
- Ken Burns effect (subtle zoom throughout scenes)
- Crossfade transitions between clips (configurable duration)
- Professional encoding: H.264 video + AAC audio at 24 fps
- Multi-audio compositing: Perfectly mixed narration + SFX + background music
- Responsive dimensions: Automatic scaling for different aspect ratios
Quickstart
Install from source (pip)
pip install .
# or: pip install git+https://github.com/<your-org>/gemini-video-assemble.git
# Launch server (loads ~/.gemini_video_assemble/config.json overrides + env vars)
gemini-video-server --host 0.0.0.0 --port 5000
# or:
python -m gemini_video_assemble --host 0.0.0.0 --port 5000
# Optional: purge cached data/config and renders
gemini-video-server --purge-data
Open http://localhost:5000 to use the UI. A Runtime Config panel in the browser lets you set API keys and defaults without restarting. Values plus run history are saved to SQLite at ~/.gemini_video_assemble/data.db (safe to delete with --purge-data).
1. System Requirements
# macOS
brew install python@3.12 ffmpeg
# Linux (Ubuntu/Debian)
sudo apt-get install python3.12 python3.12-venv ffmpeg
# Or use pyenv (recommended)
pyenv install 3.12.0 --skip-existing
pyenv virtualenv 3.12.0 videogen
pyenv local 3.12.0
2. Setup
# Clone and navigate
cd /path/to/VisaGuidanceBD-NB
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # macOS/Linux
# or: .venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
3. Environment Variables
Create a .env file with:
# Required APIs
GOOGLE_API_KEY=your_google_api_key_here
FREESOUND_KEY=your_freesound_api_key_here
PIXABAY_KEY=your_pixabay_api_key_here # Optional, for stock images
# Model Configuration
GEMINI_TEXT_MODEL=gemini-2.5-flash
GEMINI_IMAGE_MODEL=gemini-2.5-flash-image
# Text-to-Speech
TTS_LANG=en
TTS_VOICE=en-US-JennyNeural
# Video Assembly
CROSSFADE_SEC=0.6 # Crossfade duration between clips
KENBURNS_ZOOM=0.04 # Subtle zoom effect strength
SUBTITLES_ENABLED=1 # Enable/disable subtitles
# Subtitle Styling
SUBTITLE_FONT=Arial.ttf
SUBTITLE_FONTSIZE=60
SUBTITLE_COLOR=white
SUBTITLE_STROKE_COLOR=black
SUBTITLE_STROKE_WIDTH=1
# Output
OUTPUT_DIR=./renders
4. Run the Server
python app.py
Server runs on http://localhost:5000
Optional: With Gunicorn
pip install gunicorn
gunicorn -b 0.0.0.0:5000 app:app
Optional: Docker
# Build
docker build -t prompt-video .
# Run
docker run --rm -p 5000:5000 --env-file .env prompt-video
API Usage
Generate Video
curl -X POST http://localhost:5000/api/render \
-H "Content-Type: application/json" \
-d '{
"prompt": "The history of the Silk Road trade routes and their impact on civilizations",
"duration": 120,
"scenes": 4,
"aspect": "horizontal",
"image_provider": "gemini"
}'
Response:
{
"status": "ok",
"path": "renders/a1b2c3d4-e5f6-7890-abcd-ef1234567890.mp4"
}
Download Video
curl http://localhost:5000/api/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890.mp4 -o video.mp4
Runtime Config API
Configure keys and defaults without restarting:
# Read current effective config (secrets masked)
curl http://localhost:5000/api/config
# Update keys (persisted to ~/.gemini_video_assemble/config.json)
curl -X POST http://localhost:5000/api/config \
-H "Content-Type: application/json" \
-d '{"GOOGLE_API_KEY": "your-key", "FREESOUND_KEY": "..." }'
Run History API
SQLite-backed history and caching is enabled by default (stored at ~/.gemini_video_assemble/data.db):
curl http://localhost:5000/api/runs
# returns the latest runs with ids, status, and output paths (if finished)
Health Check
curl http://localhost:5000/health
Web UI
Navigate to http://localhost:5000/ for an interactive form to generate videos without using curl.
Example Video Flow
When you submit this prompt:
"The journey of a coffee bean from farm to cup in Ethiopia"
The system:
-
Plans scenes with Gemini:
{ "scenes": [ { "title": "Farm Harvest", "narration": "In the highlands of Ethiopia, coffee farmers carefully pick ripe cherries by hand...", "visual_prompt": "Ethiopian coffee farmers harvesting red coffee cherries on misty mountain slopes", "duration_sec": 20, "search_terms": "coffee harvest Ethiopia farm", "music_keywords": "earthy, natural, ambient, traditional", "sfx_keywords": "birds chirping, rustling leaves" }, { "title": "Processing", "narration": "The harvested cherries are transported to processing facilities where they undergo fermentation...", "visual_prompt": "Traditional coffee processing with fermentation tanks in Ethiopian facility", "duration_sec": 20, "search_terms": "coffee processing fermentation", "music_keywords": "rhythmic, industrial, energetic", "sfx_keywords": "water flowing, machinery grinding" } ] }
-
Generates assets:
- Images via Gemini Imagen 3
- Narration via Google TTS
- Background music via Freesound (using "earthy, natural, ambient")
- Scene SFX via Freesound (e.g., "birds chirping", "water flowing")
- Transition audio for breaks (whoosh effects)
-
Assembles video with:
Timeline (2-minute video example): [Scene 1: Farm Harvest] - 20s โโ Video: AI-generated farm image with Ken Burns zoom โโ Audio Layer 1: Narration (100% volume) โโ Audio Layer 2: "birds chirping" SFX (40% volume) โโ Audio Layer 3: "earthy, natural" background music (30% volume) โโ Subtitles: Animated narration with pop-in effect [BREAK 1: "Farm Harvest" Title] - 2.5s โโ Visual: Farm image with large title overlay โโ Audio: "whoosh" transition sound (50% volume) โโ Fade effects on title [Scene 2: Processing] - 20s โโ Video: Processing facility image โโ Audio Layer 1: Narration (100%) โโ Audio Layer 2: "water flowing, machinery" SFX (40%) โโ Audio Layer 3: Background music continues (30%) โโ Subtitles: Animated with fade-in effect [BREAK 2: "Processing" Title] - 2.5s ... and so on Throughout entire video: โโ Background music: "earthy, natural, ambient" track auto-looped to match 2-min duration -
Outputs: Complete MP4 video with all audio perfectly mixed
Audio Mixing Details
The final video combines multiple audio tracks at optimized volumes:
Per-Scene Audio Mix:
โโ Narration (100% baseline)
โ โโ CompositeAudioClip with SFX
โ โโ Narration (100%)
โ โโ Scene SFX (40% volume)
โ
โโ Global Background Music (30% volume)
โโ Auto-loops to match video duration
โโ Composited over entire final video
โโ Never clips despite multiple tracks
Break Audio Mix:
โโ Transition SFX (50% volume)
โโ (Optional background music continues)
โโ Silent periods for dramatic effect
Result: Professional, balanced mix where:
- Narration is always primary (heard clearly)
- SFX enhances without drowning narration
- Background music sets mood without competition
- All tracks play simultaneously without clipping
Configuration
All settings are loaded from .env:
# video_app/config.py
class Settings:
# API Keys
google_api_key: str # Google Gemini + TTS
freesound_key: str # Background music + SFX
pixabay_key: str # Stock images/videos
# Models
gemini_text_model: str # "gemini-2.5-flash"
gemini_image_model: str # "gemini-2.5-flash-image"
# Text-to-Speech
tts_lang: str # "en"
tts_voice: str # "en-US-JennyNeural"
# Video Assembly
crossfade_sec: float # 0.6
kenburns_zoom: float # 0.04
enable_subtitles: bool # True
# Subtitle Styling
subtitle_font: str # "Arial.ttf"
subtitle_fontsize: int # 60
subtitle_color: str # "white"
subtitle_stroke_color: str # "black"
subtitle_stroke_width: int # 1
# Output
output_dir: Path # "./renders"
default_aspect: str # "horizontal"
default_image_provider: str # "gemini"
Project Structure
VisaGuidanceBD-NB/
โโโ app.py # Flask WSGI entry point
โโโ requirements.txt # Python dependencies
โโโ Dockerfile # Docker configuration
โโโ .env # Environment variables (not in git)
โโโ README.md # Original documentation
โโโ renders/ # Output video directory
โโโ image-cache/ # Cached images by hash
โโโ video_app/
โโโ __init__.py
โโโ config.py # Settings & environment loading
โโโ models.py # Scene dataclass with all metadata
โโโ planner.py # Gemini scene planning
โโโ images.py # Image generation (Gemini + Pixabay)
โโโ media.py # Video fetching (Pixabay)
โโโ tts.py # Text-to-Speech (Google Cloud)
โโโ music.py # Audio fetching (Freesound)
โโโ assembler.py # Video assembly, effects, audio mixing
โโโ pipeline.py # Orchestration workflow
โโโ server.py # Flask routes
โโโ templates/
โโโ index.html # Web UI form
Key Technologies
- Python 3.12+ - Core language
- Flask - Web framework
- Google Gemini API - Scene planning & image generation
- Google Cloud Text-to-Speech - Professional narration
- Freesound API - Background music & sound effects
- MoviePy 1.0+ - Video composition, effects, audio mixing
- FFmpeg - Audio/video encoding and processing
- Pixabay API (optional) - Stock images/videos
Performance Notes
- First run: ~2-3 minutes per video (API calls + generation)
- Cached runs: ~1-2 minutes (images cached, reused for similar scenes)
- Output size: Typically 1-2 GB for 2-3 minute videos
- Typical costs (with free tier APIs): ~$0.05-0.15 per 2-minute video
- Audio processing: <10 seconds for multi-track mixing regardless of video length
Troubleshooting
Background music not playing
- Check
FREESOUND_KEYis set in.env - Verify Freesound account has API credits
- Check console logs for "Searching background music" message
- Fallback searches simplified keywords (first word only) if complex query fails
- Try with simpler music keywords in Gemini prompt
SFX not generating
- Ensure
FREESOUND_KEYis valid - Check internet connection
- Review planner output for
sfx_keywordsin scene JSON - Freesound may not have audio matching keywords (check logs)
Subtitles not appearing
- Check
SUBTITLES_ENABLED=1in.env - Verify font file exists (e.g.,
Arial.ttfon system) - Check text is included in scene narration
- Try with default font if custom font fails
Image generation failing
- Verify
GOOGLE_API_KEYhas Imagen API access - If using Pixabay, check
PIXABAY_KEYis set and valid - Check API quota limits in Google Cloud Console
- Ensure you have available credits
Video assembly slow
- Reduce scene count or video duration
- Lower
KENBURNS_ZOOMto 0 to skip complex transformations - Use stock images (
image_provider: stock) instead of AI generation - Check system memory and CPU availability
Audio mixing issues
- Check all audio files were generated (check temp directory)
- Verify Freesound audio files downloaded successfully
- Try reducing
background_musicvolume if clipping occurs - Ensure enough disk space for temp audio files
API Keys Setup
Google API Key
- Go to Google Cloud Console
- Create a new project
- Enable APIs:
- Gemini API
- Imagen API
- Cloud Text-to-Speech API
- Create API key (Credentials โ Create Credentials โ API Key)
- Add to
.envasGOOGLE_API_KEY=your_key
Freesound API Key
- Sign up at Freesound.org
- Go to Settings โ API Access
- Create an API token
- Add to
.envasFREESOUND_KEY=your_key
Pixabay API Key (Optional)
- Sign up at Pixabay.com
- Go to API โ Dashboard
- Copy your API key
- Add to
.envasPIXABAY_KEY=your_key
Notes & Best Practices
- Prompts: Keep concise (50-100 words) for faster processing and lower costs
- Duration: 60-180 seconds recommended for balanced results
- Scenes: 3-6 scenes optimal
- Fewer scenes = longer per scene
- More scenes = shorter per scene + more breaks
- Aspect Ratio:
- 16:9 (horizontal) for YouTube, website
- 9:16 (vertical) for TikTok, Instagram Reels
- Caching: Identical visual prompts reuse cached images - great for iterative refinement
- Audio:
- All audio tracks are perfectly synchronized by MoviePy
- Freesound previews (~30-45 seconds) are used for music/SFX
- Background music auto-loops without gaps
- Volume levels optimized to prevent clipping
Example Workflows
Workflow 1: Educational Content
curl -X POST http://localhost:5000/api/render \
-H "Content-Type: application/json" \
-d '{
"prompt": "World War II causes: Economic factors, Rise of fascism, Treaty of Versailles, Militarism",
"duration": 300,
"scenes": 6,
"aspect": "horizontal",
"image_provider": "gemini"
}'
Result: 5-minute educational video with historical images, narrator explains causes, dramatic background music, scene breaks between topics.
Workflow 2: Travel Vlog
curl -X POST http://localhost:5000/api/render \
-H "Content-Type: application/json" \
-d '{
"prompt": "Tokyo travel guide: Shibuya crossing, Senso-ji temple, Tsukiji market, Mount Fuji views",
"duration": 120,
"scenes": 4,
"aspect": "vertical",
"image_provider": "stock"
}'
Result: 2-minute vertical video (TikTok ready) with Pixabay stock footage, travel descriptions, Asian-inspired music, scene breaks.
Workflow 3: Product Marketing
curl -X POST http://localhost:5000/api/render \
-H "Content-Type: application/json" \
-d '{
"prompt": "Product launch video for eco-friendly water bottle: sustainable materials, keeps drinks cold 24 hours, stylish design, available in 5 colors",
"duration": 90,
"scenes": 3,
"aspect": "horizontal",
"image_provider": "gemini"
}'
Result: Professional product video with AI-generated product images, marketing copy, upbeat music, clean scene breaks.
Contributing
To add new features:
- Modify relevant module in
video_app/ - Update pipeline orchestration in
pipeline.pyif needed - Test with
/api/renderendpoint - Update README with new settings/examples
- Submit pull request with description
Common Customizations
Change subtitle animation effects
Edit _apply_subtitle_effect() in video_app/assembler.py - modify effects array (currently 4 effects).
Adjust audio volumes
In video_app/assembler.py:
- Line ~220: SFX volume (currently 40%)
- Line ~330: Background music volume (currently 30%)
- Line ~240: Break audio volume (currently 50%)
Modify break clip duration
In video_app/pipeline.py, change break_duration=2.5 to desired seconds.
Change Ken Burns zoom strength
In .env: KENBURNS_ZOOM=0.04 (higher = more dramatic zoom)
Customize subtitle text styling
In .env:
SUBTITLE_FONT=Arial.ttfSUBTITLE_COLOR=whiteSUBTITLE_STROKE_COLOR=blackSUBTITLE_STROKE_WIDTH=1
License
See LICENSE
Support
For issues or questions:
- Check console logs for error messages
- Review troubleshooting section above
- Verify all
.envvariables are set correctly - Check API key quotas and permissions
- Ensure ffmpeg is installed and on PATH
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gemini_video_assemble-0.1.1.tar.gz.
File metadata
- Download URL: gemini_video_assemble-0.1.1.tar.gz
- Upload date:
- Size: 39.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d300bf714ad45fd9975e6401ab5b3e94b22415efc76e2f4cfda22ad2acf08ca9
|
|
| MD5 |
aecbc7c833dbd5657c7ee72f0f6efe79
|
|
| BLAKE2b-256 |
b91aa0c14a428235b8725cd65390c6a387fc99c286ae3eceddea58e037c52d09
|
File details
Details for the file gemini_video_assemble-0.1.1-py3-none-any.whl.
File metadata
- Download URL: gemini_video_assemble-0.1.1-py3-none-any.whl
- Upload date:
- Size: 45.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
652ea94b1e2955a007ccf3523f23685d45fa61f77e100225e2e6488fb7469b1e
|
|
| MD5 |
87b29a11f5391d5777a712e8fcb883e3
|
|
| BLAKE2b-256 |
e3e94ac81a6163b6134392d495f7645ed3f49da84cc0ea98fd1541ee7d738cb4
|