Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2041789ff4 | ||
|
|
aec86f93ea | ||
|
|
57bfb03e05 | ||
|
|
c2b2476e38 | ||
|
|
fa2027d453 | ||
|
|
9d980e4917 | ||
|
|
673243c6cd | ||
|
|
7376223eb8 | ||
|
|
ecd0b8e22f | ||
|
|
c4a608b5bd | ||
|
|
4d9e21ea16 | ||
|
|
1ee3e035c4 | ||
|
|
b1c0fb3e82 | ||
|
|
de715d2afd | ||
|
|
607cf80dda |
30
CHANGELOG.md
30
CHANGELOG.md
@@ -4,6 +4,36 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v0.42.72](https://github.com/RhetTbull/osxphotos/compare/v0.42.71...v0.42.72)
|
||||
|
||||
> 2 August 2021
|
||||
|
||||
- Improved caching of detected_text results [`fa2027d`](https://github.com/RhetTbull/osxphotos/commit/fa2027d45308738d2335d4b5a72c3ef5c478491a)
|
||||
|
||||
#### [v0.42.71](https://github.com/RhetTbull/osxphotos/compare/v0.42.70...v0.42.71)
|
||||
|
||||
> 29 July 2021
|
||||
|
||||
- Updated text_detection to detect macOS version [`7376223`](https://github.com/RhetTbull/osxphotos/commit/7376223eb87a4919fd54cc685a3f263e83626879)
|
||||
- Updated detected_text docs to make it clear this only works on Catalina+ [`ecd0b8e`](https://github.com/RhetTbull/osxphotos/commit/ecd0b8e22f8bf1f8d1e98d64834bebf0394dd903)
|
||||
- Fix for #500, check for macOS version before loading Vision [`673243c`](https://github.com/RhetTbull/osxphotos/commit/673243c6cd1c267b6b741b5429cdb63c062648d1)
|
||||
|
||||
#### [v0.42.70](https://github.com/RhetTbull/osxphotos/compare/v0.42.69...v0.42.70)
|
||||
|
||||
> 29 July 2021
|
||||
|
||||
- Added error logging to {detected_text} processing, #499 [`b1c0fb3`](https://github.com/RhetTbull/osxphotos/commit/b1c0fb3e8284600394ddbfdd7dfa94916a843c81)
|
||||
- Updated README.md [skip ci] [`1ee3e03`](https://github.com/RhetTbull/osxphotos/commit/1ee3e035c42d687158f7cf73382f0f263516dc37)
|
||||
- Removed unneeded test file [skip ci] [`607cf80`](https://github.com/RhetTbull/osxphotos/commit/607cf80dda37ad529edd91fe92af3885b04b9a37)
|
||||
|
||||
#### [v0.42.69](https://github.com/RhetTbull/osxphotos/compare/v0.42.67...v0.42.69)
|
||||
|
||||
> 28 July 2021
|
||||
|
||||
- Added {detected_text} template [`c233523`](https://github.com/RhetTbull/osxphotos/commit/c2335236be7a1eecf4f25a9dcb844df4d6372b5c)
|
||||
- Added PhotoInfo.detected_text() [`123340e`](https://github.com/RhetTbull/osxphotos/commit/123340eadabb0fb07209c4207ccad13a53de3619)
|
||||
- Updated dependencies [`0c8fbd6`](https://github.com/RhetTbull/osxphotos/commit/0c8fbd69af7a0d696de5224bf3c302e0c240905f)
|
||||
|
||||
#### [v0.42.67](https://github.com/RhetTbull/osxphotos/compare/v0.42.66...v0.42.67)
|
||||
|
||||
> 24 July 2021
|
||||
|
||||
29
README.md
29
README.md
@@ -35,6 +35,7 @@ OSXPhotos provides the ability to interact with and query Apple's Photos.app lib
|
||||
+ [Raw Photos](#raw-photos)
|
||||
+ [Template System](#template-system)
|
||||
+ [ExifTool](#exiftoolExifTool)
|
||||
+ [Text Detection](#textdetection)
|
||||
+ [Utility Functions](#utility-functions)
|
||||
* [Examples](#examples)
|
||||
* [Related Projects](#related-projects)
|
||||
@@ -1700,7 +1701,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.42.69'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.42.73'
|
||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||
|
||||
The following substitutions may result in multiple values. Thus if specified for
|
||||
@@ -1769,9 +1770,10 @@ Substitution Description
|
||||
not need to reprocess each photo. You may pass a
|
||||
confidence threshold value between 0.0 and 1.0 after
|
||||
a colon as in '{detected_text:0.5}'; The default
|
||||
confidence threshold is 0.75. Note: this feature is
|
||||
not the same thing as Live Text in macOS Monterey,
|
||||
which osxphotos does not yet support.
|
||||
confidence threshold is 0.75. '{detected_text}' works
|
||||
only on macOS Catalina (10.15) or later. Note: this
|
||||
feature is not the same thing as Live Text in macOS
|
||||
Monterey, which osxphotos does not yet support.
|
||||
{shell_quote} Use in form '{shell_quote,TEMPLATE}'; quotes the
|
||||
rendered TEMPLATE value(s) for safe usage in the
|
||||
shell, e.g. My file.jpeg => 'My file.jpeg'; only adds
|
||||
@@ -2787,7 +2789,8 @@ Some substitutions, notably `album`, `keyword`, and `person` could return multip
|
||||
|
||||
See [Template System](#template-system) for additional details.
|
||||
|
||||
#### `detected_text(confidence_threshold=TEXT_DETECTION_CONFIDENCE_THRESHOLD)`
|
||||
|
||||
#### <a name="detected_text_method">`detected_text(confidence_threshold=TEXT_DETECTION_CONFIDENCE_THRESHOLD)`</a>
|
||||
|
||||
Detects text in photo and returns lists of results as (detected text, confidence)
|
||||
|
||||
@@ -2797,7 +2800,9 @@ If photo is edited, uses the edited photo, otherwise the original; falls back to
|
||||
|
||||
Returns: list of (detected text, confidence) tuples.
|
||||
|
||||
Note: This is *not* the same as Live Text in macOS Monterey. When using `detected_text()`, osxphotos will use Apple's [Vision framework](https://developer.apple.com/documentation/vision/recognizing_text_in_images?language=objc) to perform text detection on the image. On my circa 2013 MacBook Pro, this takes about 2 seconds per image. `detected_text()` does memoize the results for a given `confidence_threshold` so repeated calls will not re-process the photo.
|
||||
Note: This is *not* the same as Live Text in macOS Monterey. When using `detected_text()`, osxphotos will use Apple's [Vision framework](https://developer.apple.com/documentation/vision/recognizing_text_in_images?language=objc) to perform text detection on the image. On my circa 2013 MacBook Pro, this takes about 2 seconds per image. `detected_text()` does memoize the results for a given `confidence_threshold` so repeated calls will not re-process the photo. This works only on macOS Catalina (10.15) or later.
|
||||
|
||||
See also [Text Detection](#textdetection).
|
||||
|
||||
### ExifInfo
|
||||
[PhotosInfo.exif_info](#exif-info) returns an `ExifInfo` object with some EXIF data about the photo (Photos 5 only). `ExifInfo` contains the following properties:
|
||||
@@ -3553,7 +3558,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.42.69'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.42.73'|
|
||||
|{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|
|
||||
@@ -3568,7 +3573,7 @@ The following template field substitutions are availabe for use the templating s
|
||||
|{searchinfo.venue}|Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
||||
|{searchinfo.venue_type}|Venue types associated with a photo, e.g. 'Restaurant'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
||||
|{photo}|Provides direct access to the PhotoInfo object for the photo. Must be used in format '{photo.property}' where 'property' represents a PhotoInfo property. For example: '{photo.favorite}' is the same as '{favorite}' and '{photo.place.name}' is the same as '{place.name}'. '{photo}' provides access to properties that are not available as separate template fields but it assumes some knowledge of the underlying PhotoInfo class. See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.|
|
||||
|{detected_text}|List of text strings found in the image after performing text detection. Using '{detected_text}' will cause osxphotos to perform text detection on your photos using the built-in macOS text detection algorithms which will slow down your export. The results for each photo will be cached in the export database so that future exports with '--update' do not need to reprocess each photo. You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in '{detected_text:0.5}'; The default confidence threshold is 0.75. Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.|
|
||||
|{detected_text}|List of text strings found in the image after performing text detection. Using '{detected_text}' will cause osxphotos to perform text detection on your photos using the built-in macOS text detection algorithms which will slow down your export. The results for each photo will be cached in the export database so that future exports with '--update' do not need to reprocess each photo. You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in '{detected_text:0.5}'; The default confidence threshold is 0.75. '{detected_text}' works only on macOS Catalina (10.15) or later. Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.|
|
||||
|{shell_quote}|Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.|
|
||||
|{function}|Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.|
|
||||
<!-- OSXPHOTOS-TEMPLATE-TABLE:END -->
|
||||
@@ -3633,6 +3638,14 @@ osxphotos.exiftool also provides an `ExifToolCaching` class which caches all met
|
||||
|
||||
`ExifTool()` runs `exiftool` as a subprocess using the `-stay_open True` flag to keep the process running in the background. The subprocess will be cleaned up when your main script terminates. `ExifTool()` uses a singleton pattern to ensure that only one instance of `exiftool` is created. Multiple instances of `ExifTool()` will all use the same `exiftool` subprocess.
|
||||
|
||||
### <a name="textdetection">Text Detection</a>
|
||||
|
||||
The [PhotoInfo.detected_text()](#detected_text_method) and the `{detected_text}` template will perform text detection on the photos in your library. Text detection is a slow process so to avoid unnecessary re-processing of photos, osxphotos will cache the results of the text detection process as an extended attribute on the photo image file. Extended attributes do not modify the actual file. The extended attribute is named `osxphotos.metadata:detected_text` and can be viewed using the built-in [xattr](https://ss64.com/osx/xattr.html) command or my [osxmetadata](https://github.com/RhetTbull/osxmetadata) tool. If you want to remove the cached attribute, you can do so with osxmetadata as follows:
|
||||
|
||||
`osxmetadata --clear osxphotos.metadata:detected_text --walk ~/Pictures/Photos\ Library.photoslibrary/`
|
||||
|
||||
|
||||
|
||||
### Utility Functions
|
||||
|
||||
The following functions are located in osxphotos.utils
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.42.69"
|
||||
__version__ = "0.42.73"
|
||||
|
||||
@@ -4074,6 +4074,10 @@ def _get_selected(photosdb):
|
||||
@click.pass_context
|
||||
def repl(ctx, cli_obj, db):
|
||||
"""Run interactive osxphotos shell"""
|
||||
|
||||
from osxphotos import PhotosDB, PhotoInfo, ExifTool
|
||||
from rich import inspect as _inspect
|
||||
|
||||
pretty.install()
|
||||
print(f"python version: {sys.version}")
|
||||
print(f"osxphotos version: {osxphotos._version.__version__}")
|
||||
@@ -4090,6 +4094,10 @@ def repl(ctx, cli_obj, db):
|
||||
show = _show_photo
|
||||
get_selected = _get_selected(photosdb)
|
||||
|
||||
def inspect(obj):
|
||||
"""inspect object"""
|
||||
return _inspect(obj, methods=True)
|
||||
|
||||
print(f"Found {len(photos)} photos in {tictoc:0.2f} seconds")
|
||||
print("The following variables are defined:")
|
||||
print(f"- photosdb: PhotosDB() instance for {photosdb.library_path}")
|
||||
@@ -4105,5 +4113,8 @@ def repl(ctx, cli_obj, db):
|
||||
print(
|
||||
f"- help(object): print help text including list of methods for object; for example, help(PhotosDB)"
|
||||
)
|
||||
print(
|
||||
f"- inspect(object): print information about an object; for example inspect(photosdb)"
|
||||
)
|
||||
print(f"- quit(): exit this interactive shell\n")
|
||||
code.interact(banner="", local=locals())
|
||||
|
||||
@@ -89,11 +89,11 @@ class ExportDB_ABC(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_detected_text_for_uuid(self, uuid, confidence):
|
||||
def get_detected_text_for_uuid(self, uuid):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def set_detected_text_for_uuid(self, uuid, text, confidence):
|
||||
def set_detected_text_for_uuid(self, uuid, json_text):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@@ -170,10 +170,10 @@ class ExportDBNoOp(ExportDB_ABC):
|
||||
def get_previous_uuids(self):
|
||||
return []
|
||||
|
||||
def get_detected_text_for_uuid(self, uuid, confidence):
|
||||
return []
|
||||
def get_detected_text_for_uuid(self, uuid):
|
||||
return None
|
||||
|
||||
def set_detected_text_for_uuid(self, uuid, text, confidence):
|
||||
def set_detected_text_for_uuid(self, uuid, json_text):
|
||||
pass
|
||||
|
||||
def set_data(
|
||||
@@ -493,7 +493,7 @@ class ExportDB(ExportDB_ABC):
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
"INSERT OR REPLACE INTO detected_text(uuid, text_data) VALUES (?, ?);",
|
||||
(uuid, text_json),
|
||||
(uuid, text_json,),
|
||||
)
|
||||
conn.commit()
|
||||
except Error as e:
|
||||
|
||||
@@ -14,6 +14,7 @@ from datetime import timedelta, timezone
|
||||
from typing import Optional
|
||||
|
||||
import yaml
|
||||
from osxmetadata import OSXMetaData
|
||||
|
||||
from .._constants import (
|
||||
_MOVIE_TYPE,
|
||||
@@ -1118,6 +1119,28 @@ class PhotoInfo:
|
||||
|
||||
Returns: list of (detected text, confidence) tuples
|
||||
"""
|
||||
|
||||
try:
|
||||
return self._detected_text_cache[confidence_threshold]
|
||||
except (AttributeError, KeyError) as e:
|
||||
if isinstance(e, AttributeError):
|
||||
self._detected_text_cache = {}
|
||||
|
||||
try:
|
||||
detected_text = self._detected_text()
|
||||
except Exception as e:
|
||||
logging.warning(f"Error detecting text in photo {self.uuid}: {e}")
|
||||
detected_text = []
|
||||
|
||||
self._detected_text_cache[confidence_threshold] = [
|
||||
(text, confidence)
|
||||
for text, confidence in detected_text
|
||||
if confidence >= confidence_threshold
|
||||
]
|
||||
return self._detected_text_cache[confidence_threshold]
|
||||
|
||||
def _detected_text(self):
|
||||
"""detect text in photo, either from cached extended attribute or by attempting text detection"""
|
||||
path = (
|
||||
self.path_edited if self.hasadjustments and self.path_edited else self.path
|
||||
)
|
||||
@@ -1125,23 +1148,12 @@ class PhotoInfo:
|
||||
if not path:
|
||||
return []
|
||||
|
||||
try:
|
||||
return self._detected_text[(path, confidence_threshold)]
|
||||
except (AttributeError, KeyError) as e:
|
||||
if isinstance(e, AttributeError):
|
||||
self._detected_text = {}
|
||||
|
||||
try:
|
||||
detected_text = detect_text(path)
|
||||
except Exception as e:
|
||||
detected_text = []
|
||||
|
||||
self._detected_text[(path, confidence_threshold)] = [
|
||||
(text, confidence)
|
||||
for text, confidence in detected_text
|
||||
if confidence >= confidence_threshold
|
||||
]
|
||||
return self._detected_text[(path, confidence_threshold)]
|
||||
md = OSXMetaData(path)
|
||||
detected_text = md.get_attribute("osxphotos_detected_text")
|
||||
if detected_text is None:
|
||||
detected_text = detect_text(path)
|
||||
md.set_attribute("osxphotos_detected_text", detected_text)
|
||||
return detected_text
|
||||
|
||||
@property
|
||||
def _longitude(self):
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import datetime
|
||||
import json
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shlex
|
||||
@@ -205,6 +206,7 @@ TEMPLATE_SUBSTITUTIONS_MULTI_VALUED = {
|
||||
+ "The results for each photo will be cached in the export database so that future exports with '--update' do not need to reprocess each photo. "
|
||||
+ "You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in '{detected_text:0.5}'; "
|
||||
+ f"The default confidence threshold is {TEXT_DETECTION_CONFIDENCE_THRESHOLD}. "
|
||||
+ "'{detected_text}' works only on macOS Catalina (10.15) or later. "
|
||||
+ "Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.",
|
||||
"{shell_quote}": "Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.",
|
||||
"{function}": "Execute a python function from an external file and use return value as template substitution. "
|
||||
@@ -1443,22 +1445,8 @@ def _get_detected_text(photo, exportdb, confidence=TEXT_DETECTION_CONFIDENCE_THR
|
||||
else TEXT_DETECTION_CONFIDENCE_THRESHOLD
|
||||
)
|
||||
|
||||
detected_text = exportdb.get_detected_text_for_uuid(photo.uuid)
|
||||
if detected_text:
|
||||
detected_text = json.loads(detected_text)
|
||||
else:
|
||||
path = (
|
||||
photo.path_edited
|
||||
if photo.hasadjustments and photo.path_edited
|
||||
else photo.path
|
||||
)
|
||||
path = path or photo.path_derivatives[0] if photo.path_derivatives else None
|
||||
if not path:
|
||||
detected_text = []
|
||||
else:
|
||||
try:
|
||||
detected_text = detect_text(path)
|
||||
except Exception as e:
|
||||
detected_text = []
|
||||
exportdb.set_detected_text_for_uuid(photo.uuid, json.dumps(detected_text))
|
||||
# _detected_text caches the text detection results in an extended attribute
|
||||
# so the first time this gets called is slow but repeated accesses are fast
|
||||
detected_text = photo._detected_text()
|
||||
exportdb.set_detected_text_for_uuid(photo.uuid, json.dumps(detected_text))
|
||||
return [text for text, conf in detected_text if conf >= confidence]
|
||||
|
||||
@@ -1,19 +1,33 @@
|
||||
""" Use Apple's Vision Framework via PyObjC to perform text detection on images """
|
||||
""" Use Apple's Vision Framework via PyObjC to perform text detection on images (macOS 10.15+ only) """
|
||||
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
import objc
|
||||
import Quartz
|
||||
import Vision
|
||||
from Cocoa import NSURL
|
||||
from Foundation import NSDictionary
|
||||
|
||||
from typing import List
|
||||
|
||||
# needed to capture system-level stderr
|
||||
from wurlitzer import pipes
|
||||
|
||||
from .utils import _get_os_version
|
||||
|
||||
ver, major, minor = _get_os_version()
|
||||
if ver == "10" and int(major) < 15:
|
||||
vision = False
|
||||
else:
|
||||
import Vision
|
||||
|
||||
vision = True
|
||||
|
||||
|
||||
def detect_text(img_path: str) -> List:
|
||||
"""process image at img_path with VNRecognizeTextRequest and return list of results"""
|
||||
if not vision:
|
||||
logging.warning(f"detect_text not implemented for this version of macOS")
|
||||
return []
|
||||
|
||||
with objc.autorelease_pool():
|
||||
input_url = NSURL.fileURLWithPath_(img_path)
|
||||
|
||||
@@ -38,6 +52,9 @@ def detect_text(img_path: str) -> List:
|
||||
vision_request.dealloc()
|
||||
vision_handler.dealloc()
|
||||
|
||||
for result in results:
|
||||
result[0] = str(result[0])
|
||||
|
||||
return results
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ dataclasses==0.7;python_version<'3.7'
|
||||
wurlitzer==2.1.0
|
||||
photoscript==0.1.4
|
||||
toml==0.10.2
|
||||
osxmetadata==0.99.26
|
||||
osxmetadata==0.99.31
|
||||
textx==2.3.0
|
||||
rich==10.6.0
|
||||
bitmath==1.3.3.1
|
||||
|
||||
2
setup.py
2
setup.py
@@ -91,7 +91,7 @@ setup(
|
||||
"wurlitzer==2.1.0",
|
||||
"photoscript==0.1.4",
|
||||
"toml==0.10.2",
|
||||
"osxmetadata==0.99.26",
|
||||
"osxmetadata==0.99.31",
|
||||
"textx==2.3.0",
|
||||
"rich==10.6.0",
|
||||
"bitmath==1.3.3.1",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
""" Test ExportDB """
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
EXIF_DATA = """[{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos", "EXIF:ImageDescription": "\u2068Elder Park\u2069, \u2068Adelaide\u2069, \u2068Australia\u2069", "XMP:Description": "\u2068Elder Park\u2069, \u2068Adelaide\u2069, \u2068Australia\u2069", "XMP:Title": "Elder Park", "EXIF:GPSLatitude": "34 deg 55' 8.01\" S", "EXIF:GPSLongitude": "138 deg 35' 48.70\" E", "Composite:GPSPosition": "34 deg 55' 8.01\" S, 138 deg 35' 48.70\" E", "EXIF:GPSLatitudeRef": "South", "EXIF:GPSLongitudeRef": "East", "EXIF:DateTimeOriginal": "2017:06:20 17:18:56", "EXIF:OffsetTimeOriginal": "+09:30", "EXIF:ModifyDate": "2020:05:18 14:42:04"}]"""
|
||||
@@ -12,10 +14,11 @@ DATABASE_VERSION1 = "tests/export_db_version1.db"
|
||||
|
||||
|
||||
def test_export_db():
|
||||
""" test ExportDB """
|
||||
"""test ExportDB"""
|
||||
import os
|
||||
import tempfile
|
||||
from osxphotos.export_db import ExportDB, OSXPHOTOS_EXPORTDB_VERSION
|
||||
|
||||
from osxphotos.export_db import OSXPHOTOS_EXPORTDB_VERSION, ExportDB
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
dbname = os.path.join(tempdir.name, ".osxphotos_export.db")
|
||||
@@ -47,6 +50,9 @@ def test_export_db():
|
||||
assert db.get_sidecar_for_file(filepath) == (SIDECAR_DATA, (13, 14, 15))
|
||||
assert db.get_previous_uuids() == ["FOO-BAR"]
|
||||
|
||||
db.set_detected_text_for_uuid("FOO-BAR", json.dumps([["foo", 0.5]]))
|
||||
assert json.loads(db.get_detected_text_for_uuid("FOO-BAR")) == [["foo", 0.5]]
|
||||
|
||||
# test set_data which sets all at the same time
|
||||
filepath2 = os.path.join(tempdir.name, "test2.jpg")
|
||||
db.set_data(
|
||||
@@ -80,6 +86,7 @@ def test_export_db():
|
||||
assert db.get_stat_converted_for_file(filepath2) == (7, 8, 9)
|
||||
assert db.get_stat_edited_for_file(filepath2) == (10, 11, 12)
|
||||
assert sorted(db.get_previous_uuids()) == (["BAR-FOO", "FOO-BAR"])
|
||||
assert json.loads(db.get_detected_text_for_uuid("FOO-BAR")) == [["foo", 0.5]]
|
||||
|
||||
# update data
|
||||
db.set_uuid_for_file(filepath, "FUBAR")
|
||||
@@ -88,10 +95,11 @@ def test_export_db():
|
||||
|
||||
|
||||
def test_export_db_no_op():
|
||||
""" test ExportDBNoOp """
|
||||
"""test ExportDBNoOp"""
|
||||
import os
|
||||
import tempfile
|
||||
from osxphotos.export_db import ExportDBNoOp, OSXPHOTOS_EXPORTDB_VERSION
|
||||
|
||||
from osxphotos.export_db import OSXPHOTOS_EXPORTDB_VERSION, ExportDBNoOp
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
db = ExportDBNoOp()
|
||||
@@ -121,6 +129,9 @@ def test_export_db_no_op():
|
||||
assert db.get_sidecar_for_file(filepath) == (None, (None, None, None))
|
||||
assert db.get_previous_uuids() == []
|
||||
|
||||
db.set_detected_text_for_uuid("FOO-BAR", json.dumps([["foo", 0.5]]))
|
||||
assert db.get_detected_text_for_uuid("FOO-BAR") is None
|
||||
|
||||
# test set_data which sets all at the same time
|
||||
filepath2 = os.path.join(tempdir.name, "test2.jpg")
|
||||
db.set_data(
|
||||
@@ -148,13 +159,14 @@ def test_export_db_no_op():
|
||||
|
||||
|
||||
def test_export_db_in_memory():
|
||||
""" test ExportDBInMemory """
|
||||
"""test ExportDBInMemory"""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from osxphotos.export_db import (
|
||||
OSXPHOTOS_EXPORTDB_VERSION,
|
||||
ExportDB,
|
||||
ExportDBInMemory,
|
||||
OSXPHOTOS_EXPORTDB_VERSION,
|
||||
)
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
@@ -174,6 +186,7 @@ def test_export_db_in_memory():
|
||||
db.set_stat_edited_for_file(filepath, (10, 11, 12))
|
||||
db.set_sidecar_for_file(filepath, SIDECAR_DATA, (13, 14, 15))
|
||||
assert db.get_previous_uuids() == ["FOO-BAR"]
|
||||
db.set_detected_text_for_uuid("FOO-BAR", json.dumps([["foo", 0.5]]))
|
||||
|
||||
db.close()
|
||||
|
||||
@@ -192,6 +205,7 @@ def test_export_db_in_memory():
|
||||
assert dbram.get_stat_edited_for_file(filepath) == (10, 11, 12)
|
||||
assert dbram.get_sidecar_for_file(filepath) == (SIDECAR_DATA, (13, 14, 15))
|
||||
assert dbram.get_previous_uuids() == ["FOO-BAR"]
|
||||
assert json.loads(dbram.get_detected_text_for_uuid("FOO-BAR")) == [["foo", 0.5]]
|
||||
|
||||
# change a value
|
||||
dbram.set_uuid_for_file(filepath, "FUBAR")
|
||||
@@ -202,6 +216,7 @@ def test_export_db_in_memory():
|
||||
dbram.set_stat_converted_for_file(filepath, (1, 2, 3))
|
||||
dbram.set_stat_edited_for_file(filepath, (4, 5, 6))
|
||||
dbram.set_sidecar_for_file(filepath, "FUBAR", (20, 21, 22))
|
||||
dbram.set_detected_text_for_uuid("FUBAR", json.dumps([["bar", 0.5]]))
|
||||
|
||||
assert dbram.get_uuid_for_file(filepath_lower) == "FUBAR"
|
||||
assert dbram.get_info_for_uuid("FUBAR") == INFO_DATA2
|
||||
@@ -212,6 +227,7 @@ def test_export_db_in_memory():
|
||||
assert dbram.get_stat_edited_for_file(filepath) == (4, 5, 6)
|
||||
assert dbram.get_sidecar_for_file(filepath) == ("FUBAR", (20, 21, 22))
|
||||
assert dbram.get_previous_uuids() == ["FUBAR"]
|
||||
assert json.loads(dbram.get_detected_text_for_uuid("FUBAR")) == [["bar", 0.5]]
|
||||
|
||||
dbram.close()
|
||||
|
||||
@@ -228,13 +244,15 @@ def test_export_db_in_memory():
|
||||
assert db.get_previous_uuids() == ["FOO-BAR"]
|
||||
|
||||
assert db.get_info_for_uuid("FUBAR") is None
|
||||
assert db.get_detected_text_for_uuid("FUBAR") is None
|
||||
|
||||
|
||||
def test_export_db_in_memory_nofile():
|
||||
""" test ExportDBInMemory with no dbfile """
|
||||
"""test ExportDBInMemory with no dbfile"""
|
||||
import os
|
||||
import tempfile
|
||||
from osxphotos.export_db import ExportDBInMemory, OSXPHOTOS_EXPORTDB_VERSION
|
||||
|
||||
from osxphotos.export_db import OSXPHOTOS_EXPORTDB_VERSION, ExportDBInMemory
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||
filepath = os.path.join(tempdir.name, "test.JPG")
|
||||
@@ -254,6 +272,7 @@ def test_export_db_in_memory_nofile():
|
||||
dbram.set_stat_converted_for_file(filepath, (1, 2, 3))
|
||||
dbram.set_stat_edited_for_file(filepath, (4, 5, 6))
|
||||
dbram.set_sidecar_for_file(filepath, "FUBAR", (20, 21, 22))
|
||||
dbram.set_detected_text_for_uuid("FUBAR", json.dumps([["bar", 0.5]]))
|
||||
|
||||
assert dbram.get_uuid_for_file(filepath_lower) == "FUBAR"
|
||||
assert dbram.get_info_for_uuid("FUBAR") == INFO_DATA2
|
||||
@@ -264,5 +283,6 @@ def test_export_db_in_memory_nofile():
|
||||
assert dbram.get_stat_edited_for_file(filepath) == (4, 5, 6)
|
||||
assert dbram.get_sidecar_for_file(filepath) == ("FUBAR", (20, 21, 22))
|
||||
assert dbram.get_previous_uuids() == ["FUBAR"]
|
||||
|
||||
assert json.loads(dbram.get_detected_text_for_uuid("FUBAR")) == [["bar", 0.5]]
|
||||
|
||||
dbram.close()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" Test template.py """
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
@@ -7,6 +8,7 @@ from photoinfo_mock import PhotoInfoMock
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.exiftool import get_exiftool_path
|
||||
from osxphotos.export_db import ExportDBInMemory
|
||||
from osxphotos.phototemplate import (
|
||||
TEMPLATE_SUBSTITUTIONS,
|
||||
TEMPLATE_SUBSTITUTIONS_MULTI_VALUED,
|
||||
|
||||
Reference in New Issue
Block a user