From 13add8d58a671d43456caebc12789a9df2b92bbb Mon Sep 17 00:00:00 2001 From: Florian Obser Date: Sun, 29 Jan 2023 11:46:34 +0100 Subject: [PATCH] Static photo albums with llgal --- llgal.org | 162 +++++++++++++++ llgal/llgalrc.txt | 383 +++++++++++++++++++++++++++++++++++ llgal/slidetemplate.html.txt | 62 ++++++ 3 files changed, 607 insertions(+) create mode 100644 llgal.org create mode 100644 llgal/llgalrc.txt create mode 100644 llgal/slidetemplate.html.txt diff --git a/llgal.org b/llgal.org new file mode 100644 index 0000000..f696dc9 --- /dev/null +++ b/llgal.org @@ -0,0 +1,162 @@ +#+TITLE: Static photo albums with llgal +#+DATE: 2023-01-29 +* Prologue +I used to create photo albums on [[https://flickr.com/photos/tlakh/][flickr]] for our vacations. I even had +a paid pro account for a few years. While I have backups of all my +photos, it still felt off to show these albums using somebody else's +computer. They might disappear any day. Since I have the storage +capacity and some time to waste I set out to self-host photo albums. +* Nextcloud Photos +My first layer of photo backups is a self-hosted [[https://nextcloud.com/][Nextcloud]]. I am using +the Nextcloud android app to automatically upload pictures when on +WiFi. So I might as well use the built-in Nextcloud Photos 2.0[fn::Why +is there not way to link to a description of this thing? There is a +GitHub page and it's described on [[https://nextcloud.com/athome/][https://nextcloud.com/athome/]] but +you can't direct-link to it‽]. + +Unfortunately it's not quite there yet. In increasing order of +annoyance: +1) Video buffering is not a thing. +2) Videos do not get a preview image. +3) It is very confused about ordering. The order in the photo app is + correct but when added to the gallery sometimes two photos switch + places. There is some mumbling about mtime vs. ctime vs. exiff data + in some forum, but that's not the problem here. First of all, no + matter how Nextcloud sorts the picture it would always arrive at + the same ordering. I did not edit the photos, so mtime and ctime + are the same. But also note that the order in the photo app is + correct. It only gets confused when photos are added to an + album. The photo album tells a story. It is really bad if the + "arrival at home" photo is shown before the "last night of the + vacation" photo. +4) The absolute deal-breaker is that it does not work though. The + whole idea is to share a link to the album for anonymous users. You + can do that, and it shows you the album with thumbnails. But as + soon as you click on a photo or start the slide show it wants you + to login. I suspect you need to enable sharing via link to every + photo in the album to make it work. There does not seem to be a way + to do this in bulk so I did not try it. I have also found a + discussion online where people argue for years along the lines of + "it does not work" - "yes it does" - "no it does not". That was + tiresome and they are still going on. There was some mumbling that + you need to enable federation in Nextcloud to make anonymous albums + work, but I have that on and it still does not work. +* llgal +I then remembered that I had used a static photo album generator +before: [[http://bgoglin.free.fr/llgal/][llgal]]. But I never tried to make it "pretty" or convert all my +flickr albums to it. +** Setup +llgal is in OpenBSD ports, so it can be installed with +~pkg_add~. I store all my albums as sub-folders in +~/var/www/img.sha256.net/~ and then run ~llgal~. I was experimenting a +bit with the command line flags to figure out what I need and then +setup a =~[[file:llgal/llgalrc.txt][/.llgal/llgal.rc]]= file. + +These are the things I configured: +#+begin_export ascii +thumbnails_per_row = 3 +thumbnail_height_max = 240 +thumbnail_width_max = 240 +MVI_link_to_target = 1 +DIR_link_to_target = 1 +slide_width_max = 700 +slide_height_max = 700 +show_all_exif_tags = 1 +credits_text = "Copyright © 2014 - 2023 Florian Obser. All rights reserved." +exclude = "^js$" +sort_criteria = "revtime" +recursive = 1 +link_subgalleries = 1 +#+end_export +** Video thumbnails +llgal does not create thumbnails for video files (yet) so I had to +hack around that a bit. +First we use ~ffmpeg~ to create the thumbnails: +#+begin_src shell + for i in *.mp4; do + ffmpeg -ss 1 -i ${i} -vframes 1 -vf "scale=240:-1" \ + .llgal/my_thump_${i}.jpg + done +#+end_src +We grab one frame (=-vframes 1=), one second into the video (=-ss +1=), and scale it to 240 pixels wide while keeping the aspect ratio +(=-vf "scale=240:-1"=). We store the thumbnail in the ~.llgal~ +sub-directory, where ~llgal~ stores its own thumbnails and scaled +images. The config file has this: +#+begin_export ascii +# Additional prefix of user-provided scaled images +# user_scaled_image_filenameprefix = "my" + +# Additional prefix of user-provided thumbnails +# user_thumbnail_image_filenameprefix = "my" +#+end_export +I have not worked out what that does, but I am using the "my" prefix +because of this. + +llgal does not pick up these thumbnails but we can use the /captions/ +file to show them. First we are creating /captions files in all +albums: ~llgal --gc~. This creates =.llgal/captions= in all albums. + +It creates lines for all photos and videos that we can edit, for +videos it looks like this: +#+begin_export ascii +MVI: VID_20230120_124000.mp4 ---- Open movie VID_20230120_124000.mp4 ---- +#+end_export +And we can change it to this to show the thumbnail[fn::I am using an +emacs macro for that. YMMV.]: +#+begin_export ascii +MVI: VID_20230120_124000.mp4 ----
Open movie ---- +#+end_export +I am using the same trick to have thumbnails for the top-level [[https://img.sha256.net/index.html][album +overview]]. The only difference is that it is a =DIR= line instead of +=MVI=. +** Keyboard and swipe navigation. +llgal links to [[http://santana2000.com/][santana2000.com]] as an example. Those albums use +[[https://hammerjs.github.io/][hammer.js]] for swipe navigation and [[https://craig.is/killing/mice][mousetrap]] for keyboard navigation. +I have copied =slidetemplate.html= from =/usr/local/share/llgal= to +=~/.llgal= and edited it: [[file:llgal/slidetemplate.html.txt][slidetemplate.html]]. +We need to include the JavaScript files in the header: +#+begin_src html + + +#+end_src +Set the =id= for the navigation links: +#+begin_src html +

