batch-filecmdvideo-processingdelayedvariableexpansionvideo-watermarking

Watermark Not Working For Videos With Exclamation Marks


I made a .bat script to watermark videos with the logo slowly sliding across the video from the right to the left. Here is my code:

set ffmpeg="C:\ffmpeg\ffmpeg.exe"
set ffprobe="C:\ffmpeg\ffprobe.exe"

@echo off
setlocal enableextensions enabledelayedexpansion
for %%A in ("H:\4 - Watermark Process\Before\*.mp4") do (
    echo "________________________________________________________________________________________________________________________"
    echo _ Filename _: %%A
    rem Find videoDuration
    for /F "delims=" %%G in ('ffprobe.exe -v error -show_entries format^=duration -of default^=noprint_wrappers^=1:nokey^=1 "%%A" 2^>^&1') do (
        set /A videoDuration=%%G/1
        echo _ Video Duration _: !videoDuration!
    )

    rem Find videoWidth
    for /F "delims=" %%I in ('ffprobe -v error -show_entries stream^=width -of csv^=p^=0:s^=x "%%A"') do (
        set videoWidth=%%I
        echo _ Video Width _: !videoWidth!
    )

    rem Find videoHeight
    for /F "delims=" %%J in ('ffprobe -v error -show_entries stream^=height -of csv^=s^=x:p^=0 "%%A"') do (
        set videoHeight=%%J
        echo _ Video Height _: !videoHeight!
    )

    rem Scale logo watermark according to video Width and Height
    if !videoHeight! LSS !videoWidth! (
        echo "_ videoHeight LESS THAN videoWidth _"
        set /A scaleWidth = !videoWidth!/10
        ffmpeg -i logo.png -y -v verbose -vf scale=!scaleWidth!:-1 scaled.png
    ) else (
        echo "_ videoHeight GREATER THAN videoWidth _"
        set /A scaleWidth = !videoWidth!/4
        ffmpeg -i logo.png -y -v verbose -vf scale=!scaleWidth!:-1 scaled.png
    )

    rem Place logo watermark based on video Duration
    if !videoDuration! LSS 10 (
        echo "_ videoDuration LESS THAN 10 seconds _"
        echo "________________________________________________________________________________________________________________________"
        ffmpeg -i "%%A" -i scaled.png -filter_complex "overlay=10:main_h-overlay_h" "H:\4 - Watermark Process\After\%%~nA.mp4"
        echo "______________________________________________LESS THAN 10SEC CONVERSION COMPLETE__________________________________________________________________________"
    )
    if !videoDuration! GEQ 10 (
        set /A "videoDurationOneThird=(videoDuration*2)/3"
        echo _ videoDuration One Third _: !videoDurationOneThird!
        echo "________________________________________________________________________________________________________________________"
        ffmpeg -i "%%A" -i scaled.png -filter_complex "[0:v][1:v] overlay='if(gte(t,!videoDurationOneThird!), (main_w-(w+(t-!videoDurationOneThird!)*20)), NAN)':(main_h-overlay_h)/2:enable=between'(t,!videoDurationOneThird!,!videoDuration!)'" "H:\4 - Watermark Process\After\%%~nA.mp4"
    )
)

pause 10

Problem is I get a an error on videos with exclamation points in them saying: No such file or directory

I read that enabling delayed expansion removes the exclamation point but I need it for the script to work correctly. How can I fix this?

The failed solution I've tried is below:

set ffmpeg="C:\ffmpeg\ffmpeg.exe"
set ffprobe="C:\ffmpeg\ffprobe.exe"

@echo off
setlocal enableextensions disabledelayedexpansion
for %%A in ("H:\4 - Watermark Process\BeforeWide\*.mp4") do (
    set FILE=%%~A
    set NAME=%%~nA
    echo "________________________________________________________________________________________________________________________"
    echo _ Filename _: !FILE!
    setlocal enabledelayedexpansion
    rem Find videoDuration
    for /F "delims=" %%G in ('ffprobe.exe -v error -show_entries format^=duration -of default^=noprint_wrappers^=1:nokey^=1 !FILE! 2^>^&1') do (
        set /A videoDuration=%%G/1
        echo _ Video Duration _: !videoDuration!
    )

    rem Find videoWidth
    for /F "delims=" %%I in ('ffprobe -v error -show_entries stream^=width -of csv^=p^=0:s^=x !FILE!') do (
        set videoWidth=%%I
        echo _ Video Width _: !videoWidth!
    )

    rem Find videoHeight
    for /F "delims=" %%J in ('ffprobe -v error -show_entries stream^=height -of csv^=s^=x:p^=0 !FILE!') do (
        set videoHeight=%%J
        echo _ Video Height _: !videoHeight!
    )

    rem Scale logo watermark according to video Width and Height
    if !videoHeight! LSS !videoWidth! (
        echo "_ videoHeight LESS THAN videoWidth _"
        set /A scaleWidth = !videoWidth!/10
        ffmpeg -i logo.png -y -v verbose -vf scale=!scaleWidth!:-1 scaled.png
    ) else (
        echo "_ videoHeight GREATER THAN videoWidth _"
        set /A scaleWidth = !videoWidth!/4
        ffmpeg -i logo.png -y -v verbose -vf scale=!scaleWidth!:-1 scaled.png
    )

    rem Place logo watermark based on video Duration
    if !videoDuration! LSS 10 (
        echo "_ videoDuration LESS THAN 10 seconds _"
        echo "________________________________________________________________________________________________________________________"
        ffmpeg -i !FILE! -i scaled.png -filter_complex "overlay=10:main_h-overlay_h" "H:\4 - Watermark Process\After\!NAME!.mp4"
        echo "______________________________________________LESS THAN 10SEC CONVERSION COMPLETE__________________________________________________________________________"
    )
    if !videoDuration! GEQ 10 (
        set /A "videoDurationOneThird=(videoDuration*2)/3"
        echo _ videoDuration One Third _: !videoDurationOneThird!
        echo "________________________________________________________________________________________________________________________"
        ffmpeg -i !FILE! -i scaled.png -filter_complex "[0:v][1:v] overlay='if(gte(t,!videoDurationOneThird!), (main_w-(w+(t-!videoDurationOneThird!)*20)), NAN)':(main_h-overlay_h)/2:enable=between'(t,!videoDurationOneThird!,!videoDuration!)'" "H:\4 - Watermark Process\After\!NAME!.mp4"
    )
    endlocal
)

