#!/usr/bin/env python3 # Eryn Wells import argparse import datetime import os import os.path as osp import shutil import subprocess from PIL import Image from PIL.ExifTags import TAGS from typing import Optional from erynwells_me.metadata import slugify from erynwells_me.paths import content_path PHOTOS_CONTENT_DIR = osp.join(content_path(), 'photos') def parse_args(argv, *a, **kw): parser = argparse.ArgumentParser(*a, **kw) parser.add_argument('-e', '--edit', action='store_true') parser.add_argument('-n', '--dry-run', action='store_true') parser.add_argument('-t', '--title') parser.add_argument('-s', '--slug') parser.add_argument('--dump-exif', action='store_true') parser.add_argument('photos', nargs='+') args = parser.parse_args(argv) return args def main(argv): args = parse_args(argv[1:], prog=argv[0]) earliest_exif_date: Optional[datetime.datetime] = None for index, photo in enumerate(args.photos): print(f'image\t\t{photo}') try: image = Image.open(photo) except IOError: continue raw_exif = image._getexif() friendly_exif = {TAGS[k]: v for k, v in raw_exif.items() if k in TAGS} try: date_string = f'{friendly_exif["DateTime"]} {friendly_exif["OffsetTime"]}' exif_date = datetime.datetime.strptime(date_string, '%Y:%m:%d %H:%M:%S %z') except KeyError: exif_date = datetime.datetime.strptime(friendly_exif["DateTime"], '%Y:%m:%d %H:%M:%S') print(f'capture-time\t{exif_date.isoformat()}') iso_rating = friendly_exif.get('ISOSpeedRatings') if iso_rating: print(f'iso\t\t{iso_rating}') focal_length_35mm = friendly_exif.get('FocalLengthIn35mmFilm') focal_length = friendly_exif.get('FocalLength') if focal_length or focal_length_35mm: print(f'focal-length\t{focal_length} {focal_length_35mm}') fstop = friendly_exif.get('FNumber') if fstop: print(f'f-stop\t\t{fstop}') exposure_time = friendly_exif.get('ExposureTime') if exposure_time: print(f'exposure-time\t{exposure_time}') if not earliest_exif_date or exif_date < earliest_exif_date: earliest_exif_date = exif_date if index < len(args.photos) - 1: print() if not earliest_exif_date: earliest_exif_date = datetime.datetime.now() year = earliest_exif_date.year month = earliest_exif_date.month if args.slug: name = args.slug elif args.title: name = slugify(args.title) else: name = osp.splitext(osp.basename(photo))[0] post_path_year = os.path.join(PHOTOS_CONTENT_DIR, f'{year:04}', name) post_path_year_month = os.path.join(PHOTOS_CONTENT_DIR, f'{year:04}', f'{month:02}', name) if os.path.exists(post_path_year): if os.path.exists(post_path_year_month): print("Couldn't find path for photo post. Both of the following paths exist.", file=sys.stderr) print(f' {post_path_year}', file=sys.stderr) print(f' {post_path_year_month}', file=sys.stderr) return -1 else: post_path = post_path_year_month else: post_path = post_path_year try: hugo_command = ['hugo', 'new', '--clock', earliest_exif_date.isoformat(), post_path] if not args.dry_run: result = subprocess.run(hugo_command) result.check_returncode() else: print(' '.join(hugo_command), file=sys.stderr) except subprocess.CalledProcessError: print(f'Failed to create new Hugo post', file=sys.stderr) return -1 index_file_path = os.path.join(post_path, 'index.md') if args.title and not args.dry_run: # The hugo command can't set a title for a post so I need to do it myself. with open(index_file_path) as index_file: index_file_contents = index_file.readlines() for i in range(len(index_file_contents)): line = index_file_contents[i] if not line.startswith('title:'): continue updated_line = f'title: "{args.title}"\n' index_file_contents[i] = updated_line with open(index_file_path, 'w') as index_file: index_file.writelines(index_file_contents) if not args.dry_run and args.edit: editor = os.environ.get('EDITOR', 'vi') subprocess.run([editor, index_file_path], shell=True) for photo in args.photos: print(f'Copy {photo} -> {post_path}') try: if not args.dry_run: shutil.copy(photo, post_path) except IOError: print(f'Failed to copy {photo}', file=sys.stderr) return -2 if __name__ == '__main__': import sys result = main(sys.argv) sys.exit(0 if not result else result)