Photo Galleries for Jekyll

April 8, 2014

I had a trip to London and Iceland several weeks ago, and I wanted to share some of those photos with people. In the past I've put those sorts of photo galleries on Facebook, but some friends don't have accounts there and I figured I could/should just keep my photos with my other personal stuff here.

Unlike WordPress, Jekyll doesn't really have a concept of photo galleries, and since Jekyll is a static site generator it makes things a little more difficult. I looked through several other posts discussing Jekyll photo galleries, but they all seemed a bit more primitive than what I wanted. I wanted to:

  • stick with existing Jekyll paradigms (e.g. markdown file to static page),
  • retain metadata about my photos (e.g. location data, camera EXIF data),
  • support multiple views about my galleries (e.g. photo list, map, slideshow),
  • ensure photos can have landing pages and be easily navigated, and
  • avoid committing images to my git repository.

After giving it some thought, I realized this was going to be a multi-step process.

  1. Script the process of exporting my existing photos to Jekyll-friendly structures.
  2. Find a Jekyll/Liquid plugin to enumerate directories/files and use the results.
  3. Create templates and pages for my gallery and its photos.
  4. Publish the site!

Step 1: Export existing photo galleries (iPhoto)

I take pretty much all my photos with my phone and those photos then get synced up with iPhoto. At the end of my trip, I browse through the photos and create an album of interesting ones. Normally I don't go through and give every photo a title and description, but if I'm planning on sharing them I add brief notes within iPhoto.

I knew my iPhoto metadata was stored in AlbumData.xml, but I've always had poor performance with massive XML data files. I decided to start with a different approach: AppleScript. The following snippet gets me the file paths of all the photos (in order) from whatever album I ask for:

on run argv
    set output to ""

    tell application "iPhoto"
        set vAlbum to first item of (get every album whose name is (item 1 of argv))
        set vPhotos to get every photo in vAlbum
        
        repeat with vPhoto in vPhotos
            set output to output & original path of vPhoto & "
"
        end repeat
    end tell
        
    return output
end run

So, to get the photos in my album named "London-Iceland Trip" I can do:

$ 
osascript export-iphoto-album.applescript 'London-Iceland Trip'
~/Pictures/iPhoto Library.photolibrary/Masters/2014/03/13/20140313-154842/IMG_0303.JPG
~/Pictures/iPhoto Library.photolibrary/Masters/2014/03/13/20140313-154842/IMG_0308.JPG
...snip...

With some tweaks I can get more than just the path to a photo:

$ 
osascript export-iphoto-album.applescript 'London-Iceland Trip'
altitude: 16
latitude: 51.50038
longitude: -0.12786667
name: A Classic View
date: Thursday, March 6, 2014 at 4:44:12 PM
path: ~/Pictures/iPhoto Library.photolibrary/Masters/2014/03/13/20140313-154842/IMG_0303.JPG
title: A Classic View
------
QCon was held at The Queen Elizabeth II Conference Centre and this was the view out one of the common areas.
------------
...snip...

The next piece is to write something which will clean up the output, resize the photos, and write out all the different Jekyll files. For that I created a PHP script since it was going to be easiest for me. Once complete, I then just pipe the export results to the script and specify the image sizes I want:

$ 
osascript ../jekyll-gallery/export-iphoto.applescript 'London-Iceland Trip' | \
  php ../jekyll-gallery/convert.php 2014-london-iceland-trip \
  --export 96x96 --export 200x200 --export 640 --export 1280
df5150c-a-classic-view...96x96...200x200...640...1280...mdown...done
7cf02b5-night...96x96...200x200...640...1280...mdown...done
...snip...

Once complete, all the resized images are in asset/gallery/2014-london-iceland-trip and my markdown files with the photo details are in gallery/2014-london-iceland-trip and they're easily readable.

Step 2: Jekyll plugin

At a minimum, I wanted to have a listing of all the photos in a gallery index page. After some searches, I found two scripts which became the inspiration for my final plugin. My final plugin looks like:

Tag:
  loopdir
Attributes:
  match: a pattern to match files within the path (e.g. "*.md")
  parse: whether to load the file and parse for YAML front matter
  path: a directory, relative to the site root, to find files
  sort: a property to search by (e.g. "path")
Result:
  An "item" object is exposed to the template with a "page"-like structure.
  If parsing is enabled, the YAML properties are available as "item.title".

Which means I can easily compose a simple photo list with:

{% loopdir path:"gallery/2014-london-iceland-trip" match:"*.md" sort:"ordering" %}
    <a href="/{{ item.fullname }}.html">
        <img alt="Photo: {{ item.title }}" height="200" src="/{{ item.fullname }}~200x200.jpg" title="{{ item.title }}" width="200" />
    </a>
{% endloopdir %}

I reuse this plugin elsewhere for regular directory listings.

Step 3: Create templates

I've started out with two reusable templates in my _includes directory:

  1. Gallery List – a simple listing of thumbnails from all the photos in the gallery
  2. Interactive Map – an interactive map showing where all the photos were taken

I can pass arguments (like the gallery name) to the include which makes it easy to embed a gallery in any page:

{% include gallery_list.html gallery='2014-london-iceland-trip' %}

Step 4: Publish

After generating everything locally, I just have to do a couple steps:

  1. Commit all the new gallery/2014-london-iceland-trip files (and new templates)
  2. Run _build/aws/publish-asset.sh $AWS_S3CMD_CONFIG gallery/2014-london-iceland-trip to upload all the exported JPGs
  3. Run _build/aws/build.sh _build/aws/publish.sh $AWS_S3CMD_CONFIG to upload any modifications from the rest of the site

To make things easier for myself and, possibly, others I put the conversion scripts in my jekyll-gallery repo.

Now I'm able to refer people to the gallery or embed the gallery somewhere useful...

Disabled Content
<div style="line-height:0;padding:4px 0 0 1px;">
  {% loopdir path:"gallery/2014-london-iceland-trip" match:"*.md" sort:"ordering" %}<a href="/{{ item.fullname }}.html" style="display:inline-block;margin:3px;text-decoration:none;"><img alt="Photo: {{ item.title }}" height="48" src="{{ site.asset_prefix }}/{{ item.fullname }}~96x96.jpg" title="{{ item.title }}" width="48" style="padding:1px;" /></a>{% endloopdir %}
</div>