Last active
November 17, 2020 19:10
-
-
Save tamask/8ba578d3a788aa0c60db9163676ccc94 to your computer and use it in GitHub Desktop.
Utility script for downloading photos/images from the iPhone. Optionally converts HEIC files to JPG with `heif-convert` util. Meant for Linux but could work elsewhere. `heif-convert` is available through the `libheif-examples` package on Ubuntu, for example.
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 python | |
# HEIC image conversion depends on the heif-convert cmd utility, | |
# available through the `libheif-examples` package on Ubuntu, for example. | |
import os | |
import glob | |
import argparse | |
import datetime | |
import shutil | |
import subprocess | |
def fetch_iphone_photos( | |
source_dir, target_dir, | |
from_date=None, to_date=None, | |
include=tuple(), exclude=tuple(), | |
convert_heic=False): | |
''' | |
from_date and to_date are formated as 'YYYY-MM-DD' or None | |
include and exclude are a list of extensions, i.e. ('png', 'jpg', 'mov') | |
''' | |
if from_date is not None: | |
from_date = datetime.date.fromisoformat(from_date) | |
if to_date is not None: | |
to_date = datetime.date.fromisoformat(to_date) | |
if include: | |
include = [ext.upper() for ext in include] | |
if exclude: | |
exclude = [ext.upper() for ext in exclude] | |
for path in glob.iglob(os.path.join(source_dir, '**'), recursive=True): | |
file_basename = os.path.basename(path) | |
file_name, file_ext = os.path.splitext(file_basename) | |
path_date = datetime.date.fromtimestamp(os.path.getmtime(path)) | |
if os.path.isdir(path): | |
continue | |
if from_date and path_date < from_date: | |
continue | |
if to_date and to_date > to_date: | |
continue | |
file_ext = file_ext[1:].upper() | |
if include: | |
if file_ext not in include: | |
continue | |
elif exclude: | |
if file_ext in exclude: | |
continue | |
target_subdir = os.path.join(target_dir, path_date.strftime('%Y/%m/%d')) | |
os.makedirs(target_subdir, exist_ok=True) | |
if file_ext == 'HEIC' and convert_heic: | |
target_path = os.path.join(target_subdir, '%s.JPG' % file_name) | |
subprocess.run(['heif-convert', '-q', '100', path, target_path], check=True) | |
shutil.copystat(path, target_path) | |
else: | |
target_path = os.path.join(target_subdir, file_basename) | |
shutil.copy2(path, target_path) | |
print('%s --> %s' % (path, target_path)) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser( | |
description=( | |
'Fetch photos from iPhone, optionally convert HEIC. ' | |
'Uses [TARGET-DIR]/YYYY/MM/DD/<file> directory format. ' | |
'This is a utility script meant to help get photos into Shotwell.' | |
)) | |
parser.add_argument( | |
metavar='SOURCE-DIR', dest='src_dir', | |
help='DCIM folder on iPhone') | |
parser.add_argument( | |
metavar='TARGET-DIR', dest='dst_dir', | |
help='Location to copy photos to') | |
parser.add_argument( | |
'--from', dest='from_date', default=None, | |
help='Optional start date cutoff for the copy. Format is YYYY-MM-DD.') | |
parser.add_argument( | |
'--to', dest='to_date', default=None, | |
help='Optional end date cutoff for the copy. Format is YYYY-MM-DD.') | |
parser.add_argument( | |
'--include', dest='include', action='append', | |
help='Only include the given file extension. Option can be used multiple times.') | |
parser.add_argument( | |
'--exclude', dest='exclude', action='append', | |
help='Exclude the given file extension. Option can be used multiple times.') | |
parser.add_argument( | |
'--convert-heic', dest='convert_heic', default=False, action='store_true', | |
help='Convert source HEIC to JPG in target dir. Requires the heif-convert util.') | |
args = parser.parse_args() | |
fetch_iphone_photos( | |
args.src_dir, args.dst_dir, | |
from_date=args.from_date, to_date=args.to_date, | |
include=args.include, exclude=args.exclude, | |
convert_heic=args.convert_heic) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment