Skip to content

Instantly share code, notes, and snippets.

@angstyfrostyneko
Last active June 19, 2025 09:38
Show Gist options
  • Save angstyfrostyneko/6ce11f37ae6f068247ad294cecde72e5 to your computer and use it in GitHub Desktop.
Save angstyfrostyneko/6ce11f37ae6f068247ad294cecde72e5 to your computer and use it in GitHub Desktop.
FFMPEG wrapper that compresses videos under target size
param (
[Parameter(Mandatory)][string]$video,
[float]$size = 10.0,
[string]$name = "$($size)MB $([System.IO.Path]::GetFileNameWithoutExtension($video))",
[ValidateSet("av1", "h264", "h265", "vp9")]
[string]$encoder = "h265",
[ValidateSet("amd", "nvidia", "intel", "none")]
[string]$gpu = "amd",
[ValidatePattern("\d+:\d+:\d+")]
[string]$start = "00:00:00",
[ValidatePattern("\d+:\d+:\d+")]
[string]$end = "00:00:00",
[ValidatePattern("\d+:\d+")]
[string]$aspect_ratio = "$(ffprobe -v quiet -show_entries stream=display_aspect_ratio -of default=noprint_wrappers=1:nokey=1 $video)",
[switch]$dont_copy_to_clipboard,
[switch]$delete_original,
[switch]$preserve_logs,
[float]$volume = 1.0,
[Alias("resolution")]
[int]$height = 900,
[int]$audio_bitrate = 128
)
if ($aspect_ratio -eq "N/A") {
$w = "$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 $video)"
$h = "$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 $video)"
if ($w -gt $h) {
$a = $w
$b = $h
}
else {
$a = $h
$b = $w
}
while ($b -ne 0) {
$r = $a % $b
$a = $b
$b = $r
}
$aspect_ratio = "$([int]($w/$a)):$([int]($h/$a))"
}
if ((Get-Item $video).length / 1MB -lt $size) {
Rename-Item $video -NewName "$($name)$([System.IO.Path]::GetExtension($video))"
exit
}
$width = [math]::Round(($height / $aspect_ratio.Split(":")[1]) * $aspect_ratio.Split(":")[0])
if ($width % 2 -eq 1) { $width -= 1 }
$start_seconds = [TimeSpan]::Parse($start).TotalSeconds
$end_seconds = [TimeSpan]::Parse($end).TotalSeconds
$duration = ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 $video
if ($end_seconds -eq "0") { $end_seconds = $duration }
else { $duration -= $duration - $end_seconds }
$duration -= $start_seconds
$video_bitrate = ($size * 8KB) / $duration - $audio_bitrate
if ($video_bitrate -lt 0) {
Write-Host "With the current audio bitrate the video bitrate is negative. Please lower the audio bitrate with '-audio_bitrate'!"
exit
}
$video_codec = ""
$audio_codec = "aac"
$video_extension = "mp4"
$repeat_count = 0.01
switch ($encoder) {
"av1" {
switch ($gpu) {
"nvidia" { $video_codec = "av1_nvenc" }
"intel" { $video_codec = "av1_qsv" }
"amd" { $video_codec = "av1_amf" }
default { $video_codec = "libaom-av1" }
}
$video_extension = "mkv"
}
"vp9" {
switch ($gpu) {
"intel" { $video_codec = "vp9_qsv" }
default { $video_codec = "libvpx-vp9" }
}
$audio_codec = "libopus"
$video_extension = "webm"
}
"h264" {
switch ($gpu) {
"amd" { $video_codec = "h264_amf" }
"nvidia" { $video_codec = "h264_nvenc" }
"intel" { $video_codec = "h264_qsv" }
default { $video_codec = "libx264" }
}
}
"h265" {
switch ($gpu) {
"amd" { $video_codec = "hevc_amf" }
"nvidia" { $video_codec = "hevc_nvenc" }
"intel" { $video_codec = " hevc_qsv" }
default { $video_codec = "libx265" }
}
}
}
$video_dir = [System.IO.Path]::GetDirectoryName((Resolve-Path $video))
$destination = Join-Path $video_dir "$name.$video_extension"
do {
ffmpeg `
-y `
-i $video `
-c:v $video_codec `
-ss $start_seconds `
-to $end_seconds `
-vf "scale=-2:$($height), crop=$($width):$($height)" `
-b:v "$($video_bitrate)k" `
-pass 1 `
-fps_mode vfr `
-f null NUL
ffmpeg `
-y `
-i $video `
-c:v $video_codec `
-ss $start_seconds `
-to $end_seconds `
-vf "scale=-2:$($height), crop=$($width):$($height)" `
-b:v "$($video_bitrate)k" `
-pass 2 `
-c:a $audio_codec `
-b:a "$($audio_bitrate)k" `
-filter:a "volume=$volume" `
-max_muxing_queue_size 9999 `
$destination
$actual_size = (Get-Item "$destination").length / 1MB
$video_bitrate = ($video_bitrate * $size) / $actual_size
$video_bitrate = $video_bitrate * (1 - $repeat_count) # kept this just in case
$repeat_count += 0.03
} while ($actual_size -gt $size)
if ($delete_original) {
Remove-Item $video
}
if (!$preserve_logs) {
Remove-Item "ffmpeg2pass-0.log" -ErrorAction SilentlyContinue
Remove-Item "ffmpeg2pass-0.log.mbtree" -ErrorAction SilentlyContinue
}
if (!$dont_copy_to_clipboard) {
Add-Type -AssemblyName System.Windows.Forms
$files = New-Object System.Collections.Specialized.StringCollection
$files.Add((Resolve-Path $destination).Path)
[System.Windows.Forms.Clipboard]::SetFileDropList($files)
Write-Output "File copied to clipboard!"
}
Write-Output "Video took $(($repeat_count - 0.01) / 0.03) re-encodings until it went under $size MB."
@angstyfrostyneko
Copy link
Author

  • now the file is copied to the clipboard after its done! enabled by default

@angstyfrostyneko
Copy link
Author

  • always outputs the video in the same folder as the source, no longer where the terminal is opened

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment