uploads: factor out image dimension and filetype detection code.
* Add MediaFile abstraction. A MediaFile represents an image or video file. * Move filetype detection and dimension parsing code from uploads to MediaFile.
This commit is contained in:
77
app/logical/media_file.rb
Normal file
77
app/logical/media_file.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
class MediaFile
|
||||
extend Memoist
|
||||
attr_accessor :file
|
||||
|
||||
# delegate all File methods to `file`.
|
||||
delegate *(File.instance_methods - MediaFile.instance_methods), to: :file
|
||||
|
||||
def self.open(file)
|
||||
file = Kernel.open(file, "r", binmode: true) unless file.respond_to?(:read)
|
||||
|
||||
case file_ext(file)
|
||||
when :jpg, :gif, :png
|
||||
MediaFile::Image.new(file)
|
||||
when :swf
|
||||
MediaFile::Flash.new(file)
|
||||
when :webm, :mp4
|
||||
MediaFile::Video.new(file)
|
||||
when :zip
|
||||
MediaFile::Ugoira.new(file)
|
||||
else
|
||||
MediaFile.new(file)
|
||||
end
|
||||
end
|
||||
|
||||
def self.file_ext(file)
|
||||
header = file.pread(16, 0)
|
||||
|
||||
case header
|
||||
when /\A\xff\xd8/n
|
||||
:jpg
|
||||
when /\AGIF87a/, /\AGIF89a/
|
||||
:gif
|
||||
when /\A\x89PNG\r\n\x1a\n/n
|
||||
:png
|
||||
when /\ACWS/, /\AFWS/, /\AZWS/
|
||||
:swf
|
||||
when /\x1a\x45\xdf\xa3/n
|
||||
:webm
|
||||
when /\A....ftyp(?:isom|3gp5|mp42|MSNV|avc1)/
|
||||
:mp4
|
||||
when /\APK\x03\x04/
|
||||
:zip
|
||||
else
|
||||
:bin
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(file)
|
||||
@file = file
|
||||
end
|
||||
|
||||
def dimensions
|
||||
[0, 0]
|
||||
end
|
||||
|
||||
def width
|
||||
dimensions.first
|
||||
end
|
||||
|
||||
def height
|
||||
dimensions.second
|
||||
end
|
||||
|
||||
def md5
|
||||
Digest::MD5.file(file.path).hexdigest
|
||||
end
|
||||
|
||||
def file_ext
|
||||
MediaFile.file_ext(file)
|
||||
end
|
||||
|
||||
def file_size
|
||||
file.size
|
||||
end
|
||||
|
||||
memoize :dimensions, :file_ext, :file_size, :md5
|
||||
end
|
||||
6
app/logical/media_file/flash.rb
Normal file
6
app/logical/media_file/flash.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class MediaFile::Flash < MediaFile::Image
|
||||
def dimensions
|
||||
image_size = ImageSpec.new(file.path)
|
||||
[image_size.width, image_size.height]
|
||||
end
|
||||
end
|
||||
6
app/logical/media_file/image.rb
Normal file
6
app/logical/media_file/image.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class MediaFile::Image < MediaFile
|
||||
def dimensions
|
||||
image_size = ImageSpec.new(file.path)
|
||||
[image_size.width, image_size.height]
|
||||
end
|
||||
end
|
||||
13
app/logical/media_file/ugoira.rb
Normal file
13
app/logical/media_file/ugoira.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class MediaFile::Ugoira < MediaFile
|
||||
def dimensions
|
||||
tempfile = Tempfile.new
|
||||
folder = Zip::File.new(file.path)
|
||||
folder.first.extract(tempfile.path) { true }
|
||||
|
||||
image_file = MediaFile.open(tempfile)
|
||||
image_file.dimensions
|
||||
ensure
|
||||
image_file.close
|
||||
tempfile.close!
|
||||
end
|
||||
end
|
||||
9
app/logical/media_file/video.rb
Normal file
9
app/logical/media_file/video.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class MediaFile::Video < MediaFile
|
||||
def dimensions
|
||||
[video.width, video.height]
|
||||
end
|
||||
|
||||
def video
|
||||
@video ||= FFMPEG::Movie.new(file.path)
|
||||
end
|
||||
end
|
||||
@@ -48,7 +48,7 @@ class UploadService
|
||||
|
||||
@upload.update(status: "processing")
|
||||
|
||||
@upload.file = Utils.get_file_for_upload(@upload, file: @upload.file)
|
||||
@upload.file = Utils.get_file_for_upload(@upload, file: @upload.file&.tempfile)
|
||||
Utils.process_file(upload, @upload.file)
|
||||
|
||||
@upload.save!
|
||||
|
||||
@@ -90,7 +90,7 @@ class UploadService
|
||||
begin
|
||||
upload.update(status: "preprocessing")
|
||||
|
||||
file = Utils.get_file_for_upload(upload, file: params[:file])
|
||||
file = Utils.get_file_for_upload(upload, file: params[:file]&.tempfile)
|
||||
Utils.process_file(upload, file, original_post_id: original_post_id)
|
||||
|
||||
upload.rating = params[:rating]
|
||||
@@ -116,7 +116,7 @@ class UploadService
|
||||
# if a file was uploaded after the preprocessing occurred,
|
||||
# then process the file and overwrite whatever the preprocessor
|
||||
# did
|
||||
Utils.process_file(pred, pred.file) if pred.file.present?
|
||||
Utils.process_file(pred, pred.file.tempfile) if pred.file.present?
|
||||
|
||||
pred.status = "completed"
|
||||
pred.save
|
||||
|
||||
@@ -2,27 +2,6 @@ class UploadService
|
||||
module Utils
|
||||
module_function
|
||||
|
||||
def file_header_to_file_ext(file)
|
||||
case File.read(file.path, 16)
|
||||
when /^\xff\xd8/n
|
||||
"jpg"
|
||||
when /^GIF87a/, /^GIF89a/
|
||||
"gif"
|
||||
when /^\x89PNG\r\n\x1a\n/n
|
||||
"png"
|
||||
when /^CWS/, /^FWS/, /^ZWS/
|
||||
"swf"
|
||||
when /^\x1a\x45\xdf\xa3/n
|
||||
"webm"
|
||||
when /^....ftyp(?:isom|3gp5|mp42|MSNV|avc1)/
|
||||
"mp4"
|
||||
when /^PK\x03\x04/
|
||||
"zip"
|
||||
else
|
||||
"bin"
|
||||
end
|
||||
end
|
||||
|
||||
def corrupt?(filename)
|
||||
image = Vips::Image.new_from_file(filename, fail: true)
|
||||
image.stats
|
||||
@@ -31,36 +10,6 @@ class UploadService
|
||||
true
|
||||
end
|
||||
|
||||
def calculate_ugoira_dimensions(source_path)
|
||||
folder = Zip::File.new(source_path)
|
||||
Tempfile.open("ugoira-dim-") do |tempfile|
|
||||
folder.first.extract(tempfile.path) { true }
|
||||
image_size = ImageSpec.new(tempfile.path)
|
||||
return [image_size.width, image_size.height]
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_dimensions(upload, file)
|
||||
if upload.is_video?
|
||||
video = FFMPEG::Movie.new(file.path)
|
||||
yield(video.width, video.height)
|
||||
|
||||
elsif upload.is_ugoira?
|
||||
w, h = calculate_ugoira_dimensions(file.path)
|
||||
yield(w, h)
|
||||
|
||||
elsif upload.is_image? || upload.is_flash?
|
||||
image_size = ImageSpec.new(file.path)
|
||||
yield(image_size.width, image_size.height)
|
||||
|
||||
elsif upload.file_ext == "bin"
|
||||
yield(0, 0)
|
||||
|
||||
else
|
||||
raise ArgumentError, "unhandled file type (#{upload.file_ext})" # should not happen
|
||||
end
|
||||
end
|
||||
|
||||
def distribute_files(file, record, type, original_post_id: nil)
|
||||
# need to do this for hybrid storage manager
|
||||
post = Post.new
|
||||
@@ -120,15 +69,15 @@ class UploadService
|
||||
end
|
||||
|
||||
def process_file(upload, file, original_post_id: nil)
|
||||
upload.file = file
|
||||
upload.file_ext = Utils.file_header_to_file_ext(file)
|
||||
upload.file_size = file.size
|
||||
upload.md5 = Digest::MD5.file(file.path).hexdigest
|
||||
binding.pry if !file.is_a? Tempfile
|
||||
media_file = MediaFile.open(file)
|
||||
|
||||
Utils.calculate_dimensions(upload, file) do |width, height|
|
||||
upload.image_width = width
|
||||
upload.image_height = height
|
||||
end
|
||||
upload.file = file
|
||||
upload.file_ext = media_file.file_ext.to_s
|
||||
upload.file_size = media_file.file_size
|
||||
upload.md5 = media_file.md5
|
||||
upload.image_width = media_file.width
|
||||
upload.image_height = media_file.height
|
||||
|
||||
upload.validate!(:file)
|
||||
upload.tag_string = "#{upload.tag_string} #{Utils.automatic_tags(upload, file)}"
|
||||
|
||||
Reference in New Issue
Block a user