Skip to content

Instantly share code, notes, and snippets.

@shamil614
Created November 2, 2012 16:12
Show Gist options
  • Save shamil614/4002368 to your computer and use it in GitHub Desktop.
Save shamil614/4002368 to your computer and use it in GitHub Desktop.
Carrierwave Attachment Uploader with AWS and Zencoder.
class Attachment < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
mount_uploader :item, AttachmentUploader
# background the storage of files to AWS and processing
# makes for fast uploads!
store_in_background :item
attr_accessible :item
before_save :update_attachment_attributes
def update_attachment_attributes
if item.present? && item_changed?
self.content_type = item.file.content_type
self.file_size = item.file.size
end
end
end
# Got my idea from this article http://www.nickdesteffen.com/blog/video-encoding-with-uploadify-carrierwave-and-zencoder
# The problems I had with it....
# 1. I'm using AWS S3
# 2. I didn't use Uploadify
# 3. It only encodes one format
# 4. It uses a callback for the OUTPUT and not the JOB
# 5. There was a bunch of code in the controller that wasn't necessary
# How my implementation works....
# All versions upload to AWS
# Videos are not sent to Zencoder until after storage to AWS. Note: this is not efficient bandwidth usage, but it works!
# ToDo: prevent Carrierwave from uploading the Versions before conversion OR implement a direct upload to AWS...
class AttachmentUploader < CarrierWave::Uploader::Base
VIDEOS = {web: 'mp4', open: 'webm' }
include Rails.application.routes.url_helpers
Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
# Choose what kind of storage to use for this uploader:
storage :fog
after :store, :zencode
# content types for media
include CarrierWave::MimeTypes
process :set_content_type
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url
asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
version :thumb, :if => :is_image? do
process :resize_to_fill => [90, 90]
end
# uploads a an exact copy (different extention) of originial to S3 for Zencoder to encode
version :mp4, :if => :is_video? do
def full_filename(for_file)
"#{File.basename(for_file, File.extname(for_file))}.mp4"
end
end
# uploads a an exact copy (different extention) of originial to S3 for Zencoder to encode
version :webm, :if => :is_video? do
def full_filename(for_file)
"#{File.basename(for_file, File.extname(for_file))}.webm"
end
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
# ToDo: verify extentions
# ToDo: verify scenario with properly encoded files???
def extension_white_list
%w(mov avi mp4 mkv wmv mpg jpg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
def is_image?(attachment)
if attachment.content_type.match(/image\/jpeg|image\/gif|image\/png|image\/pjpeg|image\/jpg/i)
return true
else
return false
end
end
def is_video?(attachment)
if attachment.content_type.match(/video/i)
return true
else
return false
end
end
def zencode(args)
# only encode the original file upload callback
if self.version_name.nil?
bucket = AttachmentUploader.fog_directory
input = "s3://#{bucket}/#{self.path}"
base_url = "s3://#{bucket}/#{store_dir}"
filename = File.basename(self.path)
ext = File.extname(self.path)
outputs = []
VIDEOS.each do |key, value|
outputs << {
:base_url => base_url,
:filename => filename.gsub(ext, '.' + value),
:label => key,
:format => value,
:aspect_mode => "preserve",
:width => 854,
:height => 480,
:public => 1
}
end
zencoder_response = Zencoder::Job.create({
:input => input,
# callback on the JOB completion!
:notifications => [zencoder_callback_url(:protocol => 'http')],
:output => outputs
})
@model.job_id = zencoder_response.body["id"].to_s
@model.item_processing = true
@model.save(:validate => false)
end
end
end
class ZencoderCallbackController < ApplicationController
skip_before_filter :verify_authenticity_token
def create
job_id = params["job"]["id"]
job_state = params["job"]["state"]
video = Attachment.find_by_job_id(job_id.to_s)
if job_state == "finished" && video
video.item_processing = false
video.save
end
render :nothing => true
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment