Initial commit

This commit is contained in:
2023-11-12 11:51:39 +01:00
parent dfeb535db1
commit 5445a8a8f7
14 changed files with 629 additions and 7 deletions

9
lib/jekyll-webp.rb Normal file
View 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

View 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

View 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
View 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

View 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