TLDR: I forked carrierwave-video-thumbnailer and added MiniMagick post-processing support.


Using good ol’ Carrierwave to upload video files to S3, I came across a need to generate video thumbnails. A quick Google revealed carrierwave-video-thumbnailer, which worked perfectly.

However, I needed to rotate the thumbnails (90 degrees clockwise)1 before uploading them. I thought it would be trivial, but it turned out that Carrierwave’s DSL made using multiple processes in a version block intractable.

# original without rotating
class VideoUploader < CarrierWave::Uploader::Base
  include CarrierWave::Video::Thumbnailer

  version :preview_image do
    process thumbnail: [{format: 'jpg', quality: 8, size: 360, logger: Rails.logger}]
    def full_filename for_file
      jpg_name for_file, version_name
    end
  end
  def jpg_name for_file, version_name
    %Q{#{version_name}_#{for_file.chomp(File.extname(for_file))}.jpg}
  end
end
# with rotating, in an ideal world
# sadly, real life doesn't work that way

class VideoUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  include CarrierWave::Video::Thumbnailer

  version :preview_image do
    process thumbnail: [{format: 'jpg', quality: 8, size: 360, logger: Rails.logger}]
    process :rotate
    def full_filename for_file
      jpg_name for_file, version_name
    end
  end

  def rotate
    manipulate! do |img|
      img.rotate "90"
      img
    end
  end

  def jpg_name for_file, version_name
    %Q{#{version_name}_#{for_file.chomp(File.extname(for_file))}.jpg}
  end
end

I couldn’t find a reasonable way to proceed from here, and after spending 2-3 hours trying different permutations of the above, wrangling Carrierwave’s DSL and trying to get things to work, I finally dug into the carrierwave-video-thumbnailer gem to see if I could get MiniMagick to work inside of the process :thumbnail call.

To expose as much of MiniMagick’s API as possible, I opted for the user to pass in a Proc.

To use it, simply pass in a Proc as mini_magick_opts:

class VideoUploader < CarrierWave::Uploader::Base
  include CarrierWave::Video::Thumbnailer

  mini_magick_proc = Proc.new { |image|
    image.rotate "90" # rotates the image 90 degrees clockwise
  }

  version :preview_image do
    process thumbnail: [{format: 'jpg', quality: 8, size: 360, logger: Rails.logger, mini_magick_opts: mini_magick_proc}]
    def full_filename for_file
      jpg_name for_file, version_name
    end
  end

  def jpg_name for_file, version_name
    %Q{#{version_name}_#{for_file.chomp(File.extname(for_file))}.jpg}
  end
end

You can look a look at my commit here for more detail on how I implemented the change.

Footnotes
  1. For some reason, the thumbnails that carrierwave-video-thumbnailer were generating from portrait iOS videos were rotated 90 degrees counter-clockwise. Some others have faced this issue as well.