Implemented --post-function, #442
This commit is contained in:
27
README.md
27
README.md
@@ -1073,6 +1073,18 @@ Options:
|
|||||||
command by repeating the '--post-command'
|
command by repeating the '--post-command'
|
||||||
option with different arguments. See Post
|
option with different arguments. See Post
|
||||||
Command below.
|
Command below.
|
||||||
|
--post-function filename.py::function
|
||||||
|
Run function on exported files. Use this in
|
||||||
|
format: --post-function filename.py::function
|
||||||
|
where filename.py is a python file you've
|
||||||
|
created and function is the name of the
|
||||||
|
function in the python file you want to call.
|
||||||
|
The function will be passed information about
|
||||||
|
the photo that's been exported and a list of
|
||||||
|
all exported files associated with the photo.
|
||||||
|
You can run more than one function by
|
||||||
|
repeating the '--post-function' option with
|
||||||
|
different arguments. See Post Function below.
|
||||||
--exportdb EXPORTDB_FILE Specify alternate name for database file which
|
--exportdb EXPORTDB_FILE Specify alternate name for database file which
|
||||||
stores state information for export and
|
stores state information for export and
|
||||||
--update. If --exportdb is not specified,
|
--update. If --exportdb is not specified,
|
||||||
@@ -1571,7 +1583,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.42.41'
|
{osxphotos_version} The osxphotos version, e.g. '0.42.43'
|
||||||
{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
|
||||||
@@ -1729,6 +1741,17 @@ to ensure your commands are as expected. This will not actually run the commands
|
|||||||
but will print out the exact command string which would be executed.
|
but will print out the exact command string which would be executed.
|
||||||
|
|
||||||
|
|
||||||
|
** Post Function **
|
||||||
|
You can run your own python functions on the exported photos for post-processing
|
||||||
|
using the '--post-function' option. '--post-function' is passed the name a
|
||||||
|
python file and the name of the function in the file to call using format
|
||||||
|
'filename.py::function_name'. See the example function at
|
||||||
|
https://github.com/RhetTbull/osxphotos/blob/master/examples/post_function.py You
|
||||||
|
may specify multiple functions to run by repeating the --post-function option.
|
||||||
|
All post functions will be called immediately after export of each photo and
|
||||||
|
immediately before any --post-command commands. Post functions will not be
|
||||||
|
called if the --dry-run flag is set.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -3359,7 +3382,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.42.41'|
|
|{osxphotos_version}|The osxphotos version, e.g. '0.42.43'|
|
||||||
|{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|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.42.42"
|
__version__ = "0.42.43"
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ from .photokit import check_photokit_authorization, request_photokit_authorizati
|
|||||||
from .photosalbum import PhotosAlbum
|
from .photosalbum import PhotosAlbum
|
||||||
from .phototemplate import PhotoTemplate, RenderOptions
|
from .phototemplate import PhotoTemplate, RenderOptions
|
||||||
from .queryoptions import QueryOptions
|
from .queryoptions import QueryOptions
|
||||||
from .utils import get_preferred_uti_extension
|
from .utils import get_preferred_uti_extension, load_function
|
||||||
|
|
||||||
# global variable to control verbose output
|
# global variable to control verbose output
|
||||||
# set via --verbose/-V
|
# set via --verbose/-V
|
||||||
@@ -120,7 +120,7 @@ class DateTimeISO8601(click.ParamType):
|
|||||||
return datetime.datetime.fromisoformat(value)
|
return datetime.datetime.fromisoformat(value)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.fail(
|
self.fail(
|
||||||
f"Invalid value for --{param.name}: invalid datetime format {value}. "
|
f"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]]]]"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -154,12 +154,35 @@ class TimeISO8601(click.ParamType):
|
|||||||
return datetime.time.fromisoformat(value).replace(tzinfo=None)
|
return datetime.time.fromisoformat(value).replace(tzinfo=None)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.fail(
|
self.fail(
|
||||||
f"Invalid value for --{param.name}: invalid time format {value}. "
|
f"Invalid time format {value}. "
|
||||||
"Valid format: HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] "
|
"Valid format: HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] "
|
||||||
"however, note that timezone will be ignored."
|
"however, note that timezone will be ignored."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionCall(click.ParamType):
|
||||||
|
name = "FUNCTION"
|
||||||
|
|
||||||
|
def convert(self, value, param, ctx):
|
||||||
|
if "::" not in value:
|
||||||
|
self.fail(
|
||||||
|
f"Could not parse function name from '{value}'. "
|
||||||
|
"Valid format filename.py::function"
|
||||||
|
)
|
||||||
|
|
||||||
|
filename, funcname = value.split("::")
|
||||||
|
|
||||||
|
if not pathlib.Path(filename).is_file():
|
||||||
|
self.fail(f"'{filename}' does not appear to be a file")
|
||||||
|
|
||||||
|
try:
|
||||||
|
function = load_function(filename, funcname)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail(f"Could not load function {funcname} from {filename}")
|
||||||
|
|
||||||
|
return (function, value)
|
||||||
|
|
||||||
|
|
||||||
# Click CLI object & context settings
|
# Click CLI object & context settings
|
||||||
class CLI_Obj:
|
class CLI_Obj:
|
||||||
def __init__(self, db=None, json=False, debug=False):
|
def __init__(self, db=None, json=False, debug=False):
|
||||||
@@ -931,6 +954,18 @@ def cli(ctx, db, json_, debug):
|
|||||||
"You can run more than one command by repeating the '--post-command' option with different arguments. "
|
"You can run more than one command by repeating the '--post-command' option with different arguments. "
|
||||||
"See Post Command below.",
|
"See Post Command below.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--post-function",
|
||||||
|
metavar="filename.py::function",
|
||||||
|
nargs=1,
|
||||||
|
type=FunctionCall(),
|
||||||
|
multiple=True,
|
||||||
|
help="Run function on exported files. Use this in format: --post-function filename.py::function where filename.py is a python "
|
||||||
|
"file you've created and function is the name of the function in the python file you want to call. The function will be "
|
||||||
|
"passed information about the photo that's been exported and a list of all exported files associated with the photo. "
|
||||||
|
"You can run more than one function by repeating the '--post-function' option with different arguments. "
|
||||||
|
"See Post Function below.",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--exportdb",
|
"--exportdb",
|
||||||
metavar="EXPORTDB_FILE",
|
metavar="EXPORTDB_FILE",
|
||||||
@@ -1096,6 +1131,7 @@ def export(
|
|||||||
query_eval,
|
query_eval,
|
||||||
duplicate,
|
duplicate,
|
||||||
post_command,
|
post_command,
|
||||||
|
post_function,
|
||||||
):
|
):
|
||||||
"""Export photos from the Photos database.
|
"""Export photos from the Photos database.
|
||||||
Export path DEST is required.
|
Export path DEST is required.
|
||||||
@@ -1252,6 +1288,7 @@ def export(
|
|||||||
query_eval = cfg.query_eval
|
query_eval = cfg.query_eval
|
||||||
duplicate = cfg.duplicate
|
duplicate = cfg.duplicate
|
||||||
post_command = cfg.post_command
|
post_command = cfg.post_command
|
||||||
|
post_function = cfg.post_function
|
||||||
|
|
||||||
# config file might have changed verbose
|
# config file might have changed verbose
|
||||||
VERBOSE = bool(verbose)
|
VERBOSE = bool(verbose)
|
||||||
@@ -1655,6 +1692,20 @@ def export(
|
|||||||
export_dir=dest,
|
export_dir=dest,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if post_function:
|
||||||
|
for function in post_function:
|
||||||
|
# post function is tuple of (function, filename.py::function_name)
|
||||||
|
verbose_(f"Calling post-function {function[1]}")
|
||||||
|
if not dry_run:
|
||||||
|
try:
|
||||||
|
function[0](p, export_results, verbose_)
|
||||||
|
except Exception as e:
|
||||||
|
click.secho(
|
||||||
|
f"Error running post-function {function[1]}: {e}",
|
||||||
|
fg=CLI_COLOR_ERROR,
|
||||||
|
err=True,
|
||||||
|
)
|
||||||
|
|
||||||
run_post_command(
|
run_post_command(
|
||||||
photo=p,
|
photo=p,
|
||||||
post_command=post_command,
|
post_command=post_command,
|
||||||
|
|||||||
@@ -242,6 +242,19 @@ The following attributes may be used with '--xattr-template':
|
|||||||
+ "print out the exact command string which would be executed."
|
+ "print out the exact command string which would be executed."
|
||||||
)
|
)
|
||||||
formatter.write("\n\n")
|
formatter.write("\n\n")
|
||||||
|
formatter.write(
|
||||||
|
rich_text("[bold]** Post Function **[/bold]", width=formatter.width)
|
||||||
|
)
|
||||||
|
formatter.write_text(
|
||||||
|
"You can run your own python functions on the exported photos for post-processing "
|
||||||
|
+ "using the '--post-function' option. '--post-function' is passed the name a python file "
|
||||||
|
+ "and the name of the function in the file to call using format 'filename.py::function_name'. "
|
||||||
|
+ "See the example function at https://github.com/RhetTbull/osxphotos/blob/master/examples/post_function.py "
|
||||||
|
+ "You may specify multiple functions to run by repeating the --post-function option. "
|
||||||
|
+ "All post functions will be called immediately after export of each photo and immediately before any --post-command commands. "
|
||||||
|
+ "Post functions will not be called if the --dry-run flag is set."
|
||||||
|
)
|
||||||
|
formatter.write("\n")
|
||||||
|
|
||||||
help_text += formatter.getvalue()
|
help_text += formatter.getvalue()
|
||||||
return help_text
|
return help_text
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user