init
This commit is contained in:
commit
da48104702
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
dist/
|
||||
*.egg-info/
|
46
README.md
Normal file
46
README.md
Normal file
|
@ -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
|
||||
```
|
172
emlg/__main__.py
Normal file
172
emlg/__main__.py
Normal file
|
@ -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 = """\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<style>
|
||||
{css}
|
||||
</style>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="{jquery}"></script>
|
||||
<!-- Masonry & ImagesLoaded -->
|
||||
<script src="{masonry}"></script>
|
||||
<script src="{imagesloaded}"></script>
|
||||
<!-- Lightbox2 -->
|
||||
<link href="{lightbox_css}" rel="stylesheet">
|
||||
<script src="{lightbox}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="masonry">
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{js}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
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()
|
BIN
emlg/__pycache__/__main__.cpython-39.pyc
Normal file
BIN
emlg/__pycache__/__main__.cpython-39.pyc
Normal file
Binary file not shown.
79
gallery-gen.go
Normal file
79
gallery-gen.go
Normal file
|
@ -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
|
||||
}
|
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
22
setup.cfg
Normal file
22
setup.cfg
Normal file
|
@ -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
|
Reference in a new issue