This was a short diversion to see if I could help get a simple webcomic up and running online. It needed to be simple to update and lightweight. Unfortunately, all the webcomic frameworks I could find look like this. WordPress isn’t terrible to set up, but it can be a bother to configure. Jekyll sounded like a fun way to get the project started.
As far as I could tell, no one else had done something like this, or at least published how to do it. The plugin needed to be able to:
A few hours later, this was the result:
module Jekyll
class ComicPage < Page
def initialize(site, base, comic, img_path, img_file, total)
@site = site
@base = base
@dir = "/comics/#{comic}"
@comic = comic
@page_num = File.basename(img_file, File.extname(img_file))
@total = total
@name = @page_num + '.html'
puts "Generating #{@comic} page #{@page_num}"
self.process(@name)
self.data ||= Hash.new
self.data['src'] = "/#{img_path.chomp('/')}/#{img_file}"
self.data['next'] = next_page
self.data['prev'] = prev_page
self.data['num'] = @page_num
self.data['last'] = last_page
self.data['first'] = first_page
self.read_yaml(File.join(base, '_layouts'), comic + '.html')
end
def next_page
return nil if @page_num.to_i >= (@total - 1)
return @dir + '/' + pad(@page_num.to_i + 1) + '.html'
end
def prev_page
return nil if @page_num.to_i == 0
return @dir + '/' + pad(@page_num.to_i - 1) + '.html'
end
def last_page
return @dir + '/last.html'
end
def first_page
return @dir + '/' + pad(0) + '.html'
end
def pad(i)
width = @page_num.length
return i.to_s.rjust(width, '0')
end
end
class ComicLastPage < ComicPage
def initialize(site, base, comic, img_path, img_file, total)
super
@name = 'last.html'
self.process(@name)
end
end
class ComicIndexPage < Page
def initialize(site, base, comic, images)
@site = site
@base = base
@dir = "/comics/#{comic}"
@comic = comic
@name = 'index.html'
self.data ||= Hash.new
self.data['images'] = images.collect{ |img| File.basename(img, File.extname(img)) }
self.process(@name)
self.read_yaml(File.join(base, '_layouts'), "#{comic}_index.html")
end
end
class GenerateComics < Generator
safe true
priority :normal
def generate(site)
return unless Dir.exists?('_comics')
Dir.glob('_comics/*.yml').each do |file|
comic = File.basename(file, '.yml')
config = YAML.load_file(file)
self.write_comic(site, comic, config['images'])
end
end
def write_comic(site, comic, img_path)
images = Dir.glob("#{img_path}/*.{jpg,png,jpeg,gif,bmp}").select { |f| File.basename(f) =~ /(\d)+\.(jpg|png|jpeg|gif|bmp)/ }
images.each do |file|
c = ComicPage.new(site, site.source, comic, img_path, File.basename(file), images.length)
c.render(site.layouts, site.site_payload)
c.write(site.dest)
site.pages << c
site.static_files << c
end
l = ComicLastPage.new(site, site.source, comic, img_path, File.basename(images[-1]), images.length)
l.render(site.layouts, site.site_payload)
site.pages << l
site.static_files << l
i = ComicIndexPage.new(site, site.source, comic, images)
i.render(site.layouts, site.site_payload)
site.pages << i
site.static_files << i
end
end
end
The plugin works by adding a _comics folder, with YAML configuration files for each comic you want to roll into the site. At this point, all it cares about is a path to some image files. Each ComicPage corresponds to a single image file, which also needs to be named numerically, or at the very least, in Unix file sort order.
A sample comic config file (let’s call it ‘mycomic.yml’) would look like this:
images: images/mycomic/
The generator pumps out ComicPages, one per image, organized under /comics/. There’s probably a more efficient way to do this, but the code works well enough for a couple hundred comic pages. I’ve also made the code available as a GitHub Gist.