Miguel Arrieche
Miguel Arrieche

Reputation: 61

Ffmpeg it's not setting the correct variable names and hence throwing Unable to choose format

I'm trying to automate the subtitle addition to a folder with some videos so I wrote the following script:

forfiles /p "C:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess" /m *.mp4 /C "cmd /c set filename=@fname & ffmpeg -i @file -vf subtitles="C:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess\@fname.srt" -f mp4 "C:\Users\titos\Resilio_Sync\4K_Video_Downloader\donesubs\@fname_srt.mp4""

However, the script is throwing me the following error for each video:

[AVFormatContext @ 0000020c20220680] Unable to choose an output format for 'Der'; use a standard extension for the filename or specify the format manually.
[out#0 @ 0000020c20220580] Error initializing the muxer for Der: Invalid argument
Error opening output file Der.
Error opening output files: Invalid argument

In this case, "Der" is the second word of the video's title (And it does the same with each video where it throws the error with the second word). So I think it might be due to the videos containing spaces and ffmpeg not parsing it correctly? But why is the second word that it gets and not the first one in that case? And more importantly, what could I do to fix this issue?

I've honestly found some other threads that discuss similar issues, but quite frankly I'm still too newbie to understand any of the answers in order to adapt it to my script lol. I appreciate your help, thanks a lot in advance :)

Upvotes: 0

Views: 179

Answers (2)

Compo
Compo

Reputation: 38708

The help information for the forfiles utility, explains how to replace your nested doublequotes, " with their hex equivalent, 0x22.

Using that information, please change:

subtitles="C:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess\@fname.srt" -f mp4 "C:\Users\titos\Resilio_Sync\4K_Video_Downloader\donesubs\@fname_srt.mp4"

To

subtitles=0x22C:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess\@fname.srt0x22 -f mp4 0x22C:\Users\titos\Resilio_Sync\4K_Video_Downloader\donesubs\@fname_srt.mp40x22

You can replace any character which is causing you issues nested between the outer doublequotes with their hex equivalents. For instance &->0x26, =->0x3D.

With nested doublequotes, you also have the option of escaping them, rather than converting them to hex. You can do that with a backward slash, \.

This would, in your case, mean using:

subtitles=\"C:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess\@fname.srt\" -f mp4 \"C:\Users\titos\Resilio_Sync\4K_Video_Downloader\donesubs\@fname_srt.mp4\"

However, there is yet another potential issue here; the forfiles variables, @File, @FName, @Ext, @Path, and @RelPath are each returned doublequoted. Those doublequotes are likely to confuse ffmpeg.exe.

It would see:

subtitles="C:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess\"FileBaseName".srt" -f mp4 "C:\Users\titos\Resilio_Sync\4K_Video_Downloader\donesubs\"FileBaseName"_srt.mp4"

To solve that, you would need to somehow expand those variables to remove their surrounding doublequotes. That could be done by using a for loop:

Hex converted doublequotes example:

@Echo Off
SetLocal EnableExtensions
%SystemRoot%\System32\forfiles.exe /P "%UserProfile%\Resilio_Sync\4K_Video_Downloader\videoprocess" /M *.mp4 /C "%SystemRoot%\System32\cmd.exe /Q /D /C 0x22If @IsDir==FALSE For /F 0x22Delims=0x22 %%G In (@FName) Do 0x22P:\athTo\ffmpeg.exe0x22 -i @File -vf subtitles=0x22%UserProfile%\Resilio_Sync\4K_Video_Downloader\videoprocess\%%~G.srt0x22 -f mp4 0x22%UserProfile%\Resilio_Sync\4K_Video_Downloader\donesubs\%%~G_srt.mp40x220x22"

Escaped doublequotes example:

@Echo Off
SetLocal EnableExtensions
%SystemRoot%\System32\forfiles.exe /P "%UserProfile%\Resilio_Sync\4K_Video_Downloader\videoprocess" /M *.mp4 /C "%SystemRoot%\System32\cmd.exe /Q /D /C \"If @IsDir==FALSE For /F \"Delims=\" %%G In (@FName) Do \"P:\athTo\ffmpeg.exe\" -i @File -vf subtitles=\"%UserProfile%\Resilio_Sync\4K_Video_Downloader\videoprocess\%%~G.srt\" -f mp4 \"%UserProfile%\Resilio_Sync\4K_Video_Downloader\donesubs\%%~G_srt.mp4\"\""

The above, would ordinarily be the appropriate, and correct syntax for your shown task, but, there is another issue here, that you're likely to encounter. The value for cmd.exe's /C option cannot be more than 253 characters long, and I'd assume the above would exceed that and return an ERROR message.

To overcome that, you may be best advised to change directory first, then use relative paths, rather than the repetition of C:\Users\titos\Resilio_Sync\4K_Video_Downloader\, to see if it reduces that character number to something workable.

Hex converted doublequotes example:

