| name | narrator-ai-video-generation |
| description | Generate AI-narrated video content using narrator-ai-cli for movie commentary, short dramas, and film analysis |
| triggers | ["create a movie narration video","generate film commentary with narrator-ai","make a video narration for this movie","produce AI-narrated content","create short drama narration","generate video commentary using narrator-ai-cli","help me make a narrated movie clip","create narration video with custom voice"] |
Narrator AI Video Generation Skill
Skill by ara.so — Devtools Skills collection.
This skill enables AI agents to create professional movie narration videos using the narrator-ai-cli tool. The CLI provides access to ~100 movies, 146 BGM tracks, 63 dubbing voices, and 90+ narration templates for automated video production.
What This Tool Does
narrator-ai-cli is a command-line interface for the Narrator AI service that automates the creation of movie narration videos. It handles:
- Adapted Narration: Select existing movie content and generate narration scripts
- Original Narration: Create narration from custom scripts and material
- Voice Synthesis: Text-to-speech with 63+ voice options
- Voice Cloning: Clone custom voices for narration
- Video Composition: Automatic video assembly with BGM, dubbing, and visual templates
Installation
Prerequisites
Install CLI
pip install "narrator-ai-cli @ git+https://github.com/NarratorAI-Studio/narrator-ai-cli.git"
narrator-ai-cli --version
Configure API Key
narrator-ai-cli config set app_key $NARRATOR_APP_KEY
narrator-ai-cli config show
Environment variable setup:
export NARRATOR_APP_KEY="your-api-key-here"
Core Commands
Configuration
narrator-ai-cli config show
narrator-ai-cli config set app_key <key>
narrator-ai-cli config set base_url <url>
Resource Discovery
narrator-ai-cli movie list
narrator-ai-cli movie list --keyword "Shawshank"
narrator-ai-cli template list
narrator-ai-cli bgm list --keyword "epic"
narrator-ai-cli dubbing list --gender male --language zh-CN
Adapted Narration Workflow (Standard Path)
Use when working with existing movie content:
narrator-ai-cli file upload /path/to/reference.mp4
narrator-ai-cli movie-select create \
--movie-id <movie_id> \
--template-code <template_code> \
--reference-file-id <file_id>
narrator-ai-cli movie-select get <task_id>
narrator-ai-cli narration-script create \
--clip-data-file-id <clip_data_file_id> \
--template-code <template_code> \
--requirement "Make it funny and engaging"
narrator-ai-cli narration-script get <task_id>
narrator-ai-cli magic-video create \
--clip-data-file-id <clip_data_file_id> \
--narration-script-file-id <script_file_id> \
--bgm-code <bgm_code> \
--dubbing-code <dubbing_code>
narrator-ai-cli magic-video get <task_order_num>
Original Narration Workflow (Fast Path)
Use when creating from scratch with custom script:
narrator-ai-cli file upload /path/to/video.mp4
narrator-ai-cli narration-script create \
--template-code <template_code> \
--requirement "Create a 60-second comedy narration"
narrator-ai-cli narration-script get <task_id>
narrator-ai-cli magic-video create \
--video-file-id <uploaded_file_id> \
--narration-script-file-id <script_file_id> \
--bgm-code <bgm_code> \
--dubbing-code <dubbing_code>
narrator-ai-cli magic-video get <task_order_num>
Standalone Features
narrator-ai-cli tts create \
--text "Hello world" \
--dubbing-code <dubbing_code>
narrator-ai-cli tts get <task_id>
narrator-ai-cli voice-clone create \
--audio-file-id <audio_file_id> \
--voice-name "MyCustomVoice"
narrator-ai-cli voice-clone get <task_id>
Common Patterns
Pattern 1: Quick Movie Narration
import subprocess
import json
import time
def create_quick_narration(movie_name, style="comedy"):
result = subprocess.run(
["narrator-ai-cli", "movie", "list", "--keyword", movie_name],
capture_output=True, text=True
)
movies = json.loads(result.stdout)
movie_id = movies[0]["id"]
result = subprocess.run(
["narrator-ai-cli", "template", "list"],
capture_output=True, text=True
)
templates = json.loads(result.stdout)
template = next(t for t in templates if style in t["name"].lower())
result = subprocess.run(
["narrator-ai-cli", "movie-select", "create",
"--movie-id", movie_id,
"--template-code", template["code"]],
capture_output=True, text=True
)
task = json.loads(result.stdout)
while True:
result = subprocess.run(
["narrator-ai-cli", "movie-select", "get", task["task_id"]],
capture_output=, text=
)
status = json.loads(result.stdout)
status[] == :
status[]
time.sleep()
Pattern 2: Batch Video Creation
def batch_create_narrations(movie_ids, template_code, bgm_code, dubbing_code):
tasks = []
for movie_id in movie_ids:
result = subprocess.run([
"narrator-ai-cli", "movie-select", "create",
"--movie-id", movie_id,
"--template-code", template_code
], capture_output=True, text=True)
task = json.loads(result.stdout)
tasks.append(task)
clip_data_ids = []
for task in tasks:
while True:
result = subprocess.run([
"narrator-ai-cli", "movie-select", "get",
task["task_id"]
], capture_output=True, text=True)
status = json.loads(result.stdout)
if status["status"] == "SUCCESS":
clip_data_ids.append(status["clip_data_file_id"])
break
elif status["status"] == "FAILED":
print(f"Task {task['task_id']} failed")
break
time.sleep(5)
return clip_data_ids
Pattern 3: Custom Voice Narration
def create_with_custom_voice(audio_path, script_text, video_path):
result = subprocess.run([
"narrator-ai-cli", "file", "upload", audio_path
], capture_output=True, text=True)
audio_file = json.loads(result.stdout)
result = subprocess.run([
"narrator-ai-cli", "voice-clone", "create",
"--audio-file-id", audio_file["file_id"],
"--voice-name", "CustomVoice"
], capture_output=True, text=True)
clone_task = json.loads(result.stdout)
while True:
result = subprocess.run([
"narrator-ai-cli", "voice-clone", "get",
clone_task["task_id"]
], capture_output=True, text=True)
status = json.loads(result.stdout)
if status["status"] == "SUCCESS":
dubbing_code = status["dubbing_code"]
break
time.sleep(5)
result = subprocess.run([
"narrator-ai-cli", "file", "upload", video_path
], capture_output=True, text=True)
video_file = json.loads(result.stdout)
Key Concepts
File IDs and Task IDs
- file_id: Identifier for uploaded files (videos, audio, scripts)
- task_id: Identifier for async tasks (movie-select, narration-script, etc.)
- task_order_num: Identifier for magic-video composition tasks
- clip_data_file_id: File containing selected movie clips and metadata
- narration_script_file_id: File containing generated narration script
Task Status Flow
- PENDING: Task created, waiting to start
- PROCESSING: Task is being executed
- SUCCESS: Task completed successfully
- FAILED: Task failed (check error message)
Always poll tasks until status is SUCCESS or FAILED.
Workflow Decision Tree
User Request
├─ Has specific movie?
│ ├─ Yes → Adapted Narration (Standard Path)
│ │ └─ movie-select → narration-script → magic-video
│ └─ No → Original Narration (Fast Path)
│ └─ narration-script → magic-video
│
└─ Just voice/audio task?
└─ voice-clone OR tts
Configuration Options
Template Codes
Templates define narration style. Common categories:
- Comedy/Humor templates
- Dramatic/Serious templates
- Action/Thriller templates
- Romance/Drama templates
Use narrator-ai-cli template list to see all available templates with codes.
BGM Codes
Background music options include:
- Epic/cinematic tracks
- Emotional/romantic music
- Suspenseful/thriller music
- Light/comedy tracks
Use narrator-ai-cli bgm list to browse by keyword or mood.
Dubbing Codes
Voice options include:
- Gender: male, female, neutral
- Language: zh-CN, en-US, etc.
- Age: young, middle-aged, elderly
- Style: professional, emotional, energetic
Use narrator-ai-cli dubbing list --gender <gender> --language <lang> to filter.
Error Handling
Common Error Codes
ERROR_CODES = {
4000: "Invalid parameters",
4001: "Authentication failed - check API key",
4003: "Insufficient credits",
4004: "Resource not found",
5000: "Server error - retry later",
5001: "Task processing failed"
}
def handle_error(error_code, error_msg):
if error_code == 4001:
print("Check NARRATOR_APP_KEY environment variable")
elif error_code == 4003:
print("Contact support for credit top-up")
elif error_code in [5000, 5001]:
print("Temporary error, retry in 30 seconds")
else:
print(f"Error {error_code}: {error_msg}")
Polling Best Practices
def poll_task(task_id, command, max_retries=60, interval=5):
"""Poll task with exponential backoff"""
for attempt in range(max_retries):
result = subprocess.run(
["narrator-ai-cli", command, "get", task_id],
capture_output=True, text=True
)
if result.returncode != 0:
print(f"Command failed: {result.stderr}")
return None
status = json.loads(result.stdout)
if status["status"] == "SUCCESS":
return status
elif status["status"] == "FAILED":
print(f"Task failed: {status.get('error_msg')}")
return None
wait_time = min(interval * (1.5 ** (attempt // 10)), 30)
time.sleep(wait_time)
print("Task timed out")
return None
Agent Rules
When using this skill, AI agents MUST:
- Confirm before execution: Show the user what will be created (movie, template, voice) before running commands
- Poll asynchronously: Always poll task status until SUCCESS/FAILED
- Resource selection order:
- Ask user for preferences first
- Search resources by keyword
- Present top 3 options
- Use user selection or default to first result
- Cost awareness: Warn if creating multiple videos (each costs credits)
- Error recovery: If task fails, explain error and suggest alternatives
- File management: Track file_ids and task_ids throughout conversation
- Language chain: Match dubbing language to script language
Troubleshooting
"Authentication failed"
narrator-ai-cli config show
narrator-ai-cli config set app_key $NARRATOR_APP_KEY
"Task stuck in PROCESSING"
- Normal processing time: 2-10 minutes for video composition
- If >15 minutes: Contact support with task_id
- Retry: Create new task, don't retry same task_id
"Invalid movie_id"
narrator-ai-cli movie list --keyword "exact movie name"
"File upload failed"
ls -lh /path/to/file
file /path/to/file
narrator-ai-cli file upload "$(pwd)/video.mp4"
Rate Limiting
- API has rate limits per API key
- If hit: Wait 60 seconds before retry
- For batch operations: Add 2-3 second delays between requests
Output Files
All outputs are temporary URLs (valid 24-48 hours):
import requests
def download_result(url, output_path):
response = requests.get(url, stream=True)
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Saved to {output_path}")
Resources