@@ -1,5 +1,5 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.39.0"
|
__version__ = "0.39.1"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ class _ExifToolProc:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._exiftool = exiftool or get_exiftool_path()
|
|
||||||
self._process_running = False
|
self._process_running = False
|
||||||
|
self._exiftool = exiftool or get_exiftool_path()
|
||||||
self._start_proc()
|
self._start_proc()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -106,8 +106,8 @@ class _ExifToolProc:
|
|||||||
|
|
||||||
def _stop_proc(self):
|
def _stop_proc(self):
|
||||||
""" stop the exiftool process if it's running, otherwise, do nothing """
|
""" stop the exiftool process if it's running, otherwise, do nothing """
|
||||||
|
|
||||||
if not self._process_running:
|
if not self._process_running:
|
||||||
logging.warning("exiftool process is not running")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self._process.stdin.write(b"-stay_open\n")
|
self._process.stdin.write(b"-stay_open\n")
|
||||||
|
|||||||
@@ -596,14 +596,12 @@ class PhotoInfo:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def ismovie(self):
|
def ismovie(self):
|
||||||
""" Returns True if file is a movie, otherwise False
|
"""Returns True if file is a movie, otherwise False"""
|
||||||
"""
|
|
||||||
return True if self._info["type"] == _MOVIE_TYPE else False
|
return True if self._info["type"] == _MOVIE_TYPE else False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isphoto(self):
|
def isphoto(self):
|
||||||
""" Returns True if file is an image, otherwise False
|
"""Returns True if file is an image, otherwise False"""
|
||||||
"""
|
|
||||||
return True if self._info["type"] == _PHOTO_TYPE else False
|
return True if self._info["type"] == _PHOTO_TYPE else False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -854,7 +852,7 @@ class PhotoInfo:
|
|||||||
Returns:
|
Returns:
|
||||||
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
|
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
|
||||||
"""
|
"""
|
||||||
template = PhotoTemplate(self)
|
template = PhotoTemplate(self, exiftool_path=self._db._exiftool_path)
|
||||||
return template.render(
|
return template.render(
|
||||||
template_str,
|
template_str,
|
||||||
none_str=none_str,
|
none_str=none_str,
|
||||||
|
|||||||
@@ -149,13 +149,15 @@ MULTI_VALUE_SUBSTITUTIONS = [
|
|||||||
class PhotoTemplate:
|
class PhotoTemplate:
|
||||||
""" PhotoTemplate class to render a template string from a PhotoInfo object """
|
""" PhotoTemplate class to render a template string from a PhotoInfo object """
|
||||||
|
|
||||||
def __init__(self, photo):
|
def __init__(self, photo, exiftool_path=None):
|
||||||
""" Inits PhotoTemplate class with photo, non_str, and path_sep
|
""" Inits PhotoTemplate class with photo
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
photo: a PhotoInfo instance.
|
photo: a PhotoInfo instance.
|
||||||
|
exiftool_path: optional path to exiftool for use with {exiftool:} template; if not provided, will look for exiftool in $PATH
|
||||||
"""
|
"""
|
||||||
self.photo = photo
|
self.photo = photo
|
||||||
|
self.exiftool_path = exiftool_path
|
||||||
|
|
||||||
# holds value of current date/time for {today.x} fields
|
# holds value of current date/time for {today.x} fields
|
||||||
# gets initialized in get_template_value
|
# gets initialized in get_template_value
|
||||||
@@ -517,7 +519,7 @@ class PhotoTemplate:
|
|||||||
if not self.photo.path:
|
if not self.photo.path:
|
||||||
values = [None]
|
values = [None]
|
||||||
else:
|
else:
|
||||||
exif = ExifTool(self.photo.path)
|
exif = ExifTool(self.photo.path, exiftool=self.exiftool_path)
|
||||||
exifdict = exif.asdict()
|
exifdict = exif.asdict()
|
||||||
exifdict = {k.lower(): v for (k, v) in exifdict.items()}
|
exifdict = {k.lower(): v for (k, v) in exifdict.items()}
|
||||||
subfield = subfield.lower()
|
subfield = subfield.lower()
|
||||||
|
|||||||
@@ -357,6 +357,7 @@ CLI_EXIFTOOL = {
|
|||||||
"XMP:TagsList": "Kids",
|
"XMP:TagsList": "Kids",
|
||||||
"XMP:Title": "I found one!",
|
"XMP:Title": "I found one!",
|
||||||
"EXIF:ImageDescription": "Girl holding pumpkin",
|
"EXIF:ImageDescription": "Girl holding pumpkin",
|
||||||
|
"EXIF:Make": "Canon",
|
||||||
"XMP:Description": "Girl holding pumpkin",
|
"XMP:Description": "Girl holding pumpkin",
|
||||||
"XMP:PersonInImage": "Katie",
|
"XMP:PersonInImage": "Katie",
|
||||||
"XMP:Subject": "Kids",
|
"XMP:Subject": "Kids",
|
||||||
@@ -1114,6 +1115,54 @@ def test_export_exiftool_path():
|
|||||||
assert exif[key] == CLI_EXIFTOOL[uuid][key]
|
assert exif[key] == CLI_EXIFTOOL[uuid][key]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
|
||||||
|
def test_export_exiftool_path_render_template():
|
||||||
|
""" test --exiftool-path with {exiftool:} template rendering """
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
from osxphotos.exiftool import ExifTool
|
||||||
|
from osxphotos.utils import noop
|
||||||
|
|
||||||
|
exiftool_source = osxphotos.exiftool.get_exiftool_path()
|
||||||
|
|
||||||
|
# monkey patch get_exiftool_path so it returns None
|
||||||
|
get_exiftool_path = osxphotos.exiftool.get_exiftool_path
|
||||||
|
osxphotos.exiftool.get_exiftool_path = noop
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
# pylint: disable=not-context-manager
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
tempdir = tempfile.TemporaryDirectory()
|
||||||
|
exiftool_path = os.path.join(tempdir.name, "myexiftool")
|
||||||
|
shutil.copy2(exiftool_source, exiftool_path)
|
||||||
|
for uuid in CLI_EXIFTOOL:
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"-V",
|
||||||
|
"--filename",
|
||||||
|
"{original_name}_{exiftool:EXIF:Make}",
|
||||||
|
"--uuid",
|
||||||
|
f"{uuid}",
|
||||||
|
"--exiftool-path",
|
||||||
|
exiftool_path,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert re.search(r"Exporting.*Canon", result.output)
|
||||||
|
|
||||||
|
osxphotos.exiftool.get_exiftool_path = get_exiftool_path
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
|
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
|
||||||
def test_export_exiftool_ignore_date_modified():
|
def test_export_exiftool_ignore_date_modified():
|
||||||
import glob
|
import glob
|
||||||
@@ -5066,4 +5115,3 @@ def test_export_finder_tag_template_keywords():
|
|||||||
persons = [persons] if type(persons) != list else persons
|
persons = [persons] if type(persons) != list else persons
|
||||||
expected = [Tag(x) for x in keywords + persons]
|
expected = [Tag(x) for x in keywords + persons]
|
||||||
assert sorted(md.tags) == sorted(expected)
|
assert sorted(md.tags) == sorted(expected)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user