class Aspera::Preview::Generator

generate one preview file for one format for one file at a time

Constants

PREVIEW_FORMATS

values for preview_format : output format

Attributes

conversion_type[R]

CLI needs to know conversion type to know if need skip it

Public Class Methods

new(options,src,dst,main_temp_dir,api_mime_type) click to toggle source

@param src source file path @param dst destination file path @param api_mime_type optional mime type as provided by node api (or nil) node API mime types are from: svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types supported preview type is one of Preview::PREVIEW_FORMATS the resulting preview file type is taken from destination file extension. conversion methods are provided by private methods: convert_<conversion_type>to<preview_format> (combi = combination of source file type and destination format)

-> conversion_type is one of FileTypes::CONVERSION_TYPES
-> preview_format is one of Generator::PREVIEW_FORMATS

the conversion video->mp4 is implemented in methods: convert_video_to_mp4_using_<video_conversion>

-> conversion method is one of Generator::VIDEO_CONVERSION_METHODS
# File lib/aspera/preview/generator.rb, line 28
def initialize(options,src,dst,main_temp_dir,api_mime_type)
  @options=options
  @source_file_path=src
  @destination_file_path=dst
  @temp_folder=File.join(main_temp_dir,@source_file_path.split('/').last.gsub(/\s/, '_').gsub(/\W/, ''))
  # extract preview format from extension of target file
  @preview_format_symb=File.extname(@destination_file_path).gsub(/^\./,'').to_sym
  @conversion_type=FileTypes.instance.conversion_type(@source_file_path,api_mime_type)
end

Public Instance Methods

generate() click to toggle source

create preview as specified in constructor

# File lib/aspera/preview/generator.rb, line 62
def generate
  raise "could not detect type of file" if @conversion_type.nil?
  method_symb=processing_method_symb
  Log.log.info("#{@source_file_path}->#{@destination_file_path} (#{method_symb})")
  begin
    self.send(method_symb)
    # check that generated size does not exceed maximum
    result_size=File.size(@destination_file_path)
    if result_size > @options.max_size
      Log.log.warn("preview size exceeds maximum #{result_size} > #{@options.max_size}")
    end
  rescue => e
    Log.log.error("#{e.message}")
    Log.log.debug(e.backtrace.join("\n").red)
    FileUtils.cp(File.expand_path(@preview_format_symb.eql?(:mp4)?'video_error.png':'image_error.png',File.dirname(__FILE__)),@destination_file_path)
  ensure
    FileUtils.rm_rf(@temp_folder)
  end
end
processing_method_symb() click to toggle source

name of processing method in this object combination of: conversion type and output format (and video_conversion for video)

# File lib/aspera/preview/generator.rb, line 40
def processing_method_symb
  name="convert_#{@conversion_type}_to_#{@preview_format_symb}"
  if @conversion_type.eql?(:video)
    case @preview_format_symb
    when :mp4
      name="#{name}_using_#{@options.video_conversion}"
    when :png
      name="#{name}_using_#{@options.video_png_conv}"
    end
  end
  Log.log.debug("method: #{name}")
  return name.to_sym
end
supported?() click to toggle source

@return true if conversion is supported. for instance, plaintext to mp4 is not supported

# File lib/aspera/preview/generator.rb, line 56
def supported?
  return false if @conversion_type.nil?
  return respond_to?(processing_method_symb,true)
end

Private Instance Methods

convert_image_to_png() click to toggle source
# File lib/aspera/preview/generator.rb, line 222
def convert_image_to_png()
  Utils.external_command(:convert,[
    '-auto-orient',
    '-thumbnail',"#{@options.thumb_img_size}x#{@options.thumb_img_size}>",
    '-quality',95,
    '+dither',
    '-posterize',40,
    "#{@source_file_path}[0]",
    @destination_file_path])
  Utils.external_command(:optipng,[@destination_file_path])
end
convert_office_to_png() click to toggle source
# File lib/aspera/preview/generator.rb, line 203
def convert_office_to_png()
  tmp_pdf_file=File.join(this_tmpdir,File.basename(@source_file_path,File.extname(@source_file_path))+'.pdf')
  Utils.external_command(:unoconv,[
    '-f','pdf',
    '-o',tmp_pdf_file,
    @source_file_path])
  convert_pdf_to_png(tmp_pdf_file)
end
convert_pdf_to_png(source_file_path=nil) click to toggle source
# File lib/aspera/preview/generator.rb, line 212
def convert_pdf_to_png(source_file_path=nil)
  source_file_path||=@source_file_path
  Utils.external_command(:convert,[
    '-size',"x#{@options.thumb_img_size}",
    '-background','white',
    '-flatten',
    "#{source_file_path}[0]",
    @destination_file_path])
end
convert_plaintext_to_png() click to toggle source

text to png

# File lib/aspera/preview/generator.rb, line 235
def convert_plaintext_to_png()
  # get 100 first lines of text file
  first_lines=File.open(@source_file_path){|f|100.times.map{f.readline rescue ''}}.join
  Utils.external_command(:convert,[
    '-size',"#{@options.thumb_img_size}x#{@options.thumb_img_size}",
    'xc:white',
    '-font','Courier',
    '-pointsize',12,
    '-fill','black',
    '-annotate','+15+15',first_lines,
    '-trim',
    '-bordercolor','#FFF',
    '-border',10,
    '+repage',
    @destination_file_path])
