149 lines
4.8 KiB
Python
Executable file
149 lines
4.8 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# Eryn Wells <eryn@erynwells.me>
|
|
|
|
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 photos_path
|
|
|
|
|
|
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_path(), f'{year:04}', name)
|
|
post_path_year_month = os.path.join(photos_path(), 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)
|