GitHub: HugoPhotoSwipe

A tool for creating PhotoSwipe galleries in Hugo

For this site, I wanted to create photo galleries using PhotoSwipe by Dmitry Semenov, because I really like the user interface that it gives. The PhotoSwipe viewer looks very nice on mobile and desktop browsers and has built-in support for captions. If you haven’t seen any galleries with PhotoSwipe yet, you can take a moment to browse through some galleries of mine.

I’m using Hugo by Steve Francia for this site and creating a PhotoSwipe gallery in Hugo was not immediately trivial. PhotoSwipe requires predefined image sizes and obviously I didn’t want to set this by hand for every gallery. Moreover, I wanted to serve responsive images for the mobile version of the site and use best practices for SEO. Search Engine Optimization. For images to be properly identified by search engines, it's good to have descriptive filenames, alt-text, and captions. Finally, I wanted to create nice thumbnails automatically using SmartCrop and make it easy to add or change photos in the gallery.

This problem has bugged some other people too. The first solution I could find was by Tom Helmer Hansen who wrote a blog post about how he tackled this problem using shortcodes. This seems like a workable solution, but didn’t go quite as far as I’d like because image sizes still needed to be set by hand. A recent Bash script written by Björn Pohl goes a bit further, but still requires more manual intervention than I would like. Finally, there’s this GitHub repository by Ziosi Brunetto Marco, which contains a Go script for generating the Markdown, similar to the Bash script by Björn Pohl but with detection of image sizes. Unfortunately, none of these solutions gave the automatic workflow I would like.

So I made HugoPhotoSwipe, a command line application for creating and updating PhotoSwipe galleries in Hugo. HugoPhotoSwipe is also available from PyPI. The idea is to have a directory of photos for each gallery and a corresponding description file where captions of the photos can be defined. HugoPhotoSwipe then takes care of creating responsive image sizes and thumbnails, as well as a markdown file which can be processed by Hugo. If any photos or captions are changed or added, HugoPhotoSwipe recognizes this and updates the gallery and resized photos accordingly.

Below a more detailed description follows about how HugoPhotoSwipe works. A shorter description with step-by-step usage instructions can be found in the GitHub Readme. A complete walkthrough is also available. After you’ve installed HugoPhotoSwipe, a hps command line program should be available. There are four basic commands to this program: init, new, update, and clean. The init command creates a hugophotoswipe.yml configuration file in the current directory. This is the main configuration file for HugoPhotoSwipe and is used to define general settings that hold for all albums, such as the output directory and the dimensions of the images that will be created. With the new command you can create a new album. An album for HugoPhotoSwipe is basically a directory with an album.yml file and a subdirectory with the original photos for the gallery.

The album.yml file contains the title of the album, the captions of the photos, which image is the cover image, etc. This file also keeps track of hashes for the photo files so that changes can be detected. The update command is the one you’ll probably use most, since it generates the markdown and the resized photos and thumbnails. Finally, the clean command removes the output created by the update command, in case you want to regenerate the output. So, the intended setup for HugoPhotoSwipe is to have a source directory, with the following structure:

  ├── bologna
  │   ├── album.yml
  │   └── photos
  │       ├── photo_1.jpg
  │       ├── photo_2.jpg
  │       └── photo_3.jpg
  ├── chicago
  │   ├── album.yml
  │   └── photos
  │       ├── photo_a.jpg
  │       ├── photo_b.jpg
  │       └── photo_c.jpg
  └── hugophotoswipe.yml

In this case we have two albums, each with three photos. After running HugoPhotoSwipe using the hps update command, the output directory will look similar to this:

  ├── bologna
  │   ├── coverimage.jpg
  │   ├── large
  │   │   ├── photo_1_1066x1600.jpg
  │   │   ├── photo_2_1600x1066.jpg
  │   │   └── photo_3_1066x1600.jpg
  │   ├── small
  │   │   ├── photo_1_533x800.jpg
  │   │   ├── photo_2_800x533.jpg
  │   │   └── photo_3_533x800.jpg
  │   └── thumb
  │       ├── photo_1_256x256.jpg
  │       ├── photo_2_256x256.jpg
  │       └── photo_3_256x256.jpg
  └── chicago
      ├── coverimage.jpg
      ├── large
      │   ├── photo_a_1066x1600.jpg
      │   ├── photo_b_1066x1600.jpg
      │   └── photo_c_1066x1600.jpg
      ├── small
      │   ├── photo_a_534x800.jpg
      │   ├── photo_b_533x800.jpg
      │   └── photo_c_534x799.jpg
      └── thumb
          ├── photo_a_256x256.jpg
          ├── photo_b_256x256.jpg
          └── photo_c_256x256.jpg

As you can see, resized versions and square thumbnails are created for each photo. At the same time, the original album.yml file is updated to incorporate new photos that have been added. For instance, it hashes all the original photos and stores these hashes in the album.yml file, in order to detect changed photos later. Finally, the Markdown files are created in the markdown_dir defined in the hugophotoswipe.yml file, so that Hugo can parse these into HTML files. These files contain shortcodes A shortcode is like a template which takes named variables. like this:

{{< photo href="/photos/bologna/large/photo_1_1337x1600.jpg"
alt="Example Alt Text"
caption="Example Caption"
copyright="Example Copyright" >}}

Note how the urls to the photos all start with /photos/. This is defined in the url_prefix setting of the hugophotoswipe.yml file and should be used to ensure the urls point to the actual files on your site. Next, a user of HugoPhotoSwipe would define a shortcode in their Hugo configuration which uses the variables provided in the Markdown to generate the HTML for the image in the way that PhotoSwipe expects it. The shortcodes that HugoPhotoSwipe requires are provided in the documentation. The documentation also contains the Javascript needed for using PhotoSwipe.

Finally, there are some minor features of HugoPhotoSwipe that I’d like to highlight here:

  1. By default, HugoPhotoSwipe uses the Python version of SmartCrop to create the square thumbnails and the cover images. I’ve noticed that the original Javascript version can give slightly nicer results in some cases. Therefore, it is possible to switch to the Javascript CLI of SmartCrop in the hugophotoswipe.yml file. Simply set use_smartcrop_js to True and add the path to smartcrop-cli in the smartcrop_js_path field.

  2. HugoPhotoSwipe allows you to set some options to the Pillow save command if you’re using the JPG output format. You can set the JPG quality, whether or not to optimize the JPG and whether or not to use progressive JPGs. See the documentation of these features here. I especially like the ability to set progressive JPGs, since this helps with optimizing the delivery of the images.

  3. You can specify additional fields in the properties field of the album.yml file. For instance, in the album.yml you can have:

     country: U.S.A.
     camera: Samsung NX-300M

    These properties will show up in the front matter of the generated Markdown file, as follows:

    country = U.S.A.
    camera = Samsung NX-300M
  4. Since version 0.0.6, HugoPhotoSwipe can detect if an image has the rotation stored in an EXIF tag. This way you don’t have to worry about rotating your images correctly before running HugoPhotoSwipe.

That’s it! I hope you find HugoPhotoSwipe useful in your workflow. If you encounter any problems or have any questions please feel free to open an issue on the GitHub page.