Skip to content

Instantly share code, notes, and snippets.

@Traz
Created June 11, 2009 05:50
Show Gist options
  • Save Traz/127725 to your computer and use it in GitHub Desktop.
Save Traz/127725 to your computer and use it in GitHub Desktop.
Small tips for building a polymorphic usage of Paperclip, with video to flv, video thumbnail, mp3 tag extraction, zip file listing processors.
class AddAttachmentsDataToWrapper < ActiveRecord::Migration
def self.up
add_column :wrappers, :data_file_name, :string
add_column :wrappers, :data_content_type, :string
add_column :wrappers, :data_file_size, :integer
add_column :wrappers, :data_updated_at, :datetime
end
def self.down
remove_column :wrappers, :data_file_name
remove_column :wrappers, :data_content_type
remove_column :wrappers, :data_file_size
remove_column :wrappers, :data_updated_at
end
end
module Paperclip
class AudioTag < Processor
def initialize file, options = {}, attachment = nil
super
@file = file
@data = attachment
end
def make
begin
src = @file
src.close
mp3 = Mp3Info.open(File.expand_path(src.path))
@data.instance.info = mp3.tag.to_a.inject("#{@data.instance.info}\n---\n"){|result, element|
result + "*#{element.first}* : #{element.last}\n".titleize}
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error processing the tag extraction for #{@basename}" if @whiny
end
nil
end
end
end
class CreateWrappers < ActiveRecord::Migration
def self.up
create_table :wrappers do |t|
t.string :name
t.references :user
t.references :attachable, :polymorphic => true
t.boolean :private, :default => false
t.text :info
t.text :doc_type
t.timestamps
end
add_index :wrappers, :user_id
add_index :wrappers, :attachable_type
add_index :wrappers, :attachable_id
end
def self.down
drop_table :wrappers
end
end
module Paperclip
class Empty < Processor
def initialize file, options = {}, attachment = nil
end
def make
nil
end
end
end
Paperclip.interpolates :zfull_path do |d,s|
att = d.instance.attachable_type || "_no_type"
prn = d.instance.proc_name
ext = d.instance.data_file_name.scan(/.*\.([^.]*?)$/).to_s
if (s == :original) or
(prn == 'image') or
((prn == 'video') and (s == :thumb)) or
((prn == 'pdf') and (s != :small)) then
"wrappers/#{att}/:style/:basename.:id.:extension"
elsif (prn == 'video' and s == :large)
"wrappers/#{att}/#{s}/:basename.:id.flv"
else
"images/styles/#{ext}.:style.png"
end
end
module Paperclip
# Handles thumbnailing images that are uploaded.
class Pdf < Processor
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options
# Creates a Thumbnail object set to work on the +file+ given. It
# will attempt to transform the image into one defined by +target_geometry+
# which is a "WxH"-style string. +format+ will be inferred from the +file+
# unless specified. Thumbnail creation will raise no errors unless
# +whiny+ is true (which it is, by default. If +convert_options+ is
# set, the options will be appended to the convert command upon image conversion
def initialize file, options = {}, attachment = nil
super
geometry = options[:geometry]
@file = file
@crop = geometry[-1,1] == '#'
@target_geometry = Geometry.parse geometry
@current_geometry = Geometry.from_file @file
@convert_options = options[:convert_options]
@whiny = options[:whiny].nil? ? true : options[:whiny]
@format = options[:format]
@current_format = File.extname(@file.path)
@basename = File.basename(@file.path, @current_format)
end
# Returns true if the +target_geometry+ is meant to crop.
def crop?
@crop
end
# Returns true if the image is meant to make use of additional convert options.
def convert_options?
not @convert_options.blank?
end
# Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
# that contains the new image.
def make
src = @file
dst = Tempfile.new([@basename, @format].compact.join("."))
dst.binmode
################## added -density 196 *before* the file call for better rendering
command = <<-end_command
-density 196
"#{ File.expand_path(src.path) }[0]"
#{ transformation_command }
"#{ File.expand_path(dst.path) }"
end_command
begin
success = Paperclip.run("convert", command.gsub(/\s+/, " "))
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny
end
dst
end
# Returns the command ImageMagick's +convert+ needs to transform the image
# into the thumbnail.
def transformation_command
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
trans = "-resize \"#{scale}\""
trans << " -crop \"#{crop}\" +repage" if crop
trans << " #{convert_options}" if convert_options?
trans
end
end
end
lst = ["zip", "txt", "doc", "ppt", "mp3", "jpg", "xls", "xlsx", "docx", "flv", "wmv", "avi", "pdf"]
id = 1
ty = 'testing'
lst.each do |fic|
w = Wrapper.new
w.id = id
w.attachable_type = ty
File.open("test.#{fic}",'rb'){|f| w.data = f}
puts "test.#{fic} --> #{w.proc_name} -- #{w.data.content_type}"
puts "-------"
puts w.info
[:original,:small,:thumb,:large].each{|s| puts w.data.path(s)}
puts "------------------------------------"
w.save
id+=1
end
module Paperclip
class VideoConvert < Processor
attr_accessor :whiny, :tt, :file, :basename
def initialize(file, options = {}, attachment = nil)
super
@file = file
@whiny = options[:whiny].nil? ? true : options[:whiny]
@basename = File.basename(file.path, File.extname(file.path))
@data = attachment
end
def make
src = @file
flv = Tempfile.new([ @basename, 'flv' ].compact.join("."))
flv.close
command = <<-end_command
-i #{File.expand_path(src.path)} -ar 22050 -ab 32 -acodec libmp3lame
-s 480x360 -vcodec flv -r 25 -qscale 8 -f flv -y #{File.expand_path(flv.path)}
end_command
command.gsub!(/\s+/, " ")
@tt = command
begin
success = Paperclip.run('ffmpeg', command)
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error processing the video convert for #{@basename}" if whiny
end
flv
end
end
end
module Paperclip
class VideoThumb < Processor
attr_accessor :time_offset, :geometry, :whiny
def initialize(file, options = {}, attachment = nil)
super
@data = attachment
@time_offset = options[:time_offset] || '-4'
unless options[:geometry].nil? || (@geometry = Geometry.parse(options[:geometry])).nil?
@geometry.width = (@geometry.width / 2.0).floor * 2.0
@geometry.height = (@geometry.height / 2.0).floor * 2.0
@geometry.modifier = ''
end
@whiny = options[:whiny].nil? ? true : options[:whiny]
@basename = File.basename(file.path, File.extname(file.path))
end
def make
dst = Tempfile.new([ @basename, 'jpg' ].compact.join("."))
dst.binmode
cmd = %Q[-itsoffset #{time_offset} -i "#{File.expand_path(file.path)}" -y -vcodec mjpeg -vframes 1 -an -f rawvideo ]
cmd << "-s #{geometry.to_s} " unless geometry.nil?
cmd << %Q["#{File.expand_path(dst.path)}"]
begin
success = Paperclip.run('ffmpeg', cmd)
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if whiny
end
dst
end
end
end
class Wrapper < ActiveRecord::Base
TypesWrapper = [ 'application/x-docx',
'application/x-doc', 'application/x-rtf',
'application/x-pps', 'application/x-ppt', 'application/x-pptx',
'application/x-xls', 'application/x-xlsx','application/x-csv',
'application/x-msg',
'application/x-zip',
'text/plain',
'application/x-pdf','application/pdf',
'application/mp3', 'application/x-mp3', 'audio/mpeg', 'audio/mp3',
'image/gif', 'image/png', 'image/x-png', 'image/jpeg', 'image/jpg',
'video/mpeg', 'application/x-flv', 'video/quicktime' ,'application/x-wmv',
'application/x-avi']
acts_as_commentable
acts_as_textiled :name, :info
belongs_to :user
has_attached_file :data,
:url => "/:zfull_path",
:path => ":rails_root/public/:zfull_path",
:whiny => true
attr_protected :data_file_name, :data_content_type, :data_size
before_post_process :validate_processor
validates_attachment_size :data, :less_than => 10.megabytes
validates_attachment_content_type :data, :content_type => TypesWrapper
def proc_name
type, detail = data.content_type.scan(/^(video|audio|application|text|image)\/(.*?)$/).flatten
case type
when 'image' then 'image'
when 'video','audio' then type
when 'text' then 'text'
when 'application'
case detail
when /pdf$/ then 'pdf'
when /mp3$/ then 'audio'
when /mov|flv|wmv|avi$/ then 'video'
when /csv|xls.?$/ then 'excel'
when /doc.?|txt|rtf$/ then 'word'
when /pp..?/ then 'powerpoint'
when 'msg' then 'message'
when /^(?:x-)?zip$/ then 'zip'
else 'document'
end
end
end
def validate_processor
case proc_name
when "image" then
data.styles[:small] = { :geometry => "64x64>", :format => "png", :processors => "thumbnail" }
data.styles[:thumb] = { :geometry => "128x128#", :format => "png", :processors => "thumbnail" }
data.styles[:large] = { :geometry => "300x400#", :format => "png", :processors => "thumbnail" }
when "document",'excel','word','powerpoint','text'
data.styles[:small] = { :geometry => "64x64>", :format => "png", :processors => "empty" }
data.styles[:thumb] = { :geometry => "128x128#", :format => "png", :processors => "empty" }
data.styles[:large] = { :geometry => "300x400#", :format => "png", :processors => "empty" }
when "zip"
data.styles[:small] = { :geometry => "64x64>", :format => "png", :processors => "empty" }
data.styles[:thumb] = { :geometry => "128x128#", :format => "png", :processors => "empty" }
data.styles[:large] = { :geometry => "300x400#", :format => "png", :processors => "zip_info" }
when "video"
data.styles[:small] = { :geometry => "64x64>", :format => "png", :processors => "empty" }
data.styles[:thumb] = { :geometry => "128x128#", :format => "png", :processors => "video_thumb" }
data.styles[:large] = { :geometry => "300x400#", :format => "flv", :processors => "video_convert"}
when "audio"
data.styles[:small] = { :geometry => "64x64>", :format => "png", :processors => "audio_tag" }
data.styles[:thumb] = { :geometry => "128x128#", :format => "png", :processors => "empty" }
data.styles[:large] = { :geometry => "300x400#", :format => "png", :processors => "empty" }
when "pdf"
data.styles[:small] = { :geometry => "64x64>", :format => "png", :processors => "empty" }
data.styles[:thumb] = { :geometry => "128x128#", :format => "png", :processors => "pdf" }
data.styles[:large] = { :geometry => "300x400#", :format => "png", :processors => "pdf" }
end
true
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment