Changed default CLI behavior to export all photos
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.27.7"
|
__version__ = "0.28.0"
|
||||||
|
|||||||
@@ -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}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user