-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Shingoncoder is a backend for transcoding videos locally using ActiveJob and Ffmpeg https://github.com/jcoyne/shingoncoder
- Loading branch information
Showing
6 changed files
with
214 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
require 'active_support' | ||
require 'active_support/core_ext' | ||
|
||
module ActiveEncode | ||
module EngineAdapters | ||
class ShingoncoderAdapter < ZencoderAdapter | ||
# @param [ActiveEncode::Base] encode | ||
def create(encode) | ||
response = Shingoncoder::Job.create(input: encode.input) | ||
build_encode(job_details(response.body["id"]), encode.class) | ||
end | ||
|
||
# @param [Fixnum] id | ||
# @param [Hash] opts | ||
# @option opts :cast the class to cast the encoding job to. | ||
def find(id, opts = {}) | ||
build_encode(job_details(id), opts[:cast]) | ||
end | ||
|
||
# @param [ActiveEncode::Base] encode | ||
def cancel(encode) | ||
response = Shingoncoder::Job.cancel(encode.id) | ||
build_encode(job_details(encode.id), encode.class) if response.success? | ||
end | ||
|
||
private | ||
|
||
# @param [Fixnum] job_id the identifier for the job | ||
# @return [Shingoncoder::Response] the response from Shingoncoder | ||
def job_details(job_id) | ||
Shingoncoder::Job.details(job_id) | ||
end | ||
|
||
# @return [Shingoncoder::Response] the response from Shingoncoder | ||
def job_progress(job_id) | ||
Shingoncoder::Job.progress(job_id) | ||
end | ||
|
||
# @param [Shingoncoder::Response] job_details | ||
# @param [Class] cast the class of object to instantiate and return | ||
def build_encode(job_details, cast) | ||
return nil if job_details.nil? | ||
encode = cast.new(convert_input(job_details), convert_options(job_details)) | ||
encode.id = job_details.body["job"]["id"].to_s | ||
encode.state = convert_state(job_details) | ||
progress = job_progress(encode.id) | ||
encode.current_operations = convert_current_operations(progress) | ||
encode.percent_complete = convert_percent_complete(progress, job_details) | ||
encode.output = convert_output(job_details) | ||
encode.errors = convert_errors(job_details) | ||
encode.tech_metadata = convert_tech_metadata(job_details.body["job"]["input_media_file"]) | ||
encode | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
require 'spec_helper' | ||
require 'shingoncoder' | ||
require 'json' | ||
|
||
describe ActiveEncode::EngineAdapters::ShingoncoderAdapter do | ||
before(:all) do | ||
ActiveEncode::Base.engine_adapter = :shingoncoder | ||
end | ||
after(:all) do | ||
ActiveEncode::Base.engine_adapter = :inline | ||
end | ||
|
||
let(:create_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_create.json'))) } | ||
|
||
before do | ||
allow(Shingoncoder::Job).to receive(:create).and_return(create_response) | ||
end | ||
|
||
let(:file) { "file://#{File.absolute_path('spec/fixtures/Bars_512kb.mp4')}" } | ||
|
||
describe "#create" do | ||
before do | ||
allow(Shingoncoder::Job).to receive(:details).and_return(details_response) | ||
allow(Shingoncoder::Job).to receive(:progress).and_return(progress_response) | ||
end | ||
|
||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_create.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_create.json'))) } | ||
let(:create_output) { [{ id: "511404522", url: "https://zencoder-temp-storage-us-east-1.s3.amazonaws.com/o/20150610/c09b61e4d130ddf923f0653418a80b9c/399ae101c3f99b4f318635e78a4e587a.mp4?AWSAccessKeyId=AKIAI456JQ76GBU7FECA&Signature=GY/9LMkQAiDOrMQwS5BkmOE200s%3D&Expires=1434033527", label: nil }] } | ||
|
||
subject { ActiveEncode::Base.create(file) } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.not_to be_empty } | ||
it { is_expected.to be_running } | ||
its(:output) { is_expected.to eq create_output } | ||
its(:current_operations) { is_expected.to be_empty } | ||
its(:percent_complete) { is_expected.to eq 0 } | ||
its(:errors) { is_expected.to be_empty } | ||
its(:tech_metadata) { is_expected.to be_empty } | ||
end | ||
|
||
describe "#find" do | ||
before do | ||
allow(Shingoncoder::Job).to receive(:details).and_return(details_response) | ||
allow(Shingoncoder::Job).to receive(:progress).and_return(progress_response) | ||
end | ||
|
||
context "a running encode" do | ||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_running.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_running.json'))) } | ||
let(:running_output) { [{ id: "510582971", url: "https://zencoder-temp-storage-us-east-1.s3.amazonaws.com/o/20150609/48a6907086c012f68b9ca43461280515/1726d7ec3e24f2171bd07b2abb807b6c.mp4?AWSAccessKeyId=AKIAI456JQ76GBU7FECA&Signature=vSvlxU94wlQLEbpG3Zs8ibp4MoY%3D&Expires=1433953106", label: nil }] } | ||
let(:running_tech_metadata) { { audio_bitrate: "52", audio_codec: "aac", audio_channels: "2", duration: "57992", mime_type: "mpeg4", video_framerate: "29.97", height: "240", video_bitrate: "535", video_codec: "h264", width: "320" } } | ||
|
||
subject { ActiveEncode::Base.find('166019107') } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.to eq '166019107' } | ||
it { is_expected.to be_running } | ||
its(:output) { is_expected.to eq running_output } | ||
its(:current_operations) { is_expected.to be_empty } | ||
its(:percent_complete) { is_expected.to eq 30.0 } | ||
its(:errors) { is_expected.to be_empty } | ||
its(:tech_metadata) { is_expected.to eq running_tech_metadata } | ||
end | ||
|
||
context "a cancelled encode" do | ||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_cancelled.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_cancelled.json'))) } | ||
|
||
subject { ActiveEncode::Base.find('165866551') } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.to eq '165866551' } | ||
it { is_expected.to be_cancelled } | ||
its(:current_operations) { is_expected.to be_empty } | ||
its(:percent_complete) { is_expected.to eq 0 } | ||
its(:errors) { is_expected.to be_empty } | ||
its(:tech_metadata) { is_expected.to be_empty } | ||
end | ||
|
||
context "a completed encode" do | ||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_completed.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_completed.json'))) } | ||
let(:completed_output) { { id: "509856876", audio_bitrate: "53", audio_codec: "aac", audio_channels: "2", duration: "5000", mime_type: "mpeg4", video_framerate: "29.97", height: "240", video_bitrate: "549", video_codec: "h264", width: "320", url: "https://zencoder-temp-storage-us-east-1.s3.amazonaws.com/o/20150608/ebbe865f8ef1b960d7c2bb0663b88a12/0f1948dcb2fd701fba30ff21908fe460.mp4?AWSAccessKeyId=AKIAI456JQ76GBU7FECA&Signature=1LgIyl/el9E7zeyPxzd/%2BNwez6Y%3D&Expires=1433873646", label: nil } } | ||
let(:completed_tech_metadata) { { audio_bitrate: "52", audio_codec: "aac", audio_channels: "2", duration: "57992", mime_type: "mpeg4", video_framerate: "29.97", height: "240", video_bitrate: "535", video_codec: "h264", width: "320" } } | ||
|
||
subject { ActiveEncode::Base.find('165839139') } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.to eq '165839139' } | ||
it { is_expected.to be_completed } | ||
its(:output) { is_expected.to include completed_output } | ||
its(:current_operations) { is_expected.to be_empty } | ||
its(:percent_complete) { is_expected.to eq 100 } | ||
its(:errors) { is_expected.to be_empty } | ||
its(:tech_metadata) { is_expected.to eq completed_tech_metadata } | ||
end | ||
|
||
context "a failed encode" do | ||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_failed.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_failed.json'))) } | ||
let(:failed_tech_metadata) { { mime_type: "video/mp4", checksum: "7ae24368ccb7a6c6422a14ff73f33c9a", duration: "6314", audio_codec: "AAC", audio_channels: "2", audio_bitrate: "171030.0", video_codec: "AVC", video_bitrate: "74477.0", video_framerate: "23.719", width: "200", height: "110" } } | ||
let(:failed_errors) { "The file is an XML file, and doesn't contain audio or video tracks." } | ||
|
||
subject { ActiveEncode::Base.find('166079902') } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.to eq '166079902' } | ||
it { is_expected.to be_failed } | ||
its(:current_operations) { is_expected.to be_empty } | ||
its(:percent_complete) { is_expected.to eq 0 } | ||
its(:errors) { is_expected.to include failed_errors } | ||
its(:tech_metadata) { is_expected.to be_empty } | ||
end | ||
end | ||
|
||
describe "#cancel!" do | ||
before do | ||
allow(Shingoncoder::Job).to receive(:cancel).and_return(cancel_response) | ||
allow(Shingoncoder::Job).to receive(:details).and_return(details_response) | ||
allow(Shingoncoder::Job).to receive(:progress).and_return(progress_response) | ||
end | ||
|
||
let(:cancel_response) { Shingoncoder::Response.new(code: 200) } # TODO: check that this is the correct response code for a successful cancel | ||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_cancelled.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_cancelled.json'))) } | ||
|
||
let(:encode) { ActiveEncode::Base.create(file) } | ||
subject { encode.cancel! } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.to eq '165866551' } | ||
it { is_expected.to be_cancelled } | ||
end | ||
|
||
describe "reload" do | ||
before do | ||
allow(Shingoncoder::Job).to receive(:details).and_return(details_response) | ||
allow(Shingoncoder::Job).to receive(:progress).and_return(progress_response) | ||
end | ||
|
||
let(:details_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_details_running.json'))) } | ||
let(:progress_response) { Shingoncoder::Response.new(body: JSON.parse(File.read('spec/fixtures/zencoder/job_progress_running.json'))) } | ||
let(:reload_output) { [{ id: "510582971", url: "https://zencoder-temp-storage-us-east-1.s3.amazonaws.com/o/20150609/48a6907086c012f68b9ca43461280515/1726d7ec3e24f2171bd07b2abb807b6c.mp4?AWSAccessKeyId=AKIAI456JQ76GBU7FECA&Signature=vSvlxU94wlQLEbpG3Zs8ibp4MoY%3D&Expires=1433953106", label: nil }] } | ||
let(:reload_tech_metadata) { { audio_bitrate: "52", audio_codec: "aac", audio_channels: "2", duration: "57992", mime_type: "mpeg4", video_framerate: "29.97", height: "240", video_bitrate: "535", video_codec: "h264", width: "320" } } | ||
|
||
subject { ActiveEncode::Base.find('166019107').reload } | ||
it { is_expected.to be_a ActiveEncode::Base } | ||
its(:id) { is_expected.to eq '166019107' } | ||
it { is_expected.to be_running } | ||
its(:output) { is_expected.to eq reload_output } | ||
its(:current_operations) { is_expected.to be_empty } | ||
its(:percent_complete) { is_expected.to eq 30.0 } | ||
its(:errors) { is_expected.to be_empty } | ||
its(:tech_metadata) { is_expected.to eq reload_tech_metadata } | ||
end | ||
end |