offset-fixer.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. #!/usr/bin/env python
  2. import re
  3. from datetime import timedelta
  4. import argparse
  5. import locale
  6. import sys
  7. import math
  8. class SubtitleEntry:
  9. def __init__(self, entryNumber, text, startTime, endTime):
  10. # dd:dd:dd,ddd --> dd:dd:dd,ddd
  11. self.entryNumber = entryNumber
  12. self.text = text
  13. sH, sM, sS = startTime.replace(',', '.').split(':')
  14. self.startTime = int(sH) * 3600 + int(sM) * 60 + float(sS)
  15. sH, sM, sS = endTime.replace(',', '.').split(':')
  16. self.endTime = int(sH) * 3600 + int(sM) * 60 + float(sS)
  17. def formatEntry(self):
  18. entry = ""
  19. entry += "{}\n".format(self.entryNumber)
  20. entry += self.formatTimeString() + "\n"
  21. entry += "{}".format(self.text)
  22. return entry
  23. def formatTimeString(self):
  24. s = self.startTime
  25. startHours = s // 3600
  26. s = s - (startHours * 3600)
  27. startMinutes = s // 60
  28. s = s - (startMinutes * 60)
  29. startSeconds = s
  30. e = self.endTime
  31. endHours = e // 3600
  32. e = e - (endHours * 3600)
  33. endMinutes = e // 60
  34. e = e - (endMinutes * 60)
  35. endSeconds = e
  36. 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 )
  37. def shift(self, seconds):
  38. self.startTime = self.startTime + seconds
  39. self.endTime = self.endTime + seconds
  40. def main():
  41. Subtitles = []
  42. parser = argparse.ArgumentParser()
  43. parser.add_argument("--seconds", help="Decimal formatted seconds to adjust offsets by", type=float, required=True)
  44. parser.add_argument("--file", help="Subtitle file to shift", type=str, required=True)
  45. parser.add_argument("--inplace", required=False, help="Shift file in place, creating a backup", action='store_true')
  46. parser.add_argument("--outfile", required=False, help="New subtitle filename", type=str)
  47. parser.add_argument("--strip-first", required=False, help="Clear first subtitle entry", action='store_true')
  48. parser.add_argument("--strip-last", required=False, help="Clear last subtitle entry", action='store_true')
  49. args = parser.parse_args()
  50. if args.inplace is True:
  51. if args.outfile == None:
  52. args.outfile = str(args.file + ".backup")
  53. else:
  54. sys.exit(1)
  55. if args.outfile == None:
  56. args.outfile = str(args.file + ".new")
  57. print("Opening subtitle file: {}".format(args.file))
  58. with open(args.file, "r+") as file:
  59. entryNumber = 0
  60. text = ""
  61. timestamp = ""
  62. for line in file:
  63. 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)
  64. if line == "\n":
  65. if (entryNumber > 0) and (timestamp is not None):
  66. s = SubtitleEntry(entryNumber, text, timestamp.groups()[0], timestamp.groups()[1])
  67. Subtitles.append(s)
  68. text = ""
  69. timestamp = ""
  70. entryNumber = 0
  71. elif re.match(r"^[0-9]+$", line):
  72. entryNumber = int(line.rstrip())
  73. elif m1:
  74. timestamp = m1
  75. else:
  76. text += line
  77. if args.inplace == True:
  78. args.outfile = args.file
  79. args.file = args.file + ".backup"
  80. with open(args.file, 'w') as o:
  81. print("Writing backup to " + args.file)
  82. for entry in Subtitles:
  83. print(entry.formatEntry(), file=o)
  84. print("Updating subtitle file: " + args.outfile)
  85. lastEntryNumber = Subtitles[-1].entryNumber
  86. with open(args.outfile, 'w') as o:
  87. print("Shifting all entries by {} seconds".format(args.seconds))
  88. for idx, entry in enumerate(Subtitles):
  89. if idx == 0:
  90. print("First subtitle line: {}".format(entry.text))
  91. if args.strip_first and entry.text != "":
  92. print("Stripping first line - {}".format(entry.text))
  93. entry.text = ""
  94. if idx == lastEntryNumber - 1:
  95. print("Last subtitle line: {}".format(entry.text))
  96. if args.strip_last and entry.text != "":
  97. print("Stripping last line - {}".format(entry.text))
  98. entry.text = ""
  99. entry.shift(args.seconds)
  100. print(entry.formatEntry(), file=o)
  101. if __name__ == "__main__":
  102. main()