diff --git a/.gitignore b/.gitignore
index 25aacff..4e7a394 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
build/
dist/
*.egg-info/
+__pycache__/
diff --git a/README.md b/README.md
index 7f8cc08..c633117 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
EmlGallery
==========
-EmlGallery (Elementary Masonry & Lightbox Gallery) is a simple generator for Web
+EmlGallery (elementary Masonry & Lightbox Gallery) is a simple generator for Web
galleries, based on Masonry, a library that beautifully tiles images on a page,
and Lightbox, a library for presenting individual images nicely.
@@ -16,31 +16,33 @@ Usage
-----
Point the script to a folder with photos and it generates a JSON file with
-metadata and an index.html file to visit, push everything to a Web host and you
-are ready to go.
+metadata, push it alongside your photos to a Web host and you are ready to go.
```bash
-emlg "/home/dece/Photos/2022 dubious trip to antartica"
-ls $!
-# → data.json index.html IMG1.jpg IMG2.jpg IMG3.jpg …
+$ emlg "/home/dece/Photos/2022 dubious trip to antartica"
+# "Data JSON saved."
+$ ls $!
+# → data.json IMG1.jpg IMG2.jpg IMG3.jpg …
```
-As it relies on external libraries to work, you have to provide your own path to
-the different libraries, either a relative path on your computer or server, or
-an URL to some CDN. See the optional arguments below for the argument names.
+The `index.html` file at the root of this repository is able to load the JSON
+data and build the gallery when someone visits your page. There are two ways to
+provide the JSON data to the gallery, explained below but also at the bottom of
+`index.html` itself.
-```
-positional arguments:
- dir directory with the photos
-
-optional arguments:
- -h, --help show this help message and exit
- --title TITLE page title
- --jquery JQUERY Web path to JQuery library
- --masonry MASONRY Web path to Masonry library
- --imagesloaded IMAGESLOADED
- Web path to ImagesLoaded library
- --lightbox LIGHTBOX Web path to Lightbox2 library
- --lightbox-css LIGHTBOX_CSS
- Web path to Lightbox2 CSS
-```
+### First method: provide data.json as an URL
+
+Host the HTML file somewhere and link it with the URL to your data.json as the
+fragment part of the URL (anything after the #).
+
+Example: `http://gallery.dece.space/#http://unrelated.host/`
+
+This method lets you host only one copy of the gallery page and provide
+different links for each gallery. One drawback is that the server hosting your
+images must have its CORS policy configured to let your browser load the photos.
+
+### Second method: embed data.json into the page
+
+Override some variables as explained in `index.html` and you should be good to
+go. This method avoids the second request, but as you need to fetch the
+thumbnails anyway…
diff --git a/emlg/__main__.py b/emlg/__main__.py
index 7d9b2b4..ee608d2 100644
--- a/emlg/__main__.py
+++ b/emlg/__main__.py
@@ -1,171 +1,62 @@
"""An elementary Web gallery generator using Masonry and Lightbox."""
-import argparse
import json
import os
+from argparse import ArgumentParser
from PIL import Image
SUPPORTED_TYPES = ("jpg", "jpeg", "png", "gif", "webp")
-JQUERY_PATH = "/libs/jquery/jquery.min.js"
-MASONRY_PATH = "/libs/masonry/masonry.pkgd.min.js"
-IMAGESLOADED_PATH = "/libs/masonry/imagesloaded.pkgd.min.js"
-LIGHTBOX_PATH = "/libs/lightbox2/js/lightbox.min.js"
-LIGHTBOX_CSS_PATH = "/libs/lightbox2/css/lightbox.min.css"
-HTML = """\
-
-
-
-
- {title}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-"""
-
-CSS = """\
-body {
- background: black;
-}
-/* 4 columns by default */
-.masonry {
- margin: auto;
- width: 1470px;
-}
-.masonry-item img {
- margin-bottom: 10px;
- width: 360px;
-}
-/* 3 columns on small desktop screens */
-@media only screen and (max-width: 1500px) {
- .masonry { width: 1100px; }
-}
-/* 2 columns on medium-size screens */
-@media only screen and (max-width: 1200px) {
- .masonry { width: 730px; }
-}
-/* 1 column on mobile */
-@media only screen and (max-width: 768px) {
- .masonry { width: 100%; }
- .masonry-item { width: 100%; }
- .masonry-item img { width: 100%; }
-}
-"""
-
-JS = """\
-var dirname = path => path.replace(/\\\\/g,'/').replace(/\\/[^\\/]*$/, '');
-
-function initMasonry(json) {
- var $grid = $('.masonry');
- for (var i = 0; i < json.length; i++) {
- var entry = json[i];
- var $img = $(document.createElement('img'));
- $img.attr('src', entry.src);
-
- var $link = $(document.createElement('a'));
- $link.attr({
- 'href': entry.src,
- 'data-lightbox': 'gallery',
- 'data-title': entry.title,
- });
- $link.append($img);
-
- var $container = $(document.createElement('div'));
- $container.addClass('masonry-item');
- $container.append($link);
-
- $grid.append($container);
- }
-
- $grid.masonry({
- itemSelector: '.masonry-item',
- columnWidth: 360,
- gutter: 10,
- });
-
- $grid.imagesLoaded().progress(() => $grid.masonry('layout'));
-}
-
-var loc = dirname(location.pathname) + '/data.json';
-$(document).ready(() => {
- fetch(loc)
- .then(response => response.json())
- .then(json => initMasonry(json));
-});
-"""
+def main():
+ ap = ArgumentParser(description="Generate JSON metadata from images.")
+ ap.add_argument("dir", help="directory with the images to use")
+ ap.add_argument("--no-thumbnails", action="store_true", help="disable thumbnail generation")
+ args = ap.parse_args()
+ gen_gallery(args.dir, not bool(args.no_thumbnails))
-def gen_gallery(dirpath: str, title: str, libs: dict):
+def gen_gallery(dir_path: str, generate_thumbnails: bool = True):
+ """Generate the JSON metadata for the images found at dirpath."""
entries = []
- for filename in os.listdir(dirpath):
- ext = os.path.splitext(filename)[1][1:].lower()
- if ext not in SUPPORTED_TYPES:
+ for file_name in sorted(os.listdir(dir_path)):
+ name, ext = os.path.splitext(file_name)
+ if name.endswith("_t"): # do not add already generated thumbnails
+ continue
+ if ext[1:].lower() not in SUPPORTED_TYPES:
continue
- image = Image.open(os.path.join(dirpath, filename))
+ image = Image.open(os.path.join(dir_path, file_name))
dimensions = image.size
- entries.append({
- "src": filename,
+ entry = {
+ "src": file_name,
"w": dimensions[0],
"h": dimensions[1],
"title": "",
- })
+ }
+ if generate_thumbnails:
+ try:
+ entry["thumb"] = gen_thumbnail(image, file_name, dir_path)
+ except OSError as exc:
+ exit(f"Can't create thumbnail: {exc}")
+ entries.append(entry)
try:
- with open(os.path.join(dirpath, "data.json"), "wt") as data_file:
+ with open(os.path.join(dir_path, "data.json"), "wt") as data_file:
json.dump(entries, data_file)
except OSError as exc:
exit(f"Can't write data.json file: {exc}")
print("Data JSON saved.")
- try:
- with open(os.path.join(dirpath, "index.html"), "wt") as index_file:
- index_file.write(HTML.format(title=title, css=CSS, js=JS, **libs))
- except OSError as exc:
- exit(f"Can't write index.html file: {exc}")
- print("Web page saved.")
-
-def main():
- ap = argparse.ArgumentParser()
- ap.add_argument("dir", help="directory with the photos")
- ap.add_argument("--title", default="EmlGallery", help="page title")
- ap.add_argument("--jquery", help="Web path to JQuery library")
- ap.add_argument("--masonry", help="Web path to Masonry library")
- ap.add_argument("--imagesloaded", help="Web path to ImagesLoaded library")
- ap.add_argument("--lightbox", help="Web path to Lightbox2 library")
- ap.add_argument("--lightbox-css", help="Web path to Lightbox2 CSS")
- args = ap.parse_args()
- libs = {
- "jquery": args.jquery or JQUERY_PATH,
- "masonry": args.masonry or MASONRY_PATH,
- "imagesloaded": args.imagesloaded or IMAGESLOADED_PATH,
- "lightbox": args.lightbox or LIGHTBOX_PATH,
- "lightbox_css": args.lightbox_css or LIGHTBOX_CSS_PATH,
- }
- gen_gallery(args.dir, args.title, libs)
+def gen_thumbnail(image: Image.Image, file_name: str, dir_path: str) -> str:
+ """Generate a thumbnail from this image, return its name."""
+ thumbnail = image.copy()
+ thumbnail.thumbnail((360, 720), resample=Image.Resampling.LANCZOS)
+ name, ext = os.path.splitext(file_name)
+ thumbnail_file_name = name + "_t" + ext
+ thumbnail_path = os.path.join(dir_path, thumbnail_file_name)
+ thumbnail.save(thumbnail_path)
+ return thumbnail_file_name
if __name__ == "__main__":
diff --git a/emlg/__pycache__/__main__.cpython-39.pyc b/emlg/__pycache__/__main__.cpython-39.pyc
deleted file mode 100644
index 08ad4b5..0000000
Binary files a/emlg/__pycache__/__main__.cpython-39.pyc and /dev/null differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..f2750e0
--- /dev/null
+++ b/index.html
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/setup.cfg b/setup.cfg
index 44c424c..194dd05 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = emlg
-version = 0.0.1
+version = 0.0.2
description = Minimal HTML/JS photo gallery
long_description = file: README.md
long_description_content_type = text/markdown
@@ -16,6 +16,8 @@ classifiers =
packages = emlg
python_requires = >= 3.7
setup_requires = setuptools >= 38.3.0
+install_requires =
+ Pillow
[options.entry_points]
console_scripts =