Created
April 21, 2025 22:29
-
-
Save jamesmcm/2869c5c96471f12c09ad87dd76d053f0 to your computer and use it in GitHub Desktop.
Python script to synchronize SRT subtitles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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