Version 0.50.0 with updated template engine

This commit is contained in:
Rhet Turnbull
2022-05-28 08:12:16 -07:00
parent 0a973d67f9
commit 175d7ea223
27 changed files with 700 additions and 245 deletions

View File

@@ -1727,7 +1727,7 @@ In its simplest form, a template statement has the form: `"{template_field}"`, f
Template statements may contain one or more modifiers. The full syntax is:
`"pretext{delim+template_field:subfield|filter(path_sep)[find,replace] conditional?bool_value,default}posttext"`
`"pretext{delim+template_field:subfield(field_arg)|filter[find,replace] conditional?bool_value,default}posttext"`
Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.
@@ -1748,6 +1748,8 @@ e.g. if Photo keywords are `["foo","bar"]`:
`:subfield`: Some templates have sub-fields, For example, `{exiftool:IPTC:Make}`; the template_field is `exiftool` and the sub-field is `IPTC:Make`.
`(field_arg)`: optional arguments to pass to the field; for example, with `{folder_album}` this is used to pass the path separator used for joining folders and albums when rendering the field (default is "/" for `{folder_album}`).
`|filter`: You may optionally append one or more filter commands to the end of the template field using the vertical pipe ('|') symbol. Filters may be combined, separated by '|' as in: `{keyword|capitalize|parens}`.
Valid filters are:
@@ -1762,6 +1764,18 @@ Valid filters are:
- `brackets`: Enclose value in brackets, e.g. 'value' => '[value]'
- `shell_quote`: Quotes the value for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
- `function`: Run custom python function to filter value; use in format 'function:/path/to/file.py::function_name'. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py
- `split(x)`: Split value into a list of values using x as delimiter, e.g. 'value1;value2' => ['value1', 'value2'] if used with split(;).
- `autosplit`: Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. 'value1,value2' => ['value1', 'value2'].
- `chop(x)`: Remove x characters off the end of value, e.g. chop(1): 'Value' => 'Valu'; when applied to a list, chops characters from each list value, e.g. chop(1): ['travel', 'beach']=> ['trave', 'beac'].
- `chomp(x)`: Remove x characters from the beginning of value, e.g. chomp(1): ['Value'] => ['alue']; when applied to a list, removes characters from each list value, e.g. chomp(1): ['travel', 'beach']=> ['ravel', 'each'].
- `sort`: Sort list of values, e.g. ['c', 'b', 'a'] => ['a', 'b', 'c'].
- `rsort`: Sort list of values in reverse order, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
- `reverse`: Reverse order of values, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
- `uniq`: Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] => ['a', 'b', 'c'].
- `join(x)`: Join list of values with delimiter x, e.g. join(:): ['a', 'b', 'c'] => 'a:b:c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.
- `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
- `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
e.g. if Photo keywords are `["FOO","bar"]`:
@@ -1774,8 +1788,6 @@ e.g. if Photo description is "my description":
- `"{descr|titlecase}"` renders to: `"My Description"`
`(path_sep)`: optional path separator to use when joining path-like fields, for example `{folder_album}`. Default is "/".
e.g. If Photo is in `Album1` in `Folder1`:
- `"{folder_album}"` renders to `["Folder1/Album1"]`
@@ -1818,7 +1830,7 @@ This can be used to rename files as well, for example:
This renames any photo that is a favorite as 'Favorite-ImageName.jpg' (where 'ImageName.jpg' is the original name of the photo) and all other photos with the unmodified original name.
`?bool_value`: Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(path_sep)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is `"{hdr}"`) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is `"{title}"`) then the default value following a "," will be used.
`?bool_value`: Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(field_arg)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is `"{hdr}"`) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is `"{title}"`) then the default value following a "," will be used.
e.g. if photo is an HDR image,
@@ -1848,6 +1860,16 @@ Either or both bool_value or default (False value) may be empty which would resu
If you want to include "{" or "}" in the output, use "{openbrace}" or "{closebrace}" template substitution.
e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{Photo Title}"`.
**Variables**
You can define variables for later use in the template string using the format `{var:NAME,VALUE}`. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
Variables can also be referenced as fields in the template string, for example: `{var:year,created.year}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
If you need to use a `%` (percent sign character), you can escape the percent sign by using `%%`. You can also use the `{percent}` template field where a template field is required. For example:
`{title[:,%%]}` replaces the `:` with `%` and `{title contains Foo?{title}{percent},{title}}` adds `%` to the title if it contains `Foo`.
<!--[[[end]]] -->
The following template field substitutions are availabe for use the templating system.
@@ -1927,8 +1949,8 @@ cog.out(get_template_field_table())
|{moment}|The moment title of the photo|
|{uuid}|Photo's internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. '128FB4C6-0B16-4E7D-9108-FB2E90DA1546'|
|{id}|A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3...etc. Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{id:05d}' which results in 00001, 00002, 00003...etc. |
|{album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album_seq}_{original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{album_seq.1}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.|
|{folder_album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album_seq}_{original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq.1}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'.|
|{album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album_seq}_{original_name}"'. To start counting at a value other than 0, append append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{album_seq(1)}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.|
|{folder_album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album_seq}_{original_name}"'. To start counting at a value other than 0, append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq(1)}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{folder_album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'. |
|{comma}|A comma: ','|
|{semicolon}|A semicolon: ';'|
|{questionmark}|A question mark: '?'|
@@ -1943,7 +1965,7 @@ cog.out(get_template_field_table())
|{lf}|A line feed: '\n', alias for {newline}|
|{cr}|A carriage return: '\r'|
|{crlf}|a carriage return + line feed: '\r\n'|
|{osxphotos_version}|The osxphotos version, e.g. '0.49.9'|
|{osxphotos_version}|The osxphotos version, e.g. '0.50.0'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{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|
@@ -1964,6 +1986,7 @@ cog.out(get_template_field_table())
|{detected_text}|List of text strings found in the image after performing text detection. Using '{detected_text}' will cause osxphotos to perform text detection on your photos using the built-in macOS text detection algorithms which will slow down your export. The results for each photo will be cached in the export database so that future exports with '--update' do not need to reprocess each photo. You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in '{detected_text:0.5}'; The default confidence threshold is 0.75. '{detected_text}' works only on macOS Catalina (10.15) or later. Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.|
|{shell_quote}|Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.|
|{strip}|Use in form '{strip,TEMPLATE}'; strips whitespace from begining and end of rendered TEMPLATE value(s).|
|{format}|Use in form, '{format:TYPE:FORMAT,TEMPLATE}'; converts TEMPLATE value to TYPE then formats the value using Python string formatting codes specified by FORMAT; TYPE is one of: 'int', 'float', or 'str'. For example, '{format:float:.1f,{exiftool:EXIF:FocalLength}}' will format focal length to 1 decimal place (e.g. '100.0'). |
|{function}|Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.|
<!--[[[end]]] -->

167
README.md
View File

@@ -402,6 +402,14 @@ A powerful feature of Photos is that it uses machine learning algorithms to auto
`osxphotos export /path/to/export --exiftool --keyword-template "{label}"`
#### Removing a keyword during export
If some of your photos contain a keyword you do not want to be added to the exported file with `--exiftool`, you can use the template system to remove the keyword from the exported file. For example, if you want to remove the keyword "MyKeyword" from all your photos:
`osxphotos export /path/to/export --exiftool --keyword-template "{keyword|remove(MyKeyword)}" --replace-keywords`
In this example, `|remove(MyKeyword)` is a filter which removes `MyKeyword` from the keyword list of every photo being processed. The `--replace-keywords` option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from `--keyword-template`.
**Note**: When evaluating templates for `--directory` and `--filename`, osxphotos inserts the automatic default value "_" for any template field which is null (empty or blank). This is to ensure that there's never a null directory or filename created. For metadata templates such as `--keyword-template`, osxphotos does not provide an automatic default value thus if the template field is null, no keyword would be created. Of course, you can provide a default value if desired and osxphotos will use this. For example, to add "nolabel" as a keyword for any photo that doesn't have labels:
`osxphotos export /path/to/export --exiftool --keyword-template "{label,nolabel}"`
@@ -1405,7 +1413,7 @@ for example "{title}" which would resolve to the title of the photo.
Template statements may contain one or more modifiers. The full syntax is:
"pretext{delim+template_field:subfield|filter(path_sep)[find,replace]
"pretext{delim+template_field:subfield(field_arg)|filter[find,replace]
conditional?bool_value,default}posttext"
Template statements are white-space sensitive meaning that white space
@@ -1435,6 +1443,11 @@ full list of template fields.
:subfield: Some templates have sub-fields, For example, {exiftool:IPTC:Make};
the template_field is exiftool and the sub-field is IPTC:Make.
(field_arg): optional arguments to pass to the field; for example, with
{folder_album} this is used to pass the path separator used for joining
folders and albums when rendering the field (default is "/" for
{folder_album}).
|filter: You may optionally append one or more filter commands to the end of
the template field using the vertical pipe ('|') symbol. Filters may be
combined, separated by '|' as in: {keyword|capitalize|parens}.
@@ -1456,6 +1469,32 @@ Valid filters are:
• function: Run custom python function to filter value; use in format
'function:/path/to/file.py::function_name'. See example at https://github.c
om/RhetTbull/osxphotos/blob/master/examples/template_filter.py
• split(x): Split value into a list of values using x as delimiter, e.g.
'value1;value2' => ['value1', 'value2'] if used with split(;).
• autosplit: Automatically split delimited string into separate values; will
split strings delimited by comma, semicolon, or space, e.g. 'value1,value2'
=> ['value1', 'value2'].
• chop(x): Remove x characters off the end of value, e.g. chop(1): 'Value' =>
'Valu'; when applied to a list, chops characters from each list value, e.g.
chop(1): ['travel', 'beach']=> ['trave', 'beac'].
• chomp(x): Remove x characters from the beginning of value, e.g. chomp(1):
['Value'] => ['alue']; when applied to a list, removes characters from each
list value, e.g. chomp(1): ['travel', 'beach']=> ['ravel', 'each'].
• sort: Sort list of values, e.g. ['c', 'b', 'a'] => ['a', 'b', 'c'].
• rsort: Sort list of values in reverse order, e.g. ['a', 'b', 'c'] => ['c',
'b', 'a'].
• reverse: Reverse order of values, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
• uniq: Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] => ['a', 'b',
'c'].
• join(x): Join list of values with delimiter x, e.g. join(:): ['a', 'b',
'c'] => 'a:b:c'; the DELIM option functions similar to join(x) but with
DELIM, the join happens before being passed to any filters.
• append(x): Append x to list of values, e.g. append(d): ['a', 'b', 'c'] =>
['a', 'b', 'c', 'd'].
• prepend(x): Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c']
=> ['d', 'a', 'b', 'c'].
• remove(x): Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] =>
['a', 'c'].
e.g. if Photo keywords are ["FOO","bar"]:
@@ -1468,9 +1507,6 @@ e.g. if Photo description is "my description":
• "{descr|titlecase}" renders to: "My Description"
(path_sep): optional path separator to use when joining path-like fields, for
example {folder_album}. Default is "/".
e.g. If Photo is in Album1 in Folder1:
• "{folder_album}" renders to ["Folder1/Album1"]
@@ -1540,7 +1576,7 @@ This renames any photo that is a favorite as 'Favorite-ImageName.jpg' (where
the unmodified original name.
?bool_value: Template fields may be evaluated as boolean (True/False) by
appending "?" after the field name (and following "(path_sep)" or
appending "?" after the field name (and following "(field_arg)" or
"[find/replace]". If a field is True (e.g. photo is HDR and field is "{hdr}")
or has any value, the value following the "?" will be used to render the
template instead of the actual field value. If the template field evaluates
@@ -1590,6 +1626,34 @@ If you want to include "{" or "}" in the output, use "{openbrace}" or
e.g. "{created.year}/{openbrace}{title}{closebrace}" would result in
"2020/{Photo Title}".
Variables
You can define variables for later use in the template string using the format
{var:NAME,VALUE}. Variables may then be referenced using the format %NAME.
For example: {var:foo,bar} defines the variable %foo to have value bar. This
can be useful if you want to re-use a complex template value in multiple
places within your template string or for allowing the use of characters that
would otherwise be prohibited in a template string. For example, the "pipe"
(|) character is not allowed in a find/replace pair but you can get around
this limitation like so: {var:pipe,{pipe}}{title[-,%pipe]} which replaces the
- character with | (the value of %pipe).
Variables can also be referenced as fields in the template string, for
example: {var:year,created.year}{original_name}-{%year}. In some cases, use of
variables can make your template string more readable. Variables can be used
as template fields, as values for filters, as values for conditional
operations, or as default values. When used as a conditional value or default
value, variables should be treated like any other field and enclosed in braces
as conditional and default values are evaluated as template strings. For
example: {var:name,Katie}{person contains {%name}?{%name},Not-{%name}}.
If you need to use a % (percent sign character), you can escape the percent
sign by using %%. You can also use the {percent} template field where a
template field is required. For example:
{title[:,%%]} replaces the : with % and {title contains
Foo?{title}{percent},{title}} adds % to the title if it contains Foo.
With the --directory and --filename options you may specify a template for the
export directory or filename, respectively. The directory will be appended to
the export path specified in the export DEST argument to export. For example,
@@ -1805,16 +1869,18 @@ Substitution Description
directory "{folder_album}" --filename
"{album_seq}_{original_name}"'. To start
counting at a value other than 0, append
append a period and the starting value to
the field name. For example, to start
counting at 1 instead of 0: '{album_seq.1}'.
May be formatted using a python string
format code. For example, to format as a
5-digit integer and pad with zeros, use
'{album_seq:05d}' which results in 00000,
00001, 00002...etc. This may result in
incorrect sequences if you have duplicate
albums with the same name; see also
append '(starting_value)' to the field name.
For example, to start counting at 1 instead
of 0: '{album_seq(1)}'. May be formatted
using a python string format code. For
example, to format as a 5-digit integer and
pad with zeros, use '{album_seq:05d}' which
results in 00000, 00001, 00002...etc. To
format while also using a starting value:
'{album_seq:05d(1)}' which results in 0001,
00002...etc.This may result in incorrect
sequences if you have duplicate albums with
the same name; see also
'{folder_album_seq}'.
{folder_album_seq} An integer, starting at 0, indicating the
photo's index (sequence) in the containing
@@ -1825,17 +1891,20 @@ Substitution Description
directory "{folder_album}" --filename
"{folder_album_seq}_{original_name}"'. To
start counting at a value other than 0,
append append a period and the starting
value to the field name. For example, to
start counting at 1 instead of 0:
'{folder_album_seq.1}' May be formatted
using a python string format code. For
example, to format as a 5-digit integer and
pad with zeros, use '{folder_album_seq:05d}'
which results in 00000, 00001, 00002...etc.
This may result in incorrect sequences if
you have duplicate albums with the same name
in the same folder; see also '{album_seq}'.
append '(starting_value)' to the field name.
For example, to start counting at 1 instead
of 0: '{folder_album_seq(1)}' May be
formatted using a python string format code.
For example, to format as a 5-digit integer
and pad with zeros, use
'{folder_album_seq:05d}' which results in
00000, 00001, 00002...etc. To format while
also using a starting value:
'{folder_album_seq:05d(1)}' which results in
0001, 00002...etc.This may result in
incorrect sequences if you have duplicate
albums with the same name in the same
folder; see also '{album_seq}'.
{comma} A comma: ','
{semicolon} A semicolon: ';'
{questionmark} A question mark: '?'
@@ -1850,7 +1919,7 @@ Substitution Description
{lf} A line feed: '\n', alias for {newline}
{cr} A carriage return: '\r'
{crlf} a carriage return + line feed: '\r\n'
{osxphotos_version} The osxphotos version, e.g. '0.49.9'
{osxphotos_version} The osxphotos version, e.g. '0.50.0'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified
@@ -1941,6 +2010,14 @@ Substitution Description
{strip} Use in form '{strip,TEMPLATE}'; strips whitespace
from begining and end of rendered TEMPLATE
value(s).
{format} Use in form, '{format:TYPE:FORMAT,TEMPLATE}';
converts TEMPLATE value to TYPE then formats the
value using Python string formatting codes
specified by FORMAT; TYPE is one of: 'int',
'float', or 'str'. For example,
'{format:float:.1f,{exiftool:EXIF:FocalLength}}'
will format focal length to 1 decimal place (e.g.
'100.0').
{function} Execute a python function from an external file and
use return value as template substitution. Use in
format: {function:file.py::function_name} where
@@ -3812,7 +3889,7 @@ In its simplest form, a template statement has the form: `"{template_field}"`, f
Template statements may contain one or more modifiers. The full syntax is:
`"pretext{delim+template_field:subfield|filter(path_sep)[find,replace] conditional?bool_value,default}posttext"`
`"pretext{delim+template_field:subfield(field_arg)|filter[find,replace] conditional?bool_value,default}posttext"`
Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.
@@ -3833,6 +3910,8 @@ e.g. if Photo keywords are `["foo","bar"]`:
`:subfield`: Some templates have sub-fields, For example, `{exiftool:IPTC:Make}`; the template_field is `exiftool` and the sub-field is `IPTC:Make`.
`(field_arg)`: optional arguments to pass to the field; for example, with `{folder_album}` this is used to pass the path separator used for joining folders and albums when rendering the field (default is "/" for `{folder_album}`).
`|filter`: You may optionally append one or more filter commands to the end of the template field using the vertical pipe ('|') symbol. Filters may be combined, separated by '|' as in: `{keyword|capitalize|parens}`.
Valid filters are:
@@ -3847,6 +3926,18 @@ Valid filters are:
- `brackets`: Enclose value in brackets, e.g. 'value' => '[value]'
- `shell_quote`: Quotes the value for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
- `function`: Run custom python function to filter value; use in format 'function:/path/to/file.py::function_name'. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py
- `split(x)`: Split value into a list of values using x as delimiter, e.g. 'value1;value2' => ['value1', 'value2'] if used with split(;).
- `autosplit`: Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. 'value1,value2' => ['value1', 'value2'].
- `chop(x)`: Remove x characters off the end of value, e.g. chop(1): 'Value' => 'Valu'; when applied to a list, chops characters from each list value, e.g. chop(1): ['travel', 'beach']=> ['trave', 'beac'].
- `chomp(x)`: Remove x characters from the beginning of value, e.g. chomp(1): ['Value'] => ['alue']; when applied to a list, removes characters from each list value, e.g. chomp(1): ['travel', 'beach']=> ['ravel', 'each'].
- `sort`: Sort list of values, e.g. ['c', 'b', 'a'] => ['a', 'b', 'c'].
- `rsort`: Sort list of values in reverse order, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
- `reverse`: Reverse order of values, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
- `uniq`: Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] => ['a', 'b', 'c'].
- `join(x)`: Join list of values with delimiter x, e.g. join(:): ['a', 'b', 'c'] => 'a:b:c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.
- `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
- `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
e.g. if Photo keywords are `["FOO","bar"]`:
@@ -3859,8 +3950,6 @@ e.g. if Photo description is "my description":
- `"{descr|titlecase}"` renders to: `"My Description"`
`(path_sep)`: optional path separator to use when joining path-like fields, for example `{folder_album}`. Default is "/".
e.g. If Photo is in `Album1` in `Folder1`:
- `"{folder_album}"` renders to `["Folder1/Album1"]`
@@ -3903,7 +3992,7 @@ This can be used to rename files as well, for example:
This renames any photo that is a favorite as 'Favorite-ImageName.jpg' (where 'ImageName.jpg' is the original name of the photo) and all other photos with the unmodified original name.
`?bool_value`: Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(path_sep)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is `"{hdr}"`) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is `"{title}"`) then the default value following a "," will be used.
`?bool_value`: Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(field_arg)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is `"{hdr}"`) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is `"{title}"`) then the default value following a "," will be used.
e.g. if photo is an HDR image,
@@ -3934,6 +4023,15 @@ If you want to include "{" or "}" in the output, use "{openbrace}" or "{closebra
e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{Photo Title}"`.
**Variables**
You can define variables for later use in the template string using the format `{var:NAME,VALUE}`. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
Variables can also be referenced as fields in the template string, for example: `{var:year,created.year}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
If you need to use a `%` (percent sign character), you can escape the percent sign by using `%%`. You can also use the `{percent}` template field where a template field is required. For example:
`{title[:,%%]}` replaces the `:` with `%` and `{title contains Foo?{title}{percent},{title}}` adds `%` to the title if it contains `Foo`.
<!-- OSXPHOTOS-TEMPLATE-HELP:END -->
The following template field substitutions are availabe for use the templating system.
@@ -4010,8 +4108,8 @@ The following template field substitutions are availabe for use the templating s
|{moment}|The moment title of the photo|
|{uuid}|Photo's internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. '128FB4C6-0B16-4E7D-9108-FB2E90DA1546'|
|{id}|A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3...etc. Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{id:05d}' which results in 00001, 00002, 00003...etc. |
|{album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album_seq}_{original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{album_seq.1}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.|
|{folder_album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album_seq}_{original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq.1}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'.|
|{album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album_seq}_{original_name}"'. To start counting at a value other than 0, append append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{album_seq(1)}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.|
|{folder_album_seq}|An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album_seq}_{original_name}"'. To start counting at a value other than 0, append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq(1)}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{folder_album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'. |
|{comma}|A comma: ','|
|{semicolon}|A semicolon: ';'|
|{questionmark}|A question mark: '?'|
@@ -4026,7 +4124,7 @@ The following template field substitutions are availabe for use the templating s
|{lf}|A line feed: '\n', alias for {newline}|
|{cr}|A carriage return: '\r'|
|{crlf}|a carriage return + line feed: '\r\n'|
|{osxphotos_version}|The osxphotos version, e.g. '0.49.9'|
|{osxphotos_version}|The osxphotos version, e.g. '0.50.0'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{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|
@@ -4047,6 +4145,7 @@ The following template field substitutions are availabe for use the templating s
|{detected_text}|List of text strings found in the image after performing text detection. Using '{detected_text}' will cause osxphotos to perform text detection on your photos using the built-in macOS text detection algorithms which will slow down your export. The results for each photo will be cached in the export database so that future exports with '--update' do not need to reprocess each photo. You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in '{detected_text:0.5}'; The default confidence threshold is 0.75. '{detected_text}' works only on macOS Catalina (10.15) or later. Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.|
|{shell_quote}|Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.|
|{strip}|Use in form '{strip,TEMPLATE}'; strips whitespace from begining and end of rendered TEMPLATE value(s).|
|{format}|Use in form, '{format:TYPE:FORMAT,TEMPLATE}'; converts TEMPLATE value to TYPE then formats the value using Python string formatting codes specified by FORMAT; TYPE is one of: 'int', 'float', or 'str'. For example, '{format:float:.1f,{exiftool:EXIF:FocalLength}}' will format focal length to 1 decimal place (e.g. '100.0'). |
|{function}|Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.|
<!-- OSXPHOTOS-TEMPLATE-TABLE:END -->

View File

@@ -1,4 +1,4 @@
# 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.
config: 840166640a0eca4a34f53df341c84489
config: 2319c5f6348daed917bcf14a84e56927
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>Overview: module code - osxphotos 0.49.9 documentation</title>
<title>Overview: module code - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="../index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photoinfo - osxphotos 0.49.9 documentation</title>
<title>osxphotos.photoinfo - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosdb.photosdb - osxphotos 0.49.9 documentation</title>
<title>osxphotos.photosdb.photosdb - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.phototemplate - osxphotos 0.49.9 documentation</title>
<title>osxphotos.phototemplate - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -201,11 +201,12 @@
<span class="kn">import</span> <span class="nn">locale</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">shlex</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">suppress</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span>
<span class="kn">from</span> <span class="nn">textx</span> <span class="kn">import</span> <span class="n">TextXSyntaxError</span><span class="p">,</span> <span class="n">metamodel_from_file</span>
@@ -344,21 +345,23 @@
<span class="s2">"</span><span class="si">{album_seq}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"An integer, starting at 0, indicating the photo's index (sequence) in the containing album. "</span>
<span class="o">+</span> <span class="s2">"Only valid when used in a '--filename' template and only when '</span><span class="si">{album}</span><span class="s2">' or '</span><span class="si">{folder_album}</span><span class="s2">' is used in the '--directory' template. "</span>
<span class="o">+</span> <span class="s1">'For example </span><span class="se">\'</span><span class="s1">--directory "</span><span class="si">{folder_album}</span><span class="s1">" --filename "</span><span class="si">{album_seq}</span><span class="s1">_</span><span class="si">{original_name}</span><span class="s1">"</span><span class="se">\'</span><span class="s1">. '</span>
<span class="o">+</span> <span class="s2">"To start counting at a value other than 0, append append a period and the starting value to the field name. "</span>
<span class="o">+</span> <span class="s2">"For example, to start counting at 1 instead of 0: '</span><span class="si">{album_seq.1}</span><span class="s2">'. "</span>
<span class="o">+</span> <span class="s2">"To start counting at a value other than 0, append append '(starting_value)' to the field name. "</span>
<span class="o">+</span> <span class="s2">"For example, to start counting at 1 instead of 0: '{album_seq(1)}'. "</span>
<span class="o">+</span> <span class="s2">"May be formatted using a python string format code. "</span>
<span class="o">+</span> <span class="s2">"For example, to format as a 5-digit integer and pad with zeros, use '</span><span class="si">{album_seq:05d}</span><span class="s2">' which results in "</span>
<span class="o">+</span> <span class="s2">"00000, 00001, 00002...etc. "</span>
<span class="o">+</span> <span class="s2">"To format while also using a starting value: '{album_seq:05d(1)}' which results in 0001, 00002...etc."</span>
<span class="o">+</span> <span class="s2">"This may result in incorrect sequences if you have duplicate albums with the same name; see also '</span><span class="si">{folder_album_seq}</span><span class="s2">'."</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{folder_album_seq}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. "</span>
<span class="o">+</span> <span class="s2">"Only valid when used in a '--filename' template and only when '</span><span class="si">{folder_album}</span><span class="s2">' is used in the '--directory' template. "</span>
<span class="o">+</span> <span class="s1">'For example </span><span class="se">\'</span><span class="s1">--directory "</span><span class="si">{folder_album}</span><span class="s1">" --filename "</span><span class="si">{folder_album_seq}</span><span class="s1">_</span><span class="si">{original_name}</span><span class="s1">"</span><span class="se">\'</span><span class="s1">. '</span>
<span class="o">+</span> <span class="s2">"To start counting at a value other than 0, append append a period and the starting value to the field name. "</span>
<span class="o">+</span> <span class="s2">"For example, to start counting at 1 instead of 0: '</span><span class="si">{folder_album_seq.1}</span><span class="s2">' "</span>
<span class="o">+</span> <span class="s2">"To start counting at a value other than 0, append '(starting_value)' to the field name. "</span>
<span class="o">+</span> <span class="s2">"For example, to start counting at 1 instead of 0: '{folder_album_seq(1)}' "</span>
<span class="o">+</span> <span class="s2">"May be formatted using a python string format code. "</span>
<span class="o">+</span> <span class="s2">"For example, to format as a 5-digit integer and pad with zeros, use '</span><span class="si">{folder_album_seq:05d}</span><span class="s2">' which results in "</span>
<span class="o">+</span> <span class="s2">"00000, 00001, 00002...etc. "</span>
<span class="o">+</span> <span class="s2">"This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '</span><span class="si">{album_seq}</span><span class="s2">'."</span><span class="p">,</span>
<span class="o">+</span> <span class="s2">"To format while also using a starting value: '{folder_album_seq:05d(1)}' which results in 0001, 00002...etc."</span>
<span class="o">+</span> <span class="s2">"This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '</span><span class="si">{album_seq}</span><span class="s2">'. "</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{comma}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"A comma: ','"</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{semicolon}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"A semicolon: ';'"</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{questionmark}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"A question mark: '?'"</span><span class="p">,</span>
@@ -418,6 +421,9 @@
<span class="o">+</span> <span class="s2">"Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support."</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{shell_quote}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg =&gt; 'My file.jpeg'; only adds quotes if needed."</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{strip}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"Use in form '{strip,TEMPLATE}'; strips whitespace from begining and end of rendered TEMPLATE value(s)."</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{format}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"Use in form, '{format:TYPE:FORMAT,TEMPLATE}'; converts TEMPLATE value to TYPE then formats the value "</span>
<span class="o">+</span> <span class="s2">"using Python string formatting codes specified by FORMAT; TYPE is one of: 'int', 'float', or 'str'. "</span>
<span class="s2">"For example, '{format:float:.1f,{exiftool:EXIF:FocalLength}}' will format focal length to 1 decimal place (e.g. '100.0'). "</span><span class="p">,</span>
<span class="s2">"</span><span class="si">{function}</span><span class="s2">"</span><span class="p">:</span> <span class="s2">"Execute a python function from an external file and use return value as template substitution. "</span>
<span class="o">+</span> <span class="s2">"Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. "</span>
<span class="o">+</span> <span class="s2">"The function will be passed the PhotoInfo object for the photo. "</span>
@@ -435,6 +441,18 @@
<span class="s2">"brackets"</span><span class="p">:</span> <span class="s2">"Enclose value in brackets, e.g. 'value' =&gt; '[value]'"</span><span class="p">,</span>
<span class="s2">"shell_quote"</span><span class="p">:</span> <span class="s2">"Quotes the value for safe usage in the shell, e.g. My file.jpeg =&gt; 'My file.jpeg'; only adds quotes if needed."</span><span class="p">,</span>
<span class="s2">"function"</span><span class="p">:</span> <span class="s2">"Run custom python function to filter value; use in format 'function:/path/to/file.py::function_name'. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py"</span><span class="p">,</span>
<span class="s2">"split(x)"</span><span class="p">:</span> <span class="s2">"Split value into a list of values using x as delimiter, e.g. 'value1;value2' =&gt; ['value1', 'value2'] if used with split(;)."</span><span class="p">,</span>
<span class="s2">"autosplit"</span><span class="p">:</span> <span class="s2">"Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. 'value1,value2' =&gt; ['value1', 'value2']."</span><span class="p">,</span>
<span class="s2">"chop(x)"</span><span class="p">:</span> <span class="s2">"Remove x characters off the end of value, e.g. chop(1): 'Value' =&gt; 'Valu'; when applied to a list, chops characters from each list value, e.g. chop(1): ['travel', 'beach']=&gt; ['trave', 'beac']."</span><span class="p">,</span>
<span class="s2">"chomp(x)"</span><span class="p">:</span> <span class="s2">"Remove x characters from the beginning of value, e.g. chomp(1): ['Value'] =&gt; ['alue']; when applied to a list, removes characters from each list value, e.g. chomp(1): ['travel', 'beach']=&gt; ['ravel', 'each']."</span><span class="p">,</span>
<span class="s2">"sort"</span><span class="p">:</span> <span class="s2">"Sort list of values, e.g. ['c', 'b', 'a'] =&gt; ['a', 'b', 'c']."</span><span class="p">,</span>
<span class="s2">"rsort"</span><span class="p">:</span> <span class="s2">"Sort list of values in reverse order, e.g. ['a', 'b', 'c'] =&gt; ['c', 'b', 'a']."</span><span class="p">,</span>
<span class="s2">"reverse"</span><span class="p">:</span> <span class="s2">"Reverse order of values, e.g. ['a', 'b', 'c'] =&gt; ['c', 'b', 'a']."</span><span class="p">,</span>
<span class="s2">"uniq"</span><span class="p">:</span> <span class="s2">"Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] =&gt; ['a', 'b', 'c']."</span><span class="p">,</span>
<span class="s2">"join(x)"</span><span class="p">:</span> <span class="s2">"Join list of values with delimiter x, e.g. join(:): ['a', 'b', 'c'] =&gt; 'a:b:c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters."</span><span class="p">,</span>
<span class="s2">"append(x)"</span><span class="p">:</span> <span class="s2">"Append x to list of values, e.g. append(d): ['a', 'b', 'c'] =&gt; ['a', 'b', 'c', 'd']."</span><span class="p">,</span>
<span class="s2">"prepend(x)"</span><span class="p">:</span> <span class="s2">"Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] =&gt; ['d', 'a', 'b', 'c']."</span><span class="p">,</span>
<span class="s2">"remove(x)"</span><span class="p">:</span> <span class="s2">"Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] =&gt; ['a', 'c']."</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># Just the substitutions without the braces</span>
@@ -578,6 +596,7 @@
<span class="bp">self</span><span class="o">.</span><span class="n">filepath</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">filepath</span>
<span class="bp">self</span><span class="o">.</span><span class="n">quote</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">quote</span>
<span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">dest_path</span>
<span class="bp">self</span><span class="o">.</span><span class="n">variables</span> <span class="o">=</span> <span class="p">{}</span>
<div class="viewcode-block" id="PhotoTemplate.render"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.render">[docs]</a> <span class="k">def</span> <span class="nf">render</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -626,14 +645,13 @@
<span class="k">def</span> <span class="nf">_render_statement</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">statement</span><span class="p">,</span>
<span class="n">path_sep</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">field_arg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">):</span>
<span class="n">path_sep</span> <span class="o">=</span> <span class="n">path_sep</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_sep</span>
<span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">unmatched</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">ts</span> <span class="ow">in</span> <span class="n">statement</span><span class="o">.</span><span class="n">template_strings</span><span class="p">:</span>
<span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_template_string</span><span class="p">(</span>
<span class="n">ts</span><span class="p">,</span> <span class="n">results</span><span class="o">=</span><span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span><span class="o">=</span><span class="n">unmatched</span><span class="p">,</span> <span class="n">path_sep</span><span class="o">=</span><span class="n">path_sep</span>
<span class="n">ts</span><span class="p">,</span> <span class="n">results</span><span class="o">=</span><span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span><span class="o">=</span><span class="n">unmatched</span><span class="p">,</span> <span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span>
<span class="p">)</span>
<span class="n">rendered_strings</span> <span class="o">=</span> <span class="n">results</span>
@@ -653,7 +671,7 @@
<span class="k">def</span> <span class="nf">_render_template_string</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">ts</span><span class="p">,</span>
<span class="n">path_sep</span><span class="p">,</span>
<span class="n">field_arg</span><span class="p">,</span>
<span class="n">results</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">unmatched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">):</span>
@@ -665,11 +683,6 @@
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="p">:</span>
<span class="c1"># have a template field to process</span>
<span class="n">field</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">field</span>
<span class="n">field_part</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">if</span> <span class="n">field</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">FIELD_NAMES</span> <span class="ow">and</span> <span class="n">field_part</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">FIELD_NAMES</span><span class="p">:</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[],</span> <span class="n">unmatched</span>
<span class="n">subfield</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">subfield</span>
<span class="c1"># process filters</span>
@@ -677,14 +690,15 @@
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">filter</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">filters</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">filter</span><span class="o">.</span><span class="n">value</span>
<span class="c1"># process path_sep</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">pathsep</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">path_sep</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">pathsep</span><span class="o">.</span><span class="n">value</span>
<span class="c1"># process field arguments</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">field_arg</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span><span class="o">.</span><span class="n">value</span>
<span class="c1"># process delim</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">delim</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># if value is None, means format was {+field}</span>
<span class="n">delim</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">delim</span><span class="o">.</span><span class="n">value</span> <span class="ow">or</span> <span class="s2">""</span>
<span class="n">delim</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">delim</span><span class="p">,</span> <span class="s2">"delim"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">delim</span> <span class="o">=</span> <span class="kc">None</span>
@@ -693,7 +707,7 @@
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">bool</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">bool_val</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span>
<span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">bool</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">path_sep</span><span class="o">=</span><span class="n">path_sep</span><span class="p">,</span>
<span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
@@ -709,7 +723,7 @@
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">default</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">default</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span>
<span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">default</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">path_sep</span><span class="o">=</span><span class="n">path_sep</span><span class="p">,</span>
<span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
@@ -724,11 +738,11 @@
<span class="n">negation</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">negation</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># conditional value is also a TemplateString</span>
<span class="n">conditional_value</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span>
<span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">path_sep</span><span class="o">=</span><span class="n">path_sep</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
<span class="n">conditional_value</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">cv</span> <span class="ow">in</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">conditional</span><span class="o">.</span><span class="n">value</span><span class="p">:</span>
<span class="n">value</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_render_statement</span><span class="p">(</span><span class="n">cv</span><span class="p">)</span>
<span class="n">conditional_value</span> <span class="o">+=</span> <span class="n">value</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># this shouldn't happen</span>
<span class="n">conditional_value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">""</span><span class="p">]</span>
@@ -737,43 +751,23 @@
<span class="n">negation</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">conditional_value</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="p">(</span>
<span class="n">field</span> <span class="ow">in</span> <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span>
<span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span>
<span class="p">):</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value</span><span class="p">(</span>
<span class="n">field</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">,</span>
<span class="n">subfield</span><span class="o">=</span><span class="n">subfield</span><span class="p">,</span>
<span class="c1"># delim=delim or self.inplace_sep,</span>
<span class="c1"># path_sep=path_sep,</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"exiftool"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">subfield</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">"SyntaxError: GROUP:NAME subfield must not be null with {exiftool:GROUP:NAME}'"</span>
<span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"%"</span><span class="p">):</span>
<span class="c1"># variable in form {%var}</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">field</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="n">vals</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Variable '</span><span class="si">{</span><span class="n">field</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="si">}</span><span class="s2">' is not defined."</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"var"</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">subfield</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">default</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
<span class="s2">"var must have a subfield and value in form {var:subfield,value}"</span>
<span class="p">)</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_exiftool</span><span class="p">(</span>
<span class="n">subfield</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"function"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">subfield</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">"SyntaxError: filename and function must not be null with {function::filename.py:function_name}"</span>
<span class="p">)</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_function</span><span class="p">(</span>
<span class="n">subfield</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">MULTI_VALUE_SUBSTITUTIONS</span> <span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"photo"</span><span class="p">):</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_multi</span><span class="p">(</span>
<span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">path_sep</span><span class="o">=</span><span class="n">path_sep</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">PATHLIB_SUBSTITUTIONS</span><span class="p">:</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_pathlib</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="p">[</span><span class="n">subfield</span><span class="p">]</span> <span class="o">=</span> <span class="n">default</span>
<span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[],</span> <span class="n">unmatched</span>
<span class="n">vals</span><span class="p">,</span> <span class="n">u</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_field_values</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">field_arg</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
<span class="k">if</span> <span class="n">u</span><span class="p">:</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[],</span> <span class="n">unmatched</span>
<span class="n">vals</span> <span class="o">=</span> <span class="p">[</span><span class="n">val</span> <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">vals</span> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">]</span>
@@ -782,7 +776,7 @@
<span class="n">vals</span> <span class="o">=</span> <span class="p">[</span><span class="n">sep</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="n">vals</span><span class="p">))]</span> <span class="k">if</span> <span class="n">vals</span> <span class="k">else</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">filter_</span> <span class="ow">in</span> <span class="n">filters</span><span class="p">:</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_filter</span><span class="p">(</span><span class="n">filter_</span><span class="p">,</span> <span class="n">vals</span><span class="p">)</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_filter_values</span><span class="p">(</span><span class="n">filter_</span><span class="p">,</span> <span class="n">vals</span><span class="p">)</span>
<span class="c1"># process find/replace</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">findreplace</span><span class="p">:</span>
@@ -790,7 +784,9 @@
<span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">vals</span><span class="p">:</span>
<span class="k">for</span> <span class="n">pair</span> <span class="ow">in</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">findreplace</span><span class="o">.</span><span class="n">pairs</span><span class="p">:</span>
<span class="n">find</span> <span class="o">=</span> <span class="n">pair</span><span class="o">.</span><span class="n">find</span> <span class="ow">or</span> <span class="s2">""</span>
<span class="n">find</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">find</span><span class="p">,</span> <span class="s2">"find/replace"</span><span class="p">)</span>
<span class="n">repl</span> <span class="o">=</span> <span class="n">pair</span><span class="o">.</span><span class="n">replace</span> <span class="ow">or</span> <span class="s2">""</span>
<span class="n">repl</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">repl</span><span class="p">,</span> <span class="s2">"find/replace"</span><span class="p">)</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">val</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">find</span><span class="p">,</span> <span class="n">repl</span><span class="p">)</span>
<span class="n">new_vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
<span class="n">vals</span> <span class="o">=</span> <span class="n">new_vals</span>
@@ -817,22 +813,23 @@
<span class="k">def</span> <span class="nf">comparison_test</span><span class="p">(</span><span class="n">test_function</span><span class="p">):</span>
<span class="sd">"""Perform numerical comparisons using test_function; closure to capture conditional_val, vals, negation"""</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"comparison operators may only be used with a single value: </span><span class="si">{</span><span class="n">vals</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">"</span>
<span class="c1"># returns True if any of the values match the condition</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"comparison operators may only be used with a single conditional value: </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">match</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span>
<span class="n">test_function</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">vals</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">float</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="n">match</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span>
<span class="nb">bool</span><span class="p">(</span><span class="n">test_function</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">),</span> <span class="nb">float</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">[</span><span class="mi">0</span><span class="p">])))</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">vals</span>
<span class="p">)</span>
<span class="k">return</span> <span class="p">(</span>
<span class="p">[</span><span class="s2">"True"</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
<span class="k">else</span> <span class="p">[]</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">vals</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
@@ -874,7 +871,8 @@
<span class="k">if</span> <span class="n">is_bool</span><span class="p">:</span>
<span class="n">vals</span> <span class="o">=</span> <span class="n">default</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">vals</span> <span class="k">else</span> <span class="n">bool_val</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">vals</span><span class="p">:</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">vals</span> <span class="ow">and</span> <span class="n">field</span> <span class="o">!=</span> <span class="s2">"var"</span><span class="p">:</span>
<span class="c1"># don't assign default value if the template was variable assignment</span>
<span class="n">vals</span> <span class="o">=</span> <span class="n">default</span> <span class="ow">or</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">none_str</span><span class="p">]</span>
<span class="n">pre</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">pre</span> <span class="ow">or</span> <span class="s2">""</span>
@@ -896,14 +894,103 @@
<span class="k">return</span> <span class="n">results</span><span class="p">,</span> <span class="n">unmatched</span>
<div class="viewcode-block" id="PhotoTemplate.expand_variables_to_str"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.expand_variables_to_str">[docs]</a> <span class="k">def</span> <span class="nf">expand_variables_to_str</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">"""</span>
<span class="sd"> Expand variables in value and return a str of the expanded value.</span>
<span class="sd"> Enforce that the expanded value is a single value, raises ValueError if not.</span>
<span class="sd"> Args:</span>
<span class="sd"> value: the value to expand</span>
<span class="sd"> name: the name of the value being expanded (used in error messages)</span>
<span class="sd"> """</span>
<span class="n">expanded</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">expanded</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2"> must have a single value, not </span><span class="si">{</span><span class="n">expanded</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">expanded</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span></div>
<div class="viewcode-block" id="PhotoTemplate.expand_variables"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.expand_variables">[docs]</a> <span class="k">def</span> <span class="nf">expand_variables</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">"""Expand variables in value"""</span>
<span class="c1"># replace any variables with their values</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">value</span><span class="p">]</span>
<span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># allow %% to escape %, match variables in form %var</span>
<span class="n">variable_match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s2">"(?:</span><span class="si">%%</span><span class="s2">)*(%[\w]+)?"</span><span class="p">)</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
<span class="n">match</span> <span class="o">=</span> <span class="n">variable_match</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">match</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
<span class="k">break</span>
<span class="n">var</span> <span class="o">=</span> <span class="n">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">var_name</span> <span class="o">=</span> <span class="n">var</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
<span class="k">if</span> <span class="n">var_name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Variable '</span><span class="si">{</span><span class="n">var_name</span><span class="si">}</span><span class="s2">' is not defined."</span><span class="p">)</span>
<span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
<span class="k">for</span> <span class="n">var_val</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="p">[</span><span class="n">var_name</span><span class="p">]:</span>
<span class="n">new_values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
<span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">f</span><span class="s2">"(%%)*</span><span class="si">{</span><span class="n">var</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="sa">r</span><span class="s2">"\g&lt;1&gt;"</span> <span class="o">+</span> <span class="n">var_val</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">new_values</span> <span class="o">==</span> <span class="n">values</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">new_values</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">new_values</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># replace %% with %</span>
<span class="c1"># any %% left in the string will be replaced with %</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">value</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="si">%%</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"%"</span><span class="p">)</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">return</span> <span class="n">values</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_field_values"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_field_values">[docs]</a> <span class="k">def</span> <span class="nf">get_field_values</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
<span class="n">subfield</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
<span class="n">field_arg</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
<span class="n">default</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]:</span>
<span class="sd">"""Get the values for a field"""</span>
<span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">unmatched</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="p">(</span>
<span class="n">field</span> <span class="ow">in</span> <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span>
<span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">SINGLE_VALUE_SUBSTITUTIONS</span>
<span class="p">):</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value</span><span class="p">(</span>
<span class="n">field</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">,</span>
<span class="n">subfield</span><span class="o">=</span><span class="n">subfield</span><span class="p">,</span>
<span class="n">field_arg</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"exiftool"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">subfield</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">"SyntaxError: GROUP:NAME subfield must not be null with {exiftool:GROUP:NAME}'"</span>
<span class="p">)</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_exiftool</span><span class="p">(</span>
<span class="n">subfield</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"function"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">subfield</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">"SyntaxError: filename and function must not be null with {function::filename.py:function_name}"</span>
<span class="p">)</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_function</span><span class="p">(</span><span class="n">subfield</span><span class="p">,</span> <span class="n">field_arg</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">MULTI_VALUE_SUBSTITUTIONS</span> <span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"photo"</span><span class="p">):</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_multi</span><span class="p">(</span>
<span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">path_sep</span><span class="o">=</span><span class="n">field_arg</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">PATHLIB_SUBSTITUTIONS</span><span class="p">:</span>
<span class="n">vals</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_pathlib</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">unmatched</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[],</span> <span class="n">unmatched</span>
<span class="k">return</span> <span class="n">vals</span><span class="p">,</span> <span class="n">unmatched</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_template_value"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value">[docs]</a> <span class="k">def</span> <span class="nf">get_template_value</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">field</span><span class="p">,</span>
<span class="n">default</span><span class="p">,</span>
<span class="n">subfield</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="c1"># bool_val=None,</span>
<span class="c1"># delim=None,</span>
<span class="c1"># path_sep=None,</span>
<span class="n">subfield</span><span class="p">,</span>
<span class="n">field_arg</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">"""lookup value for template field (single-value template substitutions)</span>
@@ -1200,9 +1287,8 @@
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="ne">IndexError</span><span class="p">):</span>
<span class="n">start_id</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="n">start_id</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">start_id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">field_arg</span><span class="p">)</span> <span class="k">if</span> <span class="n">field_arg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="mi">0</span>
<span class="n">value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="n">start_id</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">format_str_value</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">subfield</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># if here, didn't get a match</span>
@@ -1250,57 +1336,116 @@
<span class="k">return</span> <span class="p">[</span><span class="n">value</span><span class="p">]</span></div>
<span class="k">def</span> <span class="nf">get_template_value_filter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filter_</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
<div class="viewcode-block" id="PhotoTemplate.get_filter_values"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_filter_values">[docs]</a> <span class="k">def</span> <span class="nf">get_filter_values</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filter_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">values</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">"""Return filtered values"""</span>
<span class="c1"># extract args, if any</span>
<span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">"\(.*\)"</span><span class="p">,</span> <span class="n">filter_</span><span class="p">):</span>
<span class="n">filter_</span><span class="p">,</span> <span class="n">args</span> <span class="o">=</span> <span class="n">filter_</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"("</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">")"</span><span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="s2">"Filter arguments"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">args</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># check that filter name (without subfields or arguments) is valid</span>
<span class="n">valid_filters</span> <span class="o">=</span> <span class="p">[</span><span class="n">f</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"("</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">FILTER_VALUES</span><span class="p">]</span>
<span class="k">if</span> <span class="n">filter_</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">":"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">valid_filters</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unknown filter: </span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">filter_</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">"split"</span><span class="p">,</span>
<span class="s2">"chop"</span><span class="p">,</span>
<span class="s2">"chomp"</span><span class="p">,</span>
<span class="s2">"join"</span><span class="p">,</span>
<span class="s2">"append"</span><span class="p">,</span>
<span class="s2">"prepend"</span><span class="p">,</span>
<span class="s2">"remove"</span><span class="p">,</span>
<span class="p">]</span> <span class="ow">and</span> <span class="p">(</span><span class="n">args</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2"> requires arguments"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"lower"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="o">.</span><span class="n">lower</span><span class="p">()]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"upper"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="o">.</span><span class="n">upper</span><span class="p">()]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"strip"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="o">.</span><span class="n">strip</span><span class="p">()]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"capitalize"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"titlecase"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">title</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="o">.</span><span class="n">title</span><span class="p">()]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">title</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"braces"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"{"</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">"}"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"{"</span> <span class="o">+</span> <span class="n">values</span> <span class="o">+</span> <span class="s2">"}"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"{"</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">"}"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"parens"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">)"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="n">values</span><span class="si">}</span><span class="s2">)"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"("</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">")"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"brackets"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"[</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">]"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"[</span><span class="si">{</span><span class="n">values</span><span class="si">}</span><span class="s2">]"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"["</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">"]"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"shell_quote"</span><span class="p">:</span>
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"split"</span><span class="p">:</span>
<span class="c1"># split on delimiter</span>
<span class="n">delim</span> <span class="o">=</span> <span class="n">args</span>
<span class="k">if</span> <span class="n">delim</span><span class="p">:</span>
<span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
<span class="n">new_values</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">delim</span><span class="p">))</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">new_values</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">values</span><span class="p">)]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"chop"</span><span class="p">:</span>
<span class="c1"># chop off characters from the end</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">chop</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid value for chop: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[:</span><span class="o">-</span><span class="n">chop</span><span class="p">]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chop</span> <span class="k">else</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"chomp"</span><span class="p">:</span>
<span class="c1"># chop off characters from the beginning</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">chomp</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid value for chomp: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[</span><span class="n">chomp</span><span class="p">:]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chomp</span> <span class="k">else</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"autosplit"</span><span class="p">:</span>
<span class="c1"># try to split keyword strings automatically</span>
<span class="n">temp_values</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">","</span><span class="p">,</span> <span class="s2">" "</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="n">temp_values</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">";"</span><span class="p">,</span> <span class="s2">" "</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">temp_values</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">temp_values</span><span class="p">:</span>
<span class="n">value</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">val</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"sort"</span><span class="p">:</span>
<span class="c1"># sort list of values</span>
<span class="n">value</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"rsort"</span><span class="p">:</span>
<span class="c1"># reverse sort list of values</span>
<span class="n">value</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"reverse"</span><span class="p">:</span>
<span class="c1"># reverse list of values</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">values</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"uniq"</span><span class="p">:</span>
<span class="c1"># remove duplicate values from list</span>
<span class="n">temp_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">temp_values</span><span class="p">:</span>
<span class="n">temp_values</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">temp_values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"join"</span><span class="p">:</span>
<span class="c1"># join list of values with delimiter</span>
<span class="n">delim</span> <span class="o">=</span> <span class="n">args</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">delim</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">values</span><span class="p">)]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"append"</span><span class="p">:</span>
<span class="c1"># append value to list</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">values</span> <span class="o">+</span> <span class="p">[</span><span class="n">args</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"prepend"</span><span class="p">:</span>
<span class="c1"># prepend value to list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">args</span><span class="p">]</span> <span class="o">+</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"remove"</span><span class="p">:</span>
<span class="c1"># remove value from list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span> <span class="k">if</span> <span class="n">v</span> <span class="o">!=</span> <span class="n">args</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"function:"</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_filter_function</span><span class="p">(</span><span class="n">filter_</span><span class="p">,</span> <span class="n">values</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template_value_filter_function</span><span class="p">(</span><span class="n">filter_</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">values</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">value</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_template_value_multi"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_multi">[docs]</a> <span class="k">def</span> <span class="nf">get_template_value_multi</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">path_sep</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="sd">"""lookup value for template field (multi-value template substitutions)</span>
@@ -1320,6 +1465,8 @@
<span class="sd">""" return list of values for a multi-valued template field """</span>
<span class="n">path_sep</span> <span class="o">=</span> <span class="n">path_sep</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_sep</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
@@ -1385,6 +1532,8 @@
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span> <span class="k">if</span> <span class="n">v</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"strip"</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"format"</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_format_values</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">subfield</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"photo"</span><span class="p">):</span>
<span class="c1"># provide access to PhotoInfo object</span>
<span class="n">properties</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)</span>
@@ -1399,10 +1548,11 @@
<span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">property_</span><span class="p">)</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">except</span> <span class="ne">AttributeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">"Invalid property for </span><span class="si">{photo}</span><span class="s2"> template: "</span> <span class="o">+</span> <span class="sa">f</span><span class="s2">"'</span><span class="si">{</span><span class="n">property_</span><span class="si">}</span><span class="s2">'"</span>
<span class="p">)</span>
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="nb">bool</span><span class="p">):</span>
@@ -1427,6 +1577,31 @@
<span class="n">values</span> <span class="o">=</span> <span class="n">values</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="k">return</span> <span class="n">values</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_format_values"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_format_values">[docs]</a> <span class="k">def</span> <span class="nf">get_format_values</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">subfield</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">default</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]]]:</span>
<span class="sd">"""Return values for {format} templates"""</span>
<span class="k">if</span> <span class="n">field</span> <span class="o">!=</span> <span class="s2">"format"</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unhandled template value in get_format_values: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">subfield</span> <span class="ow">or</span> <span class="s2">":"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">subfield</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="s2">"</span><span class="si">{format}</span><span class="s2"> requires subfield in form TYPE:FORMAT"</span><span class="p">)</span>
<span class="n">type_</span><span class="p">,</span> <span class="n">format_str</span> <span class="o">=</span> <span class="n">subfield</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">":"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="n">type_</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"int"</span><span class="p">,</span> <span class="s2">"float"</span><span class="p">,</span> <span class="s2">"str"</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"'</span><span class="si">{</span><span class="n">type_</span><span class="si">}</span><span class="s2">' is not a valid type for </span><span class="si">{</span><span class="nb">format</span><span class="si">}</span><span class="s2">: must be one of 'int', 'float', 'str'"</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">type_</span> <span class="o">==</span> <span class="s2">"int"</span><span class="p">:</span>
<span class="c1"># convert to float then int to avoid error when converting a string float to int</span>
<span class="n">default_</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">))</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">type_</span> <span class="o">==</span> <span class="s2">"float"</span><span class="p">:</span>
<span class="n">default_</span> <span class="o">=</span> <span class="p">[</span><span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">default_</span> <span class="o">=</span> <span class="n">default</span>
<span class="n">format_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expand_variables_to_str</span><span class="p">(</span><span class="n">format_str</span><span class="p">,</span> <span class="s2">"format string"</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="n">format_str_value</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">format_str</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">default_</span><span class="p">]</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_template_value_exiftool"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_exiftool">[docs]</a> <span class="k">def</span> <span class="nf">get_template_value_exiftool</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">subfield</span><span class="p">,</span>
@@ -1460,6 +1635,7 @@
<div class="viewcode-block" id="PhotoTemplate.get_template_value_function"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_function">[docs]</a> <span class="k">def</span> <span class="nf">get_template_value_function</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">subfield</span><span class="p">,</span>
<span class="n">field_arg</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">"""Get template value from external function"""</span>
@@ -1475,7 +1651,13 @@
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"'</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">' does not appear to be a file"</span><span class="p">)</span>
<span class="n">template_func</span> <span class="o">=</span> <span class="n">load_function</span><span class="p">(</span><span class="n">filename_validated</span><span class="p">,</span> <span class="n">funcname</span><span class="p">)</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># must be a PhotoInfoNone instance</span>
<span class="c1"># if no uuid, then template is being validated but not actually run</span>
<span class="c1"># so don't run the function</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="n">field_arg</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">list</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span>
@@ -1493,8 +1675,9 @@
<span class="k">return</span> <span class="n">values</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_template_value_filter_function"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_filter_function">[docs]</a> <span class="k">def</span> <span class="nf">get_template_value_filter_function</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filter_</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
<div class="viewcode-block" id="PhotoTemplate.get_template_value_filter_function"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_template_value_filter_function">[docs]</a> <span class="k">def</span> <span class="nf">get_template_value_filter_function</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filter_</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
<span class="sd">"""Filter template value from external function"""</span>
<span class="c1"># TODO: add args to filter function call? Would change signature of function</span>
<span class="n">filter_</span> <span class="o">=</span> <span class="n">filter_</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"function:"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>
@@ -1513,7 +1696,11 @@
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">)):</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="p">]</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># if uuid is None, it's a PhotoInfoNone instance and template is being validated</span>
<span class="c1"># so don't run the function</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span>
@@ -1525,10 +1712,7 @@
<div class="viewcode-block" id="PhotoTemplate.get_photo_video_type"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_photo_video_type">[docs]</a> <span class="k">def</span> <span class="nf">get_photo_video_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="sd">"""return media type, e.g. photo or video"""</span>
<span class="n">default_dict</span> <span class="o">=</span> <span class="n">parse_default_kv</span><span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">PHOTO_VIDEO_TYPE_DEFAULTS</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
<span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">"photo"</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">"video"</span><span class="p">]</span></div>
<span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">"photo"</span><span class="p">]</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span> <span class="k">else</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">"video"</span><span class="p">]</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_media_type"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_media_type">[docs]</a> <span class="k">def</span> <span class="nf">get_media_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="sd">"""return special media type, e.g. slow_mo, panorama, etc., defaults to photo or video if no special type"""</span>
@@ -1555,13 +1739,9 @@
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">default_dict</span><span class="p">[</span><span class="s2">"photo"</span><span class="p">]</span></div>
<span class="k">def</span> <span class="nf">get_photo_bool_attribute</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">default</span><span class="p">,</span> <span class="n">bool_val</span><span class="p">):</span>
<span class="c1"># get value for a PhotoInfo bool attribute</span>
<span class="n">val</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">attr</span><span class="p">)</span>
<span class="k">if</span> <span class="n">val</span><span class="p">:</span>
<span class="k">return</span> <span class="n">bool_val</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">default</span></div>
<div class="viewcode-block" id="PhotoTemplate.get_photo_bool_attribute"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoTemplate.get_photo_bool_attribute">[docs]</a> <span class="k">def</span> <span class="nf">get_photo_bool_attribute</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">default</span><span class="p">,</span> <span class="n">bool_val</span><span class="p">):</span>
<span class="sd">"""Return the boolean value for a photo attribute"""</span>
<span class="k">return</span> <span class="n">bool_val</span> <span class="k">if</span> <span class="p">(</span><span class="n">val</span> <span class="o">:=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">attr</span><span class="p">))</span> <span class="k">else</span> <span class="n">default</span></div></div>
<span class="k">def</span> <span class="nf">parse_default_kv</span><span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">default_dict</span><span class="p">):</span>

View File

@@ -8,7 +8,7 @@ In its simplest form, a template statement has the form: ``"{template_field}"``\
Template statements may contain one or more modifiers. The full syntax is:
``"pretext{delim+template_field:subfield|filter(path_sep)[find,replace] conditional?bool_value,default}posttext"``
``"pretext{delim+template_field:subfield(field_arg)|filter[find,replace] conditional?bool_value,default}posttext"``
Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.
@@ -30,6 +30,8 @@ e.g. if Photo keywords are ``["foo","bar"]``\ :
`:subfield`: Some templates have sub-fields, For example, `{exiftool:IPTC:Make}\ ``; the template_field is``\ exiftool\ ``and the sub-field is``\ IPTC:Make`.
``(field_arg)``\ : optional arguments to pass to the field; for example, with ``{folder_album}`` this is used to pass the path separator used for joining folders and albums when rendering the field (default is "/" for ``{folder_album}``\ ).
`|filter`: You may optionally append one or more filter commands to the end of the template field using the vertical pipe ('|') symbol. Filters may be combined, separated by '|' as in: ``{keyword|capitalize|parens}``.
Valid filters are:
@@ -45,6 +47,18 @@ Valid filters are:
* ``brackets``\ : Enclose value in brackets, e.g. 'value' => '[value]'
* ``shell_quote``\ : Quotes the value for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
* `function`: Run custom python function to filter value; use in format 'function:/path/to/file.py::function_name'. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py
* ``split(x)``\ : Split value into a list of values using x as delimiter, e.g. 'value1;value2' => ['value1', 'value2'] if used with split(;).
* ``autosplit``\ : Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. 'value1,value2' => ['value1', 'value2'].
* `chop(x)`: Remove x characters off the end of value, e.g. chop(1): 'Value' => 'Valu'; when applied to a list, chops characters from each list value, e.g. chop(1): ['travel', 'beach']=> ['trave', 'beac'].
* `chomp(x)`: Remove x characters from the beginning of value, e.g. chomp(1): ['Value'] => ['alue']; when applied to a list, removes characters from each list value, e.g. chomp(1): ['travel', 'beach']=> ['ravel', 'each'].
* ``sort``\ : Sort list of values, e.g. ['c', 'b', 'a'] => ['a', 'b', 'c'].
* ``rsort``\ : Sort list of values in reverse order, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
* ``reverse``\ : Reverse order of values, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
* ``uniq``\ : Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] => ['a', 'b', 'c'].
* `join(x)`: Join list of values with delimiter x, e.g. join(:): ['a', 'b', 'c'] => 'a:b:c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.
* `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
* `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
* `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
e.g. if Photo keywords are ``["FOO","bar"]``\ :
@@ -59,8 +73,6 @@ e.g. if Photo description is "my description":
* ``"{descr|titlecase}"`` renders to: ``"My Description"``
``(path_sep)``\ : optional path separator to use when joining path-like fields, for example ``{folder_album}``. Default is "/".
e.g. If Photo is in ``Album1`` in ``Folder1``\ :
@@ -106,7 +118,7 @@ This can be used to rename files as well, for example:
This renames any photo that is a favorite as 'Favorite-ImageName.jpg' (where 'ImageName.jpg' is the original name of the photo) and all other photos with the unmodified original name.
``?bool_value``\ : Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(path_sep)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is ``"{hdr}"``\ ) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is ``"{title}"``\ ) then the default value following a "," will be used.
``?bool_value``\ : Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(field_arg)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is ``"{hdr}"``\ ) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is ``"{title}"``\ ) then the default value following a "," will be used.
e.g. if photo is an HDR image,
@@ -141,6 +153,16 @@ If you want to include "{" or "}" in the output, use "{openbrace}" or "{closebra
e.g. ``"{created.year}/{openbrace}{title}{closebrace}"`` would result in ``"2020/{Photo Title}"``.
**Variables**
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}``. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
Variables can also be referenced as fields in the template string, for example: ``{var:year,created.year}{original_name}-{%year}``. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
If you need to use a ``%`` (percent sign character), you can escape the percent sign by using ``%%``. You can also use the ``{percent}`` template field where a template field is required. For example:
``{title[:,%%]}`` replaces the ``:`` with ``%`` and ``{title contains Foo?{title}{percent},{title}}`` adds ``%`` to the title if it contains ``Foo``.
Template Substitutions
----------------------
@@ -288,9 +310,9 @@ Template Substitutions
* - {id}
- A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3...etc. Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{id:05d}' which results in 00001, 00002, 00003...etc.
* - {album_seq}
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{album_seq.1}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{album_seq(1)}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.
* - {folder_album_seq}
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq.1}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'.
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq(1)}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{folder_album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'.
* - {comma}
- A comma: ','
* - {semicolon}
@@ -320,7 +342,7 @@ Template Substitutions
* - {crlf}
- a carriage return + line feed: '\r\n'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.49.9'
- The osxphotos version, e.g. '0.50.0'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}
@@ -361,6 +383,8 @@ Template Substitutions
- Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
* - {strip}
- Use in form '{strip,TEMPLATE}'; strips whitespace from begining and end of rendered TEMPLATE value(s).
* - {format}
- Use in form, '{format:TYPE:FORMAT,TEMPLATE}'; converts TEMPLATE value to TYPE then formats the value using Python string formatting codes specified by FORMAT; TYPE is one of: 'int', 'float', or 'str'. For example, '{format:float:.1f,{exiftool:EXIF:FocalLength}}' will format focal length to 1 decimal place (e.g. '100.0').
* - {function}
- Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.

