Skip to content

Instantly share code, notes, and snippets.

@arenagroove
Last active April 22, 2025 05:38
Show Gist options
  • Select an option

  • Save arenagroove/defbb8a8bebb29f27fff5f83b224792f to your computer and use it in GitHub Desktop.

Select an option

Save arenagroove/defbb8a8bebb29f27fff5f83b224792f to your computer and use it in GitHub Desktop.
PowerShell script to batch-generate PNG and WebP thumbnails from images using FFmpeg. Supports forced aspect ratios via mapping or automatic fallback to original image proportions. Configurable output size and quality settings.

Thumbnail Generator with Aspect Ratio Control (PowerShell)

Batch-generate PNG and WebP thumbnails from a folder of images using FFmpeg. It supports flexible cropping behavior based on a per-image aspect ratio map, a global override, or the original image dimensions. Output dimensions and quality for each format are configurable.

Features

  • Removes previously generated thumbnails
  • Uses FFmpeg to generate thumbnails in PNG and WebP formats
  • Supports forced aspect ratio cropping via a configurable mapping
  • Optionally forces all thumbnails to a fixed aspect ratio
  • Automatically falls back to preserving the original aspect ratio if not mapped
  • Adjustable output width and quality for both PNG and WebP
  • Logs progress and handles errors gracefully

Requirements

  • FFmpeg and ffprobe must be installed and available in the system PATH
  • PowerShell 5.0 or later

Configuration

At the top of the script, you can adjust the following parameters:

$fixedWidth = 400         # Width of the thumbnail in pixels
$pngQuality = 1           # PNG quality (1 = best, 31 = worst)
$webpQuality = 96         # WebP quality (0 = worst, 100 = best)

# Optional: force all images to use this aspect ratio (e.g. "1:1", "16:9")
# Set to $null to disable and fall back to per-image or original aspect ratios
$forceAspectRatio = "1:1"

JavaScript Attempt

CodePen Demo

# ---------------------------------------------------------
# Thumbnail Generator with Aspect Ratio Control (PowerShell)
# ---------------------------------------------------------
# Remove old thumbnails
Remove-Item *-thumb.webp -Force -ErrorAction SilentlyContinue
Remove-Item *-thumb.png -Force -ErrorAction SilentlyContinue
# Aspect ratio mapping for specific images
$aspectRatios = @{
"lr-demo-img-01" = "9:16"
"lr-demo-img-02" = "16:9"
# Add more mappings here if needed
}
# === Configuration ===
$fixedWidth = 400 # Width for all thumbnails
$pngQuality = 1 # PNG: 1 (best) to 31 (worst)
$webpQuality = 96 # WebP: 0 (worst) to 100 (best)
# Optional: force all images to use this aspect ratio (e.g. "1:1")
# Set to $null to disable global override
$forceAspectRatio = $null
# ======================
# Process each image
Get-ChildItem -File | Where-Object { $_.Extension -match "\.(jpg|png|webp)$" } | ForEach-Object {
$originalPath = $_.FullName
$filename = $_.Name
$cleanName = $_.BaseName
try {
# Get original dimensions
$width = ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=p=0 "`"$originalPath`""
$height = ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "`"$originalPath`""
$width = [int]$width
$height = [int]$height
if ($width -eq 0 -or $height -eq 0) {
Write-Output "Error: Could not retrieve dimensions for $filename. Skipping."
return
}
} catch {
Write-Output "Error: Failed to run ffprobe on $filename. Skipping."
return
}
$vfFilter = ""
if ($forceAspectRatio) {
$aspectRatio = $forceAspectRatio
Write-Output "Processing $filename with GLOBAL FORCED aspect ratio $aspectRatio"
} elseif ($aspectRatios.ContainsKey($cleanName)) {
$aspectRatio = $aspectRatios[$cleanName]
Write-Output "Processing $filename with MAPPED aspect ratio $aspectRatio"
} else {
Write-Output "Processing $filename with ORIGINAL aspect ratio (not in list)"
$scaledHeight = [math]::Ceiling($fixedWidth * ($height / $width))
$vfFilter = "scale=${fixedWidth}:${scaledHeight},setsar=1"
}
if (-not $vfFilter) {
# Aspect ratio logic
$ratioParts = $aspectRatio -split ":"
$aspectWidth = [int]$ratioParts[0]
$aspectHeight = [int]$ratioParts[1]
$targetHeight = [math]::Ceiling($fixedWidth * ($aspectHeight / $aspectWidth))
$OAR = $width / $height
$TAR = $aspectWidth / $aspectHeight
if ([math]::Round($OAR, 2) -eq [math]::Round($TAR, 2)) {
$vfFilter = "scale=${fixedWidth}:-2,setsar=1"
} elseif ($OAR -gt $TAR) {
$vfFilter = "scale=-2:${targetHeight},crop=${fixedWidth}:${targetHeight}:(iw-${fixedWidth})/2:0,setsar=1"
} else {
$vfFilter = "scale=${fixedWidth}:-2,crop=${fixedWidth}:${targetHeight}:0:(ih-${targetHeight})/2,setsar=1"
}
}
# Output file paths
$outputPathPng = "$cleanName-thumb.png"
$outputPathWebp = "$cleanName-thumb.webp"
# FFmpeg commands with quality settings
$ffmpegCommandPng = "ffmpeg -i `"$originalPath`" -vf `"$vfFilter`" -q:v ${pngQuality} `"$outputPathPng`" -y"
$ffmpegCommandWebp = "ffmpeg -i `"$originalPath`" -vf `"$vfFilter`" -q:v ${webpQuality} `"$outputPathWebp`" -y"
try {
Invoke-Expression $ffmpegCommandPng
Invoke-Expression $ffmpegCommandWebp
Write-Output "✔️ Successfully created thumbnails for $filename"
} catch {
Write-Output "❌ Error generating thumbnails for $filename : $_"
}
}
Write-Output "`n✅ Thumbnail generation complete."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment