Added --missing to export, see issue #277

This commit is contained in:
Rhet Turnbull
2020-11-29 15:30:45 -08:00
parent d9dcf0917a
commit 25eacc7cad
4 changed files with 108 additions and 11 deletions

View File

@@ -227,6 +227,9 @@ Options:
--no-comment Search for photos with no comments. --no-comment Search for photos with no comments.
--has-likes Search for photos that have likes. --has-likes Search for photos that have likes.
--no-likes Search for photos with no likes. --no-likes Search for photos with no likes.
--missing Export only photos missing from the Photos
library; must be used with --download-
missing.
--deleted Include photos from the 'Recently Deleted' --deleted Include photos from the 'Recently Deleted'
folder. folder.
--deleted-only Include only photos from the 'Recently --deleted-only Include only photos from the 'Recently
@@ -234,7 +237,8 @@ Options:
--update Only export new or updated files. See notes --update Only export new or updated files. See notes
below on export and --update. below on export and --update.
--dry-run Dry run (test) the export but don't actually --dry-run Dry run (test) the export but don't actually
export any files; most useful with --verbose export any files; most useful with
--verbose.
--export-as-hardlink Hardlink files instead of copying them. --export-as-hardlink Hardlink files instead of copying them.
Cannot be used with --exiftool which creates Cannot be used with --exiftool which creates
copies of the files with embedded EXIF data. copies of the files with embedded EXIF data.

View File

