commit da48104702585675d8738d5b700004bf39b9ec35 Author: dece Date: Tue May 17 18:25:36 2022 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25aacff --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +*.egg-info/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f8cc08 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +EmlGallery +========== + +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. + +The Go source is a prototype I did a few years ago and kept here because it's +fun to me that I used Go at some point! + +License WTFPLv2. + + + +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. + +```bash +emlg "/home/dece/Photos/2022 dubious trip to antartica" +ls $! +# → data.json index.html 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. + +``` +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 +``` diff --git a/emlg/__main__.py b/emlg/__main__.py new file mode 100644 index 0000000..7d9b2b4 --- /dev/null +++ b/emlg/__main__.py @@ -0,0 +1,172 @@ +"""An elementary Web gallery generator using Masonry and Lightbox.""" +import argparse +import json +import os + +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 gen_gallery(dirpath: str, title: str, libs: dict): + entries = [] + for filename in os.listdir(dirpath): + ext = os.path.splitext(filename)[1][1:].lower() + if ext not in SUPPORTED_TYPES: + continue + image = Image.open(os.path.join(dirpath, filename)) + dimensions = image.size + entries.append({ + "src": filename, + "w": dimensions[0], + "h": dimensions[1], + "title": "", + }) + + try: + with open(os.path.join(dirpath, "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) + + +if __name__ == "__main__": + main() diff --git a/emlg/__pycache__/__main__.cpython-39.pyc b/emlg/__pycache__/__main__.cpython-39.pyc new file mode 100644 index 0000000..08ad4b5 Binary files /dev/null and b/emlg/__pycache__/__main__.cpython-39.pyc differ diff --git a/gallery-gen.go b/gallery-gen.go new file mode 100644 index 0000000..219e58e --- /dev/null +++ b/gallery-gen.go @@ -0,0 +1,79 @@ +package main + +import ( + "bufio" + "encoding/json" + "image" + _ "image/jpeg" + _ "image/png" + "log" + "os" + "path/filepath" +) + +// FileEntry contains informations for PowerSlide. +type FileEntry struct { + Name string `json:"src"` + Width int `json:"w"` + Height int `json:"h"` + Caption string `json:"title"` +} + +func main() { + dir, err := os.Getwd() + if err != nil { + panic(err) + } + + entries := buildEntries(dir) + + encodedJSON, err := json.MarshalIndent(entries, "", " ") + if err != nil { + panic(err) + } + + outputFile, err := os.Create(filepath.Join(dir, "data.json")) + if err != nil { + panic(err) + } + defer outputFile.Close() + + outputWriter := bufio.NewWriter(outputFile) + _, err = outputWriter.Write(encodedJSON) + if err != nil { + panic(err) + } + outputWriter.Flush() +} + +func buildEntries(root string) []FileEntry { + var entries []FileEntry + filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + ext := filepath.Ext(path) + if !(ext == ".png" || ext == ".jpg" || ext == ".gif") { + return nil + } + + width, height := getImageDimensions(path) + entries = append(entries, FileEntry{info.Name(), width, height, ""}) + return nil + }) + return entries +} + +func getImageDimensions(path string) (int, int) { + file, err := os.Open(path) + if err != nil { + log.Println(err) + return 0, 0 + } + defer file.Close() + + config, _, err := image.DecodeConfig(file) + if err != nil { + log.Println(err) + return 0, 0 + } + + return config.Width, config.Height +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9787c3b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..44c424c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,22 @@ +[metadata] +name = emlg +version = 0.0.1 +description = Minimal HTML/JS photo gallery +long_description = file: README.md +long_description_content_type = text/markdown +license = GPLv3 +author = dece +author-email = shgck@pistache.land +home-page = https://git.dece.space/Dece/EmlGallery +classifiers = + Environment :: Console + Programming Language :: Python :: 3 + +[options] +packages = emlg +python_requires = >= 3.7 +setup_requires = setuptools >= 38.3.0 + +[options.entry_points] +console_scripts = + emlg = emlg.__main__:main