Skip to content

Instantly share code, notes, and snippets.

@FunkyloverOne
Last active September 25, 2018 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FunkyloverOne/7733ed808e38764ae1858b8c51107390 to your computer and use it in GitHub Desktop.
Save FunkyloverOne/7733ed808e38764ae1858b8c51107390 to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
class TranscodeJob
include Sidekiq::Worker
ORIGINAL_VERSION_NAME = 'original'
VIDEO_TRANSCODING_PIPELINE_ID = ENV['video_transcoding_pipeline_id']
# list your versions here
VIDEO_TRANSCODING_VERSIONS_OPTIONS = {
'480p' => {
preset: ENV['your_480p_preset_id_env_var_name'],
extname: '.mp4', poster_extname: '.png'
}.freeze
}.with_indifferent_access.freeze
def perform(data)
@attacher = TranscoderUploader::Attacher.load(data)
execute_transcoding_job
store_original_cached_file
apply_outputs_to_resulting_versions
attacher.swap(resulting_versions)
end
private
attr_reader :attacher, :outputs
def cached_file
@cached_file ||= attacher.get
end
def transcoder
@transcoder ||= Aws::ElasticTranscoder::Client.new
end
def transcoder_job_id
@transcoder_job_id ||= transcoder.create_job(
pipeline_id: VIDEO_TRANSCODING_PIPELINE_ID,
input: { key: cached_file_absolute_location },
outputs: job_outputs_params,
output_key_prefix: "#{store_uploader.storage.prefix}/"
).job.id
end
def execute_transcoding_job
transcoder.wait_until(:job_complete, id: transcoder_job_id)
@outputs = transcoder.read_job(id: transcoder_job_id).job.outputs
end
def job_outputs_params
versions_data.values.map do |data|
{
key: data[:location],
thumbnail_pattern: data[:poster_location_pattern],
preset_id: data[:transcoder_preset]
}
end
end
def cached_file_absolute_location
"#{attacher.cache.storage.prefix}/#{cached_file.id}"
end
def store_uploader
@store_uploader ||= attacher.store
end
def uploaded_file_class
@uploaded_file_class ||= attacher.shrine_class::UploadedFile
end
def store_original_cached_file
store_uploader.store(
cached_file,
attacher.context.merge(location: original_data[:location])
)
end
def versions_data
@versions_data ||= {}.tap do |hash|
VIDEO_TRANSCODING_VERSIONS_OPTIONS.each do |version, options|
poster_pattern = generate_poster_location_pattern_for(version)
hash[version] = {
mime_type: MIME::Types.of(options[:extname]).grep(/video/).first.to_s,
transcoder_preset: options[:preset],
location: generate_location_for(version),
filename: generate_filename_for(version),
poster_version_name: poster_version_name_for(version),
poster_filename: "poster#{options[:poster_extname]}",
poster_location_pattern: poster_pattern,
poster_location:
generate_poster_location_from(version, poster_pattern),
poster_mime_type:
MIME::Types.of(options[:poster_extname]).grep(/image/).first.to_s
}
end
end
end
def original_data
@original_data ||= {
filename: cached_file.original_filename,
location: generate_rough_location_for(ORIGINAL_VERSION_NAME)
}.tap do |hash|
extname = hash[:extname] = File.extname(hash[:filename].to_s)
hash[:location_ends_with_extname] =
extname.empty? ? false : hash[:location].match?(/#{extname}$/)
end
end
def generate_rough_location_for(version)
store_uploader.generate_location(
cached_file, attacher.context.merge(version: version)
)
end
def generate_location_for(version)
result = generate_rough_location_for(version)
version_extname = VIDEO_TRANSCODING_VERSIONS_OPTIONS[version][:extname]
original_extname = original_data[:extname]
if original_extname.empty? || !original_data[:location_ends_with_extname]
return result + version_extname
end
result.sub(/#{original_extname}$/, version_extname)
end
def generate_poster_location_pattern_for(version)
result = generate_rough_location_for(poster_version_name_for(version))
# NOTE: "{count}" part is **required** by elastic transcoder
if original_data[:location_ends_with_extname]
return result.sub(/#{original_data[:extname]}$/, '-{count}')
end
result + '-{count}'
end
def poster_version_name_for(version)
"#{version}_poster"
end
def generate_poster_location_from(version, pattern)
# NOTE: elastic transcoder will replace "{count}" with "00001.png"
# (almost), more details here:
# https://docs.aws.amazon.com/elastictranscoder/latest/developerguide/job-settings.html#job-settings-output-details-part-3
extname = VIDEO_TRANSCODING_VERSIONS_OPTIONS[version][:poster_extname]
pattern.sub('{count}', '00001' + extname)
end
def generate_filename_for(version)
return if (original_filename = original_data[:filename]).blank?
original_extname = original_data[:extname]
version_extname = VIDEO_TRANSCODING_VERSIONS_OPTIONS[version][:extname]
return original_filename + version_extname if original_extname.empty?
original_filename.sub(/#{original_extname}$/, version_extname)
end
def resulting_versions
@resulting_versions ||= {
ORIGINAL_VERSION_NAME => uploaded_file_class.new(
'id' => original_data[:location],
'storage' => 'store',
'metadata' => cached_file.metadata
)
}
end
def apply_outputs_to_resulting_versions
outputs.each_with_index do |output, index|
version = versions_data.keys[index]
version_data = versions_data[version]
resulting_versions[version] = uploaded_file_class.new(
'id' => output.key,
'storage' => 'store',
'metadata' => {
'filename' => version_data[:filename],
'width' => output.width,
'height' => output.height,
'duration' => output.duration,
'duration_millis' => output.duration_millis,
'file_size' => output.file_size,
'mime_type' => version_data[:mime_type]
}
)
resulting_versions[version_data[:poster_version_name]] =
uploaded_file_class.new(
'id' => version_data[:poster_location],
'storage' => 'store',
'metadata' => {
'filename' => version_data[:poster_filename],
'mime_type' => version_data[:poster_mime_type]
}
)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment