Can I Upload Other Video Files That Mp4 With Carrierwave

For one of the projects we have been working on , we had to be able to upload videos to the server, convert them to formats supported by browsers with <video> tag and serve them to the user. Since Scarlet and Ruby on Rails are our tools of choice nosotros used CarrierWave for file uploading and storage. Since Carrier Wave supports processing of input files into various versions, we decided to give it a try.

CarrierWave Gem

Since processing video files takes a lot of time, nosotros had to do it in the groundwork so we used carrierwave-backgrounder. When looking for a way to do video processing, we found the gem carrierwave-video that used streamio-ffmpeg for transcoding and information technology seemed fine for our task.

Our Gemfile looked like this:

          precious stone 'carrierwave', '0.x.0'            
precious stone 'carrierwave-backgrounder', '0.four.2'
gem 'carrierwave-video', '0.5.six'

To encode videos to proper formats nosotros used encode_video method from carrierwave-video:

          version :mp4 do            
process encode_video: [:mp4, { progress: :on_progress }]
end
version :webm exercise
procedure encode_video: [:webm, { progress: :on_progress }]
cease

Subsequently some time at that place was a demand in the project to be able to cut out small pieces of videos and serve them independently.

  • At first nosotros tried to practice information technology the same style as previous processing:
          version :mp4 exercise            
process encode: [:mp4, { progress: :on_progress }]
finish
version :webm do
process encode: [:webm, { progress: :on_progress }]
end
def encode(format, opts={})
encode_video(format, opts) practice |movie, params|
params[:custom] = "-ss #{model.playback.start_in_hms} -t #{model.playback.duration_in_hms}"
finish
terminate

Unfortunately this had a very serious drawback — since all of the versions were being candy from one file inputted to the uploader, they had to exist transcoded every fourth dimension. That was far from perfect as it took additional fourth dimension and unnecessarily used resources so we hacked our manner a bit: since the original video was already in the formats we needed, we could just laissez passer those versions instead and re-create the audio and video streams.

The Solution

To brand the code less cluttered, we sprinkled information technology with a flake of DSL:

          support_formats custom: proc { |model| "-ss #{model.start_time} -t #{model.duration}" },            
source: proc { |model, version| model.parent.file.versions[version].file },
mode: :copy
def support_formats(support_opts={})
FORMATS.each practice |version_name, opts|
opts = opts.reverse_merge(support_opts)
(conditional = opts.delete(:if)) && (provisional = provisional.to_sym)
uploader = version(version_name, if: conditional) { process encode_format: [opts] }
uploader[:uploader].class_eval <<-RUBY, __FILE__, __LINE__ + 1
def full_filename(file)
file + ".#{version_name}.#{opts[:extension]}" # forcing the extension, otherwise ffmpeg got confused
finish
RUBY
end
stop
def encode_format(opts={})
cache_stored_file! if !cached?
if opts[:mode] == :copy
opts[:video_codec] = 'copy'
opts[:audio_codec] = 'copy'
terminate
opts[:custom] = opts[:custom].call(model) if opts[:custom].respond_to?(:call) source = source.call(model, version_name) if source.respond_to?(:phone call)
source = file if source.nil?
source = source.path if source.respond_to?(:path)
# etc
end
➡️Check our Privacy Policy⬅️

This setup kind of worked, merely it posed a lot of problems: we didn't have control over what versions were transcoded and we had to recreate each version if whatsoever of the transcoding failed. More than that, if any transcoding happened during deployment of new version, sidekiq had to be killed and restarted and it didn't have a way of going dorsum from where it started and so the whole processing had to exist either redone or ditched altogether and marked as crashed.

We have tried diverse solutions of mitigating this problem only unfortunately using carrierwave-backgrounder made everything more messy. That gem was great for simpler logic but unfortunately choked a scrap when nosotros tried extending information technology more. This likewise caused the logic of processing to be divided betwixt non-logical parts, like processing code ending upwardly partially in sidekiq worker (because it was easy to set custom worker when mounting an uploader), non-obvious or custom callbacks beingness thrown all over the place or processing starting non-asynchronously if nosotros weren't careful enough. The API got brittle and the whole codebase gradually became a mess.

          class StreamUploadWorker < ::CarrierWave::Workers::StoreAsset                      def perform(*args)
set_args(*args) if args.present?
video = constantized_resource.find id
# custom callbacks in model
run_callback video, :before_upload
super(*args)
video.reload
run_callback video, :after_upload_success rescue
run_callback video, :after_upload_failure
video.broken! if video.respond_to?(:broken!)
# logging
end
def run_callback(video, callback)
video.ship(callback) if video.respond_to?(callback)
end
end

The terminal harbinger…

The final straw, notwithstanding, came with a new requirement. The project has matured enough to generate significant traffic and serving multimedia content from a defended server no longer seemed like a feasible solution. We needed to accept files on both a local server (for processing) and on some kind of cloud solution (for hosting). Every bit CarrierWave versions are goose egg more than than differently named files in a directory, using them seemed like a bad idea considering the amount of patchwork needed. It was time to make clean the business firm.

We solved the trouble by ditching versions altogether. We created a divide model for storing files that could either have local or remote (fog) uploader attached. Then we wrote our ain transcoding logic, acme down, piece of cake to understand, using streamio-ffmpeg directly with specified file equally the source and putting it in the path CarrierWave expected.

          class FileObject < ActiveRecord::Base            
belongs_to :owner, polymorphic: true
class Local < FileObject
mount_uploader :file, FileObjectUploader
end
class Deject < FileObject
mount_uploader :file, AwsUploader
end
end
class Processor
# partial, instance code
def recreate_versions!(video, file)
formats_with_options(video, file).each practise |format, opts|
if video.parent.present?
original = video.parent.version(format)
file = original.file if original.respond_to?(:file)
end
file_object = FileObject::Local.create!(possessor: video, version: format)
filename = "#{SecureRandom.uuid}.#{opts[:extension]}"
FileUtils.mkdir_p file_object.file.store_dir
destination = "#{file_object.file.store_dir}/#{filename}"
transcode!(source, destination, format, opts)
file_object.update_column(:file, filename)
S3UploadWorker.perform_async(file_object.id) unless Rail.env.evolution?
stop
terminate
end

Summary

CarrierWave is a bang-up solution for file uploading. Information technology'southward too a slap-up solution for uncomplicated processing. The moral of the story is, you accept to employ the appropriate tools. Carrier Wave versions are not enough for circuitous processing or whatsoever processing that doesn't use the original uploaded file every bit the source. It may seem obvious in retrospect just that's what happens when your codebase gradually evolves. When this happens, always try to observe some time to stop, expect dorsum and ask yourself: "Is this code doing what it was originally made for?".

Happy coding!

andersonsoodia.blogspot.com

Source: https://blog.untitledkingdom.com/escaping-carrierwave-versions-6e0015e6e168

0 Response to "Can I Upload Other Video Files That Mp4 With Carrierwave"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel