parent
6bf24ad2de
commit
ce5145ff85
28
examples/post_function_import.py
Normal file
28
examples/post_function_import.py
Normal file
@ -0,0 +1,28 @@
|
||||
""" Example function for use with osxphotos import --post-function option """
|
||||
|
||||
import typing as t
|
||||
import photoscript
|
||||
import pathlib
|
||||
|
||||
def post_function(
|
||||
photo: photoscript.Photo, filepath: pathlib.Path, verbose: t.Callable, **kwargs
|
||||
):
|
||||
"""Call this with osxphotos import /file/to/import --post-function post_function.py::post_function
|
||||
This will get called immediately after the photo has been imported into Photos
|
||||
and all metadata been set (e.g. --exiftool, --title, etc.)
|
||||
|
||||
Args:
|
||||
photo: photoscript.Photo instance for the photo that's just been imported
|
||||
filepath: pathlib.Path to the file that was imported (this is the path to the source file, not the path inside the Photos library)
|
||||
verbose: A function to print verbose output if --verbose is set; if --verbose is not set, acts as a no-op (nothing gets printed)
|
||||
**kwargs: reserved for future use; recommend you include **kwargs so your function still works if additional arguments are added in future versions
|
||||
|
||||
Notes:
|
||||
Use verbose(str) instead of print if you want your function to conditionally output text depending on --verbose flag
|
||||
Any string printed with verbose that contains "warning" or "error" (case-insensitive) will be printed with the appropriate warning or error color
|
||||
See https://rhettbull.github.io/PhotoScript/ for documentation on photoscript
|
||||
"""
|
||||
|
||||
# add a note to the photo's description
|
||||
verbose("Adding note to description")
|
||||
photo.description = f"{photo.description} (imported with osxphotos)"
|
||||
@ -32,7 +32,7 @@ from osxphotos._constants import _OSXPHOTOS_NONE_SENTINEL
|
||||
from osxphotos._version import __version__
|
||||
from osxphotos.cli.common import get_data_dir
|
||||
from osxphotos.cli.help import HELP_WIDTH
|
||||
from osxphotos.cli.param_types import StrpDateTimePattern, TemplateString
|
||||
from osxphotos.cli.param_types import FunctionCall, StrpDateTimePattern, TemplateString
|
||||
from osxphotos.datetime_utils import (
|
||||
datetime_has_tz,
|
||||
datetime_naive_to_local,
|
||||
@ -49,6 +49,7 @@ from osxphotos.utils import pluralize
|
||||
|
||||
from .click_rich_echo import (
|
||||
rich_click_echo,
|
||||
rich_echo_error,
|
||||
set_rich_console,
|
||||
set_rich_theme,
|
||||
set_rich_timestamp,
|
||||
@ -1128,6 +1129,27 @@ class ImportCommand(click.Command):
|
||||
patterns. The order is important as the first pattern will be tried first then the second
|
||||
and so on. If you have multiple formats in your filenames you will want to order the patterns
|
||||
from most specific to least specific to avoid false matches.
|
||||
|
||||
## Post Function
|
||||
|
||||
You can run a custom python function after each photo is imported using `--post-function`.
|
||||
The format is `osxphotos import /file/to/import --post-function post_function.py::post_function`
|
||||
where `post_function.py` is the name of the python file containing the function and `post_function`
|
||||
is the name of the function. The function will be called with the following arguments:
|
||||
`post_function(photo: photoscript.Photo, filepath: pathlib.Path, verbose: t.Callable, **kwargs)`
|
||||
|
||||
- photo: photoscript.Photo instance for the photo that's just been imported
|
||||
- filepath: pathlib.Path to the file that was imported (this is the path to the source file, not the path inside the Photos library)
|
||||
- verbose: A function to print verbose output if --verbose is set; if --verbose is not set, acts as a no-op (nothing gets printed)
|
||||
- **kwargs: reserved for future use; recommend you include **kwargs so your function still works if additional arguments are added in future versions
|
||||
|
||||
The function will get called immediately after the photo has been imported into Photos
|
||||
and all metadata been set (e.g. --exiftool, --title, etc.)
|
||||
|
||||
You may call more than one function by repeating the `--post-function` option.
|
||||
|
||||
See https://rhettbull.github.io/PhotoScript/
|
||||
for documentation on photoscript and the Photo class that is passed to the function.
|
||||
"""
|
||||
)
|
||||
console = Console()
|
||||
@ -1320,6 +1342,19 @@ class ImportCommand(click.Command):
|
||||
help="Don't actually import anything; "
|
||||
"renders template strings and date patterns so you can verify they are correct.",
|
||||
)
|
||||
@click.option(
|
||||
"--post-function",
|
||||
metavar="filename.py::function",
|
||||
nargs=1,
|
||||
type=FunctionCall(),
|
||||
multiple=True,
|
||||
help="Run python function after importing file."
|
||||
"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 a reference to the photo object and the path to the file that was imported. "
|
||||
"You can run more than one function by repeating the '--post-function' option with different arguments. "
|
||||
"See Post Function below.",
|
||||
)
|
||||
@THEME_OPTION
|
||||
@click.argument("files", nargs=-1)
|
||||
@click.pass_obj
|
||||
@ -1343,6 +1378,7 @@ def import_cli(
|
||||
merge_keywords,
|
||||
no_progress,
|
||||
parse_date,
|
||||
post_function,
|
||||
relative_to,
|
||||
report,
|
||||
resume,
|
||||
@ -1498,6 +1534,17 @@ def import_cli(
|
||||
verbose,
|
||||
)
|
||||
|
||||
if post_function:
|
||||
for function in post_function:
|
||||
# post function is tuple of (function, filename.py::function_name)
|
||||
verbose(f"Calling post-function [bold]{function[1]}")
|
||||
try:
|
||||
function[0](photo, filepath, verbose)
|
||||
except Exception as e:
|
||||
rich_echo_error(
|
||||
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
|
||||
)
|
||||
|
||||
update_report_record(report_data[filepath], photo, filepath)
|
||||
import_db.set(str(filepath), report_data[filepath])
|
||||
|
||||
|
||||
@ -995,3 +995,36 @@ def test_import_parse_date(tmp_path: pathlib.Path):
|
||||
for test_case in test_data:
|
||||
photo = photosdb.query(QueryOptions(name=[test_case[0]]))[0]
|
||||
assert datetime_remove_tz(photo.date) == test_case[1]
|
||||
|
||||
|
||||
@pytest.mark.test_import
|
||||
def test_import_post_function():
|
||||
"""Test import with --post-function"""
|
||||
|
||||
cwd = os.getcwd()
|
||||
test_image_1 = os.path.join(cwd, TEST_IMAGE_1)
|
||||
|
||||
runner = CliRunner()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
with open("foo1.py", "w") as f:
|
||||
f.writelines(
|
||||
[
|
||||
"def foo(photo, filepath, verbose, **kwargs):\n",
|
||||
" verbose('FOO BAR')\n",
|
||||
]
|
||||
)
|
||||
|
||||
tempdir = os.getcwd()
|
||||
result = runner.invoke(
|
||||
import_cli,
|
||||
[
|
||||
"import",
|
||||
"--verbose",
|
||||
test_image_1,
|
||||
"--post-function",
|
||||
f"{tempdir}/foo1.py::foo",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "FOO BAR" in result.output
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user