issues with passing FOR loop through GOTO loop to new command prompt

Viewed 260

This is my first post so I do apologize if I'm not explaining it well. Please let me know if I should re-phrase things or clarify something! I am also not exactly great with titles, so please let me know if I should change it.

To help myself explain the situation a little more, I'd like to clarify what the purpose of this is. As one of my hobbies, I help on an image archive. One of it's issues is that the only video format it supports is webm. This can sometimes result in badly converted low-quality videos by users whom have no idea how to convert, which then needs to be fixed later.

Because of this, I've been working on a bunch of batch files that could both speed up and potentially lower the amount of badly converted video, by minimizing the required user-input. You can check it out here if you'd like to: https://www.nicklo6649.com/Public%20files/index.php?dir=Batchfiles/

One of the staff members mentioned how they'd like a way to start multiple conversion at the same time and let it convert an entire folder, as their CPU is powerful enough to do so. Which is then what I've been working on now for a few days.

My current problem with the script is the fact that I cannot seem to reliably pass the FOR loop to the new command prompts that it starts in the GOTO loop, in any way.

I don't mind having to rewrite the current script, as long as it's still able to start multiple instances of ffmpeg with the FOR loop and it's specified values.

The solution would be preferred to be in form of a batch or PowerShell script, although any other language is also fine, as long as it can work alongside the batch file.

I've tried the following:

starting cmd with the FOR loop In the GOTO loop with:

start cmd.exe <FOR loop>

saving the FOR loop in a text file before the GOTO loop, starting a new batch file named command2.bat in the GOTO loop that then takes the FOR loop in the text file and saves it to a variable, and then calls the variable:

Echo "<FOR loop>"> command.txt

command2.bat script:

setlocal EnableDelayedExpansion
set /p command=<command.txt
set command="%command%"
%command%
endlocal
pause

Saving the the FOR loop in another batch file before the GOTO loop, and then starting the batch file with the GOTO loop:

Echo "<FOR loop>"> command.bat

And this is the script itself.

:next1
rem used to specify the input folder which the FOR loop should look in
set /p input=Input:

rem used to specify the output folder which the FOR loop should look in
set /p output=output:

rem used to specify how many time the GOTO loop should loop and start a new command prompt
set /p amount=:

if "%amount%"=="0" echo fatal error, please retry && goto :next1 

rem this creates a folder for the temporary files ffmpeg needs to store while doing the first pass in two-pass
mkdir "temp files"

@echo on

setlocal EnableDelayedExpansion

set loop=0
:loop
echo.
start cmd.exe for %%f in ("%input%\*.*") do ffmpeg -n -i "%%f" -c:v vp9 -c:a libopus -crf 20 -b:v 192 -pix_fmt yuv420p -pass 1 -f webm "temp files\!random!" && ffmpeg -n -i "%%f" -c:v vp9 -c:a libopus -crf 20 -b:v 192 -an -pix_fmt yuv420p -pass 2 %output%\%%~nf.webm
echo.
set /a loop=%loop%+1 
if "%loop%"=="%amount%" goto :next
if "%loop%"==11 goto :next
goto loop

:next
endlocal
del /q /f ffmpeg2pass*
del /q /f "temp files\*.*"

:end
choice /c EC /n /m "(E)xit or (C)ontinue"
if errorlevel 1 set exit=Quit
if errorlevel 2 set exit=Continue
if "%exit%"=="Quit" goto :exit
if "%exit%"=="Continue" goto :skip
:exit
exit

The expected output is that the script starts x amount of new command prompts, each using the same FOR loop that was defined in the script and uses the specified input, output and future selections, such as codecs.

2 Answers
@echo off
setlocal
set "tempdir=temp files"

:next1
rem Specify the input folder which the FOR loop should look in.
set /p "inputdir=InputDir: "
set "inputdir=%inputdir:"=%"

rem Specify the output folder which the FOR loop should look in.
set /p "outputdir=OutputDir: "
set "outputdir=%outputdir:"=%"

rem Specify how many time the GOTO loop should loop and start a new command prompt.
set "amount=0"
set /p "amount=Amount: "

rem Check if integer, else amount will be 0.
set /a "amount+=0"

rem Check if integer is 0.
if "%amount%" == "0" echo Please retry& goto :next1

