#!/usr/bin/env bash # # Remove audio from video files # Usage: mute.sh [options] [output] set -e # Default values VCODEC="copy" OVERWRITE=false # Show usage usage() { cat <<'EOF' Usage: mute.sh [options] [output] Options: -c, --copy Copy video stream without re-encoding (default, fastest) -v, --vcodec Video codec for re-encoding (e.g., libx264, libx265) -y, --yes Overwrite output without asking -h, --help Show this help Examples: mute.sh input.mp4 output.mp4 # Remove audio, copy video mute.sh input.mp4 # Creates input-muted.mp4 mute.sh -c input.mp4 # Same as above (explicit copy) mute.sh -v libx264 input.mp4 # Re-encode with H.264 mute.sh -y *.mp4 # Batch mute, overwrite existing EOF exit 0 } # Check if ffmpeg is installed check_deps() { if ! command -v ffmpeg &>/dev/null; then echo "Error: ffmpeg not found. Please install ffmpeg." >&2 echo " macOS: brew install ffmpeg" >&2 echo " Ubuntu/Debian: sudo apt-get install ffmpeg" >&2 echo " Arch: sudo pacman -S ffmpeg" >&2 return 1 fi } # Get file extension (lowercase) get_extension() { local filename="$1" echo "${filename##*.}" | tr '[:upper:]' '[:lower:]' } # Get basename without extension get_basename() { local filename="$1" echo "${filename%.*}" } # Remove audio from a single file mute_file() { local input="$1" local output="$2" if [[ ! -f "$input" ]]; then echo "Error: Input file not found: $input" >&2 return 1 fi # Auto-generate output name if not provided if [[ -z "$output" ]]; then local ext ext=$(get_extension "$input") output="$(get_basename "$input")-muted.$ext" fi # Check if output exists if [[ -f "$output" && "$OVERWRITE" == false ]]; then read -r -p "Output file exists: $output. Overwrite? [y/N] " response if [[ ! "$response" =~ ^[Yy]$ ]]; then echo "Skipping: $input" return 0 fi fi # Create output directory if needed local out_dir out_dir=$(dirname "$output") if [[ "$out_dir" != "." && ! -d "$out_dir" ]]; then mkdir -p "$out_dir" fi echo "Removing audio: $input -> $output" # Build ffmpeg command local cmd=(ffmpeg -i "$input" -an -vcodec "$VCODEC") if [[ "$OVERWRITE" == true ]]; then cmd+=(-y) fi # Add faststart for web optimization when using copy mode if [[ "$VCODEC" == "copy" ]]; then cmd+=(-movflags +faststart) fi cmd+=("$output") "${cmd[@]}" echo "Created: $output" } # Main main() { local inputs=() local output="" # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in -c | --copy) VCODEC="copy" shift ;; -v | --vcodec) VCODEC="$2" shift 2 ;; -y | --yes) OVERWRITE=true shift ;; -h | --help) usage ;; -*) echo "Unknown option: $1" >&2 exit 1 ;; *) inputs+=("$1") shift ;; esac done if [[ ${#inputs[@]} -eq 0 ]]; then echo "Error: No input files specified" >&2 usage fi # Check dependencies check_deps # Determine if last argument is an output file (single file mode) # Heuristic: single input with different extension = explicit output if [[ ${#inputs[@]} -eq 2 ]]; then local in_ext out_ext in_ext=$(get_extension "${inputs[0]}") out_ext=$(get_extension "${inputs[1]}") # If extensions differ, treat second as output if [[ "$in_ext" != "$out_ext" ]]; then output="${inputs[1]}" inputs=("${inputs[0]}") fi fi # Batch processing mode if [[ ${#inputs[@]} -gt 1 ]]; then for input in "${inputs[@]}"; do mute_file "$input" "" done return 0 fi # Single file mode mute_file "${inputs[0]}" "$output" } main "$@"