Сценарии использования и решения

Автоматическое создание видео товаров: фото, ИИ-озвучка и субтитры в ролик 9:16 с помощью ffmpeg

6 шагов 15 мин чтения 64 просмотров 0
Содержание

Короткие видео о товарах хорошо продают в TikTok Shop, Shopee Video, Instagram Reels и YouTube Shorts, но монтировать вручную по ролику на каждый товар, когда товаров целый каталог, нереально. В этой статье небольшой скрипт на Python и ffmpeg на вашем сервере превращает папку с фотографиями товара и коротким сценарием озвучки в готовое вертикальное видео 9:16 с ИИ-озвучкой, вшитыми субтитрами и фоновой музыкой (по желанию), а затем за один пакетный запуск создаёт видео для сотен товаров.

Используйте только фотографии, музыку и шрифты, на которые у вас есть права. Помечайте сгенерированный ИИ голос согласно правилам каждой платформы: TikTok, YouTube и другие требуют раскрывать использование реалистичного синтетического контента.

Шаг 1: Подготовка сервера

Эта статья продолжает руководство ИИ-озвучка с Kokoro: сначала настройте /opt/tts и tts.py, как описано там. Затем установите шрифты для китайских и японских субтитров:

apt install -y ffmpeg fonts-noto-cjk
fc-list | grep -i "Noto Sans CJK" | head -3

Шаг 2: Отдельная папка для каждого товара

/data/videos/LS-01/
├── 01.jpg          # photos, shown in name order
├── 02.jpg
├── 03.jpg
├── script.txt      # narration, one subtitle line per line
└── music.mp3       # optional background music

Шаг 3: Скрипт сборки видео

cat > /opt/tts/makevideo.py <<'EOF2'
import os, subprocess, sys
import soundfile as sf

W, H, FPS = 1080, 1920, 30
FONT = os.environ.get("SUB_FONT", "Noto Sans CJK SC")
os.chdir(sys.argv[1])
images = sorted(f for f in os.listdir(".") if f.lower().endswith((".jpg", ".jpeg", ".png", ".webp")))
if not images:
    sys.exit("No photos found in " + os.getcwd())
per = sf.info("script.wav").duration / len(images)       # each photo stays on screen for an equal share

def ffmpeg(*args):
    subprocess.run(["ffmpeg", "-y", "-loglevel", "error", *args], check=True)

with open("clips.txt", "w") as lst:
    for i, img in enumerate(images):
        clip = f"clip{i:03d}.mp4"                           # photo centred on a blurred copy of itself, 9:16
        ffmpeg("-loop", "1", "-t", f"{per:.3f}", "-i", img, "-filter_complex",
               f"[0:v]split[a][b];[a]scale={W}:{H}:force_original_aspect_ratio=increase,crop={W}:{H},boxblur=20[bg];"
               f"[b]scale={W}:{H}:force_original_aspect_ratio=decrease[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2,format=yuv420p",
               "-r", str(FPS), "-c:v", "libx264", "-preset", "veryfast", "-crf", "20", clip)
        lst.write(f"file '{clip}'\n")

style = f"FontName={FONT},FontSize=11,Bold=1,Outline=2,Shadow=0,Alignment=2,MarginV=50"
inputs = ["-f", "concat", "-safe", "0", "-i", "clips.txt", "-i", "script.wav"]
if os.path.exists("music.mp3"):                             # optional background music, quietly under the voice
    inputs += ["-stream_loop", "-1", "-i", "music.mp3"]
    audio = "[2:a]volume=0.15[m];[1:a][m]amix=inputs=2:duration=first,loudnorm=I=-14:TP=-1.5[a]"
else:
    audio = "[1:a]loudnorm=I=-14:TP=-1.5[a]"
ffmpeg(*inputs, "-filter_complex", f"[0:v]subtitles=script.srt:force_style='{style}'[v];{audio}",
       "-map", "[v]", "-map", "[a]", "-c:v", "libx264", "-preset", "veryfast", "-crf", "20",
       "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "160k", "-ar", "48000", "-ac", "2", "-shortest", "-movflags", "+faststart", "video.mp4")
for f in ["clips.txt"] + [f"clip{i:03d}.mp4" for i in range(len(images))]:
    os.remove(f)
print("done:", os.path.abspath("video.mp4"))
EOF2

Каждая фотография показывается на экране равную долю длительности озвучки. Она размещается по центру поверх размытой полноэкранной копии самой себя, поэтому и горизонтальные, и вертикальные фото заполняют вертикальный кадр. Затем скрипт склеивает клипы, добавляет голос и, если она есть, тихую фоновую музыку, вшивает субтитры, нормализует громкость примерно до -14 LUFS и сохраняет video.mp4.

Шаг 4: Первое видео

cd /data/videos/LS-01
TTS_LANG=z TTS_VOICE=zf_xiaobei /opt/tts/bin/python /opt/tts/tts.py script.txt
/opt/tts/bin/python /opt/tts/makevideo.py /data/videos/LS-01

На выходе получается видео 1080x1920, 30 кадров/с, H.264 со звуком AAC — этот формат принимают все крупные платформы коротких видео. Субтитры расположены с отступом от нижнего края кадра, чтобы их не перекрывали кнопки большинства платформ. Для японских субтитров запустите вторую команду с SUB_FONT="Noto Sans CJK JP". Чтобы изменить размер или положение субтитров, поменяйте FontSize и MarginV в строке style скрипта.

Шаг 5: Видео для всего каталога

for d in /data/videos/*/; do
  [ -f "$d/video.mp4" ] && continue                    # skip products that are already done
  TTS_LANG=z TTS_VOICE=zf_xiaobei /opt/tts/bin/python /opt/tts/tts.py "$d/script.txt" &&
  /opt/tts/bin/python /opt/tts/makevideo.py "$d" || echo "FAILED: $d"
done

Длинные пакетные задания запускайте через nohup или внутри screen, чтобы они продолжали работать после выхода из системы. В нашем тесте 16-секундное видео рендерилось около пяти секунд; основное время уходит на кодирование видео, поэтому многоядерный процессор заметно ускоряет работу.

Шаг 6: Проверка перед публикацией

  1. Посмотрите каждое видео хотя бы раз: неправильное произношение, опечатки в субтитрах и неудачно обрезанные фотографии легко заметить.
  2. Убедитесь, что музыка не заглушает голос; при необходимости уменьшите volume=0.15 в скрипте.
  3. Перед загрузкой проверьте актуальные требования каждой платформы к длительности, подписям и пометке о контенте, созданном ИИ.

Частые вопросы

Можно ли использовать короткие видеоклипы вместо фотографий?

Да, но клипы нужно так же обрезать по длительности и масштабировать, а для этого придётся доработать скрипт. Проще сгенерировать голос и SRT по руководству по озвучке и совместить их с вашими клипами в видеоредакторе.

Как сделать такое же видео на других языках?

Переведите script.txt и запустите обе команды с голосом для нужного языка. Для уже готовых видео см. ИИ-дубляж видео на другой язык.

Вместо китайских субтитров отображаются пустые квадраты

Не хватает шрифта. Установите fonts-noto-cjk, как в шаге 1, и проверьте, что fc-list показывает «Noto Sans CJK SC».

Если проблема не решена, создайте тикет — техническая поддержка IMIDC работает 24/7. Укажите IP сервера, версию ОС, структуру папок и полный текст ошибки, чтобы мы быстрее нашли причину.

Помог ли вам данный ответ?

Похожие руководства