
Muapi Animal Video Generator
- 518 installs
- 4k repo stars
- Updated August 4, 2026
- samuraigpt/generative-media-skills
muapi-animal-video-generator is an agent skill that generates AI animal videos through the muapi.ai API for developers who need short animal-themed video clips for content, demos, and social campaigns.
About
muapi-animal-video-generator in samuraigpt/generative-media-skills wraps muapi.ai video generation for animal-themed clips driven by agent prompts and bundled shell workflows. The generative-media-skills repo documents muapi-media-editing at version 0.1.0 with prompt-based image and video operations via muapi.ai, including models such as Flux Kontext, GPT-4o, Midjourney, and Qwen referenced in sibling skills. Developers reach for muapi-animal-video-generator when a product needs playful animal footage for landing pages, app store previews, or social posts without standing up a custom diffusion pipeline. Agents call MuAPI endpoints and scripts to turn text prompts into downloadable video assets ready for CMS upload or ad creative handoff. Triggers include animal video generation, muapi.ai video effects, and AI clip creation for marketing. Output is generated MP4 or hosted video URLs rather than editable project files from Premiere or DaVinci.
- muapi-animal-video-generator
Muapi Animal Video Generator by the numbers
- 518 all-time installs (skills.sh)
- +24 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #785 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/samuraigpt/generative-media-skills --skill muapi-animal-video-generatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 518 |
|---|---|
| repo stars | ★ 4k |
| Last updated | August 4, 2026 |
| Repository | samuraigpt/generative-media-skills ↗ |
How do you generate AI animal videos with muapi?
Use muapi-animal-video-generator for development tasks
Who is it for?
Developers and content engineers who need quick animal-themed AI video clips through muapi.ai without building a custom video generation stack.
Skip if: Long-form film editing, proprietary on-prem video models, or teams blocked from external generative media APIs.
When should I use this skill?
User asks to generate animal videos, create muapi.ai animal clips, or produce AI animal footage for marketing or demo content.
What you get
AI-generated animal video files or hosted URLs produced via muapi.ai prompts and generative-media-skills scripts.
- Generated animal video file
- Hosted video URL
By the numbers
- Sibling muapi-media-editing skill documented at version 0.1.0
Files
✏️ MuAPI Media Editing & Enhancement
Advanced editing and enhancement operations for images and videos.
Apply AI-powered edits, enhancements, and effects to existing media. Supports prompt-based editing with Flux Kontext, GPT-4o, and Midjourney, plus one-click operations like upscaling and background removal.
Available Scripts
| Script | Description |
|---|---|
edit-image.sh | Prompt-based image editing (Flux Kontext, GPT-4o, Midjourney, Qwen, and more) |
enhance-image.sh | One-click operations: upscale, background removal, face swap, colorize, Ghibli style, product shots |
lipsync.sh | Sync video lip movement to audio (Sync Labs, LatentSync, Creatify, Veed) |
video-effects.sh | Video/image effects: Wan AI, face swap, dance, dress change, Luma modify/reframe |
Quick Start
# Edit an image with a prompt
bash edit-image.sh --image-url "https://..." --prompt "add sunglasses" --model flux-kontext-pro
# Upscale an image
bash enhance-image.sh --op upscale --image-url "https://..."
# Remove background
bash enhance-image.sh --op background-remove --image-url "https://..."
# Lipsync a video
bash lipsync.sh --video-url "https://..." --audio-url "https://..." --model sync
# Apply dance effect
bash video-effects.sh --op dance --image-url "https://..." --audio-url "https://..."Common Flags
All scripts support: --async, --json, --timeout N, --help
Requirements
MUAPI_KEYenvironment variable (set viacore/platform/setup.sh)curl,jq,python3
#!/bin/bash
# muapi.ai Image Editing
# Usage: ./edit-image.sh --image-url URL --prompt "..." [--model MODEL]
set -e
IMAGE_URL=""
IMAGE_FILE=""
PROMPT=""
MODEL="flux-kontext-pro"
ASPECT_RATIO="1:1"
NUM_IMAGES=1
ASYNC=false
JSON_ONLY=false
JQ_EXPR=""
TIMEOUT=300
while [[ $# -gt 0 ]]; do
case $1 in
--image-url) IMAGE_URL="$2"; shift 2 ;;
--file|-f) IMAGE_FILE="$2"; shift 2 ;;
--prompt|-p) PROMPT="$2"; shift 2 ;;
--model|-m) MODEL="$2"; shift 2 ;;
--aspect-ratio) ASPECT_RATIO="$2"; shift 2 ;;
--num-images) NUM_IMAGES="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--timeout) TIMEOUT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--jq) JQ_EXPR="$2"; shift 2 ;;
--help|-h)
echo "Usage: ./edit-image.sh --image-url URL --prompt \"...\" [options]"
echo ""
muapi image models
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$PROMPT" ]; then echo "Error: --prompt is required" >&2; exit 1; fi
# Auto-upload local file if provided
if [ -n "$IMAGE_FILE" ]; then
echo "Uploading $(basename "$IMAGE_FILE")..." >&2
IMAGE_URL=$(bash "$(dirname "$0")/../media/upload.sh" --file "$IMAGE_FILE")
fi
if [ -z "$IMAGE_URL" ]; then echo "Error: --image-url or --file is required" >&2; exit 1; fi
ARGS=(--model "$MODEL" --image "$IMAGE_URL" --aspect-ratio "$ASPECT_RATIO" --num-images "$NUM_IMAGES")
[ "$JSON_ONLY" = true ] && ARGS+=(--output-json)
[ -n "$JQ_EXPR" ] && ARGS+=(--jq "$JQ_EXPR")
[ "$ASYNC" = true ] && ARGS+=(--no-wait) || ARGS+=(--wait)
muapi image edit "$PROMPT" "${ARGS[@]}"
#!/bin/bash
# muapi.ai Image Enhancement (one-click operations)
# Usage: ./enhance-image.sh --op upscale --image-url URL
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
OP=""
IMAGE_URL=""
IMAGE_FILE=""
FACE_URL=""
FACE_FILE=""
MASK_URL=""
MASK_FILE=""
PROMPT=""
ASYNC=false
JSON_ONLY=false
MAX_WAIT=300
POLL_INTERVAL=3
for arg in "$@"; do
if [ "$arg" = "--add-key" ]; then
shift
KEY_VALUE=""
if [[ -n "$1" && ! "$1" =~ ^-- ]]; then KEY_VALUE="$1"; fi
if [ -z "$KEY_VALUE" ]; then echo "Enter your muapi.ai API key:" >&2; read -r KEY_VALUE; fi
if [ -n "$KEY_VALUE" ]; then
grep -v "^MUAPI_KEY=" .env > .env.tmp 2>/dev/null || true
mv .env.tmp .env 2>/dev/null || true
echo "MUAPI_KEY=$KEY_VALUE" >> .env
echo "MUAPI_KEY saved to .env" >&2
fi
exit 0
fi
done
if [ -f ".env" ]; then source .env 2>/dev/null || true; fi
while [[ $# -gt 0 ]]; do
case $1 in
--op) OP="$2"; shift 2 ;;
--image-url) IMAGE_URL="$2"; shift 2 ;;
--file) IMAGE_FILE="$2"; shift 2 ;;
--face-url) FACE_URL="$2"; shift 2 ;;
--face-file) FACE_FILE="$2"; shift 2 ;;
--mask-url) MASK_URL="$2"; shift 2 ;;
--mask-file) MASK_FILE="$2"; shift 2 ;;
--prompt|-p) PROMPT="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Image Enhancement" >&2
echo "" >&2
echo "Usage: ./enhance-image.sh --op OPERATION --image-url URL" >&2
echo "" >&2
echo "Operations (--op):" >&2
echo " upscale Upscale image resolution" >&2
echo " background-remove Remove image background" >&2
echo " face-swap Swap face (requires --face-url)" >&2
echo " skin-enhance Smooth and enhance skin" >&2
echo " colorize Colorize black & white photo" >&2
echo " ghibli Studio Ghibli art style" >&2
echo " anime Anime style (optional --prompt)" >&2
echo " extend Extend/outpaint image" >&2
echo " product-shot Clean product shot background" >&2
echo " product-photo Professional product photography (requires --prompt)" >&2
echo " object-erase Erase object (requires --mask-url or --mask-file index)" >&2
echo "" >&2
echo "File Inputs:" >&2
echo " --file Main image file" >&2
echo " --face-file Face image for swap" >&2
echo " --mask-file Mask image for erase" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&2; exit 1; fi
if [ -z "$OP" ]; then echo "Error: --op is required" >&2; exit 1; fi
HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
# Auto-upload local files
upload_file() {
local FPATH="$1"
if [ ! -f "$FPATH" ]; then echo "Error: File not found: $FPATH" >&2; exit 1; fi
[ "$JSON_ONLY" = false ] && echo "Uploading $(basename "$FPATH")..." >&2
local RESP=$(curl -s -X POST "${MUAPI_BASE}/upload_file" -H "x-api-key: $MUAPI_KEY" -F "file=@${FPATH}")
local URL=$(echo "$RESP" | jq -r '.url // empty')
if [ -z "$URL" ]; then
local ERR=$(echo "$RESP" | jq -r '.error // .detail // "Upload failed"')
echo "Error: $ERR" >&2; exit 1
fi
echo "$URL"
}
if [ -n "$IMAGE_FILE" ]; then IMAGE_URL=$(upload_file "$IMAGE_FILE"); fi
if [ -n "$FACE_FILE" ]; then FACE_URL=$(upload_file "$FACE_FILE"); fi
if [ -n "$MASK_FILE" ]; then MASK_URL=$(upload_file "$MASK_FILE"); fi
if [ -z "$IMAGE_URL" ]; then echo "Error: --image-url or --file is required" >&2; exit 1; fi
IMAGE_URL_CLEAN=$(echo "$IMAGE_URL" | tr -d '"')
# Map operation to endpoint and payload
PROMPT_JSON=$(echo "${PROMPT:-}" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
case $OP in
upscale)
ENDPOINT="ai-image-upscale"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
background-remove)
ENDPOINT="ai-background-remover"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
face-swap)
if [ -z "$FACE_URL" ]; then echo "Error: --face-url is required for face-swap" >&2; exit 1; fi
FACE_CLEAN=$(echo "$FACE_URL" | tr -d '"')
ENDPOINT="ai-image-face-swap"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\", \"face_image_url\": \"$FACE_CLEAN\"}" ;;
skin-enhance)
ENDPOINT="ai-skin-enhancer"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
colorize)
ENDPOINT="ai-color-photo"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
ghibli)
ENDPOINT="ai-ghibli-style"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
anime)
ENDPOINT="ai-anime-generator"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\", \"prompt\": $PROMPT_JSON}" ;;
extend)
ENDPOINT="ai-image-extension"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
product-shot)
ENDPOINT="ai-product-shot"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\"}" ;;
product-photo)
ENDPOINT="ai-product-photography"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\", \"prompt\": $PROMPT_JSON}" ;;
object-erase)
if [ -z "$MASK_URL" ]; then echo "Error: --mask-url is required for object-erase" >&2; exit 1; fi
MASK_CLEAN=$(echo "$MASK_URL" | tr -d '"')
ENDPOINT="ai-object-eraser"
PAYLOAD="{\"image_url\": \"$IMAGE_URL_CLEAN\", \"mask_url\": \"$MASK_CLEAN\"}" ;;
*)
echo "Error: Unknown operation '$OP'" >&2
echo "Valid: upscale, background-remove, face-swap, skin-enhance, colorize," >&2
echo " ghibli, anime, extend, product-shot, product-photo, object-erase" >&2
exit 1 ;;
esac
[ "$JSON_ONLY" = false ] && echo "Submitting $OP to $ENDPOINT..." >&2
SUBMIT=$(curl -s -X POST "${MUAPI_BASE}/${ENDPOINT}" "${HEADERS[@]}" -d "$PAYLOAD")
if echo "$SUBMIT" | grep -q '"error"\|"detail"'; then
ERR=$(echo "$SUBMIT" | grep -o '"detail":"[^"]*"' | head -1 | cut -d'"' -f4)
[ -z "$ERR" ] && ERR=$(echo "$SUBMIT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-$SUBMIT}" >&2; exit 1
fi
REQUEST_ID=$(echo "$SUBMIT" | grep -oE '"request_id"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
if [ -z "$REQUEST_ID" ]; then echo "Error: No request_id" >&2; echo "$SUBMIT" >&2; exit 1; fi
[ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
if [ "$ASYNC" = true ]; then
[ "$JSON_ONLY" = false ] && echo "Check: bash check-result.sh --id \"$REQUEST_ID\"" >&2
echo "$SUBMIT"; exit 0
fi
[ "$JSON_ONLY" = false ] && echo "Processing..." >&2
ELAPSED=0; LAST_STATUS=""
while [ $ELAPSED -lt $MAX_WAIT ]; do
sleep $POLL_INTERVAL; ELAPSED=$((ELAPSED + POLL_INTERVAL))
RESULT=$(curl -s -X GET "${MUAPI_BASE}/predictions/${REQUEST_ID}/result" "${HEADERS[@]}")
STATUS=$(echo "$RESULT" | grep -oE '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
if [ "$STATUS" != "$LAST_STATUS" ] && [ "$JSON_ONLY" = false ]; then echo "Status: $STATUS" >&2; LAST_STATUS="$STATUS"; fi
case $STATUS in
completed)
[ "$JSON_ONLY" = false ] && echo "Done!" >&2
URL=$(echo "$RESULT" | grep -o '"outputs":\[[^]]*\]' | grep -o '"[^"]*\.\(png\|jpg\|jpeg\|webp\)"' | head -1 | tr -d '"')
[ -n "$URL" ] && [ "$JSON_ONLY" = false ] && echo "Image URL: $URL" >&2
echo "$RESULT"; exit 0 ;;
failed)
ERR=$(echo "$RESULT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-Enhancement failed}" >&2; echo "$RESULT"; exit 1 ;;
esac
done
echo "Error: Timeout after ${MAX_WAIT}s — Request ID: $REQUEST_ID" >&2; exit 1
#!/bin/bash
# muapi.ai Lipsync
# Usage: ./lipsync.sh --video-url URL --audio-url URL [--model sync]
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
VIDEO_URL=""
VIDEO_FILE=""
AUDIO_URL=""
AUDIO_FILE=""
MODEL="sync"
ASYNC=false
JSON_ONLY=false
MAX_WAIT=300
POLL_INTERVAL=5
for arg in "$@"; do
if [ "$arg" = "--add-key" ]; then
shift
KEY_VALUE=""
if [[ -n "$1" && ! "$1" =~ ^-- ]]; then KEY_VALUE="$1"; fi
if [ -z "$KEY_VALUE" ]; then echo "Enter your muapi.ai API key:" >&2; read -r KEY_VALUE; fi
if [ -n "$KEY_VALUE" ]; then
grep -v "^MUAPI_KEY=" .env > .env.tmp 2>/dev/null || true
mv .env.tmp .env 2>/dev/null || true
echo "MUAPI_KEY=$KEY_VALUE" >> .env
echo "MUAPI_KEY saved to .env" >&2
fi
exit 0
fi
done
if [ -f ".env" ]; then source .env 2>/dev/null || true; fi
while [[ $# -gt 0 ]]; do
case $1 in
--video-url) VIDEO_URL="$2"; shift 2 ;;
--video-file) VIDEO_FILE="$2"; shift 2 ;;
--audio-url) AUDIO_URL="$2"; shift 2 ;;
--audio-file) AUDIO_FILE="$2"; shift 2 ;;
--model|-m) MODEL="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Lipsync" >&2
echo "" >&2
echo "Usage: ./lipsync.sh --video-url URL --audio-url URL [options]" >&2
echo "" >&2
echo "Models (--model):" >&2
echo " sync Sync Labs — high quality (default)" >&2
echo " latent LatentSync — open source" >&2
echo " creatify Creatify — fast" >&2
echo " veed Veed — reliable" >&2
echo "" >&2
echo "File Inputs:" >&2
echo " --video-file Local video file" >&2
echo " --audio-file Local audio file" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&2; exit 1; fi
# Auto-upload local files
upload_file() {
local FPATH="$1"
if [ ! -f "$FPATH" ]; then echo "Error: File not found: $FPATH" >&2; exit 1; fi
[ "$JSON_ONLY" = false ] && echo "Uploading $(basename "$FPATH")..." >&2
local RESP=$(curl -s -X POST "${MUAPI_BASE}/upload_file" -H "x-api-key: $MUAPI_KEY" -F "file=@${FPATH}")
local URL=$(echo "$RESP" | jq -r '.url // empty')
if [ -z "$URL" ]; then
local ERR=$(echo "$RESP" | jq -r '.error // .detail // "Upload failed"')
echo "Error: $ERR" >&2; exit 1
fi
echo "$URL"
}
if [ -n "$VIDEO_FILE" ]; then VIDEO_URL=$(upload_file "$VIDEO_FILE"); fi
if [ -n "$AUDIO_FILE" ]; then AUDIO_URL=$(upload_file "$AUDIO_FILE"); fi
if [ -z "$VIDEO_URL" ]; then echo "Error: --video-url or --video-file is required" >&2; exit 1; fi
if [ -z "$AUDIO_URL" ]; then echo "Error: --audio-url or --audio-file is required" >&2; exit 1; fi
HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
VIDEO_CLEAN=$(echo "$VIDEO_URL" | tr -d '"')
AUDIO_CLEAN=$(echo "$AUDIO_URL" | tr -d '"')
case $MODEL in
sync) ENDPOINT="sync-lipsync" ;;
latent) ENDPOINT="latentsync-video" ;;
creatify) ENDPOINT="creatify-lipsync" ;;
veed) ENDPOINT="veed-lipsync" ;;
*)
echo "Error: Unknown model '$MODEL'" >&2
echo "Valid: sync, latent, creatify, veed" >&2
exit 1 ;;
esac
PAYLOAD="{\"video_url\": \"$VIDEO_CLEAN\", \"audio_url\": \"$AUDIO_CLEAN\"}"
[ "$JSON_ONLY" = false ] && echo "Submitting lipsync (model: $MODEL)..." >&2
SUBMIT=$(curl -s -X POST "${MUAPI_BASE}/${ENDPOINT}" "${HEADERS[@]}" -d "$PAYLOAD")
if echo "$SUBMIT" | grep -q '"error"\|"detail"'; then
ERR=$(echo "$SUBMIT" | grep -o '"detail":"[^"]*"' | head -1 | cut -d'"' -f4)
[ -z "$ERR" ] && ERR=$(echo "$SUBMIT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-$SUBMIT}" >&2; exit 1
fi
REQUEST_ID=$(echo "$SUBMIT" | grep -oE '"request_id"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
if [ -z "$REQUEST_ID" ]; then echo "Error: No request_id" >&2; echo "$SUBMIT" >&2; exit 1; fi
[ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
if [ "$ASYNC" = true ]; then
[ "$JSON_ONLY" = false ] && echo "Lipsync takes 30–120s. Check: bash check-result.sh --id \"$REQUEST_ID\"" >&2
echo "$SUBMIT"; exit 0
fi
[ "$JSON_ONLY" = false ] && echo "Processing lipsync (30–120s)..." >&2
ELAPSED=0; LAST_STATUS=""
while [ $ELAPSED -lt $MAX_WAIT ]; do
sleep $POLL_INTERVAL; ELAPSED=$((ELAPSED + POLL_INTERVAL))
RESULT=$(curl -s -X GET "${MUAPI_BASE}/predictions/${REQUEST_ID}/result" "${HEADERS[@]}")
STATUS=$(echo "$RESULT" | grep -oE '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
if [ "$STATUS" != "$LAST_STATUS" ] && [ "$JSON_ONLY" = false ]; then echo "Status: $STATUS (${ELAPSED}s)" >&2; LAST_STATUS="$STATUS"; fi
case $STATUS in
completed)
[ "$JSON_ONLY" = false ] && echo "" >&2
[ "$JSON_ONLY" = false ] && echo "Lipsync complete!" >&2
URL=$(echo "$RESULT" | grep -o '"outputs":\[[^]]*\]' | grep -o '"[^"]*\.mp4"' | head -1 | tr -d '"')
[ -n "$URL" ] && [ "$JSON_ONLY" = false ] && echo "Video URL: $URL" >&2
echo "$RESULT"; exit 0 ;;
failed)
ERR=$(echo "$RESULT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-Lipsync failed}" >&2; echo "$RESULT"; exit 1 ;;
esac
done
echo "Error: Timeout after ${MAX_WAIT}s — Request ID: $REQUEST_ID" >&2; exit 1
#!/bin/bash
# muapi.ai Video Effects
# Usage: ./video-effects.sh --op face-swap --video-url URL --face-url URL
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
OP=""
VIDEO_URL=""
VIDEO_FILE=""
IMAGE_URL=""
IMAGE_FILE=""
FACE_URL=""
FACE_FILE=""
AUDIO_URL=""
AUDIO_FILE=""
PROMPT=""
EFFECT=""
ASYNC=false
JSON_ONLY=false
MAX_WAIT=300
POLL_INTERVAL=5
for arg in "$@"; do
if [ "$arg" = "--add-key" ]; then
shift
KEY_VALUE=""
if [[ -n "$1" && ! "$1" =~ ^-- ]]; then KEY_VALUE="$1"; fi
if [ -z "$KEY_VALUE" ]; then echo "Enter your muapi.ai API key:" >&2; read -r KEY_VALUE; fi
if [ -n "$KEY_VALUE" ]; then
grep -v "^MUAPI_KEY=" .env > .env.tmp 2>/dev/null || true
mv .env.tmp .env 2>/dev/null || true
echo "MUAPI_KEY=$KEY_VALUE" >> .env
echo "MUAPI_KEY saved to .env" >&2
fi
exit 0
fi
done
if [ -f ".env" ]; then source .env 2>/dev/null || true; fi
while [[ $# -gt 0 ]]; do
case $1 in
--op) OP="$2"; shift 2 ;;
--video-url) VIDEO_URL="$2"; shift 2 ;;
--video-file) VIDEO_FILE="$2"; shift 2 ;;
--image-url) IMAGE_URL="$2"; shift 2 ;;
--image-file) IMAGE_FILE="$2"; shift 2 ;;
--face-url) FACE_URL="$2"; shift 2 ;;
--face-file) FACE_FILE="$2"; shift 2 ;;
--audio-url) AUDIO_URL="$2"; shift 2 ;;
--audio-file) AUDIO_FILE="$2"; shift 2 ;;
--prompt|-p) PROMPT="$2"; shift 2 ;;
--effect) EFFECT="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Video Effects" >&2
echo "" >&2
echo "Operations (--op):" >&2
echo " wan-effects Wan AI effects on image (--image-url, --prompt)" >&2
echo " video-effect Named effect on video (--video-url, --effect)" >&2
echo " image-effect Named effect on image (--image-url, --effect)" >&2
echo " dance Dance animation (--image-url, --audio-url)" >&2
echo " face-swap Face swap in video (--video-url, --face-url)" >&2
echo " dress-change Change outfit (--image-url, --prompt)" >&2
echo " luma-modify Modify video with prompt (--video-url, --prompt)" >&2
echo " luma-reframe Reframe video (--video-url, --prompt)" >&2
echo " vidu-reference Vidu character reference (--image-url, --prompt)" >&2
echo "" >&2
echo "File Inputs:" >&2
echo " --video-file Local video file" >&2
echo " --image-file Local image file" >&2
echo " --face-file Local face image file" >&2
echo " --audio-file Local audio file" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&2; exit 1; fi
if [ -z "$OP" ]; then echo "Error: --op is required" >&2; exit 1; fi
HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
PROMPT_JSON=$(echo "${PROMPT:-}" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
EFFECT_JSON=$(echo "${EFFECT:-}" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
clean_url() { echo "$1" | tr -d '"'; }
# Auto-upload local files
upload_file() {
local FPATH="$1"
if [ ! -f "$FPATH" ]; then echo "Error: File not found: $FPATH" >&2; exit 1; fi
[ "$JSON_ONLY" = false ] && echo "Uploading $(basename "$FPATH")..." >&2
local RESP=$(curl -s -X POST "${MUAPI_BASE}/upload_file" -H "x-api-key: $MUAPI_KEY" -F "file=@${FPATH}")
local URL=$(echo "$RESP" | jq -r '.url // empty')
if [ -z "$URL" ]; then
local ERR=$(echo "$RESP" | jq -r '.error // .detail // "Upload failed"')
echo "Error: $ERR" >&2; exit 1
fi
echo "$URL"
}
if [ -n "$VIDEO_FILE" ]; then VIDEO_URL=$(upload_file "$VIDEO_FILE"); fi
if [ -n "$IMAGE_FILE" ]; then IMAGE_URL=$(upload_file "$IMAGE_FILE"); fi
if [ -n "$FACE_FILE" ]; then FACE_URL=$(upload_file "$FACE_FILE"); fi
if [ -n "$AUDIO_FILE" ]; then AUDIO_URL=$(upload_file "$AUDIO_FILE"); fi
case $OP in
wan-effects)
if [ -z "$IMAGE_URL" ]; then echo "Error: --image-url required" >&2; exit 1; fi
ENDPOINT="generate_wan_ai_effects"
PAYLOAD="{\"image_url\": \"$(clean_url "$IMAGE_URL")\", \"prompt\": $PROMPT_JSON}" ;;
video-effect)
if [ -z "$VIDEO_URL" ]; then echo "Error: --video-url required" >&2; exit 1; fi
ENDPOINT="video-effects"
PAYLOAD="{\"video_url\": \"$(clean_url "$VIDEO_URL")\", \"effect\": $EFFECT_JSON}" ;;
image-effect)
if [ -z "$IMAGE_URL" ]; then echo "Error: --image-url required" >&2; exit 1; fi
ENDPOINT="image-effects"
PAYLOAD="{\"image_url\": \"$(clean_url "$IMAGE_URL")\", \"effect\": $EFFECT_JSON}" ;;
dance)
if [ -z "$IMAGE_URL" ] || [ -z "$AUDIO_URL" ]; then
echo "Error: --image-url and --audio-url are required for dance" >&2; exit 1
fi
ENDPOINT="ai-dance-effects"
PAYLOAD="{\"image_url\": \"$(clean_url "$IMAGE_URL")\", \"audio_url\": \"$(clean_url "$AUDIO_URL")\"}" ;;
face-swap)
if [ -z "$VIDEO_URL" ] || [ -z "$FACE_URL" ]; then
echo "Error: --video-url and --face-url are required for face-swap" >&2; exit 1
fi
ENDPOINT="ai-video-face-swap"
PAYLOAD="{\"video_url\": \"$(clean_url "$VIDEO_URL")\", \"face_image_url\": \"$(clean_url "$FACE_URL")\"}" ;;
dress-change)
if [ -z "$IMAGE_URL" ]; then echo "Error: --image-url required" >&2; exit 1; fi
ENDPOINT="ai-dress-change"
PAYLOAD="{\"image_url\": \"$(clean_url "$IMAGE_URL")\", \"prompt\": $PROMPT_JSON}" ;;
luma-modify)
if [ -z "$VIDEO_URL" ]; then echo "Error: --video-url required" >&2; exit 1; fi
ENDPOINT="luma-modify-video"
PAYLOAD="{\"video_url\": \"$(clean_url "$VIDEO_URL")\", \"prompt\": $PROMPT_JSON}" ;;
luma-reframe)
if [ -z "$VIDEO_URL" ]; then echo "Error: --video-url required" >&2; exit 1; fi
ENDPOINT="luma-flash-reframe"
PAYLOAD="{\"video_url\": \"$(clean_url "$VIDEO_URL")\", \"prompt\": $PROMPT_JSON}" ;;
vidu-reference)
if [ -z "$IMAGE_URL" ]; then echo "Error: --image-url required" >&2; exit 1; fi
ENDPOINT="vidu-q1-reference"
PAYLOAD="{\"image_url\": \"$(clean_url "$IMAGE_URL")\", \"prompt\": $PROMPT_JSON}" ;;
*)
echo "Error: Unknown operation '$OP'" >&2; exit 1 ;;
esac
[ "$JSON_ONLY" = false ] && echo "Submitting $OP to $ENDPOINT..." >&2
SUBMIT=$(curl -s -X POST "${MUAPI_BASE}/${ENDPOINT}" "${HEADERS[@]}" -d "$PAYLOAD")
if echo "$SUBMIT" | grep -q '"error"\|"detail"'; then
ERR=$(echo "$SUBMIT" | grep -o '"detail":"[^"]*"' | head -1 | cut -d'"' -f4)
[ -z "$ERR" ] && ERR=$(echo "$SUBMIT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-$SUBMIT}" >&2; exit 1
fi
REQUEST_ID=$(echo "$SUBMIT" | grep -oE '"request_id"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
if [ -z "$REQUEST_ID" ]; then echo "Error: No request_id" >&2; echo "$SUBMIT" >&2; exit 1; fi
[ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
if [ "$ASYNC" = true ]; then
[ "$JSON_ONLY" = false ] && echo "Check: bash check-result.sh --id \"$REQUEST_ID\"" >&2
echo "$SUBMIT"; exit 0
fi
[ "$JSON_ONLY" = false ] && echo "Processing..." >&2
ELAPSED=0; LAST_STATUS=""
while [ $ELAPSED -lt $MAX_WAIT ]; do
sleep $POLL_INTERVAL; ELAPSED=$((ELAPSED + POLL_INTERVAL))
RESULT=$(curl -s -X GET "${MUAPI_BASE}/predictions/${REQUEST_ID}/result" "${HEADERS[@]}")
STATUS=$(echo "$RESULT" | grep -oE '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
if [ "$STATUS" != "$LAST_STATUS" ] && [ "$JSON_ONLY" = false ]; then echo "Status: $STATUS (${ELAPSED}s)" >&2; LAST_STATUS="$STATUS"; fi
case $STATUS in
completed)
[ "$JSON_ONLY" = false ] && echo "Done!" >&2
URL=$(echo "$RESULT" | grep -o '"outputs":\[[^]]*\]' | grep -o '"[^"]*\.\(mp4\|gif\|png\|jpg\)"' | head -1 | tr -d '"')
[ -n "$URL" ] && [ "$JSON_ONLY" = false ] && echo "Result URL: $URL" >&2
echo "$RESULT"; exit 0 ;;
failed)
ERR=$(echo "$RESULT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-Operation failed}" >&2; echo "$RESULT"; exit 1 ;;
esac
done
echo "Error: Timeout after ${MAX_WAIT}s — Request ID: $REQUEST_ID" >&2; exit 1
Related skills
How it compares
Pick muapi-animal-video-generator for ready-made animal clip presets via muapi.ai; pick general video-editing skills when you need frame-accurate cuts on existing footage.
FAQ
What API does muapi-animal-video-generator use?
muapi-animal-video-generator drives animal-themed video generation through the muapi.ai API as part of the samuraigpt/generative-media-skills bundle, turning text prompts into downloadable video assets.
What video models appear in the generative-media-skills repo?
Sibling muapi-media-editing documentation in generative-media-skills version 0.1.0 references prompt-based editing with Flux Kontext, GPT-4o, Midjourney, and Qwen alongside muapi.ai video operations.