Refactored export_photo to enable work on #420

This commit is contained in:
Rhet Turnbull
2021-04-24 10:00:42 -07:00
parent aad435da36
commit 48c229b52c
3 changed files with 355 additions and 319 deletions

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.42.9" __version__ = "0.42.10"

View File

@@ -2163,8 +2163,6 @@ def export_photo(
global VERBOSE global VERBOSE
VERBOSE = bool(verbose) VERBOSE = bool(verbose)
results = ExportResults()
export_original = not (skip_original_if_edited and photo.hasadjustments) export_original = not (skip_original_if_edited and photo.hasadjustments)
# can't export edited if photo doesn't have edited versions # can't export edited if photo doesn't have edited versions
@@ -2206,10 +2204,15 @@ def export_photo(
if photo.hasadjustments and photo.path_edited is None: if photo.hasadjustments and photo.path_edited is None:
missing_edited = True missing_edited = True
filenames = get_filenames_from_template( sidecar = [s.lower() for s in sidecar]
photo, filename_template, original_name, strip=strip sidecar_flags = 0
) if "json" in sidecar:
for filename in filenames: sidecar_flags |= SIDECAR_JSON
if "xmp" in sidecar:
sidecar_flags |= SIDECAR_XMP
if "exiftool" in sidecar:
sidecar_flags |= SIDECAR_EXIFTOOL
rendered_suffix = "" rendered_suffix = ""
if original_suffix: if original_suffix:
try: try:
@@ -2233,6 +2236,22 @@ def export_photo(
) )
rendered_suffix = rendered_suffix[0] rendered_suffix = rendered_suffix[0]
# if download_missing and the photo is missing or path doesn't exist,
# try to download with Photos
use_photos_export = use_photos_export or (
download_missing
and (
photo.ismissing
or photo.path is None
or (export_edited and photo.path_edited is None)
)
)
results = ExportResults()
filenames = get_filenames_from_template(
photo, filename_template, original_name, strip=strip
)
for filename in filenames:
original_filename = pathlib.Path(filename) original_filename = pathlib.Path(filename)
file_ext = original_filename.suffix file_ext = original_filename.suffix
if photo.isphoto and (jpeg_ext or convert_to_jpeg): if photo.isphoto and (jpeg_ext or convert_to_jpeg):
@@ -2254,143 +2273,55 @@ def export_photo(
f"Exporting {photo.original_filename} ({photo.filename}) as {original_filename}" f"Exporting {photo.original_filename} ({photo.filename}) as {original_filename}"
) )
dest_paths = get_dirnames_from_template( results += export_photo_with_template(
photo, directory, export_by_date, dest, dry_run, strip=strip photo=photo,
) filename=original_filename,
directory=directory,
sidecar = [s.lower() for s in sidecar] edited=False,
sidecar_flags = 0 use_photos_export=use_photos_export,
if "json" in sidecar: export_by_date=export_by_date,
sidecar_flags |= SIDECAR_JSON dest=dest,
if "xmp" in sidecar: dry_run=dry_run,
sidecar_flags |= SIDECAR_XMP strip=strip,
if "exiftool" in sidecar: export_original=export_original,
sidecar_flags |= SIDECAR_EXIFTOOL missing=missing_original,
verbose=verbose,
# if download_missing and the photo is missing or path doesn't exist, sidecar_flags=sidecar_flags,
# try to download with Photos
use_photos_export = use_photos_export or (
download_missing
and (
photo.ismissing
or photo.path is None
or (export_edited and photo.path_edited is None)
)
)
# export the photo to each path in dest_paths
for dest_path in dest_paths:
# TODO: if --skip-original-if-edited, it's possible edited version is on disk but
# original is missing, in which case we should download the edited version
if export_original:
if missing_original:
space = " " if not verbose else ""
verbose_(
f"{space}Skipping missing photo {photo.original_filename} ({photo.uuid})"
)
results.missing.append(
str(pathlib.Path(dest_path) / original_filename)
)
elif photo.intrash and (not photo.path or use_photos_export):
# skip deleted files if they're missing or using use_photos_export
# as AppleScript/PhotoKit cannot export deleted photos
space = " " if not verbose else ""
verbose_(
f"{space}Skipping missing deleted photo {photo.original_filename} ({photo.uuid})"
)
results.missing.append(
str(pathlib.Path(dest_path) / original_filename)
)
else:
tries = 0
while tries <= retry:
tries += 1
error = 0
try:
export_results = photo.export2(
dest_path,
original_filename,
sidecar=sidecar_flags,
sidecar_drop_ext=sidecar_drop_ext, sidecar_drop_ext=sidecar_drop_ext,
live_photo=export_live, export_live=export_live,
raw_photo=export_raw, export_raw=export_raw,
export_as_hardlink=export_as_hardlink, export_as_hardlink=export_as_hardlink,
overwrite=overwrite, overwrite=overwrite,
use_photos_export=use_photos_export,
exiftool=exiftool, exiftool=exiftool,
merge_exif_keywords=exiftool_merge_keywords, exiftool_merge_keywords=exiftool_merge_keywords,
merge_exif_persons=exiftool_merge_persons, exiftool_merge_persons=exiftool_merge_persons,
use_albums_as_keywords=album_keyword, album_keyword=album_keyword,
use_persons_as_keywords=person_keyword, person_keyword=person_keyword,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template, description_template=description_template,
update=update, update=update,
ignore_signature=ignore_signature, ignore_signature=ignore_signature,
export_db=export_db, export_db=export_db,
fileutil=fileutil, fileutil=fileutil,
dry_run=dry_run,
touch_file=touch_file, touch_file=touch_file,
convert_to_jpeg=convert_to_jpeg, convert_to_jpeg=convert_to_jpeg,
jpeg_quality=jpeg_quality, jpeg_quality=jpeg_quality,
ignore_date_modified=ignore_date_modified, ignore_date_modified=ignore_date_modified,
use_photokit=use_photokit, use_photokit=use_photokit,
verbose=verbose_, exiftool_option=exiftool_option,
exiftool_flags=exiftool_option,
jpeg_ext=jpeg_ext, jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords, replace_keywords=replace_keywords,
retry=retry,
) )
for warning_ in export_results.exiftool_warning:
verbose_(
f"exiftool warning for file {warning_[0]}: {warning_[1]}"
)
for error_ in export_results.exiftool_error:
click.echo(
click.style(
f"exiftool error for file {error_[0]}: {error_[1]}",
fg=CLI_COLOR_ERROR,
),
err=True,
)
for error_ in export_results.error:
click.echo(
click.style(
f"Error exporting photo ({photo.uuid}: {photo.original_filename}) as {error_[0]}: {error_[1]}",
fg=CLI_COLOR_ERROR,
),
err=True,
)
error += 1
if not error or tries > retry:
results += export_results
break
else:
click.echo(
"Retrying export for photo ({photo.uuid}: {photo.original_filename})"
)
except Exception as e:
click.echo(
click.style(
f"Error exporting photo ({photo.uuid}: {photo.original_filename}) as {original_filename}: {e}",
fg=CLI_COLOR_ERROR,
),
err=True,
)
if tries > retry:
results.error.append(
(str(pathlib.Path(dest) / original_filename), e)
)
break
else:
click.echo(
f"Retrying export for photo ({photo.uuid}: {photo.original_filename})"
)
else:
verbose_(f"Skipping original version of {photo.original_filename}")
# if export-edited, also export the edited version
# verify the photo has adjustments and valid path to avoid raising an exception
if export_edited and photo.hasadjustments: if export_edited and photo.hasadjustments:
edited_filename = pathlib.Path(filename) # if export-edited, also export the edited version
edited_filenames = get_filenames_from_template(
photo, filename_template, original_name, strip=strip
)
for edited_filename in edited_filenames:
edited_filename = pathlib.Path(edited_filename)
# verify the photo has adjustments and valid path to avoid raising an exception
edited_ext = ( edited_ext = (
# rare cases on Photos <= 4 that uti_edited is None # rare cases on Photos <= 4 that uti_edited is None
"." + get_preferred_uti_extension(photo.uti_edited) "." + get_preferred_uti_extension(photo.uti_edited)
@@ -2400,11 +2331,7 @@ def export_photo(
else pathlib.Path(photo.filename).suffix else pathlib.Path(photo.filename).suffix
) )
if ( if photo.isphoto and jpeg_ext and edited_ext.lower() in [".jpg", ".jpeg"]:
photo.isphoto
and jpeg_ext
and edited_ext.lower() in [".jpg", ".jpeg"]
):
edited_ext = "." + jpeg_ext edited_ext = "." + jpeg_ext
# Big Sur uses .heic for some edited photos so need to check # Big Sur uses .heic for some edited photos so need to check
@@ -2437,24 +2364,137 @@ def export_photo(
f"Invalid template for --edited-suffix: may not use multi-valued templates: '{edited_suffix}': results={rendered_suffix}", f"Invalid template for --edited-suffix: may not use multi-valued templates: '{edited_suffix}': results={rendered_suffix}",
) )
rendered_suffix = rendered_suffix[0] rendered_suffix = rendered_suffix[0]
edited_filename = f"{edited_filename.stem}{rendered_suffix}{edited_ext}"
edited_filename = (
f"{edited_filename.stem}{rendered_suffix}{edited_ext}"
)
else: else:
edited_filename = f"{edited_filename.stem}{edited_ext}" edited_filename = f"{edited_filename.stem}{edited_ext}"
verbose_( verbose_(
f"Exporting edited version of {photo.original_filename} ({photo.filename}) as {edited_filename}" f"Exporting edited version of {photo.original_filename} ({photo.filename}) as {edited_filename}"
) )
if missing_edited:
results += export_photo_with_template(
photo=photo,
filename=edited_filename,
directory=directory,
edited=True,
use_photos_export=use_photos_export,
export_by_date=export_by_date,
dest=dest,
dry_run=dry_run,
strip=strip,
export_original=export_original,
missing=missing_edited,
verbose=verbose,
sidecar_flags=sidecar_flags,
sidecar_drop_ext=sidecar_drop_ext,
export_live=export_live,
export_raw=export_raw,
export_as_hardlink=export_as_hardlink,
overwrite=overwrite,
exiftool=exiftool,
exiftool_merge_keywords=exiftool_merge_keywords,
exiftool_merge_persons=exiftool_merge_persons,
album_keyword=album_keyword,
person_keyword=person_keyword,
keyword_template=keyword_template,
description_template=description_template,
update=update,
ignore_signature=ignore_signature,
export_db=export_db,
fileutil=fileutil,
touch_file=touch_file,
convert_to_jpeg=convert_to_jpeg,
jpeg_quality=jpeg_quality,
ignore_date_modified=ignore_date_modified,
use_photokit=use_photokit,
exiftool_option=exiftool_option,
jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords,
retry=retry,
)
return results
def export_photo_with_template(
photo,
filename,
directory,
edited,
use_photos_export,
export_by_date,
dest,
dry_run,
strip,
export_original,
missing,
verbose,
sidecar_flags,
sidecar_drop_ext,
export_live,
export_raw,
export_as_hardlink,
overwrite,
exiftool,
exiftool_merge_keywords,
exiftool_merge_persons,
album_keyword,
person_keyword,
keyword_template,
description_template,
update,
ignore_signature,
export_db,
fileutil,
touch_file,
convert_to_jpeg,
jpeg_quality,
ignore_date_modified,
use_photokit,
exiftool_option,
jpeg_ext,
replace_keywords,
retry,
):
""" Evaluate directory template then export photo to each directory """
results = ExportResults()
dest_paths = get_dirnames_from_template(
photo, directory, export_by_date, dest, dry_run, strip=strip
)
# export the photo to each path in dest_paths
for dest_path in dest_paths:
# TODO: if --skip-original-if-edited, it's possible edited version is on disk but
# original is missing, in which case we should download the edited version
if export_original:
if missing:
space = " " if not verbose else "" space = " " if not verbose else ""
verbose_( verbose_(
f"{space}Skipping missing edited photo for {edited_filename}" f"{space}Skipping missing photo {photo.original_filename} ({photo.uuid})"
) )
results.missing.append( results.missing.append(str(pathlib.Path(dest_path) / filename))
str(pathlib.Path(dest_path) / edited_filename) continue
elif photo.intrash and (not photo.path or use_photos_export):
# skip deleted files if they're missing or using use_photos_export
# as AppleScript/PhotoKit cannot export deleted photos
space = " " if not verbose else ""
verbose_(
f"{space}Skipping missing deleted photo {photo.original_filename} ({photo.uuid})"
) )
results.missing.append(str(pathlib.Path(dest_path) / filename))
continue
elif not edited:
verbose_(f"Skipping original version of {photo.original_filename}")
continue
else:
# exporting the edited version
if missing:
space = " " if not verbose else ""
verbose_(f"{space}Skipping missing edited photo for {filename}")
results.missing.append(str(pathlib.Path(dest_path) / filename))
continue
elif photo.intrash and (not photo.path_edited or use_photos_export): elif photo.intrash and (not photo.path_edited or use_photos_export):
# skip deleted files if they're missing or using use_photos_export # skip deleted files if they're missing or using use_photos_export
# as AppleScript/PhotoKit cannot export deleted photos # as AppleScript/PhotoKit cannot export deleted photos
@@ -2462,24 +2502,24 @@ def export_photo(
verbose_( verbose_(
f"{space}Skipping missing deleted photo {photo.original_filename} ({photo.uuid})" f"{space}Skipping missing deleted photo {photo.original_filename} ({photo.uuid})"
) )
results.missing.append( results.missing.append(str(pathlib.Path(dest_path) / filename))
str(pathlib.Path(dest_path) / edited_filename) continue
)
else:
tries = 0 tries = 0
while tries <= retry: while tries <= retry:
tries += 1 tries += 1
error = 0 error = 0
try: try:
export_results_edited = photo.export2( export_results = photo.export2(
dest_path, dest_path,
edited_filename, filename,
edited=edited,
sidecar=sidecar_flags, sidecar=sidecar_flags,
sidecar_drop_ext=sidecar_drop_ext, sidecar_drop_ext=sidecar_drop_ext,
live_photo=export_live,
raw_photo=export_raw,
export_as_hardlink=export_as_hardlink, export_as_hardlink=export_as_hardlink,
overwrite=overwrite, overwrite=overwrite,
edited=True,
use_photos_export=use_photos_export, use_photos_export=use_photos_export,
exiftool=exiftool, exiftool=exiftool,
merge_exif_keywords=exiftool_merge_keywords, merge_exif_keywords=exiftool_merge_keywords,
@@ -2503,11 +2543,9 @@ def export_photo(
jpeg_ext=jpeg_ext, jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords, replace_keywords=replace_keywords,
) )
for warning_ in export_results_edited.exiftool_warning: for warning_ in export_results.exiftool_warning:
verbose_( verbose_(f"exiftool warning for file {warning_[0]}: {warning_[1]}")
f"exiftool warning for file {warning_[0]}: {warning_[1]}" for error_ in export_results.exiftool_error:
)
for error_ in export_results_edited.exiftool_error:
click.echo( click.echo(
click.style( click.style(
f"exiftool error for file {error_[0]}: {error_[1]}", f"exiftool error for file {error_[0]}: {error_[1]}",
@@ -2515,17 +2553,17 @@ def export_photo(
), ),
err=True, err=True,
) )
for error_ in export_results_edited.error: for error_ in export_results.error:
click.echo( click.echo(
click.style( click.style(
f"Error exporting edited photo ({photo.uuid}: {photo.original_filename}) as {error_[0]}: {error_[1]}", f"Error exporting photo ({photo.uuid}: {photo.original_filename}) as {error_[0]}: {error_[1]}",
fg=CLI_COLOR_ERROR, fg=CLI_COLOR_ERROR,
), ),
err=True, err=True,
) )
error += 1 error += 1
if not error or tries > retry: if not error or tries > retry:
results += export_results_edited results += export_results
break break
else: else:
click.echo( click.echo(
@@ -2534,15 +2572,13 @@ def export_photo(
except Exception as e: except Exception as e:
click.echo( click.echo(
click.style( click.style(
f"Error exporting edited photo ({photo.uuid}: {photo.original_filename}) {filename} as {edited_filename}: {e}", f"Error exporting photo ({photo.uuid}: {photo.original_filename}) as {filename}: {e}",
fg=CLI_COLOR_ERROR, fg=CLI_COLOR_ERROR,
), ),
err=True, err=True,
) )
if tries > retry: if tries > retry:
results.error.append( results.error.append((str(pathlib.Path(dest) / filename), e))
(str(pathlib.Path(dest) / edited_filename), e)
)
break break
else: else:
click.echo( click.echo(

File diff suppressed because one or more lines are too long