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._version import __version__
|
||||||
from osxphotos.cli.common import get_data_dir
|
from osxphotos.cli.common import get_data_dir
|
||||||
from osxphotos.cli.help import HELP_WIDTH
|
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 (
|
from osxphotos.datetime_utils import (
|
||||||
datetime_has_tz,
|
datetime_has_tz,
|
||||||
datetime_naive_to_local,
|
datetime_naive_to_local,
|
||||||
@ -49,6 +49,7 @@ from osxphotos.utils import pluralize
|
|||||||
|
|
||||||
from .click_rich_echo import (
|
from .click_rich_echo import (
|
||||||
rich_click_echo,
|
rich_click_echo,
|
||||||
|
rich_echo_error,
|
||||||
set_rich_console,
|
set_rich_console,
|
||||||
set_rich_theme,
|
set_rich_theme,
|
||||||
set_rich_timestamp,
|
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
|
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
|
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.
|
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()
|
console = Console()
|
||||||
@ -1320,6 +1342,19 @@ class ImportCommand(click.Command):
|
|||||||
help="Don't actually import anything; "
|
help="Don't actually import anything; "
|
||||||
"renders template strings and date patterns so you can verify they are correct.",
|
"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
|
@THEME_OPTION
|
||||||
@click.argument("files", nargs=-1)
|
@click.argument("files", nargs=-1)
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
@ -1343,6 +1378,7 @@ def import_cli(
|
|||||||
merge_keywords,
|
merge_keywords,
|
||||||
no_progress,
|
no_progress,
|
||||||
parse_date,
|
parse_date,
|
||||||
|
post_function,
|
||||||
relative_to,
|
relative_to,
|
||||||
report,
|
report,
|
||||||
resume,
|
resume,
|
||||||
@ -1498,6 +1534,17 @@ def import_cli(
|
|||||||
verbose,
|
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)
|
update_report_record(report_data[filepath], photo, filepath)
|
||||||
import_db.set(str(filepath), report_data[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:
|
for test_case in test_data:
|
||||||
photo = photosdb.query(QueryOptions(name=[test_case[0]]))[0]
|
photo = photosdb.query(QueryOptions(name=[test_case[0]]))[0]
|
||||||
assert datetime_remove_tz(photo.date) == test_case[1]
|
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