rem Create a folder for the temporary files ffmpeg needs to store while doing the first pass in two-pass.
if not exist "%tempdir%" md "%tempdir%"

for %%A in ("%inputdir%\*.*") do call :encode "%%~A"
exit /b 0


:encode
setlocal
set "count=0"
set "imagename=ffmpeg.exe"

rem Run tasklist.
if %amount% gtr 1 (
    for /f %%A in ('tasklist /nh /fi "IMAGENAME eq %imagename%"') do (
        if /i "%%~A" == "%imagename%" set /a "count+=1"
    )
)

rem Check count of processes.
if %count% geq %amount% (
    >nul ping -n 10 localhost
    endlocal
    goto :encode
)

rem Set wait for 1 instance only.
set "wait="
if %amount% equ 1 set "wait=/w"

rem Encode with ffmpeg.
start "%ComSpec%" %wait%^
 cmd /c^
 ffmpeg -y -hide_banner -i "%~1" -c:v vp9 -crf 20 -b:v 192k -an -pix_fmt yuv420p -pass 1 -passlogfile "%tempdir%\%~n1" -f webm nul ^&^&^
 ffmpeg -y -hide_banner -i "%~1" -c:v vp9 -crf 20 -b:v 192k -c:a libvorbis -b:a 64k -pix_fmt yuv420p -pass 2 -passlogfile "%tempdir%\%~n1" "%outputdir%\%~n1.webm" ^&^&^
 del "%tempdir%\%~n1-0.log"
exit /b 0

This code uses tasklist to count the number of ffmpeg.exe instances. If the count is greater or equal to the amount entered, the script will wait for 10 seconds and then try again with tasklist. This allows the count of ffmpeg.exe instances to be as many as the amount entered.

ffmpeg code differences

  • Audio encoding set as -c:a libvorbis -b:a 64k.
  • Output of 1st pass saved to nul.
  • Inserted -hide_banner to reduce console output.
  • -b:v 192 changed to -b:v 192k so the bitrate unit type is specified. Seems low bitrate, perhaps should be -b:v 192K.
  • Inserted -passlogfile argument so log files have a known and possibly unique name to identify.
  • The 1st pass log file will be deleted at the end of the 2nd pass.

I hope this can help. It might have to apply some modifications. As per edit in the main post I changed the switch -y per -n.

@echo off

setlocal EnableDelayedExpansion

:Pre_Main
::How many instances should be initiated. Limit hardcoded to 11.
::Input dir , Output dir.
set /p input=Input:
set /p output=Output:
set /p amount=Amount:

REM If not defined %amount% (
    REM echo Please specify a number of instances greater than 0
    REM goto :Pre_Main
REM )

if %amount% LEQ 0 (
    echo Please specify a number of instances greater than 0
    goto :Pre_Main
)

cd input

if exist ffmpeg_temp_files (
    rd /s /q ffmpeg_temp_files
) ELSE (
    md ffmpeg_temp_files
)

set "n_loop=1"

::
::End-Pre_Main
::

:loop
for %%f in (*) do (
    @echo on
    echo inside the loop... File: %%f n_loop: !n_loop!
    start "!n_loop!...%%f" ffmpeg.exe -n -i "%%f" -c:v vp9 -c:a libopus -crf 20 -b:v 192 -pix_fmt yuv420p -pass 1 -f webm "temp files\%%~nf_!time!_!n_loop!" && ffmpeg.exe -n -i "%%f" -c:v vp9 -c:a libopus -crf 20 -b:v 192 -an -pix_fmt yuv420p -pass 2 %output%\%%~nf.webm
    @echo off

    if !n_loop! GEQ !amount! goto :Pos_Main
    if !n_loop! EQU 12       goto :Pos_Main
    set /a n_loop=!n_loop!+1
)

:Pos_Main

endlocal

if exist ffmpeg_temp_files rd /s /q ffmpeg_temp_files
del /q /f ffmpeg2pass*

cd ..

choice /c EC /n /m "(E)xit or (C)ontinue"
if %errorlevel% EQU 1 goto :exit 
if %errorlevel% EQU 2 goto :Pre_Main

::
::End-Pos_Main
::

:exit
exit /b
Related