Install
openclaw skills install explainer-videoCreate explainer videos with narration and AI-generated visuals. Triggers on: "解说视频", "explainer video", "explain this as a video", "tutorial video", "introd...
openclaw skills install explainer-video/speech or /podcast)/podcast)/image-gen)/speech)Generate explainer videos that combine a single narrator's voiceover with AI-generated visuals. Ideal for product introductions, concept explanations, and tutorials. Supports text-only script generation or full text + video output.
shared/authentication.md for API key and headersshared/common-patterns.md for polling, errors, and interaction patternsshared/config-pattern.md before any interaction~/Downloads/ — use .listenhub/explainer/ from configinfo (for Info style) or story (for Story style) — never slides (use /slides skill instead)Follow shared/config-pattern.md § API Key Check. If the key is missing, stop immediately.
Follow shared/config-pattern.md Step 0.
If file doesn't exist — ask location, then create immediately:
mkdir -p ".listenhub/explainer"
echo '{"outputDir":".listenhub","outputMode":"inline","language":null,"defaultStyle":null,"defaultSpeakers":{}}' > ".listenhub/explainer/config.json"
CONFIG_PATH=".listenhub/explainer/config.json"
# (or $HOME/.listenhub/explainer/config.json for global)
Then run Setup Flow below.
If file exists — read config, display summary, and confirm:
当前配置 (explainer):
输出方式:{inline / download / both}
语言偏好:{zh / en / 未设置}
默认风格:{info / story / 未设置}
默认主播:{speakerName / 未设置}
Ask: "使用已保存的配置?" → 确认,直接继续 / 重新配置
Ask these questions in order, then save all answers to config at once:
outputMode: Follow shared/output-mode.md § Setup Flow Question.
Language (optional): "默认语言?"
nullStyle (optional): "默认风格?"
nullAfter collecting answers, save immediately:
# Follow shared/output-mode.md § Save to Config
NEW_CONFIG=$(echo "$CONFIG" | jq --arg m "$OUTPUT_MODE" '. + {"outputMode": $m}')
echo "$NEW_CONFIG" > "$CONFIG_PATH"
CONFIG=$(cat "$CONFIG_PATH")
Note: defaultSpeakers are saved after generation (see After Successful Generation section).
Free text input. Ask the user:
What would you like to explain or introduce?
Accept: topic description, text content, or concept to explain.
If config.language is set, pre-fill and show in summary — skip this question.
Otherwise ask:
Question: "What language?"
Options:
- "Chinese (zh)" — Content in Mandarin Chinese
- "English (en)" — Content in English
If config.defaultStyle is set, pre-fill and show in summary — skip this question.
Otherwise ask:
Question: "What style of explainer?"
Options:
- "Info" — Informational, factual presentation style
- "Story" — Narrative, storytelling approach
Follow shared/speaker-selection.md for the full selection flow, including:
config.defaultSpeakers.{language} (skip step if set)Only 1 speaker is supported for explainer videos.
Question: "What output do you want?"
Options:
- "Text script only" — Generate narration script, no video
- "Text + Video" — Generate full explainer video with AI visuals
Summarize all choices:
Ready to generate explainer:
Topic: {topic}
Language: {language}
Style: {info/story}
Speaker: {speaker name}
Output: {text only / text + video}
Proceed?
Wait for explicit confirmation before calling any API.
Submit (foreground): POST /storybook/episodes with content, speaker, language, mode → extract episodeId
Tell the user the task is submitted
Poll (background): Run the following exact bash command with run_in_background: true and timeout: 600000. Do NOT use python3, awk, or any other JSON parser — use jq as shown:
EPISODE_ID="<id-from-step-1>"
for i in $(seq 1 30); do
RESULT=$(curl -sS "https://api.marswave.ai/openapi/v1/storybook/episodes/$EPISODE_ID" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" 2>/dev/null)
STATUS=$(echo "$RESULT" | tr -d '\000-\037\177' | jq -r '.data.processStatus // "pending"')
case "$STATUS" in
success|completed) echo "$RESULT"; exit 0 ;;
failed|error) echo "FAILED: $RESULT" >&2; exit 1 ;;
*) sleep 10 ;;
esac
done
echo "TIMEOUT" >&2; exit 2
When notified, download and present script:
Read OUTPUT_MODE from config. Follow shared/output-mode.md for behavior.
inline or both: Present the script inline.
Present:
解说脚本已生成!
「{title}」
在线查看:https://listenhub.ai/app/explainer/{episodeId}
download or both: Also save the script file.
.listenhub/explainer/YYYY-MM-DD-{episodeId}/{episodeId}.md from the generated script contentIf video requested: POST /storybook/episodes/{episodeId}/video (foreground) → poll again (background) using the exact bash command below with run_in_background: true and timeout: 600000. Poll for videoStatus, not processStatus:
EPISODE_ID="<id-from-step-1>"
for i in $(seq 1 30); do
RESULT=$(curl -sS "https://api.marswave.ai/openapi/v1/storybook/episodes/$EPISODE_ID" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" 2>/dev/null)
STATUS=$(echo "$RESULT" | tr -d '\000-\037\177' | jq -r '.data.videoStatus // "pending"')
case "$STATUS" in
success|completed) echo "$RESULT"; exit 0 ;;
failed|error) echo "FAILED: $RESULT" >&2; exit 1 ;;
*) sleep 10 ;;
esac
done
echo "TIMEOUT" >&2; exit 2
When notified, download and present result:
Present result
Read OUTPUT_MODE from config. Follow shared/output-mode.md for behavior.
inline or both: Display video URL and audio URL as clickable links.
Present:
解说视频已生成!
视频链接:{videoUrl}
音频链接:{audioUrl}
时长:{duration}s
消耗积分:{credits}
download or both: Also download the audio file.
DATE=$(date +%Y-%m-%d)
JOB_DIR=".listenhub/explainer/${DATE}-{jobId}"
mkdir -p "$JOB_DIR"
curl -sS -o "${JOB_DIR}/{jobId}.mp3" "{audioUrl}"
Present the download path in addition to the above summary.
Update config with the choices made this session:
NEW_CONFIG=$(echo "$CONFIG" | jq \
--arg lang "{language}" \
--arg style "{info/story}" \
--arg speakerId "{speakerId}" \
'. + {"language": $lang, "defaultStyle": $style, "defaultSpeakers": (.defaultSpeakers + {($lang): [$speakerId]})}')
echo "$NEW_CONFIG" > "$CONFIG_PATH"
Estimated times:
shared/api-speakers.mdshared/speaker-selection.mdshared/api-storybook.mdshared/common-patterns.md § Async Pollingshared/config-pattern.md/speech for voiceoverUser: "Create an explainer video introducing Claude Code"
Agent workflow:
curl -sS -X POST "https://api.marswave.ai/openapi/v1/storybook/episodes" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sources": [{"type": "text", "content": "Introduce Claude Code: what it is, key features, and how to get started"}],
"speakers": [{"speakerId": "cozy-man-english"}],
"language": "en",
"mode": "info"
}'
Poll until text is ready, then generate video if requested.