@@ -98,7 +98,7 @@ class DateTimeISO8601(click.ParamType):
def convert(self, value, param, ctx): def convert(self, value, param, ctx):
try: try:
return datetime.datetime.fromisoformat(value) return datetime.datetime.fromisoformat(value)
except: except Exception:
self.fail( self.fail(
f"Invalid value for --{param.name}: invalid datetime format {value}. " f"Invalid value for --{param.name}: invalid datetime format {value}. "
"Valid format: YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]" "Valid format: YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]"
@@ -651,7 +651,7 @@ def debug_dump(ctx, cli_obj, db, photos_library, dump, uuid, verbose_):
val = getattr(photosdb, attr) val = getattr(photosdb, attr)
print(f"{attr}:") print(f"{attr}:")
pprint.pprint(val) pprint.pprint(val)
except: except Exception:
print(f"Did not find attribute {attr} in PhotosDB") print(f"Did not find attribute {attr} in PhotosDB")
@@ -846,12 +846,12 @@ def places(ctx, cli_obj, db, json_, photos_library):
if photo.place: if photo.place:
try: try:
place_names[photo.place.name] += 1 place_names[photo.place.name] += 1
except: except Exception:
place_names[photo.place.name] = 1 place_names[photo.place.name] = 1
else: else:
try: try:
place_names[_UNKNOWN_PLACE] += 1 place_names[_UNKNOWN_PLACE] += 1
except: except Exception:
place_names[_UNKNOWN_PLACE] = 1 place_names[_UNKNOWN_PLACE] = 1
# sort by place count # sort by place count
@@ -1199,6 +1199,11 @@ def query(
@DB_OPTION @DB_OPTION
@click.option("--verbose", "-V", "verbose_", is_flag=True, help="Print verbose output.") @click.option("--verbose", "-V", "verbose_", is_flag=True, help="Print verbose output.")
@query_options @query_options
@click.option(
"--missing",
is_flag=True,
help="Export only photos missing from the Photos library; must be used with --download-missing.",
)
@deleted_options @deleted_options
@click.option( @click.option(
"--update", "--update",
@@ -1208,7 +1213,7 @@ def query(
@click.option( @click.option(
"--dry-run", "--dry-run",
is_flag=True, is_flag=True,
help="Dry run (test) the export but don't actually export any files; most useful with --verbose", help="Dry run (test) the export but don't actually export any files; most useful with --verbose.",
) )
@click.option( @click.option(
"--export-as-hardlink", "--export-as-hardlink",
@@ -1451,6 +1456,7 @@ def export(
from_date, from_date,
to_date, to_date,
verbose_, verbose_,
missing,
update, update,
dry_run, dry_run,
export_as_hardlink, export_as_hardlink,
@@ -1567,6 +1573,20 @@ def export(
click.echo(cli.commands["export"].get_help(ctx), err=True) click.echo(cli.commands["export"].get_help(ctx), err=True)
return return
if export_as_hardlink and download_missing:
click.echo(
"Incompatible export options: --export-as-hardlink is not compatible with --download-missing",
err=True,
)
raise click.Abort()
if missing and not download_missing:
click.echo(
"Incompatible export options: --missing must be used with --download-missing",
err=True,
)
raise click.Abort()
if use_photokit and not check_photokit_authorization(): if use_photokit and not check_photokit_authorization():
click.echo( click.echo(
"Requesting access to use your Photos library. Click 'OK' on the dialog box to grant access." "Requesting access to use your Photos library. Click 'OK' on the dialog box to grant access."
@@ -1674,7 +1694,7 @@ def export(
not_favorite=not_favorite, not_favorite=not_favorite,
hidden=hidden, hidden=hidden,
not_hidden=not_hidden, not_hidden=not_hidden,
missing=None, # missing -- won't export these but will warn user missing=missing,
not_missing=None, not_missing=None,
shared=shared, shared=shared,
not_shared=not_shared, not_shared=not_shared,
@@ -2575,7 +2595,7 @@ def export_photo(
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}")
except: except Exception:
click.echo( click.echo(
f"Error exporting photo {photo.original_filename} ({photo.filename}) as {original_filename}", f"Error exporting photo {photo.original_filename} ({photo.filename}) as {original_filename}",
err=True, err=True,
@@ -2603,7 +2623,7 @@ def export_photo(
f"{edited_filename.stem}{edited_suffix}{edited_ext}" f"{edited_filename.stem}{edited_suffix}{edited_ext}"
) )
verbose( verbose(
f"Exporting edited version of {filename} as {edited_filename}" f"Exporting edited version of {photo.original_filename} ({photo.filename}) as {edited_filename}"
) )
try: try:
export_results_edited = photo.export2( export_results_edited = photo.export2(
@@ -2666,7 +2686,7 @@ def export_photo(
verbose(f"Skipped up to date file {skipped}") verbose(f"Skipped up to date file {skipped}")
for touched in export_results_edited.touched: for touched in export_results_edited.touched:
verbose(f"Touched date on file {touched}") verbose(f"Touched date on file {touched}")
except: except Exception:
click.echo( click.echo(
f"Error exporting photo {filename} as {edited_filename}", f"Error exporting photo {filename} as {edited_filename}",
err=True, err=True,

View File

@@ -1,4 +1,4 @@
""" version info """ """ version info """
__version__ = "0.37.1" __version__ = "0.37.2"

View File

@@ -17,6 +17,7 @@ PLACES_PHOTOS_DB_13 = "tests/Test-Places-High-Sierra-10.13.6.photoslibrary"
PHOTOS_DB_15_4 = "tests/Test-10.15.4.photoslibrary" PHOTOS_DB_15_4 = "tests/Test-10.15.4.photoslibrary"
PHOTOS_DB_15_5 = "tests/Test-10.15.5.photoslibrary" PHOTOS_DB_15_5 = "tests/Test-10.15.5.photoslibrary"
PHOTOS_DB_15_6 = "tests/Test-10.15.6.photoslibrary" PHOTOS_DB_15_6 = "tests/Test-10.15.6.photoslibrary"
PHOTOS_DB_15_7 = "tests/Test-10.15.7.photoslibrary"
PHOTOS_DB_TOUCH = PHOTOS_DB_15_6 PHOTOS_DB_TOUCH = PHOTOS_DB_15_6
PHOTOS_DB_14_6 = "tests/Test-10.14.6.photoslibrary" PHOTOS_DB_14_6 = "tests/Test-10.14.6.photoslibrary"
@@ -3804,3 +3805,75 @@ def test_export_report_not_a_file():
assert result.exit_code != 0 assert result.exit_code != 0
assert "Aborted!" in result.output assert "Aborted!" in result.output
def test_export_as_hardlink_download_missing():
""" test export with incompatible export options """
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, CLI_PHOTOS_DB),
".",
"-V",
"--download-missing",
"--export-as-hardlink",
".",
],
)
assert result.exit_code != 0
assert "Aborted!" in result.output
def test_export_missing():
""" test export with --missing """
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_7),
".",
"-V",
"--missing",
"--download-missing",
".",
],
)
assert result.exit_code == 0
assert "Exporting 2 photos" in result.output
def test_export_missing_not_download_missing():
""" test export with incompatible export options """
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, CLI_PHOTOS_DB), ".", "-V", "--missing", "."]
)
assert result.exit_code != 0
assert "Aborted!" in result.output