opencode Skill

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

LLM Mart · 0 points · 11 views 27 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download samuraigpt-generative-media-skills-core_edit-74df8cb.zip · 8 KB
Part of samuraigpt/generative-media-skills — 72 skills

Install

skills CLI npx skills add https://github.com/SamurAIGPT/Generative-Media-Skills/tree/main/core/edit
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install samuraigpt-generative-media-skills@llmmart
Git 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_KEY environment variable (set via core/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.

No comments yet.

Reviews (0)

No reviews yet.

Related