+ +       + +       + +

+#+end_src +And then hook up JavaScript to the navigation links: +#+begin_src html + + + +#+end_src +This seems to disable pinch-zooming on the image itself on android. I +have not figured out why that is. The hammerjs documentation talks +about pinch, but my understanding is that it should just work. It does +not though... +* Epilogue +This was not a lot of work, please enjoy the first [[https://img.sha256.net/2023-01%20Malta/index.html][album from our trip +to Malta in 2023]]. +Time permitting I will convert more albums from flickr and google +photos in the future. diff --git a/llgal/llgalrc.txt b/llgal/llgalrc.txt new file mode 100644 index 0000000..31499ac --- /dev/null +++ b/llgal/llgalrc.txt @@ -0,0 +1,383 @@ +# llgal configuration options. +# These options may be defined in /etc/llgal/llgalrc, a per-user +# ${HOME}/.llgal/llgalrc file or a directory-specific .llgal/llgalrc. + +# Most of these options may later be overridden by command line options. +# Default are shown in examples below. +# Syntax is one of these: +# variable = "string" +# variable = decimal number +# variable = 0 or 1 for disabling or enabled +# For now, only static values are supported. +# Brackets indicate corresponding command line options. + +##### Name of generic llgal files ##### + +# Name of the captions header file that is inserted at the beginning +# of generated captions files. +# captions_header_filename = "captions.header" + +# Name of the CSS file. +# Such a file will be stored under $local_llgal_dir. +# css_filename = "llgal.css" + +# Name of the film tile image. +# Such a file will be stored under $local_llgal_dir. +# Note that this filename must match the one that is used in the CSS file. +# filmtile_filename = "tile.png" + +# Name for image link to the index, previous or next slide +# index_link_image_filename = "index.png" +# prev_slide_link_image_filename = "prev.png" +# next_slide_link_image_filename = "next.png" + +# Name of the index template. +# Such a template file will be used during gallery generation. +# indextemplate_filename = "indextemplate.html" + +# Name of the slide template. +# Such a template file will be used during gallery generation. +# slidetemplate_filename = "slidetemplate.html" + +##### Location of llgal files if available on the web ##### + +# Location of the CSS file if available on the web [--uc ]. +# If ending with '/', css_filename will be added. +# css_location = "" + +# Location of llgal image files if available on the web. +# If ending with '/', their generic filename will be added. +# These locations may be set alltogether with --ui . +# filmtile_location = "" +# index_link_image_location = "" +# prev_slide_link_image_location = "" +# next_slide_link_image_location = "" + +##### Location and name of generated files ##### + +# Local subdirectory where llgal files generated files will be stored. +# This option is only available in system- and user-wide configuration +# files. +# Note that HTML files are generated in place while captions and CSS +# are kept in this subdirectory. +# Gallery specific HTML templates and llgalrc configuration file +# might also be defined here. +# local_llgal_dir = ".llgal" + +# Name of the generated index file [-i ] +# index_filename = "index" + +# Prefix of generated HTML slide filenames +# Note that this prefix is used to decide what HTML to delete when --clean +# is passed. Setting this option to an empty string will make llgal remove +# all HTML files. +# slide_filenameprefix = "slide_" + +# Prefix used to determine the filenames of scaled image that are +# shown in slides (in case of --sx or --sy). +# These files will be stored under $local_llgal_dir. +# scaled_image_filenameprefix = "scaled_" + +# Prefix used to determine thumbnail filenames from original images. +# These files will be stored under $local_llgal_dir. +# thumbnail_image_filenameprefix = "thumb_" + +# Name of the captions file that will be generated when llgal is called +# with --gc. This file will be stored under $local_llgal_dir. +# captions_filename = "captions" + +# Additional prefix of user-provided scaled images +# user_scaled_image_filenameprefix = "my" + +# Additional prefix of user-provided thumbnails +# user_thumbnail_image_filenameprefix = "my" + +# Character to use to replace / in the thumbnail/scaled of subdir images +# path_separator_replacement = "@" + +##### Index ##### + +# Cellpadding in the index table [-p ] +# Must be >= 0, setting to < 0 resets to default +# index_cellpadding = 3 + +# Display links and text as regular text instead of thumbnails +# in the main gallery thumbnail index [-L] +# list_links = 0 + +# Pixels per row of thumbnails in index [--wx ] +# Must be > 0, 0 means unlimited, setting to < 0 resets to default +# pixels_per_row = 0 + +# Thumbnails per row in index [-w ] +# Must be > 0, 0 means unlimited, setting to < 0 resets to default +thumbnails_per_row = 3 + +# Do not output any absolute thumbnail sizes in HTML code and assume the +# CSS style sheet will take care of it (in table.index [td.image-slide [img]]) +# slide_dimensions_from_css = 0 + +# Maximal height of thumbnails [--ty ] +# Must be > 0, setting to < 0 resets to default +# Changing this value does not affect the maximal width (see thumbnail_width_max) +thumbnail_height_max = 240 + +# Maximal width of thumbnails [--tx ] +# Must be > 0, 0 means unlimited, setting to < 0 resets to default +# Changing this value does not affect the maximal height (see thumbnail_height_max) +thumbnail_width_max = 240 + +# Write captions under thumbnails [-u] +# show_caption_under_thumbnails = 0 + +# Show a film effect in the index of thumbnails [--fe] +# show_film_effect = 0 + +# Make thumbnail links point to the target object instead of +# the corresponding slide +MVI_link_to_target = 1 +# FIL_link_to_target = 0 +DIR_link_to_target = 1 +# LNK_link_to_target = 0 + +##### Slides ##### + +# Make no slides [-s] +# make_no_slides = 0 + +# Use filename as slide filename [-n] +# make_slide_filename_from_filename = 0 + +# Also use extension in the slide filename to differenciate +# slide filename of images that have same filenames. +# make_slide_filename_from_extension = 0 + +# Do not output any absolute image size in the HTML code and assume the +# CSS style sheet will take care of it (in table.slide td.image-slide img) +# slide_dimensions_from_css = 0 + +# Maximal width of slides [--sx ] +# Must be > 0, 0 means unlimited, setting to < 0 resets to default +slide_width_max = 700 + +# Maximal height of slides [--sy ] +# Must be > 0, 0 means unlimited, setting to < 0 resets to default +slide_height_max = 700 + +# Default size of non-image slides. +# Must be > 0, setting to < 0 resets to default +# Note that the --sx and --sy options may lead to reduction of these values. +# text_slide_width = 400 +# text_slide_height = 300 + +# Use an image instead of a text label for the link to the index, +# previous or next slide [--li] +# index_link_image = 0 +# next_slide_link_image = 0 +# prev_slide_link_image = 0 + +# Use a thumbnail preview instead of a text label for the link +# to the previous or next slide [--lt] +# next_slide_link_preview = 0 +# prev_slide_link_preview = 0 + +# Generate slide titles from captions [-k] +# make_slide_title_from_caption = 0 + +# Generate links between last and first slides or galleries. +# link_between_last_and_first = 1 + +# Display a table of EXIF tags under each image slide [--exif] +# The tags are given with their name in exiftool -list and separated +# with a comma. +# show_exif_tags = "" + +# Display a table of all available EXIF tags under each image slide [--exif] +show_all_exif_tags = 1 + +##### Captions ##### + +# This line will be added to the captions file llgal will generate when +# called with --gc. If the user doesn't want llgal to remove this captions +# file when called with --clean, it just needs to remove this line from +# the file. +# captions_removal_line = "REMOVE THIS LINE IF LLGAL SHOULD NOT REMOVE THIS FILE" + +# Generate captions from comment stored in images [--cc []] +# make_caption_from_image_comment = "" + +# Also generate captions from timestamp stored in images [--ct] +# make_caption_from_image_timestamp = 0 + +# Generate captions from filenames without their extension [--cf] +# make_caption_from_filename = 0 + +# Generate captions from filenames with their extension +# make_caption_from_extension = 0 + +# Show dimensions and/or size of the images and movies [-a, --ad, --as] +# show_dimensions = 0 +# show_size = 0 + +# Change the format of the counter shown on the slides +# %n is replaced by the slide number, %0n gets leading zeros, +# and %t is replaced by the number of slides. +# Setting to "" disables the slide counter [--nc] +# slide_counter_format = "   (%0n/%t)" + +##### Text ##### + +# Default title of the gallery. +# May be overridden with [--title ] or TITLE: in the captions file. +# index_title_default = "Index of pictures" + +# Change text in links to parent directory. +# parent_gallery_link_text = "Back to parent gallery" + +# Change text in links to previous gallery. +# prev_gallery_link_text = "Previous gallery " + +# Change text in links to next directory. +# next_gallery_link_text = "Next gallery " + +# Label of the link from a slide to the index +# index_link_text = "Index" + +# Label of the link from a slide to the previous one +# prev_slide_link_text = "←" + +# Label of the link from a slide to the next one +# next_slide_link_text = "Next>>" + +# Text prefixing the filename when generating link text +# for movies without a captions file. +# MVI_link_text = "Open movie " + +# Text prefixing the filename when generating link text +# for files without a captions file. +# FIL_link_text = "Download file " + +# Text prefixing the filename when generating link text +# for directories without a captions file. +# DIR_link_text = "Open subgallery " + +# Text shown as image alternative for full-size images in slides +# alt_full_text = "" + +# Text shown as image alternative for scaled images in slides +# alt_scaled_text = "Scaled image " + +# Text shown as image alternative for thumbnails in the index +# alt_thumbnail_text = "Thumbnail " + +# Text shown as an image alternative for the film tile in the index +# alt_film_tile_text = "Film tile" + +# Text shown when the mouse pointer is over a scaled image in a slide +# over_scaled_text = "Click to see full size " + +# Text shown when the mouse pointer is over a thumbnail +# over_thumbnail_text = "Click to enlarge " + +# Text shown when the mouse pointer is over a link from a slide to the index +# over_index_link_text = "Return to the index" + +# Text shown when the mouse pointer is over a link from a slide to the previous one +# over_prev_slide_link_text = "Previous slide " + +# Text shown when the mouse pointer is over a link from a slide to the next one +# over_next_slide_link_text = "Next slide " + +# Unit to be used when printing sizes [--asu ] +# show_size_unit = "kB" + +# Set timestamp format in captions (when enabled) using strftime format [--ct ] +# timestamp_format_in_caption = "%Y-%m-%d %H:%M:%S" + +# Credits line at the bottom of the index +credits_text = "Copyright © 2014 - 2023 Florian Obser. All rights reserved." + +##### What files to insert in the gallery ##### + +# Extensions that are matched when searching images (|-separated list) +# image_extensions = "jpg|jpeg|gif|png|tif|tiff|bmp|webp" + +# Extensions that are matched when searching movies (|-separated list) +# movie_extensions = "mpg|mpeg|avi|mov|ogm|wmv|mp4|3gp|webm" + +# Add all files to the list of entries +# not only images and movies [-A] +# add_all_files = 0 + +# Add subdirectories to the list of entries [-S] +# add_subdirs = 0 + +# Exclude files whose name matches [--exclude ] +# This option may be used several times. +exclude = "^js$" + +# Include files whose name matches and were previously excluded [--include ]. +# This option may be used several times. +# The order of includes and excludes is respected. +# include = "regexp" + +# Sort criteria when scanning files in the working directory [--sort] +sort_criteria = "revtime" + +##### Sections ##### + +# Add a new subdirectory to the list of sections [-P] +# section_dir = "subdir" + +# Add all subdirectories to the list of sections [--Pall] +# recursive_sections = 0 + +# Add the subdirectory name as a title at the beginning of each section [--Ps] +# entitle_sections = 0 + +# Add a horizontal line at the beginning of each section in the index [--Ps] +# separate_sections = 0 + +##### Recursion ##### + +# Run recursively in subdirectories [-R] +recursive = 1 + +# Add links between subgalleries [--Rl] +link_subgalleries = 1 + +# Add links to the parent directory [--parent-gal] +# parent_gallery_link = 0 + +##### Various ##### + +# Additional configuration file [--config ] +# This option may be used multiple times. +# additional_configuration_file = "my_llgal.rc" + +# Additional template directories [--templates]. +# This option may be used multiple times. +# additional_template_dir = "path" + +# Codeset to be set in HTML headers [--codeset ] +# codeset = "codeset" + +# Language to be used for generated text in HTML pages [--lang ] +# If set, the LANGUAGE environment variable might prevent this option from working. +# language = "locale" + +# Command to be used to generate scaled images and thumbnails +# thumbnail_create_command = "convert -scale x -- " +# scaled_create_command = "convert -scale x -- " + +# Force regeneration of thumbnails and scaled images [-f]. +# force_image_regeneration = 0 + +# Print notice messages [-v] +# verbose = 0 + +# Give access rights for www access [--www] +# www_access_rights = 0 + +# Extension of generate webpages [--php] +# www_extension = "html" diff --git a/llgal/slidetemplate.html.txt b/llgal/slidetemplate.html.txt new file mode 100644 index 0000000..f8050e3 --- /dev/null +++ b/llgal/slidetemplate.html.txt @@ -0,0 +1,62 @@ + + + + + + + <!--SLIDE-TITLE--> + + + + + + + + + + + + + +

+ +       + +       + +

+ +
+ +
+ +

+ +

+ +
+ +
+ + + + +