
Muapi Social Pack
- 567 installs
- 4k repo stars
- Updated August 4, 2026
- samuraigpt/generative-media-skills
muapi-social-pack is a Claude Code skill that assembles AI-generated social media image and video packs via muapi.ai for developers who need multi-format launch and campaign assets without a design toolchain.
About
muapi-social-pack is a generative-media skill from samuraigpt/generative-media-skills (version 0.1.0) that packages muapi.ai editing and generation into social-ready asset sets. The shared repo documents shell workflows such as edit-image.sh plus prompt-based editing with Flux Kontext, GPT-4o, Midjourney, and Qwen, alongside one-click upscaling, background removal, lipsync, and video effects. Developers reach for muapi-social-pack when a product launch or content calendar needs coordinated posts across formats and they want agent-driven API calls instead of manual exports from Figma or Premiere. The skill suits sessions where copy and brand references exist and the goal is repeatable social pack generation tied to muapi.ai scripts.
- muapi-social-pack
Muapi Social Pack by the numbers
- 567 all-time installs (skills.sh)
- +47 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #741 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-social-packAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 567 |
|---|---|
| repo stars | ★ 4k |
| Last updated | August 4, 2026 |
| Repository | samuraigpt/generative-media-skills ↗ |
How do you generate a social media asset pack with muapi.ai?
Use muapi-social-pack for development tasks
Who is it for?
Developers shipping product updates who need coordinated social creatives generated through muapi.ai inside coding agents.
Skip if: Teams needing strict brand-governance templates, print-ready CMYK assets, or organic community management workflows.
When should I use this skill?
The user asks for a social media pack, launch post set, or batch muapi.ai creatives for multiple channels.
What you get
Multi-format social images, edited video clips, and muapi.ai export files sized for distribution channels.
- social image set
- short-form video clips
By the numbers
- Repo version 0.1.0 in generative-media-skills
- Documents edit-image.sh for prompt-based editing
- Lists Flux Kontext, GPT-4o, Midjourney, and Qwen model support
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-social-pack for API-batch social creatives; use a design-system workflow when every pixel must match locked Figma components.
FAQ
What does muapi-social-pack produce?
muapi-social-pack produces coordinated social images and short videos by calling muapi.ai through samuraigpt/generative-media-skills (version 0.1.0). Outputs come from prompt-based editing and effects documented in scripts such as edit-image.sh.
Which AI models does the MuAPI social pack skill use?
The MuAPI media-editing repo lists Flux Kontext, GPT-4o, Midjourney, and Qwen for prompt-based edits, plus one-click upscaling, background removal, lipsync, and video effects suitable for social formats.