
Muapi Media Generation
- 1k installs
- 4k repo stars
- Updated August 4, 2026
- samuraigpt/generative-media-skills
muapi-media-generation is a generative media skill that generates custom audio tracks, music, and sound effects from natural language prompts inside an AI coding workflow using muapi.ai API scripts.
About
muapi-media-generation is a samuraigpt/generative-media-skills package that generates audio, music, and sound effects through muapi.ai from natural language prompts. It ships shell scripts such as create-music.sh that call https://api.muapi.ai/api/v1 with style and prompt flags, support async polling with configurable MAX_WAIT and POLL_INTERVAL, and store API keys in .env as MUAPI_KEY. The scripts reference SUNO_MODEL V5 and accept operations like create with duration and JSON-only output modes. Developers reach for muapi-media-generation when they want programmatic music and SFX generation without leaving their agent-driven dev session.
- CLI wrapper for the muapi.ai Audio & Music Generation API
- Supports style-based music creation with Suno V5 model
- Handles text-to-audio, audio extension, and video-linked generation
- Built-in async polling with configurable timeout and interval
- One-command API key setup stored in local .env
Muapi Media Generation by the numbers
- 1,005 all-time installs (skills.sh)
- +52 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #220 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/samuraigpt/generative-media-skills --skill muapi-media-generationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1k |
|---|---|
| repo stars | ★ 4k |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | samuraigpt/generative-media-skills ↗ |
How do you generate music from prompts in code?
Generate custom audio tracks, music, and sound effects directly from natural language prompts inside their AI coding workflow.
Who is it for?
Developers integrating generative audio into apps or prototypes who want shell-scripted muapi.ai music and sound effect generation from agent sessions.
Skip if: Video generation pipelines or teams that need a GUI-only music tool without API or shell integration.
When should I use this skill?
A developer asks to generate music, sound effects, or audio tracks from prompts using muapi.ai inside a coding workflow.
What you get
Generated audio files, muapi.ai API responses, and .env-stored MUAPI_KEY configuration for music and SFX creation.
- Generated audio files
- Shell script invocations
- .env API key configuration
By the numbers
- Poll defaults: MAX_WAIT 300 seconds, POLL_INTERVAL 5 seconds
- References SUNO_MODEL V5
- API base URL https://api.muapi.ai/api/v1
Files
🎨 MuAPI Media Generation
Schema-driven generation primitives for images, videos, and audio.
Generate professional-grade media directly from the terminal using 100+ state-of-the-art AI models. All scripts are powered by schema_data.json for dynamic model and endpoint resolution.
Available Scripts
| Script | Description | Default Model |
|---|---|---|
generate-image.sh | Text-to-image generation | flux-dev |
generate-video.sh | Text-to-video generation | minimax-pro |
image-to-video.sh | Animate a static image into video | kling-pro |
create-music.sh | Music creation, remix, extend, text/video-to-audio | Suno V5 |
upload.sh | Upload local files to CDN for use with other skills | — |
Quick Start
# Generate an image
bash generate-image.sh --prompt "a sunset over mountains" --model flux-dev --view
# Generate a video
bash generate-video.sh --prompt "ocean waves at golden hour" --model minimax-pro --view
# Animate an image
bash image-to-video.sh --image-url "https://..." --prompt "camera slowly pans right" --model kling-pro
# Create music
bash create-music.sh --style "lo-fi hip hop" --prompt "chill beats for studying"
# Upload a local file
bash upload.sh --file ./my-image.jpgCommon Flags
All scripts support: --async, --view, --json, --timeout N, --help
Requirements
MUAPI_KEYenvironment variable (set viacore/platform/setup.sh)curl,jq,python3
#!/bin/bash
# muapi.ai Audio & Music Generation
# Usage: ./create-music.sh --op create --style "lo-fi" --prompt "chill beats"
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
OP="create"
STYLE=""
PROMPT=""
SUNO_MODEL="V5"
AUDIO_URL=""
AUDIO_FILE=""
VIDEO_URL=""
VIDEO_FILE=""
DURATION=10
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 ;;
--style) STYLE="$2"; shift 2 ;;
--prompt|-p) PROMPT="$2"; shift 2 ;;
--suno-model) SUNO_MODEL="$2"; shift 2 ;;
--audio-url) AUDIO_URL="$2"; shift 2 ;;
--audio-file) AUDIO_FILE="$2"; shift 2 ;;
--video-url) VIDEO_URL="$2"; shift 2 ;;
--video-file) VIDEO_FILE="$2"; shift 2 ;;
--duration) DURATION="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Audio & Music Generation" >&2
echo "" >&2
echo "Operations (--op):" >&2
echo " create Suno music creation (default)" >&2
echo " remix Suno remix (requires --audio-url)" >&2
echo " extend Suno extend (requires --audio-url)" >&2
echo " text-to-audio MMAudio from text prompt" >&2
echo " video-to-audio MMAudio from video (requires --video-url)" >&2
echo "" >&2
echo "Examples:" >&2
echo " bash create-music.sh --style \"lo-fi hip hop\" --prompt \"chill beats\"" >&2
echo " bash create-music.sh --op text-to-audio --prompt \"thunderstorm\" --duration 15" >&2
echo " bash create-music.sh --op video-to-audio --video-url URL --prompt \"epic score\"" >&2
echo "" >&2
echo "File Inputs:" >&2
echo " --audio-file Local audio file for remix/extend" >&2
echo " --video-file Local video file for video-to-audio" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&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()))')
STYLE_JSON=$(echo "${STYLE:-}" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
# 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 "$AUDIO_FILE" ]; then AUDIO_URL=$(upload_file "$AUDIO_FILE"); fi
if [ -n "$VIDEO_FILE" ]; then VIDEO_URL=$(upload_file "$VIDEO_FILE"); fi
case $OP in
create)
if [ -z "$STYLE" ]; then echo "Error: --style is required for create" >&2; exit 1; fi
ENDPOINT="suno-create-music"
PAYLOAD="{\"style\": $STYLE_JSON, \"prompt\": $PROMPT_JSON, \"model\": \"$SUNO_MODEL\"}" ;;
remix)
if [ -z "$AUDIO_URL" ]; then echo "Error: --audio-url is required for remix" >&2; exit 1; fi
AUDIO_CLEAN=$(echo "$AUDIO_URL" | tr -d '"')
ENDPOINT="suno-remix-music"
PAYLOAD="{\"audio_url\": \"$AUDIO_CLEAN\", \"style\": $STYLE_JSON, \"prompt\": $PROMPT_JSON, \"model\": \"$SUNO_MODEL\"}" ;;
extend)
if [ -z "$AUDIO_URL" ]; then echo "Error: --audio-url is required for extend" >&2; exit 1; fi
AUDIO_CLEAN=$(echo "$AUDIO_URL" | tr -d '"')
ENDPOINT="suno-extend-music"
PAYLOAD="{\"audio_url\": \"$AUDIO_CLEAN\", \"prompt\": $PROMPT_JSON, \"model\": \"$SUNO_MODEL\"}" ;;
text-to-audio)
if [ -z "$PROMPT" ]; then echo "Error: --prompt is required for text-to-audio" >&2; exit 1; fi
ENDPOINT="mmaudio-v2/text-to-audio"
PAYLOAD="{\"prompt\": $PROMPT_JSON, \"duration\": $DURATION}" ;;
video-to-audio)
if [ -z "$VIDEO_URL" ]; then echo "Error: --video-url is required for video-to-audio" >&2; exit 1; fi
VIDEO_CLEAN=$(echo "$VIDEO_URL" | tr -d '"')
ENDPOINT="mmaudio-v2/video-to-video"
PAYLOAD="{\"video_url\": \"$VIDEO_CLEAN\", \"prompt\": $PROMPT_JSON}" ;;
*)
echo "Error: Unknown operation '$OP'" >&2
echo "Valid: create, remix, extend, text-to-audio, video-to-audio" >&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 "Music generation takes 30–90s. Check: bash check-result.sh --id \"$REQUEST_ID\"" >&2
echo "$SUBMIT"; exit 0
fi
[ "$JSON_ONLY" = false ] && echo "Generating (30–90 seconds)..." >&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 "Audio generation complete!" >&2
URL=$(echo "$RESULT" | grep -o '"outputs":\[[^]]*\]' | grep -o '"[^"]*\.\(mp3\|wav\|mp4\)"' | head -1 | tr -d '"')
[ -n "$URL" ] && [ "$JSON_ONLY" = false ] && echo "Audio URL: $URL" >&2
echo "$RESULT"; exit 0 ;;
failed)
ERR=$(echo "$RESULT" | grep -o '"error":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Error: ${ERR:-Generation 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 Text-to-Image Generation
# Usage: ./generate-image.sh --prompt "..." [--model MODEL] [options]
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
SCHEMA_FILE="$(dirname "$0")/../../schema_data.json"
# Defaults
PROMPT=""
IMAGE_URL=""
MODEL="flux-dev"
WIDTH=1024
HEIGHT=1024
ASPECT_RATIO=""
RESOLUTION="1k"
NUM_IMAGES=1
ASYNC=false
VIEW=false
JSON_ONLY=false
MAX_WAIT=300
POLL_INTERVAL=3
# Check for .env and setup
if [ -f ".env" ]; then source .env 2>/dev/null || true; fi
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--prompt|-p) PROMPT="$2"; shift 2 ;;
--image-url) IMAGE_URL="$2"; shift 2 ;;
--model|-m) MODEL="$2"; shift 2 ;;
--width) WIDTH="$2"; shift 2 ;;
--height) HEIGHT="$2"; shift 2 ;;
--aspect-ratio) ASPECT_RATIO="$2"; shift 2 ;;
--resolution) RESOLUTION="$2"; shift 2 ;;
--num-images) NUM_IMAGES="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--view) VIEW=true; shift ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Text-to-Image" >&2
echo "" >&2
echo "Usage: ./generate-image.sh --prompt \"...\" [options]" >&2
echo "" >&2
echo "Options:" >&2
echo " --prompt, -p Text description (required)" >&2
echo " --image-url Reference image URL for img2img" >&2
echo " --model, -m Model name (default: flux-dev)" >&2
echo " --aspect-ratio 1:1, 16:9, 9:16, 4:3, 3:4, 21:9" >&2
echo " --resolution 1k, 2k, 4k (for supported models)" >&2
echo " --width/--height Manual pixel override" >&2
echo " --async Return request_id immediately" >&2
echo " --view Download and open image (macOS only)" >&2
echo " --json Raw JSON output only" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&2; exit 1; fi
if [ -z "$PROMPT" ]; then echo "Error: --prompt is required" >&2; exit 1; fi
# --- DYNAMIC SCHEMA PARSING ---
if [ ! -f "$SCHEMA_FILE" ]; then echo "Error: schema_data.json not found at $SCHEMA_FILE" >&2; exit 1; fi
MODEL_DATA=$(jq -r ".[] | select(.name == \"$MODEL\")" "$SCHEMA_FILE")
if [ -z "$MODEL_DATA" ]; then
echo "Error: Model '$MODEL' not found in schema_data.json" >&2
echo "Available models: $(jq -r '.[] | .name' "$SCHEMA_FILE" | head -10)..." >&2
exit 1
fi
ENDPOINT=$(echo "$MODEL_DATA" | jq -r '.input_schema.schemas.input_data.endpoint_url')
PARAMS=$(echo "$MODEL_DATA" | jq -r '.input_schema.schemas.input_data.properties | keys[]')
# Auto-map aspect ratio to width/height if model doesn't support aspect_ratio field
SUPPORTS_AR=$(echo "$PARAMS" | grep -w "aspect_ratio" || true)
if [ -n "$ASPECT_RATIO" ] && [ -z "$SUPPORTS_AR" ]; then
case $ASPECT_RATIO in
"1:1") WIDTH=1024; HEIGHT=1024 ;;
"16:9") WIDTH=1344; HEIGHT=768 ;;
"9:16") WIDTH=768; HEIGHT=1344 ;;
"4:3") WIDTH=1152; HEIGHT=896 ;;
"3:4") WIDTH=896; HEIGHT=1152 ;;
"21:9") WIDTH=1536; HEIGHT=640 ;;
esac
fi
# Build Payload Dynamically
PROMPT_JSON=$(echo "$PROMPT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
PAYLOAD="{\"prompt\": $PROMPT_JSON"
# Add string parameters
if [ -n "$IMAGE_URL" ]; then
if echo "$PARAMS" | grep -w "image_url" >/dev/null; then
PAYLOAD="$PAYLOAD, \"image_url\": \"$IMAGE_URL\""
elif echo "$PARAMS" | grep -w "images_list" >/dev/null; then
PAYLOAD="$PAYLOAD, \"images_list\": [\"$IMAGE_URL\"]"
fi
fi
# Add numeric parameters
if echo "$PARAMS" | grep -w "num_images" >/dev/null; then PAYLOAD="$PAYLOAD, \"num_images\": $NUM_IMAGES"; fi
if echo "$PARAMS" | grep -w "width" >/dev/null && [ -z "$SUPPORTS_AR" ]; then PAYLOAD="$PAYLOAD, \"width\": $WIDTH, \"height\": $HEIGHT"; fi
# Add other string parameters
if [ -n "$SUPPORTS_AR" ]; then PAYLOAD="$PAYLOAD, \"aspect_ratio\": \"${ASPECT_RATIO:-1:1}\""; fi
if echo "$PARAMS" | grep -w "resolution" >/dev/null; then PAYLOAD="$PAYLOAD, \"resolution\": \"$RESOLUTION\""; fi
PAYLOAD="$PAYLOAD}"
# --- EXECUTION ---
HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
[ "$JSON_ONLY" = false ] && echo "Submitting to $ENDPOINT (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" | jq -r '.error // .detail // empty')
echo "Error: ${ERR:-$SUBMIT}" >&2; exit 1
fi
REQUEST_ID=$(echo "$SUBMIT" | jq -r '.request_id')
[ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
if [ "$ASYNC" = true ]; then echo "$SUBMIT"; exit 0; fi
# Polling
[ "$JSON_ONLY" = false ] && echo "Waiting for completion..." >&2
ELAPSED=0
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" | jq -r '.status')
if [ "$STATUS" = "completed" ]; then
URL=$(echo "$RESULT" | jq -r '.outputs[0]')
[ "$JSON_ONLY" = false ] && echo "Success! URL: $URL" >&2
if [ "$VIEW" = true ]; then
EXT="${URL##*.}"
[ -z "$EXT" ] || [[ "$EXT" == http* ]] && EXT="jpg"
OUTPUT_DIR="$(dirname "$0")/../../media_outputs"
mkdir -p "$OUTPUT_DIR"
TEMP_FILE="$OUTPUT_DIR/muapi_$(date +%s).$EXT"
[ "$JSON_ONLY" = false ] && echo "Downloading to $TEMP_FILE..." >&2
curl -s -o "$TEMP_FILE" "$URL"
if [[ "$OSTYPE" == "darwin"* ]]; then
open "$TEMP_FILE"
fi
fi
echo "$RESULT"; exit 0
elif [ "$STATUS" = "failed" ]; then
echo "Error: $(echo "$RESULT" | jq -r '.output.error')" >&2; exit 1
fi
done
exit 1
#!/bin/bash
# muapi.ai Text-to-Video Generation
# Usage: ./generate-video.sh --prompt "..." [--model MODEL] [options]
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
SCHEMA_FILE="$(dirname "$0")/../../schema_data.json"
# Defaults
PROMPT=""
MODEL="minimax-pro"
ASPECT_RATIO="16:9"
DURATION=5
GENERATE_AUDIO=true
ASYNC=false
VIEW=false
JSON_ONLY=false
MAX_WAIT=600
POLL_INTERVAL=5
ACTION="generate"
REQUEST_ID=""
# Check for .env
if [ -f ".env" ]; then source .env 2>/dev/null || true; fi
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--prompt|-p) PROMPT="$2"; shift 2 ;;
--model|-m) MODEL="$2"; shift 2 ;;
--aspect-ratio) ASPECT_RATIO="$2"; shift 2 ;;
--duration) DURATION="$2"; shift 2 ;;
--no-audio) GENERATE_AUDIO=false; shift ;;
--async) ASYNC=true; shift ;;
--view) VIEW=true; shift ;;
--status) ACTION="status"; REQUEST_ID="$2"; shift 2 ;;
--result) ACTION="result"; REQUEST_ID="$2"; shift 2 ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Text-to-Video" >&2
echo "" >&2
echo "Usage: ./generate-video.sh --prompt \"...\" [options]" >&2
echo "" >&2
echo "Options:" >&2
echo " --prompt, -p Text description (required)" >&2
echo " --model, -m Model name (default: minimax-pro)" >&2
echo " --aspect-ratio 16:9, 9:16, 1:1" >&2
echo " --duration Length in seconds (3-15)" >&2
echo " --no-audio Disable audio generation" >&2
echo " --async Return request_id immediately" >&2
echo " --view Download and open video (macOS only)" >&2
echo " --status ID Check status of a request" >&2
echo " --json Raw JSON output only" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&2; exit 1; fi
HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
# Handle status/result actions
if [ "$ACTION" = "status" ] || [ "$ACTION" = "result" ]; then
if [ -z "$REQUEST_ID" ]; then echo "Error: Request ID required" >&2; exit 1; fi
RESULT=$(curl -s -X GET "${MUAPI_BASE}/predictions/${REQUEST_ID}/result" "${HEADERS[@]}")
echo "$RESULT"; exit 0
fi
if [ -z "$PROMPT" ]; then echo "Error: --prompt is required" >&2; exit 1; fi
# --- DYNAMIC SCHEMA PARSING ---
if [ ! -f "$SCHEMA_FILE" ]; then echo "Error: schema_data.json not found" >&2; exit 1; fi
MODEL_DATA=$(jq -r ".[] | select(.name == \"$MODEL\")" "$SCHEMA_FILE")
if [ -z "$MODEL_DATA" ]; then
echo "Error: Model '$MODEL' not found in schema" >&2; exit 1
fi
ENDPOINT=$(echo "$MODEL_DATA" | jq -r '.input_schema.schemas.input_data.endpoint_url')
PARAMS=$(echo "$MODEL_DATA" | jq -r '.input_schema.schemas.input_data.properties | keys[]')
# Build Payload
PROMPT_JSON=$(echo "$PROMPT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
PAYLOAD="{\"prompt\": $PROMPT_JSON"
if echo "$PARAMS" | grep -w "aspect_ratio" >/dev/null; then PAYLOAD="$PAYLOAD, \"aspect_ratio\": \"$ASPECT_RATIO\""; fi
if echo "$PARAMS" | grep -w "duration" >/dev/null; then PAYLOAD="$PAYLOAD, \"duration\": $DURATION"; fi
if echo "$PARAMS" | grep -w "generate_audio" >/dev/null; then PAYLOAD="$PAYLOAD, \"generate_audio\": $GENERATE_AUDIO"; fi
PAYLOAD="$PAYLOAD}"
# --- EXECUTION ---
[ "$JSON_ONLY" = false ] && echo "Submitting 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" | jq -r '.error // .detail // empty')
echo "Error: ${ERR:-$SUBMIT}" >&2; exit 1
fi
REQUEST_ID=$(echo "$SUBMIT" | jq -r '.request_id')
if [ "$ASYNC" = true ]; then echo "$SUBMIT"; exit 0; fi
# Polling
[ "$JSON_ONLY" = false ] && echo "Waiting for completion (Request ID: $REQUEST_ID)..." >&2
ELAPSED=0
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" | jq -r '.status')
if [ "$STATUS" = "completed" ]; then
URL=$(echo "$RESULT" | jq -r '.outputs[0]')
[ "$JSON_ONLY" = false ] && echo "Success! URL: $URL" >&2
if [ "$VIEW" = true ]; then
EXT="${URL##*.}"
[ -z "$EXT" ] || [[ "$EXT" == http* ]] && EXT="mp4"
OUTPUT_DIR="$(dirname "$0")/../../media_outputs"
mkdir -p "$OUTPUT_DIR"
TEMP_FILE="$OUTPUT_DIR/muapi_$(date +%s).$EXT"
[ "$JSON_ONLY" = false ] && echo "Downloading to $TEMP_FILE..." >&2
curl -s -o "$TEMP_FILE" "$URL"
if [[ "$OSTYPE" == "darwin"* ]]; then
open "$TEMP_FILE"
fi
fi
echo "$RESULT"; exit 0
elif [ "$STATUS" = "failed" ]; then
echo "Error: $(echo "$RESULT" | jq -r '.output.error')" >&2; exit 1
fi
done
exit 1
#!/bin/bash
# muapi.ai Image-to-Video Generation
# Usage: ./image-to-video.sh --image-url URL --prompt "..." [--model MODEL] [options]
set -e
MUAPI_BASE="https://api.muapi.ai/api/v1"
# Defaults
IMAGE_URL=""
IMAGE_FILE=""
LAST_IMAGE_URL=""
LAST_IMAGE_FILE=""
PROMPT=""
MODEL="kling-pro"
ASPECT_RATIO="16:9"
DURATION=5
ASYNC=false
JSON_ONLY=false
MAX_WAIT=600
POLL_INTERVAL=5
ACTION="generate"
REQUEST_ID=""
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
--image-url) IMAGE_URL="$2"; shift 2 ;;
--file|--image) IMAGE_FILE="$2"; shift 2 ;;
--last-image-url) LAST_IMAGE_URL="$2"; shift 2 ;;
--last-image-file) LAST_IMAGE_FILE="$2"; shift 2 ;;
--prompt|-p) PROMPT="$2"; shift 2 ;;
--model|-m) MODEL="$2"; shift 2 ;;
--aspect-ratio) ASPECT_RATIO="$2"; shift 2 ;;
--duration) DURATION="$2"; shift 2 ;;
--async) ASYNC=true; shift ;;
--status) ACTION="status"; REQUEST_ID="$2"; shift 2 ;;
--result) ACTION="result"; REQUEST_ID="$2"; shift 2 ;;
--timeout) MAX_WAIT="$2"; shift 2 ;;
--json) JSON_ONLY=true; shift ;;
--help|-h)
echo "muapi.ai Image-to-Video" >&2
echo "" >&2
echo "Usage: ./image-to-video.sh --image-url URL --prompt \"...\" [options]" >&2
echo "" >&2
echo "Models (--model):" >&2
echo " kling-std, kling-pro (default), kling-master" >&2
echo " veo3, veo3-fast, wan2, wan22, seedance-pro, seedance-lite" >&2
echo " hunyuan, runway, pixverse, vidu, midjourney" >&2
echo " minimax-std, minimax-pro" >&2
echo "" >&2
echo "Options:" >&2
echo " --image-url URL Input image URL" >&2
echo " --file PATH Local file (auto-uploads)" >&2
echo " --last-image-url URL End frame URL (start+end interpolation)" >&2
echo " --last-image-file PATH Local end frame (auto-uploads)" >&2
echo " --prompt TEXT Motion description" >&2
echo " --aspect-ratio 16:9, 9:16, 1:1 (default: 16:9)" >&2
echo " --duration 5 or 10 seconds (default: 5)" >&2
echo " --async Return request_id immediately" >&2
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$MUAPI_KEY" ]; then
echo "Error: MUAPI_KEY not set" >&2
exit 1
fi
HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
# Status/result check
if [ "$ACTION" = "status" ] || [ "$ACTION" = "result" ]; then
if [ -z "$REQUEST_ID" ]; then echo "Error: Request ID required" >&2; exit 1; fi
RESULT=$(curl -s -X GET "${MUAPI_BASE}/predictions/${REQUEST_ID}/result" "${HEADERS[@]}")
STATUS=$(echo "$RESULT" | grep -oE '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
[ "$JSON_ONLY" = false ] && echo "Status: $STATUS" >&2
if [ "$STATUS" = "completed" ]; then
URL=$(echo "$RESULT" | grep -o '"outputs":\[[^]]*\]' | grep -o '"[^"]*\.mp4"' | head -1 | tr -d '"')
[ -n "$URL" ] && [ "$JSON_ONLY" = false ] && echo "Video URL: $URL" >&2
fi
echo "$RESULT"
exit 0
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 "$IMAGE_FILE" ]; then IMAGE_URL=$(upload_file "$IMAGE_FILE"); fi
if [ -n "$LAST_IMAGE_FILE" ]; then LAST_IMAGE_URL=$(upload_file "$LAST_IMAGE_FILE"); fi
if [ -z "$IMAGE_URL" ]; then
echo "Error: --image-url or --file is required" >&2
exit 1
fi
# Map model to endpoint
case $MODEL in
kling-std) ENDPOINT="kling-v2.1-standard-i2v" ;;
kling-pro) ENDPOINT="kling-v2.1-pro-i2v" ;;
kling-master) ENDPOINT="kling-v2.1-master-i2v" ;;
veo3) ENDPOINT="veo3-image-to-video" ;;
veo3-fast) ENDPOINT="veo3-fast-image-to-video" ;;
wan2) ENDPOINT="wan2.1-image-to-video" ;;
wan22) ENDPOINT="wan2.2-image-to-video" ;;
seedance-pro) ENDPOINT="seedance-pro-i2v" ;;
seedance-lite) ENDPOINT="seedance-lite-i2v" ;;
hunyuan) ENDPOINT="hunyuan-image-to-video" ;;
runway) ENDPOINT="runway-image-to-video" ;;
pixverse) ENDPOINT="pixverse-v4.5-i2v" ;;
vidu) ENDPOINT="vidu-v2.0-i2v" ;;
midjourney) ENDPOINT="midjourney-v7-image-to-video" ;;
minimax-std) ENDPOINT="minimax-hailuo-02-standard-i2v" ;;
minimax-pro) ENDPOINT="minimax-hailuo-02-pro-i2v" ;;
*)
echo "Error: Unknown model '$MODEL'" >&2
exit 1 ;;
esac
# Build payload
PROMPT_JSON=$(echo "$PROMPT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))')
IMAGE_URL_CLEAN=$(echo "$IMAGE_URL" | tr -d '"')
if [ -n "$LAST_IMAGE_URL" ]; then
LAST_JSON=$(echo "$LAST_IMAGE_URL" | tr -d '"')
PAYLOAD="{\"prompt\": $PROMPT_JSON, \"image_url\": \"$IMAGE_URL_CLEAN\", \"last_image\": \"$LAST_JSON\", \"aspect_ratio\": \"$ASPECT_RATIO\", \"duration\": $DURATION}"
else
# veo3 uses images_list array
if [[ "$ENDPOINT" == *"veo3"* ]]; then
PAYLOAD="{\"prompt\": $PROMPT_JSON, \"images_list\": [\"$IMAGE_URL_CLEAN\"], \"aspect_ratio\": \"$ASPECT_RATIO\"}"
else
PAYLOAD="{\"prompt\": $PROMPT_JSON, \"image_url\": \"$IMAGE_URL_CLEAN\", \"aspect_ratio\": \"$ASPECT_RATIO\", \"duration\": $DURATION}"
fi
fi
[ "$JSON_ONLY" = false ] && echo "Submitting 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 in response" >&2
echo "$SUBMIT" >&2
exit 1
fi
[ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
if [ "$ASYNC" = true ]; then
[ "$JSON_ONLY" = false ] && echo "" >&2
[ "$JSON_ONLY" = false ] && echo "Request submitted. Video generation may take 1-5 minutes." >&2
[ "$JSON_ONLY" = false ] && echo "Check: bash check-result.sh --id \"$REQUEST_ID\"" >&2
echo "$SUBMIT"
exit 0
fi
[ "$JSON_ONLY" = false ] && echo "Waiting for completion..." >&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 "Video generation 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: Generation failed: ${ERR:-unknown}" >&2
echo "$RESULT"
exit 1 ;;
esac
done
echo "Error: Timeout after ${MAX_WAIT}s" >&2
echo "Request ID: $REQUEST_ID — Check: bash check-result.sh --id \"$REQUEST_ID\"" >&2
exit 1
#!/bin/bash
# muapi.ai File Upload
# Usage: ./upload.sh --file /path/to/file.jpg
# Returns: CDN URL
set -e
FILE=""
JSON_ONLY=false
JQ_EXPR=".url"
while [[ $# -gt 0 ]]; do
case $1 in
--file|-f) FILE="$2"; shift 2 ;;
--json) JSON_ONLY=true; JQ_EXPR=""; shift ;;
--jq) JQ_EXPR="$2"; shift 2 ;;
--help|-h)
echo "Usage: ./upload.sh --file /path/to/file.jpg"
echo "Returns the CDN URL of the uploaded file."
exit 0 ;;
*) shift ;;
esac
done
if [ -z "$FILE" ]; then echo "Error: --file is required" >&2; exit 1; fi
if [ -f ".env" ]; then source .env 2>/dev/null || true; fi
if [ -z "$MUAPI_KEY" ]; then echo "Error: MUAPI_KEY not set" >&2; exit 1; fi
MUAPI_BASE="https://api.muapi.ai/api/v1"
[ "$JSON_ONLY" = false ] && echo "Uploading $(basename "$FILE")..." >&2
RESP=$(curl -s -X POST "${MUAPI_BASE}/upload_file" -H "x-api-key: $MUAPI_KEY" -F "file=@${FILE}")
if [ "$JSON_ONLY" = true ]; then
echo "$RESP"
elif [ -n "$JQ_EXPR" ]; then
echo "$RESP" | jq -r "$JQ_EXPR"
else
echo "$RESP" | jq -r ".url // empty"
fi
Related skills
FAQ
Which API does muapi-media-generation use?
muapi-media-generation uses the muapi.ai REST API at https://api.muapi.ai/api/v1 through shell scripts like create-music.sh. Developers pass --style and --prompt flags and can enable async mode with polling intervals.
How does muapi-media-generation store API credentials?
muapi-media-generation stores credentials as MUAPI_KEY in a project .env file via the --add-key flag. Scripts support JSON-only output, configurable duration, and reference SUNO_MODEL V5 for generation.
Is Muapi Media Generation safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.