class ActiveEncode::EngineAdapters::MatterhornAdapter

Constants

DEFAULT_ARGS

Public Instance Methods

cancel(id) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 19
def cancel(id)
  workflow_om = Rubyhorn.client.stop(id)
  build_encode(get_workflow(workflow_om))
end
create(input_url, options = {}) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 9
def create(input_url, options = {})
  workflow_id = options[:preset] || "full"
  workflow_om = Rubyhorn.client.addMediaPackageWithUrl(DEFAULT_ARGS.merge('workflow' => workflow_id, 'url' => input_url, 'filename' => File.basename(input_url), 'title' => File.basename(input_url)))
  build_encode(get_workflow(workflow_om))
end
find(id, _opts = {}) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 15
def find(id, _opts = {})
  build_encode(fetch_workflow(id))
end

Private Instance Methods

build_encode(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 51
def build_encode(workflow)
  return nil if workflow.nil?
  input_url = convert_input(workflow)
  input_url = get_workflow_title(workflow) if input_url.blank?
  encode = ActiveEncode::Base.new(input_url, convert_options(workflow))
  encode.id = convert_id(workflow)
  encode.state = convert_state(workflow)
  encode.current_operations = convert_current_operations(workflow)
  encode.percent_complete = calculate_percent_complete(workflow)
  encode.created_at = convert_created_at(workflow)
  encode.updated_at = convert_updated_at(workflow) || encode.created_at
  encode.output = convert_output(workflow, encode.options)
  encode.errors = convert_errors(workflow)

  encode.input.id = "presenter/source"
  encode.input.state = encode.state
  encode.input.created_at = encode.created_at
  encode.input.updated_at = encode.updated_at
  tech_md = convert_tech_metadata(workflow)
  [:width, :height, :duration, :frame_rate, :checksum, :audio_codec, :video_codec,
   :audio_bitrate, :video_bitrate].each do |field|
    encode.input.send("#{field}=", tech_md[field])
  end

  encode
end
calculate_percent_complete(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 204
def calculate_percent_complete(workflow)
  totals = {
    transcode: 70,
    distribution: 20,
    other: 10
  }

  completed_transcode_operations = workflow.xpath('//operation[@id="compose" and (@state="SUCCEEDED" or @state="SKIPPED")]').size
  total_transcode_operations = workflow.xpath('//operation[@id="compose"]').size
  total_transcode_operations = 1 if total_transcode_operations.zero?
  completed_distribution_operations = workflow.xpath('//operation[starts-with(@id,"distribute") and (@state="SUCCEEDED" or @state="SKIPPED")]').size
  total_distribution_operations = workflow.xpath('//operation[starts-with(@id,"distribute")]').size
  total_distribution_operations = 1 if total_distribution_operations.zero?
  completed_other_operations = workflow.xpath('//operation[@id!="compose" and not(starts-with(@id,"distribute")) and (@state="SUCCEEDED" or @state="SKIPPED")]').size
  total_other_operations = workflow.xpath('//operation[@id!="compose" and not(starts-with(@id,"distribute"))]').size
  total_other_operations = 1 if total_other_operations.zero?

  ((totals[:transcode].to_f / total_transcode_operations) * completed_transcode_operations) +
    ((totals[:distribution].to_f / total_distribution_operations) * completed_distribution_operations) +
    ((totals[:other].to_f / total_other_operations) * completed_other_operations)
end
convert_created_at(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 147
def convert_created_at(workflow)
  created_at = workflow.xpath('mediapackage/@start').last.to_s
  created_at.present? ? Time.parse(created_at).utc : nil
end
convert_current_operations(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 138
def convert_current_operations(workflow)
  current_op = workflow.xpath('//operation[@state!="INSTANTIATED"]/@description').last.to_s
  current_op.present? ? [current_op] : []
end
convert_errors(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 143
def convert_errors(workflow)
  workflow.xpath('//errors/error/text()').map(&:to_s)
end
convert_id(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 78
def convert_id(workflow)
  workflow.attribute('id').to_s
end
convert_input(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 99
def convert_input(workflow)
  # Need to do anything else since this is a MH url? and this disappears when a workflow is cleaned up
  workflow.xpath('mediapackage/media/track[@type="presenter/source"]/url/text()').to_s.strip
end
convert_options(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 169
def convert_options(workflow)
  options = {}
  options[:preset] = workflow.xpath('template/text()').to_s
  options[:stream_base] = workflow.xpath('//properties/property[@key="avalon.stream_base"]/text()').to_s if workflow.xpath('//properties/property[@key="avalon.stream_base"]/text()').present? # this is avalon-felix specific
  options
end
convert_output(workflow, options) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 112
def convert_output(workflow, options)
  outputs = []
  workflow.xpath('//track[@type="presenter/delivery" and tags/tag[text()="streaming"]]').each do |track|
    output = ActiveEncode::Output.new
    output.label = track.xpath('tags/tag[starts-with(text(),"quality")]/text()').to_s
    output.url = track.at("url/text()").to_s
    if output.url.start_with? "rtmp"
      output.url = File.join(options[:stream_base], MatterhornRtmpUrl.parse(output.url).to_path) if options[:stream_base]
    end
    output.id = track.at("@id").to_s

    tech_md = convert_track_metadata(track)
    [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
     :audio_bitrate, :video_bitrate, :file_size].each do |field|
      output.send("#{field}=", tech_md[field])
    end

    output.state = :completed
    output.created_at = convert_output_created_at(track, workflow)
    output.updated_at = convert_output_updated_at(track, workflow)

    outputs << output
  end
  outputs
end
convert_output_created_at(track, workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 157
def convert_output_created_at(track, workflow)
  quality = track.xpath('tags/tag[starts-with(text(),"quality")]/text()').to_s
  created_at = workflow.xpath("//operation[@id=\"compose\"][configurations/configuration[@key=\"target-tags\" and contains(text(), \"#{quality}\")]]/started/text()").to_s
  created_at.present? ? Time.at(created_at.to_i / 1000.0).utc : nil
end
convert_output_updated_at(track, workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 163
def convert_output_updated_at(track, workflow)
  quality = track.xpath('tags/tag[starts-with(text(),"quality")]/text()').to_s
  updated_at = workflow.xpath("//operation[@id=\"compose\"][configurations/configuration[@key=\"target-tags\" and contains(text(), \"#{quality}\")]]/completed/text()").to_s
  updated_at.present? ? Time.at(updated_at.to_i / 1000.0).utc : nil
end
convert_state(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 86
def convert_state(workflow)
  case get_workflow_state(workflow)
  when "INSTANTIATED", "RUNNING" # Should there be a queued state?
    :running
  when "STOPPED"
    :cancelled
  when "FAILED"
    workflow.xpath('//operation[@state="FAILED"]').empty? ? :cancelled : :failed
  when "SUCCEEDED", "SKIPPED" # Should there be a errored state?
    :completed
  end
end
convert_tech_metadata(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 108
def convert_tech_metadata(workflow)
  convert_track_metadata(workflow.xpath('//track[@type="presenter/source"]').first)
end
convert_track_metadata(track) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 176
def convert_track_metadata(track)
  return {} if track.nil?
  metadata = {}
  # metadata[:mime_type] = track.at("mimetype/text()").to_s if track.at('mimetype')
  metadata[:checksum] = track.at("checksum/text()").to_s.strip if track.at('checksum')
  metadata[:duration] = track.at("duration/text()").to_s.to_i if track.at('duration')
  if track.at('audio')
    metadata[:audio_codec] = track.at("audio/encoder/@type").to_s
    metadata[:audio_channels] = track.at("audio/channels/text()").to_s
    metadata[:audio_bitrate] = track.at("audio/bitrate/text()").to_s.to_f
  end
  if track.at('video')
    metadata[:video_codec] = track.at("video/encoder/@type").to_s
    metadata[:video_bitrate] = track.at("video/bitrate/text()").to_s.to_f
    metadata[:frame_rate] = track.at("video/framerate/text()").to_s.to_f
    metadata[:width] = track.at("video/resolution/text()").to_s.split('x')[0].to_i
    metadata[:height] = track.at("video/resolution/text()").to_s.split('x')[1].to_i
  end
  metadata
end
convert_updated_at(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 152
def convert_updated_at(workflow)
  updated_at = workflow.xpath('//operation[@state!="INSTANTIATED"]/completed/text()').last.to_s
  updated_at.present? ? Time.strptime(updated_at, "%Q") : nil
end
create_multiple_files(input, workflow_id) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 226
def create_multiple_files(input, workflow_id)
  # Create empty media package xml document
  mp = Rubyhorn.client.createMediaPackage

  # Next line associates workflow title to avalon via masterfile pid
  title = File.basename(input.values.first)
  dc = Nokogiri::XML('<dublincore xmlns="http://www.opencastproject.org/xsd/1.0/dublincore/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:title>' + title + '</dcterms:title></dublincore>')
  mp = Rubyhorn.client.addDCCatalog('mediaPackage' => mp.to_xml, 'dublinCore' => dc.to_xml, 'flavor' => 'dublincore/episode')

  # Add quality levels - repeated for each supplied file url
  input.each_pair do |quality, url|
    mp = Rubyhorn.client.addTrack('mediaPackage' => mp.to_xml, 'url' => url, 'flavor' => DEFAULT_ARGS['flavor'])
    # Rewrite track to include quality tag
    # Get the empty tags element under the newly added track
    tags = mp.xpath('//xmlns:track/xmlns:tags[not(node())]', 'xmlns' => 'http://mediapackage.opencastproject.org').first
    quality_tag = Nokogiri::XML::Node.new 'tag', mp
    quality_tag.content = quality
    tags.add_child quality_tag
  end
  # Finally ingest the media package
  begin
          Rubyhorn.client.start("definitionId" => workflow_id, "mediapackage" => mp.to_xml)
        rescue Rubyhorn::RestClient::Exceptions::HTTPBadRequest
          # make this two calls...one to get the workflow definition xml and then the second to submit it along with the mediapackage to start...due to unsolved issue with some MH installs
          begin
                  workflow_definition_xml = Rubyhorn.client.definition_xml(workflow_id)
                  Rubyhorn.client.start("definition" => workflow_definition_xml, "mediapackage" => mp.to_xml)
                rescue Rubyhorn::RestClient::Exceptions::HTTPNotFound
                  raise StandardError, "Unable to start workflow"
                end
        end
end
fetch_workflow(id) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 26
def fetch_workflow(id)
  workflow_om = begin
    Rubyhorn.client.instance_xml(id)
  rescue Rubyhorn::RestClient::Exceptions::HTTPNotFound
    nil
  end

  workflow_om ||= begin
    Rubyhorn.client.get_stopped_workflow(id)
  rescue
    nil
  end

  get_workflow(workflow_om)
end
get_media_package(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 197
def get_media_package(workflow)
  mp = workflow.xpath('//mediapackage')
  first_node = mp.first
  first_node['xmlns'] = 'http://mediapackage.opencastproject.org'
  mp
end
get_workflow(workflow_om) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 42
def get_workflow(workflow_om)
  return nil if workflow_om.nil?
  if workflow_om.ng_xml.is_a? Nokogiri::XML::Document
    workflow_om.ng_xml.remove_namespaces!.root
  else
    workflow_om.ng_xml
  end
end
get_workflow_state(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 82
def get_workflow_state(workflow)
  workflow.attribute('state').to_s
end
get_workflow_title(workflow) click to toggle source
# File lib/active_encode/engine_adapters/matterhorn_adapter.rb, line 104
def get_workflow_title(workflow)
  workflow.xpath('mediapackage/title/text()').to_s.strip
end