Subtítulos - SRT - Python - Creadores de contenido
5 min lectura

Subtítulos SRT adelantados o atrasados: corrígelos con un script probado

Si todos tus subtítulos SRT aparecen igual de tarde o temprano, desplaza sus tiempos con este script Python probado. Aprende a distinguirlo de una deriva progresiva.

Subtítulos SRT adelantados o atrasados: corrígelos con un script probado

Generaste subtítulos con IA para un video corto y cada frase aparece 1,2 segundos tarde. Corregir línea por línea es innecesario si el error es constante: basta desplazar todas las marcas de tiempo del archivo SRT. Antes de tocarlo, compara un subtítulo cerca del inicio con otro cerca del final. Si ambos llegan 1,2 segundos tarde, el desplazamiento sirve; si el error crece, el problema es de velocidad o de edición y este script no lo arregla.

Un archivo SRT contiene bloques numerados, cada uno con hora de inicio y fin en formato horas:minutos:segundos,milisegundos. Es texto separado del video; por eso puedes cambiar el tiempo sin recomprimir imagen ni audio. La Biblioteca del Congreso de Estados Unidos describe esa estructura y advierte que el formato no fija una única codificación. El ejemplo de abajo lee UTF-8 con o sin BOM y escribe UTF-8.

Cómo diferenciar desfase fijo de deriva progresiva en subtítulos SRT
Mide al inicio y al final: el script solo corrige un desfase constante.

Primero mide, no adivines

Reproduce el video con el SRT original y pausa donde escuchas el inicio de una frase. Si el sonido comienza en 00:00:10,000 pero el subtítulo aparece en 00:00:11,200, está 1.200 milisegundos tarde. Para adelantarlo, usa un desplazamiento negativo de -1200. Si aparece antes que la voz, usa uno positivo. Repite la medición a mitad y final: un offset constante mueve todos los subtítulos igual; no estira ni comprime el tiempo.

Script probado: desplaza sin pisar el original

Guarda el siguiente código como shift_srt.py. Exige un archivo de salida distinto, rechaza marcas de tiempo inválidas y detiene la operación si algún subtítulo quedaría antes de cero. Mantiene los números y el texto de cada bloque. Está pensado para SRT simple; no procesa WebVTT ni archivos con anotaciones extra en la línea de tiempo.

#!/usr/bin/env python3
"""Shift every SRT cue by a constant number of milliseconds."""
import argparse
import re
from pathlib import Path

TIME = re.compile(r"^(\d{2,}):(\d{2}):(\d{2}),(\d{3})$")
LINE = re.compile(r"^(\S+) --> (\S+)$")


def milliseconds(stamp):
    match = TIME.fullmatch(stamp)
    if not match:
        raise ValueError(f"Invalid SRT timestamp: {stamp}")
    hours, minutes, seconds, millis = map(int, match.groups())
    if minutes > 59 or seconds > 59:
        raise ValueError(f"Invalid SRT timestamp: {stamp}")
    return ((hours * 60 + minutes) * 60 + seconds) * 1000 + millis


def timestamp(value):
    if value < 0:
        raise ValueError("A cue would start before 00:00:00,000")
    hours, rem = divmod(value, 3_600_000)
    minutes, rem = divmod(rem, 60_000)
    seconds, millis = divmod(rem, 1_000)
    return f"{hours:02}:{minutes:02}:{seconds:02},{millis:03}"


def shift(text, offset):
    result, cues = [], 0
    for line in text.splitlines():
        if "-->" not in line:
            result.append(line)
            continue
        match = LINE.fullmatch(line)
        if not match:
            raise ValueError(f"Invalid SRT cue: {line}")
        start, end = map(milliseconds, match.groups())
        if end <= start:
            raise ValueError(f"Cue end must follow start: {line}")
        result.append(f"{timestamp(start + offset)} --> {timestamp(end + offset)}")
        cues += 1
    if not cues:
        raise ValueError("No SRT cues found")
    return "\n".join(result) + "\n", cues


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("input", type=Path)
    parser.add_argument("output", type=Path)
    parser.add_argument("--ms", type=int, required=True, help="positive delays, negative advances")
    args = parser.parse_args()
    if args.input.resolve() == args.output.resolve():
        parser.error("Use a different output path to preserve the original")
    try:
        fixed, cues = shift(args.input.read_text(encoding="utf-8-sig"), args.ms)
        args.output.write_text(fixed, encoding="utf-8")
    except (OSError, ValueError) as error:
        parser.error(str(error))
    print(f"Shifted {cues} cues by {args.ms} ms -> {args.output}")


if __name__ == "__main__":
    main()

Ejemplo: para adelantar todo 1,2 segundos, ejecuta python3 shift_srt.py original.srt corregido.srt --ms -1200. Para retrasarlo 750 milisegundos, usa --ms 750. No cambies el archivo original hasta validar el resultado en tu editor o reproductor.

Qué comprobé y qué debes comprobar tú

Probé el script localmente con dos bloques: 00:00:02,500 pasó a 00:00:01,300 con -1200, y 00:01:00,000 pasó a 00:00:58,800. También comprobé que un desplazamiento que deja un tiempo negativo se rechaza. Es una prueba del código, no de la sincronía de tu video. Abre el archivo nuevo y escucha al menos tres puntos: inicio, mitad y final. Mira que no se corten frases, que el final de un bloque no se superponga con el siguiente y que la plataforma importe el SRT.

Si al inicio el error es 1,2 segundos y al final 3,4, el desfase es progresivo. Puede deberse a un cambio de velocidad, a un corte de video posterior a la transcripción o a una base temporal distinta. Ahí toca volver a sincronizar por tramos o regenerar subtítulos sobre la versión final del audio. Si el texto está mal transcrito, mover tiempos tampoco lo corrige: escucha y revisa nombres, cifras y citas antes de publicar. Para subtítulos ya quemados en la imagen, necesitas editar el video; un SRT externo no borra esos píxeles.

Esta guía es para creadores que ya tienen un SRT editable. Si todavía necesitas revisar exactitud textual, empieza por nuestra guía para comprobar transcripciones de IA. El valor aquí es una corrección de sincronía medible, reversible y verificable en tu propio material.

Recibe lo nuevo en tu correo

Workshops gratis, herramientas y novedades de IA aplicadas a negocio, sin humo.

🎓 Workshops en vivo🛠️ Herramientas nuevas⚡ Novedades de IA

Sin spam. Te das de baja cuando quieras.

Comentarios

Aún no hay comentarios. Sé el primero.

Deja un comentario