Added --skip-original-if-edited for issue #159

This commit is contained in:
Rhet Turnbull
2020-09-07 06:33:37 -07:00
parent 58b3869a7c
commit 5f2d401048
3 changed files with 102 additions and 52 deletions

View File

@@ -1165,6 +1165,11 @@ def query(
is_flag=True, is_flag=True,
help="Do not export edited version of photo if an edited version exists.", help="Do not export edited version of photo if an edited version exists.",
) )
@click.option(
"--skip-original-if-edited",
is_flag=True,
help="Do not export original if there is an edited version (exports only the edited version).",
)
@click.option( @click.option(
"--skip-bursts", "--skip-bursts",
is_flag=True, is_flag=True,
@@ -1335,6 +1340,7 @@ def export(
overwrite, overwrite,
export_by_date, export_by_date,
skip_edited, skip_edited,
skip_original_if_edited,
skip_bursts, skip_bursts,
skip_live, skip_live,
skip_raw, skip_raw,
@@ -1417,6 +1423,7 @@ def export(
(export_as_hardlink, exiftool), (export_as_hardlink, exiftool),
(any(place), no_place), (any(place), no_place),
(deleted, deleted_only), (deleted, deleted_only),
(skip_edited, skip_original_if_edited)
] ]
if any(all(bb) for bb in exclusive): if any(all(bb) for bb in exclusive):
click.echo("Incompatible export options", err=True) click.echo("Incompatible export options", err=True)
@@ -1584,6 +1591,7 @@ def export(
export_as_hardlink=export_as_hardlink, export_as_hardlink=export_as_hardlink,
overwrite=overwrite, overwrite=overwrite,
export_edited=export_edited, export_edited=export_edited,
skip_original_if_edited=skip_original_if_edited,
original_name=original_name, original_name=original_name,
export_live=export_live, export_live=export_live,
download_missing=download_missing, download_missing=download_missing,
@@ -1624,6 +1632,7 @@ def export(
export_as_hardlink=export_as_hardlink, export_as_hardlink=export_as_hardlink,
overwrite=overwrite, overwrite=overwrite,
export_edited=export_edited, export_edited=export_edited,
skip_original_if_edited=skip_original_if_edited,
original_name=original_name, original_name=original_name,
export_live=export_live, export_live=export_live,
download_missing=download_missing, download_missing=download_missing,
@@ -1655,7 +1664,6 @@ def export(
photo_str_new = "photos" if len(results_new) != 1 else "photo" photo_str_new = "photos" if len(results_new) != 1 else "photo"
photo_str_updated = "photos" if len(results_updated) != 1 else "photo" photo_str_updated = "photos" if len(results_updated) != 1 else "photo"
photo_str_skipped = "photos" if len(results_skipped) != 1 else "photo" photo_str_skipped = "photos" if len(results_skipped) != 1 else "photo"
photo_str_touched = "photos" if len(results_touched) != 1 else "photo"
photo_str_exif_updated = ( photo_str_exif_updated = (
"photos" if len(results_exif_updated) != 1 else "photo" "photos" if len(results_exif_updated) != 1 else "photo"
) )
@@ -1665,16 +1673,13 @@ def export(
f"skipped: {len(results_skipped)} {photo_str_skipped}, " f"skipped: {len(results_skipped)} {photo_str_skipped}, "
f"updated EXIF data: {len(results_exif_updated)} {photo_str_exif_updated}" f"updated EXIF data: {len(results_exif_updated)} {photo_str_exif_updated}"
) )
if touch_file:
summary += f", touched date: {len(results_touched)} {photo_str_touched}"
click.echo(summary)
else: else:
photo_str = "photos" if len(results_exported) != 1 else "photo" photo_str = "photos" if len(results_exported) != 1 else "photo"
photo_str_touched = "photos" if len(results_touched) != 1 else "photo"
summary = f"Exported: {len(results_exported)} {photo_str}" summary = f"Exported: {len(results_exported)} {photo_str}"
if touch_file: photo_str_touched = "photos" if len(results_touched) != 1 else "photo"
summary += f", touched date: {len(results_touched)} {photo_str_touched}" if touch_file:
click.echo(summary) summary += f", touched date: {len(results_touched)} {photo_str_touched}"
click.echo(summary)
click.echo(f"Elapsed time: {(stop_time-start_time):.3f} seconds") click.echo(f"Elapsed time: {(stop_time-start_time):.3f} seconds")
else: else:
click.echo("Did not find any photos to export") click.echo("Did not find any photos to export")
@@ -2116,6 +2121,7 @@ def export_photo(
export_as_hardlink=None, export_as_hardlink=None,
overwrite=None, overwrite=None,
export_edited=None, export_edited=None,
skip_original_if_edited=None,
original_name=None, original_name=None,
export_live=None, export_live=None,
download_missing=None, download_missing=None,
@@ -2154,6 +2160,8 @@ def export_photo(
filename_template: template use to determine output file filename_template: template use to determine output file
no_extended_attributes: boolean; if True, exports photo without preserving extended attributes no_extended_attributes: boolean; if True, exports photo without preserving extended attributes
export_raw: boolean; if True exports RAW image associate with the photo export_raw: boolean; if True exports RAW image associate with the photo
export_edited: boolean; if True exports edited version of photo if there is one
skip_original_if_edited: boolean; if True does not export original if photo has been edited
album_keyword: boolean; if True, exports album names as keywords in metadata album_keyword: boolean; if True, exports album names as keywords in metadata
person_keyword: boolean; if True, exports person names as keywords in metadata person_keyword: boolean; if True, exports person names as keywords in metadata
keyword_template: list of strings; if provided use rendered template strings as keywords keyword_template: list of strings; if provided use rendered template strings as keywords
@@ -2176,18 +2184,18 @@ def export_photo(
if not download_missing: if not download_missing:
if photo.ismissing: if photo.ismissing:
space = " " if not verbose_ else "" space = " " if not verbose_ else ""
verbose(f"{space}Skipping missing photo {photo.filename}") verbose(f"{space}Skipping missing photo {photo.original_filename}")
return ExportResults([], [], [], [], [], []) return ExportResults([], [], [], [], [], [])
elif not os.path.exists(photo.path): elif not os.path.exists(photo.path):
space = " " if not verbose_ else "" space = " " if not verbose_ else ""
verbose( verbose(
f"{space}WARNING: file {photo.path} is missing but ismissing=False, " f"{space}WARNING: file {photo.path} is missing but ismissing=False, "
f"skipping {photo.filename}" f"skipping {photo.original_filename}"
) )
return ExportResults([], [], [], [], [], []) return ExportResults([], [], [], [], [], [])
elif photo.ismissing and not photo.iscloudasset or not photo.incloud: elif photo.ismissing and not photo.iscloudasset or not photo.incloud:
verbose( verbose(
f"Skipping missing {photo.filename}: not iCloud asset or missing from cloud" f"Skipping missing {photo.original_filename}: not iCloud asset or missing from cloud"
) )
return ExportResults([], [], [], [], [], []) return ExportResults([], [], [], [], [], [])
@@ -2198,9 +2206,11 @@ def export_photo(
results_exif_updated = [] results_exif_updated = []
results_touched = [] results_touched = []
export_original = not (skip_original_if_edited and photo.hasadjustments)
filenames = get_filenames_from_template(photo, filename_template, original_name) filenames = get_filenames_from_template(photo, filename_template, original_name)
for filename in filenames: for filename in filenames:
verbose(f"Exporting {photo.filename} as {filename}") verbose(f"Exporting {photo.original_filename} ({photo.filename}) as {filename}")
dest_paths = get_dirnames_from_template( dest_paths = get_dirnames_from_template(
photo, directory, export_by_date, dest, dry_run photo, directory, export_by_date, dest, dry_run
@@ -2223,47 +2233,50 @@ def export_photo(
# export the photo to each path in dest_paths # export the photo to each path in dest_paths
for dest_path in dest_paths: for dest_path in dest_paths:
export_results = photo.export2( if not export_original:
dest_path, verbose(f"Skipping original version of {photo.original_filename}")
filename, else:
sidecar_json=sidecar_json, export_results = photo.export2(
sidecar_xmp=sidecar_xmp, dest_path,
live_photo=export_live, filename,
raw_photo=export_raw, sidecar_json=sidecar_json,
export_as_hardlink=export_as_hardlink, sidecar_xmp=sidecar_xmp,
overwrite=overwrite, live_photo=export_live,
use_photos_export=use_photos_export, raw_photo=export_raw,
exiftool=exiftool, export_as_hardlink=export_as_hardlink,
no_xattr=no_extended_attributes, overwrite=overwrite,
use_albums_as_keywords=album_keyword, use_photos_export=use_photos_export,
use_persons_as_keywords=person_keyword, exiftool=exiftool,
keyword_template=keyword_template, no_xattr=no_extended_attributes,
description_template=description_template, use_albums_as_keywords=album_keyword,
update=update, use_persons_as_keywords=person_keyword,
export_db=export_db, keyword_template=keyword_template,
fileutil=fileutil, description_template=description_template,
dry_run=dry_run, update=update,
touch_file=touch_file, export_db=export_db,
) fileutil=fileutil,
dry_run=dry_run,
touch_file=touch_file,
)
results_exported.extend(export_results.exported) results_exported.extend(export_results.exported)
results_new.extend(export_results.new) results_new.extend(export_results.new)
results_updated.extend(export_results.updated) results_updated.extend(export_results.updated)
results_skipped.extend(export_results.skipped) results_skipped.extend(export_results.skipped)
results_exif_updated.extend(export_results.exif_updated) results_exif_updated.extend(export_results.exif_updated)
results_touched.extend(export_results.touched) results_touched.extend(export_results.touched)
if verbose_: if verbose_:
for exported in export_results.exported: for exported in export_results.exported:
verbose(f"Exported {exported}") verbose(f"Exported {exported}")
for new in export_results.new: for new in export_results.new:
verbose(f"Exported new file {new}") verbose(f"Exported new file {new}")
for updated in export_results.updated: for updated in export_results.updated:
verbose(f"Exported updated file {updated}") verbose(f"Exported updated file {updated}")
for skipped in export_results.skipped: for skipped in export_results.skipped:
verbose(f"Skipped up to date file {skipped}") verbose(f"Skipped up to date file {skipped}")
for touched in export_results.touched: for touched in export_results.touched:
verbose(f"Touched date on file {touched}") verbose(f"Touched date on file {touched}")
# if export-edited, also export the edited version # if export-edited, also export the edited version
# verify the photo has adjustments and valid path to avoid raising an exception # verify the photo has adjustments and valid path to avoid raising an exception

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.33.9" __version__ = "0.34.0"

View File

@@ -714,6 +714,42 @@ def test_export_skip_edited():
assert "St James Park_edited.jpeg" not in files assert "St James Park_edited.jpeg" not in files
def test_export_skip_original_if_edited():
""" test export with --skip-original-if-edited """
import glob
import os
import os.path
import osxphotos
from osxphotos.__main__ import export
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[os.path.join(cwd, PHOTOS_DB_15_6), ".", "--skip-original-if-edited", "-V"],
)
assert result.exit_code == 0
assert "Skipping original version of wedding.jpg" in result.output
assert "Skipping original version of Tulips.jpg" in result.output
assert "Skipping original version of St James Park.jpg" in result.output
files = glob.glob("*")
# make sure originals of edited version not exported
assert "wedding.jpg" not in files
assert "Tulips.jpg" not in files
assert "St James Park.jpg" not in files
# make sure edited versions did get exported
assert "wedding_edited.jpeg" in files
assert "Tulips_edited.jpeg" in files
assert "St James Park_edited.jpeg" in files
# make sure other originals did get exported
assert "Pumkins2.jpg" in files
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed") @pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
def test_export_exiftool(): def test_export_exiftool():
import glob import glob
@@ -3107,6 +3143,7 @@ def test_keywords():
json_got = json.loads(result.output) json_got = json.loads(result.output)
assert json_got == KEYWORDS_JSON assert json_got == KEYWORDS_JSON
# TODO: this fails with result.exit_code == 1 but I think this has to # TODO: this fails with result.exit_code == 1 but I think this has to
# do with how pytest is invoking the command # do with how pytest is invoking the command
# def test_albums_str(): # def test_albums_str():