As someone who has battled with programming, I know from my experience how difficult it can be to execute an idea as a program or a website from scratch. When I first started, finding and understanding all the documentation wasn’t easy. I often relied on other people’s scripts and solutions for everything, from basic HTML to complex CSS and JavaScript. How many questions must one ask/read to write a single line of code?
On top of that, finding time to learn and experiment with these different programming languages was tough to include in my daily schedule. Between work and other responsibilities, it seemed impossible to dedicate enough time to become proficient in all the necessary skills.
Nevertheless, fortunately, times have changed, and thankfully, tools are now available that can help even those with mostly superficial knowledge execute their ideas as working pieces of code. One such tool is large language models, which have made it possible to generate complex code, automate many tasks that were once time-consuming and difficult, and even provide a reasonable explanation and brainstorming with own code.
Recently, I wanted to create a gallery for my website using Hugo and JavaScript; lightweight with a nice lightbox effect but fully adjustable. However, I didn’t have the knowledge or experience to build it from scratch. That’s where ChatGPT came in - it helped me figure out the necessary steps to create the gallery and provided me with the code I needed to get started.
With the help of ChatGPT, I created a column- and row-based gallery for a Hugo-based website with a simple yet stylish lightbox. The gallery idea was borrowed from my portfolio gallery mfg92 hugo-shortcode-gallery ). However, some web mining was necessary to correct parts of the new row-based based gallery borrowed from blog.logrocket.com ). Nevertheless, as an advocate for open source, I have shared both galleries on my GitHub for anyone who would like to use a simple and nice-looking gallery. I plan to add some extra features, such as thumbnail generation and a proper lazy loading mechanism.
The experience has taught me that even with limited programming knowledge, it’s possible to achieve great things with the help of tools like ChatGPT. Correct prompt usage is, however, a key to getting expected answers. I’m excited to see what other projects I can create with these resources and explore new ideas, maybe not effortlessly, but still. Creating those two galleries still took me 3 days of work. Using tools such as ChatGPT is like having a senior programmer/personal assistant around. You can always ask a question. But there is no judgment (I hope!).
My new galleries work only on Desktop browsers with a minimum screen width of 1000px (which can be changed in the shortcodes). The reason is that Lightbox on mobile devices is unnecessary, and images look better as a single column. The gallery shortcode requires providing the unique identifier of the gallery so Lightbox knows which gallery to iterate through. Some settings and the description of the parameters I include below can be changed. The gallery can also show the EXIF or any other list information per image, and I also had a code for generating an EXIF list from the pictures.
Regarding the images themselves, they come from various points in my photographic journey in the last two years, including a middle-of-Covid hike, my previous trip in Switzerland, my first wedding shoot in the USA, a spontaneous beach walk in La Jolla, night sky gazing in 2022, surfing with friends, and some of my favorite lab photos. Enjoy!
Column-based Gallery
gallery_row.html ⬇️Usage:
{{<gallery_column folder="images1/" randomize=false uniqueID="1" scale=100% columns="3" gap=10 showAlt=true long_list="Swiss Alps, La Jolla Cliffs, Beach, Starry Night, Wedding day, Plasma, Surf, In the clouds, Snow1, Snow2" >}}
Parameters:
folder --> folder name that is present where *.md file is stored
randomize --> whether to randomize order of images
columns --> number of columns
uniqueID --> unique identifier of the gallery
height --> height of the gallery row
gap --> gap between images in the gallery
showAlt --> whether to show image annotations in the gallery
longList --> list of annotations seperated by comma (,). Can be image names or EXIF information
scale --> scale of the lightbox preview
usefile --> whether to use the file to load annotations
filename --> path to the file to load annotations
Row-based Gallery
Usage:
{{<gallery_row folder="images2/" randomize=false uniqueID="2" height=230 gap=10 showAlt=true scale=100% usefile=true filename="/content/blog/post38/data/exif2.txt">}}
Parameters:
folder --> folder name that is present where *.md file is stored
randomize --> whether to randomize order of images
uniqueID --> unique identifier of the gallery
height --> height of the gallery row
gap --> gap between images in the gallery
showAlt --> whether to show image annotations in the gallery
longList --> list of annotations seperated by comma (,). Can be image names or EXIF information
scale --> scale of the lightbox preview
usefile --> whether to use the file to load annotations (overrides longList)
filename --> path to the file to load annotations
To generate the file with EXIF data this script can be used:
get_exif_list.py ⬇️import piexif
import glob
from PIL import Image
import os
# Function to get EXIF data from an image file
def get_exif(file):
img = Image.open(file)
exif_dict = piexif.load(img.info['exif'])
return exif_dict
# Specify the folder containing image files
folder = 'folder/images2/*.jpg'
files = sorted(glob.glob(folder))
# List of metadata to retrieve
metadata_location = ['Model', 'LensModel', 'ExposureTime', 'FNumber', 'ISOSpeedRatings', 'FocalLength', 'ApertureValue']
# List to store EXIF strings for each image
exif_str_list = []
# Iterate over image files
for file in files:
exif_dict = get_exif(file)
# Initialize variables to store metadata
Model, LensModel, ExposureTime, FNumber, ISOSpeedRatings, FocalLength = "", "", "", "", "", ""
# Iterate over EXIF tags
for lfn in exif_dict:
for tag in exif_dict[lfn]:
try:
metadata = piexif.TAGS[lfn][tag]["name"]
if metadata in metadata_location:
tag = exif_dict[lfn][tag]
if metadata == 'Model':
Model = tag.decode()
elif metadata == 'LensModel':
LensModel = tag.decode()
elif metadata == 'ISOSpeedRatings':
ISOSpeedRatings = f'ISO {tag}'
elif metadata == 'ExposureTime':
ExposureTime = f'{tag[0]}/{tag[1]}s'
elif metadata == 'FNumber':
num, den = int(tag[0]), int(tag[1])
FNumber = f'F/{num/den if num % den != 0 else num//den}'
elif metadata == 'FocalLength':
num, den = int(tag[0]), int(tag[1])
FocalLength = f'{num//den}mm'
except KeyError:
pass
# Combine metadata into a single string
single_exif = f'{Model} + {LensModel} <br>{ExposureTime} {FNumber} {ISOSpeedRatings} @{FocalLength}'
exif_str_list.append(single_exif)
# Save the list of EXIF strings to a file
save_file = os.path.join(os.path.dirname(files[0]), 'exif.txt')
# Write the EXIF strings to the file without brackets and single quotes
with open(save_file, 'w') as f:
formatted_exif_str = ', '.join(exif_str_list)
f.write(formatted_exif_str)
# Read the cleaned EXIF strings back from the file (optional)
with open(save_file, 'r') as f:
cleaned_exif_str = f.read()
print("Cleaned EXIF strings:", cleaned_exif_str)
'''
Example:
NIKON Z 6 + NIKKOR Z 24-70mm f/4 S <br>1/2500s F/14 ISO 160 @70mm, NIKON D750 + 24.0-70.0 mm f/2.8 <br>1/2500s F/5.6 ISO 250 @24mm, NIKON D750 + 24.0-70.0 mm f/2.8 <br>1/3200s F/9 ISO 200 @29mm
'''