Skill: cntuang/video-editor (version 1.0.0)

--- name: video-editor description: Video editing operations using ffmpeg. Use when user needs to: (1) Cut/trim video clips, (2) Merge/join multiple videos, (3) Convert video formats, (4) Extract audio from video, (5) Add subtitles or text overlays, (6) Adjust video speed, (7) Resize/crop video dimensions, (8) Apply filters or effects. Supports common formats like MP4, MOV, AVI, MKV, WebM. homepage: https://ffmpeg.org metadata: { "openclaw": { "emoji": "🎬", "requires": { "bins": ["ffmpeg", "ffprobe"] }, "install": [ { "id": "brew", "kind": "brew", "formula": "ffmpeg", "bins": ["ffmpeg", "ffprobe"], "label": "Install ffmpeg (brew)", }, ], }, } --- # Video Editor (ffmpeg) Video editing operations for cutting, merging, converting, and processing video files. ## Prerequisites Requires `ffmpeg` and `ffprobe` installed: ```bash brew install ffmpeg ``` ## Quick Start ### Cut/Trim Video Extract a segment from a video: ```bash {baseDir}/scripts/cut.sh /path/to/input.mp4 --start 00:00:10 --end 00:00:30 --out /path/to/output.mp4 ``` Or use duration instead of end time: ```bash {baseDir}/scripts/cut.sh /path/to/input.mp4 --start 00:01:00 --duration 30 --out /path/to/output.mp4 ``` ### Merge Videos Concatenate multiple videos (must have same codec/resolution): ```bash {baseDir}/scripts/merge.sh video1.mp4 video2.mp4 video3.mp4 --out merged.mp4 ``` ### Convert Format Convert between video formats: ```bash {baseDir}/scripts/convert.sh input.mov --format mp4 --out output.mp4 ``` ### Extract Audio Extract audio track from video: ```bash {baseDir}/scripts/extract-audio.sh input.mp4 --out audio.mp3 ``` ### Add Subtitles Burn subtitles into video: ```bash {baseDir}/scripts/add-subtitles.sh input.mp4 subtitles.srt --out output.mp4 ``` ### Resize Video Change video resolution: ```bash {baseDir}/scripts/resize.sh input.mp4 --width 1920 --height 1080 --out output.mp4 ``` Or scale proportionally: ```bash {baseDir}/scripts/resize.sh input.mp4 --scale 720 --out output.mp4 ``` ### Adjust Speed Speed up or slow down video: ```bash {baseDir}/scripts/speed.sh input.mp4 --rate 2.0 --out output.mp4 # 2x faster {baseDir}/scripts/speed.sh input.mp4 --rate 0.5 --out output.mp4 # 0.5x slower ``` ### Crop Video Crop to specific region: ```bash {baseDir}/scripts/crop.sh input.mp4 --x 100 --y 100 --width 800 --height 600 --out output.mp4 ``` ## Common Workflows ### Social Media Clip Cut a segment and resize for Instagram/TikTok: ```bash # First cut the segment {baseDir}/scripts/cut.sh input.mp4 --start 00:00:15 --duration 15 --out clip.mp4 # Then resize to vertical format {baseDir}/scripts/resize.sh clip.mp4 --width 1080 --height 1920 --out tiktok.mp4 ``` ### Extract Highlights Cut multiple segments and merge: ```bash {baseDir}/scripts/cut.sh input.mp4 --start 00:00:10 --duration 5 --out highlight1.mp4 {baseDir}/scripts/cut.sh input.mp4 --start 00:01:30 --duration 5 --out highlight2.mp4 {baseDir}/scripts/merge.sh highlight1.mp4 highlight2.mp4 --out highlights.mp4 ``` ### Add Background Music Replace or mix audio: ```bash # Replace audio ffmpeg -i video.mp4 -i music.mp3 -c:v copy -map 0:v:0 -map 1:a:0 -shortest output.mp4 # Mix audio (video audio at 70%, music at 30%) ffmpeg -i video.mp4 -i music.mp3 -filter_complex "[0:a]volume=0.7[a0];[1:a]volume=0.3[a1];[a0][a1]amix=inputs=2:duration=first" -c:v copy -shortest output.mp4 ``` ## Tips - **Quality**: Use `-crf 18` for high quality, `-crf 28` for smaller files (default is 23) - **Presets**: Use `-preset slow` for better compression, `-preset fast` for quicker encoding - **Hardware acceleration**: On Apple Silicon, add `-c:v h264_videotoolbox` for faster encoding - **Copy mode**: Use `-c copy` to avoid re-encoding (much faster, but limited editing) ## Troubleshooting ### Merge fails with "Codec mismatch" Videos must have the same codec, resolution, and frame rate. Re-encode them first: ```bash ffmpeg -i input1.mp4 -c:v libx264 -c:a aac -vf "scale=1920:1080" -r 30 temp1.mp4 ffmpeg -i input2.mp4 -c:v libx264 -c:a aac -vf "scale=1920:1080" -r 30 temp2.mp4 {baseDir}/scripts/merge.sh temp1.mp4 temp2.mp4 --out merged.mp4 ``` ### Subtitles not showing Ensure subtitle file format matches extension (.srt, .ass, .vtt). Check encoding: ```bash file -I subtitles.srt # Should show charset=utf-8 ``` Convert if needed: ```bash iconv -f GBK -t UTF-8 subtitles.srt > subtitles_utf8.srt ```

