Статья Работа с видеофайлами с помощью Python

Для работы с видеофайлами создано достаточно программ. Сложнее - проще, платные - бесплатные. На любой вкус и цвет. Но что, если вам не нужно редактировать само видео, а надо по быстрому объединить два фрагмента или наоборот извлечь. А может быть, вы хотите создать по-быстрому видео из картинок или извлечь аудиодорожку. В общем, вариантов работы с видеофайлами, помимо редактирования видео может быть много. И в какой-то части из них нам может помочь Python. Тем более что написанная вами программа будет бесплатна, без водяных знаков и прочего лицензионного шума.

000.jpg


Что потребуется?

Из сторонних библиотек нужно будет установить библиотеку moviepy. С ее помощью мы и будем работать с видеофайлами. Пишем в терминале:

pip install moviepy

Также, необязательная, но просто для красоты, библиотека colorama. С ее помощью мы немного раскрасим вывод в терминале, чтобы строки не сливались друг с другом, и было понятно, что происходит в данный момент.

pip install colorama

После установки нужных библиотек импортируем их в скрипт, а также импортируем стандартные библиотеки, которые понадобятся в процессе создания скрипта.

Python:
import os.path
import time
from numpy import arange

from colorama import Fore
from colorama import init
from moviepy.editor import *

Для инициализации colorama сделаем вызов функции init().

init()

А теперь можно приступать к созданию основного кода, где и будут происходить действия с видеофайлами.


Объединение видеоклипов в один

Создадим функцию merge_video_clip(path_directory, res_name). На вход она принимает путь к папке с видео, а также название результирующего видео, в которое будут объединены все видеофайлы, которые будут там найдены.

Python:
def merge_video_clip(path_directory, res_name):
    if os.path.exists(path_directory):
        print(Fore.CYAN + '[+] Сканирование директории')
        clip_in_dir = os.listdir(path_directory)
        clip_to_merge = []
        for clip in clip_in_dir:
            if clip.endswith(".mp4") or clip.endswith(".avi"):
                VideoFileClip(os.path.join(path_directory, clip))
                clip_to_merge.append(VideoFileClip(os.path.join(path_directory, clip)))

        if len(clip_to_merge) <= 1:
            print(Fore.RED + '[-] В указанной директории нечего объединять')
            main()
            return
        else:
            print(Fore.YELLOW + f'[+] Найдено фалов: {len(clip_to_merge)}')
            merge_final = concatenate_videoclips(clip_to_merge)
            print(Fore.YELLOW + f'[+] Длительность объединяемого видео: '
                  f'{time.strftime("%H:%M:%S", time.gmtime(merge_final.duration))}\n[+] Начинаю объединение файлов...\n')
            merge_final.write_videofile(os.path.join(os.getcwd(), f'{res_name}.mp4'))
            print(Fore.GREEN + '\n[+] Объединение файлов завершено')
            print(Fore.GREEN + f'[+] Видео сохранено в папку: "{os.path.join(os.getcwd(), f"{res_name}.mp4")}"')
            main()
            return
    else:
        print(Fore.RED + '[-] Указанного пути не существует')
        main()
        return

Для начала в данной функции делается проверка на существование папки с видео. Если нет, выходим из функции в основное меню. Если же все в порядке, двигаемся дальше. Создаем список файлов в папке с помощью os.listdir. Затем в цикле пробегаемся по списку файлов и выбираем оттуда только те, что с расширением «.mp4» или «.avi». Затем создаем объекты видео из каждого найденного файла и добавляем в список для последующей обработки.

Python:
    if os.path.exists(path_directory):
        print(Fore.CYAN + '[+] Сканирование директории')
        clip_in_dir = os.listdir(path_directory)
        clip_to_merge = []
        for clip in clip_in_dir:
            if clip.endswith(".mp4") or clip.endswith(".avi"):
                VideoFileClip(os.path.join(path_directory, clip))
                clip_to_merge.append(VideoFileClip(os.path.join(path_directory, clip)))

Затем проверяем длину созданного списка, ведь если в папке не окажется видео, или оно будет только одно, то и объединять, собственно, нечего. Если все же файлы видео в папке присутствуют, сообщаем пользователю их количество. Далее с помощью функции concatenate_videoclips объединяем их в одно. После чего сообщаем пользователю длительность получившегося видео и запускаем сохранение видео в видеофайл. После чего сообщаем пользователю, что все выполнено и возвращаемся в основное меню.

Python:
if len(clip_to_merge) <= 1:
            print(Fore.RED + '[-] В указанной директории нечего объединять')
            main()
            return
        else:
            print(Fore.YELLOW + f'[+] Найдено фалов: {len(clip_to_merge)}')
            merge_final = concatenate_videoclips(clip_to_merge)
            print(Fore.YELLOW + f'[+] Длительность объединяемого видео: '
                  f'{time.strftime("%H:%M:%S", time.gmtime(merge_final.duration))}\n[+] Начинаю объединение файлов...\n')
            merge_final.write_videofile(os.path.join(os.getcwd(), f'{res_name}.mp4'))
            print(Fore.GREEN + '\n[+] Объединение файлов завершено')
            print(Fore.GREEN + f'[+] Видео сохранено в папку: "{os.path.join(os.getcwd(), f"{res_name}.mp4")}"')
            main()
            return

