Added --skip-uuid, --skip-uuid-from-file, #563

This commit is contained in:
Rhet Turnbull 2021-12-31 08:35:26 -08:00
parent 44594a8e43
commit 04930c3644
13 changed files with 137 additions and 18 deletions

View File

@ -615,7 +615,8 @@ Options:
FILENAME. If more than one --name options is FILENAME. If more than one --name options is
specified, they are treated as "OR", e.g. find specified, they are treated as "OR", e.g. find
photos matching any FILENAME. photos matching any FILENAME.
--uuid UUID Search for photos with UUID(s). --uuid UUID Search for photos with UUID(s). May be
repeated to include multiple UUIDs.
--uuid-from-file FILE Search for photos with UUID(s) loaded from --uuid-from-file FILE Search for photos with UUID(s) loaded from
FILE. Format is a single UUID per line. Lines FILE. Format is a single UUID per line. Lines
preceded with # are ignored. preceded with # are ignored.
@ -837,6 +838,11 @@ Options:
photos if the RAW photo does not have an photos if the RAW photo does not have an
associated JPEG image (e.g. the RAW file was associated JPEG image (e.g. the RAW file was
imported to Photos without a JPEG preview). imported to Photos without a JPEG preview).
--skip-uuid UUID Skip photos with UUID(s) during export. May be
repeated to include multiple UUIDs.
--skip-uuid-from-file FILE Skip photos with UUID(s) loaded from FILE.
Format is a single UUID per line. Lines
preceded with # are ignored.
--current-name Use photo's current filename instead of --current-name Use photo's current filename instead of
original filename for export. Note: Starting original filename for export. Note: Starting
with Photos 5, all photos are renamed upon with Photos 5, all photos are renamed upon
@ -1715,7 +1721,7 @@ Substitution Description
{lf} A line feed: '\n', alias for {newline} {lf} A line feed: '\n', alias for {newline}
{cr} A carriage return: '\r' {cr} A carriage return: '\r'
{crlf} a carriage return + line feed: '\r\n' {crlf} a carriage return + line feed: '\r\n'
{osxphotos_version} The osxphotos version, e.g. '0.44.0' {osxphotos_version} The osxphotos version, e.g. '0.44.1'
{osxphotos_cmd_line} The full command line used to run osxphotos {osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified for The following substitutions may result in multiple values. Thus if specified for
@ -3617,7 +3623,7 @@ The following template field substitutions are availabe for use the templating s
|{lf}|A line feed: '\n', alias for {newline}| |{lf}|A line feed: '\n', alias for {newline}|
|{cr}|A carriage return: '\r'| |{cr}|A carriage return: '\r'|
|{crlf}|a carriage return + line feed: '\r\n'| |{crlf}|a carriage return + line feed: '\r\n'|
|{osxphotos_version}|The osxphotos version, e.g. '0.44.0'| |{osxphotos_version}|The osxphotos version, e.g. '0.44.1'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos| |{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in| |{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder| |{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|

View File

@ -1,4 +1,4 @@
# Sphinx build info version 1 # Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 4b8efce5e11b970d619d6d5988967d84 config: 58505ca56d322ccc59c38bc440ccc347
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.44.0', VERSION: '0.44.1',
LANGUAGE: 'None', LANGUAGE: 'None',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',

View File

@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.44.0 documentation</title> <title>osxphotos command line interface (CLI) &#8212; osxphotos 0.44.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; osxphotos 0.44.0 documentation</title> <title>Index &#8212; osxphotos 0.44.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.44.0 documentation</title> <title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.44.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos &#8212; osxphotos 0.44.0 documentation</title> <title>osxphotos &#8212; osxphotos 0.44.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos package &#8212; osxphotos 0.44.0 documentation</title> <title>osxphotos package &#8212; osxphotos 0.44.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; osxphotos 0.44.0 documentation</title> <title>Search &#8212; osxphotos 0.44.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />

View File

@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.44.0" __version__ = "0.44.1"

View File

@ -298,7 +298,8 @@ def QUERY_OPTIONS(f):
metavar="UUID", metavar="UUID",
default=None, default=None,
multiple=True, multiple=True,
help="Search for photos with UUID(s).", help="Search for photos with UUID(s). "
"May be repeated to include multiple UUIDs.",
), ),
o( o(
"--uuid-from-file", "--uuid-from-file",
@ -699,6 +700,23 @@ def cli(ctx, db, json_, debug):
"Note: this does not skip RAW photos if the RAW photo does not have an associated JPEG image " "Note: this does not skip RAW photos if the RAW photo does not have an associated JPEG image "
"(e.g. the RAW file was imported to Photos without a JPEG preview).", "(e.g. the RAW file was imported to Photos without a JPEG preview).",
) )
@click.option(
"--skip-uuid",
metavar="UUID",
default=None,
multiple=True,
help="Skip photos with UUID(s) during export. "
"May be repeated to include multiple UUIDs.",
)
@click.option(
"--skip-uuid-from-file",
metavar="FILE",
default=None,
multiple=False,
help="Skip photos with UUID(s) loaded from FILE. "
"Format is a single UUID per line. Lines preceded with # are ignored.",
type=click.Path(exists=True),
)
@click.option( @click.option(
"--current-name", "--current-name",
is_flag=True, is_flag=True,
@ -1124,6 +1142,8 @@ def export(
skip_bursts, skip_bursts,
skip_live, skip_live,
skip_raw, skip_raw,
skip_uuid,
skip_uuid_from_file,
person_keyword, person_keyword,
album_keyword, album_keyword,
keyword_template, keyword_template,
@ -1292,6 +1312,8 @@ def export(
skip_bursts = cfg.skip_bursts skip_bursts = cfg.skip_bursts
skip_live = cfg.skip_live skip_live = cfg.skip_live
skip_raw = cfg.skip_raw skip_raw = cfg.skip_raw
skip_uuid = cfg.skip_uuid
skip_uuid_from_file = cfg.skip_uuid_from_file
person_keyword = cfg.person_keyword person_keyword = cfg.person_keyword
album_keyword = cfg.album_keyword album_keyword = cfg.album_keyword
keyword_template = cfg.keyword_template keyword_template = cfg.keyword_template
@ -1703,6 +1725,13 @@ def export(
else: else:
raise ValueError(e) raise ValueError(e)
if skip_uuid:
photos = [p for p in photos if p.uuid not in skip_uuid]
if skip_uuid_from_file:
skip_uuid_list = load_uuid_from_file(skip_uuid_from_file)
photos = [p for p in photos if p.uuid not in skip_uuid_list]
if photos and only_new: if photos and only_new:
# ignore previously exported files # ignore previously exported files
previous_uuids = {uuid: 1 for uuid in export_db.get_previous_uuids()} previous_uuids = {uuid: 1 for uuid in export_db.get_previous_uuids()}
@ -2807,7 +2836,8 @@ def _render_suffix_template(
rendered_suffix, unmatched = photo.render_template(suffix_template, options) rendered_suffix, unmatched = photo.render_template(suffix_template, options)
except ValueError as e: except ValueError as e:
raise click.BadOptionUsage( raise click.BadOptionUsage(
var_name, f"Invalid template for {option_name} '{suffix_template}': {e}", var_name,
f"Invalid template for {option_name} '{suffix_template}': {e}",
) )
if not rendered_suffix or unmatched: if not rendered_suffix or unmatched:
raise click.BadOptionUsage( raise click.BadOptionUsage(
@ -3499,7 +3529,12 @@ def write_finder_tags(
def write_extended_attributes( def write_extended_attributes(
photo, files, xattr_template, strip=False, export_dir=None, export_db=None, photo,
files,
xattr_template,
strip=False,
export_dir=None,
export_db=None,
): ):
"""Writes extended attributes to exported files """Writes extended attributes to exported files
@ -3653,7 +3688,8 @@ def uninstall(packages, yes):
@click.option( @click.option(
"--uuid", "--uuid",
metavar="UUID", metavar="UUID",
help="Use with '--dump photos' to dump only certain UUIDs", help="Use with '--dump photos' to dump only certain UUIDs. "
"May be repeated to include multiple UUIDs.",
multiple=True, multiple=True,
) )
@click.option("--verbose", "-V", "verbose", is_flag=True, help="Print verbose output.") @click.option("--verbose", "-V", "verbose", is_flag=True, help="Print verbose output.")
@ -4085,7 +4121,9 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMA
@cli.command(name="tutorial") @cli.command(name="tutorial")
@click.argument( @click.argument(
"WIDTH", nargs=-1, type=click.INT, "WIDTH",
nargs=-1,
type=click.INT,
) )
@click.pass_obj @click.pass_obj
@click.pass_context @click.pass_context
@ -4172,7 +4210,7 @@ def repl(ctx, cli_obj, db, emacs):
logger = logging.getLogger() logger = logging.getLogger()
logger.disabled = True logger.disabled = True
pretty.install() pretty.install()
print(f"python version: {sys.version}") print(f"python version: {sys.version}")
print(f"osxphotos version: {osxphotos._version.__version__}") print(f"osxphotos version: {osxphotos._version.__version__}")

View File

@ -0,0 +1,6 @@
# test file for --skip-uuid-from-file
# wedding.jpg
E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51
# Tulips.jpg
6191423D-8DB8-4D4C-92BE-9BBBA308AAC4

View File

@ -49,6 +49,7 @@ UUID_SKIP_LIVE_PHOTOKIT = {
UUID_DOWNLOAD_MISSING = "C6C712C5-9316-408D-A3C3-125661422DA9" # IMG_8844.JPG UUID_DOWNLOAD_MISSING = "C6C712C5-9316-408D-A3C3-125661422DA9" # IMG_8844.JPG
UUID_FILE = "tests/uuid_from_file.txt" UUID_FILE = "tests/uuid_from_file.txt"
SKIP_UUID_FILE = "tests/skip_uuid_from_file.txt"
CLI_OUTPUT_NO_SUBCOMMAND = [ CLI_OUTPUT_NO_SUBCOMMAND = [
"Options:", "Options:",
@ -767,6 +768,14 @@ CLI_EXPORT_UUID_FROM_FILE_FILENAMES = [
"wedding_edited.jpeg", "wedding_edited.jpeg",
] ]
CLI_EXPORT_SKIP_UUID = ["E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51", "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"]
CLI_EXPORT_SKIP_UUID_FILENAMES = [
"Tulips.jpg",
"Tulips_edited.jpeg",
"wedding.jpg",
"wedding_edited.jpeg",
]
UUID_HAS_COMMENTS = [ UUID_HAS_COMMENTS = [
"4E4944A0-3E5C-4028-9600-A8709F2FA1DB", "4E4944A0-3E5C-4028-9600-A8709F2FA1DB",
"4AD7C8EF-2991-4519-9D3A-7F44A6F031BE", "4AD7C8EF-2991-4519-9D3A-7F44A6F031BE",
@ -1423,6 +1432,66 @@ def test_export_uuid_from_file():
files = glob.glob("*") files = glob.glob("*")
assert sorted(files) == sorted(CLI_EXPORT_UUID_FROM_FILE_FILENAMES) assert sorted(files) == sorted(CLI_EXPORT_UUID_FROM_FILE_FILENAMES)
def test_export_skip_uuid_from_file():
"""Test export with --skip-uuid-from-file"""
import glob
import os
import os.path
import osxphotos
from osxphotos.cli 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",
"--skip-uuid-from-file",
os.path.join(cwd, SKIP_UUID_FILE),
],
)
assert result.exit_code == 0
files = glob.glob("*")
for skipped_file in CLI_EXPORT_SKIP_UUID_FILENAMES:
assert skipped_file not in files
def test_export_skip_uuid():
"""Test export with --skip-uuid"""
import glob
import os
import os.path
import osxphotos
from osxphotos.cli import export
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
uuid_option = []
for uuid in CLI_EXPORT_SKIP_UUID:
uuid_option.append("--skip-uuid")
uuid_option.append(uuid)
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
os.path.join(cwd, PHOTOS_DB_15_7),
".",
"-V",
*uuid_option,
],
)
assert result.exit_code == 0
files = glob.glob("*")
for skipped_file in CLI_EXPORT_SKIP_UUID_FILENAMES:
assert skipped_file not in files
def test_export_preview(): def test_export_preview():
"""test export with --preview""" """test export with --preview"""