Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
223 lines (183 sloc)
6.58 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Copyright (c) 2020 Daniel Jakots | |
# | |
# Licensed under the MIT license. See the LICENSE file. | |
import datetime | |
import glob | |
import os | |
import PIL | |
import PIL.Image | |
import PIL.ExifTags | |
import rfeed | |
import jinja2 | |
SMALL_IMAGE_WORD = "small" | |
MAX_HORIZONTAL_SIZE = 800 | |
PAGINATION = 10 | |
OUTPUT_DIR = "output" | |
PICTURES_PATH = f"{OUTPUT_DIR}/pictures/*" | |
SITE = {} | |
SITE["url"] = "https://px.chown.me" | |
SITE["author"] = "Daniel Jakots" | |
SITE["name"] = f"{SITE['author']}' photography" | |
def get_exif(picture_path): | |
img = PIL.Image.open(picture_path) | |
exif = { | |
PIL.ExifTags.TAGS[k]: v | |
for k, v in img._getexif().items() | |
if k in PIL.ExifTags.TAGS | |
} | |
return { | |
"Model": exif["Model"], | |
"LensModel": exif["LensModel"], | |
"DateTime": exif["DateTime"], | |
"ExposureTime": exif["ExposureTime"], | |
"FNumber": exif["FNumber"], | |
"ISOSpeedRatings": exif["ISOSpeedRatings"], | |
"FocalLength": exif["FocalLength"], | |
} | |
def reduce_image(picture_path, new_picture_path): | |
image = PIL.Image.open(picture_path) | |
if image.size[0] < MAX_HORIZONTAL_SIZE: | |
return | |
new_size = calculate_reduced_size(image.size) | |
image = image.resize(new_size, PIL.Image.ANTIALIAS) | |
image.save(new_picture_path, optimize=True, quality=85) | |
def calculate_reduced_size(size): | |
horizontal_ratio = size[0] / MAX_HORIZONTAL_SIZE | |
new_size = (int(size[0] / horizontal_ratio), int(size[1] / horizontal_ratio)) | |
return new_size | |
def clean_exposure_time(ET): | |
# (1, 1) -> 1s | |
if ET[0] == 1 and ET[1] == 1: | |
return "1s" | |
# (1, 60) -> 1/60s | |
elif ET[0] == 1 and ET[1] != 1: | |
return f"{ET[0]}/{ET[1]}s" | |
# (25, 10) -> 2.5s | |
elif ET[0] != 1 and ET[1] != 1: | |
return f"{float(ET[0] / ET[1])}s" | |
# (8, 1) -> 8s | |
elif ET[0] != 1 and ET[1] == 1: | |
return f"{ET[0]}s" | |
def clean_aperture(FL): | |
FL = FL[0] / FL[1] | |
# strip .0 if needed | |
FL = "{:g}".format(FL) | |
return f"f/{FL}" | |
def analyze_pictures(pictures_path): | |
pictures = [] | |
for picture_path in glob.glob(pictures_path): | |
if SMALL_IMAGE_WORD in picture_path: | |
continue | |
ce = analyze_picture(picture_path) | |
pictures.append(ce) | |
if not os.path.isfile(small_picture_path(picture_path)): | |
reduce_image(picture_path, small_picture_path(picture_path)) | |
return pictures | |
def analyze_picture(picture_path): | |
exif = get_exif(picture_path) | |
picture_path = picture_path[len(OUTPUT_DIR) + 1 :] | |
exposure_time = clean_exposure_time(exif["ExposureTime"]) | |
aperture = clean_aperture(exif["FNumber"]) | |
cleaned_exif = {} | |
cleaned_exif["model"] = exif["Model"] | |
cleaned_exif["lens_model"] = exif["LensModel"] | |
cleaned_exif["date"] = f"{exif['DateTime']} (UTC)" | |
cleaned_exif["focal_length"] = f"{exif['FocalLength'][0]}mm" | |
cleaned_exif["aperture"] = aperture | |
cleaned_exif["exposure_time"] = exposure_time | |
cleaned_exif["iso"] = f"ISO {exif['ISOSpeedRatings']}" | |
cleaned_exif["title"] = picture_path.rpartition("/")[2][11:].partition(".")[0] | |
cleaned_exif["path"] = picture_path | |
cleaned_exif["small_path"] = small_picture_path(picture_path) | |
cleaned_exif["html_path"] = picture_path.rpartition("/")[2] | |
return cleaned_exif | |
def small_picture_path(picture_path): | |
return f"{picture_path.rpartition('.')[0]}-{SMALL_IMAGE_WORD}.{picture_path.rpartition('.')[2]}" | |
def create_html_indexes(pictures): | |
jinja2_env = jinja2.Environment( | |
loader=jinja2.FileSystemLoader("templates"), trim_blocks=True | |
) | |
for rank, pictures_in_page in enumerate(pictures): | |
jinja2_template = jinja2_env.get_template("index.html.j2") | |
pagination = {} | |
pagination["current"] = rank + 1 | |
pagination["total"] = len(pictures) | |
if rank > 0: | |
pagination["previous"] = rank - 1 | |
# check if we're on the last page (i.e. last loop) | |
if rank != len(pictures) - 1: | |
pagination["next"] = rank + 1 | |
result = jinja2_template.render( | |
pagination=pagination, pictures=pictures_in_page, site=SITE | |
) | |
if rank == 0: | |
rank = "" | |
else: | |
rank = str(rank) | |
with open(f"{OUTPUT_DIR}/index{rank}.html", "w") as f: | |
f.write(result) | |
def create_html_picture(picture): | |
jinja2_env = jinja2.Environment( | |
loader=jinja2.FileSystemLoader("templates"), trim_blocks=True | |
) | |
jinja2_template = jinja2_env.get_template("picture.html.j2") | |
result = jinja2_template.render(picture=picture, site=SITE) | |
with open(f"{OUTPUT_DIR}/{picture['html_path']}.html", "w") as f: | |
f.write(result) | |
def create_html_all(pictures): | |
jinja2_env = jinja2.Environment( | |
loader=jinja2.FileSystemLoader("templates"), trim_blocks=True | |
) | |
jinja2_template = jinja2_env.get_template("all.html.j2") | |
result = jinja2_template.render(pictures=pictures, site=SITE) | |
with open(f"{OUTPUT_DIR}/all.html", "w") as f: | |
f.write(result) | |
def create_pagination(pictures): | |
"""Split the list of pictures into a list of lists of PAGINATION pictures.""" | |
pictures_for_page = [] | |
offset = 0 | |
for _ in range((len(pictures) // PAGINATION) + 1): | |
pictures_for_page.append(pictures[offset : offset + PAGINATION]) | |
offset = offset + PAGINATION | |
return pictures_for_page | |
def create_feed(feed_items): | |
return rfeed.Feed( | |
title=SITE["name"], | |
link=SITE["url"], | |
description=f"RSS feed for {SITE['url']}", | |
language="en-US", | |
lastBuildDate=datetime.datetime.now(), | |
items=feed_items, | |
) | |
def create_feed_item(title, link, date): | |
return rfeed.Item( | |
title=title, | |
link=link, | |
description=title, | |
author=SITE["author"], | |
guid=rfeed.Guid(link), | |
pubDate=date, | |
) | |
def main(): | |
pictures = analyze_pictures(PICTURES_PATH) | |
pictures.sort( | |
reverse=True, key=lambda i: i["date"].replace(":", "").replace(" ", "") | |
) | |
create_html_indexes(create_pagination(pictures)) | |
create_html_all(pictures) | |
feed_items = [] | |
for picture in pictures: | |
create_html_picture(picture) | |
# 2014:12:27 15:43:55 -> ('2014', '12', '27', '15', '43', '55') | |
date = [int(i) for i in picture["date"][:-6].replace(" ", ":").split(":")] | |
date = datetime.datetime(*date) | |
feed_items.append( | |
create_feed_item( | |
picture["title"], f"{SITE['url']}/{picture['html_path']}.html", date | |
) | |
) | |
with open(f"{OUTPUT_DIR}/feed.xml", "w") as f: | |
f.write(create_feed(feed_items).rss()) | |
if __name__ == "__main__": | |
main() |