require 'fastimage' require "English" module Spaceship # Set of utility methods useful to work with media files module Utilities #:nodoc: # Identifies the content_type of a file based on its file name extension. # Supports all formats required by DU-UTC right now (video, images and json) # @param path (String) the path to the file def content_type(path) supported_file_types = { '.jpg' => 'image/jpeg', '.jpeg' => 'image/jpeg', '.png' => 'image/png', '.geojson' => 'application/json', '.mov' => 'video/quicktime', '.m4v' => 'video/mp4', '.mp4' => 'video/mp4', '.txt' => 'text/plain', '.pdf' => 'application/pdf', '.doc' => 'application/msword', '.docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.rtf' => 'application/rtf', '.pages' => 'application/x-iwork-pages-sffpages', '.xls' => 'application/vnd.ms-excel', '.xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', '.numbers' => 'application/x-iwork-numbers-sffnumbers', '.rar' => 'application/x-rar-compressed', '.plist' => 'application/xml', '.crash' => 'text/x-apport', '.avi' => 'video/x-msvideo', '.zip' => 'application/zip' } extension = File.extname(path.downcase) return supported_file_types[extension] if supported_file_types[extension] raise "Unknown content-type for file #{path}" end # Identifies the resolution of a video or an image. # Supports all video and images required by DU-UTC right now # @param path (String) the path to the file def resolution(path) return FastImage.size(path) if content_type(path).start_with?("image") return video_resolution(path) if content_type(path).start_with?("video") raise "Cannot find resolution of file #{path}" end # Is the video or image in portrait mode ? # Supports all video and images required by DU-UTC right now # @param path (String) the path to the file def portrait?(path) resolution = resolution(path) resolution[0] < resolution[1] end # Grabs a screenshot from the specified video at the specified timestamp using `ffmpeg` # @param video_path (String) the path to the video file # @param timestamp (String) the `ffmpeg` timestamp format (e.g. 00.00) # @param dimensions (Array) the dimension of the screenshot to generate # @return the TempFile containing the generated screenshot def grab_video_preview(video_path, timestamp, dimensions) width, height = dimensions require 'tempfile' tmp = Tempfile.new(['video_preview', ".jpg"]) file = tmp.path command = "ffmpeg -y -i \"#{video_path}\" -s #{width}x#{height} -ss \"#{timestamp}\" -vframes 1 \"#{file}\" 2>&1 >/dev/null" # puts "COMMAND: #{command}" `#{command}` raise "Failed to grab screenshot at #{timestamp} from #{video_path} (using #{command})" unless $CHILD_STATUS.to_i == 0 tmp end # identifies the resolution of a video using `ffmpeg` # @param video_path (String) the path to the video file # @return [Array] the resolution of the video def video_resolution(video_path) command = "ffmpeg -i \"#{video_path}\" 2>&1" # puts "COMMAND: #{command}" output = `#{command}` # Note: ffmpeg exits with 1 if no output specified # raise "Failed to find video information from #{video_path} (using #{command})" unless $CHILD_STATUS.to_i == 0 output = output.force_encoding("BINARY") video_infos = output.split("\n").select { |l| l =~ /Stream.*Video/ } raise "Unable to find Stream Video information from ffmpeg output of #{command}" if video_infos.count == 0 video_info = video_infos[0] res = video_info.match(/.* ([0-9]+)x([0-9]+).*/) raise "Unable to parse resolution information from #{video_info}" if res.size < 3 [res[1].to_i, res[2].to_i] end # @return (String) md5 checksum of given file def md5digest(file_path) Digest::MD5.hexdigest(File.read(file_path)) end module_function :content_type, :grab_video_preview, :portrait?, :resolution, :video_resolution, :md5digest end end