How to re-synchronize (shift) subtitles of a movie from a SRT file in Python 3

I can't count all the times i downloaded a film ... legally, i promise ... and the movie comes in a language that i don't understand, however the quality is without a doubt the best that you can't find on the internet, so you simply decide to keep the file and search for subtitles on the internet. Unfortunately, the subtitles doesn't match with the parts where the actors speak. Probably, you already use a player program that includes the feature to delay the time of the subtitles, for example with Power DVD 17, you can easily do it in the subtitles option:

Subtitles SRT Movie

Nobody is downloading any pirate version of the John Wick moview here. As you can see, there's an area to delay the subtitles in a specified ammount of seconds. That's pretty useful, however the configuration will be stored in the player, so when you move the files of computer, you will see the problem with the subtitles again.

The faster solution, is to use an online tool like this one offered by the guys at Bits'n'Bites that allows you to upload a SRT file and synchronize (shift) the time of the subtitles. These tools shift all the time stamps of a movie subtitle file. It can be used for synchronizing the subtitles to a movie when there is a slight offset between the two (this can be the case when the subtitles and the movie come from two different sources), or when there is a time scale difference (for instance if the movie and the subtitle file have different frame rates).

However, Our Code World is not about how to solve normal users problems right!? Are you trying to implement the same tool of the website that we recommend you previously? Then today is your lucky day as we'll explain you how to do it with an open source Python script.

1. Creating the resync script

A very nice implementation in Python that you can find to achieve this task, is the srt-resync project available at Github here. However, unless you are working in a Linux based environment, the script that you found in the repository won't work in windows correctly due to its functionality way (working as a bash executable), so if you try to run the script with python srt-resync {argument} in an environment like Windows, you will see an error of the main namespace. To solve this issue we recommend you to copy the content of the main script and create your own python file as we are going to explain now.

To get started, create a python file namely srt-resync.py and store the following code on the file:

"""
  Copyright (c) 2013, William Ting

  *  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3, or (at your option)
  any later version.

  *  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  *  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    Original code: https://github.com/wting/srt-resync
        Issue: the file in the repo doesn't contain any python format, so in
        environments like Windows it can't run without modifying its extension
        to srt-resync.py
"""

import argparse
import datetime
import os
import re
import shutil
import sys

from io import FileIO as file

VERSION = "0.1"

def parse_options():
    global VERSION

    parser = argparse.ArgumentParser(
            description = 'Offset srt subtitles.')

    parser.add_argument('offset', type = float)
    parser.add_argument('srt_file', type = file)
    parser.add_argument('-o', '--overwrite', action = "store_true", default = False,
            help = "overwite original file")
    parser.add_argument('--version', action = "version", version = "%(prog)s " + VERSION,
            help = "show version information and quit")

    return parser.parse_args()

def rzeropad(ms):
    ms = str(int(ms))
    while len(ms) < 3:
        ms += "0"
    return ms

def offset_time(offset, time_string):
    #FIXME: does not support timestamps >= 24 hours
    ts = time_string.replace(',', ':').split(':')
    ts = [int(x) for x in ts]
    ts = datetime.datetime(2013, 1, 1, ts[0], ts[1], ts[2], ts[3] * 1000) # millisecond -> microsecond

    delta = datetime.timedelta(seconds = offset)
    ts += delta

    if ts.year != 2013 or ts.month != 1 or ts.day != 1:
        sys.exit("ERROR: invalid offset resulting timestamp overflow")

    return "%s,%s" % (ts.strftime("%H:%M:%S"), rzeropad(ts.microsecond / 1000)) # microsecond -> millisecond

def modify_file(options):
    if '.srt' not in options.srt_file.name:
        sys.exit("ERROR: invalid srt file")

    out_filename = os.path.splitext(options.srt_file.name)[0] + '-resync.srt'
    with open(out_filename, 'w', encoding = 'utf-8') as out:
        with open(options.srt_file.name, 'r', encoding = 'utf-8') as srt:
            for line in srt.readlines():
                match = re.search(r'^(\d+:\d+:\d+,\d+)\s+--\>\s+(\d+:\d+:\d+,\d+)', line)
                if match:
                    out.write("%s --> %s\n" % (
                        offset_time(options.offset, match.group(1)),
                        offset_time(options.offset, match.group(2))
                        ))
                else:
                    out.write(line)

    if options.overwrite:
        shutil.move(out_filename, options.srt_file.name)

if __name__ == "__main__":
    modify_file(parse_options())

This script shifts subtitles timing in an .srt file by a given offset in seconds. Negative numbers and partial seconds are supported. Save the changes of the file and proceed to learn how to use it in the next step.

For more information about this script, please visit the official repository at Github here.

2. Using the script

Now that you have the python script, you can simply run it in your terminal providing as first positional argument the number of seconds that you want to shift, for example -0.50, +1, +1.50 etc and as second positional argument, the path to the original file of the subtitles that you want to fix:

python srt-resync.py [+seconds or -seconds] [original_subtitles_file.srt]

By default, the scripts creates a new file with the resync prefix at the end of the file, for example if you provide an input file namely subtitles.srt, the output file will be subtitles-resync.srt. You can as well overwrite the original file using the -o option or --overwrite.

The content of the original srt file in our case is the following one:

1
00:03:36,055 --> 00:03:38,142
¿Ya cargaron todo?

2
00:03:38,183 --> 00:03:42,188
Casi.

3
00:03:42,687 --> 00:03:45,816
Con todo respeto,
¿es nuestra mejor opción?

4
00:03:45,857 --> 00:03:50,070
- ¿Por qué no corregimos el problema?
- Porque mi maldito sobrino...

5
00:03:50,862 --> 00:03:52,782
mató a un perro.

6
00:03:53,948 --> 00:03:55,743
Y robó un auto.

Then the output of the SRT file with a modification (yourfile-resync.srt) of -0.50 seconds with the following command:

REM move the subtitles by minus half second
python srt-resync.py -0.50 ./subtitles.srt

will be:

1
00:03:35,555 --> 00:03:37,642
¿Ya cargaron todo?

2
00:03:37,683 --> 00:03:41,688
Casi.

3
00:03:42,187 --> 00:03:45,316
Con todo respeto,
¿es nuestra mejor opción?

4
00:03:45,357 --> 00:03:49,570
- ¿Por qué no corregimos el problema?
- Porque mi maldito sobrino...

5
00:03:50,362 --> 00:03:52,282
mató a un perro.

6
00:03:53,448 --> 00:03:55,243
Y robó un auto.

Pretty easy right? Now you have a fully functional subtitles file that matches with the time of your fully legal downloaded movie ...

Happy coding !

This could interest you

Become a more social person