offset-fixer.py 4.0 KB

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