Subtitula videos en Ruby con herramientas de código abierto
Agregar subtítulos a los videos mejora notablemente la accesibilidad, la participación de los espectadores y el SEO. Los desarrolladores de Ruby tienen acceso a varias herramientas potentes de código abierto que simplifican la integración y la edición de subtítulos. Veamos cómo puedes aprovecharlas de forma eficaz.
Por qué importan los subtítulos
Los subtítulos no solo benefician a los espectadores con discapacidad auditiva: también mejoran la comprensión de quienes no son hablantes nativos, refuerzan el SEO al hacer que el contenido sea buscable y pueden aumentar la retención de espectadores. Ofrecer subtítulos se está convirtiendo en una práctica estándar para el contenido inclusivo y accesible.
Herramientas de Ruby de código abierto para subtítulos
Ruby ofrece varias bibliotecas robustas para gestionar subtítulos, principalmente mediante la conexión con el potente framework multimedia FFmpeg o la manipulación directa de los archivos de subtítulos:
- streamio-ffmpeg: un wrapper de Ruby completo para FFmpeg que permite realizar diversas tareas de procesamiento de video, como incrustar subtítulos en los videos o convertir formatos de subtítulos.
- srt: una gema dedicada a analizar, manipular y generar archivos de subtítulos SRT (SubRip Text), el formato de subtítulos más común.
Veamos algunos ejemplos prácticos con estas herramientas.
Agregar subtítulos con streamio-ffmpeg
La gema streamio-ffmpeg ofrece una interfaz de Ruby cómoda para FFmpeg, lo que facilita incrustar
subtítulos en los videos. A este proceso se le suele llamar «quemar» subtítulos, porque pasan a
formar parte de los fotogramas del video.
Usa una versión de Ruby con mantenimiento y una compilación de FFmpeg que incluya el filtro
subtitles de libass, el codificador libx264 y FFprobe. Verifica el filtro con
ffmpeg -filters; no se incluye en todas las compilaciones de paquetes. Instala estas versiones
de gemas probadas:
gem install streamio-ffmpeg:3.0.2 srt:0.1.5
Guarda esta implementación compartida como subtitle_tools.rb. Todos los ejemplos siguientes la
reutilizan. El directorio temporal mantiene el nombre del archivo de subtítulos fuera de la sintaxis
de filtros de FFmpeg y aísla el comportamiento de sobrescritura del wrapper respecto de las
salidas existentes. La publicación usa un enlace duro en el sistema de archivos de salida; ese
sistema de archivos debe admitir enlaces duros.
require 'streamio-ffmpeg'
require 'srt'
require 'tmpdir'
module SubtitleTools
def self.load_srt(path)
raise ArgumentError, 'Subtitle input must be a regular file' unless File.file?(path)
text = File.binread(path, 1024 * 1024 + 1)
raise ArgumentError, 'Subtitle input exceeds 1 MiB' if text.bytesize > 1024 * 1024
text.force_encoding(Encoding::UTF_8)
raise ArgumentError, 'Subtitles must be valid UTF-8' unless text.valid_encoding?
subtitles = SRT::File.parse(text)
if subtitles.lines.empty? || !subtitles.errors.empty?
raise ArgumentError, 'Invalid or empty SRT input'
end
unless subtitles.lines.all? { |line| line.start_time >= 0 && line.end_time > line.start_time }
raise ArgumentError, 'Invalid subtitle timing'
end
subtitles
end
def self.with_output(output_path, extension)
output = File.expand_path(output_path)
if File.exist?(output) || File.symlink?(output)
raise ArgumentError, 'Output must not exist'
end
Dir.mktmpdir('.subtitles-', File.dirname(output)) do |temporary|
candidate = File.join(temporary, "result#{extension}")
yield candidate, temporary
raise IOError, 'No nonempty output produced' unless File.file?(candidate) && File.size(candidate) > 0
File.link(candidate, output)
end
end
def self.burn(video_path, subtitle_path, output_path)
subtitles = load_srt(subtitle_path)
movie = FFMPEG::Movie.new(File.expand_path(video_path))
raise ArgumentError, 'Invalid video input' unless movie.valid?
with_output(output_path, '.mp4') do |_candidate, temporary|
File.write(File.join(temporary, 'captions.srt'), subtitles.to_s)
# Fixed basenames avoid FFmpeg's separate filter-path escaping rules.
Dir.chdir(temporary) do
movie.transcode('result.mp4', {
video_codec: 'libx264', audio_codec: 'aac',
custom: ['-nostdin', '-vf', 'subtitles=captions.srt', '-threads', '2']
})
end
end
end
def self.write_srt(subtitles, output_path)
with_output(output_path, '.srt') { |candidate| File.write(candidate, subtitles.to_s) }
end
def self.convert_to_vtt(subtitle_path, output_path)
subtitles = load_srt(subtitle_path)
with_output(output_path, '.vtt') do |candidate, temporary|
input = File.join(temporary, 'captions.srt')
File.write(input, subtitles.to_s)
unless system('ffmpeg', '-nostdin', '-n', '-i', input, '-c:s', 'webvtt', candidate)
raise IOError, 'Subtitle conversion failed'
end
end
end
end
Esta implementación está pensada para un proceso de trabajo de CLI secuencial: Dir.chdir afecta
a todo el proceso de Ruby, así que no lo llames de forma concurrente desde varios hilos. Usa
procesos aislados e independientes cuando necesites escalar. FFmpeg y el analizador deben ejecutarse
igualmente con límites de recursos para los archivos no confiables; la seguridad del nombre de
archivo no equivale a un entorno aislado para el procesamiento multimedia. La función auxiliar
vuelve a codificar el video y el audio en MP4.
Úsala desde un script en el mismo directorio:
require_relative 'subtitle_tools'
SubtitleTools.burn('input.mp4', 'subtitles.srt', 'output_with_subs.mp4')
puts 'Subtitles added'
Manipular archivos SRT con la gema SRT
La gema srt te permite analizar, modificar y crear archivos de subtítulos SRT mediante código.
Esto resulta útil para tareas como ajustar los tiempos, corregir el texto o generar subtítulos desde
cero.
require_relative 'subtitle_tools'
subtitles = SubtitleTools.load_srt('subtitles.srt')
subtitles.timeshift(all: '+2.5s')
SubtitleTools.write_srt(subtitles, 'modified.srt')
puts 'Subtitles shifted by 2.5 seconds'
El método timeshift de la gema recibe un hash de opciones, no un retardo numérico. Su analizador
puede registrar errores en las líneas de subtítulos en lugar de lanzar un SRT::Error específico;
el cargador compartido revisa esos errores registrados y valida que cada cue tenga una duración
positiva antes de procesarlo.
Convertir formatos de subtítulos
Aunque la gema srt se centra específicamente en el formato SRT, puedes aprovechar FFmpeg a
través de la gema streamio-ffmpeg para convertir entre varios formatos de subtítulos. Esto resulta
especialmente útil para los reproductores de video web, que a menudo prefieren el formato WebVTT.
require_relative 'subtitle_tools'
SubtitleTools.convert_to_vtt('subtitles.srt', 'subtitles.vtt')
puts 'WebVTT subtitles saved'
La función auxiliar ejecuta FFmpeg con argumentos separados, comprueba su estado de salida y publica únicamente un resultado no vacío. Mantén juntas las elecciones de formato y de extensión cuando la adaptes a otro códec.
Manejo de errores en la integración de subtítulos
Deja que los fallos de las funciones auxiliares se propaguen hasta un único límite de la CLI. Guarda
esto como add_subtitles.rb junto a subtitle_tools.rb; devuelve un estado distinto de cero sin
exponer los errores sin procesar del decodificador ni del documento.
require_relative 'subtitle_tools'
abort 'Usage: ruby add_subtitles.rb <video.mp4> <subtitles.srt> <new-output.mp4>' unless ARGV.length == 3
begin
SubtitleTools.burn(*ARGV)
puts 'Subtitles added'
rescue StandardError
warn 'Subtitle processing failed. Check the inputs, output path, and FFmpeg installation.'
exit 1
end
Ejecuta ruby add_subtitles.rb input.mp4 subtitles.srt output_with_subs.mp4. Es posible que los diagnósticos del
decodificador sigan apareciendo en un registro local de FFmpeg; no envíes esos registros
directamente a los clientes web.
Formatos de subtítulos compatibles
Cuando trabajas con subtítulos, sobre todo con FFmpeg, puedes encontrarte con varios formatos. Entre los formatos principales están:
- SRT (SubRip Text):
.srt, el formato de texto plano con mayor compatibilidad. La gemasrty FFmpeg lo manejan directamente. - WebVTT (Web Video Text Tracks):
.vtt, el estándar para los subtítulos de video en HTML5, con soporte para estilos y posicionamiento. FFmpeg puede convertir a este formato y desde él. - ASS/SSA (Advanced SubStation Alpha / SubStation Alpha):
.ass/.ssa, formatos más avanzados que admiten estilos, posicionamiento y efectos complejos, y que se usan a menudo en los fansubs de anime. FFmpeg ofrece buen soporte. - SBV (SubViewer):
.sbv, un formato sencillo que usa YouTube. FFmpeg puede encargarse de la conversión.
FFmpeg ofrece amplio soporte para convertir entre estos y otros formatos. La gema srt
se centra específicamente en analizar y manipular el formato SRT.
Ejemplo práctico: procesamiento por lotes de videos con subtítulos
Aquí tienes un script que muestra cómo procesar varios videos de un directorio y agregar los subtítulos SRT correspondientes si se encuentran.
require_relative 'subtitle_tools'
def batch_process_videos(video_dir, subtitle_dir, output_dir)
video_files = Dir.children(video_dir).sort.filter_map do |name|
path = File.join(video_dir, name)
path if File.extname(name).downcase == '.mp4' && File.file?(path) && !File.symlink?(path)
end
raise ArgumentError, 'No MP4 files found' if video_files.empty?
Dir.mkdir(output_dir, 0700)
failures = 0
video_files.each do |video|
basename = File.basename(video, File.extname(video))
begin
SubtitleTools.burn(video, File.join(subtitle_dir, "#{basename}.srt"),
File.join(output_dir, "#{basename}_subtitled.mp4"))
puts "Converted #{basename}"
rescue StandardError
failures += 1
warn "Conversion failed for #{basename}"
end
end
failures.zero? ? 0 : 1
end
abort 'Usage: ruby batch_subtitles.rb <videos> <subtitles> <new-output-directory>' unless ARGV.length == 3
begin
exit batch_process_videos(*ARGV)
rescue StandardError
warn 'Cannot prepare batch. Check input directories and use a new output directory.'
exit 1
end
Este script recorre los archivos MP4 de un directorio determinado, busca un archivo SRT coincidente
en otro directorio y usa SubtitleTools.burn para procesar cada par, colocando la
salida en una carpeta designada. Guárdalo como batch_subtitles.rb. Los subtítulos ausentes o
inválidos cuentan como fallos, pero se intenta procesar igualmente los archivos restantes. Las
salidas completadas se conservan y los archivos temporales parciales se eliminan. El estado de
salida general indica si todas las conversiones se realizaron correctamente.
Conclusión
Agregar subtítulos a los videos con Ruby es sencillo si usas herramientas de código abierto como
streamio-ffmpeg (que se conecta con FFmpeg) y la gema srt. Estas bibliotecas simplifican
incrustar subtítulos en los videos, manipular archivos SRT y convertir entre formatos, lo que mejora
notablemente la accesibilidad de tu video y la participación de los espectadores. Recuerda manejar
con cuidado los posibles errores para lograr aplicaciones robustas.
Para flujos de trabajo de subtítulos más complejos, procesamiento de video a gran escala o soluciones en la nube, considera un servicio dedicado. El Robot 🤖 /video/subtitle de Transloadit ofrece capacidades avanzadas de integración de subtítulos como parte de su servicio de Encoding de video, y maneja distintos formatos y estilos de forma eficiente.
