2025, Oct 18 21:17
Почему ffmpeg ломается в Python и как правильно передавать аргументы через subprocess
Как без ошибок вызывать ffmpeg из Python: передавайте аргументы в subprocess как отдельные токены, избегайте пробелов в одном элементе, используйте shlex.
При вызове ffmpeg из Python легко наткнуться на непонятные ошибки, даже если та же самая командная строка безупречно работает в Terminal. Распространённая ловушка — способ передачи аргументов в subprocess. Shell и Python по‑разному обращаются с пробелами, и этого тонкого несоответствия достаточно, чтобы получить ошибки вроде “Unrecognized option” или “Invalid stream specifier”. Ниже — короткое объяснение, что именно идёт не так и как это исправить, не меняя сами параметры ffmpeg, которые вы хотите использовать.
Контекст и рабочая команда в shell
Задача проста: конвертировать файл FLAC в MP3 320k, сохранив метаданные и установив ID3v2 версии 3. В macOS Sequoia 15.5 с Python 3 и ffmpeg 4.2.1 следующая команда в Terminal выполняется как ожидается:
/usr/local/bin/ffmpeg -i "/Volumes/MainData/Media/Media Server/_Stage/03 - People Like Us - Aaron Tippin.flac" -b:a 320k -map_metadata 0 -id3v2_version 3 "/Volumes/MainData/Media/Media Server/_Stage/03 - People Like Us - Aaron Tippin.mp3"Неработающий пример на Python
Перенос этого в вызов subprocess, когда опции вместе со значениями запихиваются в отдельные элементы списка, на первый взгляд выглядит логично, но приводит к сбоям:
import subprocess
argv = [
    '/usr/local/bin/ffmpeg',
    '-i',
    '/Volumes/MainData/Media/Media Server/_Stage/03 - People Like Us - Aaron Tippin.flac',
    '-b:a 320k',
    '-map_metadata 0',
    '-id3v2_version 3',
    '/Volumes/MainData/Media/Media Server/_Stage/03 - People Like Us - Aaron Tippin.mp3',
]
exit_status = subprocess.call(argv)В результате появляются ошибки вроде “Unrecognized option 'id3v2_version 3'”, а после удаления этого параметра — “Invalid stream specifier: a 320k.”
В чём на самом деле проблема
Shell разбивает командную строку на токены по пробелам, если только вы не заключили части в кавычки, чтобы они держались вместе. subprocess в Python, когда получает список, не делает никакого разбиения внутри элементов. Каждый элемент передаётся как один аргумент без изменений. Поэтому элемент списка “-id3v2_version 3” ffmpeg видит как имя одной опции с пробелом — и не распознаёт его. То же относится к “-b:a 320k”: ffmpeg ожидает “-b:a” и “320k” как два отдельных аргумента, а не один элемент, содержащий оба.
Решение: передавайте каждый токен отдельным элементом списка
Оставляйте каждый флаг и каждое значение отдельными элементами. Не помещайте пробелы внутри одного элемента списка argv. Исправленный вызов выглядит так:
import subprocess
cmd_parts = [
    '/usr/local/bin/ffmpeg',
    '-i',
    '/Volumes/MainData/Media/Media Server/_Stage/03 - People Like Us - Aaron Tippin.flac',
    '-b:a',
    '320k',
    '-map_metadata',
    '0',
    '-id3v2_version',
    '3',
    '/Volumes/MainData/Media/Media Server/_Stage/03 - People Like Us - Aaron Tippin.mp3',
]
status = subprocess.call(cmd_parts)Так программа, опции и их значения остаются теми же, что и в рабочей командной строке, но записаны в том виде, которого действительно ждёт subprocess.
Если вы начинаете с одной командной строки и нужно безопасно её разбить, используйте shlex — он токенизирует её так же, как это сделал бы shell.
Почему эта деталь важна
Смешение правил кавычек shell с обработкой аргументов в Python порождает хрупкие скрипты и вводящие в заблуждение сообщения. Сообщения об ошибках ffmpeg здесь точны, но косвенны: истинная проблема не в поведении ffmpeg, а в том, как приходят аргументы. Если гарантировать, что каждая опция и её значение — отдельные элементы, вы получите предсказуемое поведение в разных окружениях и избавитесь от тонких багов, проявляющихся только при путях с пробелами или когда опции требуется значение.
Вывод
Вызывая консольные утилиты вроде ffmpeg из Python, относитесь к списку argv как к последовательности уже разбитых на токены аргументов. Держите флаги и значения раздельно, не помещайте пробелы внутрь одного элемента списка, а если нужно разбить строку команды — используйте инструмент, предназначенный для этого, например shlex. Эта небольшая дисциплина экономит время, предотвращает странные ошибки и делает скрипты автоматизации устойчивее.