Add support for cropping (out_w:out_h:x:y) and preview of result +
possible robustness fixes.
This commit is contained in:
+124
-84
@@ -1,7 +1,7 @@
|
||||
@ECHO OFF
|
||||
:: Description: Video to GIF/APNG/WEBP converter
|
||||
:: By: MDHEXT, Nabi KaramAliZadeh, Pathduck
|
||||
:: Version: 6.0
|
||||
:: Version: 6.1
|
||||
:: Url: https://github.com/Pathduck/vid2ani/
|
||||
:: License: GNU General Public License v3.0 (GPLv3)
|
||||
|
||||
@@ -16,25 +16,25 @@ SET "YELLOW=[33m"
|
||||
SET "BLUE=[94m"
|
||||
SET "CYAN=[96m"
|
||||
|
||||
:: Checking for blank input or help commands
|
||||
IF "%~1" == "" GOTO :help_message
|
||||
IF "%~1" == "-?" GOTO :help_message
|
||||
IF "%~1" == "/?" GOTO :help_message
|
||||
IF "%~1" == "--help" GOTO :help_message
|
||||
:: Check for blank input or help commands
|
||||
IF "%~1"=="" GOTO :help_message
|
||||
IF "%~1"=="-?" GOTO :help_message
|
||||
IF "%~1"=="/?" GOTO :help_message
|
||||
IF "%~1"=="--help" GOTO :help_message
|
||||
|
||||
:: Check if FFmpeg exists on PATH, if not exit
|
||||
WHERE /q ffmpeg.exe || ( ECHO %RED%FFmpeg not found in PATH, please install it first%OFF% & GOTO :EOF )
|
||||
|
||||
:: Assign input and output
|
||||
SET input="%~1"
|
||||
SET output=%~n1
|
||||
SET "input=%~1"
|
||||
SET "output=%~n1"
|
||||
|
||||
:: Validate input file
|
||||
IF NOT EXIST %input% (
|
||||
ECHO %RED%Input file not found: %input%%OFF%
|
||||
IF NOT EXIST "%input%" (
|
||||
ECHO %RED%Input file not found: "%input%"%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
|
||||
:: Setting the path to the working directory
|
||||
SET WD=%TEMP%\VID2ANI
|
||||
|
||||
:: Clearing input vars and setting defaults
|
||||
SET "fps=15"
|
||||
SET "mode=1"
|
||||
@@ -42,83 +42,100 @@ SET "dither=0"
|
||||
SET "scale=-1"
|
||||
SET "filetype=gif"
|
||||
SET "webp_lossy="
|
||||
SET "webp_lossy_def=75"
|
||||
SET "webp_lossy_q=75"
|
||||
SET "loglevel=error"
|
||||
SET "bayerscale="
|
||||
SET "colormax="
|
||||
SET "start_time="
|
||||
SET "end_time="
|
||||
SET "crop="
|
||||
SET "errorswitch="
|
||||
SET "picswitch="
|
||||
|
||||
GOTO :varin
|
||||
SET "playswitch="
|
||||
|
||||
:varin
|
||||
:: Using SHIFT command to go through the input and storing each setting into its own variable
|
||||
IF NOT "%~1" =="" (
|
||||
IF "%~1" =="-o" SET "output=%~dpn2" & SHIFT
|
||||
IF "%~1" =="-t" SET "filetype=%~2" & SHIFT
|
||||
IF "%~1" =="-r" SET "scale=%~2" & SHIFT
|
||||
IF "%~1" =="-l" ( IF 1%2 NEQ +1%~2 ( SET "webp_lossy=%webp_lossy_def%"
|
||||
) ELSE IF "%~2" == "" ( SET "webp_lossy=%webp_lossy_def%"
|
||||
) ELSE ( SET "webp_lossy=%~2" & SHIFT )
|
||||
:: Parse Arguments, first shift input one left
|
||||
SHIFT
|
||||
:parse_loop
|
||||
IF NOT "%~1"=="" (
|
||||
IF "%~1"=="-o" SET "output=%~dpn2" & SHIFT
|
||||
IF "%~1"=="-t" SET "filetype=%~2" & SHIFT
|
||||
IF "%~1"=="-r" SET "scale=%~2" & SHIFT
|
||||
IF "%~1"=="-l" ( IF 1%2 NEQ +1%2 ( SET "webp_lossy=1"
|
||||
) ELSE IF "%~2"=="" ( SET "webp_lossy=1"
|
||||
) ELSE ( SET "webp_lossy=1" & SET "webp_lossy_q=%~2" & SHIFT )
|
||||
)
|
||||
IF "%~1" =="-f" SET "fps=%~2" & SHIFT
|
||||
IF "%~1" =="-s" SET "start_time=%~2" & SHIFT
|
||||
IF "%~1" =="-e" SET "end_time=%~2" & SHIFT
|
||||
IF "%~1" =="-d" SET "dither=%~2" & SHIFT
|
||||
IF "%~1" =="-b" SET "bayerscale=%~2" & SHIFT
|
||||
IF "%~1" =="-m" SET "mode=%~2" & SHIFT
|
||||
IF "%~1" =="-c" SET "colormax=%~2" & SHIFT
|
||||
IF "%~1" =="-v" SET "loglevel=%~2" & SHIFT
|
||||
IF "%~1" =="-k" SET "errorswitch=1"
|
||||
IF "%~1" =="-p" SET "picswitch=1"
|
||||
SHIFT & GOTO :varin
|
||||
IF "%~1"=="-f" SET "fps=%~2" & SHIFT
|
||||
IF "%~1"=="-s" SET "start_time=%~2" & SHIFT
|
||||
IF "%~1"=="-e" SET "end_time=%~2" & SHIFT
|
||||
IF "%~1"=="-d" SET "dither=%~2" & SHIFT
|
||||
IF "%~1"=="-b" SET "bayerscale=%~2" & SHIFT
|
||||
IF "%~1"=="-m" SET "mode=%~2" & SHIFT
|
||||
IF "%~1"=="-c" SET "colormax=%~2" & SHIFT
|
||||
IF "%~1"=="-v" SET "loglevel=%~2" & SHIFT
|
||||
IF "%~1"=="-x" SET "crop=%~2" & SHIFT
|
||||
IF "%~1"=="-k" SET "errorswitch=1"
|
||||
IF "%~1"=="-p" SET "picswitch=1"
|
||||
IF "%~1"=="-y" SET "playswitch=1"
|
||||
SHIFT & GOTO :parse_loop
|
||||
)
|
||||
GOTO :safchek
|
||||
|
||||
:safchek
|
||||
:: Validate if output file is set
|
||||
FOR %%f IN ("%output%") DO SET "out_base=%%~nf"
|
||||
IF "%output%"=="" ( ECHO %RED%Missing value for -o%OFF% & GOTO :EOF )
|
||||
IF DEFINED out_base (
|
||||
IF "!out_base:~0,1!"=="-" ( ECHO %RED%Missing value for -o%OFF% & GOTO :EOF )
|
||||
)
|
||||
|
||||
:: Validate if output is a directory; strip trailing slash and use input filename
|
||||
IF EXIST "%output%\*" (
|
||||
IF "%output:~-1%"=="\" SET "output=%output:~0,-1%"
|
||||
FOR %%f IN ("!input!") DO SET "filename=%%~nf"
|
||||
SET "output=!output!\!filename!"
|
||||
)
|
||||
|
||||
:: Validate output file extension
|
||||
ECHO %filetype% | FINDSTR /R "\<gif\> \<png\> \<apng\> \<webp\>" >nul
|
||||
IF %errorlevel% NEQ 0 (
|
||||
ECHO %RED%Not a valid file type: %filetype%%OFF%
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO %RED%Not a valid file type: !filetype!%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
IF "%filetype%"=="gif" SET "output=%output%.gif"
|
||||
IF "%filetype%"=="webp" SET "output=%output%.webp"
|
||||
IF "%filetype%"=="png" SET "filetype=apng"
|
||||
IF "%filetype%"=="apng" SET "output=%output%.png"
|
||||
IF "%filetype%"=="webp" SET "output=%output%.webp"
|
||||
|
||||
:: Validate Palettegen
|
||||
IF !mode! GTR 3 (
|
||||
ECHO %RED%Not a valid palettegen ^(-m^) mode%OFF%
|
||||
ECHO %RED%Not a valid palettegen ^(-m^) mode.%OFF%
|
||||
GOTO :EOF
|
||||
) ELSE IF !%mode! LSS 1 (
|
||||
ECHO %RED%Not a valid palettegen ^(-m^) mode%OFF%
|
||||
) ELSE IF !mode! LSS 1 (
|
||||
ECHO %RED%Not a valid palettegen ^(-m^) mode.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
|
||||
:: Validate Dithering
|
||||
IF !dither! GTR 8 (
|
||||
ECHO %RED%Not a valid dither ^(-d^) algorithm %OFF%
|
||||
ECHO %RED%Not a valid dither ^(-d^) algorithm.%OFF%
|
||||
GOTO :EOF
|
||||
) ELSE IF !dither! LSS 0 (
|
||||
ECHO %RED%Not a valid dither ^(-d^) algorithm%OFF%
|
||||
ECHO %RED%Not a valid dither ^(-d^) algorithm.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
|
||||
:: Validate Bayerscale
|
||||
IF DEFINED bayerscale (
|
||||
IF !bayerscale! GTR 5 (
|
||||
ECHO %RED%Not a valid bayerscale ^(-b^) value %OFF%
|
||||
ECHO %RED%Not a valid bayerscale ^(-b^) value.%OFF%
|
||||
GOTO :EOF
|
||||
) ELSE IF !bayerscale! LSS 0 (
|
||||
ECHO %RED%Not a valid bayerscale ^(-b^) value%OFF%
|
||||
ECHO %RED%Not a valid bayerscale ^(-b^) value.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
IF !dither! NEQ 1 (
|
||||
IF !bayerscale! LEQ 5 (
|
||||
ECHO %RED%Bayerscale ^(-b^) only works with Bayer dithering%OFF%
|
||||
ECHO %RED%Bayerscale ^(-b^) only works with Bayer dithering.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
)
|
||||
@@ -126,14 +143,14 @@ IF DEFINED bayerscale (
|
||||
|
||||
:: Validate Lossy WEBP
|
||||
IF DEFINED webp_lossy (
|
||||
IF NOT "%filetype%" == "webp" (
|
||||
ECHO %RED%Lossy ^(-l^) is only valid for filetype webp%OFF%
|
||||
IF NOT "!filetype!"=="webp" (
|
||||
ECHO %RED%Lossy ^(-l^) is only valid for filetype webp.%OFF%
|
||||
GOTO :EOF
|
||||
) ELSE IF !webp_lossy! GTR 100 (
|
||||
ECHO %RED%Not a valid lossy ^(-l^) quality value%OFF%
|
||||
) ELSE IF !webp_lossy_q! GTR 100 (
|
||||
ECHO %RED%Not a valid lossy ^(-l^) quality value.%OFF%
|
||||
GOTO :EOF
|
||||
) ELSE IF !webp_lossy! LSS 0 (
|
||||
ECHO %RED%Not a valid lossy ^(-l^) quality value%OFF%
|
||||
) ELSE IF !webp_lossy_q! LSS 0 (
|
||||
ECHO %RED%Not a valid lossy ^(-l^) quality value.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
)
|
||||
@@ -142,13 +159,13 @@ IF DEFINED webp_lossy (
|
||||
IF DEFINED start_time (
|
||||
IF DEFINED end_time SET "trim=-ss !start_time! -to !end_time!"
|
||||
IF NOT DEFINED end_time (
|
||||
ECHO %RED%Please input the end time ^(-e^)%OFF%
|
||||
ECHO %RED%End time ^(-e^) is required when Start time ^(-s^) is specified.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
)
|
||||
IF DEFINED end_time (
|
||||
IF NOT DEFINED start_time (
|
||||
ECHO %RED%Please input the start time ^(-s^)%OFF%
|
||||
ECHO %RED%Start time ^(-s^) is required when End time ^(-e^) is specified.%OFF%
|
||||
GOTO :EOF
|
||||
)
|
||||
)
|
||||
@@ -173,34 +190,40 @@ IF DEFINED colormax (
|
||||
)
|
||||
)
|
||||
|
||||
GOTO :script_start
|
||||
|
||||
:script_start
|
||||
:: Storing FFmpeg version string
|
||||
FOR /F "delims=" %%a in ('ffmpeg -version') DO (
|
||||
IF NOT DEFINED version (
|
||||
SET "version=%%a"
|
||||
) ELSE IF NOT DEFINED build (
|
||||
SET "build=%%a"
|
||||
)
|
||||
)
|
||||
|
||||
:: Displaying FFmpeg version string and creating the working directory
|
||||
ECHO %YELLOW%%version%%OFF%
|
||||
ECHO %YELLOW%%build%%OFF%
|
||||
ECHO %GREEN%Output file:%OFF% %output%
|
||||
ECHO %GREEN%Creating working directory...%OFF%
|
||||
:: Setting the path to working directory and creating it
|
||||
SET WD=%TEMP%\vid2ani-%random%
|
||||
MD "%WD%"
|
||||
|
||||
:palettegen
|
||||
:: Putting together command to generate palette
|
||||
SET palette=%WD%\palette_%%05d.png
|
||||
SET filters=fps=%fps%,scale=%scale%:-1:flags=lanczos
|
||||
SET "filters=fps=%fps%"
|
||||
IF DEFINED crop (SET "filters=%filters%,crop=%crop%")
|
||||
SET "filters=%filters%,scale=%scale%:-1:flags=lanczos"
|
||||
|
||||
:: APNG muxer does not support multiple palettes so fallback to using palettegen diff mode
|
||||
:: FFplay preview
|
||||
IF DEFINED playswitch (
|
||||
:: Check if ffplay exists on PATH, if not exit
|
||||
WHERE /q ffplay.exe || ( ECHO %RED%FFplay not found in PATH, please install it first%OFF% & GOTO :EOF )
|
||||
|
||||
FOR /F "delims=" %%a in ('ffplay -version') DO (
|
||||
IF NOT DEFINED ffplay_version ( SET "ffplay_version=%%a"
|
||||
) ELSE IF NOT DEFINED ffplay_build ( SET "ffplay_build=%%a" )
|
||||
)
|
||||
ECHO %YELLOW%!ffplay_version!%OFF%
|
||||
ECHO %YELLOW%!ffplay_build!%OFF%
|
||||
|
||||
IF NOT DEFINED start_time SET "start_time=0"
|
||||
IF NOT DEFINED end_time SET "end_time=3"
|
||||
ffplay -v %loglevel% -i "%input%" -vf "%filters%" -an -loop 0 -ss !start_time! -t !end_time!
|
||||
GOTO :EOF
|
||||
)
|
||||
|
||||
:: APNG muxer does not support multiple palettes, fallback to palettegen diff mode
|
||||
IF "%filetype%"=="apng" (
|
||||
IF !mode! EQU 2 (
|
||||
ECHO %YELLOW%APNG does not support multiple palettes - falling back to Palettegen mode 1 ^(diff^)%OFF%
|
||||
ECHO %YELLOW%APNG does not support multiple palettes, falling back to Palettegen mode 1 ^(diff^).%OFF%
|
||||
SET mode=1
|
||||
)
|
||||
)
|
||||
@@ -216,9 +239,20 @@ IF DEFINED colormax (
|
||||
IF !mode! EQU 3 SET "mcol==max_colors=%colormax%"
|
||||
)
|
||||
|
||||
:: Storing FFmpeg version string
|
||||
FOR /F "delims=" %%a in ('ffmpeg -version') DO (
|
||||
IF NOT DEFINED ffmpeg_version ( SET "ffmpeg_version=%%a"
|
||||
) ELSE IF NOT DEFINED ffmpeg_build ( SET "ffmpeg_build=%%a" )
|
||||
)
|
||||
|
||||
:: Displaying FFmpeg version string and output file
|
||||
ECHO %YELLOW%!ffmpeg_version!%OFF%
|
||||
ECHO %YELLOW%!ffmpeg_build!%OFF%
|
||||
ECHO %GREEN%Output file:%OFF% !output!
|
||||
|
||||
:: Executing command to generate palette
|
||||
ECHO %GREEN%Generating palette...%OFF%
|
||||
ffmpeg -v %loglevel% %trim% -i %input% -vf "%filters%,%encode%%mcol%" -y "%palette%"
|
||||
ffmpeg -v %loglevel% %trim% -i "%input%" -vf "%filters%,%encode%%mcol%" -y "%palette%"
|
||||
|
||||
:: Checking if the palette file is in the Working Directory, if not cleaning up
|
||||
IF NOT EXIST "%WD%\palette_00001.png" (
|
||||
@@ -262,27 +296,29 @@ IF NOT DEFINED bayerscale SET "bayer="
|
||||
IF DEFINED bayerscale SET "bayer=:bayer_scale=%bayerscale%"
|
||||
|
||||
:: WEBP pixel format and lossy quality
|
||||
IF "%filetype%" == "webp" (
|
||||
IF "%filetype%"=="webp" (
|
||||
IF DEFINED webp_lossy (
|
||||
SET "webp_lossy=-lossless 0 -pix_fmt yuva420p -quality %webp_lossy%"
|
||||
) ELSE SET "webp_lossy=-lossless 1"
|
||||
SET "type_opts=-lossless 0 -pix_fmt yuva420p -quality %webp_lossy_q%"
|
||||
) ELSE SET "type_opts=-lossless 1"
|
||||
)
|
||||
|
||||
:: Executing the encoding command
|
||||
ECHO %GREEN%Encoding animation...%OFF%
|
||||
ffmpeg -v %loglevel% %trim% -i %input% -thread_queue_size 512 -i "%palette%" ^
|
||||
ffmpeg -v %loglevel% %trim% -i "%input%" -thread_queue_size 512 -i "%palette%" ^
|
||||
-lavfi "%filters% [x]; [x][1:v] %decode%%errordiff%%ditherenc%%bayer%" ^
|
||||
-f %filetype% %webp_lossy% -loop 0 -plays 0 -y "%output%"
|
||||
-f %filetype% %type_opts% -loop 0 -plays 0 -y "%output%"
|
||||
|
||||
:: Checking if file was created and cleaning up if not
|
||||
IF NOT EXIST "%output%" ECHO %RED%Failed to generate animation: %output% not found.%OFF% && GOTO :cleanup
|
||||
IF NOT EXIST "%output%" (
|
||||
ECHO ECHO %RED%Failed to generate animation: %output% not found.%OFF%
|
||||
GOTO :cleanup
|
||||
)
|
||||
|
||||
:: Open output file if picswitch is set
|
||||
IF DEFINED picswitch START "" "%output%"
|
||||
|
||||
:cleanup
|
||||
:: Cleaning up
|
||||
ECHO %GREEN%Deleting temporary files...%OFF%
|
||||
RMDIR /S /Q "%WD%"
|
||||
ECHO %GREEN%Done.%OFF%
|
||||
ENDLOCAL
|
||||
@@ -290,7 +326,7 @@ GOTO :EOF
|
||||
|
||||
:help_message
|
||||
:: Print usage message
|
||||
ECHO %GREEN%Video to GIF/APNG/WEBP converter v6.0%OFF%
|
||||
ECHO %GREEN%Video to GIF/APNG/WEBP converter v6.1%OFF%
|
||||
ECHO %BLUE%By MDHEXT, Nabi KaramAliZadeh, Pathduck%OFF%
|
||||
ECHO:
|
||||
ECHO %GREEN%Usage:%OFF%
|
||||
@@ -299,16 +335,20 @@ ECHO:
|
||||
ECHO %GREEN%Arguments:%OFF%
|
||||
ECHO -o Output file. Default is the same as input file, sans extension
|
||||
ECHO -t Output file type: 'gif' (default), 'apng', 'png', 'webp'
|
||||
ECHO -r Scale or size. Width of the animation in pixels
|
||||
ECHO -r Resize output width in pixels. Default is original input size
|
||||
ECHO -l Enable lossy WebP compression and quality, range 0-100 (default 75)
|
||||
ECHO -f Framerate in frames per seconds (default 15)
|
||||
ECHO -c Maximum colors usable per palette, range 3-256 (default 256)
|
||||
ECHO -s Start time of the animation (HH:MM:SS.MS)
|
||||
ECHO -e End time of the animation (HH:MM:SS.MS)
|
||||
ECHO -x Crop the input video (out_w:out_h:x:y)
|
||||
ECHO Note that cropping occurs before output is scaled
|
||||
ECHO -d Dithering algorithm to be used (default 0)
|
||||
ECHO -b Bayer Scale setting, range 0-5 (default 2)
|
||||
ECHO -m Palettegen mode: 1 (diff, default), 2 (single), 3 (full)
|
||||
ECHO -k Enables paletteuse error diffusion
|
||||
ECHO -y Preview animation using 'FFplay' (part of FFmpeg)
|
||||
ECHO Useful for testing cropping, but will not use exact start/end time
|
||||
ECHO -p Opens the resulting animation in the default image viewer
|
||||
ECHO -v Set FFmpeg log level (default: error)
|
||||
ECHO:
|
||||
|
||||
+108
-69
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash
|
||||
# Description: Video to GIF/APNG/WEBP converter
|
||||
# By: MDHEXT, Nabi KaramAliZadeh, Pathduck
|
||||
# Version: 6.0
|
||||
# Version: 6.1
|
||||
# Url: https://github.com/Pathduck/vid2ani/
|
||||
# License: GNU General Public License v3.0 (GPLv3)
|
||||
|
||||
# Enable error handling
|
||||
# set -euo pipefail
|
||||
#set -euo pipefail
|
||||
|
||||
### Start Main ###
|
||||
main() {
|
||||
@@ -19,31 +19,35 @@ YELLOW=$(tput setaf 11)
|
||||
BLUE=$(tput setaf 12)
|
||||
CYAN=$(tput setaf 14)
|
||||
|
||||
# Checking for blank input or help commands
|
||||
if [ $# -eq 0 ]; then print_help; exit; fi
|
||||
# Check for blank input or help commands
|
||||
if [[ $# -eq 0 ]]; then print_help; exit; fi
|
||||
case "$1" in
|
||||
-h) print_help; exit;;
|
||||
-?) print_help; exit;;
|
||||
--help) print_help; exit;;
|
||||
esac
|
||||
|
||||
# Check if ffmpeg exists on PATH, if not exit
|
||||
if ! command -v 'ffmpeg' >/dev/null 2>&1; then
|
||||
echo ${RED}"FFmpeg not found in PATH, please install it first"${OFF}; exit 1
|
||||
fi
|
||||
|
||||
# Assign input and output
|
||||
input="$1"
|
||||
output="${input%.*}"
|
||||
output=$(basename "${input%.*}")
|
||||
|
||||
# Validate input file
|
||||
if [[ ! -f "$input" ]]; then
|
||||
echo ${RED}"Input file not found: $input"${OFF}; exit 1
|
||||
fi
|
||||
|
||||
# Fix paths for Cygwin and create working directory
|
||||
if [[ "$(uname)" == *"CYGWIN"* ]]; then
|
||||
# Use Windows-compatible directories for Cygwin
|
||||
input=$(cygpath -w "$input")
|
||||
output=$(cygpath -w "$output")
|
||||
# Set uname for later use
|
||||
uname_os=$(uname)
|
||||
|
||||
# Create working directory
|
||||
if [[ $uname_os == *"CYGWIN"* ]]; then
|
||||
WD=$(cygpath -w "$(mktemp -d -t vid2ani-XXXXXX)")
|
||||
else
|
||||
# Use POSIX-compatible directories
|
||||
WD=$(mktemp -d -t vid2ani-XXXXXX)
|
||||
fi
|
||||
|
||||
@@ -57,24 +61,27 @@ dither=0
|
||||
scale="-1"
|
||||
filetype="gif"
|
||||
webp_lossy=""
|
||||
webp_lossy_def=75
|
||||
webp_lossy_q=75
|
||||
loglevel="error"
|
||||
bayerscale=""
|
||||
colormax=""
|
||||
start_time=""
|
||||
end_time=""
|
||||
crop=""
|
||||
errorswitch=""
|
||||
picswitch=""
|
||||
playswitch=""
|
||||
|
||||
# Parse Arguments
|
||||
# Parse Arguments, first shift input one left
|
||||
shift
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o) output="${2%.*}"; shift;;
|
||||
-o) [[ ${2##*/} == *.* ]] && output="${2%.*}" || output="$2"; shift;;
|
||||
-t) filetype="$2"; shift;;
|
||||
-r) scale="$2"; shift;;
|
||||
-l) if [[ "$2" =~ ^[0-9]+$ ]]; then webp_lossy="$2"; shift
|
||||
else webp_lossy=$webp_lossy_def; fi ;;
|
||||
-l) if [[ "$2" =~ ^[0-9]+$ ]]; then
|
||||
webp_lossy=1; webp_lossy_q="$2"; shift
|
||||
else webp_lossy=1; fi ;;
|
||||
-f) fps="$2"; shift;;
|
||||
-s) start_time="$2"; shift;;
|
||||
-e) end_time="$2"; shift;;
|
||||
@@ -83,15 +90,26 @@ while [[ $# -gt 0 ]]; do
|
||||
-m) mode="$2"; shift;;
|
||||
-c) colormax="$2"; shift;;
|
||||
-v) loglevel="$2"; shift;;
|
||||
-x) crop="$2"; shift;;
|
||||
-k) errorswitch=1;;
|
||||
-p) picswitch=1;;
|
||||
-y) playswitch=1;;
|
||||
*) echo ${RED}"Unknown option $1"${OFF}; exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Validate if output file is set and not starts with a -
|
||||
[[ -z $output || $output == -* ]] && { echo ${RED}"Missing value for -o"${OFF}; exit 1; }
|
||||
|
||||
# Validate if output is a directory; strip trailing slash and use input filename
|
||||
if [[ -d "$output" ]]; then
|
||||
output="${output%/}/"$(basename "${input%.*}")
|
||||
echo $output
|
||||
fi
|
||||
|
||||
# Validate output file extension
|
||||
case "$filetype" in
|
||||
case $filetype in
|
||||
gif) output="$output.gif";;
|
||||
png) output="$output.png"; filetype="apng";;
|
||||
apng) output="$output.png";;
|
||||
@@ -100,38 +118,38 @@ case "$filetype" in
|
||||
esac
|
||||
|
||||
# Validate Palettegen
|
||||
if [[ "$mode" -lt 1 || "$mode" -gt 3 ]]; then
|
||||
if [[ $mode -lt 1 || $mode -gt 3 ]]; then
|
||||
echo ${RED}"Not a valid palettegen (-m) mode"${OFF}; exit 1
|
||||
fi
|
||||
|
||||
# Validate Dithering
|
||||
if [[ "$dither" -gt 8 || "$dither" -lt 0 ]]; then
|
||||
if [[ $dither -gt 8 || $dither -lt 0 ]]; then
|
||||
echo ${RED}"Not a valid dither (-d) algorithm"${OFF}; exit 1
|
||||
fi
|
||||
|
||||
# Validate Bayerscale
|
||||
if [[ -n "$bayerscale" ]]; then
|
||||
if [[ "$bayerscale" -gt 5 || "$bayerscale" -lt 0 ]]; then
|
||||
if [[ -n $bayerscale ]]; then
|
||||
if [[ $bayerscale -gt 5 || $bayerscale -lt 0 ]]; then
|
||||
echo ${RED}"Not a valid bayerscale (-b) value"${OFF}; exit 1
|
||||
fi
|
||||
if [[ "$dither" -ne 1 ]]; then
|
||||
if [[ $dither -ne 1 ]]; then
|
||||
echo ${RED}"Bayerscale (-b) only works with Bayer dithering"${OFF}; exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate Lossy WEBP
|
||||
if [[ -n "$webp_lossy" ]]; then
|
||||
if [[ -n $webp_lossy ]]; then
|
||||
if [[ "$filetype" != "webp" ]]; then
|
||||
echo ${RED}"Lossy (-l) is only valid for filetype webp"${OFF}; exit 1
|
||||
fi
|
||||
if [[ "$webp_lossy" -gt 100 || "$webp_lossy" -lt 0 ]]; then
|
||||
if [[ $webp_lossy_q -gt 100 || $webp_lossy_q -lt 0 ]]; then
|
||||
echo ${RED}"Not a valid lossy (-l) quality value"${OFF}; exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate Clipping
|
||||
if [[ -n "$start_time" && -z "$end_time" ]]; then
|
||||
echo ${RED}"End time (-e) is required when Start time (-e) is specified."${OFF}; exit 1
|
||||
echo ${RED}"End time (-e) is required when Start time (-s) is specified."${OFF}; exit 1
|
||||
elif [[ -n "$end_time" && -z "$start_time" ]]; then
|
||||
echo ${RED}"Start time (-s) is required when End time (-e) is specified."${OFF}; exit 1
|
||||
elif [[ -n "$end_time" && -n "$start_time" ]]; then
|
||||
@@ -139,49 +157,66 @@ elif [[ -n "$end_time" && -n "$start_time" ]]; then
|
||||
fi
|
||||
|
||||
# Validate Framerate
|
||||
if [[ "$fps" -le 0 ]]; then
|
||||
if [[ $fps -le 0 ]]; then
|
||||
echo ${RED}"Framerate (-f) must be greater than 0."${OFF}; exit 1
|
||||
fi
|
||||
|
||||
# Validate Max Colors
|
||||
if [[ -n "$colormax" && "$colormax" -lt 3 || "$colormax" -gt 256 ]]; then
|
||||
if [[ -n $colormax && ( $colormax -lt 3 || $colormax -gt 256 ) ]]; then
|
||||
echo ${RED}"Max colors (-c) must be between 3 and 256."${OFF}; exit 1
|
||||
fi
|
||||
|
||||
# Displaying FFmpeg version string
|
||||
ffmpeg_version=$(ffmpeg -version | head -n2)
|
||||
echo ${YELLOW}"$ffmpeg_version"${OFF}
|
||||
echo ${GREEN}Output file:${OFF} $output
|
||||
|
||||
## Putting together command to generate palette ##
|
||||
# Putting together command to generate palette
|
||||
palette="$WD/palette_%05d.png"
|
||||
filters="fps=$fps,scale=$scale:-1:flags=lanczos"
|
||||
filters="fps=$fps"
|
||||
[[ -n "$crop" ]] && filters+=",crop=$crop"
|
||||
filters+=",scale=$scale:-1:flags=lanczos"
|
||||
|
||||
# APNG muxer does not support multiple palettes so fallback to using palettegen diff mode
|
||||
if [[ "$filetype" == "apng" && "$mode" -eq 2 ]]; then
|
||||
# Fix paths for Cygwin before running ffmpeg/ffplay
|
||||
if [[ $uname_os == *"CYGWIN"* ]]; then
|
||||
input=$(cygpath -w "$input")
|
||||
output=$(cygpath -w "$output")
|
||||
palette=$(cygpath -w "$palette")
|
||||
fi
|
||||
|
||||
# FFplay preview
|
||||
if [[ -n $playswitch ]]; then
|
||||
# Check if ffplay exists on PATH, if not exit
|
||||
if ! command -v 'ffplay' >/dev/null 2>&1; then
|
||||
echo ${RED}"FFplay not found in PATH, please install it first"${OFF}; exit 1
|
||||
fi
|
||||
echo ${YELLOW}"$(ffplay -version | head -n2)"${OFF}
|
||||
ffplay -v ${loglevel} -i "${input}" -vf "${filters}" -an -loop 0 -ss ${start_time:-0} -t ${end_time:-3}
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# APNG muxer does not support multiple palettes, fallback to palettegen diff mode
|
||||
if [[ $filetype == "apng" && $mode -eq 2 ]]; then
|
||||
echo ${YELLOW}"APNG does not support multiple palettes - falling back to Palettegen mode 1 (diff)"${OFF}
|
||||
mode=1
|
||||
fi
|
||||
|
||||
# Palettegen encode mode
|
||||
if [[ -n "$mode" ]]; then
|
||||
case "$mode" in
|
||||
1) encode="palettegen=stats_mode=diff";;
|
||||
2) encode="palettegen=stats_mode=single";;
|
||||
3) encode="palettegen";;
|
||||
*) echo ${RED}"Invalid palettegen (-m) mode"${OFF}; exit 1;;
|
||||
esac
|
||||
fi
|
||||
case $mode in
|
||||
1) encode="palettegen=stats_mode=diff";;
|
||||
2) encode="palettegen=stats_mode=single";;
|
||||
3) encode="palettegen";;
|
||||
*) echo ${RED}"Invalid palettegen (-m) mode"${OFF}; exit 1;;
|
||||
esac
|
||||
|
||||
# Max colors
|
||||
if [[ -n "$colormax" ]]; then
|
||||
if [[ "$mode" -le 2 ]]; then mcol=":max_colors=${colormax}"; fi
|
||||
if [[ "$mode" -eq 3 ]]; then mcol="=max_colors=${colormax}"; fi
|
||||
if [[ -n $colormax ]]; then
|
||||
if [[ $mode -le 2 ]]; then mcol=":max_colors=${colormax}"; fi
|
||||
if [[ $mode -eq 3 ]]; then mcol="=max_colors=${colormax}"; fi
|
||||
fi
|
||||
|
||||
# Displaying FFmpeg version string and output file
|
||||
echo ${YELLOW}"$(ffmpeg -version | head -n2)"${OFF}
|
||||
echo ${GREEN}Output file:${OFF} $output
|
||||
|
||||
# Executing command to generate palette
|
||||
echo ${GREEN}"Generating palette..."${OFF}
|
||||
ffmpeg -v "${loglevel}" ${trim:-} -i "${input}" -vf "${filters},${encode}${mcol}" -y "${palette}"
|
||||
ffmpeg -v ${loglevel} ${trim:-} -i "${input}" -vf "${filters},${encode}${mcol:-}" -y "${palette}"
|
||||
|
||||
# Checking if the palette file is in the Working Directory, if not cleaning up
|
||||
if [[ ! -f "$WD/palette_00001.png" ]]; then
|
||||
@@ -191,8 +226,8 @@ fi
|
||||
## Setting variables to put the encode command together ##
|
||||
|
||||
# Palettegen decode mode
|
||||
if [[ -n "$mode" ]]; then
|
||||
case "$mode" in
|
||||
if [[ -n $mode ]]; then
|
||||
case $mode in
|
||||
1) decode="paletteuse";;
|
||||
2) decode="paletteuse=new=1";;
|
||||
3) decode="paletteuse";;
|
||||
@@ -201,8 +236,8 @@ if [[ -n "$mode" ]]; then
|
||||
fi
|
||||
|
||||
# Error diffusion
|
||||
if [[ -n "$errorswitch" ]]; then
|
||||
case "$mode" in
|
||||
if [[ -n $errorswitch ]]; then
|
||||
case $mode in
|
||||
1) errordiff="=diff_mode=rectangle";;
|
||||
2) errordiff=":diff_mode=rectangle";;
|
||||
3) errordiff="=diff_mode=rectangle";;
|
||||
@@ -211,7 +246,7 @@ if [[ -n "$errorswitch" ]]; then
|
||||
fi
|
||||
|
||||
# Prepare dithering and encoding options
|
||||
case "$dither" in
|
||||
case $dither in
|
||||
0) ditheralg="none";;
|
||||
1) ditheralg="bayer";;
|
||||
2) ditheralg="heckbert";;
|
||||
@@ -225,29 +260,29 @@ case "$dither" in
|
||||
esac
|
||||
|
||||
# Paletteuse error diffusion
|
||||
if [[ "$mode" -ne 2 ]]; then
|
||||
if [[ -n "$errorswitch" ]]; then ditherenc=":dither=$ditheralg"; fi
|
||||
if [[ -z "$errorswitch" ]]; then ditherenc="=dither=$ditheralg"; fi
|
||||
if [[ $mode -ne 2 ]]; then
|
||||
if [[ -n $errorswitch ]]; then ditherenc=":dither=$ditheralg"; fi
|
||||
if [[ -z $errorswitch ]]; then ditherenc="=dither=$ditheralg"; fi
|
||||
else
|
||||
ditherenc=":dither=$ditheralg"
|
||||
fi
|
||||
|
||||
# Checking for Bayer Scale and adjusting command
|
||||
if [[ -n "$bayerscale" ]]; then bayer=":bayer_scale=$bayerscale"; fi
|
||||
if [[ -z "$bayerscale" ]]; then bayer=""; fi
|
||||
if [[ -z $bayerscale ]]; then bayer=""; fi
|
||||
if [[ -n $bayerscale ]]; then bayer=":bayer_scale=$bayerscale"; fi
|
||||
|
||||
# WEBP pixel format and lossy quality
|
||||
if [[ "$filetype" == "webp" && -n "$webp_lossy" ]]; then
|
||||
webp_lossy="-lossless 0 -quality $webp_lossy -pix_fmt yuva420p"
|
||||
elif [[ "$filetype" == "webp" && -z "$webp_lossy" ]]; then
|
||||
webp_lossy="-lossless 1"
|
||||
if [[ $filetype == "webp" && -n $webp_lossy ]]; then
|
||||
type_opts="-lossless 0 -pix_fmt yuva420p -quality $webp_lossy_q"
|
||||
elif [[ $filetype == "webp" && -z $webp_lossy ]]; then
|
||||
type_opts="-lossless 1"
|
||||
fi
|
||||
|
||||
# Executing the encoding command
|
||||
echo ${GREEN}"Encoding animation..."${OFF}
|
||||
ffmpeg -v "${loglevel}" ${trim:-} -i "${input}" -thread_queue_size 512 -i "${palette}" \
|
||||
-lavfi "${filters} [x]; [x][1:v] ${decode}${errordiff}${ditherenc}${bayer}" \
|
||||
-f "${filetype}" ${webp_lossy:-} -loop 0 -plays 0 -y "${output}"
|
||||
ffmpeg -v ${loglevel} ${trim:-} -i "${input}" -thread_queue_size 512 -i "${palette}" \
|
||||
-lavfi "${filters} [x]; [x][1:v] ${decode}${errordiff:-}${ditherenc}${bayer}" \
|
||||
-f ${filetype} ${type_opts:-} -loop 0 -plays 0 -y "${output}"
|
||||
|
||||
# Checking if output file was created
|
||||
if [[ ! -f "$output" ]]; then
|
||||
@@ -255,7 +290,7 @@ if [[ ! -f "$output" ]]; then
|
||||
fi
|
||||
|
||||
# Open output file if picswitch is enabled
|
||||
if [[ -n "$picswitch" ]]; then
|
||||
if [[ -n $picswitch ]]; then
|
||||
xdg-open "$output"
|
||||
fi
|
||||
|
||||
@@ -267,25 +302,29 @@ echo ${GREEN}"Done."${OFF}
|
||||
### Function to print the help message ###
|
||||
print_help() {
|
||||
cat << EOF
|
||||
${GREEN}Video to GIF/APNG/WEBP converter v6.0${OFF}
|
||||
${GREEN}Video to GIF/APNG/WEBP converter v6.1${OFF}
|
||||
${BLUE}By MDHEXT, Nabi KaramAliZadeh, Pathduck${OFF}
|
||||
|
||||
${GREEN}Usage:${OFF}
|
||||
$(basename $0) [input_file] [arguments]
|
||||
$(basename "$0") [input_file] [arguments]
|
||||
|
||||
${GREEN}Arguments:${OFF}
|
||||
-o Output file. Default is the same as input file, sans extension
|
||||
-t Output file type: 'gif' (default), 'apng', 'png', 'webp'
|
||||
-r Scale or size. Width of the animation in pixels
|
||||
-r Resize output width in pixels. Default is original input size
|
||||
-l Enable lossy WebP compression and quality, range 0-100 (default 75)
|
||||
-f Framerate in frames per seconds (default 15)
|
||||
-c Maximum colors usable per palette, range 3-256 (default 256)
|
||||
-s Start time of the animation (HH:MM:SS.MS)
|
||||
-e End time of the animation (HH:MM:SS.MS)
|
||||
-x Crop the input video (out_w:out_h:x:y)
|
||||
Note that cropping occurs before output is scaled
|
||||
-d Dithering algorithm to be used (default 0)
|
||||
-b Bayer Scale setting, range 0-5 (default 2)
|
||||
-m Palettegen mode: 1 (diff, default), 2 (single), 3 (full)
|
||||
-k Enables paletteuse error diffusion
|
||||
-y Preview animation using 'FFplay' (part of FFmpeg)
|
||||
(Useful for testing cropping, but will not use exact start/end time)
|
||||
-p Opens the resulting animation in the default image viewer
|
||||
-v Set FFmpeg log level (default: error)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user