SubRip (.srt) subtitle parser and writer for Python
—
File-level operations for SubRip subtitle files, including parsing, saving, batch operations, and file format handling. The SubRipFile class provides a list-like interface for managing collections of subtitles.
Create new subtitle files or load existing ones with automatic encoding detection.
class SubRipFile:
def __init__(self, items=None, eol=None, path=None, encoding='utf-8'):
"""
Create a new SubRipFile instance.
Args:
items (list, optional): List of SubRipItem instances
eol (str, optional): End of line character (defaults to os.linesep)
path (str, optional): File path for saving
encoding (str): File encoding (default: 'utf-8')
"""
@classmethod
def open(cls, path='', encoding=None, error_handling=0):
"""
Open and parse a SubRip file from filesystem.
Args:
path (str): Path to subtitle file
encoding (str, optional): File encoding (auto-detected if None)
error_handling (int): Error handling mode (ERROR_PASS/ERROR_LOG/ERROR_RAISE)
Returns:
SubRipFile: Parsed subtitle file
"""
@classmethod
def from_string(cls, source, **kwargs):
"""
Create SubRipFile from string content.
Args:
source (str): Subtitle content as string
**kwargs: Additional arguments passed to constructor
Returns:
SubRipFile: Parsed subtitle file
"""Save subtitle files with encoding and format control.
def save(self, path=None, encoding=None, eol=None):
"""
Save subtitles to file.
Args:
path (str, optional): Output path (uses instance path if None)
encoding (str, optional): Output encoding (uses instance encoding if None)
eol (str, optional): End of line character (uses instance eol if None)
"""
def write_into(self, output_file, eol=None):
"""
Write subtitles to file-like object.
Args:
output_file: Any object with write() method
eol (str, optional): End of line character
"""Stream subtitles as they are parsed without storing them in memory.
@classmethod
def stream(cls, source_file, error_handling=0):
"""
Generator that yields SubRipItem instances as they are parsed.
Args:
source_file: Iterable yielding unicode strings (file-like object)
error_handling (int): Error handling mode
Yields:
SubRipItem: Parsed subtitle items
"""
def read(self, source_file, error_handling=0):
"""
Parse subtitles from file-like object and add to current instance.
Args:
source_file: Iterable yielding unicode strings
error_handling (int): Error handling mode
Returns:
SubRipFile: Self for method chaining
"""Filter subtitles based on timing constraints.
def slice(self, starts_before=None, starts_after=None, ends_before=None, ends_after=None):
"""
Filter subtitles by time constraints.
Args:
starts_before: SubRipTime or coercible - filter items starting before this time
starts_after: SubRipTime or coercible - filter items starting after this time
ends_before: SubRipTime or coercible - filter items ending before this time
ends_after: SubRipTime or coercible - filter items ending after this time
Returns:
SubRipFile: New instance with filtered subtitles (references original items)
"""
def at(self, timestamp=None, **kwargs):
"""
Get subtitles visible at specific timestamp.
Args:
timestamp: SubRipTime or coercible, or None to use kwargs
**kwargs: Time components (hours, minutes, seconds, milliseconds)
Returns:
SubRipFile: New instance with subtitles visible at timestamp
"""Apply operations to all subtitles in the file.
def shift(self, *args, **kwargs):
"""
Shift timing of all subtitles.
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 all times by this ratio
"""
def clean_indexes(self):
"""
Sort subtitles by start time and renumber sequentially.
Should be called after operations that may disorder subtitles.
"""Access file-level information and computed properties.
@property
def text(self):
"""
Combined text content of all subtitles.
Returns:
str: All subtitle text joined with newlines
"""
@property
def eol(self):
"""
End of line character used in file.
Returns:
str: EOL character (\\n, \\r\\n, or \\r)
"""
@property
def path(self):
"""File path if set during creation or opening."""
@property
def encoding(self):
"""File encoding used for reading/writing."""SubRipFile inherits from UserList and supports standard list operations.
# Indexing and slicing
item = subs[0] # Get first subtitle
items = subs[1:5] # Get range of subtitles
# Iteration
for sub in subs: # Iterate over all subtitles
print(sub.text)
# Length and membership
count = len(subs) # Number of subtitles
has_item = item in subs # Check if item is in file
# Modification
subs.append(new_item) # Add subtitle to end
subs.extend(more_items) # Add multiple subtitles
subs.insert(0, first_item) # Insert at specific position
subs.remove(item) # Remove specific item
del subs[0] # Delete by index
# Sorting
subs.sort() # Sort by start time (default)
subs.reverse() # Reverse orderERROR_PASS: int = 0 # Ignore parsing errors
ERROR_LOG: int = 1 # Log parsing errors to stderr
ERROR_RAISE: int = 2 # Raise exceptions on parsing errors
DEFAULT_ENCODING: str = 'utf_8' # Default file encodingimport pysrt
# Load subtitle file
subs = pysrt.open('movie.srt')
print(f"Loaded {len(subs)} subtitles")
# Handle encoding explicitly
subs = pysrt.open('movie.srt', encoding='latin1')
# Error handling
try:
subs = pysrt.open('malformed.srt', error_handling=pysrt.ERROR_RAISE)
except pysrt.Error as e:
print(f"Parsing error: {e}")# Shift all subtitles 2.5 seconds forward
subs.shift(seconds=2, milliseconds=500)
# Convert framerate from 23.976 to 25 fps
subs.shift(ratio=25/23.976)
# Get subtitles visible at 1 minute mark
minute_subs = subs.at(minutes=1)
# Get subtitles in first 30 seconds
early_subs = subs.slice(ends_before={'seconds': 30})# Stream processing for large files
for subtitle in pysrt.stream(open('large_movie.srt', 'r', encoding='utf-8')):
if 'profanity' in subtitle.text.lower():
subtitle.text = '[CENSORED]'
print(subtitle)
# Create from string content
srt_content = """1
00:00:01,500 --> 00:00:05,000
Hello, World!
2
00:00:06,000 --> 00:00:10,000
Welcome to PySRT!
"""
subs = pysrt.from_string(srt_content)# Clean up subtitle file
subs = pysrt.open('messy_subtitles.srt')
subs.clean_indexes() # Sort and renumber
subs.shift(milliseconds=500) # Fine-tune timing
subs.save('clean_subtitles.srt')
# Split subtitles into multiple files
full_movie = pysrt.open('full_movie.srt')
first_half = full_movie.slice(ends_before={'minutes': 60})
second_half = full_movie.slice(starts_after={'minutes': 60})
second_half.shift(minutes=-60) # Reset timing for second part
first_half.save('part1.srt')
second_half.save('part2.srt')Install with Tessl CLI
npx tessl i tessl/pypi-pysrt