Initial commit
This commit is contained in:
9
lib/jekyll-webp.rb
Normal file
9
lib/jekyll-webp.rb
Normal file
@ -0,0 +1,9 @@
|
||||
require "jekyll-webp/version"
|
||||
require "jekyll-webp/defaults"
|
||||
require "jekyll-webp/webpExec"
|
||||
require "jekyll-webp/webpGenerator"
|
||||
|
||||
module Jekyll
|
||||
module Webp
|
||||
end # module Webp
|
||||
end # module Jekyll
|
63
lib/jekyll-webp/defaults.rb
Normal file
63
lib/jekyll-webp/defaults.rb
Normal file
@ -0,0 +1,63 @@
|
||||
module Jekyll
|
||||
module Webp
|
||||
|
||||
# The default configuration for the Webp generator
|
||||
# The values here represent the defaults if nothing is set
|
||||
DEFAULT = {
|
||||
'enabled' => false,
|
||||
|
||||
# The quality of the webp conversion 0 to 100 (where 100 is least lossy)
|
||||
'quality' => 75,
|
||||
|
||||
# Other flags to pass to the webp binary. For a list of valid parameters check here:
|
||||
# https://developers.google.com/speed/webp/docs/cwebp#options
|
||||
'flags' => "-m 4 -pass 4 -af",
|
||||
|
||||
# List of directories containing images to optimize, Nested directories only be checked if `nested` is true
|
||||
'img_dir' => ["/img"],
|
||||
|
||||
# Whether to search in nested directories or not
|
||||
'nested' => false,
|
||||
|
||||
# add ".gif" to the format list to generate webp for animated gifs as well
|
||||
'formats' => [".jpeg", ".jpg", ".png", ".tiff"],
|
||||
|
||||
# append .webp to existing extension instead of replacing it
|
||||
# (Enables more efficient nginx rules.
|
||||
# See http://www.lazutkin.com/blog/2014/02/23/serve-files-with-nginx-conditionally/)
|
||||
'append_ext' => false,
|
||||
|
||||
# File extensions for animated gif files
|
||||
'gifs' => [".gif"],
|
||||
|
||||
# Set to true to always regenerate existing webp files
|
||||
'regenerate'=> false,
|
||||
|
||||
# Local path to the WebP utilities to use (relative or absolute)
|
||||
# Leave as nil to use the cmd line utilities shipped with the gem, override to use your local install
|
||||
'webp_path' => nil,
|
||||
|
||||
# List of files or directories to exclude
|
||||
# e.g. custom or hand generated webp conversion files
|
||||
'exclude' => [],
|
||||
|
||||
# List of files or directories to explicitly include
|
||||
# e.g. single files outside of the main image directories
|
||||
'include' => [],
|
||||
|
||||
# Use a different output subdirectory
|
||||
# e.g. value of "/optimized" will create files at "/source/optimized" instead of "/source"
|
||||
'output_img_sub_dir' => "",
|
||||
|
||||
# Generate thumbnails
|
||||
'thumbs' => false,
|
||||
|
||||
# Thumbnails sub directory
|
||||
'thumbs_dir' => "/thumbs",
|
||||
|
||||
# generate .5x images
|
||||
'generate_50p' => false
|
||||
}
|
||||
|
||||
end # module Webp
|
||||
end # module Jekyll
|
8
lib/jekyll-webp/version.rb
Normal file
8
lib/jekyll-webp/version.rb
Normal file
@ -0,0 +1,8 @@
|
||||
module Jekyll
|
||||
module Webp
|
||||
VERSION = "1.1.1"
|
||||
# When modifying remember to issue a new tag command in git before committing, then push the new tag
|
||||
# git tag -a v1.0.0 -m "Gem v1.0.0"
|
||||
# git push origin --tags
|
||||
end #module Webp
|
||||
end #module Jekyll
|
110
lib/jekyll-webp/webpExec.rb
Normal file
110
lib/jekyll-webp/webpExec.rb
Normal file
@ -0,0 +1,110 @@
|
||||
require 'open3'
|
||||
require 'fastimage'
|
||||
|
||||
module Jekyll
|
||||
module Webp
|
||||
|
||||
class WebpExec
|
||||
|
||||
#
|
||||
# Runs the WebP executable for the given input parameters
|
||||
# the function detects the OS platform and architecture automatically
|
||||
#
|
||||
def self.run(quality, flags, input_file, output_file, webp_bin_fullpath)
|
||||
|
||||
if webp_bin_fullpath
|
||||
full_path = webp_bin_fullpath
|
||||
else
|
||||
# What is the path to the execs inside the gem? perhaps just bin/?
|
||||
bin_path = "bin/"
|
||||
|
||||
# What is the OS and architecture specific executable name?
|
||||
exe_name = WebpExec.exe_name
|
||||
|
||||
# We need to locate the Gems bin path as we're currently running inside the
|
||||
# jekyll site working directory
|
||||
# http://stackoverflow.com/a/10083594/779521
|
||||
gem_spec = Gem::Specification.find_by_name("jekyll-webp")
|
||||
gem_root = gem_spec.gem_dir
|
||||
|
||||
# Construct the full path to the executable
|
||||
full_path = File.join(gem_root, bin_path, exe_name)
|
||||
end
|
||||
|
||||
# Construct the full program call
|
||||
cmd = "\"#{full_path}\" -quiet -mt -q #{quality.to_s} #{flags} \"#{input_file}\" -o \"#{output_file}\""
|
||||
|
||||
# Execute the command
|
||||
exit_code = 0
|
||||
error = ""
|
||||
output = ""
|
||||
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
||||
stdin.close # we don't pass any input to the process
|
||||
output = stdout.gets
|
||||
error = stderr.gets
|
||||
|
||||
exit_code = wait_thr.value
|
||||
end
|
||||
|
||||
if exit_code != 0
|
||||
# Jekyll.logger.error("WebP:","Conversion for image #{input_file} failed, no webp version could be created for this image")
|
||||
Jekyll.logger.error("WebP:","cwebp returned #{exit_code} with error #{error}")
|
||||
end
|
||||
|
||||
# Return any captured return value
|
||||
return [output, error]
|
||||
end #function run
|
||||
|
||||
#
|
||||
# Returns the correct executable name depending on the OS platform and OS architecture
|
||||
#
|
||||
def self.exe_name
|
||||
if OS.mac?
|
||||
return "osx-cwebp"
|
||||
elsif OS.windows?
|
||||
if OS.x32?
|
||||
return "win-x86-cwebp.exe"
|
||||
else
|
||||
return "win-x64-cwebp.exe"
|
||||
end
|
||||
elsif OS.unix? || OS.linux?
|
||||
if OS.x32?
|
||||
return "linux-x86-cwebp"
|
||||
else
|
||||
return "linux-x64-cwebp"
|
||||
end
|
||||
else
|
||||
raise ArgumentError.new("OS platform could not be identified (gem can only be run on linux,osx or windows)")
|
||||
end
|
||||
end #function exe_name
|
||||
|
||||
end #class WebpExec
|
||||
|
||||
end #module Webp
|
||||
|
||||
module OS
|
||||
def OS.windows?
|
||||
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
||||
end
|
||||
|
||||
def OS.mac?
|
||||
(/darwin/ =~ RUBY_PLATFORM) != nil
|
||||
end
|
||||
|
||||
def OS.unix?
|
||||
!OS.windows?
|
||||
end
|
||||
|
||||
def OS.linux?
|
||||
OS.unix? and not OS.mac?
|
||||
end
|
||||
|
||||
def OS.x32?
|
||||
return 1.size != 8
|
||||
end
|
||||
|
||||
def OS.x64?
|
||||
return 1.size == 8
|
||||
end
|
||||
end #module OS
|
||||
end #module Jekyll
|
157
lib/jekyll-webp/webpGenerator.rb
Normal file
157
lib/jekyll-webp/webpGenerator.rb
Normal file
@ -0,0 +1,157 @@
|
||||
require 'jekyll/document'
|
||||
require 'fileutils'
|
||||
|
||||
module Jekyll
|
||||
module Webp
|
||||
|
||||
#
|
||||
# A static file to hold the generated webp image after generation
|
||||
# so that Jekyll will copy it into the site output directory
|
||||
class WebpFile < StaticFile
|
||||
def write(dest)
|
||||
true # Recover from strange exception when starting server without --auto
|
||||
end
|
||||
end #class WebpFile
|
||||
|
||||
class WebpGenerator < Generator
|
||||
# This generator is safe from arbitrary code execution.
|
||||
safe true
|
||||
|
||||
# This generator should be passive with regard to its execution
|
||||
priority :lowest
|
||||
|
||||
# Generate paginated pages if necessary (Default entry point)
|
||||
# site - The Site.
|
||||
#
|
||||
# Returns nothing.
|
||||
def generate(site)
|
||||
|
||||
# Retrieve and merge the configuration from the site yml file
|
||||
@config = DEFAULT.merge(site.config['webp'] || {})
|
||||
|
||||
# If disabled then simply quit
|
||||
if !@config['enabled']
|
||||
Jekyll.logger.info "WebP:","Disabled in site.config."
|
||||
return
|
||||
end
|
||||
|
||||
Jekyll.logger.debug "WebP:","Starting"
|
||||
|
||||
# If the site destination directory has not yet been created then create it now. Otherwise, we cannot write our file there.
|
||||
Dir::mkdir(site.dest) if !File.directory? site.dest
|
||||
|
||||
# If nesting is enabled, get all the nested directories too
|
||||
if @config['nested']
|
||||
newdir = []
|
||||
for imgdir in @config['img_dir']
|
||||
# Get every directory below (and including) imgdir, recursively
|
||||
newdir.concat(Dir.glob(imgdir + "/**/"))
|
||||
end
|
||||
@config['img_dir'] = newdir
|
||||
end
|
||||
|
||||
# Counting the number of files generated
|
||||
file_count = 0
|
||||
thumb_count = 0
|
||||
|
||||
# Iterate through every image in each of the image folders and create a webp image
|
||||
# if one has not been created already for that image.
|
||||
for imgdir in @config['img_dir']
|
||||
imgdir_source = File.join(site.source, imgdir)
|
||||
imgdir_destination = File.join(site.dest, imgdir)
|
||||
FileUtils::mkdir_p(imgdir_destination)
|
||||
if @config['output_img_sub_dir'] != ""
|
||||
FileUtils::mkdir_p(File.join(imgdir_destination, @config['output_img_sub_dir']))
|
||||
end
|
||||
if @config['thumbs']
|
||||
FileUtils::mkdir_p(File.join(imgdir_destination, @config['thumbs_dir']))
|
||||
end
|
||||
Jekyll.logger.info "WebP:","Processing #{imgdir_source}"
|
||||
|
||||
# handle only jpg, jpeg, png and gif
|
||||
for imgfile in Dir[imgdir_source + "**/*.*"]
|
||||
imgfile_relative_path = File.dirname(imgfile.sub(imgdir_source, ""))
|
||||
|
||||
# Skip empty stuff
|
||||
file_ext = File.extname(imgfile).downcase
|
||||
|
||||
# If the file is not one of the supported formats, exit early
|
||||
next if !@config['formats'].include? file_ext
|
||||
|
||||
# TODO: Do an exclude check
|
||||
|
||||
# Create the output file path
|
||||
outfile_filename = if @config['append_ext']
|
||||
File.basename(imgfile) + '.webp'
|
||||
else
|
||||
file_noext = File.basename(imgfile, file_ext)
|
||||
file_noext + ".webp"
|
||||
end
|
||||
|
||||
small_outfile_filename = File.basename(imgfile, file_ext) + "-small" + ".webp"
|
||||
|
||||
FileUtils::mkdir_p(imgdir_destination + imgfile_relative_path)
|
||||
outfile_fullpath_webp = File.join(imgdir_destination + imgfile_relative_path, @config['output_img_sub_dir'], outfile_filename)
|
||||
small_outfile_fullpath_webp = File.join(imgdir_destination + imgfile_relative_path, @config['output_img_sub_dir'], small_outfile_filename)
|
||||
thumb_outfile_fullpath_webp = File.join(imgdir_destination + imgfile_relative_path, @config['thumbs_dir'], outfile_filename)
|
||||
|
||||
# Check if the file already has a webp alternative?
|
||||
# If we're force rebuilding all webp files then ignore the check
|
||||
# also check the modified time on the files to ensure that the webp file
|
||||
# is newer than the source file, if not then regenerate
|
||||
if @config['regenerate'] || !File.file?(outfile_fullpath_webp) ||
|
||||
File.mtime(outfile_fullpath_webp) <= File.mtime(imgfile)
|
||||
Jekyll.logger.info "WebP:", "Change to source image file #{imgfile} detected, regenerating WebP"
|
||||
|
||||
# Generate the file
|
||||
WebpExec.run(@config['quality'], @config['flags'], imgfile, outfile_fullpath_webp, @config['webp_path'])
|
||||
file_count += 1
|
||||
|
||||
if @config['generate_50p']
|
||||
# Get the image size
|
||||
Jekyll.logger.info "WebP:", "Generating small image file #{outfile_filename}"
|
||||
image_size = FastImage.size(imgfile, :raise_on_failure=>true, :timeout=>2.0)
|
||||
h_width = image_size[0] / 2
|
||||
size_flags = "-resize #{h_width} 0" + " " + @config['flags']
|
||||
WebpExec.run(@config['quality'], size_flags, imgfile, small_outfile_fullpath_webp, @config['webp_path'])
|
||||
end
|
||||
|
||||
# Generate the thumbnails
|
||||
if @config['thumbs']
|
||||
Jekyll.logger.info "WebP:", "Generating thumbnail for #{outfile_filename} in #{@config['thumbs_dir']}"
|
||||
thumb_flags = "-resize 400 0" + " " + @config['flags']
|
||||
WebpExec.run(@config['quality'], thumb_flags, imgfile, thumb_outfile_fullpath_webp, @config['webp_path'])
|
||||
thumb_count += 1
|
||||
end
|
||||
end
|
||||
|
||||
if File.file?(outfile_fullpath_webp)
|
||||
# Keep the webp file from being cleaned by Jekyll
|
||||
site.static_files << WebpFile.new(site,
|
||||
site.dest,
|
||||
File.join(imgdir, imgfile_relative_path, @config['output_img_sub_dir']),
|
||||
outfile_filename)
|
||||
if @config['thumbs']
|
||||
site.static_files << WebpFile.new(site,
|
||||
site.dest,
|
||||
File.join(imgdir, imgfile_relative_path, @config['thumbs_dir']),
|
||||
outfile_filename)
|
||||
end
|
||||
if @config['generate_50p']
|
||||
site.static_files << WebpFile.new(site,
|
||||
site.dest,
|
||||
File.join(imgdir, imgfile_relative_path, @config['output_img_sub_dir']),
|
||||
small_outfile_filename)
|
||||
end
|
||||
end
|
||||
end # dir.foreach
|
||||
end # img_dir
|
||||
|
||||
Jekyll.logger.info "WebP:","Generator Complete: #{file_count} file(s) generated #{thumb_count} thumbnail(s) generated"
|
||||
|
||||
end #function generate
|
||||
|
||||
end #class WebPGenerator
|
||||
|
||||
end #module Webp
|
||||
end #module Jekyll
|
Reference in New Issue
Block a user