@Echo Off
SetLocal EnableExtensions
CD /D "%UserProfile%\Resilio_Sync\4K_Video_Downloader" 2>NUL || Exit /B
%SystemRoot%\System32\forfiles.exe /P "videoprocess" /M *.mp4 /C "%SystemRoot%\System32\cmd.exe /Q /D /C 0x22If @IsDir==FALSE For /F 0x22Delims=0x22 %%G In (@FName) Do 0x22P:\athTo\ffmpeg.exe0x22 -i @File -vf subtitles=0x22videoprocess\%%~G.srt0x22 -f mp4 0x22donesubs\%%~G_srt.mp40x220x22"

Escaped doublequotes example:

@Echo Off
SetLocal EnableExtensions
CD /D "%UserProfile%\Resilio_Sync\4K_Video_Downloader" 2>NUL || Exit /B
%SystemRoot%\System32\forfiles.exe /P "videoprocess" /M *.mp4 /C "%SystemRoot%\System32\cmd.exe /Q /D /C \"If @IsDir==FALSE For /F \"Delims=\" %%G In (@FName) Do \"P:\athTo\ffmpeg.exe\" -i @File -vf subtitles=\"videoprocess\%%~G.srt\" -f mp4 \"donesubs\%%~G_srt.mp4\"\""

Only if the above fails to reduce the character count sufficiently, should you consider removing the full paths to your forfiles.exe, cmd.exe, and ffmpeg.exe files, (after confirming that each do have their parent locations listed in the value of %Path%):

Hex converted doublequotes example:

@Echo Off
SetLocal EnableExtensions
CD /D "%UserProfile%\Resilio_Sync\4K_Video_Downloader" 2>NUL || Exit /B
forfiles.exe /P "videoprocess" /M *.mp4 /C "cmd.exe /Q /D /C 0x22If @IsDir==FALSE For /F 0x22Delims=0x22 %%G In (@FName) Do ffmpeg.exe -i @File -vf subtitles=0x22videoprocess\%%~G.srt0x22 -f mp4 0x22donesubs\%%~G_srt.mp40x220x22"

Escaped doublequotes example:

@Echo Off
SetLocal EnableExtensions
CD /D "%UserProfile%\Resilio_Sync\4K_Video_Downloader" 2>NUL || Exit /B
forfiles.exe /P "videoprocess" /M *.mp4 /C "cmd.exe /Q /D /C \"If @IsDir==FALSE For /F \"Delims=\" %%G In (@FName) Do ffmpeg.exe -i @File -vf subtitles=\"videoprocess\%%~G.srt\" -f mp4 \"donesubs\%%~G_srt.mp4\"\""

As you can see, working with forfiles.exe can throw many obstacles in front of you, notwithstanding that every matching .mp4 file it encounters runs in a new cmd.exe instance, which is very inefficient for anything other than a small number of matches.

In my opinion this type of task is better suited to a standard For/For /F loop, (forfiles.exe is better suited to those rare times that its @RelPath variable is needed). You already have an answer which uses one of those, so I will not post my version here.

Upvotes: 2

Magoo
Magoo

Reputation: 80193

I set up a directory with some dummy files matching the problem name format and simply echoed the resultant ffmpeg line. The result was of the form

set filename="1 der 1" _and_ ffmpeg -i "1 der 1.mp4" -vf 
subtitles=subtitledir\"1 der 1".srt -f mp4 donesubsdir\"1 der 1"_srt.mp4

(OK - I've broken the line for ease of viewing and substituted subtitledir and donesubsdir for the directorynames.)

Observing that the quoted filename appears within a string and theorising that ffmpeg may have difficulty with that structure, I suggest

SET "vpath=U:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess"
FOR /f "delims=" %%e IN ('dir /b /a-d "%vpath%\*.mp4" 2^>nul') DO ECHO ffmpeg -i "%vpath%\%%~ne" -vf subtitles="subtitledir\%%~ne.srt" -f mp4 "donesubsdir\%%~ne_srt.mp4"

Where you need to follow the bouncing ball for subtitledir and donesubsdir.

My results were:

ffmpeg -i "U:\Users\titos\Resilio_Sync\4K_Video_Downloader\videoprocess\1 der 1" -vf 
subtitles="subtitledir\1 der 1.srt" -f mp4 "donesubsdir\1 der 1_srt.mp4"

(OK - I've broken the line for ease of viewing and substituted subtitledir and donesubsdir for the directorynames.)

Theory:

The command dir /b /a-d "%vpath%\*.mp4" 2^>nul is executed and produces lines of the filename only (/b) with no directorynames (/a-d) which are applied to %%e."delims=" tells for that no tokenising is required, so the entire name appears in %%e, The 2^>nul suppresses error messages - the ^ tells cmd that the > is part of the dir command, not the for.

(there are hundreds, if not thousands, of similar applications posted on SO)

See for /? or thousands of examples on SO for the use of metavariable-modifiers.

Then %%~ne is the name-part only of the filename in %%e.

Upvotes: 1

Related Questions