View File

@@ -218,6 +218,15 @@ A powerful feature of Photos is that it uses machine learning algorithms to auto
``osxphotos export /path/to/export --exiftool --keyword-template "{label}"``
Removing a keyword during export
--------------------------------
If some of your photos contain a keyword you do not want to be added to the exported file with ``--exiftool``\ , you can use the template system to remove the keyword from the exported file. For example, if you want to remove the keyword "MyKeyword" from all your photos:
``osxphotos export /path/to/export --exiftool --keyword-template "{keyword|remove(MyKeyword)}" --replace-keywords``
In this example, ``|remove(MyKeyword)`` is a filter which removes ``MyKeyword`` from the keyword list of every photo being processed. The ``--replace-keywords`` option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from ``--keyword-template``.
**Note**\ : When evaluating templates for ``--directory`` and ``--filename``\ , osxphotos inserts the automatic default value "_" for any template field which is null (empty or blank). This is to ensure that there's never a null directory or filename created. For metadata templates such as ``--keyword-template``\ , osxphotos does not provide an automatic default value thus if the template field is null, no keyword would be created. Of course, you can provide a default value if desired and osxphotos will use this. For example, to add "nolabel" as a keyword for any photo that doesn't have labels:
``osxphotos export /path/to/export --exiftool --keyword-template "{label,nolabel}"``

