class SRT::File
Attributes
lines[W]
Public Class Methods
parse(input, options = {})
click to toggle source
# File lib/srt/file.rb, line 3 def self.parse(input, options = {}) @debug = options.fetch(:debug, false) if input.is_a?(String) parse_string(input) elsif input.is_a?(::File) parse_file(input) else raise "Invalid input. Expected a String or File, got #{input.class.name}." end end
parse_file(srt_file)
click to toggle source
# File lib/srt/file.rb, line 14 def self.parse_file(srt_file) parse_string ::File.open(srt_file, 'rb') { |f| srt_file.read } end
parse_string(srt_data)
click to toggle source
# File lib/srt/file.rb, line 18 def self.parse_string(srt_data) result = new line = Line.new split_srt_data(srt_data).each_with_index do |str, index| begin if str.strip.empty? result.lines << line unless line.empty? line = Line.new elsif !line.error if line.sequence.nil? line.sequence = str.to_i elsif line.start_time.nil? if mres = str.match(/(?<start_timecode>[^[[:space:]]]+) -+> (?<end_timecode>[^[[:space:]]]+) ?(?<display_coordinates>X1:\d+ X2:\d+ Y1:\d+ Y2:\d+)?/) if (line.start_time = Parser.timecode(mres["start_timecode"])) == nil line.error = "#{index}, Invalid formatting of start timecode, [#{mres["start_timecode"]}]" $stderr.puts line.error if @debug end if (line.end_time = Parser.timecode(mres["end_timecode"])) == nil line.error = "#{index}, Invalid formatting of end timecode, [#{mres["end_timecode"]}]" $stderr.puts line.error if @debug end if mres["display_coordinates"] line.display_coordinates = mres["display_coordinates"] end else line.error = "#{index}, Invalid Time Line formatting, [#{str}]" $stderr.puts line.error if @debug end else line.text << str.strip end end rescue line.error = "#{index}, General Error, [#{str}]" $stderr.puts line.error if @debug end end result end
split_srt_data(srt_data)
click to toggle source
Ruby often gets the wrong encoding for a file and will throw errors on `split` for invalid byte sequences. This chain of fallback encodings lets us get something that works.
# File lib/srt/file.rb, line 65 def self.split_srt_data(srt_data) begin srt_data.split(/\n/) + ["\n"] rescue begin srt_data = srt_data.unpack("C*").pack("U*") srt_data.force_encoding('utf-8').split(/\n/) + ["\n"] rescue srt_data.force_encoding('iso-8859-1').split(/\n/) + ["\n"] end end end
Public Instance Methods
append(options)
click to toggle source
# File lib/srt/file.rb, line 78 def append(options) if options.length == 1 && options.values[0].class == self.class reshift = Parser.timecode(options.keys[0]) || (lines.last.end_time + Parser.timespan(options.keys[0])) renumber = lines.last.sequence options.values[0].lines.each do |line| lines << line.clone lines.last.sequence += renumber lines.last.start_time += reshift lines.last.end_time += reshift end end self end
errors()
click to toggle source
# File lib/srt/file.rb, line 231 def errors lines.collect { |l| l.error if l.error }.compact end
lines()
click to toggle source
# File lib/srt/file.rb, line 227 def lines @lines ||= [] end
split(options)
click to toggle source
# File lib/srt/file.rb, line 94 def split(options) options = { :timeshift => true, :renumber => true }.merge(options) split_points = [] if (options[:at]) split_points = [options[:at]].flatten.map{ |timecode| Parser.timecode(timecode) }.sort elsif (options[:every]) interval = Parser.timecode(options[:every]) max = lines.last.end_time (interval..max).step(interval){ |t| split_points << t } end if (split_points.count > 0) split_offsprings = [File.new] reshift = 0 renumber = 0 lines.each do |line| if split_points.empty? || line.end_time <= split_points.first cloned_line = line.clone cloned_line.sequence -= renumber if options[:renumber] if options[:timeshift] cloned_line.start_time -= reshift cloned_line.end_time -= reshift end split_offsprings.last.lines << cloned_line elsif line.start_time < split_points.first cloned_line = line.clone cloned_line.sequence -= renumber if options[:renumber] if options[:timeshift] cloned_line.start_time -= reshift cloned_line.end_time = split_points.first - reshift end split_offsprings.last.lines << cloned_line renumber = line.sequence - 1 reshift = split_points.first split_points.delete_at(0) split_offsprings << File.new cloned_line = line.clone cloned_line.sequence -= renumber if options[:renumber] if options[:timeshift] cloned_line.start_time = 0 cloned_line.end_time -= reshift end split_offsprings.last.lines << cloned_line else renumber = line.sequence - 1 reshift = split_points.first split_points.delete_at(0) split_offsprings << File.new cloned_line = line.clone cloned_line.sequence -= renumber if options[:renumber] if options[:timeshift] cloned_line.start_time -= reshift cloned_line.end_time -= reshift end split_offsprings.last.lines << cloned_line end end end split_offsprings end
timeshift(options)
click to toggle source
# File lib/srt/file.rb, line 163 def timeshift(options) if options.length == 1 if options[:all] && (seconds = Parser.timespan(options[:all])) lines.each do |line| line.start_time += seconds line.end_time += seconds end elsif (original_framerate = Parser.framerate(options.keys[0])) && (target_framerate = Parser.framerate(options.values[0])) time_ratio = original_framerate / target_framerate lines.each do |line| line.start_time *= time_ratio line.end_time *= time_ratio end end elsif options.length == 2 origins, targets = options.keys, options.values [0,1].each do |i| if origins[i].is_a?(String) && Parser.id(origins[i]) origins[i] = lines[Parser.id(origins[i]) - 1].start_time elsif origins[i].is_a?(String) && Parser.timecode(origins[i]) origins[i] = Parser.timecode(origins[i]) end if targets[i].is_a?(String) && Parser.timecode(targets[i]) targets[i] = Parser.timecode(targets[i]) elsif targets[i].is_a?(String) && Parser.timespan(targets[i]) targets[i] = origins[i] + Parser.timespan(targets[i]) end end time_rescale_factor = (targets[1] - targets[0]) / (origins[1] - origins[0]) time_rebase_shift = targets[0] - origins[0] * time_rescale_factor lines.each do |line| line.start_time = line.start_time * time_rescale_factor + time_rebase_shift line.end_time = line.end_time * time_rescale_factor + time_rebase_shift end end if lines.reject! { |line| line.end_time < 0 } lines.sort_by! { |line| line.sequence } lines.each_with_index do |line, index| line.sequence = index + 1 line.start_time = 0 if line.start_time < 0 end end end
to_s(time_str_function=:time_str)
click to toggle source
# File lib/srt/file.rb, line 212 def to_s(time_str_function=:time_str) lines.map { |l| l.to_s(time_str_function) }.join("\n") end
to_webvtt()
click to toggle source
# File lib/srt/file.rb, line 216 def to_webvtt header = <<eos WEBVTT X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000 eos header + to_s(:webvtt_time_str) end