path: scripts/add-subtitles.sh
content: #!/bin/bash
# Add subtitles to video
# Usage: add-subtitles.sh <input> <subtitles.srt> --out <output>

set -e

INPUT=""
SUBTITLES=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      elif [[ -z "$SUBTITLES" ]]; then
        SUBTITLES="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$SUBTITLES" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: add-subtitles.sh <input> <subtitles.srt> --out <output>"
  exit 1
fi

# Add subtitles (burn into video)
ffmpeg -i "$INPUT" -vf "subtitles='$SUBTITLES'" -c:a copy "$OUTPUT"

echo "Subtitles added: $OUTPUT"


path: scripts/convert.sh
content: #!/bin/bash
# Convert video format
# Usage: convert.sh <input> --format <mp4|mov|avi|mkv|webm> --out <output>

set -e

INPUT=""
FORMAT=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --format)
      FORMAT="$2"
      shift 2
      ;;
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: convert.sh <input> --format <mp4|mov|avi|mkv|webm> --out <output>"
  exit 1
fi

# Convert video
ffmpeg -i "$INPUT" -c:v libx264 -c:a aac -pix_fmt yuv420p "$OUTPUT"

echo "Conversion complete: $OUTPUT"


path: scripts/crop.sh
content: #!/bin/bash
# Crop video region
# Usage: crop.sh <input> --x <x> --y <y> --width <w> --height <h> --out <output>

set -e

INPUT=""
X=""
Y=""
WIDTH=""
HEIGHT=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --x)
      X="$2"
      shift 2
      ;;
    --y)
      Y="$2"
      shift 2
      ;;
    --width)
      WIDTH="$2"
      shift 2
      ;;
    --height)
      HEIGHT="$2"
      shift 2
      ;;
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$X" ]] || [[ -z "$Y" ]] || [[ -z "$WIDTH" ]] || [[ -z "$HEIGHT" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: crop.sh <input> --x <x> --y <y> --width <w> --height <h> --out <output>"
  exit 1
fi

# Crop video
ffmpeg -i "$INPUT" -vf "crop=$WIDTH:$HEIGHT:$X:$Y" -c:a copy "$OUTPUT"

echo "Crop complete: $OUTPUT"


path: scripts/cut.sh
content: #!/bin/bash
# Cut/trim video segment
# Usage: cut.sh <input> --start <time> [--end <time> | --duration <seconds>] --out <output>

set -e

INPUT=""
START=""
END=""
DURATION=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --start)
      START="$2"
      shift 2
      ;;
    --end)
      END="$2"
      shift 2
      ;;
    --duration)
      DURATION="$2"
      shift 2
      ;;
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$START" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: cut.sh <input> --start <time> [--end <time> | --duration <seconds>] --out <output>"
  echo "Time format: HH:MM:SS or seconds"
  exit 1
fi

if [[ -z "$END" ]] && [[ -z "$DURATION" ]]; then
  echo "Error: Must specify either --end or --duration"
  exit 1
fi

# Build ffmpeg command
if [[ -n "$DURATION" ]]; then
  ffmpeg -i "$INPUT" -ss "$START" -t "$DURATION" -c copy -avoid_negative_ts make_zero "$OUTPUT"
