Skip to content

Instantly share code, notes, and snippets.

@donal56
Last active March 4, 2026 04:18
Show Gist options
  • Select an option

  • Save donal56/27435f73c2e038f61a839b44f7f041ea to your computer and use it in GitHub Desktop.

Select an option

Save donal56/27435f73c2e038f61a839b44f7f041ea to your computer and use it in GitHub Desktop.
Wizard to compress videos with ffmpeg and ffprobe
:: Wizard para comprimir videos usando ffmpeg y ffprobe.
:: El script está seccionado como se describe y todos los comentarios y salidas a consola son en inglés.
:: Descripción del proceso del programa:
::
:: 1.- El programa se llama cleanVideo y toma como argumento un video.
:: Opcionalmente puede tomar -s o --silent para saltarse todas las opciones del wizard.
:: Siempre aplica:
:: - Codec de video: libx265 (H.265)
:: - Contenedor: MP4
:: - Nombre de salida: nombre original + " new" (si existe "new", intenta "new2", "new3", etc.)
:: - CRF: 27 (Fijo)
::
:: 2.- Se realizan validaciones básicas.
:: 2.1.- Verificar que los argumentos sean válidos.
:: 2.2.- Verificar que el video sea un archivo existente.
:: 2.3.- Verificar que el video tenga una extensión válida (.mp4, .mov, .avi, .mkv, .webm, .flv, .ts).
:: 2.4.- Verificar que ffmpeg y ffprobe estén instalados.
::
:: 3.- Recupera y muestra información metadata del video. Si algún dato no puede ser recuperado entonces el programa fallará. Datos mostrados:
:: - Nombre del archivo incluyendo extensión
:: - Tamaño del archivo
:: - Duración del video
:: - Codec de video
:: - Resolución (ancho x alto)
:: - Aspecto ratio (detecta automáticamente: 16:9, 3:2, 4:3, 2:3, 9:16, 3:4, 1:1 o muestra como custom)
:: - FPS (frames por segundo)
:: - Bitrate de video
:: - Bitrate de audio
:: - Bitrate total
::
:: 4.- Ofrece opciones de downscale según el aspecto ratio detectado.
:: Si es una proporción estándar (16:9, 3:2, 4:3, 2:3, 9:16, 3:4, 1:1):
:: - Muestra 4 opciones predefinidas con resoluciones apropiadas para el aspecto ratio
:: - El usuario selecciona [1-4] o ingresa cualquier otra tecla para saltar
:: Ejemplo para 16:9: [1] 2560x1440, [2] 1920x1080, [3] 1280x720, [4] 800x450
::
:: Si el aspecto ratio es custom (no detectado como estándar):
:: - Verifica automáticamente si está cercano a una proporción estándar usando tolerancia
:: - Si está cercano, ofrece automáticamente cropear a esa proporción (y/n)
:: - Si el usuario rechaza el crop o no está cercano a ninguno, pregunta por el ancho final deseado (el alto se calcula automáticamente manteniendo el aspecto ratio original)
::
:: 5.- Si el video tiene más de 30 fps, pregunta si desea cambiar los fps (e.g., 24, 30). Si ingresa un número lo usa como nuevo fps, cualquier otra entrada salta este paso.
::
:: 6.- Pregunta si desea modificar el bitrate de video. Si ingresa un número lo usa como kbps, cualquier otra entrada salta este paso.
::
:: 7.- Ofrece rotar el video 90 grados hacia la izquierda o derecha. El usuario puede seleccionar [1] Right, [2] Left o cualquier otra tecla para saltar.
::
:: 8.- Ofrece cortar el video. Solicita timestamp de inicio (mm:ss) y fin (mm:ss). Si no se ingresa nada se usan los valores por defecto (inicio del video y final). Si no se especifica ni inicio ni fin, se omite el trimming.
::
:: 9.- Pregunta si desea remover el audio (solo si el video tiene pista de audio).
::
:: 10.- Ejecuta el comando ffmpeg con los parámetros seleccionados (resolución, fps, rotación, recorte, bitrate, trimming, etc.).
:: Después de la codificación muestra:
:: - Tamaño del archivo original y nuevo
:: - Reducción/aumento de tamaño en bytes y porcentaje
:: - Ruta del archivo de salida
@echo off
setlocal enabledelayedexpansion
:: Wizard to compress videos with ffmpeg and ffprobe
:: By ChatGPT - cleanVideo.bat
:: Supported aspect ratio values for auto-crop
set "RATIO_16_9_NUM=16"
set "RATIO_16_9_DEN=9"
set "RATIO_4_3_NUM=4"
set "RATIO_4_3_DEN=3"
set "RATIO_9_16_NUM=9"
set "RATIO_9_16_DEN=16"
set "RATIO_3_4_NUM=3"
set "RATIO_3_4_DEN=4"
set "RATIO_3_2_NUM=3"
set "RATIO_3_2_DEN=2"
set "RATIO_2_3_NUM=2"
set "RATIO_2_3_DEN=3"
set "RATIO_1_1_NUM=1"
set "RATIO_1_1_DEN=1"
:: Tolerance for detecting close aspect ratios (in pixels of difference)
set "RATIO_TOLERANCE=50"
:: =============================
:: 1. Parse arguments
:: =============================
set "VIDEO=%~1"
set "SILENT=0"
if "%~2"=="-s" set SILENT=1
if "%~2"=="--silent" set SILENT=1
if not "%~3"=="" (
echo Error: Invalid argument. Usage: cleanVideo ^<video_file^> [-s^|--silent]
exit /b 1
)
if not "%~2"=="" (
if not "%~2"=="-s" (
if not "%~2"=="--silent" (
echo Error: Invalid flag. Only -s or --silent are allowed.
exit /b 1
)
)
)
:: =============================
:: 2. Basic validations
:: =============================
if "%VIDEO%"=="" (
echo Error: No video file provided.
exit /b 1
)
if not exist "%VIDEO%" (
echo Error: File does not exist: %VIDEO%
exit /b 1
)
set "EXT=%~x1"
set "VALID_EXT=.mp4 .mov .avi .mkv .webm .flv .ts"
set "FOUND=0"
for %%e in (%VALID_EXT%) do (
if /I "%%e"=="%EXT%" set FOUND=1
)
if %FOUND%==0 (
echo Error: Invalid video file extension: %EXT%
exit /b 1
)
where ffmpeg >nul 2>nul || (echo Error: ffmpeg not found & exit /b 1)
where ffprobe >nul 2>nul || (echo Error: ffprobe not found & exit /b 1)
:: =============================
:: 3. Extract and display metadata
:: =============================
for /f "tokens=1,* delims==" %%a in ('"ffprobe -v error -select_streams v:0 -show_entries stream=width,height,codec_name,bit_rate,display_aspect_ratio,r_frame_rate -of default=noprint_wrappers=1 "%VIDEO%""') do set "%%a=%%b"
for /f "tokens=1,* delims==" %%a in ('"ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 "%VIDEO%""') do set "AUDIO_%%a=%%b"
for /f "tokens=1,* delims==" %%a in ('"ffprobe -v error -show_entries format=duration,bit_rate -of default=noprint_wrappers=1 "%VIDEO%""') do set "%%a=%%b"
set "filename=%~nx1"
if "!filename!"=="" echo Error: Could not retrieve filename & exit /b 1
if "!duration!"=="" echo Error: Could not retrieve duration & exit /b 1
if "!codec_name!"=="" echo Error: Could not retrieve codec & exit /b 1
if "!width!"=="" echo Error: Could not retrieve resolution & exit /b 1
if "!bit_rate!"=="" echo Error: Could not retrieve video bitrate & exit /b 1
REM Parse fps from r_frame_rate (e.g., "30/1" or "24000/1001")
if defined r_frame_rate (
for /f "tokens=1 delims=/" %%a in ("!r_frame_rate!") do set "fps=%%a"
for /f "tokens=2 delims=/" %%a in ("!r_frame_rate!") do set "fps_den=%%a"
if defined fps_den (
if !fps_den! GTR 1 set /a fps=!fps! / !fps_den!
)
) else (
set "fps=N/A"
)
:: Convert duration to MM:SS format if >= 60 seconds
REM Trim spaces and extract integer part from duration
for /f "tokens=* delims= " %%a in ("!duration!") do set "duration=%%a"
for /f "tokens=1 delims=." %%a in ("!duration!") do set "duration_int=%%a"
if !duration_int! GEQ 60 (
set /a minutes=!duration_int! / 60
set /a seconds_remainder=!duration_int! %% 60
set "duration=!minutes!m !seconds_remainder!s"
) else (
set "duration=!duration_int!s"
)
set /a TOTAL_bit_rate=!bit_rate!
if "!AUDIO_bit_rate!"=="" (
set "AUDIO_bit_rate=N/A"
set "HAS_AUDIO=0"
) else (
set "HAS_AUDIO=1"
set /a AUDIO_bit_rate=!AUDIO_bit_rate! / 1024
set /a bit_rate= !TOTAL_bit_rate! - !AUDIO_bit_rate!
)
set /a bit_rate= !bit_rate! / 1024
set /a TOTAL_bit_rate= !TOTAL_bit_rate! / 1024
if !height! GTR 0 (
set /a ratio=!width!*100/!height!
) else (
set "ratio=0"
)
if !ratio! GEQ 175 if !ratio! LEQ 178 set "ASPECT=16_9"
if !ratio! GEQ 148 if !ratio! LEQ 152 set "ASPECT=3_2"
if !ratio! GEQ 132 if !ratio! LEQ 135 set "ASPECT=4_3"
if !ratio! GEQ 65 if !ratio! LEQ 68 set "ASPECT=2_3"
if !ratio! GEQ 55 if !ratio! LEQ 58 set "ASPECT=9_16"
if !ratio! GEQ 74 if !ratio! LEQ 77 set "ASPECT=3_4"
if "!ratio!"=="100" set "ASPECT=1_1"
if not defined ASPECT set "ASPECT=Custom"
:: Calculate file size
set /a FILE_SIZE_BYTES=%~z1
set /a FILE_SIZE_MB=!FILE_SIZE_BYTES! / 1048576
set /a FILE_SIZE_REMAINDER=!FILE_SIZE_BYTES! %% 1048576
set /a FILE_SIZE_KB=!FILE_SIZE_REMAINDER! / 1024
if !FILE_SIZE_MB! GTR 0 (
if !FILE_SIZE_KB! LSS 10 (
set "FILE_SIZE=!FILE_SIZE_MB!.0!FILE_SIZE_KB! MB"
) else (
set "FILE_SIZE=!FILE_SIZE_MB!.!FILE_SIZE_KB! MB"
)
) else (
set /a FILE_SIZE_KB=!FILE_SIZE_BYTES! / 1024
if !FILE_SIZE_KB! EQU 0 set "FILE_SIZE=!FILE_SIZE_BYTES! bytes"
if !FILE_SIZE_KB! GTR 0 set "FILE_SIZE=!FILE_SIZE_KB! KB"
)
:: If silent mode is enabled, skip wizard and go directly to encoding
if %SILENT% equ 1 goto encode
echo ===== VIDEO METADATA =====
echo File: !filename!
echo File size: !FILE_SIZE!
echo Duration: !duration!
echo Codec: !codec_name!
echo Resolution: !width!x!height!
echo Aspect ratio: !ASPECT:_=:!
echo FPS: !fps!
echo Video bitrate: !bit_rate! kbps
echo Audio bitrate: !AUDIO_bit_rate! kbps
echo Total bitrate: !TOTAL_bit_rate! kbps
echo =============================
:: =============================
:: 4. Resolution wizard
:: =============================
set "DOWNSCALE="
set "ORIENT="
if "!ASPECT!"=="16_9" set "ORIENT=H169"
if "!ASPECT!"=="3_2" set "ORIENT=H32"
if "!ASPECT!"=="4_3" set "ORIENT=H43"
if "!ASPECT!"=="2_3" set "ORIENT=V23"
if "!ASPECT!"=="9_16" set "ORIENT=V916"
if "!ASPECT!"=="3_4" set "ORIENT=V43"
if "!ASPECT!"=="1_1" set "ORIENT=SQUARE"
if defined ORIENT (
echo Would you like to downscale the video?
if "!ORIENT!"=="H169" (
echo [1] 2560x1440
echo [2] 1920x1080
echo [3] 1280x720
echo [4] 800x450
) else if "!ORIENT!"=="H32" (
echo [1] 1920x1280
echo [2] 1440x960
echo [3] 960x640
echo [4] 640x426
) else if "!ORIENT!"=="H43" (
echo [1] 2048x1536
echo [2] 1024x768
echo [3] 800x600
echo [4] 512x384
) else if "!ORIENT!"=="V23" (
echo [1] 1280x1920
echo [2] 960x1440
echo [3] 640x960
echo [4] 426x640
) else if "!ORIENT!"=="V916" (
echo [1] 1440x2560
echo [2] 1080x1920
echo [3] 720x1280
echo [4] 450x800
) else if "!ORIENT!"=="V43" (
echo [1] 1536x2048
echo [2] 768x1024
echo [3] 600x800
echo [4] 384x512
) else if "!ORIENT!"=="SQUARE" (
echo [1] 1200x1200
echo [2] 1000x1000
echo [3] 800x800
echo [4] 600x600
)
set /p OPTION=Choose option [1-4] or any other key to skip:
REM Trim spaces from OPTION
for /f "tokens=* delims= " %%a in ("!OPTION!") do set "OPTION=%%a"
set "DOWNSCALE_CODE=!ORIENT!_!OPTION!"
REM Map downscale codes to actual dimensions (OUTSIDE nested parentheses to avoid colon issues)
if "!DOWNSCALE_CODE!"=="H169_1" set "DOWNSCALE=2560x1440"
if "!DOWNSCALE_CODE!"=="H169_2" set "DOWNSCALE=1920x1080"
if "!DOWNSCALE_CODE!"=="H169_3" set "DOWNSCALE=1280x720"
if "!DOWNSCALE_CODE!"=="H169_4" set "DOWNSCALE=800x450"
if "!DOWNSCALE_CODE!"=="H32_1" set "DOWNSCALE=1920x1280"
if "!DOWNSCALE_CODE!"=="H32_2" set "DOWNSCALE=1440x960"
if "!DOWNSCALE_CODE!"=="H32_3" set "DOWNSCALE=960x640"
if "!DOWNSCALE_CODE!"=="H32_4" set "DOWNSCALE=640x426"
if "!DOWNSCALE_CODE!"=="H43_1" set "DOWNSCALE=2048x1536"
if "!DOWNSCALE_CODE!"=="H43_2" set "DOWNSCALE=1024x768"
if "!DOWNSCALE_CODE!"=="H43_3" set "DOWNSCALE=800x600"
if "!DOWNSCALE_CODE!"=="H43_4" set "DOWNSCALE=512x384"
if "!DOWNSCALE_CODE!"=="V23_1" set "DOWNSCALE=1280x1920"
if "!DOWNSCALE_CODE!"=="V23_2" set "DOWNSCALE=960x1440"
if "!DOWNSCALE_CODE!"=="V23_3" set "DOWNSCALE=640x960"
if "!DOWNSCALE_CODE!"=="V23_4" set "DOWNSCALE=426x640"
if "!DOWNSCALE_CODE!"=="V916_1" set "DOWNSCALE=1440x2560"
if "!DOWNSCALE_CODE!"=="V916_2" set "DOWNSCALE=1080x1920"
if "!DOWNSCALE_CODE!"=="V916_3" set "DOWNSCALE=720x1280"
if "!DOWNSCALE_CODE!"=="V916_4" set "DOWNSCALE=450x800"
if "!DOWNSCALE_CODE!"=="V43_1" set "DOWNSCALE=1536x2048"
if "!DOWNSCALE_CODE!"=="V43_2" set "DOWNSCALE=768x1024"
if "!DOWNSCALE_CODE!"=="V43_3" set "DOWNSCALE=600x800"
if "!DOWNSCALE_CODE!"=="V43_4" set "DOWNSCALE=384x512"
if "!DOWNSCALE_CODE!"=="SQUARE_1" set "DOWNSCALE=1200x1200"
if "!DOWNSCALE_CODE!"=="SQUARE_2" set "DOWNSCALE=1000x1000"
if "!DOWNSCALE_CODE!"=="SQUARE_3" set "DOWNSCALE=800x800"
if "!DOWNSCALE_CODE!"=="SQUARE_4" set "DOWNSCALE=600x600"
) else (
REM For custom aspect ratio, check if it's close to standard ratios
set "CROP="
set "CLOSE_RATIO="
REM Check 16:9
set /a diff_16_9=!width!*!RATIO_16_9_DEN! - !height!*!RATIO_16_9_NUM!
if !diff_16_9! LSS 0 set /a diff_16_9=!diff_16_9!*-1
if !diff_16_9! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=16_9"
)
REM Check 4:3
if not defined CLOSE_RATIO (
set /a diff_4_3=!width!*!RATIO_4_3_DEN! - !height!*!RATIO_4_3_NUM!
if !diff_4_3! LSS 0 set /a diff_4_3=!diff_4_3!*-1
if !diff_4_3! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=4_3"
)
)
REM Check 3:2
if not defined CLOSE_RATIO (
set /a diff_3_2=!width!*!RATIO_3_2_DEN! - !height!*!RATIO_3_2_NUM!
if !diff_3_2! LSS 0 set /a diff_3_2=!diff_3_2!*-1
if !diff_3_2! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=3_2"
)
)
REM Check 2:3
if not defined CLOSE_RATIO (
set /a diff_2_3=!width!*!RATIO_2_3_DEN! - !height!*!RATIO_2_3_NUM!
if !diff_2_3! LSS 0 set /a diff_2_3=!diff_2_3!*-1
if !diff_2_3! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=2_3"
)
)
REM Check 9:16
if not defined CLOSE_RATIO (
set /a diff_9_16=!width!*!RATIO_9_16_DEN! - !height!*!RATIO_9_16_NUM!
if !diff_9_16! LSS 0 set /a diff_9_16=!diff_9_16!*-1
if !diff_9_16! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=9_16"
)
)
REM Check 3:4
if not defined CLOSE_RATIO (
set /a diff_3_4=!width!*!RATIO_3_4_DEN! - !height!*!RATIO_3_4_NUM!
if !diff_3_4! LSS 0 set /a diff_3_4=!diff_3_4!*-1
if !diff_3_4! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=3_4"
)
)
REM Check 1:1
if not defined CLOSE_RATIO (
set /a diff_1_1=!width! - !height!
if !diff_1_1! LSS 0 set /a diff_1_1=!diff_1_1!*-1
if !diff_1_1! LSS !RATIO_TOLERANCE! (
set "CLOSE_RATIO=1_1"
)
)
REM Map internal ratio codes to display format (need to be OUTSIDE all parentheses)
REM Create mapping without direct colon assignment issues
if "!CLOSE_RATIO!"=="16_9" set "DISPLAY_RATIO_19=16"
if "!CLOSE_RATIO!"=="16_9" set "DISPLAY_RATIO_9=9"
if "!CLOSE_RATIO!"=="4_3" set "DISPLAY_RATIO_19=4"
if "!CLOSE_RATIO!"=="4_3" set "DISPLAY_RATIO_9=3"
if "!CLOSE_RATIO!"=="9_16" set "DISPLAY_RATIO_19=9"
if "!CLOSE_RATIO!"=="9_16" set "DISPLAY_RATIO_9=16"
if "!CLOSE_RATIO!"=="3_4" set "DISPLAY_RATIO_19=3"
if "!CLOSE_RATIO!"=="3_4" set "DISPLAY_RATIO_9=4"
if "!CLOSE_RATIO!"=="3_2" set "DISPLAY_RATIO_19=3"
if "!CLOSE_RATIO!"=="3_2" set "DISPLAY_RATIO_9=2"
if "!CLOSE_RATIO!"=="2_3" set "DISPLAY_RATIO_19=2"
if "!CLOSE_RATIO!"=="2_3" set "DISPLAY_RATIO_9=3"
if "!CLOSE_RATIO!"=="1_1" set "DISPLAY_RATIO_19=1"
if "!CLOSE_RATIO!"=="1_1" set "DISPLAY_RATIO_9=1"
REM Now construct DISPLAY_RATIO with x instead of colon for now
if defined DISPLAY_RATIO_19 (
set "DISPLAY_RATIO=!DISPLAY_RATIO_19!x!DISPLAY_RATIO_9!"
)
REM Convert x to : for display (MUST BE OUTSIDE parentheses)
if defined DISPLAY_RATIO (
set "DISPLAY_RATIO_SHOW=!DISPLAY_RATIO:x=:!"
)
REM If close ratio found, ask for crop (set /p OUTSIDE of if blocks)
set "CROP_OPTION="
if defined CLOSE_RATIO (
set /p CROP_OPTION=The video is very close to the !DISPLAY_RATIO_SHOW! ratio, auto-crop to match it? [y/n]:
)
REM Process crop if user said yes
if /I "!CROP_OPTION!"=="y" (
REM Calculate crop dimensions based on closest ratio
REM Use separate sequential if statements instead of else if to avoid batch syntax errors
REM 16:9 ratio
if "!CLOSE_RATIO!"=="16_9" (
if !width! LSS !height! (
set /a crop_w=!height! * 16 / 9
if !crop_w! GTR !width! set /a crop_w=!width!
set /a crop_h=!crop_w! * 9 / 16
) else (
set /a crop_h=!width! * 9 / 16
set /a crop_w=!crop_h! * 16 / 9
if !crop_w! GTR !width! (
set /a crop_w=!width!
set /a crop_h=!crop_w! * 9 / 16
)
)
set /a crop_x=^(!width! - !crop_w!^) / 2
set /a crop_y=^(!height! - !crop_h!^) / 2
set "CROP=!crop_w!x!crop_h!x!crop_x!x!crop_y!"
)
REM 4:3 ratio
if "!CLOSE_RATIO!"=="4_3" (
if !width! LSS !height! (
set /a crop_w=!height! * 4 / 3
if !crop_w! GTR !width! set /a crop_w=!width!
set /a crop_h=!crop_w! * 3 / 4
) else (
set /a crop_h=!width! * 3 / 4
set /a crop_w=!crop_h! * 4 / 3
if !crop_w! GTR !width! (
set /a crop_w=!width!
set /a crop_h=!crop_w! * 3 / 4
)
)
set /a crop_x=^(!width! - !crop_w!^) / 2
set /a crop_y=^(!height! - !crop_h!^) / 2
set "CROP=!crop_w!x!crop_h!x!crop_x!x!crop_y!"
)
REM 9:16 ratio
if "!CLOSE_RATIO!"=="9_16" (
if !width! GTR !height! (
set /a crop_h=!width! * 16 / 9
if !crop_h! GTR !height! set /a crop_h=!height!
set /a crop_w=!crop_h! * 9 / 16
) else (
set /a crop_w=!height! * 9 / 16
set /a crop_h=!crop_w! * 16 / 9
if !crop_h! GTR !height! (
set /a crop_h=!height!
set /a crop_w=!crop_h! * 9 / 16
)
)
set /a crop_x=^(!width! - !crop_w!^) / 2
set /a crop_y=^(!height! - !crop_h!^) / 2
set "CROP=!crop_w!x!crop_h!x!crop_x!x!crop_y!"
)
REM 3:4 ratio
if "!CLOSE_RATIO!"=="3_4" (
if !width! GTR !height! (
set /a crop_h=!width! * 4 / 3
if !crop_h! GTR !height! set /a crop_h=!height!
set /a crop_w=!crop_h! * 3 / 4
) else (
set /a crop_w=!height! * 3 / 4
set /a crop_h=!crop_w! * 4 / 3
if !crop_h! GTR !height! (
set /a crop_h=!height!
set /a crop_w=!crop_h! * 3 / 4
)
)
set /a crop_x=^(!width! - !crop_w!^) / 2
set /a crop_y=^(!height! - !crop_h!^) / 2
set "CROP=!crop_w!x!crop_h!x!crop_x!x!crop_y!"
)
REM 3:2 ratio
if "!CLOSE_RATIO!"=="3_2" (
if !width! GTR !height! (
set /a crop_h=!width! * 2 / 3
if !crop_h! GTR !height! set /a crop_h=!height!
set /a crop_w=!crop_h! * 3 / 2
) else (
set /a crop_w=!height! * 3 / 2
set /a crop_h=!crop_w! * 2 / 3
if !crop_h! GTR !height! (
set /a crop_h=!height!
set /a crop_w=!crop_h! * 3 / 2
)
)
set /a crop_x=^(!width! - !crop_w!^) / 2
set /a crop_y=^(!height! - !crop_h!^) / 2
set "CROP=!crop_w!x!crop_h!x!crop_x!x!crop_y!"
)
REM 2:3 ratio
if "!CLOSE_RATIO!"=="2_3" (
if !width! GTR !height! (
set /a crop_h=!width! * 3 / 2
if !crop_h! GTR !height! set /a crop_h=!height!
set /a crop_w=!crop_h! * 2 / 3
) else (
set /a crop_w=!height! * 2 / 3
set /a crop_h=!crop_w! * 3 / 2
if !crop_h! GTR !height! (
set /a crop_h=!height!
set /a crop_w=!crop_h! * 2 / 3
)
)
set /a crop_x=^(!width! - !crop_w!^) / 2
set /a crop_y=^(!height! - !crop_h!^) / 2
set "CROP=!crop_w!x!crop_h!x!crop_x!x!crop_y!"
)
REM 1:1 ratio
if "!CLOSE_RATIO!"=="1_1" (
set /a min_dim=!width!
if !height! LSS !min_dim! set /a min_dim=!height!
set /a crop_x=^(!width! - !min_dim!^) / 2
set /a crop_y=^(!height! - !min_dim!^) / 2
set "CROP=!min_dim!x!min_dim!x!crop_x!x!crop_y!"
)
) else (
REM If neither crop nor downscale was selected, ask for final width
set /p FINAL_WIDTH=Enter desired final width [pixels]:
REM Validate that FINAL_WIDTH is a valid number and greater than 0
if not "!FINAL_WIDTH!"=="" (
REM Try to use it in arithmetic - if it fails, errorlevel will be non-zero
set /a FINAL_WIDTH_TEST=!FINAL_WIDTH!
if !FINAL_WIDTH! GTR 0 (
set /a FINAL_HEIGHT=!FINAL_WIDTH! * !height! / !width!
set "DOWNSCALE=!FINAL_WIDTH!x!FINAL_HEIGHT!"
)
)
)
)
:: =============================
:: 5. FPS change
:: =============================
set "NEW_FPS="
if not "!fps!"=="N/A" if !fps! GTR 30 set /p NEW_FPS=Enter desired FPS (press enter to skip):
:: =============================
:: 6. Bitrate change
:: =============================
set /p NEWBR=Enter new video bitrate [kbps] or press enter to skip:
:: =============================
:: 7. Rotation
:: =============================
set "ROTATION="
set /p ROTATE_OPTION=Rotate video? Press [1] to rotate to the right, [2] for left, or enter to skip:
if "!ROTATE_OPTION!"=="1" set "ROTATION=transpose=1"
if "!ROTATE_OPTION!"=="2" set "ROTATION=transpose=2"
:: =============================
:: 8. Trimming
:: =============================
set /p START=Enter start timestamp [mm:ss] or press enter to skip:
set /p END=Enter end timestamp [mm:ss] or press enter to skip:
:: =============================
:: 9. Remove audio
:: =============================
if !HAS_AUDIO! EQU 1 set /p REMOVE_AUDIO=Remove audio? [y/n]:
:: =============================
:: 10. Encode
:: =============================
:encode
set "OUTFILE=%~dpn1 new.mp4"
set /a COUNT=2
:checkexist
if exist "%OUTFILE%" (
set "OUTFILE=%~dpn1 new!COUNT!.mp4"
set /a COUNT+=1
goto checkexist
)
set "CMD=ffmpeg -loglevel quiet -stats"
set CMD=!CMD! -i "!VIDEO!"
:: ss before input may be faster but can result in length mismatches
if defined START set "CMD=!CMD! -ss !START!"
if defined END set "CMD=!CMD! -to !END!"
REM Build video filters (crop and/or scale and/or rotation)
set "FILTERS="
if defined CROP (
REM Convert x back to : for ffmpeg crop filter
set "CROP_FIXED=!CROP:x=:!"
set "FILTERS=crop=!CROP_FIXED!"
)
if defined DOWNSCALE (
REM Convert x back to : for ffmpeg scale filter
set "DOWNSCALE_FIXED=!DOWNSCALE:x=:!"
if defined FILTERS (
set "FILTERS=!FILTERS!,scale=!DOWNSCALE_FIXED!"
) else (
set "FILTERS=scale=!DOWNSCALE_FIXED!"
)
)
if defined ROTATION (
if defined FILTERS (
set "FILTERS=!FILTERS!,!ROTATION!"
) else (
set "FILTERS=!ROTATION!"
)
)
if defined FILTERS (
set CMD=!CMD! -vf "!FILTERS!"
)
if defined NEW_FPS set "CMD=!CMD! -r !NEW_FPS!"
if /I "!REMOVE_AUDIO!"=="y" set "CMD=!CMD! -an"
if not defined REMOVE_AUDIO set CMD=!CMD! -map 0 -c^:s copy
set CMD=!CMD! -c^:v libx265
if defined NEWBR (
set CMD=!CMD! -b^:v !NEWBR!k
) else (
set CMD=!CMD! -crf 27
)
set CMD=!CMD! -y "!OUTFILE!"
if %SILENT% equ 0 (
echo Running:
echo !CMD!
)
!CMD!
for %%A in ("!OUTFILE!") do set "OUT_SIZE_BYTES=%%~zA"
REM Calculate size in KB first for accuracy
set /a OUT_SIZE_KB_TOTAL=!OUT_SIZE_BYTES! / 1024
if !OUT_SIZE_KB_TOTAL! LSS 1024 (
set "OUT_SIZE=!OUT_SIZE_KB_TOTAL! KB"
) else (
REM Calculate MB with decimal precision
set /a OUT_SIZE_MB_TIMES100=!OUT_SIZE_BYTES! * 100 / 1048576
set /a OUT_SIZE_MB=!OUT_SIZE_MB_TIMES100! / 100
set /a OUT_SIZE_DECIMAL=!OUT_SIZE_MB_TIMES100! %% 100
if !OUT_SIZE_DECIMAL! LSS 10 (
set "OUT_SIZE=!OUT_SIZE_MB!.0!OUT_SIZE_DECIMAL! MB"
) else (
set "OUT_SIZE=!OUT_SIZE_MB!.!OUT_SIZE_DECIMAL! MB"
)
)
set /a SIZE_DIFF=!FILE_SIZE_BYTES! - !OUT_SIZE_BYTES!
set /a PERCENT_CHANGE=^(!SIZE_DIFF! * 100^) / !FILE_SIZE_BYTES!
if %SILENT% equ 0 (
echo.
echo Finished successfully. Output saved to: !OUTFILE!
echo Size: !FILE_SIZE! -^> !OUT_SIZE!
if !SIZE_DIFF! GEQ 0 (
echo Reduced by !SIZE_DIFF! bytes ^(!PERCENT_CHANGE!%%^)
) else (
set /a SIZE_DIFF_ABS=!OUT_SIZE_BYTES! - !FILE_SIZE_BYTES!
set /a PERCENT_INCREASE=^(!SIZE_DIFF_ABS! * 100^) / !FILE_SIZE_BYTES!
echo Increased by !SIZE_DIFF_ABS! bytes ^(!PERCENT_INCREASE!%%^)
)
) else (
echo File "!filename!" compressed from !FILE_SIZE! to !OUT_SIZE!
)
endlocal
exit /b 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment