Fix for issue #263
This commit is contained in:
30
README.md
30
README.md
@@ -374,20 +374,24 @@ Options:
|
|||||||
do not include an extension in the FILENAME
|
do not include an extension in the FILENAME
|
||||||
template. See below for additional details
|
template. See below for additional details
|
||||||
on templating system.
|
on templating system.
|
||||||
--edited-suffix SUFFIX Optional suffix for naming edited photos.
|
--edited-suffix SUFFIX Optional suffix template for naming edited
|
||||||
Default name for edited photos is in form
|
photos. Default name for edited photos is
|
||||||
'photoname_edited.ext'. For example, with '
|
in form 'photoname_edited.ext'. For example,
|
||||||
--edited-suffix _bearbeiten', the edited
|
with '--edited-suffix _bearbeiten', the
|
||||||
photo would be named
|
edited photo would be named
|
||||||
'photoname_bearbeiten.ext'. The default
|
'photoname_bearbeiten.ext'. The default
|
||||||
suffix is '_edited'.
|
suffix is '_edited'. Multi-value templates
|
||||||
--original-suffix SUFFIX Optional suffix for naming original photos.
|
(see Templating System) are not permitted
|
||||||
Default name for original photos is in form
|
with --edited-suffix.
|
||||||
'filename.ext'. For example, with '--
|
--original-suffix SUFFIX Optional suffix template for naming original
|
||||||
original-suffix _original', the original
|
photos. Default name for original photos is
|
||||||
|
in form 'filename.ext'. For example, with '
|
||||||
|
--original-suffix _original', the original
|
||||||
photo would be named
|
photo would be named
|
||||||
'filename_original.ext'. The default suffix
|
'filename_original.ext'. The default suffix
|
||||||
is '' (no suffix).
|
is '' (no suffix). Multi-value templates
|
||||||
|
(see Templating System) are not permitted
|
||||||
|
with --original-suffix.
|
||||||
--use-photos-export Force the use of AppleScript or PhotoKit to
|
--use-photos-export Force the use of AppleScript or PhotoKit to
|
||||||
export even if not missing (see also '--
|
export even if not missing (see also '--
|
||||||
download-missing' and '--use-photokit').
|
download-missing' and '--use-photokit').
|
||||||
@@ -569,6 +573,9 @@ Substitution Description
|
|||||||
'{photo_or_video,photo=fotos;video=videos}'
|
'{photo_or_video,photo=fotos;video=videos}'
|
||||||
{hdr} Photo is HDR?; True/False value, use in
|
{hdr} Photo is HDR?; True/False value, use in
|
||||||
format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'
|
format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'
|
||||||
|
{edited} Photo has been edited (has adjustments)?;
|
||||||
|
True/False value, use in format
|
||||||
|
'{edited?VALUE_IF_TRUE,VALUE_IF_FALSE}'
|
||||||
{created.date} Photo's creation date in ISO format, e.g.
|
{created.date} Photo's creation date in ISO format, e.g.
|
||||||
'2020-03-22'
|
'2020-03-22'
|
||||||
{created.year} 4-digit year of photo creation time
|
{created.year} 4-digit year of photo creation time
|
||||||
@@ -2041,6 +2048,7 @@ The following template field substitutions are availabe for use with `PhotoInfo.
|
|||||||
|{media_type}|Special media type resolved in this precedence: selfie, time_lapse, panorama, slow_mo, screenshot, portrait, live_photo, burst, photo, video. Defaults to 'photo' or 'video' if no special type. Customize one or more media types using format: '{media_type,video=vidéo;time_lapse=vidéo_accélérée}'|
|
|{media_type}|Special media type resolved in this precedence: selfie, time_lapse, panorama, slow_mo, screenshot, portrait, live_photo, burst, photo, video. Defaults to 'photo' or 'video' if no special type. Customize one or more media types using format: '{media_type,video=vidéo;time_lapse=vidéo_accélérée}'|
|
||||||
|{photo_or_video}|'photo' or 'video' depending on what type the image is. To customize, use default value as in '{photo_or_video,photo=fotos;video=videos}'|
|
|{photo_or_video}|'photo' or 'video' depending on what type the image is. To customize, use default value as in '{photo_or_video,photo=fotos;video=videos}'|
|
||||||
|{hdr}|Photo is HDR?; True/False value, use in format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|
|{hdr}|Photo is HDR?; True/False value, use in format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|
||||||
|
|{edited}|Photo has been edited (has adjustments)?; True/False value, use in format '{edited?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|
||||||
|{created.date}|Photo's creation date in ISO format, e.g. '2020-03-22'|
|
|{created.date}|Photo's creation date in ISO format, e.g. '2020-03-22'|
|
||||||
|{created.year}|4-digit year of photo creation time|
|
|{created.year}|4-digit year of photo creation time|
|
||||||
|{created.yy}|2-digit year of photo creation time|
|
|{created.yy}|2-digit year of photo creation time|
|
||||||
|
|||||||
@@ -1402,16 +1402,18 @@ def query(
|
|||||||
@click.option(
|
@click.option(
|
||||||
"--edited-suffix",
|
"--edited-suffix",
|
||||||
metavar="SUFFIX",
|
metavar="SUFFIX",
|
||||||
help="Optional suffix for naming edited photos. Default name for edited photos is in form "
|
help="Optional suffix template for naming edited photos. Default name for edited photos is in form "
|
||||||
"'photoname_edited.ext'. For example, with '--edited-suffix _bearbeiten', the edited photo "
|
"'photoname_edited.ext'. For example, with '--edited-suffix _bearbeiten', the edited photo "
|
||||||
f"would be named 'photoname_bearbeiten.ext'. The default suffix is '{DEFAULT_EDITED_SUFFIX}'.",
|
f"would be named 'photoname_bearbeiten.ext'. The default suffix is '{DEFAULT_EDITED_SUFFIX}'. "
|
||||||
|
"Multi-value templates (see Templating System) are not permitted with --edited-suffix.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--original-suffix",
|
"--original-suffix",
|
||||||
metavar="SUFFIX",
|
metavar="SUFFIX",
|
||||||
help="Optional suffix for naming original photos. Default name for original photos is in form "
|
help="Optional suffix template for naming original photos. Default name for original photos is in form "
|
||||||
"'filename.ext'. For example, with '--original-suffix _original', the original photo "
|
"'filename.ext'. For example, with '--original-suffix _original', the original photo "
|
||||||
"would be named 'filename_original.ext'. The default suffix is '' (no suffix).",
|
"would be named 'filename_original.ext'. The default suffix is '' (no suffix). "
|
||||||
|
"Multi-value templates (see Templating System) are not permitted with --original-suffix.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--use-photos-export",
|
"--use-photos-export",
|
||||||
@@ -1578,8 +1580,6 @@ def export(
|
|||||||
ignore=["ctx", "cli_obj", "dest", "load_config", "save_config"],
|
ignore=["ctx", "cli_obj", "dest", "load_config", "save_config"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# print(jpeg_quality, edited_suffix, original_suffix)
|
|
||||||
|
|
||||||
global VERBOSE
|
global VERBOSE
|
||||||
VERBOSE = bool(verbose)
|
VERBOSE = bool(verbose)
|
||||||
|
|
||||||
@@ -1716,11 +1716,7 @@ def export(
|
|||||||
("jpeg_quality", ("convert_to_jpeg")),
|
("jpeg_quality", ("convert_to_jpeg")),
|
||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
cfg.validate(
|
cfg.validate(exclusive=exclusive_options, dependent=dependent_options, cli=True)
|
||||||
exclusive=exclusive_options,
|
|
||||||
dependent=dependent_options,
|
|
||||||
cli=True,
|
|
||||||
)
|
|
||||||
except ConfigOptionsInvalidError as e:
|
except ConfigOptionsInvalidError as e:
|
||||||
click.echo(f"Incompatible export options: {e.message}", err=True)
|
click.echo(f"Incompatible export options: {e.message}", err=True)
|
||||||
raise click.Abort()
|
raise click.Abort()
|
||||||
@@ -1736,8 +1732,6 @@ def export(
|
|||||||
DEFAULT_ORIGINAL_SUFFIX if original_suffix is None else original_suffix
|
DEFAULT_ORIGINAL_SUFFIX if original_suffix is None else original_suffix
|
||||||
)
|
)
|
||||||
|
|
||||||
# print(jpeg_quality, edited_suffix, original_suffix)
|
|
||||||
|
|
||||||
if not os.path.isdir(dest):
|
if not os.path.isdir(dest):
|
||||||
click.echo(f"DEST {dest} must be valid path", err=True)
|
click.echo(f"DEST {dest} must be valid path", err=True)
|
||||||
raise click.Abort()
|
raise click.Abort()
|
||||||
@@ -2693,10 +2687,25 @@ def export_photo(
|
|||||||
filenames = get_filenames_from_template(photo, filename_template, original_name)
|
filenames = get_filenames_from_template(photo, filename_template, original_name)
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if original_suffix:
|
if original_suffix:
|
||||||
|
rendered_suffix, unmatched = photo.render_template(
|
||||||
|
original_suffix, filename=True
|
||||||
|
)
|
||||||
|
if not rendered_suffix or unmatched:
|
||||||
|
raise click.BadOptionUsage(
|
||||||
|
"original_suffix",
|
||||||
|
f"Invalid template for --original-suffix '{original_suffix}': results={rendered_suffix} unmatched={unmatched}",
|
||||||
|
)
|
||||||
|
if len(rendered_suffix) > 1:
|
||||||
|
raise click.BadOptionUsage(
|
||||||
|
"original_suffix",
|
||||||
|
f"Invalid template for --original-suffix: may not use multi-valued templates: '{original_suffix}': results={rendered_suffix}",
|
||||||
|
)
|
||||||
|
rendered_suffix = rendered_suffix[0]
|
||||||
|
|
||||||
original_filename = pathlib.Path(filename)
|
original_filename = pathlib.Path(filename)
|
||||||
original_filename = (
|
original_filename = (
|
||||||
original_filename.parent
|
original_filename.parent
|
||||||
/ f"{original_filename.stem}{original_suffix}{original_filename.suffix}"
|
/ f"{original_filename.stem}{rendered_suffix}{original_filename.suffix}"
|
||||||
)
|
)
|
||||||
original_filename = str(original_filename)
|
original_filename = str(original_filename)
|
||||||
else:
|
else:
|
||||||
@@ -2813,7 +2822,30 @@ def export_photo(
|
|||||||
# use filename suffix which might be wrong,
|
# use filename suffix which might be wrong,
|
||||||
# will be corrected by use_photos_export
|
# will be corrected by use_photos_export
|
||||||
edited_ext = pathlib.Path(photo.filename).suffix
|
edited_ext = pathlib.Path(photo.filename).suffix
|
||||||
edited_filename = f"{edited_filename.stem}{edited_suffix}{edited_ext}"
|
|
||||||
|
if edited_suffix:
|
||||||
|
rendered_suffix, unmatched = photo.render_template(
|
||||||
|
edited_suffix, filename=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rendered_suffix or unmatched:
|
||||||
|
raise click.BadOptionUsage(
|
||||||
|
"edited_suffix",
|
||||||
|
f"Invalid template for --edited-suffix '{edited_suffix}': results={rendered_suffix} unmatched={unmatched}",
|
||||||
|
)
|
||||||
|
if len(rendered_suffix) > 1:
|
||||||
|
raise click.BadOptionUsage(
|
||||||
|
"edited_suffix",
|
||||||
|
f"Invalid template for --edited-suffix: may not use multi-valued templates: '{edited_suffix}': results={rendered_suffix}",
|
||||||
|
)
|
||||||
|
rendered_suffix = rendered_suffix[0]
|
||||||
|
|
||||||
|
edited_filename = (
|
||||||
|
f"{edited_filename.stem}{rendered_suffix}{edited_ext}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
edited_filename = f"{edited_filename.stem}{edited_ext}"
|
||||||
|
|
||||||
verbose_(
|
verbose_(
|
||||||
f"Exporting edited version of {photo.original_filename} ({photo.filename}) as {edited_filename}"
|
f"Exporting edited version of {photo.original_filename} ({photo.filename}) as {edited_filename}"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.38.3"
|
__version__ = "0.38.4"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ TEMPLATE_SUBSTITUTIONS = {
|
|||||||
),
|
),
|
||||||
"{photo_or_video}": "'photo' or 'video' depending on what type the image is. To customize, use default value as in '{photo_or_video,photo=fotos;video=videos}'",
|
"{photo_or_video}": "'photo' or 'video' depending on what type the image is. To customize, use default value as in '{photo_or_video,photo=fotos;video=videos}'",
|
||||||
"{hdr}": "Photo is HDR?; True/False value, use in format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'",
|
"{hdr}": "Photo is HDR?; True/False value, use in format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'",
|
||||||
|
"{edited}": "Photo has been edited (has adjustments)?; True/False value, use in format '{edited?VALUE_IF_TRUE,VALUE_IF_FALSE}'",
|
||||||
"{created.date}": "Photo's creation date in ISO format, e.g. '2020-03-22'",
|
"{created.date}": "Photo's creation date in ISO format, e.g. '2020-03-22'",
|
||||||
"{created.year}": "4-digit year of photo creation time",
|
"{created.year}": "4-digit year of photo creation time",
|
||||||
"{created.yy}": "2-digit year of photo creation time",
|
"{created.yy}": "2-digit year of photo creation time",
|
||||||
@@ -632,7 +633,9 @@ class PhotoTemplate:
|
|||||||
elif field == "photo_or_video":
|
elif field == "photo_or_video":
|
||||||
value = self.get_photo_video_type(default)
|
value = self.get_photo_video_type(default)
|
||||||
elif field == "hdr":
|
elif field == "hdr":
|
||||||
value = self.get_photo_hdr(default, bool_val)
|
value = self.get_photo_bool_attribute("hdr", default, bool_val)
|
||||||
|
elif field == "edited":
|
||||||
|
value = self.get_photo_bool_attribute("hasadjustments", default, bool_val)
|
||||||
elif field == "created.date":
|
elif field == "created.date":
|
||||||
value = DateTimeFormatter(self.photo.date).date
|
value = DateTimeFormatter(self.photo.date).date
|
||||||
elif field == "created.year":
|
elif field == "created.year":
|
||||||
@@ -962,8 +965,10 @@ class PhotoTemplate:
|
|||||||
else:
|
else:
|
||||||
return default_dict["photo"]
|
return default_dict["photo"]
|
||||||
|
|
||||||
def get_photo_hdr(self, default, bool_val):
|
def get_photo_bool_attribute(self, attr, default, bool_val):
|
||||||
if self.photo.hdr:
|
# get value for a PhotoInfo bool attribute
|
||||||
|
val = getattr(self.photo, attr)
|
||||||
|
if val:
|
||||||
return bool_val
|
return bool_val
|
||||||
else:
|
else:
|
||||||
return default
|
return default
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ CLI_EXPORT_FILENAMES_ALBUM_UNICODE = ["IMG_4547.jpg"]
|
|||||||
CLI_EXPORT_FILENAMES_DELETED_TWIN = ["wedding.jpg", "wedding_edited.jpeg"]
|
CLI_EXPORT_FILENAMES_DELETED_TWIN = ["wedding.jpg", "wedding_edited.jpeg"]
|
||||||
|
|
||||||
CLI_EXPORT_EDITED_SUFFIX = "_bearbeiten"
|
CLI_EXPORT_EDITED_SUFFIX = "_bearbeiten"
|
||||||
|
CLI_EXPORT_EDITED_SUFFIX_TEMPLATE = "{edited?_edited,}"
|
||||||
CLI_EXPORT_ORIGINAL_SUFFIX = "_original"
|
CLI_EXPORT_ORIGINAL_SUFFIX = "_original"
|
||||||
|
CLI_EXPORT_ORIGINAL_SUFFIX_TEMPLATE = "{edited?_original,}"
|
||||||
|
|
||||||
CLI_EXPORT_FILENAMES_EDITED_SUFFIX = [
|
CLI_EXPORT_FILENAMES_EDITED_SUFFIX = [
|
||||||
"Pumkins1.jpg",
|
"Pumkins1.jpg",
|
||||||
@@ -79,6 +81,17 @@ CLI_EXPORT_FILENAMES_EDITED_SUFFIX = [
|
|||||||
"wedding_bearbeiten.jpeg",
|
"wedding_bearbeiten.jpeg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CLI_EXPORT_FILENAMES_EDITED_SUFFIX_TEMPLATE = [
|
||||||
|
"Pumkins1.jpg",
|
||||||
|
"Pumkins2.jpg",
|
||||||
|
"Pumpkins3.jpg",
|
||||||
|
"St James Park.jpg",
|
||||||
|
"St James Park_edited.jpeg",
|
||||||
|
"Tulips.jpg",
|
||||||
|
"wedding.jpg",
|
||||||
|
"wedding_edited.jpeg",
|
||||||
|
]
|
||||||
|
|
||||||
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX = [
|
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX = [
|
||||||
"Pumkins1_original.jpg",
|
"Pumkins1_original.jpg",
|
||||||
"Pumkins2_original.jpg",
|
"Pumkins2_original.jpg",
|
||||||
@@ -90,6 +103,17 @@ CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX = [
|
|||||||
"wedding_edited.jpeg",
|
"wedding_edited.jpeg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX_TEMPLATE = [
|
||||||
|
"Pumkins1.jpg",
|
||||||
|
"Pumkins2.jpg",
|
||||||
|
"Pumpkins3.jpg",
|
||||||
|
"St James Park_original.jpg",
|
||||||
|
"St James Park_edited.jpeg",
|
||||||
|
"Tulips.jpg",
|
||||||
|
"wedding_original.jpg",
|
||||||
|
"wedding_edited.jpeg",
|
||||||
|
]
|
||||||
|
|
||||||
CLI_EXPORT_FILENAMES_CURRENT = [
|
CLI_EXPORT_FILENAMES_CURRENT = [
|
||||||
"1EB2B765-0765-43BA-A90C-0D0580E6172C.jpeg",
|
"1EB2B765-0765-43BA-A90C-0D0580E6172C.jpeg",
|
||||||
"3DD2C897-F19E-4CA6-8C22-B027D5A71907.jpeg",
|
"3DD2C897-F19E-4CA6-8C22-B027D5A71907.jpeg",
|
||||||
@@ -1087,6 +1111,33 @@ def test_export_edited_suffix():
|
|||||||
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_EDITED_SUFFIX)
|
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_EDITED_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_edited_suffix_template():
|
||||||
|
""" test export with --edited-suffix template """
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
# pylint: disable=not-context-manager
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||||
|
".",
|
||||||
|
"--edited-suffix",
|
||||||
|
CLI_EXPORT_EDITED_SUFFIX_TEMPLATE,
|
||||||
|
"-V",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
files = glob.glob("*")
|
||||||
|
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_EDITED_SUFFIX_TEMPLATE)
|
||||||
|
|
||||||
|
|
||||||
def test_export_original_suffix():
|
def test_export_original_suffix():
|
||||||
""" test export with --original-suffix """
|
""" test export with --original-suffix """
|
||||||
import glob
|
import glob
|
||||||
@@ -1114,6 +1165,33 @@ def test_export_original_suffix():
|
|||||||
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX)
|
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_original_suffix_template():
|
||||||
|
""" test export with --original-suffix template """
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
# pylint: disable=not-context-manager
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||||
|
".",
|
||||||
|
"--original-suffix",
|
||||||
|
CLI_EXPORT_ORIGINAL_SUFFIX_TEMPLATE,
|
||||||
|
"-V",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
files = glob.glob("*")
|
||||||
|
assert sorted(files) == sorted(CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX_TEMPLATE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
"OSXPHOTOS_TEST_CONVERT" not in os.environ,
|
"OSXPHOTOS_TEST_CONVERT" not in os.environ,
|
||||||
reason="Skip if running in Github actions, no GPU.",
|
reason="Skip if running in Github actions, no GPU.",
|
||||||
|
|||||||
@@ -59,10 +59,16 @@ TEMPLATE_VALUES_TITLE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Boolean type values that render to True
|
# Boolean type values that render to True
|
||||||
UUID_BOOL_VALUES = {"hdr": "D11D25FF-5F31-47D2-ABA9-58418878DC15"}
|
UUID_BOOL_VALUES = {
|
||||||
|
"hdr": "D11D25FF-5F31-47D2-ABA9-58418878DC15",
|
||||||
|
"edited": "51F2BEF7-431A-4D31-8AC1-3284A57826AE",
|
||||||
|
}
|
||||||
|
|
||||||
# Boolean type values that render to False
|
# Boolean type values that render to False
|
||||||
UUID_BOOL_VALUES_NOT = {"hdr": "51F2BEF7-431A-4D31-8AC1-3284A57826AE"}
|
UUID_BOOL_VALUES_NOT = {
|
||||||
|
"hdr": "51F2BEF7-431A-4D31-8AC1-3284A57826AE",
|
||||||
|
"edited": "CCBE0EB9-AE9F-4479-BFFD-107042C75227",
|
||||||
|
}
|
||||||
|
|
||||||
# for exiftool template
|
# for exiftool template
|
||||||
UUID_EXIFTOOL = {
|
UUID_EXIFTOOL = {
|
||||||
|
|||||||
Reference in New Issue
Block a user