diff --git a/README.md b/README.md
index 59c432c2..e24059bf 100644
--- a/README.md
+++ b/README.md
@@ -1704,7 +1704,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.43.3'
+{osxphotos_version} The osxphotos version, e.g. '0.43.4'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified for
@@ -3574,7 +3574,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.43.3'|
+|{osxphotos_version}|The osxphotos version, e.g. '0.43.4'|
|{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|
diff --git a/docs/.buildinfo b/docs/.buildinfo
index 27af843b..13cd2650 100644
--- a/docs/.buildinfo
+++ b/docs/.buildinfo
@@ -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: c7ae18f496751028df3c1bcd1ca5245c
+config: 7a3415c9b6b46da1269550f16ddeb35c
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/_modules/index.html b/docs/_modules/index.html
index a688b844..1d8d17cf 100644
--- a/docs/_modules/index.html
+++ b/docs/_modules/index.html
@@ -5,7 +5,7 @@
- Overview: module code — osxphotos 0.43.3 documentation
+ Overview: module code — osxphotos 0.43.4 documentation
diff --git a/docs/_modules/osxphotos/photoinfo/_photoinfo_export.html b/docs/_modules/osxphotos/photoinfo/_photoinfo_export.html
index 0a196bd5..f19c934e 100644
--- a/docs/_modules/osxphotos/photoinfo/_photoinfo_export.html
+++ b/docs/_modules/osxphotos/photoinfo/_photoinfo_export.html
@@ -5,7 +5,7 @@
- osxphotos.photoinfo._photoinfo_export — osxphotos 0.42.94 documentation
+ osxphotos.photoinfo._photoinfo_export — osxphotos 0.43.4 documentation
@@ -1299,6 +1299,7 @@
dest.name,version=PHOTOS_VERSION_CURRENT,overwrite=overwrite,
+ video=live_photo,)all_results.exported.extend(exported)exceptExceptionase:
@@ -1346,6 +1347,7 @@
dest.name,version=PHOTOS_VERSION_ORIGINAL,overwrite=overwrite,
+ video=live_photo,)all_results.exported.extend(exported)exceptExceptionase:
diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js
index 56494323..cfc330f7 100644
--- a/docs/_static/documentation_options.js
+++ b/docs/_static/documentation_options.js
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
- VERSION: '0.43.3',
+ VERSION: '0.43.4',
LANGUAGE: 'None',
COLLAPSE_INDEX: false,
BUILDER: 'html',
diff --git a/docs/cli.html b/docs/cli.html
index 7d7ace33..aa9569be 100644
--- a/docs/cli.html
+++ b/docs/cli.html
@@ -5,7 +5,7 @@
- osxphotos command line interface (CLI) — osxphotos 0.43.3 documentation
+ osxphotos command line interface (CLI) — osxphotos 0.43.4 documentation
diff --git a/docs/genindex.html b/docs/genindex.html
index 0a52d3cf..f2aa7ce7 100644
--- a/docs/genindex.html
+++ b/docs/genindex.html
@@ -5,7 +5,7 @@
- Index — osxphotos 0.43.3 documentation
+ Index — osxphotos 0.43.4 documentation
diff --git a/docs/index.html b/docs/index.html
index d892352c..3bf66f7a 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -5,7 +5,7 @@
- Welcome to osxphotos’s documentation! — osxphotos 0.43.3 documentation
+ Welcome to osxphotos’s documentation! — osxphotos 0.43.4 documentation
diff --git a/docs/modules.html b/docs/modules.html
index 8a18386e..9b5ecf27 100644
--- a/docs/modules.html
+++ b/docs/modules.html
@@ -5,7 +5,7 @@
- osxphotos — osxphotos 0.43.3 documentation
+ osxphotos — osxphotos 0.43.4 documentation
diff --git a/docs/reference.html b/docs/reference.html
index 008ca800..ee14e863 100644
--- a/docs/reference.html
+++ b/docs/reference.html
@@ -5,7 +5,7 @@
- osxphotos package — osxphotos 0.43.3 documentation
+ osxphotos package — osxphotos 0.43.4 documentation
diff --git a/docs/search.html b/docs/search.html
index 613b4933..13a8635a 100644
--- a/docs/search.html
+++ b/docs/search.html
@@ -5,7 +5,7 @@
- Search — osxphotos 0.43.3 documentation
+ Search — osxphotos 0.43.4 documentation
diff --git a/osxphotos/_version.py b/osxphotos/_version.py
index 5608dbbb..881d4c0f 100644
--- a/osxphotos/_version.py
+++ b/osxphotos/_version.py
@@ -1,3 +1,3 @@
""" version info """
-__version__ = "0.43.3"
+__version__ = "0.43.4"
diff --git a/osxphotos/photoinfo/_photoinfo_export.py b/osxphotos/photoinfo/_photoinfo_export.py
index 191a9955..f836fae7 100644
--- a/osxphotos/photoinfo/_photoinfo_export.py
+++ b/osxphotos/photoinfo/_photoinfo_export.py
@@ -1266,6 +1266,7 @@ def _export_photo_with_photos_export(
dest.name,
version=PHOTOS_VERSION_CURRENT,
overwrite=overwrite,
+ video=live_photo,
)
all_results.exported.extend(exported)
except Exception as e:
@@ -1313,6 +1314,7 @@ def _export_photo_with_photos_export(
dest.name,
version=PHOTOS_VERSION_ORIGINAL,
overwrite=overwrite,
+ video=live_photo,
)
all_results.exported.extend(exported)
except Exception as e:
diff --git a/osxphotos/photokit.py b/osxphotos/photokit.py
index 9da8990c..157bb70f 100644
--- a/osxphotos/photokit.py
+++ b/osxphotos/photokit.py
@@ -6,8 +6,6 @@
"""
# NOTES:
-# - This likely leaks memory like a sieve as I need to ensure all the
-# Objective C objects are cleaned up.
# - There are several techniques used for handling PhotoKit's various
# asynchronous calls used in this code: event loop+notification, threading
# event, while loop. I've experimented with each to find the one that works.
@@ -200,16 +198,6 @@ class PHAssetResourceData:
self.data = b""
-# class LivePhotoData:
-# """ Simple class to hold the data passed to the handler for
-# requestLivePhotoForAsset:targetSize:contentMode:options:resultHandler:
-# """
-
-# def __init__(self):
-# self.live_photo = None
-# self.info = None
-
-
class PhotoKitNotificationDelegate(NSObject):
"""Handles notifications from NotificationCenter;
used with asynchronous PhotoKit requests to stop event loop when complete
@@ -487,6 +475,7 @@ class PhotoAsset:
version=PHOTOS_VERSION_CURRENT,
overwrite=False,
raw=False,
+ **kwargs,
):
"""Export image to path
@@ -496,6 +485,7 @@ class PhotoAsset:
version: which version of image (PHOTOS_VERSION_ORIGINAL or PHOTOS_VERSION_CURRENT)
overwrite: bool, if True, overwrites destination file if it already exists; default is False
raw: bool, if True, export RAW component of RAW+JPEG pair, default is False
+ **kwargs: used only to avoid issues with each asset type having slightly different export arguments
Returns:
List of path to exported image(s)
@@ -504,9 +494,6 @@ class PhotoAsset:
ValueError if dest is not a valid directory
"""
- # if self.live:
- # raise NotImplementedError("Live photos not implemented yet")
-
with objc.autorelease_pool():
filename = (
pathlib.Path(filename)
@@ -615,9 +602,7 @@ class PhotoAsset:
nonlocal requestdata
- options = {}
- # pylint: disable=no-member
- options[Quartz.kCGImageSourceShouldCache] = Foundation.kCFBooleanFalse
+ options = {Quartz.kCGImageSourceShouldCache: Foundation.kCFBooleanFalse}
imgSrc = Quartz.CGImageSourceCreateWithData(imageData, options)
requestdata.metadata = Quartz.CGImageSourceCopyPropertiesAtIndex(
imgSrc, 0, options
@@ -701,9 +686,7 @@ class PhotoAsset:
nonlocal data
- options = {}
- # pylint: disable=no-member
- options[Quartz.kCGImageSourceShouldCache] = Foundation.kCFBooleanFalse
+ options = {Quartz.kCGImageSourceShouldCache: Foundation.kCFBooleanFalse}
imgSrc = Quartz.CGImageSourceCreateWithData(imageData, options)
data.metadata = Quartz.CGImageSourceCopyPropertiesAtIndex(
imgSrc, 0, options
@@ -789,7 +772,6 @@ class SlowMoVideoExporter(NSObject):
self.url = None
self.done = None
self.nc = None
- # super(NSObject, self).dealloc()
class VideoAsset(PhotoAsset):
@@ -801,7 +783,12 @@ class VideoAsset(PhotoAsset):
# https://developer.apple.com/documentation/photokit/phimagemanager/1616981-requestexportsessionforvideo?language=objc
# above 10.15 only
def export(
- self, dest, filename=None, version=PHOTOS_VERSION_CURRENT, overwrite=False
+ self,
+ dest,
+ filename=None,
+ version=PHOTOS_VERSION_CURRENT,
+ overwrite=False,
+ **kwargs,
):
"""Export video to path
@@ -810,6 +797,7 @@ class VideoAsset(PhotoAsset):
filename: str, optional name of exported file; if not provided, defaults to asset's original filename
version: which version of image (PHOTOS_VERSION_ORIGINAL or PHOTOS_VERSION_CURRENT)
overwrite: bool, if True, overwrites destination file if it already exists; default is False
+ **kwargs: used only to avoid issues with each asset type having slightly different export arguments
Returns:
List of path to exported image(s)
@@ -1043,6 +1031,7 @@ class LivePhotoAsset(PhotoAsset):
overwrite=False,
photo=True,
video=True,
+ **kwargs,
):
"""Export image to path
@@ -1053,6 +1042,7 @@ class LivePhotoAsset(PhotoAsset):
overwrite: bool, if True, overwrites destination file if it already exists; default is False
photo: bool, if True, export photo component of live photo
video: bool, if True, export live video component of live photo
+ **kwargs: used only to avoid issues with each asset type having slightly different export arguments
Returns:
list of [path to exported image and/or video]
@@ -1104,39 +1094,6 @@ class LivePhotoAsset(PhotoAsset):
photo_output_file = pathlib.Path(increment_filename(photo_output_file))
video_output_file = pathlib.Path(increment_filename(video_output_file))
- # def handler(error):
- # if error:
- # raise PhotoKitExportError(f"writeDataForAssetResource error: {error}")
-
- # resource_manager = Photos.PHAssetResourceManager.defaultManager()
- # options = Photos.PHAssetResourceRequestOptions.alloc().init()
- # options.setNetworkAccessAllowed_(True)
- # exported = []
- # Note: Tried writeDataForAssetResource_toFile_options_completionHandler_ which works
- # but sets quarantine flag and for reasons I can't determine (maybe quarantine flag)
- # causes pathlib.Path().is_file() to fail in tests
-
- # if photo:
- # photo_output_url = path_to_NSURL(photo_output_file)
- # resource_manager.writeDataForAssetResource_toFile_options_completionHandler_(
- # photo_resource, photo_output_url, options, handler
- # )
- # exported.append(str(photo_output_file))
-
- # if video:
- # video_output_url = path_to_NSURL(video_output_file)
- # resource_manager.writeDataForAssetResource_toFile_options_completionHandler_(
- # video_resource, video_output_url, options, handler
- # )
- # exported.append(str(video_output_file))
-
- # def completion_handler(error):
- # if error:
- # raise PhotoKitExportError(f"writeDataForAssetResource error: {error}")
-
- # would be nice to be able to usewriteDataForAssetResource_toFile_options_completionHandler_
- # but it sets quarantine flags that cause issues so instead, request the data and write the files directly
-
exported = []
if photo:
data = self._request_resource_data(photo_resource)
@@ -1155,41 +1112,6 @@ class LivePhotoAsset(PhotoAsset):
request.dealloc()
return exported
- # def request_image_data(self, version=PHOTOS_VERSION_CURRENT):
- # # Returns an NSImage which isn't overly useful
- # # https://developer.apple.com/documentation/photokit/phimagemanager/1616964-requestimageforasset?language=objc
-
- # # requestImageForAsset:targetSize:contentMode:options:resultHandler:
-
- # options = Photos.PHImageRequestOptions.alloc().init()
- # options.setVersion_(version)
- # options.setNetworkAccessAllowed_(True)
- # options.setSynchronous_(True)
- # options.setDeliveryMode_(
- # Photos.PHImageRequestOptionsDeliveryModeHighQualityFormat
- # )
-
- # event = threading.Event()
- # image_data = ImageData()
-
- # def handler(result, info):
- # nonlocal image_data
- # if not info["PHImageResultIsDegradedKey"]:
- # image_data.image_data = result
- # image_data.info = info
- # event.set()
-
- # self._manager.requestImageForAsset_targetSize_contentMode_options_resultHandler_(
- # self._phasset,
- # Photos.PHImageManagerMaximumSize,
- # Photos.PHImageContentModeDefault,
- # options,
- # handler,
- # )
- # event.wait()
- # options.dealloc()
- # return image_data
-
class PhotoLibrary:
"""Interface to PhotoKit PHImageManager and PHPhotoLibrary"""
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 1c651eb5..45e57ffb 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -40,6 +40,11 @@ UUID_BURST_ALBUM = {
],
}
+UUID_SKIP_LIVE_PHOTOKIT = {
+ "54A01B04-16D7-4FDE-8860-19F2A641E433": ["IMG_3203_edited.jpeg"],
+ "1F3DF341-B822-4531-999E-724D642FD8E7": ["IMG_4179.jpeg"],
+}
+
UUID_DOWNLOAD_MISSING = "C6C712C5-9316-408D-A3C3-125661422DA9" # IMG_8844.JPG
UUID_FILE = "tests/uuid_from_file.txt"
@@ -6645,6 +6650,43 @@ def test_export_download_missing_file_exists():
assert "exported: 1" in result.output
+@pytest.mark.skipif(
+ "OSXPHOTOS_TEST_EXPORT" not in os.environ,
+ reason="Skip if not running on author's personal library.",
+)
+def test_export_skip_live_photokit():
+ """test that --skip-live works with --use-photokit (issue #537)"""
+ import os
+ import os.path
+ import pathlib
+
+ from osxphotos.cli import export
+
+ runner = CliRunner()
+ cwd = os.getcwd()
+ # pylint: disable=not-context-manager
+ for uuid in UUID_SKIP_LIVE_PHOTOKIT:
+ with runner.isolated_filesystem():
+ result = runner.invoke(
+ export,
+ [
+ os.path.join(cwd, PHOTOS_DB_RHET),
+ ".",
+ "-V",
+ "--uuid",
+ uuid,
+ "--use-photos-export",
+ "--use-photokit",
+ "--skip-live",
+ "--skip-original-if-edited",
+ "--convert-to-jpeg",
+ ],
+ )
+ assert result.exit_code == 0
+ files = [str(p) for p in pathlib.Path(".").glob("IMG*")]
+ assert sorted(files) == sorted(UUID_SKIP_LIVE_PHOTOKIT[uuid])
+
+
def test_query_name():
"""test query --name"""
import json