Skip to content

Commit b8d71ff

Browse files
author
Aaron Purnomo Murniadi
committed
Add support for threaded/parallel generation of webp
1 parent 162996d commit b8d71ff

File tree

4 files changed

+92
-24
lines changed

4 files changed

+92
-24
lines changed

jekyll-webp.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# coding: utf-8
2+
require 'date'
23
require_relative 'lib/jekyll-webp/version'
34

45
Gem::Specification.new do |spec|

lib/jekyll-webp/defaults.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ module Webp
4343

4444
# List of files or directories to explicitly include
4545
# e.g. single files outside of the main image directories
46-
'include' => []
46+
'include' => [],
47+
48+
# Number of threads to use for parallel generation.
49+
# 0 or 1 means sequential (no parallelism).
50+
'threads' => 0
4751
}
4852

4953
end # module Webp

lib/jekyll-webp/version.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module Jekyll
22
module Webp
3-
VERSION = "1.0.0"
3+
VERSION = "1.0.1"
44
# When modifying remember to issue a new tag command in git before committing, then push the new tag
5-
# git tag -a v1.0.0 -m "Gem v1.0.0"
5+
# git tag -a v1.0.1 -m "Gem v1.0.1"
66
# git push origin --tags
77
end #module Webp
88
end #module Jekyll

lib/jekyll-webp/webpGenerator.rb

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ def generate(site)
5353
# Counting the number of files generated
5454
file_count = 0
5555

56-
# Iterate through every image in each of the image folders and create a webp image
57-
# if one has not been created already for that image.
56+
# Collection of tasks to be processed
57+
# Each task is a hash with all necessary info to process the file
58+
tasks = []
59+
60+
# Iterate through every image in each of the image folders and collect files to process
5861
for imgdir in @config['img_dir']
5962
imgdir_source = File.join(site.source, imgdir)
6063
imgdir_destination = File.join(site.dest, imgdir)
@@ -83,28 +86,88 @@ def generate(site)
8386
FileUtils::mkdir_p(imgdir_destination + imgfile_relative_path)
8487
outfile_fullpath_webp = File.join(imgdir_destination + imgfile_relative_path, outfile_filename)
8588

86-
# Check if the file already has a webp alternative?
87-
# If we're force rebuilding all webp files then ignore the check
88-
# also check the modified time on the files to ensure that the webp file
89-
# is newer than the source file, if not then regenerate
90-
if @config['regenerate'] || !File.file?(outfile_fullpath_webp) ||
91-
File.mtime(outfile_fullpath_webp) <= File.mtime(imgfile)
92-
Jekyll.logger.info "WebP:", "Change to source image file #{imgfile} detected, regenerating WebP"
93-
94-
# Generate the file
95-
WebpExec.run(@config['quality'], @config['flags'], imgfile, outfile_fullpath_webp, @config['webp_path'])
96-
file_count += 1
97-
end
98-
if File.file?(outfile_fullpath_webp)
99-
# Keep the webp file from being cleaned by Jekyll
100-
site.static_files << WebpFile.new(site,
101-
site.dest,
102-
File.join(imgdir, imgfile_relative_path),
103-
outfile_filename)
104-
end
89+
# Add to tasks list
90+
tasks << {
91+
'imgfile' => imgfile,
92+
'outfile_fullpath_webp' => outfile_fullpath_webp,
93+
'imgdir' => imgdir,
94+
'imgfile_relative_path' => imgfile_relative_path,
95+
'outfile_filename' => outfile_filename
96+
}
10597
end # dir.foreach
10698
end # img_dir
10799

100+
101+
# Define the work block
102+
mutex = Mutex.new
103+
104+
process_file = Proc.new do |task|
105+
imgfile = task['imgfile']
106+
outfile_fullpath_webp = task['outfile_fullpath_webp']
107+
imgdir = task['imgdir']
108+
imgfile_relative_path = task['imgfile_relative_path']
109+
outfile_filename = task['outfile_filename']
110+
111+
# Check if the file already has a webp alternative?
112+
# If we're force rebuilding all webp files then ignore the check
113+
# also check the modified time on the files to ensure that the webp file
114+
# is newer than the source file, if not then regenerate
115+
if @config['regenerate'] || !File.file?(outfile_fullpath_webp) ||
116+
File.mtime(outfile_fullpath_webp) <= File.mtime(imgfile)
117+
Jekyll.logger.info "WebP:", "Change to source image file #{imgfile} detected, regenerating WebP"
118+
119+
# Generate the file
120+
WebpExec.run(@config['quality'], @config['flags'], imgfile, outfile_fullpath_webp, @config['webp_path'])
121+
122+
# Thread-safe increment
123+
if @config['threads'] && @config['threads'] > 1
124+
mutex.synchronize { file_count += 1 }
125+
else
126+
file_count += 1
127+
end
128+
end
129+
130+
if File.file?(outfile_fullpath_webp)
131+
# Keep the webp file from being cleaned by Jekyll
132+
if @config['threads'] && @config['threads'] > 1
133+
mutex.synchronize do
134+
site.static_files << WebpFile.new(site, site.dest, File.join(imgdir, imgfile_relative_path), outfile_filename)
135+
end
136+
else
137+
site.static_files << WebpFile.new(site, site.dest, File.join(imgdir, imgfile_relative_path), outfile_filename)
138+
end
139+
end
140+
end
141+
142+
# Execute tasks
143+
thread_count = @config['threads'].to_i
144+
145+
if thread_count > 1
146+
Jekyll.logger.info "WebP:", "Parallel processing enabled with #{thread_count} threads"
147+
queue = Queue.new
148+
tasks.each { |t| queue << t }
149+
150+
workers = (1..thread_count).map do
151+
Thread.new do
152+
begin
153+
while !queue.empty?
154+
# Non-blocking pop; rescue if empty
155+
task = queue.pop(true) rescue nil
156+
process_file.call(task) if task
157+
end
158+
rescue ThreadError
159+
# Queue empty
160+
end
161+
end
162+
end
163+
workers.each(&:join)
164+
else
165+
# Sequential execution
166+
tasks.each do |task|
167+
process_file.call(task)
168+
end
169+
end
170+
108171
Jekyll.logger.info "WebP:","Generator Complete: #{file_count} file(s) generated"
109172

110173
end #function generate

0 commit comments

Comments
 (0)