288 lines
9.8 KiB
Bash
Executable File
288 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Script to convert WebM files to GIF with alpha transparency
|
|
# Usage: ./webm_to_gif.sh [directory_path] [fps] [resize_factor] [duration] [background] [compression]
|
|
|
|
# Function to display usage
|
|
usage() {
|
|
echo "Usage: $0 [directory_path] [fps] [resize_factor] [duration] [background] [compression]"
|
|
echo " directory_path: Path to directory containing WebM files (default: current directory)"
|
|
echo " fps: Target frames per second for GIF (default: 7)"
|
|
echo " resize_factor: Scale factor for resizing (default: 0.2 = 5x smaller)"
|
|
echo " duration: Duration in seconds to convert from start (default: 3)"
|
|
echo " background: Background color for transparency (default: transparent)"
|
|
echo " - 'transparent' for alpha transparency"
|
|
echo " - Hex colors: '#000000', '#ffffff', '#ff0000', etc."
|
|
echo " - Named colors: 'black', 'white', 'red', 'blue', etc."
|
|
echo " compression: Compression level 1-5 (default: 3)"
|
|
echo " - 1: Highest quality, largest files (256 colors)"
|
|
echo " - 2: High quality (128 colors)"
|
|
echo " - 3: Balanced (64 colors)"
|
|
echo " - 4: Good compression (32 colors)"
|
|
echo " - 5: Maximum compression (16 colors)"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 # All defaults"
|
|
echo " $0 /path/to/files # Custom dir"
|
|
echo " $0 /path/to/files 15 # Custom dir and fps"
|
|
echo " $0 /path/to/files 15 0.5 # Custom dir, fps, and scale"
|
|
echo " $0 /path/to/files 15 0.5 5 # All params except background/compression"
|
|
echo " $0 /path/to/files 15 0.5 5 black # Black background"
|
|
echo " $0 /path/to/files 15 0.5 5 black 5 # Black background, max compression"
|
|
echo ""
|
|
echo "This script will:"
|
|
echo " - Find all .webm files in the specified directory"
|
|
echo " - Convert to GIF format maintaining alpha transparency"
|
|
echo " - Use VP9 decoder for optimal alpha preservation"
|
|
echo " - Save results in a 'gif' subdirectory"
|
|
exit 1
|
|
}
|
|
|
|
# Check if ffmpeg is installed
|
|
if ! command -v ffmpeg &> /dev/null; then
|
|
echo "Error: ffmpeg is not installed or not in PATH"
|
|
echo "Please install ffmpeg to use this script"
|
|
exit 1
|
|
fi
|
|
|
|
# Set default values
|
|
DIRECTORY="."
|
|
FPS=7
|
|
RESIZE_FACTOR=0.2
|
|
DURATION=3
|
|
BACKGROUND="transparent"
|
|
COMPRESSION=3
|
|
|
|
# Parse arguments
|
|
if [ $# -eq 0 ]; then
|
|
DIRECTORY="."
|
|
elif [ $# -eq 1 ]; then
|
|
DIRECTORY="$1"
|
|
elif [ $# -eq 2 ]; then
|
|
DIRECTORY="$1"
|
|
FPS="$2"
|
|
elif [ $# -eq 3 ]; then
|
|
DIRECTORY="$1"
|
|
FPS="$2"
|
|
RESIZE_FACTOR="$3"
|
|
elif [ $# -eq 4 ]; then
|
|
DIRECTORY="$1"
|
|
FPS="$2"
|
|
RESIZE_FACTOR="$3"
|
|
DURATION="$4"
|
|
elif [ $# -eq 5 ]; then
|
|
DIRECTORY="$1"
|
|
FPS="$2"
|
|
RESIZE_FACTOR="$3"
|
|
DURATION="$4"
|
|
BACKGROUND="$5"
|
|
elif [ $# -eq 6 ]; then
|
|
DIRECTORY="$1"
|
|
FPS="$2"
|
|
RESIZE_FACTOR="$3"
|
|
DURATION="$4"
|
|
BACKGROUND="$5"
|
|
COMPRESSION="$6"
|
|
else
|
|
echo "Error: Too many arguments"
|
|
usage
|
|
fi
|
|
|
|
# Validate fps parameter
|
|
if ! [[ "$FPS" =~ ^[0-9]+(\.[0-9]+)?$ ]] || (( $(echo "$FPS <= 0" | bc -l) )); then
|
|
echo "Error: fps must be a positive number"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate resize factor parameter
|
|
if ! [[ "$RESIZE_FACTOR" =~ ^[0-9]+(\.[0-9]+)?$ ]] || (( $(echo "$RESIZE_FACTOR <= 0" | bc -l) )); then
|
|
echo "Error: resize_factor must be a positive number"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate duration parameter
|
|
if ! [[ "$DURATION" =~ ^[0-9]+(\.[0-9]+)?$ ]] || (( $(echo "$DURATION <= 0" | bc -l) )); then
|
|
echo "Error: duration must be a positive number"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate background parameter
|
|
if [ "$BACKGROUND" != "transparent" ]; then
|
|
# Check if it's a valid hex color or named color
|
|
if ! [[ "$BACKGROUND" =~ ^#[0-9a-fA-F]{6}$ ]] && ! [[ "$BACKGROUND" =~ ^[a-zA-Z]+$ ]]; then
|
|
echo "Error: background must be 'transparent', a hex color (#000000), or a named color (black, white, etc.)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Validate compression parameter
|
|
if ! [[ "$COMPRESSION" =~ ^[1-5]$ ]]; then
|
|
echo "Error: compression must be between 1-5"
|
|
exit 1
|
|
fi
|
|
|
|
# Set compression values based on level
|
|
case $COMPRESSION in
|
|
1) MAX_COLORS=256; DITHER_SCALE=2 ;; # Highest quality
|
|
2) MAX_COLORS=128; DITHER_SCALE=3 ;; # High quality
|
|
3) MAX_COLORS=64; DITHER_SCALE=4 ;; # Balanced (default)
|
|
4) MAX_COLORS=32; DITHER_SCALE=5 ;; # Good compression
|
|
5) MAX_COLORS=16; DITHER_SCALE=5 ;; # Maximum compression
|
|
esac
|
|
|
|
# Check if directory exists
|
|
if [ ! -d "$DIRECTORY" ]; then
|
|
echo "Error: Directory '$DIRECTORY' does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# Convert to absolute path
|
|
DIRECTORY=$(realpath "$DIRECTORY")
|
|
echo "Processing WebM files in: $DIRECTORY"
|
|
echo "Target FPS: $FPS"
|
|
echo "Resize factor: ${RESIZE_FACTOR}x ($(echo "scale=1; 1/$RESIZE_FACTOR" | bc)x smaller)"
|
|
echo "Duration: first $DURATION seconds"
|
|
echo "Background: $BACKGROUND"
|
|
echo "Compression: Level $COMPRESSION ($MAX_COLORS colors, dither scale $DITHER_SCALE)"
|
|
|
|
# Create gif output directory
|
|
GIF_DIR="$DIRECTORY/gif"
|
|
if [ ! -d "$GIF_DIR" ]; then
|
|
mkdir -p "$GIF_DIR"
|
|
echo "Created output directory: $GIF_DIR"
|
|
else
|
|
echo "Using existing output directory: $GIF_DIR"
|
|
fi
|
|
|
|
# Find all WebM files in the directory
|
|
WEBM_FILES=($(find "$DIRECTORY" -maxdepth 1 -name "*.webm" -type f))
|
|
|
|
# Check if any WebM files were found
|
|
if [ ${#WEBM_FILES[@]} -eq 0 ]; then
|
|
echo "No WebM files found in '$DIRECTORY'"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Found ${#WEBM_FILES[@]} WebM file(s)"
|
|
echo "Note: Existing GIF files will be overwritten to ensure proper transparency"
|
|
echo ""
|
|
|
|
# Counter for processed files
|
|
PROCESSED=0
|
|
FAILED=0
|
|
OVERWRITTEN=0
|
|
NEW_FILES=0
|
|
|
|
# Process each WebM file
|
|
for webm_file in "${WEBM_FILES[@]}"; do
|
|
# Get the base filename without path and extension
|
|
base_name=$(basename "$webm_file" .webm)
|
|
|
|
# Output GIF file path
|
|
gif_output="$GIF_DIR/${base_name}.gif"
|
|
|
|
echo "Processing: $(basename "$webm_file")"
|
|
|
|
# Check if output file already exists
|
|
FILE_EXISTS=false
|
|
if [ -f "$gif_output" ]; then
|
|
echo " → Overwriting existing file: ${base_name}.gif"
|
|
FILE_EXISTS=true
|
|
fi
|
|
|
|
# Detect codec type (VP8 or VP9) to use correct decoder
|
|
CODEC_INFO=$(ffprobe -v quiet -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 "$webm_file" 2>/dev/null)
|
|
|
|
# Set appropriate decoder based on codec (VP9 preferred for alpha)
|
|
if [ "$CODEC_INFO" = "vp9" ]; then
|
|
DECODER="libvpx-vp9"
|
|
echo " → Using VP9 decoder (optimal for alpha preservation)"
|
|
else
|
|
DECODER="libvpx" # Default to VP8 decoder
|
|
echo " → Using VP8 decoder"
|
|
fi
|
|
|
|
# Create temporary files for palette generation
|
|
TEMP_DIR=$(mktemp -d)
|
|
PALETTE_FILE="$TEMP_DIR/palette.png"
|
|
|
|
echo " → Generating optimized palette with transparency support..."
|
|
|
|
# Set up filter chains based on background setting
|
|
if [ "$BACKGROUND" = "transparent" ]; then
|
|
PALETTE_FILTER="fps=$FPS,scale=iw*$RESIZE_FACTOR:ih*$RESIZE_FACTOR:flags=lanczos,palettegen=transparency_color=ffffff:reserve_transparent=on:max_colors=$MAX_COLORS"
|
|
CONVERT_FILTER="fps=$FPS,scale=iw*$RESIZE_FACTOR:ih*$RESIZE_FACTOR:flags=lanczos[x];[x][1:v]paletteuse=alpha_threshold=128:dither=bayer:bayer_scale=$DITHER_SCALE"
|
|
echo " → Using transparent background (alpha channel preserved)"
|
|
else
|
|
PALETTE_FILTER="fps=$FPS,scale=iw*$RESIZE_FACTOR:ih*$RESIZE_FACTOR:flags=lanczos,split[a][b];[a]geq=r=0:g=0:b=0:a=255,drawbox=color=$BACKGROUND@1.0:replace=1:t=fill[bg];[bg][b]overlay=format=auto,palettegen=max_colors=$MAX_COLORS"
|
|
CONVERT_FILTER="fps=$FPS,scale=iw*$RESIZE_FACTOR:ih*$RESIZE_FACTOR:flags=lanczos,split[a][b];[a]geq=r=0:g=0:b=0:a=255,drawbox=color=$BACKGROUND@1.0:replace=1:t=fill[bg];[bg][b]overlay=format=auto[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=$DITHER_SCALE"
|
|
echo " → Using background color: $BACKGROUND (fills transparent areas only)"
|
|
fi
|
|
|
|
# Generate palette with transparency support
|
|
if ffmpeg -c:v "$DECODER" \
|
|
-i "$webm_file" \
|
|
-t "$DURATION" \
|
|
-vf "$PALETTE_FILTER" \
|
|
-y \
|
|
"$PALETTE_FILE" \
|
|
-v quiet 2>/dev/null; then
|
|
|
|
echo " → Converting to GIF..."
|
|
|
|
# Convert to GIF using the generated palette
|
|
if ffmpeg -c:v "$DECODER" \
|
|
-i "$webm_file" \
|
|
-i "$PALETTE_FILE" \
|
|
-t "$DURATION" \
|
|
-lavfi "$CONVERT_FILTER" \
|
|
-y \
|
|
"$gif_output" \
|
|
-v quiet 2>/dev/null; then
|
|
|
|
if [ "$FILE_EXISTS" = true ]; then
|
|
echo " ✓ Successfully overwritten: ${base_name}.gif"
|
|
((OVERWRITTEN++))
|
|
else
|
|
echo " ✓ Successfully created: ${base_name}.gif"
|
|
((NEW_FILES++))
|
|
fi
|
|
((PROCESSED++))
|
|
else
|
|
echo " ✗ Failed to convert to GIF: $(basename "$webm_file")"
|
|
((FAILED++))
|
|
fi
|
|
else
|
|
echo " ✗ Failed to generate palette for: $(basename "$webm_file")"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Clean up temp directory
|
|
rm -rf "$TEMP_DIR"
|
|
echo ""
|
|
done
|
|
|
|
# Summary
|
|
echo "=== Processing Complete ==="
|
|
echo "Total files processed: $PROCESSED"
|
|
if [ $NEW_FILES -gt 0 ]; then
|
|
echo "New files created: $NEW_FILES"
|
|
fi
|
|
if [ $OVERWRITTEN -gt 0 ]; then
|
|
echo "Existing files overwritten: $OVERWRITTEN"
|
|
fi
|
|
if [ $FAILED -gt 0 ]; then
|
|
echo "Failed to process: $FAILED files"
|
|
fi
|
|
echo "Output directory: $GIF_DIR"
|
|
echo "Settings used - FPS: $FPS, Scale: ${RESIZE_FACTOR}x, Duration: ${DURATION}s, Background: $BACKGROUND, Compression: $COMPRESSION"
|
|
echo ""
|
|
|
|
# List generated files
|
|
if [ $PROCESSED -gt 0 ]; then
|
|
echo "Generated GIF files:"
|
|
ls -la "$GIF_DIR"/*.gif 2>/dev/null | while read -r line; do
|
|
echo " $line"
|
|
done
|
|
fi
|