muapi-media-editing
Edit and enhance images and videos with AI via muapi.ai — prompt-based editing, upscaling, background removal, face swap, lipsync, video effects, and more
Install
npx skills add https://github.com/SamurAIGPT/Generative-Media-Skills/tree/main/core/edit
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install samuraigpt-generative-media-skills@llmmart
git clone https://github.com/SamurAIGPT/Generative-Media-Skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole samuraigpt/generative-media-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
✏️ 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
Files (generative-media-skills)
-
edit-image.sh 1.7 KB
#!/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[@]}" -
enhance-image.sh 7.9 KB
#!/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 -
lipsync.sh 5.5 KB
#!/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 -
SKILL.md 1.7 KB
--- name: muapi-media-editing version: 0.1.0 description: Edit and enhance images and videos with AI via muapi.ai — prompt-based editing, upscaling, background removal, face swap, lipsync, video effects, and more --- # ✏️ 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 ```bash # 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_KEY` environment variable (set via `core/platform/setup.sh`) - `curl`, `jq`, `python3` -
video-effects.sh 8.4 KB
#!/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
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.