Tim's blah blah blah

Self Hosted Photo Albums With Piwigo

(Updated: )

After setting up my nextcloud instance (vanwerkhoven.org), I set out to organize my photo albums online. I had been using Apple’s excellent shared photo albums before, but I prefer to have my photos on my own devices. I settled for Piwigo (piwigo.org) which was fairly easy to set-up and use, and has similar viewing experience as Apple’s version.

Update September 2021: I migrated to a new setup using pigallery2 (github.io), which is even easier. See my guide and considerations here (vanwerkhoven.org).


I was looking to have a similar experience as commercial/cloud albums, meanning:


Setting up Piwigo

  1. Get source, unzip to /var/www/piwigo, chown www-data:www-data
  2. Set-up virtualhost
    1. Create virtualhost nginx
    2. Add subdomain to SSL certificate
      1. sudo certbot certificates
      2. sudo certbot certonly --http-01-port 9080 --cert-name <certname> -d <domain1>,<domain2>,<domain3>
      3. Choose option 1 - nginx-based verification
  3. Create MySQL database user (linuxize.com)
    1. Generate password mkpwd -t 2 -n 30 -m 30
    2. sudo mysql
    3. CREATE USER 'piwigo'@'localhost' IDENTIFIED BY 'xuRroMzYn4lySHCDAh3H';
    4. CREATE DATABASE piwigo;
    5. GRANT ALL PRIVILEGES ON piwigo.* TO 'piwigo'@'localhost’;
    6. SHOW GRANTS FOR 'piwigo'@'localhost’;
  4. Install piwigo via web interface
  5. Add extensions
    1. Share Album (piwigo.org)
    2. RV thumbnail scroller (piwigo.org)
    3. Add video support with piwigo-videojs (piwigo.org)
      1. Activate & configure Local Parameters (github.com)
  6. Configure piwigo
    1. Add ‘administrator’ users for photo uploading via app
  7. Get app, upload photos

Compressing videos as well

While piwio nicely creates various compressions for photos, piwigo-videojs doesnot (github.com). Since the viewers of my albums don’t need the full resolution, I’ve hacked together a solution to compress videos as well outside piwigo’s environment based on watching changes in the directory with inotifywait (stackoverflow.com).

  1. Get ffmpeg with HW accel (jellyfin.org) in my case via Intel QuickSync (QSV)
  2. Make inotifywait watcher script that converts when files are moved into upload dir. Note that files initially are uploaded to a upload/buffer/ dir, and from there moved into the repository named by year. By watching only the repository, we will not trigger on moves being uploaded to upload/buffer/. We only watch the current year as not to overflow inotifywait (debian.org), hence this requires a reboot on January first :) (xkcd.com) I decided to watch all directories of the current century, as the defualt limit of 8192 watched directories is enough for 8192/2/365 = 11 years.
    WATCHDIR=/var/www/photos/upload/$(date +%C)*
    inotifywait --quiet -e MOVED_TO --monitor --recursive "${WATCHDIR}" | while read line
        # line == <dir> <event> <filen>, e.g. /var/www/photos/upload/2021/02/04/ MOVED_TO 20210204135704-a4e62c48.jpg
        echo "triggered " $line
        mime=$(file --brief --mime-type "${thisfilepath}")
        # For any video, try to compress using ffmpeg. This might misfire in case of 
        # low-quality videos which are upscaled, but that's unlikely/impossible in my use case
        if [[  "${mime}" =~ ^video/ ]]; then
            echo "video triggered " $mime
            # For this video, we need to:
            # 1. Compress it: $file to ${thisfilepath}-x264_aac.mp4
            # 2. Replace it without moving: cp ${thisfilepath}-x264_aac.mp4 $file && rm ${thisfilepath}-x264_aac.mp4
            # which shouldn't trigger ourself
            nice ffmpeg -hide_banner -nostdin -nostats -loglevel error -i "${thisfilepath}" -profile:v high -level 4.0 -pix_fmt yuv420p -c:v libx264 -preset slower -movflags use_metadata_tags -crf 28 -vf "scale='iw*min(1,sqrt(1280*720/ih/iw)):-2" -c:a aac -vbr 3 -threads 0 -y "${thisfilepath}-x264_aac.mp4"
            cp "${thisfilepath}-x264_aac.mp4" "${thisfilepath}" && rm "${thisfilepath}-x264_aac.mp4"
            echo "video conversion done"
  3. Start script on reboot using e.g. cron (crude but works)
    sudo crontab -u www-data -e
    @reboot /var/www/photos/videowatcher.sh
  4. Test
    sudo su - www-data -s /bin/bash -c '/var/www/photos/videowatcher.sh'
    sudo killall videowatcher.sh