pause 10

Solution

  • Here's an untested example for you, based upon the information you provided:

    @Echo Off
    SetLocal EnableExtensions DisableDelayedExpansion
    
    Set "Source=H:\4 - Watermark Process"
    Set "Before=BeforeWide"
    Set "After=After"
    Set "Glob=*.mp4"
    Set "ffmpeg=C:\ffmpeg\ffmpeg.exe"
    Set "ffprobe=C:\ffmpeg\ffprobe.exe"
    Set "logo=%CD%\logo.png"
    Set "scaled=%CD%\scaled.png"
    
    For %%A In ("%Source%\%Before%\%Glob%") Do (
        Set "FullName=%%A"
        Set "NameOnly=%%~nxA"
        If Not Exist "%Source%\%After%\" MD "%Source%\%After%" || GoTo :EOF  2> NUL
        SetLocal EnableDelayedExpansion
        Echo ________________________________________________________________________________________________________________________
        Echo _ Filename _: !FullName!
        Set /A "videoDuration=videoWidth=videoHeight=0"
        Rem Find videoDuration
        For /F Delims^=^ EOL^= %%G In (
            '^""%ffprobe%" -v error -show_entries format^=duration -of default^=noprint_wrappers^=1:nokey^=1 "!FullName!" 2^>^&1^"'
        ) Do Set /A "videoDuration=%%G / 1" 2> NUL
        Echo _ Video Duration _: !videoDuration!
        Rem Find videoWidth
        For /F Delims^=^ EOL^= %%I In (
            '^""%ffprobe%" -v error -show_entries stream^=width -of csv^=p^=0:s^=x "!FullName!"^"'
        ) Do Set /A "videoWidth=%%I" 2> NUL 
        Echo _ Video Width _: !videoWidth!
        Rem Find videoHeight
        For /F Delims^=^ EOL^= %%J In (
            '^""%ffprobe%" -v error -show_entries stream^=height -of csv^=s^=x:p^=0 "!FullName!"^"'
        ) Do Set /A "videoHeight=%%J" 2> NUL
        Echo _ Video Height _: !videoHeight!
        Rem Scale logo watermark according to video Width and Height
        If !videoHeight! Lss !videoWidth! (
            Echo _ videoHeight LESS THAN videoWidth _
            Set /A "scaleWidth=videoWidth / 10"
        ) Else (
            Echo _ videoHeight GREATER THAN videoWidth _
            Set /A "scaleWidth=videoWidth / 4"
        )
        "%ffmpeg%" -i "%logo%" -y -v verbose -vf scale=!scaleWidth!:-1 "%scaled%"
        Rem Place logo watermark based on video Duration
        If !videoDuration! Lss 10 (
            Echo _ videoDuration LESS THAN 10 seconds _
            Echo ________________________________________________________________________________________________________________________
            "%ffmpeg%" -i "!FullName!" -i "%scaled%" -filter_complex "overlay=10:main_h-overlay_h" "%Source%\%After%\!NameOnly!"
            Echo __________________________________________LESS THAN 10SEC CONVERSION COMPLETE___________________________________________
        )
        If !videoDuration! GEq 10 (
            Set /A "videoDurationOneThird = (videoDuration * 2) / 3"
            Echo _ videoDuration One Third _: !videoDurationOneThird!
            Echo ________________________________________________________________________________________________________________________
            "%ffmpeg%" -i "!FullName!" -i "%scaled%" -filter_complex "[0:v][1:v] overlay='if(gte(t,!videoDurationOneThird!), (main_w-(w+(t-!videoDurationOneThird!)*20)), NAN)':(main_h-overlay_h)/2:enable=between'(t,!videoDurationOneThird!,!videoDuration!)'" "%Source%\%After%\!NameOnly!"
        )
        EndLocal
    )
    %SystemRoot%\System32\timeout.exe /T 10 /NoBreak 1> NUL
    

    You should only need to modify the variable values, in the isolated Set group. Please note that I've used %CD% for the .png files, as you hadn't provided a location. However, as you've not set the current directory either, you may actually have those files in the same directory as the running script. If that is the case you should really change both from %CD% to %~dp0.