Blog Post

How to Bulk-Delete Sentences from SRT Files Using Python

Have you ever downloaded subtitle files (.srt) for an entire season of a TV show, only to find that every single episode contains an annoying advertisement like "Subtitles synced by Subz.com"?

The Analogy: Manually opening and editing 100 episodes of an anime or TV show is like picking weeds in a massive field by hand. Python is the industrial lawnmower.

Most beginners try to solve this by writing a simple Python script using `str.replace()`. However, doing so will instantly corrupt your video playback experience. To process data like an expert, we need to understand the rigid structure of SRT files and use Regular Expressions (Regex).

The "Ghost Block" Trap

An SRT file isn't just plain text; it's broken into rigid blocks containing an Index, a Timestamp, and the Text. If you use a basic text replace to delete the spam sentence, you leave behind the Index and Timestamp with a blank text field. Video players (like VLC or Plex) will literally display a blank, black box on your screen for 5 seconds. We must delete the entire block.

The Expert Python Script

Here is the robust, production-ready script. It uses Python's re (Regular Expressions) module to find and safely excise the entire ghost block, and it includes advanced file encoding fallbacks. Save this as srt_cleaner.py.

import os
import re

def delete_sentences_in_srt_files():
    """
    Scans a directory for .srt files, uses Regex to safely delete the ENTIRE 
    subtitle block containing the target text, and saves it in a safe sandbox.
    """
    source_directory = input("Enter the path to the folder containing the .srt files: ")

    if not os.path.isdir(source_directory):
        print(f"Error: Directory '{source_directory}' not found.")
        return

    sentences_input = input("Enter the sentences to delete (separate with a semicolon ';'): ")
    sentences_to_delete = [s.strip() for s in sentences_input.split(';') if s.strip()]

    if not sentences_to_delete:
        print("No sentences provided. Exiting.")
        return

    # Advanced fallback encodings for downloaded files
    encodings = ['utf-8', 'utf-8-sig', 'latin-1', 'cp1252']
    updated_files_count = 0

    print("\nStarting file processing...")

    for root, _, files in os.walk(source_directory):
        for filename in files:
            if filename.lower().endswith(".srt"):
                filepath = os.path.join(root, filename)
                
                content = None
                
                # 1. Safely read the file using encoding fallbacks
                for enc in encodings:
                    try:
                        with open(filepath, 'r', encoding=enc) as f:
                            content = f.read()
                        break
                    except UnicodeDecodeError:
                        continue
                
                if content is None:
                    print(f"  -> Error: Could not decode {filename}. Skipping.")
                    continue

                original_content = content

                # 2. Use Regex as a laser scalpel to remove the entire block
                for sentence in sentences_to_delete:
                    escaped_sentence = re.escape(sentence)
                    
                    # Regex pattern to match Index, Timestamp, and Text up to the double newline
                    pattern = r'(?m)^\d+\n\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}\n.*?' + escaped_sentence + r'.*?(?:\n\n|\Z)'
                    
                    content = re.sub(pattern, '', content, flags=re.DOTALL)

                # 3. Save to a safe sandbox if changes were made
                if content != original_content:
                    # Clean up any leftover awkward newlines
                    content = re.sub(r'\n{3,}', '\n\n', content).strip()

                    relative_path = os.path.relpath(root, source_directory)
                    updated_subfolder_path = os.path.join(source_directory, "updated", relative_path)
                    os.makedirs(updated_subfolder_path, exist_ok=True)

                    output_filepath = os.path.join(updated_subfolder_path, filename)

                    with open(output_filepath, 'w', encoding='utf-8') as f:
                        f.write(content)
                        
                    print(f"  -> Cleaned: {filename}")
                    updated_files_count += 1

    print(f"\nComplete! {updated_files_count} files were successfully cleaned.")

if __name__ == "__main__":
    delete_sentences_in_srt_files()

Step-by-Step Code Breakdown

1. The Encoding Nightmare

If you download subtitles from the internet, you will quickly learn they are a mess. Some are saved in standard UTF-8, but older files might use Windows CP1252 or Latin-1.

Robust Try/Except Loops

Expert Tip: A naive script assumes utf-8 and will completely crash on episode 12 when it hits a weird character encoding. Our script iterates through an array of encodings = ['utf-8', 'utf-8-sig', 'latin-1', 'cp1252']. If it throws a UnicodeDecodeError, it gracefully catches it and tries the next encoding format on the list.

2. The Regex Laser Scalpel

If str.replace() is a blunt machete, Regular Expressions (Regex) are a laser scalpel. Here is exactly what our complex pattern is doing:

  • ^\d+\n: Finds the block index number (e.g., "45") followed by a new line.
  • \d{2}:\d{2}:\d{2},\d{3} --> ...: Finds the exact timestamp format (e.g., 00:01:23,400).
  • .*?: Scans through the text until it hits your target sentence (the escaped_sentence).
  • (?:\n\n|\Z): Selects everything up until it hits a double line break (the end of the block) or the end of the file.

Using re.sub(), we target this entire mass of text and replace it with nothing (''), cleanly excising the ghost block from existence.

3. The "Sandbox" Principle for Safe Data

A core rule of data manipulation is never mutate the source data. What if you made a typo in the sentence you wanted to delete? Overwriting the original files would destroy your entire subtitle library.

Instead, we use os.makedirs and os.path.relpath to perfectly clone your original folder structure into a safe updated/ sandbox directory. Your originals remain untouched, and the cleaned files are neatly organized exactly where they belong.

RP

About Rohit Patil

Rohit Patil is a Toronto-based Senior Web Performance & Security Architect specializing in CDN engineering, Akamai, Cloudflare, WAF, and Edge Security.