Здесь хотелось бы сказать, что будьте внимательны при объединении видео, потому, что если они будут разного разрешения, у вас в результате будет отображаться только то, которое было добавлено в список первым. Остальные видео, возможно будут примерно вот в таком виде:

screenshot2.png


Извлечение фрагмента из видео и сохранение в файл

Создадим функцию clip_video(path_file, start_time, end_time). На входе она принимает путь к видео, из которого нужно извлечь фрагмент, время начала фрагмента и время окончания.

Python:
def clip_video(path_file, start_time, end_time):
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            dir_vid = os.path.split(path_file)[0]
            suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
            vid_clip_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_clip{suff}'

            try:
                start = int(start_time.split(":")[0]) * 60 + int(start_time.split(":")[1])
            except IndexError:
                start = int(start_time)
            except ValueError:
                print(Fore.RED + '[-] Вы ввели не число')
                main()
                return
            try:
                end = int(end_time.split(":")[0]) * 60 + int(end_time.split(":")[1])
            except IndexError:
                end = int(end_time)
            except ValueError:
                print(Fore.RED + '[-] Вы ввели не число')
                main()
                return

            print(Fore.CYAN + '[+] Начинаю вырезку фрагмента видео')
            print(Fore.YELLOW + f'   - Длительность фрагмента: {end - start} секунд')

            clip = VideoFileClip(path_file)
            print(Fore.YELLOW + f'   - Общая продолжительность видео: {clip.duration} секунд\n')

            clip_clip = clip.subclip(start, end)
            print(Fore.CYAN + '[+] Записываю фрагмент видео...\n')
            clip_clip.write_videofile(os.path.join(dir_vid, f'{vid_clip_name}'))
            clip.reader.close()
            clip.audio.reader.close_proc()
            print(Fore.GREEN + f'\n[+] Видео сохранено в папку: "{os.path.join(dir_vid, f"{vid_clip_name}")}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Файл не поддерживаемого формата')
            main()
            return
    else:
        print(Fore.RED + '[-] Не указан файл')
        main()
        return

Все начинается с проверки существования файла. Если он существует, проверяем его разрешение. И уже если здесь все в порядке сохраняем в переменную путь к видео, затем в переменную suff сохраняем расширение файла, и в переменной vid_clip_name собираем имя файла с суффиксом «_clip», чтобы случайно не перезаписать исходное видео.

Python:
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            dir_vid = os.path.split(path_file)[0]
            suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
            vid_clip_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_clip{suff}'

Затем переводим время начала в секунды, а если вы ввели, к примеру, время не в том формате, в котором нужно, а 1, что будет соответствовать 1 секунде, присваиваем переменной это значение без перевода. Также поступаем и с временем окончания фрагмента. Если же вы ввели не число, выходим из функции.

Python:
            try:
                start = int(start_time.split(":")[0]) * 60 + int(start_time.split(":")[1])
            except IndexError:
                start = int(start_time)
            except ValueError:
                print(Fore.RED + '[-] Вы ввели не число')
                main()
                return
            try:
                end = int(end_time.split(":")[0]) * 60 + int(end_time.split(":")[1])
            except IndexError:
                end = int(end_time)
            except ValueError:
                print(Fore.RED + '[-] Вы ввели не число')
                main()
                return

Затем сообщаем пользователю о начале извлечения фрагмента, а также длительность фрагмента в секундах, общую длительность видео в секундах и передаем в функцию clip.subclip время начала и окончания фрагмента. После чего сохраняем фрагмент в видеофайл. А затем закрываем открытый в самом начале видео файл и аудиопоток. Иначе, если этого не сделать, функция сделает свое дело, но в конце вызовет исключение.

Python:
            print(Fore.CYAN + '[+] Начинаю вырезку фрагмента видео')
            print(Fore.YELLOW + f'   - Длительность фрагмента: {end - start} секунд')

            clip = VideoFileClip(path_file)
            print(Fore.YELLOW + f'   - Общая продолжительность видео: {clip.duration} секунд\n')

            clip_clip = clip.subclip(start, end)
            print(Fore.CYAN + '[+] Записываю фрагмент видео...\n')
            clip_clip.write_videofile(os.path.join(dir_vid, f'{vid_clip_name}'))
            clip.reader.close()
            clip.audio.reader.close_proc()
            print(Fore.GREEN + f'\n[+] Видео сохранено в папку: "{os.path.join(dir_vid, f"{vid_clip_name}")}"')
            main()
            return


Удаление аудиодорожки из видеофайла

Создадим функцию remove_audio_from_video(path_file). На входе она принимает только путь к видеофайлу.

