agile-leaf / 50mm

Create minimal HTML5 image galleries from S3 buckets.
MIT License
57 stars 12 forks source link

50mm

50mm (Go package name fiftymm) is a HTML image gallery software written in Go. It can serve very minimalistic HTML galleries of your photographs.

You can set up 50mm to serve one album per domain (an example can be seen at https://baku.50mm.asadjb.com/), or you can set up 50mm to serve multiple albums per domain. An example of the later can be seen at https://50mm.asadjb.com/.

Why another web image gallery software?

Fair question. There are a couple of great options out there for web image galleries. But we had a very specific list of requirements we wanted, which is why we created 50mm:

How do I use it?

You'll need a working installation of Go to build 50mm. At this time, we don't provide prebuilt binaries. You'll also want to have a web server where you can run this.

Deploying the web application

You can get and build the 50mm software by running:

go get github.com/agile-leaf/50mm

This should produce a binary file named 50mm inside the bin folder in your Go workspace. This is the server component of the application. To keep things organised, let's copy the binary file to a new folder, which I refer to in the rest of this documentation as the deploy folder.

Next copy the templates and static folders from $GOPATH/src/github.com/agile-leaf/50mm into the deploy folder. Your deploy folder should now have the following structure, although the exact files in the static and templates folders may differ for different versions of the software. What matters is the placement of those folders relative to the binary file 50mm:

deploy
├── 50mm
├── static
│   ├── album.css
│   ├── base.css
│   ├── echo.min.js
│   ├── index.css
│   └── placeholder.png
└── templates
    ├── album.html
    └── index.html

Next we need to create a config folder to hold the configuration files for our sites and albums. This folder can be anywhere on your system, but I just create it inside the deploy folder to keep things simple.

Configure a new site and album

Inside the config folder, create a new INI file. Call it whatever you want, but it's best to name it after the site domain, as it allows you to easily find it again. For this example, I'll call it 50mm.ini. Here's the sample config file I use for my site:

[DEFAULT]
Domain = 50mm.asadjb.com
CanonicalSecure = 1
BucketRegion = eu-west-1
BucketName = 50mm.photos
ResizingService = imgix
BaseUrl = https://50mm-photos.imgix.net
AWSKeyId = AWS_ACCESS_KEY
AWSKey = AWS_SECRET_KEY
SiteTitle = 50mm
MetaTitle = 50mm | Photos by Jibran
HasAlbumIndex = 1

[Baku]
Path = /baku/
BucketPrefix = baku/
MetaTitle = Baku, Azerbaijan | Photos by Jibran
AlbumTitle = Baku, Azerbaijan

[Salalah]
Path = /salalah/
BucketPrefix = salalah/
MetaTitle = Salalah, Oman | Photos by Jibran
AlbumTitle = Salalah, Oman

This configuration is for a site that has an index page, uses Imgix for optimised images, and has two albums. If you want to serve multiple sites, create multiple configuration files. Read on to understand what each of these configuration options mean.

The [DEFAULT] section holds configurations for the entire site. Any other section in the config file is parsed as configuration for an album in the site.

DEFAULT configuration options

There are a few things to remember about using authentication:

You can also have albums served on the site root. So instead of showing a list of albums on the root domain 50mm.asadjb.com, you can instead just show the album page. To configure this, set the HasAlbumIndex in the site config to 0 and set the Path for the album you want at the root to /.

Configuring Image Resizing Subsystem

You can use a few image transformation services to serve optimised images. To do so, you need to do some configuration.

Imgix (imgix)

Setting up imgix requires you set a source to point to the same AWS S3 bucket you have configured for the site.

Once that's done, you can copy the "Imgix Domain" for that source, which looks something like https://source-name.imgix.net and use it as the value for BaseUrl in your site config.

Required configuration variables: ResizingService set to imgix, BaseUrl.

Thumbor (thumbor)

Thumbor is a popular open source image manipulation web application. It can be deployed as a standalone service. Many websites use Thumbor internally for their image manipulation. 50mm thumbor support requires you use a shared secret for security purposes (see their security documentation for details). The BaseUrl for thumbor is wherever your thumbor server lies, e.g: https://thumbor.example.com

Required configuration variables: ResizingService set to thumbor, BaseUrl, ResizingServiceSecret.

Thumbor + AWS Lambda + AWS Cloudfront (thumbor+cloudfront)

AWS provides a serverless image manipulation configuration that is easy to deploy. It runs thumbor under the hood and does it's distributed via Cloudfront. The Thumbor+Cloudfront system uses Cloudfront signed urls rather than thumbor signed urls, so the implementation and configuration is slightly more complex (see configuration options).

You will need to sort yourself a Cloudfront keypair. 50mm only needs your private key file (in pem format) and your access key id. Please take the usual precautions when configuring your private keys.

Required configuration variables: ResizingService set to thumbor+cloudfront, BaseUrl, AWSCloudfrontKeyPath, AWSCloudfrontKeyPairId.

Configuring Nginx

If you use Nginx as your reverse proxy in-front of 50mm, you can use a configuration file similar to this:

server {
    listen 80;
    server_name 50mm.asadjb.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $http_host;
    }
}

