Moved AppleScript to photoscript
This commit is contained in:
parent
52c054f81f
commit
3c85f26f90
@ -21,9 +21,10 @@ import re
|
|||||||
import tempfile
|
import tempfile
|
||||||
from collections import namedtuple # pylint: disable=syntax-error
|
from collections import namedtuple # pylint: disable=syntax-error
|
||||||
|
|
||||||
|
import photoscript
|
||||||
from mako.template import Template
|
from mako.template import Template
|
||||||
|
|
||||||
from .._applescript import AppleScript
|
# from .._applescript import AppleScript
|
||||||
from .._constants import (
|
from .._constants import (
|
||||||
_MAX_IPTC_KEYWORD_LEN,
|
_MAX_IPTC_KEYWORD_LEN,
|
||||||
_OSXPHOTOS_NONE_SENTINEL,
|
_OSXPHOTOS_NONE_SENTINEL,
|
||||||
@ -31,8 +32,8 @@ from .._constants import (
|
|||||||
_UNKNOWN_PERSON,
|
_UNKNOWN_PERSON,
|
||||||
_XMP_TEMPLATE_NAME,
|
_XMP_TEMPLATE_NAME,
|
||||||
)
|
)
|
||||||
from ..export_db import ExportDBNoOp
|
|
||||||
from ..exiftool import ExifTool
|
from ..exiftool import ExifTool
|
||||||
|
from ..export_db import ExportDBNoOp
|
||||||
from ..fileutil import FileUtil
|
from ..fileutil import FileUtil
|
||||||
from ..utils import dd_to_dms_str, findfiles
|
from ..utils import dd_to_dms_str, findfiles
|
||||||
|
|
||||||
@ -78,33 +79,33 @@ def _export_photo_uuid_applescript(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# setup the applescript to do the export
|
# setup the applescript to do the export
|
||||||
export_scpt = AppleScript(
|
# export_scpt = AppleScript(
|
||||||
"""
|
# """
|
||||||
on export_by_uuid(theUUID, thePath, original, edited, theTimeOut)
|
# on export_by_uuid(theUUID, thePath, original, edited, theTimeOut)
|
||||||
tell application "Photos"
|
# tell application "Photos"
|
||||||
set thePath to thePath
|
# set thePath to thePath
|
||||||
set theItem to media item id theUUID
|
# set theItem to media item id theUUID
|
||||||
set theFilename to filename of theItem
|
# set theFilename to filename of theItem
|
||||||
set itemList to {theItem}
|
# set itemList to {theItem}
|
||||||
|
|
||||||
if original then
|
# if original then
|
||||||
with timeout of theTimeOut seconds
|
# with timeout of theTimeOut seconds
|
||||||
export itemList to POSIX file thePath with using originals
|
# export itemList to POSIX file thePath with using originals
|
||||||
end timeout
|
# end timeout
|
||||||
end if
|
# end if
|
||||||
|
|
||||||
if edited then
|
# if edited then
|
||||||
with timeout of theTimeOut seconds
|
# with timeout of theTimeOut seconds
|
||||||
export itemList to POSIX file thePath
|
# export itemList to POSIX file thePath
|
||||||
end timeout
|
# end timeout
|
||||||
end if
|
# end if
|
||||||
|
|
||||||
return theFilename
|
# return theFilename
|
||||||
end tell
|
# end tell
|
||||||
|
|
||||||
end export_by_uuid
|
# end export_by_uuid
|
||||||
"""
|
# """
|
||||||
)
|
# )
|
||||||
|
|
||||||
dest = pathlib.Path(dest)
|
dest = pathlib.Path(dest)
|
||||||
if not dest.is_dir():
|
if not dest.is_dir():
|
||||||
@ -115,30 +116,32 @@ def _export_photo_uuid_applescript(
|
|||||||
|
|
||||||
tmpdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
tmpdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||||
|
|
||||||
# export original
|
exported_files = []
|
||||||
filename = None
|
filename = None
|
||||||
try:
|
try:
|
||||||
filename = export_scpt.call(
|
photo = photoscript.Photo(uuid)
|
||||||
"export_by_uuid", uuid, tmpdir.name, original, edited, timeout
|
filename = photo.filename
|
||||||
)
|
exported_files = photo.export(tmpdir.name, original=original, timeout=timeout)
|
||||||
|
# filename = export_scpt.call(
|
||||||
|
# "export_by_uuid", uuid, tmpdir.name, original, edited, timeout
|
||||||
|
# )
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"Error exporting uuid {uuid}: {e}")
|
logging.warning(f"Error exporting uuid {uuid}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if filename is not None:
|
if exported_files and filename:
|
||||||
# need to find actual filename as sometimes Photos renames JPG to jpeg on export
|
# need to find actual filename as sometimes Photos renames JPG to jpeg on export
|
||||||
# may be more than one file exported (e.g. if Live Photo, Photos exports both .jpeg and .mov)
|
# may be more than one file exported (e.g. if Live Photo, Photos exports both .jpeg and .mov)
|
||||||
# TemporaryDirectory will cleanup on return
|
# TemporaryDirectory will cleanup on return
|
||||||
filename_stem = pathlib.Path(filename).stem
|
filename_stem = pathlib.Path(filename).stem
|
||||||
files = glob.glob(os.path.join(tmpdir.name, "*"))
|
|
||||||
exported_paths = []
|
exported_paths = []
|
||||||
for fname in files:
|
for fname in exported_files:
|
||||||
path = pathlib.Path(fname)
|
path = pathlib.Path(tmpdir.name) / fname
|
||||||
if len(files) > 1 and not live_photo and path.suffix.lower() == ".mov":
|
if len(exported_files) > 1 and not live_photo and path.suffix.lower() == ".mov":
|
||||||
# it's the .mov part of live photo but not requested, so don't export
|
# it's the .mov part of live photo but not requested, so don't export
|
||||||
logging.debug(f"Skipping live photo file {path}")
|
logging.debug(f"Skipping live photo file {path}")
|
||||||
continue
|
continue
|
||||||
if len(files) > 1 and burst and path.stem != filename_stem:
|
if len(exported_files) > 1 and burst and path.stem != filename_stem:
|
||||||
# skip any burst photo that's not the one we asked for
|
# skip any burst photo that's not the one we asked for
|
||||||
logging.debug(f"Skipping burst photo file {path}")
|
logging.debug(f"Skipping burst photo file {path}")
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -266,9 +266,6 @@ class PhotosDB:
|
|||||||
# photoanalysisd sometimes maintains this lock even after Photos is closed
|
# photoanalysisd sometimes maintains this lock even after Photos is closed
|
||||||
# In those cases, make a temp copy of the file for sqlite3 to read
|
# In those cases, make a temp copy of the file for sqlite3 to read
|
||||||
if _db_is_locked(self._dbfile):
|
if _db_is_locked(self._dbfile):
|
||||||
try:
|
|
||||||
self._tmp_db = self._link_db_file(self._dbfile)
|
|
||||||
except:
|
|
||||||
verbose(f"Database locked, creating temporary copy.")
|
verbose(f"Database locked, creating temporary copy.")
|
||||||
self._tmp_db = self._copy_db_file(self._dbfile)
|
self._tmp_db = self._copy_db_file(self._dbfile)
|
||||||
|
|
||||||
@ -285,9 +282,6 @@ class PhotosDB:
|
|||||||
verbose(f"Processing database {self._dbfile_actual}")
|
verbose(f"Processing database {self._dbfile_actual}")
|
||||||
# if database is exclusively locked, make a copy of it and use the copy
|
# if database is exclusively locked, make a copy of it and use the copy
|
||||||
if _db_is_locked(self._dbfile_actual):
|
if _db_is_locked(self._dbfile_actual):
|
||||||
try:
|
|
||||||
self._tmp_db = self._link_db_file(self._dbfile_actual)
|
|
||||||
except:
|
|
||||||
verbose(f"Database locked, creating temporary copy.")
|
verbose(f"Database locked, creating temporary copy.")
|
||||||
self._tmp_db = self._copy_db_file(self._dbfile_actual)
|
self._tmp_db = self._copy_db_file(self._dbfile_actual)
|
||||||
|
|
||||||
@ -553,31 +547,32 @@ class PhotosDB:
|
|||||||
|
|
||||||
return dest_path
|
return dest_path
|
||||||
|
|
||||||
def _link_db_file(self, fname):
|
# NOTE: This method seems to cause problems with applescript
|
||||||
""" links the sqlite database file to a temp file """
|
# Bummer...would'be been nice to avoid copying the DB
|
||||||
""" returns the name of the temp file """
|
# def _link_db_file(self, fname):
|
||||||
""" If sqlite shared memory and write-ahead log files exist, those are copied too """
|
# """ links the sqlite database file to a temp file """
|
||||||
# required because python's sqlite3 implementation can't read a locked file
|
# """ returns the name of the temp file """
|
||||||
# _, suffix = os.path.splitext(fname)
|
# """ If sqlite shared memory and write-ahead log files exist, those are copied too """
|
||||||
dest_name = dest_path = ""
|
# # required because python's sqlite3 implementation can't read a locked file
|
||||||
try:
|
# # _, suffix = os.path.splitext(fname)
|
||||||
dest_name = pathlib.Path(fname).name
|
# dest_name = dest_path = ""
|
||||||
dest_path = os.path.join(self._tempdir_name, dest_name)
|
# try:
|
||||||
FileUtil.hardlink(fname, dest_path)
|
# dest_name = pathlib.Path(fname).name
|
||||||
# link write-ahead log and shared memory files (-wal and -shm) files if they exist
|
# dest_path = os.path.join(self._tempdir_name, dest_name)
|
||||||
if os.path.exists(f"{fname}-wal"):
|
# FileUtil.hardlink(fname, dest_path)
|
||||||
FileUtil.hardlink(f"{fname}-wal", f"{dest_path}-wal")
|
# # link write-ahead log and shared memory files (-wal and -shm) files if they exist
|
||||||
if os.path.exists(f"{fname}-shm"):
|
# if os.path.exists(f"{fname}-wal"):
|
||||||
FileUtil.hardlink(f"{fname}-shm", f"{dest_path}-shm")
|
# FileUtil.hardlink(f"{fname}-wal", f"{dest_path}-wal")
|
||||||
except:
|
# if os.path.exists(f"{fname}-shm"):
|
||||||
print("Error linking " + fname + " to " + dest_path, file=sys.stderr)
|
# FileUtil.hardlink(f"{fname}-shm", f"{dest_path}-shm")
|
||||||
raise Exception
|
# except:
|
||||||
|
# print("Error linking " + fname + " to " + dest_path, file=sys.stderr)
|
||||||
|
# raise Exception
|
||||||
|
|
||||||
if _debug():
|
# if _debug():
|
||||||
logging.debug(dest_path)
|
# logging.debug(dest_path)
|
||||||
|
|
||||||
return dest_path
|
|
||||||
|
|
||||||
|
# return dest_path
|
||||||
|
|
||||||
def _process_database4(self):
|
def _process_database4(self):
|
||||||
""" process the Photos database to extract info
|
""" process the Photos database to extract info
|
||||||
|
|||||||
1
setup.py
1
setup.py
@ -79,6 +79,7 @@ setup(
|
|||||||
"pathvalidate==2.2.1",
|
"pathvalidate==2.2.1",
|
||||||
"dataclasses==0.7;python_version<'3.7'",
|
"dataclasses==0.7;python_version<'3.7'",
|
||||||
"wurlitzer>=2.0.1",
|
"wurlitzer>=2.0.1",
|
||||||
|
"photoscript>=0.1.0",
|
||||||
],
|
],
|
||||||
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli"]},
|
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli"]},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|||||||
@ -11,9 +11,9 @@ pytestmark = pytest.mark.skipif(
|
|||||||
PHOTOS_DB = "/Users/rhet/Pictures/Photos Library.photoslibrary"
|
PHOTOS_DB = "/Users/rhet/Pictures/Photos Library.photoslibrary"
|
||||||
|
|
||||||
UUID_DICT = {
|
UUID_DICT = {
|
||||||
"has_adjustments": "A8111956-E900-4DEC-9191-A04A87C07BC5",
|
"has_adjustments": "2B2D5434-6D31-49E2-BF47-B973D34A317B",
|
||||||
"no_adjustments": "EA7BB55F-92F1-4818-94E3-E8DEDC6B2E31",
|
"no_adjustments": "A8D646C3-89A9-4D74-8001-4EB46BA55B94",
|
||||||
"live": "9032C168-9319-40C0-8210-5ADC42F4C603",
|
"live": "BFF29EBD-22DF-4FCF-9817-317E7104EA50",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -20,10 +20,10 @@ NAMES_DICT = {
|
|||||||
"heic": "7783E8E6-9CAC-40F3-BE22-81FB7051C266.jpeg",
|
"heic": "7783E8E6-9CAC-40F3-BE22-81FB7051C266.jpeg",
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID_LIVE_HEIC = "1337F3F6-5C9F-4FC7-80CC-BD9A5B928F72"
|
UUID_LIVE_HEIC = "612CE30B-3D8F-417A-9B14-EC42CBA10ACC"
|
||||||
NAMES_LIVE_HEIC = [
|
NAMES_LIVE_HEIC = [
|
||||||
"1337F3F6-5C9F-4FC7-80CC-BD9A5B928F72.jpeg",
|
"612CE30B-3D8F-417A-9B14-EC42CBA10ACC.jpeg",
|
||||||
"1337F3F6-5C9F-4FC7-80CC-BD9A5B928F72.mov",
|
"612CE30B-3D8F-417A-9B14-EC42CBA10ACC.mov",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
""" Test PhotosDB._link_db_file """
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from tempdiskimage import TempDiskImage
|
|
||||||
|
|
||||||
PHOTOS_DB = "tests/Test-Movie-5_0.photoslibrary"
|
|
||||||
|
|
||||||
def test_link_db(capsys):
|
|
||||||
""" Test that database doesn't get copied when opened """
|
|
||||||
import osxphotos
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB, verbose=print)
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
assert "creating temporary copy" not in captured.out
|
|
||||||
|
|
||||||
def test_copy_db(capsys):
|
|
||||||
""" Test that database does get copied if on different filesystem """
|
|
||||||
import pathlib
|
|
||||||
import tempfile
|
|
||||||
import osxphotos
|
|
||||||
|
|
||||||
from osxphotos.fileutil import FileUtil
|
|
||||||
|
|
||||||
with TempDiskImage(prefix="osxphotos") as tmpimg:
|
|
||||||
newdb = pathlib.Path(tmpimg.name) / pathlib.Path(PHOTOS_DB).name
|
|
||||||
FileUtil.copy(PHOTOS_DB,newdb)
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=newdb, verbose=print)
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
assert "creating temporary copy" in captured.out
|
|
||||||
Loading…
x
Reference in New Issue
Block a user