0

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']
4
  • The difference here is between shell=True and the formatting used when you don't have that keyword argument. subprocess.run is basically a replacement for subprocess.call but it doesn't differ in this particular aspect. (They differ in what they return; call simply returns the status code, while run returns a CompletedProcess object which includes the status code as an attribute, but also various other pieces of information about the process you ran.) Commented Dec 27, 2021 at 10:51
  • Your translation seems competent (except '{0}'.format(s) is a clumsy way to write str(s) or simply s if the variable is already a string); I don't think this error is reproducible. Commented Dec 27, 2021 at 10:51
  • Please edit your post, and add the value of s. Please make sure that your code sample is executable, and that the error message is reproducible. Commented Dec 27, 2021 at 16:36
  • 1
    Thanks for (finally) providing a minimal reproducible example. I think you can see why we require it now. Commented Dec 28, 2021 at 6:52

1 Answer 1

1

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).)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.