Wyatt
Wyatt

Reputation: 1

Issue Adding Subtitles to MoviePy Video

I've been trying to use Movie Py to create a .srt file, and add the subtitles to a video. The first video without captions, made in _match_audio_duration(), exports sucessfully. The captions file is also created sucessfully, and appears to be in the right format, but the script crashes when trying to add the captions file to the video.

The line that fails: final_clip = mp.CompositeVideoClip([input_clip, caption_clip.set_position(("center", "bottom"))])

import os
import subprocess
import time
import moviepy.editor as mp
import pyttsx3
from moviepy.video.tools.subtitles import SubtitlesClip

class VideoEditor:
    def __init__(self, input_text_path, input_video_path, output_dir):
        self.input_text_path = input_text_path
        self.input_video_path = input_video_path
        self.output_dir = output_dir

        self.text = ""
        self.audio_path = ""
        self.video_path = ""
        self.caption_path = ""

    def _convert_text_to_audio(self):
        # Read text from file
        with open(self.input_text_path, "r") as f:
            self.text = f.read()

        # Set up pyttsx3
        engine = pyttsx3.init()

        # Convert text to speech and save as MP3
        self.audio_path = os.path.join(self.output_dir, "audio.mp3")
        engine.save_to_file(self.text, self.audio_path)
        engine.runAndWait()

    def _match_audio_duration(self):
        # Set up moviepy
        input_clip = mp.VideoFileClip(self.input_video_path)
        audio_clip = mp.AudioFileClip(self.audio_path)
        if audio_clip.duration > input_clip.duration:
            input_clip = input_clip.fx(mp.vfx.loop, duration=audio_clip.duration)

        # Add audio to video
        final_clip = input_clip.set_audio(audio_clip)

        # Cut video to match audio duration
        final_clip = final_clip.subclip(0, audio_clip.duration)

        # Save output video
        self.video_path = os.path.join(self.output_dir, "output_video.mp4")
        final_clip.write_videofile(self.video_path, fps=24)

    def _create_captions(self):
        # Create captions from text
        self.caption_path = os.path.join(self.output_dir, "captions.srt")
        caption_txt = ""
        for i, line in enumerate(self.text.splitlines()):
            start = i * 3
            end = (i + 1) * 3
            caption_txt += f"{start}00 --> {end}00\n{line}\n\n"
        with open(self.caption_path, "w") as f:
            f.write(caption_txt)

    def add_captions_to_video(self):
        self._convert_text_to_audio()
        self._match_audio_duration()
        self._create_captions()

        # Set up moviepy
        input_clip = mp.VideoFileClip(self.video_path)

        # Create TextClip for captions
        captions = SubtitlesClip(self.caption_path)

        caption_clip = captions.set_position(("center", "bottom"))

        # Overlay captions on video
        final_clip = mp.CompositeVideoClip([input_clip, caption_clip.set_position(("center", "bottom"))])

        # Save output video
        output_path = os.path.join(self.output_dir, "output_video_captions.mp4")
        final_clip.write_videofile(output_path, fps=24)

I double checked the format of the captions file, everything I could find about this error suggested that it was being the captions were in the wrong format or something.

The error:

Traceback (most recent call last):
  File "*directory*", line 86, in <module>
    editor.add_captions_to_video()
  File "*directory*", line 69, in add_captions_to_video
    captions = SubtitlesClip(self.caption_path)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*directory*", line 55, in __init__
    self.duration = max([tb for ((ta,tb), txt) in self.subtitles])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*directory*", line 55, in <listcomp>
    self.duration = max([tb for ((ta,tb), txt) in self.subtitles])
                                 ^^^^^^^
TypeError: cannot unpack non-iterable NoneType object
Exception ignored in: <function FFMPEG_VideoReader.__del__ at 0x000002097E824400>
Traceback (most recent call last):
  File "*directory*", line 199, in __del__
    self.close()
  File "*directory*", line 190, in close
    self.proc.terminate()
  File "*directory*", line 1642, in terminate
    _winapi.TerminateProcess(self._handle, 1)
OSError: [WinError 6] The handle is invalid

Upvotes: 0

Views: 826

Answers (1)

Rotem
Rotem

Reputation: 32124

As far as I can tell the issue is the format of subtitles file captions.srt.
It looks like SubtitlesClip is very sensitive to the .srt format structure.

I am not sure, it's the only issue, because the code fails in captions = SubtitlesClip(self.caption_path) and not in final_clip = mp.CompositeVideoClip.
When fixing the captions.srt file, the code finishes successfully.


For correcting the format of captions.srt we may use the following code (note that I don't know how to synchronize the captions with the audio):

def _create_captions(self):
    # Create captions from text
    self.caption_path = os.path.join(self.output_dir, "captions.srt")
    caption_txt = ""
    for i, line in enumerate(self.text.splitlines()):
        start = i * 3
        end = (i + 1) * 3
        start_str = str(datetime.timedelta(seconds=start)) + ',0'  # Convert from seconds to hh:mm:ss,0 format
        end_str = str(datetime.timedelta(seconds=end)) + ',0'
        caption_txt += f"{i+1}\n"  # Write the sequential counter
        caption_txt += f"{start_str} --> {end_str}\n{line}\n\n"
    with open(self.caption_path, "w") as f:
        f.write(caption_txt)

As far as I know, the .srt file format is as follows:

1
00:00:00,0 --> 00:00:01,0
First sentence

2
00:00:01,0 --> 00:00:02,0
Second sentence

3
00:00:02,0 --> 00:00:03,0
Third sentence

4
00:00:03,0 --> 00:00:04,0
Forth sentence

5
00:00:04,0 --> 00:00:05,0
Fifth sentence

For debugging, we may replace captions.srt with the text above.

Upvotes: 0

Related Questions