How to embed subtitles in multiple movies in an automated way using python?

135 Views Asked by At

This python script identifies all movies with mp4 extension and, if they have subtitles in srt format, converts the subtitle encoding to utf-8 (without this conversion you will get error message) and starts building the movie with the subtitle embedded.

for filename in glob.glob("*.mp4"):
    print(str(filename))
    if 'wsubs' not in filename:
        for legenda in glob.glob(filename.replace(".mp4",".srt")):
            try:
                with open(legenda, 'r') as arquivo_orig:
                    conteudo = arquivo_orig.read()
                with open(legenda, 'w', encoding='utf-8') as novo_arquivo:
                    novo_arquivo.write(conteudo)
            except Exception as e:
                print("Já codificado")
            print(str(legenda))
            os.system('ffmpeg -i "' + str(filename) + '" -vf subtitles="' + str(legenda) + '" -maxrate 2000000 -crf 20 "' + str(filename.replace('.mp4','-wsubs.mp4')) + '"')

The end result will be a subtitled movie with the expression "wsubs" at the end of the name. The original files will be preserved.

1

There are 1 best solutions below

0
Trykowo On

I am not entirely sure what you are asking as it looks like you have answered your own question. So if it is an opinion on how to achieve what I think you are doing, this is how I would do it. [I would have liked to have left this as a comment but my reputation is not high enough.]

import os
import subprocess as sp


VIDEO_EXT = ".mp4"
SUBTITLE_EXT = ".srt"
SUB_TOKEN = "-wsubs"


def main(working_directory: str):
    for file in os.listdir(working_directory):
        full_name = os.path.join(working_directory, file)

        # Keep this statement in, but remove `main(full_name)` if you
        #   do not want to check all subdirectories.
        if os.path.isdir(full_name):
            main(full_name)
            continue

        file_base_name, file_ext = os.path.splitext(file)
        sub_file = os.path.join(working_directory, file_base_name + SUBTITLE_EXT)
        result_file = os.path.join(working_directory, file_base_name + SUB_TOKEN + file_ext)

        # The video is already subbed.
        if os.path.exists(result_file):
            print(f"Video: {full_name} already has a subbed video.")
            continue

        if SUB_TOKEN not in file and VIDEO_EXT in file_ext and os.path.exists(sub_file):
            cmd = [
                "ffmpeg",
                "-v", "error",
                "-i", full_name,
                "-c:a", "copy",
                "-c:v", "libx264",
                "-x264-params", "nal-hrd=vbr",
                "-vf", "subtitles=pipe\\\\:0",
                "-crf", "20",
                "-maxrate", "2M",
                "-f", "mp4",
                result_file,
                "-y"
            ]

            # Pass the file handler to the subprocess directly
            with open(sub_file, "r") as f_:
                with sp.Popen(cmd, stdin=f_, stderr=sp.PIPE) as proc:
                    outs, err = proc.communicate()

            if proc.returncode != 0 and err:
                try:
                    os.remove(result_file)
                except OSError:
                    pass
                print("ffmpeg ran into an error: " + err.decode("UTF-8"))


if __name__ == "__main__":
    main(os.path.join(os.getcwd(), "files"))

The primary difference between the scripts is that I use subprocess to execute the ffmpeg command. I also pass the subtitles file though a pipe as with my experience; ffmpeg does not like certain filenames within the -vf argument. While not an important difference; I also use the os library to handle my filename manipulations.