#!/usr/bin/env python import re from datetime import timedelta import argparse import locale import math class SubtitleEntry: def __init__(self, entryNumber, text, startTime, endTime): # dd:dd:dd,ddd --> dd:dd:dd,ddd self.entryNumber = entryNumber self.text = text sH, sM, sS = startTime.replace(',', '.').split(':') self.startTime = int(sH) * 3600 + int(sM) * 60 + float(sS) sH, sM, sS = endTime.replace(',', '.').split(':') self.endTime = int(sH) * 3600 + int(sM) * 60 + float(sS) def formatEntry(self): entry = "" entry += "{}\n".format(self.entryNumber) entry += self.formatTimeString() + "\n" entry += "{}".format(self.text) return entry def formatTimeString(self): s = self.startTime startHours = s // 3600 s = s - (startHours * 3600) startMinutes = s // 60 s = s - (startMinutes * 60) startSeconds = s e = self.endTime endHours = e // 3600 e = e - (endHours * 3600) endMinutes = e // 60 e = e - (endMinutes * 60) endSeconds = e return '{:02.0f}:{:02.0f}:{:02.0f},{:03.0f} --> {:02.0f}:{:02.0f}:{:02.0f},{:03.0f}'.format(startHours, startMinutes, math.floor(startSeconds), (startSeconds % 1) * 1000 , endHours, endMinutes, math.floor(endSeconds), (endSeconds % 1) * 1000 ) def shift(self, seconds): self.startTime = self.startTime + seconds self.endTime = self.endTime + seconds def main(): Subtitles = [] parser = argparse.ArgumentParser() parser.add_argument("--seconds", help="Decimal formatted seconds to adjust offsets by", type=float, required=True) parser.add_argument("--file", help="Subtitle file to shift", type=str, required=True) parser.add_argument("--inplace", required=False, help="Shift file in place, creating a backup", action='store_true') parser.add_argument("--outfile", required=False, help="New subtitle filename", type=str) parser.add_argument("--strip-first", required=False, help="Clear first subtitle entry", action='store_true') parser.add_argument("--strip-last", required=False, help="Clear last subtitle entry", action='store_true') args = parser.parse_args() if args.inplace is True: if args.outfile == None: args.outfile = str(args.file + ".backup") else: sys.exit(1) if args.outfile == None: args.outfile = str(args.file + ".new") print("Opening subtitle file: {}".format(args.file)) with open(args.file, "r+") as file: entryNumber = 0 text = "" timestamp = "" for line in file: m1 = re.match(r"^([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}) --> ([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3})", line) if line == "\n": if (entryNumber > 0) and (timestamp is not None): s = SubtitleEntry(entryNumber, text, timestamp.groups()[0], timestamp.groups()[1]) Subtitles.append(s) text = "" timestamp = "" entryNumber = 0 elif re.match(r"^[0-9]+$", line): entryNumber = int(line.rstrip()) elif m1: timestamp = m1 else: text += line if args.inplace == True: args.outfile = args.file args.file = args.file + ".backup" with open(args.file, 'w') as o: print("Writing backup to " + args.file) for entry in Subtitles: print(entry.formatEntry(), file=o) print("Updating subtitle file: " + args.outfile) lastEntryNumber = Subtitles[-1].entryNumber with open(args.outfile, 'w') as o: print("Shifting all entries by {} seconds".format(args.seconds)) for idx, entry in enumerate(Subtitles): if idx == 0: print("First subtitle line: {}".format(entry.text)) if args.strip_first and entry.text != "": print("Stripping first line - {}".format(entry.text)) entry.text = "" if idx == lastEntryNumber - 1: print("Last subtitle line: {}".format(entry.text)) if args.strip_last and entry.text != "": print("Stripping last line - {}".format(entry.text)) entry.text = "" entry.shift(args.seconds) print(entry.formatEntry(), file=o) if __name__ == "__main__": main()