Changed default CLI behavior to export all photos

This commit is contained in:
Rhet Turnbull
2020-04-17 22:52:11 -07:00
parent 55daa31c71
commit ed425724a0
5 changed files with 103 additions and 46 deletions

View File

@@ -855,12 +855,6 @@ def query(
@cli.command(cls=ExportCommand) @cli.command(cls=ExportCommand)
@DB_OPTION @DB_OPTION
# @click.option(
# "--all",
# is_flag=True,
# help="Export all versions of photos including "
# "edited photos, live photos, burst photos, and RAW photos.",
# )
@click.option("--verbose", "-V", is_flag=True, help="Print verbose output.") @click.option("--verbose", "-V", is_flag=True, help="Print verbose output.")
@query_options @query_options
@click.option( @click.option(
@@ -878,29 +872,26 @@ def query(
"(e.g. DEST/2019/12/20/photoname.jpg).", "(e.g. DEST/2019/12/20/photoname.jpg).",
) )
@click.option( @click.option(
"--export-edited", "--skip-edited",
is_flag=True, is_flag=True,
help="Also export edited version of photo if an edited version exists. " help="Do not export edited version of photo if an edited version exists.",
'Edited photo will be named in form of "photoname_edited.ext"',
) )
@click.option( @click.option(
"--export-bursts", "--skip-bursts",
is_flag=True, is_flag=True,
help="If a photo is a burst photo export all associated burst images in the library. " help="Do not export all associated burst images in the library if a photo is a burst photo. ",
"Not currently compatible with --download-misssing; see note on --download-missing.",
) )
@click.option( @click.option(
"--export-live", "--skip-live",
is_flag=True, is_flag=True,
help="If a photo is a live photo export the associated live video component." help="Do not export the associated live video component of a live photo.",
" Live video will have same name as photo but with .mov extension. ",
) )
@click.option( @click.option(
"--export-raw", "--skip-raw",
is_flag=True, is_flag=True,
help="If a photo was imported in RAW format with associated jpeg, also export the " help="Do not export associated RAW images of a RAW/jpeg pair. "
"RAW photo in addition to the jpeg. (By default, Photos treats the jpeg as the " "Note: this does not skip RAW photos if the RAW photo does not have an associated jpeg image "
"original image.)", "(e.g. the RAW file was imported to Photos without a jpeg preview.",
) )
@click.option( @click.option(
"--original-name", "--original-name",
@@ -986,10 +977,10 @@ def export(
verbose, verbose,
overwrite, overwrite,
export_by_date, export_by_date,
export_edited, skip_edited,
export_bursts, skip_bursts,
export_live, skip_live,
export_raw, skip_raw,
original_name, original_name,
sidecar, sidecar,
only_photos, only_photos,
@@ -1027,6 +1018,10 @@ def export(
if more than one option is provided, they are treated as "AND" if more than one option is provided, they are treated as "AND"
(e.g. search for photos matching all options). (e.g. search for photos matching all options).
If no query options are provided, all photos will be exported. If no query options are provided, all photos will be exported.
By default, all versions of all photos will be exported including edited
versions, live photo movies, burst photos, and associated RAW images.
See --skip-edited, --skip-live, --skip-bursts, and --skip-raw options
to modify this behavior.
""" """
if not os.path.isdir(dest): if not os.path.isdir(dest):
@@ -1055,6 +1050,12 @@ def export(
click.echo(cli.commands["export"].get_help(ctx), err=True) click.echo(cli.commands["export"].get_help(ctx), err=True)
return return
# initialize export flags
# by default, will export all versions of photos unless skip flag is set
(export_edited, export_bursts, export_live, export_raw) = [
not x for x in [skip_edited, skip_bursts, skip_live, skip_raw]
]
# verify exiftool installed an in path # verify exiftool installed an in path
if exiftool: if exiftool:
try: try:

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.27.7" __version__ = "0.28.0"

View File

@@ -718,7 +718,7 @@ class PhotoInfo:
# Photo's often converts .JPG to .jpeg # Photo's often converts .JPG to .jpeg
suffixes = sorted([x.lower() for x in [dest.suffix, actual_suffix]]) suffixes = sorted([x.lower() for x in [dest.suffix, actual_suffix]])
if dest.suffix != actual_suffix and suffixes != [".jpeg", ".jpg"]: if dest.suffix != actual_suffix and suffixes != [".jpeg", ".jpg"]:
logging.warning( logging.debug(
f"Invalid destination suffix: {dest.suffix}, should be {actual_suffix}" f"Invalid destination suffix: {dest.suffix}, should be {actual_suffix}"
) )

View File

@@ -206,6 +206,25 @@ def test_export():
import osxphotos import osxphotos
from osxphotos.__main__ import export 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), ".", "--original-name", "-V"]
)
assert result.exit_code == 0
files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES)
def test_export_skip_edited():
import glob
import os
import os.path
import osxphotos
from osxphotos.__main__ import export
runner = CliRunner() runner = CliRunner()
cwd = os.getcwd() cwd = os.getcwd()
# pylint: disable=not-context-manager # pylint: disable=not-context-manager
@@ -215,14 +234,14 @@ def test_export():
[ [
os.path.join(cwd, CLI_PHOTOS_DB), os.path.join(cwd, CLI_PHOTOS_DB),
".", ".",
"--skip-edited",
"--original-name", "--original-name",
"--export-edited",
"-V", "-V",
], ],
) )
assert result.exit_code == 0 assert result.exit_code == 0
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES) assert "St James Park_edited.jpeg" not in files
def test_query_date(): def test_query_date():
@@ -290,6 +309,25 @@ def test_export_live():
import osxphotos import osxphotos
from osxphotos.__main__ import export 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, LIVE_PHOTOS_DB), ".", "--live", "--original-name", "-V"],
)
files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_LIVE_ORIGINAL)
def test_export_skip_live():
import glob
import os
import os.path
import osxphotos
from osxphotos.__main__ import export
runner = CliRunner() runner = CliRunner()
cwd = os.getcwd() cwd = os.getcwd()
# pylint: disable=not-context-manager # pylint: disable=not-context-manager
@@ -299,14 +337,13 @@ def test_export_live():
[ [
os.path.join(cwd, LIVE_PHOTOS_DB), os.path.join(cwd, LIVE_PHOTOS_DB),
".", ".",
"--live", "--skip-live",
"--original-name", "--original-name",
"--export-live",
"-V", "-V",
], ],
) )
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_LIVE_ORIGINAL) assert "img_0728.mov" not in [f.lower() for f in files]
def test_export_raw(): def test_export_raw():
@@ -320,11 +357,33 @@ def test_export_raw():
cwd = os.getcwd() cwd = os.getcwd()
# pylint: disable=not-context-manager # pylint: disable=not-context-manager
with runner.isolated_filesystem(): with runner.isolated_filesystem():
result = runner.invoke(export, [os.path.join(cwd, RAW_PHOTOS_DB), ".", "-V"]) result = runner.invoke(
export, [os.path.join(cwd, RAW_PHOTOS_DB), ".", "--skip-edited", "-V"]
)
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_RAW) assert sorted(files) == sorted(CLI_EXPORT_RAW)
# TODO: Update this once RAW db is added
# def test_skip_raw():
# 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, RAW_PHOTOS_DB), ".", "--skip-raw", "-V"]
# )
# files = glob.glob("*")
# for rawname in CLI_EXPORT_RAW:
# assert rawname.lower() not in [f.lower() for f in files]
def test_export_raw_original(): def test_export_raw_original():
import glob import glob
import os import os
@@ -337,7 +396,14 @@ def test_export_raw_original():
# pylint: disable=not-context-manager # pylint: disable=not-context-manager
with runner.isolated_filesystem(): with runner.isolated_filesystem():
result = runner.invoke( result = runner.invoke(
export, [os.path.join(cwd, RAW_PHOTOS_DB), ".", "--original-name", "-V"] export,
[
os.path.join(cwd, RAW_PHOTOS_DB),
".",
"--skip-edited",
"--original-name",
"-V",
],
) )
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_RAW_ORIGINAL) assert sorted(files) == sorted(CLI_EXPORT_RAW_ORIGINAL)
@@ -354,9 +420,7 @@ def test_export_raw_edited():
cwd = os.getcwd() cwd = os.getcwd()
# pylint: disable=not-context-manager # pylint: disable=not-context-manager
with runner.isolated_filesystem(): with runner.isolated_filesystem():
result = runner.invoke( result = runner.invoke(export, [os.path.join(cwd, RAW_PHOTOS_DB), ".", "-V"])
export, [os.path.join(cwd, RAW_PHOTOS_DB), ".", "--export-edited", "-V"]
)
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_RAW_EDITED) assert sorted(files) == sorted(CLI_EXPORT_RAW_EDITED)
@@ -373,14 +437,7 @@ def test_export_raw_edited_original():
# pylint: disable=not-context-manager # pylint: disable=not-context-manager
with runner.isolated_filesystem(): with runner.isolated_filesystem():
result = runner.invoke( result = runner.invoke(
export, export, [os.path.join(cwd, RAW_PHOTOS_DB), ".", "--original-name", "-V"]
[
os.path.join(cwd, RAW_PHOTOS_DB),
".",
"--export-edited",
"--original-name",
"-V",
],
) )
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_RAW_EDITED_ORIGINAL) assert sorted(files) == sorted(CLI_EXPORT_RAW_EDITED_ORIGINAL)

View File

@@ -104,7 +104,7 @@ def test_export_edited_default():
assert pathlib.Path(got_dest).name == FILENAME_DICT["current_edited"] assert pathlib.Path(got_dest).name == FILENAME_DICT["current_edited"]
def test_export_edited_wrong_suffix(caplog): def test_export_edited_wrong_suffix():
# export edited file with name provided but wrong suffix # export edited file with name provided but wrong suffix
# should produce a warning via logging.warning # should produce a warning via logging.warning
import os import os
@@ -126,7 +126,6 @@ def test_export_edited_wrong_suffix(caplog):
expected_dest = os.path.join(dest, filename) expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, edited=True)[0] got_dest = photos[0].export(dest, filename, edited=True)[0]
assert "Invalid destination suffix" in caplog.text # assert "Invalid destination suffix" in caplog.text
assert got_dest == expected_dest assert got_dest == expected_dest
assert pathlib.Path(got_dest).name == filename assert pathlib.Path(got_dest).name == filename