Fix for --use-photokit with --skip-live, #537

This commit is contained in:
Rhet Turnbull
2021-11-11 10:54:48 -08:00
parent b00978c61a
commit 0e6c92dbd9
15 changed files with 72 additions and 104 deletions

View File

@@ -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|

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: c7ae18f496751028df3c1bcd1ca5245c
config: 7a3415c9b6b46da1269550f16ddeb35c
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Overview: module code &#8212; osxphotos 0.43.3 documentation</title>
<title>Overview: module code &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
<script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.photoinfo._photoinfo_export &#8212; osxphotos 0.42.94 documentation</title>
<title>osxphotos.photoinfo._photoinfo_export &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/alabaster.css" />
<script data-url_root="../../../" id="documentation_options" src="../../../_static/documentation_options.js"></script>
@@ -1299,6 +1299,7 @@
<span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="n">PHOTOS_VERSION_CURRENT</span><span class="p">,</span>
<span class="n">overwrite</span><span class="o">=</span><span class="n">overwrite</span><span class="p">,</span>
<span class="n">video</span><span class="o">=</span><span class="n">live_photo</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">all_results</span><span class="o">.</span><span class="n">exported</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">exported</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
@@ -1346,6 +1347,7 @@
<span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="n">PHOTOS_VERSION_ORIGINAL</span><span class="p">,</span>
<span class="n">overwrite</span><span class="o">=</span><span class="n">overwrite</span><span class="p">,</span>
<span class="n">video</span><span class="o">=</span><span class="n">live_photo</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">all_results</span><span class="o">.</span><span class="n">exported</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">exported</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>

View File

@@ -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',

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.43.3 documentation</title>
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; osxphotos 0.43.3 documentation</title>
<title>Index &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.43.3 documentation</title>
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos &#8212; osxphotos 0.43.3 documentation</title>
<title>osxphotos &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos package &#8212; osxphotos 0.43.3 documentation</title>
<title>osxphotos package &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; osxphotos 0.43.3 documentation</title>
<title>Search &#8212; osxphotos 0.43.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.43.3"
__version__ = "0.43.4"

View File

@@ -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:

View File

@@ -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"""

View File

@@ -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