else
  ffmpeg -i "$INPUT" -ss "$START" -to "$END" -c copy -avoid_negative_ts make_zero "$OUTPUT"
fi

echo "Cut complete: $OUTPUT"


path: scripts/extract-audio.sh
content: #!/bin/bash
# Extract audio from video
# Usage: extract-audio.sh <input> --out <output.mp3|aac|wav>

set -e

INPUT=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: extract-audio.sh <input> --out <output.mp3|aac|wav>"
  exit 1
fi

# Determine codec from extension
EXT="${OUTPUT##*.}"
case "$EXT" in
  mp3)
    CODEC="libmp3lame"
    ;;
  aac|m4a)
    CODEC="aac"
    ;;
  wav)
    CODEC="pcm_s16le"
    ;;
  flac)
    CODEC="flac"
    ;;
  *)
    CODEC="copy"
    ;;
esac

# Extract audio
ffmpeg -i "$INPUT" -vn -c:a "$CODEC" "$OUTPUT"

echo "Audio extraction complete: $OUTPUT"


path: scripts/merge.sh
content: #!/bin/bash
# Merge multiple videos
# Usage: merge.sh <video1> <video2> [video3...] --out <output>

set -e

VIDEOS=()
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      VIDEOS+=("$1")
      shift
      ;;
  esac
done

if [[ ${#VIDEOS[@]} -lt 2 ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: merge.sh <video1> <video2> [video3...] --out <output>"
  exit 1
fi

# Create temp file list
TEMP_LIST=$(mktemp)
trap "rm -f $TEMP_LIST" EXIT

for video in "${VIDEOS[@]}"; do
  echo "file '$(realpath "$video")'" >> "$TEMP_LIST"
done

# Merge videos
ffmpeg -f concat -safe 0 -i "$TEMP_LIST" -c copy "$OUTPUT"

echo "Merge complete: $OUTPUT"


path: scripts/resize.sh
content: #!/bin/bash
# Resize video
# Usage: resize.sh <input> [--width <w> --height <h> | --scale <height>] --out <output>

set -e

INPUT=""
WIDTH=""
HEIGHT=""
SCALE=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --width)
      WIDTH="$2"
      shift 2
      ;;
    --height)
      HEIGHT="$2"
      shift 2
      ;;
    --scale)
      SCALE="$2"
      shift 2
      ;;
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: resize.sh <input> [--width <w> --height <h> | --scale <height>] --out <output>"
  exit 1
fi

if [[ -n "$SCALE" ]]; then
  # Scale proportionally to height
  ffmpeg -i "$INPUT" -vf "scale=-2:$SCALE" -c:a copy "$OUTPUT"
elif [[ -n "$WIDTH" ]] && [[ -n "$HEIGHT" ]]; then
  # Exact dimensions
  ffmpeg -i "$INPUT" -vf "scale=$WIDTH:$HEIGHT" -c:a copy "$OUTPUT"
else
  echo "Error: Must specify either --scale or both --width and --height"
  exit 1
fi

echo "Resize complete: $OUTPUT"


path: scripts/speed.sh
content: #!/bin/bash
# Adjust video speed
# Usage: speed.sh <input> --rate <speed> --out <output>
# speed: 2.0 = 2x faster, 0.5 = half speed

set -e

INPUT=""
RATE=""
OUTPUT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --rate)
      RATE="$2"
      shift 2
      ;;
    --out)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      if [[ -z "$INPUT" ]]; then
        INPUT="$1"
      else
        echo "Unexpected argument: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if [[ -z "$INPUT" ]] || [[ -z "$RATE" ]] || [[ -z "$OUTPUT" ]]; then
  echo "Usage: speed.sh <input> --rate <speed> --out <output>"
  echo "speed: 2.0 = 2x faster, 0.5 = half speed"
  exit 1
fi

# Adjust speed using setpts for video and atempo for audio
# atempo only works between 0.5 and 2.0, so we may need to chain filters
if (( $(echo "$RATE >= 0.5 && $RATE <= 2.0" | bc -l) )); then
  ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=PTS/$RATE[v];[0:a]atempo=$RATE[a]" -map "[v]" -map "[a]" "$OUTPUT"
else
  echo "Warning: Speed rate outside 0.5-2.0 range may require multiple filter passes"
  ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=PTS/$RATE[v]" -map "[v]" -an "$OUTPUT"
fi

echo "Speed adjustment complete: $OUTPUT"


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)