I'm trying to automate concatenating a folder of (correctly formatted) mp4 video files.
(This edited version of my question reduces the problem to its lowest level of my confusion. The original title asked about the differences between subprocess.call
and subprocess.run
but it turns out the problem was elsewhere.)
Why does
subprocess.call('ffmpeg -hide_banner -loglevel error -i movie1.mp4 -i movie2.mp4 -i credits.mp4 \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0][2:v:0][2:a:0]concat=n=3:v=1:a=1[outv][outa]" \
-map "[outv]" -map "[outa]" "output.mp4"',shell=True)
work fine (where s
is a string of inputs, and count
is the number of inputs), but
#python3
#makeFinalExample.py
import subprocess
s = '-i movie1.mp4 -i movie2.mp4 -i credits.mp4'
count = 3
print(f's prints out as: {s}')
commandList = ['ffmpeg',
'-hide_banner',
'-loglevel',
'error',
#str(s),
'{0}'.format(s),
'-filter_complex',
"[0:v:0][0:a:0][1:v:0][1:a:0]concat=n={0}:v=1:a=1[outv][outa]".format(count),
'-map',
"[outv]",
'-map',
"[outa]",
"output.mp4"]
print(f'the command list prints out as {commandList}')
subprocess.run(commandList)
gets the error (whether the string is delivered as str(s)
, or in the formatting shown...
Unrecognized option 'i movie1.mp4 -i movie2.mp4 -i credits.mp4'.
Error splitting the argument list: Option not found
Here is a printout of the input string
-i movie1.mp4 -i movie2.mp4 -i credits.mp4
And here is a printout of the commandList
['ffmpeg', '-hide_banner', '-loglevel', 'error', '-i movie1.mp4 -i movie2.mp4 -i credits.mp4', '-filter_complex', '[0:v:0][0:a:0][1:v:0][1:a:0]concat=n=3:v=1:a=1[outv][outa]', '-map', '[outv]', '-map', '[outa]', 'output.mp4']
This has nothing with subprocess.call
vs subprocess.run
to do, the difference is that you are using shell=True
in the first case, and in the second case not. The two functions behave exactly the same in this, and almost every other regard (subprocess.run
is a newer function which supports many, many more options and returns a more useful object, but in its most basic form, it performs exactly the same job, using the same API).
The problem is that you need to split the string s
into tokens just like you are splitting all the other command-line arguments (and the error message actually reveals this, but I suppose you have to know what to look for in order to catch it). When you omit shell=True
each option needs to be a separate list item, like
[ ..., '-i', 'movie1.mp4', '-i', 'movie2.mp4', '-i', 'credits.mp4', ...]
The function shlex.split()
can help you correctly split a command into tokens:
commandList = [
'ffmpeg',
'-hide_banner',
'-loglevel', 'error',
*shlex.split(s),
'-filter_complex',
"[0:v:0][0:a:0][1:v:0][1:a:0]concat=n={0}:v=1:a=1[outv][outa]".format(count),
'-map', "[outv]",
'-map', "[outa]",
"output.mp4"]
but a much better design for your function is probably to allow the user to pass in just a list of input video file names, and take it from there. (Then I guess count
will not need to be specified explicitly either; it's just the value of len(inputfilenames)
.)