You can also have SSL configured on Nginx if needed. Just remember to turn on the CanonicalSecure setting in your site config.

Set up the 50mm server (binary)

You can use whichever solution you want to keep the 50mm server running in the background. I personally use supervisord, but you can use init, upstart, systemd, or any other solution you want; including running it inside a tmux session if you feel brave!

Just remember to set the FIFTYMM_CONFIG_DIR and FIFTYMM_PORT environment variables.

Here's the supervisord config I use:

[program:50mm]
command=/home/asadjb/webapps/50mm/fiftymm
directory=/home/asadjb/webapps/50mm
environment=FIFTYMM_CONFIG_DIR="/home/asadjb/webapps/50mm/config",FIFTYMM_PORT="12536"
stdout_logfile=/home/asadjb/logs/user/50mm_stdout.log
stderr_logfile=/home/asadjb/logs/user/50mm_stderr.log

Set up the 50mm server (docker)

You may also choose to run 50mm in a docker environment, for the moment you'll have to build your own image with docker build -t 50mm:latest ., you may then run it with docker run -p <reachable_port>:80 -v /path/to/config/directory:/deploy/config 50mm:latest. Make sure your configuration reflects the domain as it would be seen in your browser.

Upload photos and bask in the glory!

Once the web app is up and running, you can upload photos to your S3 bucket (inside the folders/prefixes) you have configured for each album.

The app caches image keys for 1 hour in memory. If you want to clear that cache, restart the server binary and that's it.

The frontend uses echo to lazy load images that are not in view. It also unloads images that scroll out of the view. This was done because we usually have albums with tons of images, and having them all loaded at once would hog memory.

Customize album ordering

Sometimes the ordering of your photos matters - you want to images in a certain order and you don't want to rename all your photos to get that ordering.

In order to customize your album ordering in 50mm you'll have to build and upload a yaml file in to the corresponding bucket with the name ordering.yaml. The file has three main sections (each of which is optional), some basic examples:

cover: PA036337.jpg
thumbnails:
  - PA036278.jpg
  - PA036279.jpg
  - PA036280.jpg
  - PA036281.jpg
  - PA036282.jpg
ordering:
  - PA036278.jpg
  - PA036282.jpg
  - PA015843.jpg
  - PA015848.jpg
  - PA015852.jpg
  - PA015853.jpg
  - PA015854.jpg
  - PA015856.jpg
ordering:
  - PA036278.jpg
  - PA036282.jpg
  - PA015843.jpg
  - PA015848.jpg
  - PA015852.jpg
  - PA015853.jpg
  - PA015854.jpg
  - PA015856.jpg
thumbnails:
  - PA036278.jpg
  - PA036279.jpg
ordering:
  - PA036278.jpg
  - PA036282.jpg
  - PA015843.jpg
  - PA015848.jpg

The section names are pretty self-explanatory, each element in the list should correspond to an image key in the corresponding bucket. A few important behaviours:

  1. 50mm processes the filenames in order. Filenames that exist in the actual bucket but not in the thumbnails or ordering sections causes the omitted filenames to appear later in the album (i.e: the ordering is a sort of "put these images first"). As an example, if your album has 50 images and your ordering section has specified two filenames, those files are plucked out of their spots in the bucket ordering and placed at the start of the album.
  2. If a filename is specified in the yaml file but does not exist in the bucket, we ignore that entry.
  3. Malformed yaml files are warned about but ultimately ignored.

Migrating from flickr

flickr_to_50mm is a sister project that can generate the ordering.yaml files by reading the flickr API. There is also flickrtouchr to download your photos from flickr if you no longer have the originals.

Final thoughts

50mm was created because of a frustration we felt. As amateur photographers, we take lots of photographs, and didn't find an easy solution to share those photos with our friends and family. 50mm is our answer to that frustration.

After having used 50mm for more than a month, we feel that for us, this is the solution we wanted. But there are a lot of missing features. Here's an initial list of things we plan to add:

We'd love to hear feedback from other developers and amateur photographers about 50mm. If you want to use this for your own site but are not a developer (or are not that familiar with setting up software on web servers), we'd love to help you there as well.

Hit us up on hello@agileleaf.com.