Last active
June 19, 2025 09:38
-
-
Save angstyfrostyneko/6ce11f37ae6f068247ad294cecde72e5 to your computer and use it in GitHub Desktop.
FFMPEG wrapper that compresses videos under target size
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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." |
- added support for amd's av1 video encoder
- the bitrate decreases in bigger steps the more it has to re-encode the video because it would be over the size limit
- made the target bitrate change based on an rule of three approximation with the resulting size of the first encode result
- for some reason ffprobe can give back
N/A
as the aspect ratio. weird. added a shitty workaround for that just in case by computing the aspect ratio manually
- now the file is copied to the clipboard after its done! enabled by default
- 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
Uh oh!
There was an error while loading. Please reload this page.