Skip to content

Instantly share code, notes, and snippets.

@Madh93
Last active August 1, 2016 15:59
Show Gist options
  • Save Madh93/65a58063c24a277435ef5e926eafd216 to your computer and use it in GitHub Desktop.
Save Madh93/65a58063c24a277435ef5e926eafd216 to your computer and use it in GitHub Desktop.
Sync Subtitles

Sync Subs

Synchronize .srt subtitles

Requirements

  • Ruby
Usage

Usage: ruby syncsubs.rb [options]

  • -i, --input NAME
    • SRT input file name
  • -o, --output NAME
    • SRT output file name
  • -s, --sync TIME
    • Sync time in milliseconds
  • -h, --help
    • Prints this help

Examples:

ruby syncsubs.rb -i input.srt -o output.srt -s -500
ruby syncsubs.rb -i input.srt -o output.srt -s 1750
#! /usr/bin/env ruby
# NAME : Sync Subs
# DESCRIPTION : Synchronize .srt subtitles
# AUTHOR : Madh93 (Miguel Hernandez)
# VERSION : 0.0.1
# LICENSE : GNU General Public License v3
# USAGE : ruby syncsubs.rb
require 'optparse'
Options = Struct.new(:input, :output, :sync)
# Extends String
class String
def to_milli
a = [1, 1000, 60_000, 3_600_000] * 2
split(/[:\.]/).map { |time| time.to_i * a.pop }.inject(&:+)
end
end
# Extends Fixnum
class Fixnum
def to_time
Time.at(to_f / 1000).utc.strftime('%H:%M:%S.%L')
end
end
module SyncSubs
# Argument Parser Class
class ArgParser
def self.parse(options)
args = Options.new
opt_parser = OptionParser.new do |opts|
opts.banner = 'Usage: ruby syncsubs.rb [options]'
opts.on('-i', '--input NAME', 'SRT input file name') do |f|
args.input = f
end
opts.on('-o', '--output NAME', 'SRT output file name') do |f|
args.output = f
end
opts.on('-s', '--sync TIME', 'Sync time in milliseconds') do |t|
args.sync = t.to_i
end
opts.on('-h', '--help', 'Prints this help') do
puts opts
puts 'Example: ruby syncsubs.rb -i input.srt -o output.srt -s -500'
exit
end
end
opt_parser.parse!(options)
if args.count(&:nil?) != 0
puts 'Try: ruby syncsubs.rb --help'
exit
end
args
end
end
end
module SyncSubs
# Sync Subs class
class SyncSubs
attr_accessor :input, :output, :sync
def initialize
yield self if block_given?
end
def synchronize!
# Read input subtitles
begin
data = File.readlines(@input)
rescue
puts "'#{@input}' can't be opened"
exit
end
# Synchronize times
data.map! { |line| line.include?('-->') ? sync_time(line) : line }
# Export to output subtitles file
File.open("#{@output}", 'w+') do |f|
data.each { |line| f.write(line) }
end
end
private
def sync_time(str)
# Split and remove commas
str = str.tr(',', '.').split
# Apply new sync time
start = str[0].to_milli + @sync
ending = str[2].to_milli + @sync
if start < 0 || ending < 0
puts "New sync time below 0... exiting."
exit
end
"#{start.to_time} #{str[1]} #{ending.to_time}\n"
end
end
end
# Main
args = SyncSubs::ArgParser.parse(ARGV)
SyncSubs::SyncSubs.new do |s|
s.input = args.input
s.output = args.output
s.sync = args.sync
end.synchronize!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment