module CommonUtils

Constants

CREDITS
SCC_DEFAULT_FRAME_RATE

Public Instance Methods

create_file(src_type, dest_type, output_file, target_lang) click to toggle source

Method to create the file with basic header informations which can be further updated with the transformed caption details by respective implementations

  • src_type - Source caption type. Refer to AllFather::TYPE_SCC type constants

  • dest_type - Target caption type. Refer to AllFather::TYPE_SCC type constants

  • output_file - Creates this output_file to which type specific

information would be dumped into
  • target_lang - Target lang of the output_file

Returns

true if the file is created with right headers and false otherwise

# File lib/utils/common_utils.rb, line 24
  def create_file(src_type, dest_type, output_file, target_lang)
    file = nil
    done = false
    begin
      # Create the file in overwrite mode
      file = File.open(output_file, "w")

      # Dump the initial info into the file to start off with
      case dest_type
      when AllFather::TYPE_SCC
        file.write("Scenarist_SCC V1.0\n\n")

      when AllFather::TYPE_SRT
        file.write("NOTE #{CREDITS}\n\n")

      when AllFather::TYPE_VTT
        file.write("WEBVTT\n\n")
        file.write("NOTE #{CREDITS}\n\n")

      when AllFather::TYPE_TTML
        target_lang ||= ""
        # TODO: Move this to a template file and load from there !!
        data = <<-EOF
<tt xml:lang="" xmlns="http://www.w3.org/ns/ttml">
  <head>
    <metadata xmlns:ttm="http://www.w3.org/ns/ttml#metadata">
      <ttm:desc>#{CREDITS}</ttm:desc>
    </metadata>
  </head>
  <body>
    <div xml:lang=\"#{target_lang}\">
          EOF
          file.write(data)

      when AllFather::TYPE_DFXP
        target_lang ||= ""
        data = <<-EOF
<tt xml:lang="" xmlns="http://www.w3.org/2004/11/ttaf1">
  <head>
    <meta xmlns:ttm="http://www.w3.org/2004/11/ttaf1#metadata">
      <ttm:desc>#{CREDITS}</ttm:desc>
    </meta>
  </head>
  <body>
    <div xml:lang=\"#{target_lang}\">
          EOF
          file.write(data)
      else
        raise AllFather::InvalidInputException.new("Not a valid type; Failed to create output file for type #{type}")
      end
      done = true
    ensure
      file.close if file rescue nil
    end
    done
  end
extension_from_type(type) click to toggle source

Method to return a valid extension for a given caption type Refer to `AllFather#VALID_FILES`

  • type - Must be one of the valid type defined in `AllFather`

Raises

InvalidInputException if a valid type is not provided

# File lib/utils/common_utils.rb, line 90
def extension_from_type(type)
  case type 
  when AllFather::TYPE_SCC
    return AllFather::VALID_FILES[0]
  when AllFather::TYPE_SRT
    return AllFather::VALID_FILES[1]
  when AllFather::TYPE_VTT
    return AllFather::VALID_FILES[2]
  when AllFather::TYPE_TTML
    return AllFather::VALID_FILES[3]
  when AllFather::TYPE_DFXP
    return AllFather::VALID_FILES[4]
  else
    raise AllFather::InvalidInputException.new("Not a valid type; Failed to create output file for type #{type}")
  end
end
new_cue(model, target_type, last_cue = false) click to toggle source

Method to return the cue info of the caption based on the model and target caption type which can be used by the caller's transformation routine

  • model - `CueInfo` instance which is caption agnostic details of a cue

  • target_type - The target type to which the new cue is to be generated

  • last_cue - true for last cue and false otherwise.

# File lib/utils/common_utils.rb, line 139
def new_cue(model, target_type, last_cue = false)
  message = nil
  case target_type
  when AllFather::TYPE_SCC
    start_unit = model.start_time_units
    h = start_unit[0].to_s.rjust(2, "0")
    m = start_unit[1].to_s.rjust(2, "0")
    s = start_unit[2].to_s.rjust(2, "0")
    ms = start_unit[3]
    # Convert to Frames assuming a framerate of 23.976
    # Pad 0 if frames is <= 9
    frames = ((ms.to_f * SCC_DEFAULT_FRAME_RATE) / 1000.0).to_i.to_s.rjust(2, "0").to_i
    # TODO: Might have to strip off non-english characters here
    message = "#{h}:#{m}:#{s}:#{frames} " + scc_encode(model.message)
  when AllFather::TYPE_VTT, AllFather::TYPE_SRT
    start_unit = model.start_time_units
    end_unit = model.end_time_units
    message = ""
    if model.sequence
      message = model.sequence + "\n"
    else
      message = model.index.to_s + "\n"
    end
    delimiter_added = false
    [start_unit, end_unit].each do |unit|
      h = unit[0].to_s.rjust(2, "0")
      m = unit[1].to_s.rjust(2, "0")
      s = unit[2].to_s.rjust(2, "0")
      ms = unit[3]
      if ms < 100
        ms = ms.to_s.rjust(3, "0")
      end
      if target_type == AllFather::TYPE_VTT
        message << "#{h}:#{m}:#{s}:#{ms}"
      else
        message << "#{h}:#{m}:#{s},#{ms}"
      end
      unless delimiter_added
        message << " --> "
        delimiter_added = true
      end
    end
    message << "\n"
    message << model.message
    message << "\n"
    message << "\n" unless model.message.end_with?("\n")
  when AllFather::TYPE_TTML, AllFather::TYPE_DFXP
    start_unit = model.start_time_units
    end_unit = model.end_time_units
    h = start_unit[0].to_s.rjust(2, "0")
    m = start_unit[1].to_s.rjust(2, "0")
    s = start_unit[2].to_s.rjust(2, "0")
    ms = start_unit[3]
    begin_time = "#{h}:#{m}:#{s}"
    begin_time << ".#{ms.to_s.rjust(3, "0")}" if ms > 0
    h = end_unit[0].to_s.rjust(2, "0")
    m = end_unit[1].to_s.rjust(2, "0")
    s = end_unit[2].to_s.rjust(2, "0")
    ms = end_unit[3]
    end_time = "#{h}:#{m}:#{s}"
    end_time << ".#{ms.to_s.rjust(3, "0")}" if ms > 0
    message = "<p begin=\"#{begin_time}\" end=\"#{end_time}\">#{model.message.encode(:xml => :text)}</p>"
    message << "</div>\n</body>\n</tt>" if last_cue
  end
  message
end
scc_encode(free_text) click to toggle source

Method to encode a text to SCC format

  • free_text - Text that needs to be encoded

Returns

The encoded string that can be added to SCC file

# File lib/utils/common_utils.rb, line 115
def scc_encode(free_text)
  encoded_str = ""
  count = 0
  free_text.each_byte do |char|
    count += 1
    binval = char.to_s(2).count("1") % 2 == 0 ? (char.to_i | 128 ).to_s(2) : char.to_s(2)
    encode_char = binval.to_i(2).to_s(16)
    if ((count > 0) && (count % 2 == 0))
      encoded_str << encode_char << " "
    else
      encoded_str << encode_char
    end
  end
  encoded_str
end
time_details(time_stamp, type) click to toggle source

Method that normalizes the timestamps from various different caption formats into a caption agnostic format

  • time_stamp - The timestamp parsed from the caption file for a given caption type

  • type - A valid caption type. Refer to `AllFather` for valid types

# File lib/utils/common_utils.rb, line 213
def time_details(time_stamp, type)
  h = m = s = ms = nil
  elapsed_seconds = nil
  case type 
  when AllFather::TYPE_SCC
    tokens = time_stamp.split(":")
    h = tokens[0].to_i
    m = tokens[1].to_i
    s = tokens[2].to_i
    frames = tokens[3].to_i
    ms = (frames * 1000 / SCC_DEFAULT_FRAME_RATE).round(0).to_s.rjust(3, "0").to_i
    if ms >= 1000
      ms = 999
    end
  when AllFather::TYPE_SRT
    tokens = time_stamp.split(",")
    ms = tokens[1].to_i
    tokens = tokens[0].split(":")
    h = tokens[0].to_i
    m = tokens[1].to_i
    s = tokens[2].to_i
  when AllFather::TYPE_VTT
    tokens = time_stamp.split(".")
    ms = tokens[1].to_i
    tokens = tokens[0].split(":")
    if tokens.size == 2
      h = 0
      m = tokens[0].to_i
      s = tokens[1].to_i
    else
      h = tokens[0].to_i
      m = tokens[1].to_i
      s = tokens[2].to_i
    end
  when AllFather::TYPE_TTML, AllFather::TYPE_DFXP
    # We support only clock-time without framerate / tickrate and only media timebase
    # For offset hence we don't support frames / ticks
    tokens = time_stamp.split(":")
    if tokens.size > 1
      if tokens.size > 3
        # This is specified with frames and/or subframes. Unsupported
        raise AllFather::InvalidInputException.new("TTML file with clock-time referencing frames / ticks is unsupported")
      end
      h = tokens[0].to_i
      m = tokens[1].to_i
      ms_tokens = tokens[2].split(".")
      if ms_tokens.size == 1
        ms = 0
      else
        ms = ms_tokens[1].to_i
      end
      s = ms_tokens[0].to_i
    else
      # Parsing in offset mode
      if time_stamp.end_with?("ms")
        unit = "ms"
        time_with_no_unit = time_stamp[0, time_stamp.size - 2]
      else
        unit = time_stamp[time_stamp.size - 1]
        time_with_no_unit = time_stamp[0, time_stamp.size - 1]
      end
      case unit 
      when "m"
        time_with_no_unit = time_with_no_unit.to_f * 60
      when "h"
        time_with_no_unit = time_with_no_unit.to_f * (60 * 60)
      when "s"
        # do nothing
      when "ms"
        time_with_no_unit = time_with_no_unit.to_f / 1000.0
      else
        # Fail out f / t
        raise AllFather::InvalidInputException.new("TTML file with offset-time referencing frames / ticks is unsupported")
      end
      tokens = time_with_no_unit.to_s.split(".")
      h = m = 0
      if tokens.size == 1
        s = time_with_no_unit
        ms = 0
      else
        s = tokens[0].to_i
        ms = tokens[1].to_i
      end
      h = s / 3600
      m = (s / 60) % 60
      s = s % 60
    end
  end
  elapsed_seconds = (h * 60 * 60) + (m * 60) + s
  return [h, m, s, ms, elapsed_seconds]
end
write_cue(model, file_map, last_cue = false) click to toggle source

Method to write the cue details to the output files

  • model - Cue instance

  • file_map - Hash of files for each caption type

  • last_cue - true for last cue and false otherwise

# File lib/utils/common_utils.rb, line 313
def write_cue(model, file_map, last_cue = false)
  file_map.each do |type, file_path|
    File.open(file_path, "a") do |f|
      f.puts new_cue(model, type, last_cue)
    end
  end
  if last_cue
    # Pretty print the output for ttml & dfxp
    file_map.each do |type, file_path|
      next unless [AllFather::TYPE_DFXP, AllFather::TYPE_TTML].include?(type)
      file = File.open(file_path, "r")
      xml_doc = Nokogiri::XML(file, &:noblanks)
      File.write(file_path, xml_doc.to_s)
    end
  end
end