end
convert_video_to_mp4_using_blend() click to toggle source
# File lib/aspera/preview/generator.rb, line 99
def convert_video_to_mp4_using_blend()
  p_duration = Utils.video_get_duration(@source_file_path)
  p_start_offset = @options.video_start_sec.to_i
  p_keyframecount = @options.blend_keyframes.to_i
  last_keyframe = nil
  current_index = 1
  1.upto(p_keyframecount) do |i|
    offset_seconds=get_offset(p_duration,p_start_offset,p_keyframecount,i)
    Utils.video_dump_frame(@source_file_path, offset_seconds, @options.video_scale, this_tmpdir, current_index)
    Utils.video_dupe_frame(this_tmpdir, current_index, @options.blend_pauseframes)
    Utils.video_blend_frames(this_tmpdir,last_keyframe, current_index) unless last_keyframe.nil?
    # go to last dupe frame
    last_keyframe = current_index + @options.blend_pauseframes
    # go after last dupe frame and keep space to blend
    current_index = last_keyframe + 1 + @options.blend_transframes
  end
  Utils.ffmpeg(
  in_f: Utils.ffmpeg_fmt(this_tmpdir),
  in_p: ['-framerate',@options.blend_fps],
  out_f: @destination_file_path,
  out_p: [
    '-filter:v',"scale='trunc(iw/2)*2:trunc(ih/2)*2'",
    '-codec:v','libx264',
    '-r',30,
    '-pix_fmt','yuv420p'])
end
convert_video_to_mp4_using_clips() click to toggle source

generate n clips starting at offset

# File lib/aspera/preview/generator.rb, line 127
def convert_video_to_mp4_using_clips()
  p_duration = Utils.video_get_duration(@source_file_path)
  filelist = File.join(this_tmpdir,'clip_files.txt')
  File.open(filelist, 'w+') do |f|
    1.upto(@options.clips_count.to_i) do |i|
      offset_seconds=get_offset(p_duration,@options.video_start_sec.to_i,@options.clips_count.to_i,i)
      tmpfilename=sprintf("clip%04d.mp4",i)
      Utils.ffmpeg(
      in_f: @source_file_path,
      in_p: ['-ss',0.9*offset_seconds],
      out_f: File.join(this_tmpdir,tmpfilename),
      out_p: [
        '-ss',0.1*offset_seconds,
        '-t',@options.clips_length,
        '-filter:v',"scale=#{@options.video_scale}",
        '-codec:a','libmp3lame'])
      f.puts("file '#{tmpfilename}'")
    end
  end
  # concat clips
  Utils.ffmpeg(
  in_f: filelist,
  in_p: ['-f','concat'],
  out_f: @destination_file_path,
  out_p: ['-codec','copy'])
end
convert_video_to_mp4_using_reencode() click to toggle source

do a simple reencoding

# File lib/aspera/preview/generator.rb, line 155
def convert_video_to_mp4_using_reencode()
  Utils.ffmpeg(
  in_f: @source_file_path,
  out_f: @destination_file_path,
  out_p: [
    '-t','60',
    '-codec:v','libx264',
    '-profile:v','high',
    '-pix_fmt','yuv420p',
    '-preset','slow',
    '-b:v','500k',
    '-maxrate','500k',
    '-bufsize','1000k',
    '-filter:v',"scale=#{@options.video_scale}",
    '-threads','0',
    '-codec:a','libmp3lame',
    '-ac','2',
    '-b:a','128k',
    '-movflags','faststart'])
end
convert_video_to_png_using_animated() click to toggle source

trac.ffmpeg.org/wiki/SponsoringPrograms/GSoC/2015#AnimatedPortableNetworkGraphicsAPNG ffmpeg -h muxer=apng thumb is 32x32 ffmpeg output.png

# File lib/aspera/preview/generator.rb, line 188
def convert_video_to_png_using_animated()
  Utils.ffmpeg(
  in_f: @source_file_path,
  in_p: [
    '-ss',10, # seek to input position
    '-t',20, # max seconds
  ],
  out_f: @destination_file_path,
  out_p: [
    '-vf','fps=5,scale=120:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse',
    '-loop',0,
    '-f','gif'
  ])
end
convert_video_to_png_using_fixed() click to toggle source
# File lib/aspera/preview/generator.rb, line 176
def convert_video_to_png_using_fixed()
  Utils.video_dump_frame(
  @source_file_path,
  Utils.video_get_duration(@source_file_path)*@options.thumb_vid_fraction,
  @options.thumb_vid_scale,
  @destination_file_path)
end
get_offset(duration, start_offset, total_count, index) click to toggle source

@return offset in seconds suitable for ffmpeg -ss option @param duration of video @param start_offset of parts @param total_count of parts @param index of part (start at 1)

# File lib/aspera/preview/generator.rb, line 95
def get_offset(duration, start_offset, total_count, index)
  return start_offset + (index-1)*(1.0*duration - start_offset) / total_count
end
this_tmpdir() click to toggle source

creates a unique temp folder for file

# File lib/aspera/preview/generator.rb, line 85
def this_tmpdir
  FileUtils.mkdir_p(@temp_folder)
  return @temp_folder
end