View File

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

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.49.9 documentation</title>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.49.9 documentation</title>
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -145,7 +145,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -2386,14 +2386,18 @@
</li>
<li><a href="reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar">exiftool_json_sidecar() (osxphotos.PhotoExporter method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.expand_variables">expand_variables() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.expand_variables_to_str">expand_variables_to_str() (osxphotos.PhotoTemplate method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotoExporter.export">export() (osxphotos.PhotoExporter method)</a>
<ul>
<li><a href="reference.html#osxphotos.PhotoInfo.export">(osxphotos.PhotoInfo method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.ExportOptions.export_as_hardlink">export_as_hardlink (osxphotos.ExportOptions attribute)</a>
</li>
<li>
@@ -2491,21 +2495,29 @@
<li><a href="reference.html#osxphotos.ExportDB.get_export_results">get_export_results() (osxphotos.ExportDB method)</a>
</li>
<li><a href="reference.html#osxphotos.ExportDB.get_exported_files">get_exported_files() (osxphotos.ExportDB method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.get_field_values">get_field_values() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.ExportDB.get_file_record">get_file_record() (osxphotos.ExportDB method)</a>
</li>
<li><a href="reference.html#osxphotos.ExportDB.get_files_for_uuid">get_files_for_uuid() (osxphotos.ExportDB method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.get_filter_values">get_filter_values() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.get_format_values">get_format_values() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.get_media_type">get_media_type() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosDB.get_photo">get_photo() (osxphotos.PhotosDB method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.get_photo_bool_attribute">get_photo_bool_attribute() (osxphotos.PhotoTemplate method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotoTemplate.get_photo_video_type">get_photo_video_type() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.ExportDB.get_photoinfo_for_uuid">get_photoinfo_for_uuid() (osxphotos.ExportDB method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.ExportDB.get_previous_uuids">get_previous_uuids() (osxphotos.ExportDB method)</a>
</li>
<li><a href="reference.html#osxphotos.ExportDB.get_target_for_file">get_target_for_file() (osxphotos.ExportDB method)</a>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos 0.49.9 documentation</title>
<title>osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="#"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -216,6 +216,7 @@
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#missing-photos">Missing photos</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#exporting-to-external-disks">Exporting to external disks</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#exporting-metadata-with-exported-photos">Exporting metadata with exported photos</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#removing-a-keyword-during-export">Removing a keyword during export</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#sidecar-files">Sidecar files</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#updating-a-previous-export">Updating a previous export</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#dry-run">Dry Run</a></li>

Binary file not shown.

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotoss documentation!" href="index.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos - osxphotos 0.49.9 documentation</title>
<title>OSXPhotos - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos python API" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Python Package Overview - osxphotos 0.49.9 documentation</title>
<title>OSXPhotos Python Package Overview - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.49.9 documentation</title>
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -145,7 +145,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="OSXPhotos Python Package Overview" href="package_overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos python API - osxphotos 0.49.9 documentation</title>
<title>OSXPhotos python API - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -1800,18 +1800,57 @@ Returns None if no associated RAW image</p>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">PhotoTemplate</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">photo</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">exiftool_path</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate" title="Permalink to this definition">#</a></dt>
<dd><p>PhotoTemplate class to render a template string from a PhotoInfo object</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.expand_variables">
<span class="sig-name descname"><span class="pre">expand_variables</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">value</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon"></span> <span class="sig-return-typehint"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.expand_variables"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.expand_variables" title="Permalink to this definition">#</a></dt>
<dd><p>Expand variables in value</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.expand_variables_to_str">
<span class="sig-name descname"><span class="pre">expand_variables_to_str</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">value</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">name</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon"></span> <span class="sig-return-typehint"><span class="pre">str</span></span></span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.expand_variables_to_str"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.expand_variables_to_str" title="Permalink to this definition">#</a></dt>
<dd><p>Expand variables in value and return a str of the expanded value.
Enforce that the expanded value is a single value, raises ValueError if not.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>value</strong> the value to expand</p></li>
<li><p><strong>name</strong> the name of the value being expanded (used in error messages)</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_field_values">
<span class="sig-name descname"><span class="pre">get_field_values</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">field</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">subfield</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">field_arg</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">default</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon"></span> <span class="sig-return-typehint"><span class="pre">Tuple</span><span class="p"><span class="pre">[</span></span><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_field_values"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_field_values" title="Permalink to this definition">#</a></dt>
<dd><p>Get the values for a field</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_filter_values">
<span class="sig-name descname"><span class="pre">get_filter_values</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filter_</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon"></span> <span class="sig-return-typehint"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_filter_values"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_filter_values" title="Permalink to this definition">#</a></dt>
<dd><p>Return filtered values</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_format_values">
<span class="sig-name descname"><span class="pre">get_format_values</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">field</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">subfield</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">default</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon"></span> <span class="sig-return-typehint"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_format_values"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_format_values" title="Permalink to this definition">#</a></dt>
<dd><p>Return values for {format} templates</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_media_type">
<span class="sig-name descname"><span class="pre">get_media_type</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">default</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_media_type"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_media_type" title="Permalink to this definition">#</a></dt>
<dd><p>return special media type, e.g. slow_mo, panorama, etc., defaults to photo or video if no special type</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_photo_bool_attribute">
<span class="sig-name descname"><span class="pre">get_photo_bool_attribute</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">attr</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">default</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">bool_val</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_photo_bool_attribute"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_photo_bool_attribute" title="Permalink to this definition">#</a></dt>
<dd><p>Return the boolean value for a photo attribute</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_photo_video_type">
<span class="sig-name descname"><span class="pre">get_photo_video_type</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">default</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_photo_video_type"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_photo_video_type" title="Permalink to this definition">#</a></dt>
<dd><p>return media type, e.g. photo or video</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_template_value">
<span class="sig-name descname"><span class="pre">get_template_value</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">field</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">default</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">subfield</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_template_value"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_template_value" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_template_value</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">field</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">default</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">subfield</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">field_arg</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_template_value"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_template_value" title="Permalink to this definition">#</a></dt>
<dd><p>lookup value for template field (single-value template substitutions)</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
@@ -1839,12 +1878,12 @@ Returns None if no associated RAW image</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_template_value_filter_function">
<span class="sig-name descname"><span class="pre">get_template_value_filter_function</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filter_</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">values</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_template_value_filter_function"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_template_value_filter_function" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_template_value_filter_function</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filter_</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">args</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">values</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_template_value_filter_function"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_template_value_filter_function" title="Permalink to this definition">#</a></dt>
<dd><p>Filter template value from external function</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoTemplate.get_template_value_function">
<span class="sig-name descname"><span class="pre">get_template_value_function</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">subfield</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_template_value_function"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_template_value_function" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_template_value_function</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">subfield</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">field_arg</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/phototemplate.html#PhotoTemplate.get_template_value_function"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoTemplate.get_template_value_function" title="Permalink to this definition">#</a></dt>
<dd><p>Get template value from external function</p>
</dd></dl>
<dl class="py method">

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.49.9 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.50.0 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -121,7 +121,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -144,7 +144,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Template System - osxphotos 0.49.9 documentation</title>
<title>OSXPhotos Template System - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -199,7 +199,7 @@
<p>The templating system converts one or template statements, written in osxphotos metadata templating language, to one or more rendered values using information from the photo being processed.</p>
<p>In its simplest form, a template statement has the form: <code class="docutils literal notranslate"><span class="pre">"{template_field}"</span></code>, for example <code class="docutils literal notranslate"><span class="pre">"{title}"</span></code> which would resolve to the title of the photo.</p>
<p>Template statements may contain one or more modifiers. The full syntax is:</p>
<p><code class="docutils literal notranslate"><span class="pre">"pretext{delim+template_field:subfield|filter(path_sep)[find,replace]</span> <span class="pre">conditional?bool_value,default}posttext"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">"pretext{delim+template_field:subfield(field_arg)|filter[find,replace]</span> <span class="pre">conditional?bool_value,default}posttext"</span></code></p>
<p>Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.</p>
<p><code class="docutils literal notranslate"><span class="pre">pretext</span></code> and <code class="docutils literal notranslate"><span class="pre">posttext</span></code> are free form text. For example, if a photo has title “My Photo Title” the template statement <code class="docutils literal notranslate"><span class="pre">"The</span> <span class="pre">title</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">{title}"</span></code>, resolves to <code class="docutils literal notranslate"><span class="pre">"The</span> <span class="pre">title</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">My</span> <span class="pre">Photo</span> <span class="pre">Title"</span></code>. The <code class="docutils literal notranslate"><span class="pre">pretext</span></code> in this example is <code class="docutils literal notranslate"><span class="pre">"The</span> <span class="pre">title</span> <span class="pre">if</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">"</span></code> and the template_field is <code class="docutils literal notranslate"><span class="pre">{title}</span></code>.</p>
<p><code class="docutils literal notranslate"><span class="pre">delim</span></code>: optional delimiter string to use when expanding multi-valued template values in-place</p>
@@ -213,6 +213,7 @@
</ul>
<p><code class="docutils literal notranslate"><span class="pre">template_field</span></code>: The template field to resolve. See <a class="reference external" href="#template-substitutions">Template Substitutions</a> for full list of template fields.</p>
<p><cite>:subfield</cite>: Some templates have sub-fields, For example, <cite>{exiftool:IPTC:Make}`</cite>; the template_field is``exiftool<code class="docutils literal notranslate"><span class="pre">and</span> <span class="pre">the</span> <span class="pre">sub-field</span> <span class="pre">is</span></code>IPTC:Make`.</p>
<p><code class="docutils literal notranslate"><span class="pre">(field_arg)</span></code>: optional arguments to pass to the field; for example, with <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> this is used to pass the path separator used for joining folders and albums when rendering the field (default is “/” for <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code>).</p>
<p><cite>|filter</cite>: You may optionally append one or more filter commands to the end of the template field using the vertical pipe (|) symbol. Filters may be combined, separated by | as in: <code class="docutils literal notranslate"><span class="pre">{keyword|capitalize|parens}</span></code>.</p>
<p>Valid filters are:</p>
<ul class="simple">
@@ -226,6 +227,18 @@
<li><p><code class="docutils literal notranslate"><span class="pre">brackets</span></code>: Enclose value in brackets, e.g. value =&gt; [value]</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">shell_quote</span></code>: Quotes the value for safe usage in the shell, e.g. My file.jpeg =&gt; My file.jpeg; only adds quotes if needed.</p></li>
<li><p><cite>function</cite>: Run custom python function to filter value; use in format function:/path/to/file.py::function_name. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">split(x)</span></code>: Split value into a list of values using x as delimiter, e.g. value1;value2 =&gt; [value1, value2] if used with split(;).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">autosplit</span></code>: Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. value1,value2 =&gt; [value1, value2].</p></li>
<li><p><cite>chop(x)</cite>: Remove x characters off the end of value, e.g. chop(1): Value =&gt; Valu; when applied to a list, chops characters from each list value, e.g. chop(1): [travel, beach]=&gt; [trave, beac].</p></li>
<li><p><cite>chomp(x)</cite>: Remove x characters from the beginning of value, e.g. chomp(1): [Value] =&gt; [alue]; when applied to a list, removes characters from each list value, e.g. chomp(1): [travel, beach]=&gt; [ravel, each].</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">sort</span></code>: Sort list of values, e.g. [c, b, a] =&gt; [a, b, c].</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">rsort</span></code>: Sort list of values in reverse order, e.g. [a, b, c] =&gt; [c, b, a].</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">reverse</span></code>: Reverse order of values, e.g. [a, b, c] =&gt; [c, b, a].</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">uniq</span></code>: Remove duplicate values, e.g. [a, b, c, b, a] =&gt; [a, b, c].</p></li>
<li><p><cite>join(x)</cite>: Join list of values with delimiter x, e.g. join(:): [a, b, c] =&gt; a:b:c; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.</p></li>
<li><p><cite>append(x)</cite>: Append x to list of values, e.g. append(d): [a, b, c] =&gt; [a, b, c, d].</p></li>
<li><p><cite>prepend(x)</cite>: Prepend x to list of values, e.g. prepend(d): [a, b, c] =&gt; [d, a, b, c].</p></li>
<li><p><cite>remove(x)</cite>: Remove x from list of values, e.g. remove(b): [a, b, c] =&gt; [a, c].</p></li>
</ul>
<p>e.g. if Photo keywords are <code class="docutils literal notranslate"><span class="pre">["FOO","bar"]</span></code>:</p>
<ul class="simple">
@@ -238,7 +251,6 @@
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{descr|titlecase}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"My</span> <span class="pre">Description"</span></code></p></li>
</ul>
<p><code class="docutils literal notranslate"><span class="pre">(path_sep)</span></code>: optional path separator to use when joining path-like fields, for example <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code>. Default is “/”.</p>
<p>e.g. If Photo is in <code class="docutils literal notranslate"><span class="pre">Album1</span></code> in <code class="docutils literal notranslate"><span class="pre">Folder1</span></code>:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{folder_album}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">["Folder1/Album1"]</span></code></p></li>
@@ -274,7 +286,7 @@
<p>This can be used to rename files as well, for example:
<code class="docutils literal notranslate"><span class="pre">--filename</span> <span class="pre">"{favorite?Favorite-{original_name},{original_name}}"</span></code></p>
<p>This renames any photo that is a favorite as Favorite-ImageName.jpg (where ImageName.jpg is the original name of the photo) and all other photos with the unmodified original name.</p>
<p><code class="docutils literal notranslate"><span class="pre">?bool_value</span></code>: Template fields may be evaluated as boolean (True/False) by appending “?” after the field name (and following “(path_sep)” or “[find/replace]”. If a field is True (e.g. photo is HDR and field is <code class="docutils literal notranslate"><span class="pre">"{hdr}"</span></code>) or has any value, the value following the “?” will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is <code class="docutils literal notranslate"><span class="pre">"{title}"</span></code>) then the default value following a “,” will be used.</p>
<p><code class="docutils literal notranslate"><span class="pre">?bool_value</span></code>: Template fields may be evaluated as boolean (True/False) by appending “?” after the field name (and following “(field_arg)” or “[find/replace]”. If a field is True (e.g. photo is HDR and field is <code class="docutils literal notranslate"><span class="pre">"{hdr}"</span></code>) or has any value, the value following the “?” will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is <code class="docutils literal notranslate"><span class="pre">"{title}"</span></code>) then the default value following a “,” will be used.</p>
<p>e.g. if photo is an HDR image,</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{hdr?ISHDR,NOTHDR}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"ISHDR"</span></code></p></li>
@@ -298,6 +310,11 @@
<p>Either or both bool_value or default (False value) may be empty which would result in empty string <code class="docutils literal notranslate"><span class="pre">""</span></code> when rendered.</p>
<p>If you want to include “{” or “}” in the output, use “{openbrace}” or “{closebrace}” template substitution.</p>
<p>e.g. <code class="docutils literal notranslate"><span class="pre">"{created.year}/{openbrace}{title}{closebrace}"</span></code> would result in <code class="docutils literal notranslate"><span class="pre">"2020/{Photo</span> <span class="pre">Title}"</span></code>.</p>
<p><strong>Variables</strong></p>
<p>You can define variables for later use in the template string using the format <code class="docutils literal notranslate"><span class="pre">{var:NAME,VALUE}</span></code>. Variables may then be referenced using the format <code class="docutils literal notranslate"><span class="pre">%NAME</span></code>. For example: <code class="docutils literal notranslate"><span class="pre">{var:foo,bar}</span></code> defines the variable <code class="docutils literal notranslate"><span class="pre">%foo</span></code> to have value <code class="docutils literal notranslate"><span class="pre">bar</span></code>. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the “pipe” (<code class="docutils literal notranslate"><span class="pre">|</span></code>) character is not allowed in a find/replace pair but you can get around this limitation like so: <code class="docutils literal notranslate"><span class="pre">{var:pipe,{pipe}}{title[-,%pipe]}</span></code> which replaces the <code class="docutils literal notranslate"><span class="pre">-</span></code> character with <code class="docutils literal notranslate"><span class="pre">|</span></code> (the value of <code class="docutils literal notranslate"><span class="pre">%pipe</span></code>).</p>
<p>Variables can also be referenced as fields in the template string, for example: <code class="docutils literal notranslate"><span class="pre">{var:year,created.year}{original_name}-{%year}</span></code>. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: <code class="docutils literal notranslate"><span class="pre">{var:name,Katie}{person</span> <span class="pre">contains</span> <span class="pre">{%name}?{%name},Not-{%name}}</span></code>.</p>
<p>If you need to use a <code class="docutils literal notranslate"><span class="pre">%</span></code> (percent sign character), you can escape the percent sign by using <code class="docutils literal notranslate"><span class="pre">%%</span></code>. You can also use the <code class="docutils literal notranslate"><span class="pre">{percent}</span></code> template field where a template field is required. For example:</p>
<p><code class="docutils literal notranslate"><span class="pre">{title[:,%%]}</span></code> replaces the <code class="docutils literal notranslate"><span class="pre">:</span></code> with <code class="docutils literal notranslate"><span class="pre">%</span></code> and <code class="docutils literal notranslate"><span class="pre">{title</span> <span class="pre">contains</span> <span class="pre">Foo?{title}{percent},{title}}</span></code> adds <code class="docutils literal notranslate"><span class="pre">%</span></code> to the title if it contains <code class="docutils literal notranslate"><span class="pre">Foo</span></code>.</p>
<section id="id1">
<h2>Template Substitutions<a class="headerlink" href="#id1" title="Permalink to this headline">#</a></h2>
<div class="table-wrapper"><table class="docutils align-default">
@@ -519,10 +536,10 @@
<td><p>A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3…etc. Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {id:05d} which results in 00001, 00002, 00003…etc.</p></td>
</tr>
<tr class="row-odd"><td><p>{album_seq}</p></td>
<td><p>An integer, starting at 0, indicating the photos index (sequence) in the containing album. Only valid when used in a filename template and only when {album} or {folder_album} is used in the directory template. For example directory “{folder_album}” filename “{album<em>seq}</em>{original_name}”’. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: {album_seq.1}. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {album_seq:05d} which results in 00000, 00001, 00002…etc. This may result in incorrect sequences if you have duplicate albums with the same name; see also {folder_album_seq}.</p></td>
<td><p>An integer, starting at 0, indicating the photos index (sequence) in the containing album. Only valid when used in a filename template and only when {album} or {folder_album} is used in the directory template. For example directory “{folder_album}” filename “{album<em>seq}</em>{original_name}”’. To start counting at a value other than 0, append append (starting_value) to the field name. For example, to start counting at 1 instead of 0: {album_seq(1)}. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {album_seq:05d} which results in 00000, 00001, 00002…etc. To format while also using a starting value: {album_seq:05d(1)} which results in 0001, 00002…etc.This may result in incorrect sequences if you have duplicate albums with the same name; see also {folder_album_seq}.</p></td>
</tr>
<tr class="row-even"><td><p>{folder_album_seq}</p></td>
<td><p>An integer, starting at 0, indicating the photos index (sequence) in the containing album and folder path. Only valid when used in a filename template and only when {folder_album} is used in the directory template. For example directory “{folder_album}” filename “{folder_album<em>seq}</em>{original_name}”’. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: {folder_album_seq.1} May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {folder_album_seq:05d} which results in 00000, 00001, 00002…etc. This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also {album_seq}.</p></td>
<td><p>An integer, starting at 0, indicating the photos index (sequence) in the containing album and folder path. Only valid when used in a filename template and only when {folder_album} is used in the directory template. For example directory “{folder_album}” filename “{folder_album<em>seq}</em>{original_name}”’. To start counting at a value other than 0, append (starting_value) to the field name. For example, to start counting at 1 instead of 0: {folder_album_seq(1)} May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {folder_album_seq:05d} which results in 00000, 00001, 00002…etc. To format while also using a starting value: {folder_album_seq:05d(1)} which results in 0001, 00002…etc.This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also {album_seq}.</p></td>
</tr>
<tr class="row-odd"><td><p>{comma}</p></td>
<td><p>A comma: ,</p></td>
@@ -567,7 +584,7 @@
<td><p>a carriage return + line feed: rn</p></td>
</tr>
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
<td><p>The osxphotos version, e.g. 0.49.9</p></td>
<td><p>The osxphotos version, e.g. 0.50.0</p></td>
</tr>
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
<td><p>The full command line used to run osxphotos</p></td>
@@ -629,7 +646,10 @@
<tr class="row-odd"><td><p>{strip}</p></td>
<td><p>Use in form {strip,TEMPLATE}; strips whitespace from begining and end of rendered TEMPLATE value(s).</p></td>
</tr>
<tr class="row-even"><td><p>{function}</p></td>
<tr class="row-even"><td><p>{format}</p></td>
<td><p>Use in form, {format:TYPE:FORMAT,TEMPLATE}; converts TEMPLATE value to TYPE then formats the value using Python string formatting codes specified by FORMAT; TYPE is one of: int, float, or str. For example, {format:float:.1f,{exiftool:EXIF:FocalLength}} will format focal length to 1 decimal place (e.g. 100.0).</p></td>
</tr>
<tr class="row-odd"><td><p>{function}</p></td>
<td><p>Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where file.py is the name of the python file and function_name is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.</p></td>
</tr>
</tbody>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Tutorial - osxphotos 0.49.9 documentation</title>
<title>OSXPhotos Tutorial - osxphotos 0.50.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.49.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.49.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -331,6 +331,12 @@
<p>The above command will write all the regular metadata that <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form “Folder1&gt;Folder2&gt;Album”. If you did not include the <code class="docutils literal notranslate"><span class="pre">(&gt;)</span></code> in the template string (e.g. <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code>), folder_album would render in form “Folder1/Folder2/Album”.</p>
<p>A powerful feature of Photos is that it uses machine learning algorithms to automatically classify or label photos. These labels are used when you search for images in Photos but are not otherwise available to the user. osxphotos is able to read all the labels associated with a photo and makes those available through the template system via the <code class="docutils literal notranslate"><span class="pre">{label}</span></code>. Think of these as automatic keywords as opposed to the keywords you assign manually in Photos. One common use case is to use the automatic labels to create new keywords when exporting images so that these labels are embedded in the images metadata:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">"{label}"</span></code></p>
</section>
<section id="removing-a-keyword-during-export">
<h2>Removing a keyword during export<a class="headerlink" href="#removing-a-keyword-during-export" title="Permalink to this headline">#</a></h2>
<p>If some of your photos contain a keyword you do not want to be added to the exported file with <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code>, you can use the template system to remove the keyword from the exported file. For example, if you want to remove the keyword “MyKeyword” from all your photos:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">"{keyword|remove(MyKeyword)}"</span> <span class="pre">--replace-keywords</span></code></p>
<p>In this example, <code class="docutils literal notranslate"><span class="pre">|remove(MyKeyword)</span></code> is a filter which removes <code class="docutils literal notranslate"><span class="pre">MyKeyword</span></code> from the keyword list of every photo being processed. The <code class="docutils literal notranslate"><span class="pre">--replace-keywords</span></code> option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code>.</p>
<p><strong>Note</strong>: When evaluating templates for <code class="docutils literal notranslate"><span class="pre">--directory</span></code> and <code class="docutils literal notranslate"><span class="pre">--filename</span></code>, osxphotos inserts the automatic default value “_” for any template field which is null (empty or blank). This is to ensure that theres never a null directory or filename created. For metadata templates such as <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code>, osxphotos does not provide an automatic default value thus if the template field is null, no keyword would be created. Of course, you can provide a default value if desired and osxphotos will use this. For example, to add “nolabel” as a keyword for any photo that doesnt have labels:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">"{label,nolabel}"</span></code></p>
</section>
@@ -568,6 +574,7 @@ template fields.
<li><a class="reference internal" href="#missing-photos">Missing photos</a></li>
<li><a class="reference internal" href="#exporting-to-external-disks">Exporting to external disks</a></li>
<li><a class="reference internal" href="#exporting-metadata-with-exported-photos">Exporting metadata with exported photos</a></li>
<li><a class="reference internal" href="#removing-a-keyword-during-export">Removing a keyword during export</a></li>
<li><a class="reference internal" href="#sidecar-files">Sidecar files</a></li>
<li><a class="reference internal" href="#updating-a-previous-export">Updating a previous export</a></li>
<li><a class="reference internal" href="#dry-run">Dry Run</a></li>

View File

@@ -8,7 +8,7 @@ In its simplest form, a template statement has the form: ``"{template_field}"``\
Template statements may contain one or more modifiers. The full syntax is:
``"pretext{delim+template_field:subfield|filter(path_sep)[find,replace] conditional?bool_value,default}posttext"``
``"pretext{delim+template_field:subfield(field_arg)|filter[find,replace] conditional?bool_value,default}posttext"``
Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.
@@ -30,6 +30,8 @@ e.g. if Photo keywords are ``["foo","bar"]``\ :
`:subfield`: Some templates have sub-fields, For example, `{exiftool:IPTC:Make}\ ``; the template_field is``\ exiftool\ ``and the sub-field is``\ IPTC:Make`.
``(field_arg)``\ : optional arguments to pass to the field; for example, with ``{folder_album}`` this is used to pass the path separator used for joining folders and albums when rendering the field (default is "/" for ``{folder_album}``\ ).
`|filter`: You may optionally append one or more filter commands to the end of the template field using the vertical pipe ('|') symbol. Filters may be combined, separated by '|' as in: ``{keyword|capitalize|parens}``.
Valid filters are:
@@ -45,6 +47,18 @@ Valid filters are:
* ``brackets``\ : Enclose value in brackets, e.g. 'value' => '[value]'
* ``shell_quote``\ : Quotes the value for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
* `function`: Run custom python function to filter value; use in format 'function:/path/to/file.py::function_name'. See example at https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter.py
* ``split(x)``\ : Split value into a list of values using x as delimiter, e.g. 'value1;value2' => ['value1', 'value2'] if used with split(;).
* ``autosplit``\ : Automatically split delimited string into separate values; will split strings delimited by comma, semicolon, or space, e.g. 'value1,value2' => ['value1', 'value2'].
* `chop(x)`: Remove x characters off the end of value, e.g. chop(1): 'Value' => 'Valu'; when applied to a list, chops characters from each list value, e.g. chop(1): ['travel', 'beach']=> ['trave', 'beac'].
* `chomp(x)`: Remove x characters from the beginning of value, e.g. chomp(1): ['Value'] => ['alue']; when applied to a list, removes characters from each list value, e.g. chomp(1): ['travel', 'beach']=> ['ravel', 'each'].
* ``sort``\ : Sort list of values, e.g. ['c', 'b', 'a'] => ['a', 'b', 'c'].
* ``rsort``\ : Sort list of values in reverse order, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
* ``reverse``\ : Reverse order of values, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
* ``uniq``\ : Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] => ['a', 'b', 'c'].
* `join(x)`: Join list of values with delimiter x, e.g. join(:): ['a', 'b', 'c'] => 'a:b:c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.
* `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
* `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
* `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
e.g. if Photo keywords are ``["FOO","bar"]``\ :
@@ -59,8 +73,6 @@ e.g. if Photo description is "my description":
* ``"{descr|titlecase}"`` renders to: ``"My Description"``
``(path_sep)``\ : optional path separator to use when joining path-like fields, for example ``{folder_album}``. Default is "/".
e.g. If Photo is in ``Album1`` in ``Folder1``\ :
@@ -106,7 +118,7 @@ This can be used to rename files as well, for example:
This renames any photo that is a favorite as 'Favorite-ImageName.jpg' (where 'ImageName.jpg' is the original name of the photo) and all other photos with the unmodified original name.
``?bool_value``\ : Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(path_sep)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is ``"{hdr}"``\ ) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is ``"{title}"``\ ) then the default value following a "," will be used.
``?bool_value``\ : Template fields may be evaluated as boolean (True/False) by appending "?" after the field name (and following "(field_arg)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is ``"{hdr}"``\ ) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is ``"{title}"``\ ) then the default value following a "," will be used.
e.g. if photo is an HDR image,
@@ -141,6 +153,16 @@ If you want to include "{" or "}" in the output, use "{openbrace}" or "{closebra
e.g. ``"{created.year}/{openbrace}{title}{closebrace}"`` would result in ``"2020/{Photo Title}"``.
**Variables**
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}``. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
Variables can also be referenced as fields in the template string, for example: ``{var:year,created.year}{original_name}-{%year}``. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
If you need to use a ``%`` (percent sign character), you can escape the percent sign by using ``%%``. You can also use the ``{percent}`` template field where a template field is required. For example:
``{title[:,%%]}`` replaces the ``:`` with ``%`` and ``{title contains Foo?{title}{percent},{title}}`` adds ``%`` to the title if it contains ``Foo``.
Template Substitutions
----------------------
@@ -288,9 +310,9 @@ Template Substitutions
* - {id}
- A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3...etc. Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{id:05d}' which results in 00001, 00002, 00003...etc.
* - {album_seq}
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{album_seq.1}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album. Only valid when used in a '--filename' template and only when '{album}' or '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{album_seq(1)}'. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name; see also '{folder_album_seq}'.
* - {folder_album_seq}
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append append a period and the starting value to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq.1}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'.
- An integer, starting at 0, indicating the photo's index (sequence) in the containing album and folder path. Only valid when used in a '--filename' template and only when '{folder_album}' is used in the '--directory' template. For example '--directory "{folder_album}" --filename "{folder_album\ *seq}*\ {original_name}"'. To start counting at a value other than 0, append '(starting_value)' to the field name. For example, to start counting at 1 instead of 0: '{folder_album_seq(1)}' May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{folder_album_seq:05d}' which results in 00000, 00001, 00002...etc. To format while also using a starting value: '{folder_album_seq:05d(1)}' which results in 0001, 00002...etc.This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also '{album_seq}'.
* - {comma}
- A comma: ','
* - {semicolon}
@@ -320,7 +342,7 @@ Template Substitutions
* - {crlf}
- a carriage return + line feed: '\r\n'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.49.9'
- The osxphotos version, e.g. '0.50.0'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}
@@ -361,6 +383,8 @@ Template Substitutions
- Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
* - {strip}
- Use in form '{strip,TEMPLATE}'; strips whitespace from begining and end of rendered TEMPLATE value(s).
* - {format}
- Use in form, '{format:TYPE:FORMAT,TEMPLATE}'; converts TEMPLATE value to TYPE then formats the value using Python string formatting codes specified by FORMAT; TYPE is one of: 'int', 'float', or 'str'. For example, '{format:float:.1f,{exiftool:EXIF:FocalLength}}' will format focal length to 1 decimal place (e.g. '100.0').
* - {function}
- Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.

View File

@@ -218,6 +218,15 @@ A powerful feature of Photos is that it uses machine learning algorithms to auto
``osxphotos export /path/to/export --exiftool --keyword-template "{label}"``
Removing a keyword during export
--------------------------------
If some of your photos contain a keyword you do not want to be added to the exported file with ``--exiftool``\ , you can use the template system to remove the keyword from the exported file. For example, if you want to remove the keyword "MyKeyword" from all your photos:
``osxphotos export /path/to/export --exiftool --keyword-template "{keyword|remove(MyKeyword)}" --replace-keywords``
In this example, ``|remove(MyKeyword)`` is a filter which removes ``MyKeyword`` from the keyword list of every photo being processed. The ``--replace-keywords`` option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from ``--keyword-template``.
**Note**\ : When evaluating templates for ``--directory`` and ``--filename``\ , osxphotos inserts the automatic default value "_" for any template field which is null (empty or blank). This is to ensure that there's never a null directory or filename created. For metadata templates such as ``--keyword-template``\ , osxphotos does not provide an automatic default value thus if the template field is null, no keyword would be created. Of course, you can provide a default value if desired and osxphotos will use this. For example, to add "nolabel" as a keyword for any photo that doesn't have labels:
``osxphotos export /path/to/export --exiftool --keyword-template "{label,nolabel}"``

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.49.9"
__version__ = "0.50.0"

Binary file not shown.

View File

@@ -178,6 +178,14 @@ A powerful feature of Photos is that it uses machine learning algorithms to auto
`osxphotos export /path/to/export --exiftool --keyword-template "{label}"`
## Removing a keyword during export
If some of your photos contain a keyword you do not want to be added to the exported file with `--exiftool`, you can use the template system to remove the keyword from the exported file. For example, if you want to remove the keyword "MyKeyword" from all your photos:
`osxphotos export /path/to/export --exiftool --keyword-template "{keyword|remove(MyKeyword)}" --replace-keywords`
In this example, `|remove(MyKeyword)` is a filter which removes `MyKeyword` from the keyword list of every photo being processed. The `--replace-keywords` option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from `--keyword-template`.
**Note**: When evaluating templates for `--directory` and `--filename`, osxphotos inserts the automatic default value "_" for any template field which is null (empty or blank). This is to ensure that there's never a null directory or filename created. For metadata templates such as `--keyword-template`, osxphotos does not provide an automatic default value thus if the template field is null, no keyword would be created. Of course, you can provide a default value if desired and osxphotos will use this. For example, to add "nolabel" as a keyword for any photo that doesn't have labels:
`osxphotos export /path/to/export --exiftool --keyword-template "{label,nolabel}"`