Python:
def remove_audio_from_video(path_file):
    suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_witout_audio{suff}'
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            print(Fore.CYAN + '[+] Удаление аудио')
            video = VideoFileClip(path_file)
            video.without_audio().write_videofile(os.path.join(os.path.split(path_file)[0], vid_name))
            print(Fore.GREEN + f'[+] Удаление аудио завершено. \n   - Файл сохранен: '
                  f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Формат файла не поддерживается')
            main()
            return
    else:
        print(Fore.RED + '[-] Файл не обнаружен')
        main()
        return

Затем производим манипуляции с расширением файла, чтобы сформировать название результирующего видео, после чего проверяем, существует ли переданный файл. Если он существует, проверяем расширение, и если все в порядке создаем объект видео, в который передаем путь. Затем с помощью функции without_audio() сохраняем видео в новый файл со сформированным ранее названием.

Python:
    suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_witout_audio{suff}'
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            print(Fore.CYAN + '[+] Удаление аудио')
            video = VideoFileClip(path_file)
            video.without_audio().write_videofile(os.path.join(os.path.split(path_file)[0], vid_name))
            print(Fore.GREEN + f'[+] Удаление аудио завершено. \n   - Файл сохранен: '
                  f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return


Извлечение аудиодорожки из файла и сохранение в mp3

Создадим функцию extract_mp3(path_file), которая на входе принимает путь к видеофайлу или путь к папке с видео, чтобы можно было извлечь аудио из всех файлов, которые в ней находятся.

Python:
def extract_mp3(path_file):
    file_in_dir = []
    if os.path.isdir(path_file):
        print(Fore.CYAN + '[+] Сканирование директории')
        file_in_dir = os.listdir(path_file)
    elif os.path.isfile(path_file):
        file_in_dir = os.listdir(os.getcwd())

    video_to_extract = []
    mp3_list = []
    
    for file in file_in_dir:
        print(Fore.CYAN + f'\r[+] Добавляю файлы для извлечения: "{file}"', end='')
        if file.endswith(".mp4") or file.endswith(".avi"):
            mp3_suff = f'.{file.replace(".", "_").split("_")[-1]}'
            mp3_list.append(f'{file.removesuffix(mp3_suff)}.mp3')
            video_to_extract.append(VideoFileClip(os.path.join(os.getcwd(), file)))

    if len(video_to_extract) > 0:
        print(Fore.CYAN + '\n[+] Запуск извлечения аудио\n')
        for num, video in enumerate(video_to_extract):
            if os.path.exists(os.path.join(os.getcwd(), mp3_list[num])):
                mp3_name = f'{mp3_list[num].removesuffix(".mp3")}_{num + 1}.mp3'
                video.audio.write_audiofile(os.path.join(os.getcwd(), mp3_name))
                print(Fore.YELLOW + f'[+] Аудио из файла: "{mp3_name}" извлечено\n')
            else:
                video.audio.write_audiofile(os.path.join(os.getcwd(), mp3_list[num]))
                print(Fore.YELLOW + f'[+] Аудио из файла: "{mp3_list[num]}" извлечено\n')
        print(Fore.GREEN + '[+] Все видео файлы в директории обработаны. Аудио извлечено')
    else:
        print(Fore.RED + '\n[-] Файлов в директории не обнаружено')
        return

Создаем список для файлов видео в папке. Проверяем, является ли переданный путь папкой. Если это папка, сканируем ее без изменения. Если это файл, передаем для сканирования текущую папку.

Python:
    file_in_dir = []
    if os.path.isdir(path_file):
        print(Fore.CYAN + '[+] Сканирование директории')
        file_in_dir = os.listdir(path_file)
    elif os.path.isfile(path_file):
        file_in_dir = os.listdir(os.getcwd())

Создаем список для объектов видео и список для названий аудиофайлов, чтобы они соответствовали названиям видео. В цикле пробегаемся по списку файлов в папке и добавляем в созданные списки объекты с видеофайлами и названия создаваемых аудиофайлов.

Python:
    video_to_extract = []
    mp3_list = []

    for file in file_in_dir:
        print(Fore.CYAN + f'\r[+] Добавляю файлы для извлечения: "{file}"', end='')
        if file.endswith(".mp4") or file.endswith(".avi"):
            mp3_suff = f'.{file.replace(".", "_").split("_")[-1]}'
            mp3_list.append(f'{file.removesuffix(mp3_suff)}.mp3')
            video_to_extract.append(VideoFileClip(os.path.join(os.getcwd(), file)))

Проверяем, не является ли список файлов пустым. Если нет, запускаем цикл по списку объектов видеофайлов. Проверяем, существует ли аудиофайл в папке. Если существует, формируем название файла из названия файла из списка, к которому добавляем номер. И сохраняем в файл «.mp3». Если файла не существует, сохраняем с названием из списка.

Python:
    if len(video_to_extract) > 0:
        print(Fore.CYAN + '\n[+] Запуск извлечения аудио\n')
        for num, video in enumerate(video_to_extract):
            if os.path.exists(os.path.join(os.getcwd(), mp3_list[num])):
                mp3_name = f'{mp3_list[num].removesuffix(".mp3")}_{num + 1}.mp3'
                video.audio.write_audiofile(os.path.join(os.getcwd(), mp3_name))
                print(Fore.YELLOW + f'[+] Аудио из файла: "{mp3_name}" извлечено\n')
            else:
                video.audio.write_audiofile(os.path.join(os.getcwd(), mp3_list[num]))
                print(Fore.YELLOW + f'[+] Аудио из файла: "{mp3_list[num]}" извлечено\n')


Увеличение или уменьшение громкости звука в видео

Следующая функция, которую мы создадим, будет функцией для увеличения или уменьшения громкости звука в видеофайле: zoom_in_out(path_file, value).

Python:
def zoom_in_out(path_file, value):
    try:
        float(value)
    except ValueError:
        print(Fore.RED + '[-] Введено неверное значение коэффициента')
        main()
        return

    suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_zoom{suff}'
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            print(Fore.CYAN + f'[+] Изменение громкости звука видео на: {value}')
            video = VideoFileClip(path_file)
            video.volumex(float(value)).write_videofile(os.path.join(os.path.split(path_file)[0], vid_name))
            print(Fore.GREEN + f'[+] Громкость изменена. Видео сохранено в папку: '
                  f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Файл не поддерживаемого формата')
            main()
            return
    else:
        print(Fore.RED + '[-] Указанного файла не существует')
        main()
        return

На входе функция принимает название файла и коэффициент на который будет произведено уменьшение или увеличение звука. То есть, стартовый коэффициент равен 1. И соответственно, звук можно увеличивать, передавая значение, например 2, то есть, звук будет увеличен в два раза. Или 0.1, тогда будет произведено уменьшение громкости звука до значения коэффициента.

Для начала проверяем, что передал пользователь. Является ли это числом. Если это число переводим во float. Если это буква, вызывается исключение, в котором мы сообщаем пользователю, что он ввел неверные данные, и выходим в основное меню.

Python:
    try:
        float(value)
    except ValueError:
        print(Fore.RED + '[-] Введено неверное значение коэффициента')
        main()
        return

Затем производим манипуляции с расширением файла, чтобы сформировать новое название для видео с увеличенным или уменьшенным звуком. Проверяем, является ли переданный путь файлом, затем проверяем расширение файла. Если все в порядке, создаем объект видео, в который передаем путь к файлу. С помощью функции volumex устанавливаем новое значение громкости, и сохраняем видео в файл, после чего переходим в основное меню.

Python:
    suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_zoom{suff}'
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            print(Fore.CYAN + f'[+] Изменение громкости звука видео на: {value}')
            video = VideoFileClip(path_file)
            video.volumex(float(value)).write_videofile(os.path.join(os.path.split(path_file)[0], vid_name))
            print(Fore.GREEN + f'[+] Громкость изменена. Видео сохранено в папку: '
                  f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')


Объединение аудио и видеофайла

Создадим функцию merge_video_audio(path_file_v, path_file_a). Она принимает на входе путь к видеофайлу и путь к аудиофайлу.

Python:
def merge_video_audio(path_file_v, path_file_a):
    if not os.path.exists(path_file_v):
        print(Fore.RED + '[-] С видеофайлом непорядок')
        main()
        return

    if not os.path.exists(path_file_a):
        print(Fore.RED + '[-] С аудиофайлом непорядок')
        main()
        return

    suff = f'.{os.path.split(path_file_v)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file_v)[-1].removesuffix(suff)}_aud{suff}'
    if path_file_v.endswith(".mp4") or path_file_v.endswith(".avi") and path_file_a.endswith(".mp3"):
        print(Fore.CYAN + '[+] Добавляю аудио к видео')
        videoclip = VideoFileClip(path_file_v)
        audioclip = AudioFileClip(path_file_a)

        videoclip.audio = audioclip
        videoclip.write_videofile(os.path.join(os.path.split(path_file_v)[0], vid_name))
        print(Fore.GREEN + f'[+] Аудио добавлено. Файл сохранен: "{os.path.join(os.path.split(path_file_v)[0], vid_name)}"')
        main()
        return
    else:
        print(Fore.RED + '[-] Неверный формат файлов')
        main()
        return

По очереди проверяем, существуют ли переданные файлы.

Python:
    if not os.path.exists(path_file_v):
        print(Fore.RED + '[-] С видеофайлом непорядок')
        main()
        return

    if not os.path.exists(path_file_a):
        print(Fore.RED + '[-] С аудиофайлом непорядок')
        main()
        return

Затем формируем название выходного видео, проверяем, является ли расширение переданных аудио и видео верным, после чего, если все в порядке, создаем объект видео и объект аудио, в которые передаем путь к соответствующим файлам. Добавляем аудио к видео и сохраняем в файл, после чего возвращаемся в основное меню.

Python:
    suff = f'.{os.path.split(path_file_v)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file_v)[-1].removesuffix(suff)}_aud{suff}'
    if path_file_v.endswith(".mp4") or path_file_v.endswith(".avi") and path_file_a.endswith(".mp3"):
        print(Fore.CYAN + '[+] Добавляю аудио к видео')
        videoclip = VideoFileClip(path_file_v)
        audioclip = AudioFileClip(path_file_a)

        videoclip.audio = audioclip
        videoclip.write_videofile(os.path.join(os.path.split(path_file_v)[0], vid_name))
        print(Fore.GREEN + f'[+] Аудио добавлено. Файл сохранен: "{os.path.join(os.path.split(path_file_v)[0], vid_name)}"')
        main()
        return


Извлечение кадров из видеофайла

Создадим функцию extract_image_from_video(path_file). На входе она принимает путь к файлу видео, из которого нужно будет извлечь кадры.

Python:
def extract_image_from_video(path_file):
    if os.path.exists(path_file):
        suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
        vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}'
        if path_file.endswith(".mp4") or path_file.endswith(".avi"):
            print(Fore.CYAN + '[+] Сохранение кадров из видео')
            if not os.path.isdir(os.path.join(os.path.split(path_file)[0], vid_name)):
                os.mkdir(os.path.join(os.path.split(path_file)[0], vid_name))

            video = VideoFileClip(path_file)
            s_fps = (1 / video.fps) * 10
            for clip in arange(0, video.duration, s_fps):
                print(Fore.YELLOW + f'\r[+] Сохраняю: {clip}/{video.duration}', end='')
                video.save_frame(os.path.join(os.path.split(path_file)[0], vid_name, f'{vid_name}_{clip}.jpg'), clip)
            print(Fore.GREEN + f'\n[+] Кадры из видео сохранены в папку: "{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Неверное расширение файла')
            main()
            return
    else:
        print(Fore.RED + '[-] Указанного видео не существует')
        main()
        return

Проверяем, существует ли файл, формируем общее название для файлов изображений. Затем проверяем, является ли расширение файла видео и, если все в порядке, создаем папку для сохранения картинок.

Python:
    if os.path.exists(path_file):
        suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
        vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}'
        if path_file.endswith(".mp4") or path_file.endswith(".avi"):
            print(Fore.CYAN + '[+] Сохранение кадров из видео')
            if not os.path.isdir(os.path.join(os.path.split(path_file)[0], vid_name)):
                os.mkdir(os.path.join(os.path.split(path_file)[0], vid_name))

Создаем объект видео, в который передаем путь к файлу. Устанавливаем значение переменной, в которую сохраняем шаг диапазона, через который мы будем извлекать картинки. Я сделал его равным единице деленной на fps видео, которое равно 30 или 25 кадров, ну или другое значение. И умножил на 10, чтобы картинок не было слишком много. Если вам нужно сделать шаг извлечения картинок меньше, уменьшите значение 10 до нужного вам.

Затем создаем цикл в диапазоне от 0 до длительности видеофайла, с нужным шагом, который сформировали ранее. И сохраняем фреймы по указанному шагу в файлы изображений. После чего возвращаемся в основное меню.

Python:
            video = VideoFileClip(path_file)
            s_fps = (1 / video.fps) * 10
            for clip in arange(0, video.duration, s_fps):
                print(Fore.YELLOW + f'\r[+] Сохраняю: {clip}/{video.duration}', end='')
                video.save_frame(os.path.join(os.path.split(path_file)[0], vid_name, f'{vid_name}_{clip}.jpg'), clip)
            print(Fore.GREEN + f'\n[+] Кадры из видео сохранены в папку: '
                               f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return


Создаем видео из изображений

Создадим функцию clip_from_image(path_dir, name_clip, s_duration). Здесь на функции передается путь к папке с изображениями, имя создаваемого клипа, и длительность каждого кадра в секундах или долях секунд.

Python:
def clip_from_image(path_dir, name_clip, s_duration):
    try:
        dur = float(s_duration)
    except ValueError:
        print(Fore.RED + '[-] Неверное значение длительности кадра')
        main()
        return

    if os.path.exists(path_dir):
        print(Fore.CYAN + '[+] Создание видео из картинок')
        os.chdir(path_dir)
        images = list(filter(lambda img: img.endswith(".jpg"), os.listdir(path_dir)))
        clips = [ImageClip(im).set_duration(dur) for im in images]
        video_merge = concatenate_videoclips(clips, method='compose')
        video_merge.write_videofile(f'{name_clip}.mp4', fps=25)
        print(Fore.GREEN + f'[+] Видео создано и сохранено в папку: {path_dir}')
        main()
        return
    else:
        print(Fore.RED + '[-] Указанной директории не существует')
        main()
        return

Сначала переводим полученное значение длительности кадра во float. Таким образом, выполняем проверку, является ли переданное значение числом. Если нет, возвращаемся в основную функцию.

Python:
    try:
        dur = float(s_duration)
    except ValueError:
        print(Fore.RED + '[-] Неверное значение длительности кадра')
        main()
        return

Затем проверяем, существует ли папка. Если да, переходим в нее. Фильтруем файлы в папке по расширениям и создаем из нужных файлов список. Обратите внимание на то, что расширения у файлов должны быть одинаковые. Затем создаем список с объектами картинок, в которые передаем путь к каждой из картинок в папке. С помощью функции concatenate_videoclips создаем видео, после чего сохраняем его в файл, с указанием fps.

Python:
    if os.path.exists(path_dir):
        print(Fore.CYAN + '[+] Создание видео из картинок')
        os.chdir(path_dir)
        images = list(filter(lambda img: img.endswith(".jpg"), os.listdir(path_dir)))
        clips = [ImageClip(im).set_duration(dur) for im in images]
        video_merge = concatenate_videoclips(clips, method='compose')
        video_merge.write_videofile(f'{name_clip}.mp4', fps=25)

После того, как видеофайл создан, возвращаемся в основное меню.


Меню выбора действий над видео

И функция main(). Думаю, что подробно ее описывать не имеет смысла, так как она лишь выводит на экран список с действиями доступными для пользователя, после чего производиться проверка пользовательского выбора и на основании этого запускается та или иная функция с запросом ввода нужных параметров.

Python:
def main():
    user_change = input(Fore.RESET + '\n[+] Выберите действие:\n   [1] Объединить видео\n   [2] Вырезать фрагмент\n   '
                        '[3] Удалить аудиодорожку из видео\n   [4] Извлечь аудио\n   [5] Изменить громкость видео\n'
                        '   [6] Добавить аудио к видео\n   [7] Извлечь кадры из видео\n   '
                        '[8] Создать клип из картинок\n   [9] Выход\n   >>> ')
    if user_change == "1":
        merge_video_clip(input('\n[+] Введите путь к папке с файлами: '),
                         input('[+] Введите название для объединенного видео: '))
    elif user_change == "2":
        clip_video(input('\n[+] Введите путь к файлу видео: '), input('[+] Введите время начала фрагмента\n'
                                                                      '   - пример: 02:25\n   >>> '),
                   input('[+] Введите время окончания фрагмента\n'
                         '   - пример: 03:50\n   >>> '))
    elif user_change == "3":
        remove_audio_from_video(input('\n[+] Введите путь к файлу видео: '))
    elif user_change == "4":
        extract_mp3(input('\n[+] Введите путь к видео или, к папке с видео: '))
    elif user_change == "5":
        zoom_in_out(input('\n[+] Введите путь к файлу видео: '),
                    input('[+] Введите коэффициент для изменения громкости\n   '
                          '- Нормальная громкость: 1 (пример коэффициента: 0.5, 1.2)\n   >>> '))
    elif user_change == "6":
        merge_video_audio(input('\n[+] Введите путь к файлу видео: '), input('[+] Введите путь к файлу аудио: '))
    elif user_change == "7":
        extract_image_from_video(input('\n[+] Введите путь к файлу видео: '))
    elif user_change == "8":
        clip_from_image(input('\n[+] Введите путь к папке с картинками: '), input('[+] Введите название клипа: '),
                        input('[+] Введите продолжительность показа кадра (прим.: 0.1 или 1): '))
    elif user_change == "9":
        exit(0)
    else:
        print(Fore.RED + '\n[-] Неопознанный выбор. Повторите снова')
        main()

А на этом, пожалуй, все. Вот таким вот, несложным способом можно выполнять действия над видеофайлами. Но, библиотека moviepy умеет не только это. И если вы перейдете в ее документацию и ознакомитесь с ней подробнее, вы обнаружите, что с помощью этой библиотеки можно не только выполнять действия над видеофайлами, но и редактировать видео. Там есть даже пример создания заставки, как в начале фильма «Звездные войны».

Python:
# pip install moviepy
# pip install colorama

import os.path
import time
from numpy import arange

from colorama import Fore
from colorama import init
from moviepy.editor import *

init()


def merge_video_clip(path_directory, res_name):
    if os.path.exists(path_directory):
        print(Fore.CYAN + '[+] Сканирование директории')
        clip_in_dir = os.listdir(path_directory)
        clip_to_merge = []
        for clip in clip_in_dir:
            if clip.endswith(".mp4") or clip.endswith(".avi"):
                VideoFileClip(os.path.join(path_directory, clip))
                clip_to_merge.append(VideoFileClip(os.path.join(path_directory, clip)))

        if len(clip_to_merge) <= 1:
            print(Fore.RED + '[-] В указанной директории нечего объединять')
            main()
            return
        else:
            print(Fore.YELLOW + f'[+] Найдено фалов: {len(clip_to_merge)}')
            merge_final = concatenate_videoclips(clip_to_merge)
            print(Fore.YELLOW + f'[+] Длительность объединяемого видео: '
                  f'{time.strftime("%H:%M:%S", time.gmtime(merge_final.duration))}\n[+] Начинаю объединение файлов...\n')
            merge_final.write_videofile(os.path.join(os.getcwd(), f'{res_name}.mp4'))
            print(Fore.GREEN + '\n[+] Объединение файлов завершено')
            print(Fore.GREEN + f'[+] Видео сохранено в папку: "{os.path.join(os.getcwd(), f"{res_name}.mp4")}"')
            main()
            return
    else:
        print(Fore.RED + '[-] Указанного пути не существует')
        main()
        return


def clip_video(path_file, start_time, end_time):
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            dir_vid = os.path.split(path_file)[0]
            suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
            vid_clip_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_clip{suff}'

            try:
                start = int(start_time.split(":")[0]) * 60 + int(start_time.split(":")[1])
            except IndexError:
                start = int(start_time)
            except ValueError:
                print(Fore.RED + '[-] Вы ввели не число')
                main()
                return
            try:
                end = int(end_time.split(":")[0]) * 60 + int(end_time.split(":")[1])
            except IndexError:
                end = int(end_time)
            except ValueError:
                print(Fore.RED + '[-] Вы ввели не число')
                main()
                return

            print(Fore.CYAN + '[+] Начинаю вырезку фрагмента видео')
            print(Fore.YELLOW + f'   - Длительность фрагмента: {end - start} секунд')

            clip = VideoFileClip(path_file)
            print(Fore.YELLOW + f'   - Общая продолжительность видео: {clip.duration} секунд\n')

            clip_clip = clip.subclip(start, end)
            print(Fore.CYAN + '[+] Записываю фрагмент видео...\n')
            clip_clip.write_videofile(os.path.join(dir_vid, f'{vid_clip_name}'))
            clip.reader.close()
            clip.audio.reader.close_proc()
            print(Fore.GREEN + f'\n[+] Видео сохранено в папку: "{os.path.join(dir_vid, f"{vid_clip_name}")}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Файл не поддерживаемого формата')
            main()
            return
    else:
        print(Fore.RED + '[-] Не указан файл')
        main()
        return


def remove_audio_from_video(path_file):
    suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_witout_audio{suff}'
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            print(Fore.CYAN + '[+] Удаление аудио')
            video = VideoFileClip(path_file)
            video.without_audio().write_videofile(os.path.join(os.path.split(path_file)[0], vid_name))
            print(Fore.GREEN + f'[+] Удаление аудио завершено. \n   - Файл сохранен: '
                  f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Формат файла не поддерживается')
            main()
            return
    else:
        print(Fore.RED + '[-] Файл не обнаружен')
        main()
        return


def extract_mp3(path_file):
    file_in_dir = []
    if os.path.isdir(path_file):
        print(Fore.CYAN + '[+] Сканирование директории')
        file_in_dir = os.listdir(path_file)
    elif os.path.isfile(path_file):
        file_in_dir = os.listdir(os.getcwd())

    video_to_extract = []
    mp3_list = []

    for file in file_in_dir:
        print(Fore.CYAN + f'\r[+] Добавляю файлы для извлечения: "{file}"', end='')
        if file.endswith(".mp4") or file.endswith(".avi"):
            mp3_suff = f'.{file.replace(".", "_").split("_")[-1]}'
            mp3_list.append(f'{file.removesuffix(mp3_suff)}.mp3')
            video_to_extract.append(VideoFileClip(os.path.join(os.getcwd(), file)))

    if len(video_to_extract) > 0:
        print(Fore.CYAN + '\n[+] Запуск извлечения аудио\n')
        for num, video in enumerate(video_to_extract):
            if os.path.exists(os.path.join(os.getcwd(), mp3_list[num])):
                mp3_name = f'{mp3_list[num].removesuffix(".mp3")}_{num + 1}.mp3'
                video.audio.write_audiofile(os.path.join(os.getcwd(), mp3_name))
                print(Fore.YELLOW + f'[+] Аудио из файла: "{mp3_name}" извлечено\n')
            else:
                video.audio.write_audiofile(os.path.join(os.getcwd(), mp3_list[num]))
                print(Fore.YELLOW + f'[+] Аудио из файла: "{mp3_list[num]}" извлечено\n')
        print(Fore.GREEN + '[+] Все видео файлы в директории обработаны. Аудио извлечено')
    else:
        print(Fore.RED + '\n[-] Файлов в директории не обнаружено')
        return


def zoom_in_out(path_file, value):
    try:
        float(value)
    except ValueError:
        print(Fore.RED + '[-] Введено неверное значение коэффициента')
        main()
        return

    suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}_zoom{suff}'
    if os.path.isfile(path_file):
        if os.path.split(path_file)[-1].endswith(".mp4") or os.path.split(path_file)[-1].endswith(".avi"):
            print(Fore.CYAN + f'[+] Изменение громкости звука видео на: {value}')
            video = VideoFileClip(path_file)
            video.volumex(float(value)).write_videofile(os.path.join(os.path.split(path_file)[0], vid_name))
            print(Fore.GREEN + f'[+] Громкость изменена. Видео сохранено в папку: '
                  f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Файл не поддерживаемого формата')
            main()
            return
    else:
        print(Fore.RED + '[-] Указанного файла не существует')
        main()
        return


def merge_video_audio(path_file_v, path_file_a):
    if not os.path.exists(path_file_v):
        print(Fore.RED + '[-] С видеофайлом непорядок')
        main()
        return

    if not os.path.exists(path_file_a):
        print(Fore.RED + '[-] С аудиофайлом непорядок')
        main()
        return

    suff = f'.{os.path.split(path_file_v)[-1].split(".")[-1]}'
    vid_name = f'{os.path.split(path_file_v)[-1].removesuffix(suff)}_aud{suff}'
    if path_file_v.endswith(".mp4") or path_file_v.endswith(".avi") and path_file_a.endswith(".mp3"):
        print(Fore.CYAN + '[+] Добавляю аудио к видео')
        videoclip = VideoFileClip(path_file_v)
        audioclip = AudioFileClip(path_file_a)

        videoclip.audio = audioclip
        videoclip.write_videofile(os.path.join(os.path.split(path_file_v)[0], vid_name))
        print(Fore.GREEN + f'[+] Аудио добавлено. Файл сохранен: "{os.path.join(os.path.split(path_file_v)[0], vid_name)}"')
        main()
        return
    else:
        print(Fore.RED + '[-] Неверный формат файлов')
        main()
        return


def extract_image_from_video(path_file):
    if os.path.exists(path_file):
        suff = f'.{os.path.split(path_file)[-1].split(".")[-1]}'
        vid_name = f'{os.path.split(path_file)[-1].removesuffix(suff)}'
        if path_file.endswith(".mp4") or path_file.endswith(".avi"):
            print(Fore.CYAN + '[+] Сохранение кадров из видео')
            if not os.path.isdir(os.path.join(os.path.split(path_file)[0], vid_name)):
                os.mkdir(os.path.join(os.path.split(path_file)[0], vid_name))

            video = VideoFileClip(path_file)
            s_fps = (1 / video.fps) * 10
            for clip in arange(0, video.duration, s_fps):
                print(Fore.YELLOW + f'\r[+] Сохраняю: {clip}/{video.duration}', end='')
                video.save_frame(os.path.join(os.path.split(path_file)[0], vid_name, f'{vid_name}_{clip}.jpg'), clip)
            print(Fore.GREEN + f'\n[+] Кадры из видео сохранены в папку: '
                               f'"{os.path.join(os.path.split(path_file)[0], vid_name)}"')
            main()
            return
        else:
            print(Fore.RED + '[-] Неверное расширение файла')
            main()
            return
    else:
        print(Fore.RED + '[-] Указанного видео не существует')
        main()
        return


def clip_from_image(path_dir, name_clip, s_duration):
    try:
        dur = float(s_duration)
    except ValueError:
        print(Fore.RED + '[-] Неверное значение длительности кадра')
        main()
        return

    if os.path.exists(path_dir):
        print(Fore.CYAN + '[+] Создание видео из картинок')
        os.chdir(path_dir)
        images = list(filter(lambda img: img.endswith(".jpg"), os.listdir(path_dir)))
        clips = [ImageClip(im).set_duration(dur) for im in images]
        video_merge = concatenate_videoclips(clips, method='compose')
        video_merge.write_videofile(f'{name_clip}.mp4', fps=25)
        print(Fore.GREEN + f'[+] Видео создано и сохранено в папку: {path_dir}')
        main()
        return
    else:
        print(Fore.RED + '[-] Указанной директории не существует')
        main()
        return


def main():
    user_change = input(Fore.RESET + '\n[+] Выберите действие:\n   [1] Объединить видео\n   [2] Вырезать фрагмент\n   '
                        '[3] Удалить аудиодорожку из видео\n   [4] Извлечь аудио\n   [5] Изменить громкость видео\n'
                        '   [6] Добавить аудио к видео\n   [7] Извлечь кадры из видео\n   '
                        '[8] Создать клип из картинок\n   [9] Выход\n   >>> ')
    if user_change == "1":
        merge_video_clip(input('\n[+] Введите путь к папке с файлами: '),
                         input('[+] Введите название для объединенного видео: '))
    elif user_change == "2":
        clip_video(input('\n[+] Введите путь к файлу видео: '), input('[+] Введите время начала фрагмента\n'
                                                                      '   - пример: 02:25\n   >>> '),
                   input('[+] Введите время окончания фрагмента\n'
                         '   - пример: 03:50\n   >>> '))
    elif user_change == "3":
        remove_audio_from_video(input('\n[+] Введите путь к файлу видео: '))
    elif user_change == "4":
        extract_mp3(input('\n[+] Введите путь к видео или, к папке с видео: '))
    elif user_change == "5":
        zoom_in_out(input('\n[+] Введите путь к файлу видео: '),
                    input('[+] Введите коэффициент для изменения громкости\n   '
                          '- Нормальная громкость: 1 (пример коэффициента: 0.5, 1.2)\n   >>> '))
    elif user_change == "6":
        merge_video_audio(input('\n[+] Введите путь к файлу видео: '), input('[+] Введите путь к файлу аудио: '))
    elif user_change == "7":
        extract_image_from_video(input('\n[+] Введите путь к файлу видео: '))
    elif user_change == "8":
        clip_from_image(input('\n[+] Введите путь к папке с картинками: '), input('[+] Введите название клипа: '),
                        input('[+] Введите продолжительность показа кадра (прим.: 0.1 или 1): '))
    elif user_change == "9":
        exit(0)
    else:
        print(Fore.RED + '\n[-] Неопознанный выбор. Повторите снова')
        main()


if __name__ == "__main__":
    main()

Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!