Skip to content

Instantly share code, notes, and snippets.

@jamesmcm
Created April 21, 2025 22:29
Show Gist options
  • Save jamesmcm/2869c5c96471f12c09ad87dd76d053f0 to your computer and use it in GitHub Desktop.
Save jamesmcm/2869c5c96471f12c09ad87dd76d053f0 to your computer and use it in GitHub Desktop.
Python script to synchronize SRT subtitles
#!/bin/python
import re
import argparse
from datetime import datetime, timedelta
def parse_time(time_str):
"""Convert SRT timestamp to total seconds."""
hours, minutes, seconds = time_str.replace(',', '.').split(':')
return int(hours) * 3600 + int(minutes) * 60 + float(seconds)
def format_time(seconds):
"""Convert seconds back to SRT timestamp format."""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
seconds = seconds % 60
whole_seconds = int(seconds)
milliseconds = int((seconds - whole_seconds) * 1000)
return f"{hours:02d}:{minutes:02d}:{whole_seconds:02d},{milliseconds:03d}"
def calculate_synchronization_parameters(reference_points):
"""
Calculate time offset and speed factor from reference points.
Each reference point is a tuple of (srt_time, video_time).
"""
if len(reference_points) < 2:
raise ValueError("Need at least two reference points to calculate speed factor")
# Convert times to seconds
points_in_seconds = [(parse_time(srt), parse_time(video)) for srt, video in reference_points]
# For better accuracy, use the first and last point
first_point = points_in_seconds[0]
last_point = points_in_seconds[-1]
# Calculate speed factor: ratio of video time span to SRT time span
srt_time_span = last_point[0] - first_point[0]
video_time_span = last_point[1] - first_point[1]
speed_factor = video_time_span / srt_time_span
# Calculate offset using the first point and the speed factor
# video_time = srt_time * speed_factor + offset
offset = first_point[1] - (first_point[0] * speed_factor)
return speed_factor, offset
def adjust_subtitle_time(time_str, speed_factor, offset):
"""Apply speed factor and offset to a timestamp."""
seconds = parse_time(time_str)
adjusted_seconds = seconds * speed_factor + offset
return format_time(max(0, adjusted_seconds)) # Ensure time is not negative
def synchronize_srt(input_file, output_file, speed_factor, offset):
"""Synchronize SRT file using specified speed factor and offset."""
# Pattern to match time ranges in SRT format
time_pattern = re.compile(r'(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})')
with open(input_file, 'r', encoding='utf-8') as f_in, open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
# Check if line contains timestamps
match = time_pattern.search(line)
if match:
start_time, end_time = match.groups()
adjusted_start = adjust_subtitle_time(start_time, speed_factor, offset)
adjusted_end = adjust_subtitle_time(end_time, speed_factor, offset)
adjusted_line = f"{adjusted_start} --> {adjusted_end}\n"
f_out.write(adjusted_line)
else:
f_out.write(line)
def main():
parser = argparse.ArgumentParser(description='Synchronize SRT subtitles with video.')
parser.add_argument('input_file', help='Input SRT file path')
parser.add_argument('output_file', help='Output SRT file path')
parser.add_argument('--manual', action='store_true',
help='Manually specify speed factor and offset')
parser.add_argument('--speed', type=float, help='Speed factor (if manual)')
parser.add_argument('--offset', type=float, help='Time offset in seconds (if manual)')
args = parser.parse_args()
if args.manual:
if args.speed is None or args.offset is None:
parser.error("--speed and --offset are required with --manual")
speed_factor = args.speed
offset = args.offset
else:
# TODO: Fill these in from your SRT file and video
reference_points = [
("00:02:36,833", "01:26:42"),
("00:03:30,129", "01:27:37"),
("01:27:47,028", "02:48:27")
]
speed_factor, offset = calculate_synchronization_parameters(reference_points)
print(f"Calculated speed factor: {speed_factor:.6f}")
print(f"Calculated offset: {offset:.6f} seconds")
synchronize_srt(args.input_file, args.output_file, speed_factor, offset)
print(f"Synchronized subtitles saved to {args.output_file}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment