SubRip (.srt) subtitle parser and writer for Python
—
Individual subtitle manipulation through the SubRipItem class. Each subtitle item contains timing information, text content, positioning data, and supports various text processing and timing operations.
Create subtitle items from various sources and formats.
class SubRipItem:
def __init__(self, index=0, start=None, end=None, text='', position=''):
"""
Create a new subtitle item.
Args:
index (int): Subtitle sequence number (default: 0)
start: Start time (SubRipTime or coercible, default: 0)
end: End time (SubRipTime or coercible, default: 0)
text (str): Subtitle text content (default: '')
position (str): Display position/coordinates (default: '')
"""
@classmethod
def from_string(cls, source):
"""
Parse subtitle item from string representation.
Args:
source (str): Complete subtitle item in SRT format
Returns:
SubRipItem: Parsed subtitle item
"""
@classmethod
def from_lines(cls, lines):
"""
Parse subtitle item from list of lines.
Args:
lines (list): List of strings representing subtitle lines
Returns:
SubRipItem: Parsed subtitle item
"""
@classmethod
def split_timestamps(cls, line):
"""
Split timestamp line into start time, end time, and position.
Args:
line (str): Timestamp line (e.g., "00:00:01,500 --> 00:00:05,000")
Returns:
tuple: (start_time_str, end_time_str, position_str)
"""Access and modify subtitle item properties.
@property
def index(self):
"""
Subtitle sequence number.
Returns:
int: Index number in subtitle sequence
"""
@property
def start(self):
"""
Start time of subtitle.
Returns:
SubRipTime: Start timestamp
"""
@property
def end(self):
"""
End time of subtitle.
Returns:
SubRipTime: End timestamp
"""
@property
def text(self):
"""
Subtitle text content.
Returns:
str: Text content with formatting tags
"""
@property
def position(self):
"""
Display position/coordinates string.
Returns:
str: Position specification (e.g., "X1:40 X2:600 Y1:20 Y2:50")
"""
@property
def duration(self):
"""
Duration of subtitle (computed as end - start).
Returns:
SubRipTime: Duration of subtitle display
"""
@property
def text_without_tags(self):
"""
Text content with HTML/formatting tags removed.
Returns:
str: Plain text without markup tags
"""
@property
def characters_per_second(self):
"""
Reading speed metric based on character count and duration.
Returns:
float: Characters per second (0.0 if duration is zero)
"""Modify subtitle timing and perform time-based calculations.
def shift(self, *args, **kwargs):
"""
Adjust start and end times by offset or ratio.
Args:
*args: Positional time arguments (hours, minutes, seconds, milliseconds)
**kwargs: Named time arguments or ratio for proportional scaling
Supported kwargs:
hours (int): Hours to add
minutes (int): Minutes to add
seconds (int): Seconds to add
milliseconds (int): Milliseconds to add
ratio (float): Multiply times by this ratio
"""Convert subtitle items to various string formats.
def __str__(self):
"""
Convert to standard SRT format string.
Returns:
str: Complete subtitle item in SRT format
"""SubRipItem supports comparison operations based on timing.
# Comparison operators based on start time, then end time
item1 < item2 # item1 starts before item2
item1 <= item2 # item1 starts before or at same time as item2
item1 == item2 # item1 and item2 have same timing
item1 >= item2 # item1 starts at or after item2
item1 > item2 # item1 starts after item2
item1 != item2 # item1 and item2 have different timingimport pysrt
# Create from scratch
item = pysrt.SubRipItem(
index=1,
start=pysrt.SubRipTime(0, 0, 1, 500), # 00:00:01,500
end=pysrt.SubRipTime(0, 0, 5, 0), # 00:00:05,000
text="Hello, World!"
)
# Create with position information
positioned_item = pysrt.SubRipItem(
index=2,
start={'seconds': 6},
end={'seconds': 10},
text="Bottom right text",
position="X1:400 X2:600 Y1:300 Y2:350"
)
# Parse from string
srt_text = """1
00:00:01,500 --> 00:00:05,000
Hello, World!"""
item = pysrt.SubRipItem.from_string(srt_text)# Working with formatted text
item.text = "<i>Italic text</i> and <b>bold text</b>"
plain_text = item.text_without_tags # "Italic text and bold text"
# Multi-line subtitles
item.text = "First line\nSecond line\nThird line"
# Reading analysis
print(f"Reading speed: {item.characters_per_second:.2f} chars/sec")# Adjust timing
item.shift(seconds=2) # Delay by 2 seconds
item.shift(milliseconds=-500) # Advance by 0.5 seconds
item.shift(ratio=1.1) # Speed up by 10%
# Duration calculations
print(f"Duration: {item.duration}") # Show how long subtitle displays
print(f"Start: {item.start}") # Show start time
print(f"End: {item.end}") # Show end time
# Create item with specific duration
start_time = pysrt.SubRipTime(minutes=1)
duration = pysrt.SubRipTime(seconds=3, milliseconds=500)
item = pysrt.SubRipItem(
start=start_time,
end=start_time + duration,
text="3.5 second subtitle"
)# Load and modify existing subtitles
subs = pysrt.open('movie.srt')
for item in subs:
# Uppercase all text
item.text = item.text.upper()
# Add prefix to all subtitles
item.text = f"[MOVIE] {item.text}"
# Remove HTML tags
if '<' in item.text:
item.text = item.text_without_tags
# Extend short subtitles
if item.duration.ordinal < 1000: # Less than 1 second
item.end += pysrt.SubRipTime(milliseconds=500)
subs.save('modified_movie.srt')# Sort subtitles by timing
subtitle_list = [item3, item1, item2] # Out of order
subtitle_list.sort() # Now sorted by start time
# Find overlapping subtitles
for i, current in enumerate(subs[:-1]):
next_item = subs[i + 1]
if current.end > next_item.start:
print(f"Overlap detected between items {current.index} and {next_item.index}")
# Filter by timing criteria
long_subtitles = [item for item in subs if item.duration.ordinal > 5000] # > 5 seconds
short_subtitles = [item for item in subs if item.duration.ordinal < 1000] # < 1 secondimport re
# Remove specific formatting tags
def clean_tags(text):
# Remove specific HTML tags but keep content
text = re.sub(r'</?[bi]>', '', text) # Remove bold/italic tags
text = re.sub(r'<font[^>]*>', '', text) # Remove font tags
text = re.sub(r'</font>', '', text)
return text
# Process all subtitles
for item in subs:
# Clean formatting
item.text = clean_tags(item.text)
# Fix common encoding issues
item.text = item.text.replace('’', "'") # Fix apostrophes
item.text = item.text.replace('“', '"') # Fix quotes
item.text = item.text.replace(' ', '"')
# Break long lines
if len(item.text) > 50 and '\n' not in item.text:
words = item.text.split(' ')
mid = len(words) // 2
item.text = ' '.join(words[:mid]) + '\n' + ' '.join(words[mid:])# Create subtitles with specific positioning
top_subtitle = pysrt.SubRipItem(
start={'seconds': 10},
end={'seconds': 15},
text="Top of screen",
position="Y1:50 Y2:100" # Top positioning
)
bottom_subtitle = pysrt.SubRipItem(
start={'seconds': 10},
end={'seconds': 15},
text="Bottom of screen",
position="Y1:400 Y2:450" # Bottom positioning
)
# Multiple simultaneous subtitles (different positions)
subs = pysrt.SubRipFile()
subs.append(top_subtitle)
subs.append(bottom_subtitle)
subs.save('positioned_subtitles.srt')Install with Tessl CLI
npx tessl i tessl/pypi-pysrt