Compare commits

...

149 Commits

Author SHA1 Message Date
Rhet Turnbull
aeb6283b2b Version bump, fix for #859, wrong edited path in Mojave 2022-12-13 23:05:59 -08:00
Rhet Turnbull
47e2454584 Bug edited path bad mojave 859 (#870)
* Partial fix for #859, missing path edited on Mojave

* Fixed annotation issue

* Fix for HEIC edited images on Mojave, #859

* Additional fix for live photo edited paths, #859
2022-12-13 22:55:17 -08:00
Rhet Turnbull
ee370f5dfb Added template function example 2022-12-12 22:41:09 -08:00
Rhet Turnbull
3e2076df12 Updated timewarp example 2022-12-11 23:24:24 -08:00
Rhet Turnbull
2afab9e3b1 Added timewarp --function example 2022-12-11 23:21:48 -08:00
Rhet Turnbull
3c8d7e13b9 Added edited live video path to inspect, #865 2022-12-11 20:43:01 -08:00
Rhet Turnbull
c3bd04f257 Updated README for supported OS versions 2022-12-11 20:33:30 -08:00
Rhet Turnbull
12fecec3de Updated CHANGELOG.md [skip ci] 2022-12-11 19:38:26 -08:00
Rhet Turnbull
e4faf3779c Version bump, fix for #859, wrong edited path in Mojave 2022-12-11 19:34:56 -08:00
Rhet Turnbull
53a61ed5aa Bug edited path bad mojave 859 (#864)
* Partial fix for #859, missing path edited on Mojave

* Fixed annotation issue

* Fix for HEIC edited images on Mojave, #859
2022-12-11 19:30:28 -08:00
Rhet Turnbull
debc001af9 Update tests.yml 2022-12-11 19:05:05 -08:00
Rhet Turnbull
025ee36086 Fixed edit_resource_id for Photos 5+ 2022-12-11 12:32:10 -08:00
Rhet Turnbull
d66cb6dc2b Updated CHANGELOG.md [skip ci] 2022-12-11 12:21:29 -08:00
Rhet Turnbull
88e56bc0b9 Added target architecture, #857 2022-12-11 12:16:31 -08:00
Rhet Turnbull
327f19809e Updated build for Ventura 2022-12-11 12:13:22 -08:00
Rhet Turnbull
924a5f2f61 Added Ventura to list of supported OS (#863) 2022-12-11 11:20:23 -08:00
Rhet Turnbull
3557658b73 Partial fix for #859, missing path edited on Mojave (#862)
* Partial fix for #859, missing path edited on Mojave

* Fixed annotation issue
2022-12-11 11:19:11 -08:00
allcontributors[bot]
bb65765afa add drodner as a contributor for bug, and userTesting (#861)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-12-11 09:16:36 -08:00
Rhet Turnbull
f8d8028631 Updated CHANGELOG.md [skip ci] 2022-11-24 10:31:30 -08:00
Rhet Turnbull
cad4e1eeff Version bump for release 2022-11-24 09:32:56 -08:00
Rhet Turnbull
ce5145ff85 Added --post-function to import, #842 (#851) 2022-11-24 09:26:09 -08:00
Rhet Turnbull
6bf24ad2de Feature import parse date 847 (#850)
* Working on #847

* Added additional help for --parse-date

* Added tests for --parse-date
2022-11-23 22:56:02 -08:00
Rhet Turnbull
d6fc8fc3b1 Added test for #848 2022-11-19 18:09:47 -08:00
Rhet Turnbull
003531b052 Updated CHANGELOG.md [skip ci] 2022-11-19 14:16:18 -08:00
allcontributors[bot]
e673ab64ce add zephyr325 as a contributor for bug (#844)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-11-16 20:54:51 -08:00
Rhet Turnbull
9ed1b394a9 Version bump 2022-11-16 20:52:12 -08:00
Rhet Turnbull
40de05c5fd Fix for timewarp failure on Ventura, #841 2022-11-16 20:47:19 -08:00
Rhet Turnbull
f610d3cc65 Updated search_info test 2022-11-15 20:49:28 -08:00
Rhet Turnbull
d0232284f0 Updated CHANGELOG.md [skip ci] 2022-11-14 22:25:42 -08:00
Rhet Turnbull
548071e8a6 Version bump 2022-11-14 21:53:04 -08:00
Rhet Turnbull
a727dc9294 Added --alt-copy method for #807 (#835) 2022-11-14 21:42:02 -08:00
Rhet Turnbull
ea76297800 Fixed help text for , #828 2022-11-14 06:01:00 -08:00
Rhet Turnbull
0dca5d2154 Updated CHANGELOG.md [skip ci] 2022-11-13 21:57:16 -08:00
Rhet Turnbull
155f29a373 Updated docs 2022-11-13 21:43:57 -08:00
Rhet Turnbull
644582b540 Updated build script, dev dependencies 2022-11-13 21:43:22 -08:00
Rhet Turnbull
f957e43ee1 Version bump 2022-11-13 21:25:13 -08:00
Rhet Turnbull
0995076fe7 Updated dependencies for #832 2022-11-13 21:20:21 -08:00
Rhet Turnbull
c2c2da6c95 Bug search info macos13 816 (#831)
* Fixed SearchInfo for macOS 13, #816

* Additional fixes for SearchInfo, #816
2022-11-13 21:17:43 -08:00
allcontributors[bot]
de14583215 add dmd as a contributor for userTesting (#829)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-11-13 08:00:13 -08:00
Rhet Turnbull
831eecfdf7 Added tests for macOS 13 / Ventura, added test for labels on macOS 13, #816 2022-11-13 07:57:00 -08:00
Rhet Turnbull
04a0b8a13e Updated CHANGELOG.md [skip ci] 2022-11-12 20:27:06 -08:00
Rhet Turnbull
e9c1d494fd Removed note about pipx error [skip ci] 2022-11-12 20:14:01 -08:00
Rhet Turnbull
dc1a600493 Version bump 2022-11-12 20:07:41 -08:00
Rhet Turnbull
0c8d7e171f Added .sourcery.yaml 2022-11-12 20:03:16 -08:00
Rhet Turnbull
ff981ddc0a Updated dependencies for python 3.11, #817, #825 2022-11-12 20:02:18 -08:00
Rhet Turnbull
6dc91fbc94 Added python 3.11 2022-11-12 19:35:25 -08:00
Rhet Turnbull
7d72499ac2 Updated dependencies for python 3.11, #817, #825 2022-11-12 19:34:33 -08:00
Rhet Turnbull
5364e93e64 Updated CHANGELOG.md [skip ci] 2022-11-12 11:45:44 -08:00
allcontributors[bot]
6b93363e57 add dmd as a contributor for bug (#824)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-11-12 11:25:05 -08:00
Rhet Turnbull
ae560d24cb Bug labels ventura 816 (#823)
* Partial fix for #816, labels on Ventura / macOS 13.0

* Version bump
2022-11-12 11:22:44 -08:00
Rhet Turnbull
6dbeaae541 Updated example to match API 2022-11-08 05:57:04 -08:00
Rhet Turnbull
2e38a56f26 Added instructions for python 3.11/pipx 2022-11-07 22:13:22 -08:00
Rhet Turnbull
dcc16c92c1 Added ImportInfo __bool__, #820 2022-11-07 22:11:13 -08:00
Rhet Turnbull
2cd61dccf9 Update README.md 2022-11-07 05:44:05 -08:00
Rhet Turnbull
57ee40ecb7 Updated CHANGELOG.md [skip ci] 2022-11-06 19:21:31 -08:00
Rhet Turnbull
61ac447e3e Updated dependencies 2022-11-06 19:15:02 -08:00
Rhet Turnbull
6fa07d48c5 Fixed typo in requirements.txt 2022-11-06 18:16:24 -08:00
Rhet Turnbull
d91bf14790 Version bump 2022-11-06 17:55:54 -08:00
allcontributors[bot]
2f9b0b8fa1 add dalisoft as a contributor for code, and test (#806)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-10-16 14:21:22 -07:00
Davlatjon Shavkatov
7b28d7c24b fix: remove warning for macOS 11.7 (#805)
This commit should fix the case where warning appears on macOS 11.7

```
2022-10-16 21:11:25,014 - WARNING - photosdb.py - 119 - WARNING: This module has only been tested with macOS versions [10.12, 10.13, 10.14, 10.15, 10.16, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 12.0, 12.1, 12.2, 12.3, 12.4, 12.5, 12.6]: you have Darwin, OS version: 11.7
```

macOS 11.7 fixes only security patches as i known and i'm did not noticed any new feature or broken feature, so i think it should work
2022-10-16 14:19:12 -07:00
Rhet Turnbull
5665cf1804 Refactor update osxmetadata (#804)
* Updated osxmetadata to use v1.0.0

* Added README_DEV

* fix for missing detected_text xattr

* fix for missing detected_text xattr
2022-10-15 22:12:11 -07:00
Rhet Turnbull
0ba8bc3eb9 Bugfix for bare {filepath} template 2022-10-13 21:00:49 -07:00
Rhet Turnbull
c75d357249 Updated CHANGELOG.md [skip ci] 2022-09-28 09:00:17 -07:00
Rhet Turnbull
00eb80044e Release 0.51.8, bug fix for exportdb migration 2022-09-25 09:04:52 -07:00
Rhet Turnbull
191a2cd618 Bugfix exportdb migration 794 (#795)
* Fixed migration for already migrated database, #794

* Version bump
2022-09-25 07:55:15 -07:00
Rhet Turnbull
eedc2f0b05 Tested on 12.6, #792 2022-09-17 20:33:12 -07:00
Rhet Turnbull
43fcdbc371 Updated dependencies 2022-09-17 09:03:40 -07:00
Rhet Turnbull
b5b7a28539 Release 0.51.7, support for reading iPhone Photos.sqlite 2022-09-11 22:31:33 -07:00
Rhet Turnbull
3cb4be4254 Feature read iphone db #745 (#791)
* Added ability to read iPhone database

* Added comments

* Fixed tests

* Fixed albums for iPhone database
2022-09-11 22:26:45 -07:00
Snyk bot
d8d2b1c4a1 fix: requirements.txt to reduce vulnerabilities (#789)
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-MAKO-3017600
2022-09-06 22:12:01 -07:00
Snyk bot
3ac2b3c290 fix: dev_requirements.txt to reduce vulnerabilities (#725)
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-MISTUNE-2940625
2022-09-06 22:11:16 -07:00
Rhet Turnbull
83ce702a46 Updated QR Code example 2022-09-05 18:32:09 -07:00
Rhet Turnbull
74a730e420 Added QR code example 2022-09-05 10:06:29 -07:00
Rhet Turnbull
5391d1059c Refactor phototemplate (#788)
* Refactored date code in phototemplate.py

* Refactored date code in phototemplate.py

* Refactored place values in PhotoTemplate

* Refactored place values in PhotoTemplate
2022-09-04 15:13:59 -07:00
Rhet Turnbull
9b175d17d6 Refactored ExportResults (#786) 2022-09-03 21:01:48 -07:00
Rhet Turnbull
d5a9001661 Updated PhotosAlbum code 2022-09-02 18:07:09 -07:00
Rhet Turnbull
fe514e79ab Added close for import_db 2022-09-01 20:46:08 -07:00
Rhet Turnbull
cee2acad53 Updated sqlitekvstore 2022-09-01 06:17:15 -07:00
Rhet Turnbull
e605ed847d Updated CHANGELOG.md [skip ci] 2022-08-31 22:32:27 -07:00
Rhet Turnbull
5794df0069 Release 0.51.6, added --resume to import 2022-08-31 22:30:01 -07:00
Rhet Turnbull
b8ad8016aa Added --resume to import, #768 (#784) 2022-08-31 22:14:49 -07:00
Rhet Turnbull
026b86abe9 Updated CHANGELOG.md [skip ci] 2022-08-27 21:26:01 -07:00
Rhet Turnbull
3ca0e2bb5b Added release files for 0.51.5 2022-08-27 21:25:17 -07:00
Rhet Turnbull
e39776b51e Added --field to dump and query, #777 (#779) 2022-08-27 21:05:57 -07:00
Rhet Turnbull
02d772c921 Updated CHANGELOG.md [skip ci] 2022-08-27 13:48:39 -07:00
allcontributors[bot]
817bdf0604 docs: add tkrunning as a contributor for code, bug (#776)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-08-27 13:47:48 -07:00
allcontributors[bot]
a74520f747 docs: add jmuccigr as a contributor for bug, ideas (#775)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-08-27 13:46:13 -07:00
Rhet Turnbull
bb480f6991 Release 0.51.4, added --print to dump 2022-08-27 11:25:46 -07:00
Rhet Turnbull
af9311c9c8 Fixed --print to work with {tab} 2022-08-27 11:11:38 -07:00
Rhet Turnbull
b12d112793 Updated CHANGELOG.md [skip ci] 2022-08-27 10:53:42 -07:00
Rhet Turnbull
5eaeb72c3e Added --print to dump, added {tab} 2022-08-27 10:50:25 -07:00
Rhet Turnbull
320fb86559 Added bump2version 2022-08-27 09:47:52 -07:00
Rhet Turnbull
d576ca5494 Release 0.51.3, added --print (#769), --quiet (#770) 2022-08-27 09:31:37 -07:00
Rhet Turnbull
8e986b451e Added --print, --quiet, #769, #770 (#773) 2022-08-26 22:17:12 -07:00
Rhet Turnbull
f0bdfb5eac Updated CHANGELOG.md [skip ci] 2022-08-26 07:03:59 -07:00
Rhet Turnbull
4ca681ee4f Release 0.51.2, added new filter(x) to template filters (#772) 2022-08-26 06:55:45 -07:00
Rhet Turnbull
66f6002a57 Feature filter filter 759 (#771)
* Added filter(x) filter, #759

* Added int, float filters
2022-08-26 06:39:32 -07:00
Rhet Turnbull
d845e9b66e Release 0.51.1, added --report to import (#767) 2022-08-22 09:31:47 -07:00
Rhet Turnbull
991511af07 Added --report to import command (#766) 2022-08-22 07:29:53 -07:00
Rhet Turnbull
3c98906158 Fixed template function to work with import command (#765) 2022-08-21 14:33:18 -07:00
Rhet Turnbull
08b806ff7d Updated CHANGELOG.md [skip ci] 2022-08-21 12:39:28 -07:00
Rhet Turnbull
b5f4c48ec9 Updated README [skip ci] 2022-08-21 12:37:56 -07:00
Rhet Turnbull
b7cfd1da9b Release 0.51.0 (#763) 2022-08-21 09:45:32 -07:00
Rhet Turnbull
c88fc75013 Feature add import 754 (#762)
* Initial alpha version of import command

* Refactored

* Improved help, added --clear-metadata

* Added --clear-metadata, --exiftool to import

* Added --keyword, --title, --description

* Added --location

* Added test for --location

* Changed --auto-folder to --split-folder, added docs

* Added --walk, updated docs

* Added --check-templates

* Updated help text for import
2022-08-21 09:07:22 -07:00
Rhet Turnbull
46738d05b2 Updated xmp_rating example 2022-08-15 10:13:25 -07:00
Rhet Turnbull
1e053aa708 Updated xmp_rating example 2022-08-15 10:08:00 -07:00
Rhet Turnbull
c7e3a552db Updated examples [skip ci] 2022-08-14 14:39:51 -07:00
Rhet Turnbull
5979d6245d Updated tested versions (#757) 2022-08-13 11:44:15 -07:00
Rhet Turnbull
099afdb3ad Updated CHANGELOG.md [skip ci] 2022-08-13 08:59:26 -07:00
Rhet Turnbull
8e3578e29b Release 0.50.13 (#756) 2022-08-13 08:50:16 -07:00
Rhet Turnbull
38a5998063 Feature orphans (#755)
* Initial implementation of orphan command

* Implemented orphans command, #84
2022-08-13 08:43:02 -07:00
Rhet Turnbull
2103d8bcad Added bad_photos example [skip ci] 2022-08-11 22:06:33 -07:00
Rhet Turnbull
26a9028497 Add PhotosAlbumPhotosKit to __all__ 2022-08-10 21:37:33 -06:00
Rhet Turnbull
e41f89480a Add PhotosAlbum to osxphotos __all__ 2022-08-10 20:53:07 -06:00
Rhet Turnbull
db3c37fb5b Hot fix for 749 (#750) 2022-08-08 06:26:08 -07:00
Rhet Turnbull
3d83e184b8 Added strip_live.py example (#747) 2022-08-06 21:19:51 -07:00
Rhet Turnbull
28e03ee86a Added reddit badge for r/osxphotos (#746) 2022-08-02 07:11:55 -07:00
Rhet Turnbull
fac45c7141 Updated CHANGELOG.md [skip ci] 2022-08-01 21:00:44 -07:00
Rhet Turnbull
f4154e8691 Feature not reference 738 (#744)
* Added --not-reference, 738

* Release files for #738
2022-07-28 06:24:58 -07:00
allcontributors[bot]
b4c4d9fb90 docs: add nullpointerninja as a contributor for ideas (#743)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-27 09:46:19 -07:00
allcontributors[bot]
402bbbdbae docs: add franzone as a contributor for bug (#742)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-27 09:44:25 -07:00
Rhet Turnbull
f7771909d6 Updated docs for v0.58.10 [skip-ci] (#741) 2022-07-27 08:25:06 -07:00
Rhet Turnbull
76625b9e84 Feature add keep 730 (#740)
* Implemented #730, --keep

* Implemented #739, fixed --keep to accept relative paths

* Updated to macos-latest

Ref: https://github.com/actions/virtual-environments/issues/5583

* Release files for #730
2022-07-27 08:14:00 -07:00
allcontributors[bot]
fb4329e0ed docs: add Se7enair as a contributor for ideas (#737)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-24 04:50:20 -07:00
Rhet Turnbull
2b52460de7 Updated CHANGELOG.md [skip ci] 2022-07-23 18:27:16 -07:00
Rhet Turnbull
f8f9bd7b93 Release files for #732, add --favorite-rating 2022-07-23 18:25:07 -07:00
Rhet Turnbull
5d33dcdcc3 Implemented --favorite-rating, #732 2022-07-23 18:16:06 -07:00
Rhet Turnbull
b4caea15fa Updated CHANGELOG.md [skip ci] 2022-07-23 16:44:50 -07:00
Rhet Turnbull
6e33540e60 Fixed report_summart view 2022-07-23 16:42:02 -07:00
Rhet Turnbull
2e85f9be89 Fixed report_summart view 2022-07-23 16:33:24 -07:00
Rhet Turnbull
7484c7b994 Added report_summary view to export report database 2022-07-23 16:27:49 -07:00
Rhet Turnbull
f279217118 Added report_summary view to export report database 2022-07-23 16:21:21 -07:00
Rhet Turnbull
b998684821 Updated CHANGELOG.md [skip ci] 2022-07-23 15:50:03 -07:00
Rhet Turnbull
f3557d1991 Updated docs [skip ci] 2022-07-23 15:42:34 -07:00
Rhet Turnbull
855d417e81 Refactored implementation for #731 2022-07-23 15:33:31 -07:00
Rhet Turnbull
bd33b61882 Implemented #731, export_id in report database 2022-07-23 10:11:39 -07:00
allcontributors[bot]
337d422346 docs: add infused-kim as a contributor for ideas (#736)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-23 07:30:47 -07:00
Rhet Turnbull
7497a02aaf Added live video and raw photo size to inspect, #734 2022-07-23 07:26:39 -07:00
Rhet Turnbull
1fadea8864 Updated CHANGELOG.md [skip ci] 2022-07-15 22:56:10 -04:00
Rhet Turnbull
5a43fb7410 Bug fix for #726 2022-07-15 22:53:09 -04:00
Rhet Turnbull
30bf06e794 Possible fix for #726 2022-07-15 22:38:47 -04:00
Rhet Turnbull
091adc9925 Updated CHANGELOG.md [skip ci] 2022-07-01 08:36:51 -07:00
allcontributors[bot]
f000f21ba5 docs: add nullpointerninja as a contributor for bug (#724)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-01 08:35:02 -07:00
Rhet Turnbull
8ac584e708 Fix for large files and exiftool, #722 2022-07-01 08:14:56 -07:00
Rhet Turnbull
292fdf3c74 Fix for large files and exiftool, #722 (#723) 2022-07-01 08:03:21 -07:00
Rhet Turnbull
c20a3994c0 Added example [skip ci] 2022-06-20 00:00:55 -07:00
Rhet Turnbull
7d84b3d6cc Updated README.md [skip ci] 2022-06-18 11:50:13 -07:00
Rhet Turnbull
6b0db223a7 Updated README.md [skip ci] 2022-06-18 11:48:15 -07:00
Rhet Turnbull
451e01e65e Updated CHANGELOG.md [skip ci] 2022-06-17 15:08:07 -07:00
347 changed files with 29917 additions and 5911 deletions

View File

@@ -54,7 +54,9 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/41439?v=4",
"profile": "http://3e.org/",
"contributions": [
"code"
"code",
"bug",
"userTesting"
]
},
{
@@ -326,6 +328,92 @@
"contributions": [
"code"
]
},
{
"login": "nullpointerninja",
"name": "nullpointerninja",
"avatar_url": "https://avatars.githubusercontent.com/u/62975432?v=4",
"profile": "https://github.com/nullpointerninja",
"contributions": [
"bug",
"ideas"
]
},
{
"login": "infused-kim",
"name": "Kim",
"avatar_url": "https://avatars.githubusercontent.com/u/7404004?v=4",
"profile": "https://github.com/infused-kim",
"contributions": [
"ideas"
]
},
{
"login": "Se7enair",
"name": "Christoph",
"avatar_url": "https://avatars.githubusercontent.com/u/1680106?v=4",
"profile": "https://github.com/Se7enair",
"contributions": [
"ideas"
]
},
{
"login": "franzone",
"name": "franzone",
"avatar_url": "https://avatars.githubusercontent.com/u/900684?v=4",
"profile": "http://www.franzone.com",
"contributions": [
"bug"
]
},
{
"login": "jmuccigr",
"name": "John Muccigrosso",
"avatar_url": "https://avatars.githubusercontent.com/u/615115?v=4",
"profile": "http://jmuccigr.github.io/",
"contributions": [
"bug",
"ideas"
]
},
{
"login": "tkrunning",
"name": "Thomas K. Running",
"avatar_url": "https://avatars.githubusercontent.com/u/1646041?v=4",
"profile": "https://nomadgate.com",
"contributions": [
"code",
"bug"
]
},
{
"login": "dalisoft",
"name": "Davlatjon Shavkatov",
"avatar_url": "https://avatars.githubusercontent.com/u/3511344?v=4",
"profile": "http://dalisoft.uz",
"contributions": [
"code",
"test"
]
},
{
"login": "zephyr325",
"name": "zephyr325",
"avatar_url": "https://avatars.githubusercontent.com/u/5245609?v=4",
"profile": "https://github.com/zephyr325",
"contributions": [
"bug"
]
},
{
"login": "drodner",
"name": "drodner",
"avatar_url": "https://avatars.githubusercontent.com/u/10236892?v=4",
"profile": "https://github.com/drodner",
"contributions": [
"bug",
"userTesting"
]
}
],
"contributorsPerLine": 7,

8
.bumpversion.cfg Normal file
View File

@@ -0,0 +1,8 @@
[bumpversion]
current_version = 0.55.2
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
serialize = {major}.{minor}.{patch}
[bumpversion:file:osxphotos/_version.py]
parse = __version__\s=\s\"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\"
serialize = {major}.{minor}.{patch}

View File

@@ -9,13 +9,13 @@ jobs:
strategy:
max-parallel: 4
matrix:
os: [macos-10.15]
python-version: ['3.8', '3.9', '3.10']
os: [macos-latest]
python-version: ['3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

2
.sourcery.yaml Normal file
View File

@@ -0,0 +1,2 @@
refactor:
python_version: '3.9'

View File

@@ -1815,6 +1815,9 @@ Valid filters are:
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
- `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
- `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
- `filter(x)`: Filter list of values using predicate x; for example, `{folder_album|filter(contains Events)}` returns only folders/albums containing the word 'Events' in their path.
- `int`: Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float.
- `float`: Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int.
e.g. if Photo keywords are `["FOO","bar"]`:
@@ -1929,6 +1932,7 @@ cog.out(get_template_field_table())
|{edited}|True if photo has been edited (has adjustments), otherwise False; use in format '{edited?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|{edited_version}|True if template is being rendered for the edited version of a photo, otherwise False. |
|{favorite}|Photo has been marked as favorite?; True/False value, use in format '{favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|{created}|Photo's creation date in ISO format, e.g. '2020-03-22'|
|{created.date}|Photo's creation date in ISO format, e.g. '2020-03-22'|
|{created.year}|4-digit year of photo creation time|
|{created.yy}|2-digit year of photo creation time|
@@ -1942,6 +1946,7 @@ cog.out(get_template_field_table())
|{created.min}|2-digit minute of the photo creation time|
|{created.sec}|2-digit second of the photo creation time|
|{created.strftime}|Apply strftime template to file creation date/time. Should be used in form {created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {created.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. See https://strftime.org/ for help on strftime templates.|
|{modified}|Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified|
|{modified.date}|Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified|
|{modified.year}|4-digit year of photo modification time; uses creation date if photo is not modified|
|{modified.yy}|2-digit year of photo modification time; uses creation date if photo is not modified|
@@ -1955,6 +1960,7 @@ cog.out(get_template_field_table())
|{modified.min}|2-digit minute of the photo modification time; uses creation date if photo is not modified|
|{modified.sec}|2-digit second of the photo modification time; uses creation date if photo is not modified|
|{modified.strftime}|Apply strftime template to file modification date/time. Should be used in form {modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. Uses creation date if photo is not modified. See https://strftime.org/ for help on strftime templates.|
|{today}|Current date in iso format, e.g. '2020-03-22'|
|{today.date}|Current date in iso format, e.g. '2020-03-22'|
|{today.year}|4-digit year of current date|
|{today.yy}|2-digit year of current date|
@@ -2004,8 +2010,9 @@ cog.out(get_template_field_table())
|{newline}|A newline: '\n'|
|{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.50.4'|
|{crlf}|A carriage return + line feed: '\r\n'|
|{tab}|:A tab: '\t'|
|{osxphotos_version}|The osxphotos version, e.g. '0.55.2'|
|{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|
@@ -2050,10 +2057,11 @@ True
['Keyword1', 'Keyword2', 'Keyword3']
```
`ExifTool(filepath, exiftool=None)`
`ExifTool(filepath, exiftool=None, large_file_support=True)`
* `filepath`: str, path to photo
* `exiftool`: str, optional path to `exiftool`; if not provided, will look for `exiftool` in the system path
* `large_file_support`: bool, if True, enables large file support in exiftool (`-api largefilesupport=1`)
#### ExifTool methods
@@ -2196,9 +2204,9 @@ Attributes:
### <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:
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 `xattr` as follows:
`osxmetadata --clear osxphotos.metadata:detected_text --walk ~/Pictures/Photos\ Library.photoslibrary/`
`find ~/Pictures/Photos\ Library.photoslibrary | xargs -I{} xattr -c osxphotos.metadata:detected_text '{}'`
### Utility Functions

View File

@@ -4,6 +4,271 @@ 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.55.1](https://github.com/RhetTbull/osxphotos/compare/v0.55.0...v0.55.1)
> 11 December 2022
- Bug edited path bad mojave 859 [`#864`](https://github.com/RhetTbull/osxphotos/pull/864)
- Version bump, fix for #859, wrong edited path in Mojave [`e4faf37`](https://github.com/RhetTbull/osxphotos/commit/e4faf3779c6c56982fba909a0efda21b86890b73)
- Update tests.yml [`debc001`](https://github.com/RhetTbull/osxphotos/commit/debc001af9684d04a31836a6fa5705b706eb36f0)
- Fixed edit_resource_id for Photos 5+ [`025ee36`](https://github.com/RhetTbull/osxphotos/commit/025ee36086d1515aa16a0018aaa5ae371a8a332d)
#### [v0.55.0](https://github.com/RhetTbull/osxphotos/compare/v0.54.4...v0.55.0)
> 11 December 2022
- Added Ventura to list of supported OS [`#863`](https://github.com/RhetTbull/osxphotos/pull/863)
- Partial fix for #859, missing path edited on Mojave [`#862`](https://github.com/RhetTbull/osxphotos/pull/862)
- add drodner as a contributor for bug, and userTesting [`#861`](https://github.com/RhetTbull/osxphotos/pull/861)
- Updated build for Ventura [`327f198`](https://github.com/RhetTbull/osxphotos/commit/327f19809ee0f8883977a27eb547dcc7f9e93e11)
- Added target architecture, #857 [`88e56bc`](https://github.com/RhetTbull/osxphotos/commit/88e56bc0b978d75b606a4adf36fa2d77ef16eb95)
#### [v0.54.4](https://github.com/RhetTbull/osxphotos/compare/v0.54.3...v0.54.4)
> 24 November 2022
- Added --post-function to import, #842 [`#851`](https://github.com/RhetTbull/osxphotos/pull/851)
- Feature import parse date 847 [`#850`](https://github.com/RhetTbull/osxphotos/pull/850)
- Version bump for release [`cad4e1e`](https://github.com/RhetTbull/osxphotos/commit/cad4e1eeff54a37826c0e08e2be1b3df3b392f94)
- Added test for #848 [`d6fc8fc`](https://github.com/RhetTbull/osxphotos/commit/d6fc8fc3b1d276fd6b22550e50ec1bdeeb3acf6f)
#### [v0.54.3](https://github.com/RhetTbull/osxphotos/compare/v0.54.2...v0.54.3)
> 16 November 2022
- add zephyr325 as a contributor for bug [`#844`](https://github.com/RhetTbull/osxphotos/pull/844)
- Version bump [`9ed1b39`](https://github.com/RhetTbull/osxphotos/commit/9ed1b394a9b2df1eca04f489c083ca3a71a7809c)
- Fix for timewarp failure on Ventura, #841 [`40de05c`](https://github.com/RhetTbull/osxphotos/commit/40de05c5fdbc8efd8e4bd21eb8b2e17d49f4864e)
- Updated search_info test [`f610d3c`](https://github.com/RhetTbull/osxphotos/commit/f610d3cc65a7909cfe3bd9ad4d5209f193c88a87)
#### [v0.54.2](https://github.com/RhetTbull/osxphotos/compare/v0.54.1...v0.54.2)
> 14 November 2022
- Added --alt-copy method for #807 [`#835`](https://github.com/RhetTbull/osxphotos/pull/835)
- Version bump [`548071e`](https://github.com/RhetTbull/osxphotos/commit/548071e8a6f626b1f22ae7c92d209dd98bf83c27)
- Fixed help text for , #828 [`ea76297`](https://github.com/RhetTbull/osxphotos/commit/ea76297800f3e72e6584618c126fe818f21bc1ae)
#### [v0.54.1](https://github.com/RhetTbull/osxphotos/compare/v0.54.0...v0.54.1)
> 13 November 2022
- Bug search info macos13 816 [`#831`](https://github.com/RhetTbull/osxphotos/pull/831)
- add dmd as a contributor for userTesting [`#829`](https://github.com/RhetTbull/osxphotos/pull/829)
- Updated docs [`155f29a`](https://github.com/RhetTbull/osxphotos/commit/155f29a3735e8c93eaa66f3d979cb1a12b7cd4f8)
- Updated build script, dev dependencies [`644582b`](https://github.com/RhetTbull/osxphotos/commit/644582b540c0b4928a2ece3eb3e56eb63af78877)
- Added tests for macOS 13 / Ventura, added test for labels on macOS 13, #816 [`831eecf`](https://github.com/RhetTbull/osxphotos/commit/831eecfdf70992a2aae8f2454a3b96a44ec85e9c)
- Version bump [`f957e43`](https://github.com/RhetTbull/osxphotos/commit/f957e43ee1242f6902b93e36150233b0cab8a42c)
- Updated dependencies for #832 [`0995076`](https://github.com/RhetTbull/osxphotos/commit/0995076fe78e11124b207e6d3796d834582d506f)
#### [v0.54.0](https://github.com/RhetTbull/osxphotos/compare/v0.53.0...v0.54.0)
> 12 November 2022
- Version bump [`dc1a600`](https://github.com/RhetTbull/osxphotos/commit/dc1a600493b0b3ef598b34a321b0d25b9c7424ac)
- Updated dependencies for python 3.11, #817, #825 [`ff981dd`](https://github.com/RhetTbull/osxphotos/commit/ff981ddc0ae2280636e827e421ccee74ed8ad9e9)
- Updated dependencies for python 3.11, #817, #825 [`7d72499`](https://github.com/RhetTbull/osxphotos/commit/7d72499ac2700c5b53528f817af2f79b0f242057)
#### [v0.53.0](https://github.com/RhetTbull/osxphotos/compare/v0.52.0...v0.53.0)
> 12 November 2022
- add dmd as a contributor for bug [`#824`](https://github.com/RhetTbull/osxphotos/pull/824)
- Bug labels ventura 816 [`#823`](https://github.com/RhetTbull/osxphotos/pull/823)
- Added ImportInfo __bool__, #820 [`dcc16c9`](https://github.com/RhetTbull/osxphotos/commit/dcc16c92c16e5e59f6551e6561eaf5824470f3c3)
- Added instructions for python 3.11/pipx [`2e38a56`](https://github.com/RhetTbull/osxphotos/commit/2e38a56f26b873e235db715a64149b5b7129d2d8)
- Updated example to match API [`6dbeaae`](https://github.com/RhetTbull/osxphotos/commit/6dbeaae54174bafce01897599f782d02787d6fe7)
- Update README.md [`2cd61dc`](https://github.com/RhetTbull/osxphotos/commit/2cd61dccf9d36db02c83cbd82743699b9bf8dda6)
#### [v0.52.0](https://github.com/RhetTbull/osxphotos/compare/v0.51.8...v0.52.0)
> 6 November 2022
- add dalisoft as a contributor for code, and test [`#806`](https://github.com/RhetTbull/osxphotos/pull/806)
- fix: remove warning for macOS 11.7 [`#805`](https://github.com/RhetTbull/osxphotos/pull/805)
- Refactor update osxmetadata [`#804`](https://github.com/RhetTbull/osxphotos/pull/804)
- Version bump [`d91bf14`](https://github.com/RhetTbull/osxphotos/commit/d91bf14790616818dbb8b70431a4ee11601838aa)
- Updated dependencies [`61ac447`](https://github.com/RhetTbull/osxphotos/commit/61ac447e3e425b83a5eba986ed3dbe1d31c66105)
- Fixed typo in requirements.txt [`6fa07d4`](https://github.com/RhetTbull/osxphotos/commit/6fa07d48c55b615a695c309f007675d8b93ade2d)
- Bugfix for bare {filepath} template [`0ba8bc3`](https://github.com/RhetTbull/osxphotos/commit/0ba8bc3eb9caaa9fe7319fd694ef8d64263b9472)
#### [v0.51.8](https://github.com/RhetTbull/osxphotos/compare/v0.51.7...v0.51.8)
> 25 September 2022
- Bugfix exportdb migration 794 [`#795`](https://github.com/RhetTbull/osxphotos/pull/795)
- Release 0.51.8, bug fix for exportdb migration [`00eb800`](https://github.com/RhetTbull/osxphotos/commit/00eb80044e4623330b07de174e710597544847d6)
- Updated dependencies [`43fcdbc`](https://github.com/RhetTbull/osxphotos/commit/43fcdbc371a1142dc2f9a002f45764c97f30db6e)
- Tested on 12.6, #792 [`eedc2f0`](https://github.com/RhetTbull/osxphotos/commit/eedc2f0b059617dcd6809dc6603b4d96dc397071)
#### [v0.51.7](https://github.com/RhetTbull/osxphotos/compare/v0.51.6...v0.51.7)
> 11 September 2022
- Feature read iphone db #745 [`#791`](https://github.com/RhetTbull/osxphotos/pull/791)
- fix: requirements.txt to reduce vulnerabilities [`#789`](https://github.com/RhetTbull/osxphotos/pull/789)
- fix: dev_requirements.txt to reduce vulnerabilities [`#725`](https://github.com/RhetTbull/osxphotos/pull/725)
- Refactor phototemplate [`#788`](https://github.com/RhetTbull/osxphotos/pull/788)
- Refactored ExportResults [`#786`](https://github.com/RhetTbull/osxphotos/pull/786)
- Release 0.51.7, support for reading iPhone Photos.sqlite [`b5b7a28`](https://github.com/RhetTbull/osxphotos/commit/b5b7a2853940dbf68bfda8743ee6054f58fb88f8)
- Added QR code example [`74a730e`](https://github.com/RhetTbull/osxphotos/commit/74a730e420fc3e7ae23fba4394a5cfb4d27c8f76)
- Updated PhotosAlbum code [`d5a9001`](https://github.com/RhetTbull/osxphotos/commit/d5a900166103914f2d7c3e17ce4f14c38f6df9c3)
- Updated sqlitekvstore [`cee2aca`](https://github.com/RhetTbull/osxphotos/commit/cee2acad53af6762f234324c44bd28daf132b85f)
- Updated QR Code example [`83ce702`](https://github.com/RhetTbull/osxphotos/commit/83ce702a4690d3f5b570a26b0e684590789e28bb)
#### [v0.51.6](https://github.com/RhetTbull/osxphotos/compare/v0.51.5...v0.51.6)
> 31 August 2022
- Added --resume to import, #768 [`#784`](https://github.com/RhetTbull/osxphotos/pull/784)
- Added release files for 0.51.5 [`3ca0e2b`](https://github.com/RhetTbull/osxphotos/commit/3ca0e2bb5b7f5333b55381aded3300ed09162e1c)
- Release 0.51.6, added --resume to import [`5794df0`](https://github.com/RhetTbull/osxphotos/commit/5794df00694370fc01b2f3001a1eb53ddf9f7e23)
#### [v0.51.5](https://github.com/RhetTbull/osxphotos/compare/v0.51.4...v0.51.5)
> 27 August 2022
- Added --field to dump and query, #777 [`#779`](https://github.com/RhetTbull/osxphotos/pull/779)
- docs: add tkrunning as a contributor for code, bug [`#776`](https://github.com/RhetTbull/osxphotos/pull/776)
- docs: add jmuccigr as a contributor for bug, ideas [`#775`](https://github.com/RhetTbull/osxphotos/pull/775)
#### [v0.51.4](https://github.com/RhetTbull/osxphotos/compare/v0.51.3...v0.51.4)
> 27 August 2022
- Release 0.51.4, added --print to dump [`bb480f6`](https://github.com/RhetTbull/osxphotos/commit/bb480f69914ed351b6f4309b0f6aa539add1a9fb)
- Added --print to dump, added {tab} [`5eaeb72`](https://github.com/RhetTbull/osxphotos/commit/5eaeb72c3ee296af6abc6ca6ddf8ad05baf02052)
- Fixed --print to work with {tab} [`af9311c`](https://github.com/RhetTbull/osxphotos/commit/af9311c9c86a3d0a5764ebd1539d40f14e62f2ec)
#### [v0.51.3](https://github.com/RhetTbull/osxphotos/compare/v0.51.2...v0.51.3)
> 27 August 2022
- Added --print, --quiet, #769, #770 [`#773`](https://github.com/RhetTbull/osxphotos/pull/773)
- Release 0.51.3, added --print (#769), --quiet (#770) [`d576ca5`](https://github.com/RhetTbull/osxphotos/commit/d576ca54948c0cbbd16f04231459a294f0333f89)
- Added bump2version [`320fb86`](https://github.com/RhetTbull/osxphotos/commit/320fb8655980882fd75e354d33450f0904babf2a)
#### [v0.51.2](https://github.com/RhetTbull/osxphotos/compare/v0.51.1...v0.51.2)
> 26 August 2022
- Release 0.51.2, added new filter(x) to template filters [`#772`](https://github.com/RhetTbull/osxphotos/pull/772)
- Feature filter filter 759 [`#771`](https://github.com/RhetTbull/osxphotos/pull/771)
#### [v0.51.1](https://github.com/RhetTbull/osxphotos/compare/v0.51.0...v0.51.1)
> 22 August 2022
- Release 0.51.1, added --report to import [`#767`](https://github.com/RhetTbull/osxphotos/pull/767)
- Added --report to import command [`#766`](https://github.com/RhetTbull/osxphotos/pull/766)
- Fixed template function to work with import command [`#765`](https://github.com/RhetTbull/osxphotos/pull/765)
- Updated README [skip ci] [`b5f4c48`](https://github.com/RhetTbull/osxphotos/commit/b5f4c48ec98b344b5d7ed7c2a5e9a445d322df13)
#### [v0.51.0](https://github.com/RhetTbull/osxphotos/compare/v0.50.13...v0.51.0)
> 21 August 2022
- Release 0.51.0 [`#763`](https://github.com/RhetTbull/osxphotos/pull/763)
- Feature add import 754 [`#762`](https://github.com/RhetTbull/osxphotos/pull/762)
- Updated tested versions [`#757`](https://github.com/RhetTbull/osxphotos/pull/757)
- Updated examples [skip ci] [`c7e3a55`](https://github.com/RhetTbull/osxphotos/commit/c7e3a552db60321fc9999153b6b5624bc1bb76dc)
- Updated xmp_rating example [`1e053aa`](https://github.com/RhetTbull/osxphotos/commit/1e053aa7086af44a207d1be045d698c7d10b97f5)
- Updated xmp_rating example [`46738d0`](https://github.com/RhetTbull/osxphotos/commit/46738d05b213d1d8ef390add71142623892385ce)
#### [v0.50.13](https://github.com/RhetTbull/osxphotos/compare/v0.50.12...v0.50.13)
> 13 August 2022
- Release 0.50.13 [`#756`](https://github.com/RhetTbull/osxphotos/pull/756)
- Feature orphans [`#755`](https://github.com/RhetTbull/osxphotos/pull/755)
- Added bad_photos example [skip ci] [`2103d8b`](https://github.com/RhetTbull/osxphotos/commit/2103d8bcad65807d23f14315dbc4a76b3b6badfe)
- Add PhotosAlbumPhotosKit to __all__ [`26a9028`](https://github.com/RhetTbull/osxphotos/commit/26a9028497d147c047a4b73c5cf0fe964f7b2e00)
- Add PhotosAlbum to osxphotos __all__ [`e41f894`](https://github.com/RhetTbull/osxphotos/commit/e41f89480a37a19778c7b97fa0c8b0ceedfd56cd)
#### [v0.50.12](https://github.com/RhetTbull/osxphotos/compare/v0.50.11...v0.50.12)
> 8 August 2022
- Hot fix for 749 [`#750`](https://github.com/RhetTbull/osxphotos/pull/750)
- Added strip_live.py example [`#747`](https://github.com/RhetTbull/osxphotos/pull/747)
- Added reddit badge for r/osxphotos [`#746`](https://github.com/RhetTbull/osxphotos/pull/746)
#### [v0.50.11](https://github.com/RhetTbull/osxphotos/compare/v0.50.10...v0.50.11)
> 28 July 2022
- Feature not reference 738 [`#744`](https://github.com/RhetTbull/osxphotos/pull/744)
- docs: add nullpointerninja as a contributor for ideas [`#743`](https://github.com/RhetTbull/osxphotos/pull/743)
- docs: add franzone as a contributor for bug [`#742`](https://github.com/RhetTbull/osxphotos/pull/742)
#### [v0.50.10](https://github.com/RhetTbull/osxphotos/compare/v0.50.9...v0.50.10)
> 27 July 2022
- Updated docs for v0.58.10 [skip-ci] [`#741`](https://github.com/RhetTbull/osxphotos/pull/741)
- Feature add keep 730 [`#740`](https://github.com/RhetTbull/osxphotos/pull/740)
- docs: add Se7enair as a contributor for ideas [`#737`](https://github.com/RhetTbull/osxphotos/pull/737)
#### [v0.50.9](https://github.com/RhetTbull/osxphotos/compare/v0.50.8...v0.50.9)
> 23 July 2022
- Release files for #732, add --favorite-rating [`f8f9bd7`](https://github.com/RhetTbull/osxphotos/commit/f8f9bd7b933c077528649560d692ceb22d254768)
- Implemented --favorite-rating, #732 [`5d33dcd`](https://github.com/RhetTbull/osxphotos/commit/5d33dcdcc3af1ca9dfa11b7be2ab51f6906d9e61)
#### [v0.50.8](https://github.com/RhetTbull/osxphotos/compare/v0.50.7...v0.50.8)
> 23 July 2022
- Added report_summary view to export report database [`7484c7b`](https://github.com/RhetTbull/osxphotos/commit/7484c7b9942d430089039d203fb7dc37004e8af9)
- Fixed report_summart view [`2e85f9b`](https://github.com/RhetTbull/osxphotos/commit/2e85f9be891e1d762b548abbea7b5ca2b3ed7da3)
- Added report_summary view to export report database [`f279217`](https://github.com/RhetTbull/osxphotos/commit/f279217118e2051ecdb54d14fb207c627ab36a7e)
#### [v0.50.7](https://github.com/RhetTbull/osxphotos/compare/v0.50.6...v0.50.7)
> 23 July 2022
- docs: add infused-kim as a contributor for ideas [`#736`](https://github.com/RhetTbull/osxphotos/pull/736)
- Implemented #731, export_id in report database [`bd33b61`](https://github.com/RhetTbull/osxphotos/commit/bd33b61882fa746e9750be7cd80e6e2f785131b2)
- Refactored implementation for #731 [`855d417`](https://github.com/RhetTbull/osxphotos/commit/855d417e816d796165208658dc276ca378bf3337)
- Added live video and raw photo size to inspect, #734 [`7497a02`](https://github.com/RhetTbull/osxphotos/commit/7497a02aaf155bf719e4ccc2c9d3a443f351353a)
- Updated docs [skip ci] [`f3557d1`](https://github.com/RhetTbull/osxphotos/commit/f3557d1991021766dfa8a5018f95b3f7a95777b6)
#### [v0.50.6](https://github.com/RhetTbull/osxphotos/compare/0.50.5...v0.50.6)
> 15 July 2022
- docs: add nullpointerninja as a contributor for bug [`#724`](https://github.com/RhetTbull/osxphotos/pull/724)
- Bug fix for #726 [`5a43fb7`](https://github.com/RhetTbull/osxphotos/commit/5a43fb7410c2fc5407bbe41f4b7c6e3cefb54f0d)
- Possible fix for #726 [`30bf06e`](https://github.com/RhetTbull/osxphotos/commit/30bf06e79489005f85a72fa45a5986cac506fdad)
#### [0.50.5](https://github.com/RhetTbull/osxphotos/compare/v0.50.4...0.50.5)
> 1 July 2022
- Fix for large files and exiftool, #722 [`#723`](https://github.com/RhetTbull/osxphotos/pull/723)
- Added example [skip ci] [`c20a399`](https://github.com/RhetTbull/osxphotos/commit/c20a3994c01bddea2e3bc42a9656ac9e6c858f34)
- Updated README.md [skip ci] [`6b0db22`](https://github.com/RhetTbull/osxphotos/commit/6b0db223a7d43ab2dd0d2358e549f7014bf6dd32)
- Updated README.md [skip ci] [`7d84b3d`](https://github.com/RhetTbull/osxphotos/commit/7d84b3d6cc0305e381f5b4870a2a83640d3d7d2a)
#### [v0.50.4](https://github.com/RhetTbull/osxphotos/compare/v0.50.3...v0.50.4)
> 17 June 2022
- Initial support for Ventura developer preview [`#715`](https://github.com/RhetTbull/osxphotos/pull/715)
- Added initial support for macOS Ventura/13.0 beta [`51317a6`](https://github.com/RhetTbull/osxphotos/commit/51317a607c5e3788f9b6ebbde83027fa2e31cc4f)
- Added example [skip ci] [`561c684`](https://github.com/RhetTbull/osxphotos/commit/561c6846e40b4bbbc58c46a802309d56553238e1)
- Updated examples [skip ci] [`04c2f61`](https://github.com/RhetTbull/osxphotos/commit/04c2f6121affc313f98562959be2aa74de93dbe6)
- Updated examples [skip ci] [`56435b1`](https://github.com/RhetTbull/osxphotos/commit/56435b101fb99893916728206ec74154fbc203b3)
- Updated examples [skip ci] [`f47aa72`](https://github.com/RhetTbull/osxphotos/commit/f47aa721654e4c002bdfd7bef5997011d86564ec)
#### [v0.50.3](https://github.com/RhetTbull/osxphotos/compare/v0.50.2...v0.50.3)
> 29 May 2022
- Added --template to inspect command [`c2f02c3`](https://github.com/RhetTbull/osxphotos/commit/c2f02c3b7bf212c8c987868cd8a2374fcc8ffaf0)
- Fixed docs [`04e1149`](https://github.com/RhetTbull/osxphotos/commit/04e1149cadce034fc36fd6a593975432c6c99d07)
#### [v0.50.2](https://github.com/RhetTbull/osxphotos/compare/v0.50.1...v0.50.2)
> 28 May 2022
@@ -1138,7 +1403,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
> 17 April 2021
- Fixed bug for multi-field templates and --xattr-template, #422 [`6a28867`](https://github.com/RhetTbull/osxphotos/commit/6a288676a14ce23380181d43db19128afdda7731)
- Add @ubrandes as a contributor [`874ad2f`](https://github.com/RhetTbull/osxphotos/commit/874ad2fa34d8306c071cd479625a9aa97f6488b2)
- Add @ubrandes as a contributor [`874ad2f`](https://github.com/RhetTbull/osxphotos/commit/874ad2fa34d8306c071cd479625a9aa97f6488b2)
#### [v0.42.1](https://github.com/RhetTbull/osxphotos/compare/v0.41.11...v0.42.1)
@@ -1321,7 +1586,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
> 2 February 2021
- Add @davidjroos as a contributor [`8dbedef`](https://github.com/RhetTbull/osxphotos/commit/8dbedef1874882815afb4a885184249aae73bf9f)
- Add @davidjroos as a contributor [`8dbedef`](https://github.com/RhetTbull/osxphotos/commit/8dbedef1874882815afb4a885184249aae73bf9f)
- Fixed documentation, #359 [`77371b6`](https://github.com/RhetTbull/osxphotos/commit/77371b6e5d8a9b8662b7b7d540378beb897f6988)
#### [v0.40.5](https://github.com/RhetTbull/osxphotos/compare/v0.40.3...v0.40.5)
@@ -1373,7 +1638,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
> 18 January 2021
- Beta fix for Digikam reading XMP [`3799594`](https://github.com/RhetTbull/osxphotos/commit/379959447373f951ffca372598ea8f1d5834fe52)
- Add @martinhrpi as a contributor [`db43017`](https://github.com/RhetTbull/osxphotos/commit/db430173b59732f944ca52b53c928370684580df)
- Add @martinhrpi as a contributor [`db43017`](https://github.com/RhetTbull/osxphotos/commit/db430173b59732f944ca52b53c928370684580df)
#### [v0.39.21](https://github.com/RhetTbull/osxphotos/compare/v0.39.20...v0.39.21)
@@ -1409,8 +1674,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Fixed test for M1, added about command, closes #315 [`#315`](https://github.com/RhetTbull/osxphotos/issues/315)
- Fixed time zone for tests [`165f9b0`](https://github.com/RhetTbull/osxphotos/commit/165f9b08f5056d1f0b2ca7c74cec84d42b635663)
- Add @narensankar0529 as a contributor [`039118c`](https://github.com/RhetTbull/osxphotos/commit/039118c1aaa217f46354b351ea36b0729e3e1c35)
- Update @narensankar0529 as a contributor [`61f649e`](https://github.com/RhetTbull/osxphotos/commit/61f649e59d53a3e3011602476b72cc64951d38c0)
- Add @narensankar0529 as a contributor [`039118c`](https://github.com/RhetTbull/osxphotos/commit/039118c1aaa217f46354b351ea36b0729e3e1c35)
- Update @narensankar0529 as a contributor [`61f649e`](https://github.com/RhetTbull/osxphotos/commit/61f649e59d53a3e3011602476b72cc64951d38c0)
#### [v0.39.16](https://github.com/RhetTbull/osxphotos/compare/v0.39.15...v0.39.16)
@@ -1440,9 +1705,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Added PhotoInfo.visible, PhotoInfo.date_trashed, closes #333, #334 [`#333`](https://github.com/RhetTbull/osxphotos/issues/333)
- Create terminalizer-demo.yml [`5dc2eea`](https://github.com/RhetTbull/osxphotos/commit/5dc2eeaf9a7265873c81db23bbc86d3023189a26)
- Force cleanup of objects with autorelease pool [`b67f11a`](https://github.com/RhetTbull/osxphotos/commit/b67f11a3bb95c08a39a185b6d884092870e949f2)
- doc: Recorded screencast and updated of readme [`658e8ac`](https://github.com/RhetTbull/osxphotos/commit/658e8ac096d141fce48483dbfc1426bea317d806)
- doc: Recorded screencast and updated of readme [`658e8ac`](https://github.com/RhetTbull/osxphotos/commit/658e8ac096d141fce48483dbfc1426bea317d806)
- doc: fixed toc in readme [`aba50c5`](https://github.com/RhetTbull/osxphotos/commit/aba50c5c733420dc30f861d866a2c0bdc8933714)
- Add @Rott-Apple as a contributor [`71cb015`](https://github.com/RhetTbull/osxphotos/commit/71cb01572d2d946df18dd7b36f95b2f2e5b48f86)
- Add @Rott-Apple as a contributor [`71cb015`](https://github.com/RhetTbull/osxphotos/commit/71cb01572d2d946df18dd7b36f95b2f2e5b48f86)
#### [v0.39.11](https://github.com/RhetTbull/osxphotos/compare/v0.39.10...v0.39.11)
@@ -1498,7 +1763,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- doc simplify readme [`02ef0f9`](https://github.com/RhetTbull/osxphotos/commit/02ef0f9a254e83a3729a09cea1ae523407074896)
- Added exception handling/capture for convert-to-jpeg, issue #322 [`05f111a`](https://github.com/RhetTbull/osxphotos/commit/05f111a287e882ed6b451a550a87753501316aba)
- Cleanup up the readme [`38842ff`](https://github.com/RhetTbull/osxphotos/commit/38842ff9249e6f5b3069a88a759c8df97ddce51c)
- Add @synox as a contributor [`83915c6`](https://github.com/RhetTbull/osxphotos/commit/83915c65abb880036f80ebd830eb1e34292f9599)
- Add @synox as a contributor [`83915c6`](https://github.com/RhetTbull/osxphotos/commit/83915c65abb880036f80ebd830eb1e34292f9599)
#### [v0.39.5](https://github.com/RhetTbull/osxphotos/compare/v0.39.4...v0.39.5)
@@ -1644,7 +1909,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Documentation fix for #293. Thanks to @finestream [`#295`](https://github.com/RhetTbull/osxphotos/pull/295)
- Patch 1 [`#1`](https://github.com/RhetTbull/osxphotos/pull/1)
- Added additional test cases for #286, --ignore-signature [`880a9b6`](https://github.com/RhetTbull/osxphotos/commit/880a9b67a14787ef23ae68ad3164d7eda1af16ec)
- Add @finestream as a contributor [`ad860b1`](https://github.com/RhetTbull/osxphotos/commit/ad860b1500dffd846322e05562ba4f2019cd1017)
- Add @finestream as a contributor [`ad860b1`](https://github.com/RhetTbull/osxphotos/commit/ad860b1500dffd846322e05562ba4f2019cd1017)
- Fixed issue #296 [`a7c688c`](https://github.com/RhetTbull/osxphotos/commit/a7c688cfc2221833e0252d71bbe596eee5f9a6e8)
- Updated README.md [`d40b16a`](https://github.com/RhetTbull/osxphotos/commit/d40b16a456c64014674505b7c715c80b977da76a)
- Update __main__.py [`e097f3a`](https://github.com/RhetTbull/osxphotos/commit/e097f3aad546b5be5eabab529bd2c35ce3056876)
@@ -1766,7 +2031,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Fix EXIF GPS format for XMP sidecar, thanks to @jstrine for the fix! [`#270`](https://github.com/RhetTbull/osxphotos/pull/270)
- Continue even if the original filename is None, thanks to @jstrine for the fix! [`#268`](https://github.com/RhetTbull/osxphotos/pull/268)
- Added test for missing original_filename [`116cb66`](https://github.com/RhetTbull/osxphotos/commit/116cb662fbddf9153f6858c6ea97dc7f65c77705)
- Add @jstrine as a contributor [`7460bc8`](https://github.com/RhetTbull/osxphotos/commit/7460bc88fcc5e1e7435c9b9bcdf7ec9c7c5e39ea)
- Add @jstrine as a contributor [`7460bc8`](https://github.com/RhetTbull/osxphotos/commit/7460bc88fcc5e1e7435c9b9bcdf7ec9c7c5e39ea)
- Escape characters which cause XML parsing issues [`c42050a`](https://github.com/RhetTbull/osxphotos/commit/c42050a10cac40b0b5ac70c587e07f257a9b50dd)
- Fix tests for apostrophe [`d0d2e80`](https://github.com/RhetTbull/osxphotos/commit/d0d2e8080096bf66f93a830386800ce713680c51)
- Fix test for XMP sidecar with GPS info [`c27cfb1`](https://github.com/RhetTbull/osxphotos/commit/c27cfb1223fa82b9e5549b93c283e9444693270a)
@@ -1876,7 +2141,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Updated --exiftool to set dates/times as Photos does, issue #247 [`11459d1`](https://github.com/RhetTbull/osxphotos/commit/11459d1da4d7d13e36e9db4bdc940b74baad9d11)
- Partial fix for issue #247 on Mojave [`6ac3111`](https://github.com/RhetTbull/osxphotos/commit/6ac311199e9f7afe6170cbbd68ceaa1bb9f0682b)
- Add @mwort as a contributor [`9cff8e8`](https://github.com/RhetTbull/osxphotos/commit/9cff8e89c6e939d3d371a4f60649f6e5595a55b9)
- Add @mwort as a contributor [`9cff8e8`](https://github.com/RhetTbull/osxphotos/commit/9cff8e89c6e939d3d371a4f60649f6e5595a55b9)
#### [v0.36.2](https://github.com/RhetTbull/osxphotos/compare/v0.36.1...v0.36.2)

581
README.md
View File

@@ -5,8 +5,9 @@
[![tests](https://github.com/RhetTbull/osxphotos/workflows/Tests/badge.svg)](https://github.com/RhetTbull/osxphotos/workflows/Tests/badge.svg)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/osxphotos)
[![Downloads](https://static.pepy.tech/personalized-badge/osxphotos?period=month&units=international_system&left_color=black&right_color=brightgreen&left_text=downloads/month)](https://pepy.tech/project/osxphotos)
[![subreddit](https://img.shields.io/reddit/subreddit-subscribers/osxphotos?style=social)](https://www.reddit.com/r/osxphotos/)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-34-orange.svg?style=flat)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-43-orange.svg?style=flat)](#contributors)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS. You can query the Photos library database — for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc. You can also easily export both the original and edited photos.
@@ -36,16 +37,19 @@ Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) through mac
| macOS Version | macOS name | Photos.app version |
| ----------------- |------------|:-------------------|
| 12.0 | Monterey | 7.0 ✅ |
| 13.0 | Ventura | 8.0 ✅ * |
| 12.0 - 12.6 | Monterey | 7.0 ✅ * |
| 10.16, 11.0-11.4 | Big Sur | 6.0 ✅ |
| 10.15.1 - 10.15.7 | Catalina | 5.0 ✅ |
| 10.14.5, 10.14.6 | Mojave | 4.0 ✅ |
| 10.13.6 | High Sierra| 3.0 ✅ |
| 10.12.6 | Sierra | 2.0 ✅ |
\* Some features may not be fully supported on Monterey and Ventura. Notably, `--use-photokit` and `--download-missing` may or may not work depending on your configuration; this is a known issue that will be fixed if I can find a solution. Many users successfully use OSXPhotos on Monterey without problem.
This package will read Photos databases for any supported version on any supported macOS version. E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine running macOS 10.12 and vice versa.
Requires python >= `3.8`.
Requires python >= `3.9`.
## Installation
@@ -97,6 +101,12 @@ Once you've installed osxphotos via the git repository, to upgrade to the latest
You can also download a stand-alone pre-built executable--that doesn't require installing python--from the [releases](https://github.com/RhetTbull/osxphotos/releases) page. Look for the file with a name similar to `osxphotos_MacOS_exe_darwin_x64_v0.42.9.zip`. In this case `v0.42.9` specifies version 0.42.9. Unzip the file and put the included `osxphotos` binary in your system path. Currently, the binary is not signed or notarized so you'll have to authorize the app to run in the System Preferences | Security & Privacy settings. It's also likely this executable will not run on M1 Macs. If you don't know how to do this, I recommend using `pipx` as described above.
## Getting Help
OSXPhotos is well documented. See the [tutorial](#tutorial) for a description of key features. The tutorial can be accessed using the command `osxphotos tutorial` via the command line. If you are interested in using OSXPhotos in your own code, see [API_README.md](https://github.com/RhetTbull/osxphotos/blob/master/API_README.md) for a description of the API as well as the [example](https://github.com/RhetTbull/osxphotos/tree/master/examples) programs. The full documentation is [available online](https://rhettbull.github.io/osxphotos/) and can also be accessed using the command `osxphotos docs` via the command line.
If you have questions, would like to show off projects created with OSXPhotos, or if you just want to say hello, please use the [GitHub discussions forum](https://github.com/RhetTbull/osxphotos/discussions) or the [osxphotos subreddit](https://www.reddit.com/r/osxphotos/) on Reddit.
## Command Line Usage
This package will install a command line utility called `osxphotos` that allows you to query the Photos database. Alternatively, you can also run the command line utility like this: `python3 -m osxphotos`
@@ -130,12 +140,14 @@ Commands:
export Export photos from the Photos database.
exportdb Utilities for working with the osxphotos export database
help Print help; for help on commands: help <command>.
import Import photos and videos into Photos.
info Print out descriptive info of the Photos library database.
inspect Interactively inspect photos selected in Photos.
install Install Python packages into the same environment as osxphotos
keywords Print out keywords found in the Photos library.
labels Print out image classification labels found in the Photos...
list Print list of Photos libraries found on the system.
orphans Find orphaned photos in a Photos library
persons Print out persons (faces) found in the Photos library.
places Print out places found in the Photos library.
query Query the Photos database using 1 or more search options; if...
@@ -252,6 +264,22 @@ the exported files would be:
/path/to/export/Travel/IMG_1234.JPG
/path/to/export/Vacation/IMG_1234.JPG
If your photos are organized in folders and albums in Photos you can preserve this structure on export by using the `{folder_album}` template field with the `--directory` option. For example, if you have a photo in the album `Vacation` which is in the `Travel` folder, the following command would export the photo to the `Travel/Vacation` directory:
`osxphotos export /path/to/export --directory "{folder_album}"`
Photos can belong to more than one album. In this case, the template field `{folder_album}` will expand to all the album names that the photo belongs to. For example, if a photo belongs to the albums `Vacation` and `Travel`, the template field `{folder_album}` would expand to `Vacation`, `Travel`. If the photo belongs to no albums, the template field `{folder_album}` would expand to "_" (the default value).
All template fields including `{folder_album}` can be further filtered using a number of different filters. To convert all directory names to lower case for example, use the `lower` filter:
`osxphotos export /path/to/export --directory "{folder_album|lower}"`
If all your photos were organized into various albums under a folder named `Events` but some where also included in other top-level albums and you wanted to export only the `Events` folder, you could use the `filter` option to filter out the other top-level albums by selecting only those folder/album paths that start with `Events`:
`osxphotos export /path/to/export --directory "{folder_album|filter(startswith Events)}"`
You can learn more about the other filters using `osxphotos help export`.
#### Specify exported filename
By default, osxphotos will use the original filename of the photo when exporting. That is, the filename the photo had when it was taken or imported into Photos. This is often something like `IMG_1234.JPG` or `DSC05678.dng`. osxphotos allows you to specify a custom filename template using the `--filename` option in the same way as `--directory` allows you to specify a custom directory name. For example, Photos allows you specify a title or caption for a photo and you can use this in place of the original filename:
@@ -431,6 +459,14 @@ You can use the `--report` option to create a report, in comma-separated values
`osxphotos export /path/to/export --report export.csv`
You can also create reports in JSON or SQLite format by changing the extension of the report filename. For example, to create a JSON report:
`osxphotos export /path/to/export --report export.json`
And to create a SQLite report:
`osxphotos export /path/to/export --report export.sqlite`
#### Exporting only certain photos
By default, osxphotos will export your entire Photos library. If you want to export only certain photos, osxphotos provides a rich set of "query options" that allow you to query the Photos database to filter out only certain photos that match your query criteria. The tutorial does not cover all the query options as there are over 50 of them--read the help text (`osxphotos help export`) to better understand the available query options. No matter which subset of photos you would like to export, there is almost certainly a way for osxphotos to filter these. For example, you can filter for only images that contain certain keywords or images without a title, images from a specific time of day or specific date range, images contained in specific albums, etc.
@@ -763,6 +799,9 @@ Options:
--is-reference Search for photos that were imported as
referenced files (not copied into Photos
library).
--not-reference Search for photos that are not references,
that is, they were copied into the Photos
library and are managed by Photos.
--in-album Search for photos that are in one or more
albums.
--not-in-album Search for photos that are not in any albums.
@@ -1040,6 +1079,10 @@ Options:
--exiftool-merge-persons Merge any persons found in the original file
with persons used for '--exiftool' and '--
sidecar'.
--favorite-rating When used with --exiftool or --sidecar, set
XMP:Rating=5 for photos marked as Favorite and
XMP:Rating=0 for non-Favorites. If not
specified, XMP:Rating is not set.
--ignore-date-modified If used with --exiftool or --sidecar, will
ignore the photo modification date and set
EXIF:ModifyDate to EXIF:DateTimeOriginal; this
@@ -1097,13 +1140,12 @@ Options:
value. Valid attributes are: 'authors',
'comment', 'copyright', 'creator',
'description', 'findercomment', 'headline',
'keywords', 'participants', 'projects',
'rating', 'subject', 'title', 'version'. For
example, to set Finder comment to the photo's
title and description: '--xattr-template
findercomment "{title}; {descr}" See Extended
Attributes below for additional details on
this option.
'participants', 'projects', 'starrating',
'subject', 'title', 'version'. For example, to
set Finder comment to the photo's title and
description: '--xattr-template findercomment
"{title}; {descr}" See Extended Attributes
below for additional details on this option.
--directory DIRECTORY Optional template for specifying name of
output directory in the form '{name,DEFAULT}'.
See below for additional details on templating
@@ -1180,6 +1222,29 @@ Options:
you intend before using --cleanup. Use --dry-
run with --cleanup first if you're not
certain.
--keep KEEP_PATH When used with --cleanup, prevents file or
directory KEEP_PATH from being deleted when
cleanup is run. Use this if there are files in
the export directory that you don't want to be
deleted when --cleanup is run. KEEP_PATH may
be a file path, e.g.
'/Volumes/Photos/keep.jpg', or a file path and
wild card, e.g. '/Volumes/Photos/*.txt', or a
directory, e.g. '/Volumes/Photos/KeepMe'.
KEEP_PATH may be an absolute path or a
relative path. If it is relative, it must be
relative to the export destination. For
example if export destination is
`/Volumes/Photos` and you want to keep all
`.txt` files, you can specify `--keep
"/Volumes/Photos/*.txt"` or `--keep "*.txt"`.
If wild card is used, KEEP_PATH must be
enclosed in quotes to prevent the shell from
expanding the wildcard, e.g. `--keep
"/Volumes/Photos/*.txt"`. If KEEP_PATH is a
directory, all files and directories contained
in KEEP_PATH will be kept. --keep may be
repeated to keep additional files/directories.
--add-exported-to-album ALBUM Add all exported photos to album ALBUM in
Photos. Album ALBUM will be created if it
doesn't exist. All exported photos will be
@@ -1257,6 +1322,14 @@ Options:
not the system volume, osxphotos may run
faster if you specify a temporary directory on
the same volume as the Photos library.
--alt-copy Use alternate copy method that may be more
reliable for some network attached storage
(NAS) devices. Use --alt-copy if you
experience problems exporting to a NAS device
or SMB volume. Unlike the default copy method,
--alt-copy does not support copy-on-write on
APFS volumes nor does it preserve filesystem
metadata.
--load-config CONFIG_FILE Load options from file as written with --save-
config. This allows you to save a complex
export command to file for later reuse. For
@@ -1273,6 +1346,13 @@ Options:
--config-only If specified, saves the config file but does
not export any files; must be used with
--save-config.
--print TEMPLATE Render TEMPLATE string for each photo being
exported and print to stdout. TEMPLATE is an
osxphotos template string. This may be useful
for creating custom reports, etc. TEMPLATE
will be printed after the photo is exported or
skipped. May be repeated to print multiple
template strings.
--theme THEME Specify the color theme to use for --verbose
output. Valid themes are 'dark', 'light',
'mono', and 'plain'. Defaults to 'dark' or
@@ -1314,65 +1394,64 @@ option to re-export the entire library thus rebuilding the
Extended Attributes
Some options (currently '--finder-tag-template', '--finder-tag-keywords',
'-xattr-template') write additional metadata to extended attributes in the
file. These options will only work if the destination filesystem supports
extended attributes (most do). For example, --finder-tag-keyword writes all
keywords (including any specified by '--keyword-template' or other options) to
Finder tags that are searchable in Spotlight using the syntax: 'tag:tagname'.
For example, if you have images with keyword "Travel" then using '--finder-
tag-keywords' you could quickly find those images in the Finder by typing
'-xattr-template') write additional metadata accessible by Spotlight to
facilitate searching. For example, --finder-tag-keyword writes all keywords
(including any specified by '--keyword-template' or other options) to Finder
tags that are searchable in Spotlight using the syntax: 'tag:tagname'. For
example, if you have images with keyword "Travel" then using '--finder-tag-
keywords' you could quickly find those images in the Finder by typing
'tag:Travel' in the Spotlight search bar. Finder tags are written to the
'com.apple.metadata:_kMDItemUserTags' extended attribute. Unlike EXIF
metadata, extended attributes do not modify the actual file. Most cloud
storage services do not synch extended attributes. Dropbox does sync them and
any changes to a file's extended attributes will cause Dropbox to re-sync the
files.
metadata, extended attributes do not modify the actual file; the metadata is
written to extended attributes associated with the file and the Spotlight
metadata database. Most cloud storage services do not synch extended
attributes. Dropbox does sync them and any changes to a file's extended
attributes will cause Dropbox to re-sync the files.
The following attributes may be used with '--xattr-template':
Attribute Description
authors The author, or authors, of the contents of the file. A list
of strings. (com.apple.metadata:kMDItemAuthors)
comment A comment related to the file. This differs from the Finder
comment, kMDItemFinderComment. A string.
(com.apple.metadata:kMDItemComment)
copyright The copyright owner of the file contents. A string.
(com.apple.metadata:kMDItemCopyright)
creator Application used to create the document content (for example
“Word”, “Pages”, and so on). A string.
(com.apple.metadata:kMDItemCreator)
description A description of the content of the resource. The
description may include an abstract, table of contents,
reference to a graphical representation of content or a free-
text account of the content. A string.
(com.apple.metadata:kMDItemDescription)
findercomment Finder comments for this file. A string.
(com.apple.metadata:kMDItemFinderComment)
headline A publishable entry providing a synopsis of the contents of
the file. A string. (com.apple.metadata:kMDItemHeadline)
keywords Keywords associated with this file. For example, “Birthday”,
“Important”, etc. This differs from Finder tags
(_kMDItemUserTags) which are keywords/tags shown in the
Finder and searchable in Spotlight using "tag:tag_name". A
list of strings. (com.apple.metadata:kMDItemKeywords)
participants The list of people who are visible in an image or movie or
written about in a document. A list of strings.
(com.apple.metadata:kMDItemParticipants)
projects The list of projects that this file is part of. For example,
if you were working on a movie all of the files could be
marked as belonging to the project “My Movie”. A list of
strings. (com.apple.metadata:kMDItemProjects)
rating User rating of this item. For example, the stars rating of an
iTunes track. An integer.
(com.apple.metadata:kMDItemStarRating)
subject Subject of the this item. A string.
(com.apple.metadata:kMDItemSubject)
title The title of the file. For example, this could be the title
of a document, the name of a song, or the subject of an email
message. A string. (com.apple.metadata:kMDItemTitle)
version The version number of this file. A string.
(com.apple.metadata:kMDItemVersion)
authors kMDItemAuthors; com.apple.metadata:kMDItemAuthors; The
author, or authors, of the contents of the file.; list of
strings
comment kMDItemComment; com.apple.metadata:kMDItemComment; A comment
related to the file. This differs from the Finder comment,
kMDItemFinderComment.; string
copyright kMDItemCopyright; com.apple.metadata:kMDItemCopyright; The
copyright owner of the file contents.; string
creator kMDItemCreator; com.apple.metadata:kMDItemCreator;
Application used to create the document content (for example
"Word", "Pages", and so on).; string
description kMDItemDescription; com.apple.metadata:kMDItemDescription; A
description of the content of the resource. The description
may include an abstract, table of contents, reference to a
graphical representation of content or a free-text account of
the content.; string
findercomment kMDItemFinderComment;
com.apple.metadata:kMDItemFinderComment; Finder comments for
this file.; string
headline kMDItemHeadline; com.apple.metadata:kMDItemHeadline; A
publishable entry providing a synopsis of the contents of the
file. For example, "Apple Introduces the iPod Photo".; string
participants kMDItemParticipants; com.apple.metadata:kMDItemParticipants;
The list of people who are visible in an image or movie or
written about in a document.; list of strings
projects kMDItemProjects; com.apple.metadata:kMDItemProjects; The list
of projects that this file is part of. For example, if you
were working on a movie all of the files could be marked as
belonging to the project "My Movie".; list of strings
starrating kMDItemStarRating; com.apple.metadata:kMDItemStarRating; User
rating of this item. For example, the stars rating of an
iTunes track.; number
subject kMDItemSubject; com.apple.metadata:kMDItemSubject; Subject of
the this item.; string
title kMDItemTitle; com.apple.metadata:kMDItemTitle; The title of
the file. For example, this could be the title of a document,
the name of a song, or the subject of an email message.;
string
version kMDItemVersion; com.apple.metadata:kMDItemVersion; The
version number of this file.; string
For additional information on extended attributes see: https://developer.apple
.com/documentation/coreservices/file_metadata/mditem/common_metadata_attribute
@@ -1444,8 +1523,9 @@ Valid filters are:
• shell_quote: Quotes the value for safe usage in the shell, e.g. My
file.jpeg => 'My file.jpeg'; only adds quotes if needed.
• function: Run custom python function to filter value; use in format
'function:/path/to/file.py::function_name'. See example at https://github.c
om/RhetTbull/osxphotos/blob/master/examples/template_filter.py
'function:/path/to/file.py::function_name'. See example at
https://github.com/RhetTbull/osxphotos/blob/master/examples/template_filter
.py
• split(x): Split value into a list of values using x as delimiter, e.g.
'value1;value2' => ['value1', 'value2'] if used with split(;).
• autosplit: Automatically split delimited string into separate values; will
@@ -1483,6 +1563,15 @@ Valid filters are:
• sslice(start:stop:step): [s(tring) slice] Slice values in a list using same
semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc';
sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
• filter(x): Filter list of values using predicate x; for example,
{folder_album|filter(contains Events)} returns only folders/albums
containing the word 'Events' in their path.
• int: Convert values in list to integer, e.g. 1.0 => 1. If value cannot be
converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See
also float.
• float: Convert values in list to floating point number, e.g. 1 => 1.0. If
value cannot be converted to float, remove value from list. ['1', 'x'] =>
['1.0']. See also int.
e.g. if Photo keywords are ["FOO","bar"]:
@@ -1691,6 +1780,8 @@ Substitution Description
{favorite} Photo has been marked as favorite?;
True/False value, use in format
'{favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}'
{created} Photo's creation date in ISO format, e.g.
'2020-03-22'
{created.date} Photo's creation date in ISO format, e.g.
'2020-03-22'
{created.year} 4-digit year of photo creation time
@@ -1720,6 +1811,9 @@ Substitution Description
with no template will return null value. See
https://strftime.org/ for help on strftime
templates.
{modified} Photo's modification date in ISO format,
e.g. '2020-03-22'; uses creation date if
photo is not modified
{modified.date} Photo's modification date in ISO format,
e.g. '2020-03-22'; uses creation date if
photo is not modified
@@ -1764,6 +1858,8 @@ Substitution Description
Uses creation date if photo is not modified.
See https://strftime.org/ for help on
strftime templates.
{today} Current date in iso format, e.g.
'2020-03-22'
{today.date} Current date in iso format, e.g.
'2020-03-22'
{today.year} 4-digit year of current date
@@ -1910,8 +2006,9 @@ Substitution Description
{newline} A newline: '\n'
{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.50.4'
{crlf} A carriage return + line feed: '\r\n'
{tab} :A tab: '\t'
{osxphotos_version} The osxphotos version, e.g. '0.55.2'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified
@@ -2201,6 +2298,9 @@ Valid filters are:
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
- `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
- `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
- `filter(x)`: Filter list of values using predicate x; for example, `{folder_album|filter(contains Events)}` returns only folders/albums containing the word 'Events' in their path.
- `int`: Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float.
- `float`: Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int.
e.g. if Photo keywords are `["FOO","bar"]`:
@@ -2312,6 +2412,7 @@ The following template field substitutions are availabe for use the templating s
|{edited}|True if photo has been edited (has adjustments), otherwise False; use in format '{edited?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|{edited_version}|True if template is being rendered for the edited version of a photo, otherwise False. |
|{favorite}|Photo has been marked as favorite?; True/False value, use in format '{favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}'|
|{created}|Photo's creation date in ISO format, e.g. '2020-03-22'|
|{created.date}|Photo's creation date in ISO format, e.g. '2020-03-22'|
|{created.year}|4-digit year of photo creation time|
|{created.yy}|2-digit year of photo creation time|
@@ -2325,6 +2426,7 @@ The following template field substitutions are availabe for use the templating s
|{created.min}|2-digit minute of the photo creation time|
|{created.sec}|2-digit second of the photo creation time|
|{created.strftime}|Apply strftime template to file creation date/time. Should be used in form {created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {created.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. See https://strftime.org/ for help on strftime templates.|
|{modified}|Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified|
|{modified.date}|Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified|
|{modified.year}|4-digit year of photo modification time; uses creation date if photo is not modified|
|{modified.yy}|2-digit year of photo modification time; uses creation date if photo is not modified|
@@ -2338,6 +2440,7 @@ The following template field substitutions are availabe for use the templating s
|{modified.min}|2-digit minute of the photo modification time; uses creation date if photo is not modified|
|{modified.sec}|2-digit second of the photo modification time; uses creation date if photo is not modified|
|{modified.strftime}|Apply strftime template to file modification date/time. Should be used in form {modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. Uses creation date if photo is not modified. See https://strftime.org/ for help on strftime templates.|
|{today}|Current date in iso format, e.g. '2020-03-22'|
|{today.date}|Current date in iso format, e.g. '2020-03-22'|
|{today.year}|4-digit year of current date|
|{today.yy}|2-digit year of current date|
@@ -2387,8 +2490,9 @@ The following template field substitutions are availabe for use the templating s
|{newline}|A newline: '\n'|
|{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.50.4'|
|{crlf}|A carriage return + line feed: '\r\n'|
|{tab}|:A tab: '\t'|
|{osxphotos_version}|The osxphotos version, e.g. '0.55.2'|
|{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|
@@ -2413,244 +2517,6 @@ The following template field substitutions are availabe for use the templating s
|{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 -->
### <a name="exiftoolExifTool">ExifTool</a>
osxphotos includes its own `exiftool` library that can be accessed via `osxphotos.exiftool`:
```python
>>> from osxphotos.exiftool import ExifTool
>>> exiftool = ExifTool("/Users/rhet/Downloads/test.jpeg")
>>> exifdict = exiftool.asdict()
>>> exifdict["EXIF:Make"]
'Canon'
>>> exiftool.setvalue("IPTC:Keywords","Keyword1")
True
>>> exiftool.asdict()["IPTC:Keywords"]
'Keyword1'
>>> exiftool.addvalues("IPTC:Keywords","Keyword2","Keyword3")
True
>>> exiftool.asdict()["IPTC:Keywords"]
['Keyword1', 'Keyword2', 'Keyword3']
```
`ExifTool(filepath, exiftool=None)`
* `filepath`: str, path to photo
* `exiftool`: str, optional path to `exiftool`; if not provided, will look for `exiftool` in the system path
#### ExifTool methods
* `asdict(tag_groups=True)`: returns all EXIF metadata found in the file as a dictionary in following form (Note: this shows just a subset of available metadata). See [exiftool](https://exiftool.org/) documentation to understand which metadata keys are available. If `tag_groups` is True (default) dict keys are in form "GROUP:TAG", e.g. "IPTC:Keywords". If `tag_groups` is False, dict keys do not have group names, e.g. "Keywords".
```python
{'Composite:Aperture': 2.2,
'Composite:GPSPosition': '-34.9188916666667 138.596861111111',
'Composite:ImageSize': '2754 2754',
'EXIF:CreateDate': '2017:06:20 17:18:56',
'EXIF:LensMake': 'Apple',
'EXIF:LensModel': 'iPhone 6s back camera 4.15mm f/2.2',
'EXIF:Make': 'Apple',
'XMP:Title': 'Elder Park',
}
```
* `json()`: returns same information as `asdict()` but as a serialized JSON string.
* `setvalue(tag, value)`: write to the EXIF data in the photo file. To delete a tag, use setvalue with value = `None`. For example:
```python
photo.exiftool.setvalue("XMP:Title", "Title of photo")
```
* `addvalues(tag, *values)`: Add one or more value(s) to tag. For a tag that accepts multiple values, like "IPTC:Keywords", this will add the values as additional list values. However, for tags which are not usually lists, such as "EXIF:ISO" this will literally add the new value to the old value which is probably not the desired effect. Be sure you understand the behavior of the individual tag before using this. For example:
```python
photo.exiftool.addvalues("IPTC:Keywords", "vacation", "beach")
```
osxphotos.exiftool also provides an `ExifToolCaching` class which caches all metadata after the first call to `exiftool`. This can significantly speed up repeated access to the metadata but should only be used if you do not intend to modify the file's metadata.
[`PhotoInfo.exiftool`](#exiftool) returns an `ExifToolCaching` instance for the original image in the Photos library.
#### Implementation Note
`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="photoexporter">PhotoExporter</a>
[PhotoInfo.export()](#photoinfo) provides a simple method to export a photo. This method actually calls `PhotoExporter.export()` to do the export. `PhotoExporter` provides many more options to configure the export and report results and this is what the osxphotos command line export tools uses.
#### `export(dest, filename=None, options: Optional[ExportOptions]=None) -> ExportResults`
Export a photo.
Args:
* dest: must be valid destination path or exception raised
* filename: (optional): name of exported picture; if not provided, will use current filename
* options (ExportOptions): optional ExportOptions instance
Returns: ExportResults instance
*Note*: to use dry run mode, you must set options.dry_run=True and also pass in memory version of export_db, and no-op fileutil (e.g. `ExportDBInMemory` and `FileUtilNoOp`) in options.export_db and options.fileutil respectively.
#### `ExportOptions`
Options class for exporting photos with `export`
Attributes:
* convert_to_jpeg (bool): if True, converts non-jpeg images to jpeg
* description_template (str): optional template string that will be rendered for use as photo description
* download_missing: (bool, default=False): if True will attempt to export photo via applescript interaction with Photos if missing (see also use_photokit, use_photos_export)
* dry_run: (bool, default=False): set to True to run in "dry run" mode
* edited: (bool, default=False): if True will export the edited version of the photo otherwise exports the original version
* exiftool_flags (list of str): optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"]
* exiftool: (bool, default = False): if True, will use exiftool to write metadata to export file
* export_as_hardlink: (bool, default=False): if True, will hardlink files instead of copying them
* export_db: (ExportDB): instance of a class that conforms to ExportDB with methods for getting/setting data related to exported files to compare update state
* fileutil: (FileUtilABC): class that conforms to FileUtilABC with various file utilities
* ignore_date_modified (bool): for use with sidecar and exiftool; if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
* ignore_signature (bool, default=False): ignore file signature when used with update (look only at filename)
* increment (bool, default=True): if True, will increment file name until a non-existant name is found if overwrite=False and increment=False, export will fail if destination file already exists
* jpeg_ext (str): if set, will use this value for extension on jpegs converted to jpeg with convert_to_jpeg; if not set, uses jpeg; do not include the leading "."
* jpeg_quality (float in range 0.0 <= jpeg_quality <= 1.0): a value of 1.0 specifies use best quality, a value of 0.0 specifies use maximum compression.
* keyword_template (list of str): list of template strings that will be rendered as used as keywords
* live_photo (bool, default=False): if True, will also export the associated .mov for live photos
* location (bool): if True, include location in exported metadata
* merge_exif_keywords (bool): if True, merged keywords found in file's exif data (requires exiftool)
* merge_exif_persons (bool): if True, merged persons found in file's exif data (requires exiftool)
* overwrite (bool, default=False): if True will overwrite files if they already exist
* persons (bool): if True, include persons in exported metadata
* preview_suffix (str): optional string to append to end of filename for preview images
* preview (bool): if True, also exports preview image
* raw_photo (bool, default=False): if True, will also export the associated RAW photo
* render_options (RenderOptions): optional osxphotos.phototemplate.RenderOptions instance to specify options for rendering templates
* replace_keywords (bool): if True, keyword_template replaces any keywords, otherwise it's additive
* sidecar_drop_ext (bool, default=False): if True, drops the photo's extension from sidecar filename (e.g. 'IMG_1234.json' instead of 'IMG_1234.JPG.json')
* sidecar: bit field (int): set to one or more of SIDECAR_XMP, SIDECAR_JSON, SIDECAR_EXIFTOOL
* SIDECAR_JSON: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json; includes exiftool tag group names (e.g. `exiftool -G -j`)
* SIDECAR_EXIFTOOL: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json; does not include exiftool tag group names (e.g. `exiftool -j`)
* SIDECAR_XMP: if set will write an XMP sidecar with IPTC data sidecar filename will be dest/filename.xmp
* strip (bool): if True, strip whitespace from rendered templates
* timeout (int, default=120): timeout in seconds used with use_photos_export
* touch_file (bool, default=False): if True, sets file's modification time upon photo date
* update (bool, default=False): if True export will run in update mode, that is, it will not export the photo if the current version already exists in the destination
* use_albums_as_keywords (bool, default = False): if True, will include album names in keywords when exporting metadata with exiftool or sidecar
* use_persons_as_keywords (bool, default = False): if True, will include person names in keywords when exporting metadata with exiftool or sidecar
* use_photos_export (bool, default=False): if True will attempt to export photo via applescript interaction with Photos even if not missing (see also use_photokit, download_missing)
* use_photokit (bool, default=False): if True, will use photokit to export photos when use_photos_export is True
* verbose (Callable): optional callable function to use for printing verbose text during processing; if None (default), does not print output.
* tmpfile (str): optional path to use for temporary files
#### `ExportResults`
`PhotoExporter().export()` returns an instance of this class.
`ExportResults` has the following properties:
* datetime: date/time of export in ISO 8601 format
* exported: list of all exported files (A single call to export could export more than one file, e.g. original file, preview, live video, raw, etc.)
* new: list of new files exported when used with update=True
* updated: list of updated files when used with update=True
* skipped: list of skipped files when used with update=True
* exif_updated: list of updated files when used with update=True and exiftool
* touched: list of files touched during export (e.g. file date/time updated with touch_file=True)
* to_touch: Reserved for internal use of export
* converted_to_jpeg: list of files converted to jpeg when convert_to_jpeg=True
* sidecar_json_written: list of JSON sidecars written
* sidecar_json_skipped: list of JSON sidecars skipped when update=True
* sidecar_exiftool_written: list of exiftool sidecars written
* sidecar_exiftool_skipped: list of exiftool sidecars skipped when update=True
* sidecar_xmp_written: list of XMP sidecars written
* sidecar_xmp_skipped: list of XMP sidecars skipped when update=True
* missing: list of missing files
* error: list of tuples containing (filename, error) if error generated during export
* exiftool_warning: list of warnings generated by exiftool during export
* exiftool_error: list of errors generated by exiftool during export
* xattr_written: list of files with extended attributes written during export
* xattr_skipped: list of files where extended attributes were skipped when update=True
* metadata_changed: list of files where metadata changed since last export
* deleted_files: reserved for use by osxphotos CLI
* deleted_directories: reserved for use by osxphotos CLI
* exported_album: reserved for use by osxphotos CLI
* skipped_album: reserved for use by osxphotos CLI
* missing_album: reserved for use by osxphotos CLI
### <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
#### `get_system_library_path()`
**MacOS 10.15 Only** Returns path to System Photo Library as string. On MacOS version < 10.15, returns None.
#### `get_last_library_path()`
Returns path to last opened Photo Library as string.
#### `list_photo_libraries()`
Returns list of Photos libraries found on the system. **Note**: On MacOS 10.15, this appears to list all libraries. On older systems, it may not find some libraries if they are not located in ~/Pictures. Provided for convenience but do not rely on this to find all libraries on the system.
## Examples
```python
import osxphotos
def main():
photosdb = osxphotos.PhotosDB("/Users/smith/Pictures/Photos Library.photoslibrary")
print(f"db file = {photosdb.db_path}")
print(f"db version = {photosdb.db_version}")
print(photosdb.keywords)
print(photosdb.persons)
print(photosdb.album_names)
print(photosdb.keywords_as_dict)
print(photosdb.persons_as_dict)
print(photosdb.albums_as_dict)
# find all photos with Keyword = Kids and containing person Katie
photos = photosdb.photos(keywords=["Kids"], persons=["Katie"])
print(f"found {len(photos)} photos")
# find all photos that include Katie but do not contain the keyword wedding
photos = [
p
for p in photosdb.photos(persons=["Katie"])
if p not in photosdb.photos(keywords=["wedding"])
]
# get all photos in the database
photos = photosdb.photos()
for p in photos:
print(
p.uuid,
p.filename,
p.date,
p.description,
p.title,
p.keywords,
p.albums,
p.persons,
p.path,
p.ismissing,
p.hasadjustments,
)
if __name__ == "__main__":
main()
```
## Related Projects
* [rhettbull/exif2findertags](https://github.com/RhetTbull/exif2findertags): Read EXIF metadata from image and video files and convert it to macOS Finder tags and/or Finder comments and other extended attributes.
@@ -2676,50 +2542,65 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/britiscurious"><img src="https://avatars1.githubusercontent.com/u/25646439?v=4?s=75" width="75px;" alt=""/><br /><sub><b>britiscurious</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=britiscurious" title="Documentation">📖</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=britiscurious" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mwort"><img src="https://avatars3.githubusercontent.com/u/8170417?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Michel Wortmann</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=mwort" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/PabloKohan"><img src="https://avatars3.githubusercontent.com/u/8790976?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Pablo 'merKur' Kohan</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=PabloKohan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hshore29"><img src="https://avatars2.githubusercontent.com/u/7023497?v=4?s=75" width="75px;" alt=""/><br /><sub><b>hshore29</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hshore29" title="Code">💻</a></td>
<td align="center"><a href="http://3e.org/"><img src="https://avatars0.githubusercontent.com/u/41439?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Daniel M. Drucker</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dmd" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jystervinou"><img src="https://avatars3.githubusercontent.com/u/132356?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Jean-Yves Stervinou</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=jystervinou" title="Code">💻</a></td>
<td align="center"><a href="https://dethi.me/"><img src="https://avatars2.githubusercontent.com/u/1011520?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Thibault Deutsch</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dethi" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/grundsch"><img src="https://avatars0.githubusercontent.com/u/3874928?v=4?s=75" width="75px;" alt=""/><br /><sub><b>grundsch</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=grundsch" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/agprimatic"><img src="https://avatars1.githubusercontent.com/u/4685054?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Ag Primatic</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=agprimatic" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hhoeck"><img src="https://avatars1.githubusercontent.com/u/6313998?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Horst Höck</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hhoeck" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jstrine"><img src="https://avatars1.githubusercontent.com/u/33943447?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Jonathan Strine</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=jstrine" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/finestream"><img src="https://avatars1.githubusercontent.com/u/16638513?v=4?s=75" width="75px;" alt=""/><br /><sub><b>finestream</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=finestream" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/synox"><img src="https://avatars2.githubusercontent.com/u/2250964?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Aravindo Wingeier</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=synox" title="Documentation">📖</a></td>
<td align="center"><a href="https://kradalby.no"><img src="https://avatars1.githubusercontent.com/u/98431?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Kristoffer Dalby</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=kradalby" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Rott-Apple"><img src="https://avatars1.githubusercontent.com/u/67875570?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Rott-Apple</b></sub></a><br /><a href="#research-Rott-Apple" title="Research">🔬</a></td>
<td align="center"><a href="https://github.com/narensankar0529"><img src="https://avatars3.githubusercontent.com/u/74054766?v=4?s=75" width="75px;" alt=""/><br /><sub><b>narensankar0529</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anarensankar0529" title="Bug reports">🐛</a> <a href="#userTesting-narensankar0529" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/martinhrpi"><img src="https://avatars2.githubusercontent.com/u/19407684?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Martin</b></sub></a><br /><a href="#research-martinhrpi" title="Research">🔬</a> <a href="#userTesting-martinhrpi" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/davidjroos"><img src="https://avatars.githubusercontent.com/u/15630844?v=4?s=75" width="75px;" alt=""/><br /><sub><b>davidjroos </b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=davidjroos" title="Documentation">📖</a></td>
<td align="center"><a href="https://neilpa.me"><img src="https://avatars.githubusercontent.com/u/42419?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Neil Pankey</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=neilpa" title="Code">💻</a></td>
<td align="center"><a href="https://aaronweb.net/"><img src="https://avatars.githubusercontent.com/u/604665?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Aaron van Geffen</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=AaronVanGeffen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ubrandes"><img src="https://avatars.githubusercontent.com/u/59647284?v=4?s=75" width="75px;" alt=""/><br /><sub><b>ubrandes </b></sub></a><br /><a href="#ideas-ubrandes" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="http://blog.dewost.com/"><img src="https://avatars.githubusercontent.com/u/17090228?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Philippe Dewost</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=pdewost" title="Documentation">📖</a> <a href="#example-pdewost" title="Examples">💡</a> <a href="#ideas-pdewost" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/kaduskj"><img src="https://avatars.githubusercontent.com/u/983067?v=4?s=75" width="75px;" alt=""/><br /><sub><b>kaduskj</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Akaduskj" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mkirkland4874"><img src="https://avatars.githubusercontent.com/u/36466711?v=4?s=75" width="75px;" alt=""/><br /><sub><b>mkirkland4874</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Amkirkland4874" title="Bug reports">🐛</a> <a href="#example-mkirkland4874" title="Examples">💡</a></td>
<td align="center"><a href="https://github.com/jcommisso07"><img src="https://avatars.githubusercontent.com/u/3111054?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Joseph Commisso</b></sub></a><br /><a href="#data-jcommisso07" title="Data">🔣</a></td>
<td align="center"><a href="https://github.com/dssinger"><img src="https://avatars.githubusercontent.com/u/1817903?v=4?s=75" width="75px;" alt=""/><br /><sub><b>David Singer</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Adssinger" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/oPromessa"><img src="https://avatars.githubusercontent.com/u/21261491?v=4?s=75" width="75px;" alt=""/><br /><sub><b>oPromessa</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3AoPromessa" title="Bug reports">🐛</a> <a href="#ideas-oPromessa" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Tests">⚠️</a></td>
<td align="center"><a href="http://spencerchang.me"><img src="https://avatars.githubusercontent.com/u/14796580?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Spencer Chang</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aspencerc99" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.cs.purdue.edu/homes/dgleich"><img src="https://avatars.githubusercontent.com/u/33995?v=4?s=75" width="75px;" alt=""/><br /><sub><b>David Gleich</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dgleich" title="Code">💻</a></td>
<td align="center"><a href="https://alandefreitas.github.io/alandefreitas/"><img src="https://avatars.githubusercontent.com/u/5369819?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Alan de Freitas</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aalandefreitas" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://hyfen.net"><img src="https://avatars.githubusercontent.com/u/6291?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Andrew Louis</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hyfen" title="Documentation">📖</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=hyfen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/neebah"><img src="https://avatars.githubusercontent.com/u/71442026?v=4?s=75" width="75px;" alt=""/><br /><sub><b>neebah</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aneebah" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ahti123"><img src="https://avatars.githubusercontent.com/u/22232632?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Ahti Liin</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=ahti123" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aahti123" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xwu64"><img src="https://avatars.githubusercontent.com/u/10580396?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Xiaoliang Wu</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=xwu64" title="Code">💻</a></td>
</tr>
<tbody>
<tr>
<td align="center"><a href="https://github.com/britiscurious"><img src="https://avatars1.githubusercontent.com/u/25646439?v=4?s=75" width="75px;" alt="britiscurious"/><br /><sub><b>britiscurious</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=britiscurious" title="Documentation">📖</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=britiscurious" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mwort"><img src="https://avatars3.githubusercontent.com/u/8170417?v=4?s=75" width="75px;" alt="Michel Wortmann"/><br /><sub><b>Michel Wortmann</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=mwort" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/PabloKohan"><img src="https://avatars3.githubusercontent.com/u/8790976?v=4?s=75" width="75px;" alt="Pablo 'merKur' Kohan"/><br /><sub><b>Pablo 'merKur' Kohan</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=PabloKohan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hshore29"><img src="https://avatars2.githubusercontent.com/u/7023497?v=4?s=75" width="75px;" alt="hshore29"/><br /><sub><b>hshore29</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hshore29" title="Code">💻</a></td>
<td align="center"><a href="http://3e.org/"><img src="https://avatars0.githubusercontent.com/u/41439?v=4?s=75" width="75px;" alt="Daniel M. Drucker"/><br /><sub><b>Daniel M. Drucker</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dmd" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Admd" title="Bug reports">🐛</a> <a href="#userTesting-dmd" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/jystervinou"><img src="https://avatars3.githubusercontent.com/u/132356?v=4?s=75" width="75px;" alt="Jean-Yves Stervinou"/><br /><sub><b>Jean-Yves Stervinou</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=jystervinou" title="Code">💻</a></td>
<td align="center"><a href="https://dethi.me/"><img src="https://avatars2.githubusercontent.com/u/1011520?v=4?s=75" width="75px;" alt="Thibault Deutsch"/><br /><sub><b>Thibault Deutsch</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dethi" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/grundsch"><img src="https://avatars0.githubusercontent.com/u/3874928?v=4?s=75" width="75px;" alt="grundsch"/><br /><sub><b>grundsch</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=grundsch" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/agprimatic"><img src="https://avatars1.githubusercontent.com/u/4685054?v=4?s=75" width="75px;" alt="Ag Primatic"/><br /><sub><b>Ag Primatic</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=agprimatic" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hhoeck"><img src="https://avatars1.githubusercontent.com/u/6313998?v=4?s=75" width="75px;" alt="Horst Höck"/><br /><sub><b>Horst Höck</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hhoeck" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jstrine"><img src="https://avatars1.githubusercontent.com/u/33943447?v=4?s=75" width="75px;" alt="Jonathan Strine"/><br /><sub><b>Jonathan Strine</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=jstrine" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/finestream"><img src="https://avatars1.githubusercontent.com/u/16638513?v=4?s=75" width="75px;" alt="finestream"/><br /><sub><b>finestream</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=finestream" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/synox"><img src="https://avatars2.githubusercontent.com/u/2250964?v=4?s=75" width="75px;" alt="Aravindo Wingeier"/><br /><sub><b>Aravindo Wingeier</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=synox" title="Documentation">📖</a></td>
<td align="center"><a href="https://kradalby.no"><img src="https://avatars1.githubusercontent.com/u/98431?v=4?s=75" width="75px;" alt="Kristoffer Dalby"/><br /><sub><b>Kristoffer Dalby</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=kradalby" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Rott-Apple"><img src="https://avatars1.githubusercontent.com/u/67875570?v=4?s=75" width="75px;" alt="Rott-Apple"/><br /><sub><b>Rott-Apple</b></sub></a><br /><a href="#research-Rott-Apple" title="Research">🔬</a></td>
<td align="center"><a href="https://github.com/narensankar0529"><img src="https://avatars3.githubusercontent.com/u/74054766?v=4?s=75" width="75px;" alt="narensankar0529"/><br /><sub><b>narensankar0529</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anarensankar0529" title="Bug reports">🐛</a> <a href="#userTesting-narensankar0529" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/martinhrpi"><img src="https://avatars2.githubusercontent.com/u/19407684?v=4?s=75" width="75px;" alt="Martin"/><br /><sub><b>Martin</b></sub></a><br /><a href="#research-martinhrpi" title="Research">🔬</a> <a href="#userTesting-martinhrpi" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/davidjroos"><img src="https://avatars.githubusercontent.com/u/15630844?v=4?s=75" width="75px;" alt="davidjroos "/><br /><sub><b>davidjroos </b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=davidjroos" title="Documentation">📖</a></td>
<td align="center"><a href="https://neilpa.me"><img src="https://avatars.githubusercontent.com/u/42419?v=4?s=75" width="75px;" alt="Neil Pankey"/><br /><sub><b>Neil Pankey</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=neilpa" title="Code">💻</a></td>
<td align="center"><a href="https://aaronweb.net/"><img src="https://avatars.githubusercontent.com/u/604665?v=4?s=75" width="75px;" alt="Aaron van Geffen"/><br /><sub><b>Aaron van Geffen</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=AaronVanGeffen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ubrandes"><img src="https://avatars.githubusercontent.com/u/59647284?v=4?s=75" width="75px;" alt="ubrandes "/><br /><sub><b>ubrandes </b></sub></a><br /><a href="#ideas-ubrandes" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="http://blog.dewost.com/"><img src="https://avatars.githubusercontent.com/u/17090228?v=4?s=75" width="75px;" alt="Philippe Dewost"/><br /><sub><b>Philippe Dewost</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=pdewost" title="Documentation">📖</a> <a href="#example-pdewost" title="Examples">💡</a> <a href="#ideas-pdewost" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/kaduskj"><img src="https://avatars.githubusercontent.com/u/983067?v=4?s=75" width="75px;" alt="kaduskj"/><br /><sub><b>kaduskj</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Akaduskj" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mkirkland4874"><img src="https://avatars.githubusercontent.com/u/36466711?v=4?s=75" width="75px;" alt="mkirkland4874"/><br /><sub><b>mkirkland4874</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Amkirkland4874" title="Bug reports">🐛</a> <a href="#example-mkirkland4874" title="Examples">💡</a></td>
<td align="center"><a href="https://github.com/jcommisso07"><img src="https://avatars.githubusercontent.com/u/3111054?v=4?s=75" width="75px;" alt="Joseph Commisso"/><br /><sub><b>Joseph Commisso</b></sub></a><br /><a href="#data-jcommisso07" title="Data">🔣</a></td>
<td align="center"><a href="https://github.com/dssinger"><img src="https://avatars.githubusercontent.com/u/1817903?v=4?s=75" width="75px;" alt="David Singer"/><br /><sub><b>David Singer</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Adssinger" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/oPromessa"><img src="https://avatars.githubusercontent.com/u/21261491?v=4?s=75" width="75px;" alt="oPromessa"/><br /><sub><b>oPromessa</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3AoPromessa" title="Bug reports">🐛</a> <a href="#ideas-oPromessa" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Tests">⚠️</a></td>
<td align="center"><a href="http://spencerchang.me"><img src="https://avatars.githubusercontent.com/u/14796580?v=4?s=75" width="75px;" alt="Spencer Chang"/><br /><sub><b>Spencer Chang</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aspencerc99" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.cs.purdue.edu/homes/dgleich"><img src="https://avatars.githubusercontent.com/u/33995?v=4?s=75" width="75px;" alt="David Gleich"/><br /><sub><b>David Gleich</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dgleich" title="Code">💻</a></td>
<td align="center"><a href="https://alandefreitas.github.io/alandefreitas/"><img src="https://avatars.githubusercontent.com/u/5369819?v=4?s=75" width="75px;" alt="Alan de Freitas"/><br /><sub><b>Alan de Freitas</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aalandefreitas" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://hyfen.net"><img src="https://avatars.githubusercontent.com/u/6291?v=4?s=75" width="75px;" alt="Andrew Louis"/><br /><sub><b>Andrew Louis</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hyfen" title="Documentation">📖</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=hyfen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/neebah"><img src="https://avatars.githubusercontent.com/u/71442026?v=4?s=75" width="75px;" alt="neebah"/><br /><sub><b>neebah</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aneebah" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ahti123"><img src="https://avatars.githubusercontent.com/u/22232632?v=4?s=75" width="75px;" alt="Ahti Liin"/><br /><sub><b>Ahti Liin</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=ahti123" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aahti123" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xwu64"><img src="https://avatars.githubusercontent.com/u/10580396?v=4?s=75" width="75px;" alt="Xiaoliang Wu"/><br /><sub><b>Xiaoliang Wu</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=xwu64" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nullpointerninja"><img src="https://avatars.githubusercontent.com/u/62975432?v=4?s=75" width="75px;" alt="nullpointerninja"/><br /><sub><b>nullpointerninja</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anullpointerninja" title="Bug reports">🐛</a> <a href="#ideas-nullpointerninja" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/infused-kim"><img src="https://avatars.githubusercontent.com/u/7404004?v=4?s=75" width="75px;" alt="Kim"/><br /><sub><b>Kim</b></sub></a><br /><a href="#ideas-infused-kim" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/Se7enair"><img src="https://avatars.githubusercontent.com/u/1680106?v=4?s=75" width="75px;" alt="Christoph"/><br /><sub><b>Christoph</b></sub></a><br /><a href="#ideas-Se7enair" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="http://www.franzone.com"><img src="https://avatars.githubusercontent.com/u/900684?v=4?s=75" width="75px;" alt="franzone"/><br /><sub><b>franzone</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Afranzone" title="Bug reports">🐛</a></td>
<td align="center"><a href="http://jmuccigr.github.io/"><img src="https://avatars.githubusercontent.com/u/615115?v=4?s=75" width="75px;" alt="John Muccigrosso"/><br /><sub><b>John Muccigrosso</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Ajmuccigr" title="Bug reports">🐛</a> <a href="#ideas-jmuccigr" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://nomadgate.com"><img src="https://avatars.githubusercontent.com/u/1646041?v=4?s=75" width="75px;" alt="Thomas K. Running"/><br /><sub><b>Thomas K. Running</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=tkrunning" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Atkrunning" title="Bug reports">🐛</a></td>
<td align="center"><a href="http://dalisoft.uz"><img src="https://avatars.githubusercontent.com/u/3511344?v=4?s=75" width="75px;" alt="Davlatjon Shavkatov"/><br /><sub><b>Davlatjon Shavkatov</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dalisoft" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=dalisoft" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/zephyr325"><img src="https://avatars.githubusercontent.com/u/5245609?v=4?s=75" width="75px;" alt="zephyr325"/><br /><sub><b>zephyr325</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Azephyr325" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/drodner"><img src="https://avatars.githubusercontent.com/u/10236892?v=4?s=75" width="75px;" alt="drodner"/><br /><sub><b>drodner</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Adrodner" title="Bug reports">🐛</a> <a href="#userTesting-drodner" title="User Testing">📓</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->

View File

@@ -21,7 +21,7 @@ Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) through mac
This package will read Photos databases for any supported version on any supported macOS version.
E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine running macOS 10.12 and vice versa.
Requires python >= ``3.8``.
Requires python >= ``3.9``.
Installation
------------

25
README_DEV.md Normal file
View File

@@ -0,0 +1,25 @@
# Developer Notes for osxphotos
These are notes for developers working on osxphotos. They're mostly to help me remember how to do things in this repo but will be useful to anyone who wants to contribute to osxphotos.
## Installing osxphotos
- Clone the repo: `git clone git@github.com:RhetTbull/osxphotos.git`
- Create a virtual environment and activate it: `python3 -m venv venv` then `source venv/bin/activate`. I use [pyenv](https://github.com/pyenv/pyenv) with [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv) to manage my virtual environments
- Install the requirements: `pip install -r requirements.txt`
- Install the development requirements: `pip install -r requirements-dev.txt`
- Install osxphotos: `pip install -e .`
## Running tests
- Run all tests: `pytest`
See the [test README.md](tests/README.md) for more information on running tests.
## Building the package
- Run `./build.sh` to run the build script.
## Other Notes
[cogapp](https://nedbatchelder.com/code/cog/index.html) is used to update the README.md and other files. cog will be called from the build script as needed.

View File

@@ -45,3 +45,12 @@ python3 -m build
# build CLI executable
echo "Building CLI executable"
./make_cli_exe.sh
# zip up CLI executable
echo "Zipping CLI executable"
OSXPHOTOSVERSION=$(python3 -c "import osxphotos; print(osxphotos.__version__)")
ARCHSTR=$(uname -m)
ZIPNAME=osxphotos_MacOS_exe_darwin_${ARCHSTR}_v${OSXPHOTOSVERSION}.zip
echo "Zipping CLI executable to $ZIPNAME"
cd dist && zip $ZIPNAME osxphotos && cd ..
rm dist/osxphotos

View File

@@ -1,11 +1,13 @@
build
bump2version==1.0.1
cogapp>=3.3.0,<4.0.0
furo
m2r2
m2r2==0.3.3
pdbpp
pyinstaller==4.10
pyinstaller==5.6.2
pytest-cov==4.0.0
pytest-mock
pytest==7.0.1
pytest==7.2.0
Sphinx
sphinx_click
sphinx_rtd_theme

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: ad1b9f346384c93c414bf29025529641
config: e16dca91a167d1b8d610ea04175e7a9b
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -1,13 +1,13 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>Overview: module code - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>Overview: module code - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="../index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -179,7 +179,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -205,6 +206,7 @@
<li><a href="osxphotos/personinfo.html">osxphotos.personinfo</a></li>
<li><a href="osxphotos/photoexporter.html">osxphotos.photoexporter</a></li>
<li><a href="osxphotos/photoinfo.html">osxphotos.photoinfo</a></li>
<li><a href="osxphotos/photosalbum.html">osxphotos.photosalbum</a></li>
<li><a href="osxphotos/photosdb/_photosdb_process_comments.html">osxphotos.photosdb._photosdb_process_comments</a></li>
<li><a href="osxphotos/photosdb/photosdb.html">osxphotos.photosdb.photosdb</a></li>
<li><a href="osxphotos/phototemplate.html">osxphotos.phototemplate</a></li>
@@ -249,7 +251,9 @@
</div><script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/sphinx_highlight.js"></script>
<script src="../_static/scripts/furo.js"></script>
<script src="../_static/clipboard.min.js"></script>
<script src="../_static/copybutton.js"></script>

View File

@@ -1,13 +1,13 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos._constants - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos._constants - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -179,7 +179,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -194,24 +195,24 @@
</div>
<article role="main">
<h1>Source code for osxphotos._constants</h1><div class="highlight"><pre>
<span></span><span class="sd">"""</span>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">Constants used by osxphotos </span>
<span class="sd">"""</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
<span class="n">APP_NAME</span> <span class="o">=</span> <span class="s2">"osxphotos"</span>
<span class="n">APP_NAME</span> <span class="o">=</span> <span class="s2">&quot;osxphotos&quot;</span>
<span class="n">OSXPHOTOS_URL</span> <span class="o">=</span> <span class="s2">"https://github.com/RhetTbull/osxphotos"</span>
<span class="n">OSXPHOTOS_URL</span> <span class="o">=</span> <span class="s2">&quot;https://github.com/RhetTbull/osxphotos&quot;</span>
<span class="c1"># Time delta: add this to Photos times to get unix time</span>
<span class="c1"># Apple Epoch is Jan 1, 2001</span>
<span class="n">TIME_DELTA</span> <span class="o">=</span> <span class="p">(</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2001</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">-</span> <span class="n">datetime</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()</span>
<span class="c1"># Unicode format to use for comparing strings</span>
<span class="n">UNICODE_FORMAT</span> <span class="o">=</span> <span class="s2">"NFC"</span>
<span class="n">UNICODE_FORMAT</span> <span class="o">=</span> <span class="s2">&quot;NFC&quot;</span>
<span class="c1"># which Photos library database versions have been tested</span>
<span class="c1"># Photos 2.0 (10.12.6) == 2622</span>
@@ -219,23 +220,23 @@
<span class="c1"># Photos 4.0 (10.14.5) == 4016</span>
<span class="c1"># Photos 4.0 (10.14.6) == 4025</span>
<span class="c1"># Photos 5.0 (10.15.0) == 6000 or 5001</span>
<span class="n">_TESTED_DB_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"6000"</span><span class="p">,</span> <span class="s2">"5001"</span><span class="p">,</span> <span class="s2">"4025"</span><span class="p">,</span> <span class="s2">"4016"</span><span class="p">,</span> <span class="s2">"3301"</span><span class="p">,</span> <span class="s2">"2622"</span><span class="p">]</span>
<span class="n">_TESTED_DB_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;6000&quot;</span><span class="p">,</span> <span class="s2">&quot;5001&quot;</span><span class="p">,</span> <span class="s2">&quot;4025&quot;</span><span class="p">,</span> <span class="s2">&quot;4016&quot;</span><span class="p">,</span> <span class="s2">&quot;3301&quot;</span><span class="p">,</span> <span class="s2">&quot;2622&quot;</span><span class="p">]</span>
<span class="c1"># database model versions (applies to Photos 5, Photos 6)</span>
<span class="c1"># these come from PLModelVersion key in binary plist in Z_METADATA.Z_PLIST</span>
<span class="c1"># Photos 5 (10.15.1) == 13537</span>
<span class="c1"># Photos 5 (10.15.4, 10.15.5, 10.15.6) == 13703</span>
<span class="c1"># Photos 6 (10.16.0 Beta) == 14104</span>
<span class="n">_TEST_MODEL_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"13537"</span><span class="p">,</span> <span class="s2">"13703"</span><span class="p">,</span> <span class="s2">"14104"</span><span class="p">]</span>
<span class="n">_TEST_MODEL_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;13537&quot;</span><span class="p">,</span> <span class="s2">&quot;13703&quot;</span><span class="p">,</span> <span class="s2">&quot;14104&quot;</span><span class="p">]</span>
<span class="n">_PHOTOS_2_VERSION</span> <span class="o">=</span> <span class="s2">"2622"</span>
<span class="n">_PHOTOS_2_VERSION</span> <span class="o">=</span> <span class="s2">&quot;2622&quot;</span>
<span class="c1"># only version 3 - 4 have RKVersion.selfPortrait</span>
<span class="n">_PHOTOS_3_VERSION</span> <span class="o">=</span> <span class="s2">"3301"</span>
<span class="n">_PHOTOS_3_VERSION</span> <span class="o">=</span> <span class="s2">&quot;3301&quot;</span>
<span class="c1"># versions 5.0 and later have a different database structure</span>
<span class="n">_PHOTOS_4_VERSION</span> <span class="o">=</span> <span class="s2">"4025"</span> <span class="c1"># latest Mojove version on 10.14.6</span>
<span class="n">_PHOTOS_5_VERSION</span> <span class="o">=</span> <span class="s2">"5000"</span> <span class="c1"># I've seen both 5001 and 6000. 6000 is most common on Catalina and up but there are some version 5001 database in the wild</span>
<span class="n">_PHOTOS_4_VERSION</span> <span class="o">=</span> <span class="s2">&quot;4025&quot;</span> <span class="c1"># latest Mojove version on 10.14.6</span>
<span class="n">_PHOTOS_5_VERSION</span> <span class="o">=</span> <span class="s2">&quot;5000&quot;</span> <span class="c1"># I&#39;ve seen both 5001 and 6000. 6000 is most common on Catalina and up but there are some version 5001 database in the wild</span>
<span class="c1"># Ranges for model version by Photos version</span>
<span class="n">_PHOTOS_5_MODEL_VERSION</span> <span class="o">=</span> <span class="p">[</span><span class="mi">13000</span><span class="p">,</span> <span class="mi">13999</span><span class="p">]</span>
@@ -246,95 +247,99 @@
<span class="c1"># some table names differ between Photos 5 and Photos 6</span>
<span class="n">_DB_TABLE_NAMES</span> <span class="o">=</span> <span class="p">{</span>
<span class="mi">5</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"ASSET"</span><span class="p">:</span> <span class="s2">"ZGENERICASSET"</span><span class="p">,</span>
<span class="s2">"KEYWORD_JOIN"</span><span class="p">:</span> <span class="s2">"Z_1KEYWORDS.Z_37KEYWORDS"</span><span class="p">,</span>
<span class="s2">"ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS.Z_34ASSETS"</span><span class="p">,</span>
<span class="s2">"ALBUM_SORT_ORDER"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS.Z_FOK_34ASSETS"</span><span class="p">,</span>
<span class="s2">"IMPORT_FOK"</span><span class="p">:</span> <span class="s2">"ZGENERICASSET.Z_FOK_IMPORTSESSION"</span><span class="p">,</span>
<span class="s2">"DEPTH_STATE"</span><span class="p">:</span> <span class="s2">"ZGENERICASSET.ZDEPTHSTATES"</span><span class="p">,</span>
<span class="s2">"UTI_ORIGINAL"</span><span class="p">:</span> <span class="s2">"ZINTERNALRESOURCE.ZUNIFORMTYPEIDENTIFIER"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS.Z_26ALBUMS"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_TABLE"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS"</span><span class="p">,</span>
<span class="s2">"HDR_TYPE"</span><span class="p">:</span> <span class="s2">"ZCUSTOMRENDEREDVALUE"</span><span class="p">,</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZGENERICASSET&quot;</span><span class="p">,</span>
<span class="s2">&quot;KEYWORD_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_1KEYWORDS.Z_37KEYWORDS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_34ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_SORT_ORDER&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_FOK_34ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;IMPORT_FOK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZGENERICASSET.Z_FOK_IMPORTSESSION&quot;</span><span class="p">,</span>
<span class="s2">&quot;DEPTH_STATE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZGENERICASSET.ZDEPTHSTATES&quot;</span><span class="p">,</span>
<span class="s2">&quot;UTI_ORIGINAL&quot;</span><span class="p">:</span> <span class="s2">&quot;ZINTERNALRESOURCE.ZUNIFORMTYPEIDENTIFIER&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_26ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZCUSTOMRENDEREDVALUE&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">6</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"ASSET"</span><span class="p">:</span> <span class="s2">"ZASSET"</span><span class="p">,</span>
<span class="s2">"KEYWORD_JOIN"</span><span class="p">:</span> <span class="s2">"Z_1KEYWORDS.Z_36KEYWORDS"</span><span class="p">,</span>
<span class="s2">"ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS.Z_3ASSETS"</span><span class="p">,</span>
<span class="s2">"ALBUM_SORT_ORDER"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS.Z_FOK_3ASSETS"</span><span class="p">,</span>
<span class="s2">"IMPORT_FOK"</span><span class="p">:</span> <span class="s2">"null"</span><span class="p">,</span>
<span class="s2">"DEPTH_STATE"</span><span class="p">:</span> <span class="s2">"ZASSET.ZDEPTHTYPE"</span><span class="p">,</span>
<span class="s2">"UTI_ORIGINAL"</span><span class="p">:</span> <span class="s2">"ZINTERNALRESOURCE.ZUNIFORMTYPEIDENTIFIER"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS.Z_26ALBUMS"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_TABLE"</span><span class="p">:</span> <span class="s2">"Z_26ASSETS"</span><span class="p">,</span>
<span class="s2">"HDR_TYPE"</span><span class="p">:</span> <span class="s2">"ZCUSTOMRENDEREDVALUE"</span><span class="p">,</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
<span class="s2">&quot;KEYWORD_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_1KEYWORDS.Z_36KEYWORDS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_SORT_ORDER&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_FOK_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;IMPORT_FOK&quot;</span><span class="p">:</span> <span class="s2">&quot;null&quot;</span><span class="p">,</span>
<span class="s2">&quot;DEPTH_STATE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET.ZDEPTHTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;UTI_ORIGINAL&quot;</span><span class="p">:</span> <span class="s2">&quot;ZINTERNALRESOURCE.ZUNIFORMTYPEIDENTIFIER&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_26ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZCUSTOMRENDEREDVALUE&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">7</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"ASSET"</span><span class="p">:</span> <span class="s2">"ZASSET"</span><span class="p">,</span>
<span class="s2">"KEYWORD_JOIN"</span><span class="p">:</span> <span class="s2">"Z_1KEYWORDS.Z_38KEYWORDS"</span><span class="p">,</span>
<span class="s2">"ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_27ASSETS.Z_3ASSETS"</span><span class="p">,</span>
<span class="s2">"ALBUM_SORT_ORDER"</span><span class="p">:</span> <span class="s2">"Z_27ASSETS.Z_FOK_3ASSETS"</span><span class="p">,</span>
<span class="s2">"IMPORT_FOK"</span><span class="p">:</span> <span class="s2">"null"</span><span class="p">,</span>
<span class="s2">"DEPTH_STATE"</span><span class="p">:</span> <span class="s2">"ZASSET.ZDEPTHTYPE"</span><span class="p">,</span>
<span class="s2">"UTI_ORIGINAL"</span><span class="p">:</span> <span class="s2">"ZINTERNALRESOURCE.ZCOMPACTUTI"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_27ASSETS.Z_27ALBUMS"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_TABLE"</span><span class="p">:</span> <span class="s2">"Z_27ASSETS"</span><span class="p">,</span>
<span class="s2">"HDR_TYPE"</span><span class="p">:</span> <span class="s2">"ZHDRTYPE"</span><span class="p">,</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
<span class="s2">&quot;KEYWORD_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_1KEYWORDS.Z_38KEYWORDS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_27ASSETS.Z_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_SORT_ORDER&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_27ASSETS.Z_FOK_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;IMPORT_FOK&quot;</span><span class="p">:</span> <span class="s2">&quot;null&quot;</span><span class="p">,</span>
<span class="s2">&quot;DEPTH_STATE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET.ZDEPTHTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;UTI_ORIGINAL&quot;</span><span class="p">:</span> <span class="s2">&quot;ZINTERNALRESOURCE.ZCOMPACTUTI&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_27ASSETS.Z_27ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_27ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZHDRTYPE&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">8</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"ASSET"</span><span class="p">:</span> <span class="s2">"ZASSET"</span><span class="p">,</span>
<span class="s2">"KEYWORD_JOIN"</span><span class="p">:</span> <span class="s2">"Z_1KEYWORDS.Z_40KEYWORDS"</span><span class="p">,</span>
<span class="s2">"ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_28ASSETS.Z_3ASSETS"</span><span class="p">,</span>
<span class="s2">"ALBUM_SORT_ORDER"</span><span class="p">:</span> <span class="s2">"Z_28ASSETS.Z_FOK_3ASSETS"</span><span class="p">,</span>
<span class="s2">"IMPORT_FOK"</span><span class="p">:</span> <span class="s2">"null"</span><span class="p">,</span>
<span class="s2">"DEPTH_STATE"</span><span class="p">:</span> <span class="s2">"ZASSET.ZDEPTHTYPE"</span><span class="p">,</span>
<span class="s2">"UTI_ORIGINAL"</span><span class="p">:</span> <span class="s2">"ZINTERNALRESOURCE.ZCOMPACTUTI"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_JOIN"</span><span class="p">:</span> <span class="s2">"Z_28ASSETS.Z_28ALBUMS"</span><span class="p">,</span>
<span class="s2">"ASSET_ALBUM_TABLE"</span><span class="p">:</span> <span class="s2">"Z_28ASSETS"</span><span class="p">,</span>
<span class="s2">"HDR_TYPE"</span><span class="p">:</span> <span class="s2">"ZHDRTYPE"</span><span class="p">,</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
<span class="s2">&quot;KEYWORD_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_1KEYWORDS.Z_40KEYWORDS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_SORT_ORDER&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_FOK_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;IMPORT_FOK&quot;</span><span class="p">:</span> <span class="s2">&quot;null&quot;</span><span class="p">,</span>
<span class="s2">&quot;DEPTH_STATE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET.ZDEPTHTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;UTI_ORIGINAL&quot;</span><span class="p">:</span> <span class="s2">&quot;ZINTERNALRESOURCE.ZCOMPACTUTI&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_28ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZHDRTYPE&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="c1"># which version operating systems have been tested</span>
<span class="n">_TESTED_OS_VERSIONS</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="s2">"10"</span><span class="p">,</span> <span class="s2">"12"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"10"</span><span class="p">,</span> <span class="s2">"13"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"10"</span><span class="p">,</span> <span class="s2">"14"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"10"</span><span class="p">,</span> <span class="s2">"15"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"10"</span><span class="p">,</span> <span class="s2">"16"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"0"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"1"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"2"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"3"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"4"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"5"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"11"</span><span class="p">,</span> <span class="s2">"6"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"12"</span><span class="p">,</span> <span class="s2">"0"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"12"</span><span class="p">,</span> <span class="s2">"1"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"12"</span><span class="p">,</span> <span class="s2">"2"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"12"</span><span class="p">,</span> <span class="s2">"3"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"12"</span><span class="p">,</span> <span class="s2">"4"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;10&quot;</span><span class="p">,</span> <span class="s2">&quot;12&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;10&quot;</span><span class="p">,</span> <span class="s2">&quot;13&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;10&quot;</span><span class="p">,</span> <span class="s2">&quot;14&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;10&quot;</span><span class="p">,</span> <span class="s2">&quot;15&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;10&quot;</span><span class="p">,</span> <span class="s2">&quot;16&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;0&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;1&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;2&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;3&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;4&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;5&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;6&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;11&quot;</span><span class="p">,</span> <span class="s2">&quot;7&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;0&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;1&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;2&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;3&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;4&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;5&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;6&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;13&quot;</span><span class="p">,</span> <span class="s2">&quot;0&quot;</span><span class="p">),</span>
<span class="p">]</span>
<span class="c1"># Photos 5 has persons who are empty string if unidentified face</span>
<span class="n">_UNKNOWN_PERSON</span> <span class="o">=</span> <span class="s2">"_UNKNOWN_"</span>
<span class="n">_UNKNOWN_PERSON</span> <span class="o">=</span> <span class="s2">&quot;_UNKNOWN_&quot;</span>
<span class="c1"># photos with no reverse geolocation info (place)</span>
<span class="n">_UNKNOWN_PLACE</span> <span class="o">=</span> <span class="s2">"_UNKNOWN_"</span>
<span class="n">_UNKNOWN_PLACE</span> <span class="o">=</span> <span class="s2">&quot;_UNKNOWN_&quot;</span>
<span class="n">_EXIF_TOOL_URL</span> <span class="o">=</span> <span class="s2">"https://exiftool.org/"</span>
<span class="n">_EXIF_TOOL_URL</span> <span class="o">=</span> <span class="s2">&quot;https://exiftool.org/&quot;</span>
<span class="c1"># Where are shared iCloud photos located?</span>
<span class="n">_PHOTOS_5_SHARED_PHOTO_PATH</span> <span class="o">=</span> <span class="s2">"resources/cloudsharing/data"</span>
<span class="n">_PHOTOS_5_SHARED_PHOTO_PATH</span> <span class="o">=</span> <span class="s2">&quot;resources/cloudsharing/data&quot;</span>
<span class="c1"># What type of file? Based on ZGENERICASSET.ZKIND in Photos 5 database</span>
<span class="n">_PHOTO_TYPE</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">_MOVIE_TYPE</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c1"># Name of XMP template file</span>
<span class="n">_TEMPLATE_DIR</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> <span class="s2">"templates"</span><span class="p">)</span>
<span class="n">_XMP_TEMPLATE_NAME</span> <span class="o">=</span> <span class="s2">"xmp_sidecar.mako"</span>
<span class="n">_XMP_TEMPLATE_NAME_BETA</span> <span class="o">=</span> <span class="s2">"xmp_sidecar_beta.mako"</span>
<span class="n">_TEMPLATE_DIR</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> <span class="s2">&quot;templates&quot;</span><span class="p">)</span>
<span class="n">_XMP_TEMPLATE_NAME</span> <span class="o">=</span> <span class="s2">&quot;xmp_sidecar.mako&quot;</span>
<span class="n">_XMP_TEMPLATE_NAME_BETA</span> <span class="o">=</span> <span class="s2">&quot;xmp_sidecar_beta.mako&quot;</span>
<span class="c1"># Constants used for processing folders and albums</span>
<span class="n">_PHOTOS_5_ALBUM_KIND</span> <span class="o">=</span> <span class="mi">2</span> <span class="c1"># normal user album</span>
@@ -349,79 +354,114 @@
<span class="n">_PHOTOS_4_ALBUM_TYPE_PROJECT</span> <span class="o">=</span> <span class="mi">9</span> <span class="c1"># RKAlbum.albumType</span>
<span class="n">_PHOTOS_4_ALBUM_TYPE_SLIDESHOW</span> <span class="o">=</span> <span class="mi">8</span> <span class="c1"># RKAlbum.albumType</span>
<span class="n">_PHOTOS_4_TOP_LEVEL_ALBUMS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"TopLevelAlbums"</span><span class="p">,</span>
<span class="s2">"TopLevelKeepsakes"</span><span class="p">,</span>
<span class="s2">"TopLevelSlideshows"</span><span class="p">,</span>
<span class="s2">&quot;TopLevelAlbums&quot;</span><span class="p">,</span>
<span class="s2">&quot;TopLevelKeepsakes&quot;</span><span class="p">,</span>
<span class="s2">&quot;TopLevelSlideshows&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">_PHOTOS_4_ROOT_FOLDER</span> <span class="o">=</span> <span class="s2">"LibraryFolder"</span>
<span class="n">_PHOTOS_4_ROOT_FOLDER</span> <span class="o">=</span> <span class="s2">&quot;LibraryFolder&quot;</span>
<span class="c1"># EXIF related constants</span>
<span class="c1"># max keyword length for IPTC:Keyword, reference</span>
<span class="c1"># https://www.iptc.org/std/photometadata/documentation/userguide/</span>
<span class="n">_MAX_IPTC_KEYWORD_LEN</span> <span class="o">=</span> <span class="mi">64</span>
<span class="c1"># Sentinel value for detecting if a template in keyword_template doesn't match</span>
<span class="c1"># Sentinel value for detecting if a template in keyword_template doesn&#39;t match</span>
<span class="c1"># If anyone has a keyword matching this, then too bad...</span>
<span class="n">_OSXPHOTOS_NONE_SENTINEL</span> <span class="o">=</span> <span class="s2">"OSXPhotosXYZZY42_Sentinel$"</span>
<span class="n">_OSXPHOTOS_NONE_SENTINEL</span> <span class="o">=</span> <span class="s2">&quot;OSXPhotosXYZZY42_Sentinel$&quot;</span>
<span class="c1"># SearchInfo categories for Photos 5, corresponds to categories in database/search/psi.sqlite</span>
<span class="n">SEARCH_CATEGORY_LABEL</span> <span class="o">=</span> <span class="mi">2024</span>
<span class="n">SEARCH_CATEGORY_PLACE_NAME</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">SEARCH_CATEGORY_STREET</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">SEARCH_CATEGORY_NEIGHBORHOOD</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">SEARCH_CATEGORY_LOCALITY_4</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">SEARCH_CATEGORY_SUB_LOCALITY_5</span> <span class="o">=</span> <span class="mi">5</span>
<span class="n">SEARCH_CATEGORY_SUB_LOCALITY_6</span> <span class="o">=</span> <span class="mi">6</span>
<span class="n">SEARCH_CATEGORY_CITY</span> <span class="o">=</span> <span class="mi">7</span>
<span class="n">SEARCH_CATEGORY_LOCALITY_8</span> <span class="o">=</span> <span class="mi">8</span>
<span class="n">SEARCH_CATEGORY_NAMED_AREA</span> <span class="o">=</span> <span class="mi">9</span>
<span class="n">SEARCH_CATEGORY_ALL_LOCALITY</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">SEARCH_CATEGORY_LOCALITY_4</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_SUB_LOCALITY_5</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_SUB_LOCALITY_6</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_LOCALITY_8</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_NAMED_AREA</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">SEARCH_CATEGORY_STATE</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">SEARCH_CATEGORY_STATE_ABBREVIATION</span> <span class="o">=</span> <span class="mi">11</span>
<span class="n">SEARCH_CATEGORY_COUNTRY</span> <span class="o">=</span> <span class="mi">12</span>
<span class="n">SEARCH_CATEGORY_BODY_OF_WATER</span> <span class="o">=</span> <span class="mi">14</span>
<span class="n">SEARCH_CATEGORY_MONTH</span> <span class="o">=</span> <span class="mi">1014</span>
<span class="n">SEARCH_CATEGORY_YEAR</span> <span class="o">=</span> <span class="mi">1015</span>
<span class="n">SEARCH_CATEGORY_KEYWORDS</span> <span class="o">=</span> <span class="mi">2016</span>
<span class="n">SEARCH_CATEGORY_TITLE</span> <span class="o">=</span> <span class="mi">2017</span>
<span class="n">SEARCH_CATEGORY_DESCRIPTION</span> <span class="o">=</span> <span class="mi">2018</span>
<span class="n">SEARCH_CATEGORY_HOME</span> <span class="o">=</span> <span class="mi">2020</span>
<span class="n">SEARCH_CATEGORY_PERSON</span> <span class="o">=</span> <span class="mi">2021</span>
<span class="n">SEARCH_CATEGORY_ACTIVITY</span> <span class="o">=</span> <span class="mi">2027</span>
<span class="n">SEARCH_CATEGORY_HOLIDAY</span> <span class="o">=</span> <span class="mi">2029</span>
<span class="n">SEARCH_CATEGORY_SEASON</span> <span class="o">=</span> <span class="mi">2030</span>
<span class="n">SEARCH_CATEGORY_WORK</span> <span class="o">=</span> <span class="mi">2036</span>
<span class="n">SEARCH_CATEGORY_VENUE</span> <span class="o">=</span> <span class="mi">2038</span>
<span class="n">SEARCH_CATEGORY_VENUE_TYPE</span> <span class="o">=</span> <span class="mi">2039</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_VIDEO</span> <span class="o">=</span> <span class="mi">2044</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_SLOMO</span> <span class="o">=</span> <span class="mi">2045</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_LIVE</span> <span class="o">=</span> <span class="mi">2046</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_SCREENSHOT</span> <span class="o">=</span> <span class="mi">2047</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_PANORAMA</span> <span class="o">=</span> <span class="mi">2048</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_TIMELAPSE</span> <span class="o">=</span> <span class="mi">2049</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_BURSTS</span> <span class="o">=</span> <span class="mi">2052</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_PORTRAIT</span> <span class="o">=</span> <span class="mi">2053</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_SELFIES</span> <span class="o">=</span> <span class="mi">2054</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_FAVORITES</span> <span class="o">=</span> <span class="mi">2055</span>
<span class="n">SEARCH_CATEGORY_MEDIA_TYPES</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_VIDEO</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_SLOMO</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_LIVE</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_SCREENSHOT</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_PANORAMA</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_TIMELAPSE</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_BURSTS</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_PORTRAIT</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_SELFIES</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PHOTO_TYPE_FAVORITES</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">SEARCH_CATEGORY_PHOTO_NAME</span> <span class="o">=</span> <span class="mi">2056</span>
<span class="k">class</span> <span class="nc">SearchCategory</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;SearchInfo categories for Photos 5+; corresponds to categories in database/search/psi.sqlite:groups.category</span>
<span class="sd"> Note: This is a simple enum class; the values are not meant to be changed.</span>
<span class="sd"> Would be great if Python enums actually let you access the value directly.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">LABEL</span> <span class="o">=</span> <span class="mi">2024</span>
<span class="n">PLACE_NAME</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">STREET</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">NEIGHBORHOOD</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">LOCALITY_4</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">SUB_LOCALITY_5</span> <span class="o">=</span> <span class="mi">5</span>
<span class="n">SUB_LOCALITY_6</span> <span class="o">=</span> <span class="mi">6</span>
<span class="n">CITY</span> <span class="o">=</span> <span class="mi">7</span>
<span class="n">LOCALITY_8</span> <span class="o">=</span> <span class="mi">8</span>
<span class="n">NAMED_AREA</span> <span class="o">=</span> <span class="mi">9</span>
<span class="n">ALL_LOCALITY</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">LOCALITY_4</span><span class="p">,</span>
<span class="n">SUB_LOCALITY_5</span><span class="p">,</span>
<span class="n">SUB_LOCALITY_6</span><span class="p">,</span>
<span class="n">LOCALITY_8</span><span class="p">,</span>
<span class="n">NAMED_AREA</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">STATE</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">STATE_ABBREVIATION</span> <span class="o">=</span> <span class="mi">11</span>
<span class="n">COUNTRY</span> <span class="o">=</span> <span class="mi">12</span>
<span class="n">BODY_OF_WATER</span> <span class="o">=</span> <span class="mi">14</span>
<span class="n">MONTH</span> <span class="o">=</span> <span class="mi">1014</span>
<span class="n">YEAR</span> <span class="o">=</span> <span class="mi">1015</span>
<span class="n">KEYWORDS</span> <span class="o">=</span> <span class="mi">2016</span>
<span class="n">TITLE</span> <span class="o">=</span> <span class="mi">2017</span>
<span class="n">DESCRIPTION</span> <span class="o">=</span> <span class="mi">2018</span>
<span class="n">HOME</span> <span class="o">=</span> <span class="mi">2020</span>
<span class="n">PERSON</span> <span class="o">=</span> <span class="mi">2021</span>
<span class="n">ACTIVITY</span> <span class="o">=</span> <span class="mi">2027</span>
<span class="n">HOLIDAY</span> <span class="o">=</span> <span class="mi">2029</span>
<span class="n">SEASON</span> <span class="o">=</span> <span class="mi">2030</span>
<span class="n">WORK</span> <span class="o">=</span> <span class="mi">2036</span>
<span class="n">VENUE</span> <span class="o">=</span> <span class="mi">2038</span>
<span class="n">VENUE_TYPE</span> <span class="o">=</span> <span class="mi">2039</span>
<span class="n">PHOTO_TYPE_VIDEO</span> <span class="o">=</span> <span class="mi">2044</span>
<span class="n">PHOTO_TYPE_SLOMO</span> <span class="o">=</span> <span class="mi">2045</span>
<span class="n">PHOTO_TYPE_LIVE</span> <span class="o">=</span> <span class="mi">2046</span>
<span class="n">PHOTO_TYPE_SCREENSHOT</span> <span class="o">=</span> <span class="mi">2047</span>
<span class="n">PHOTO_TYPE_PANORAMA</span> <span class="o">=</span> <span class="mi">2048</span>
<span class="n">PHOTO_TYPE_TIMELAPSE</span> <span class="o">=</span> <span class="mi">2049</span>
<span class="n">PHOTO_TYPE_BURSTS</span> <span class="o">=</span> <span class="mi">2052</span>
<span class="n">PHOTO_TYPE_PORTRAIT</span> <span class="o">=</span> <span class="mi">2053</span>
<span class="n">PHOTO_TYPE_SELFIES</span> <span class="o">=</span> <span class="mi">2054</span>
<span class="n">PHOTO_TYPE_FAVORITES</span> <span class="o">=</span> <span class="mi">2055</span>
<span class="n">MEDIA_TYPES</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">PHOTO_TYPE_VIDEO</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_SLOMO</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_LIVE</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_SCREENSHOT</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_PANORAMA</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_TIMELAPSE</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_BURSTS</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_PORTRAIT</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_SELFIES</span><span class="p">,</span>
<span class="n">PHOTO_TYPE_FAVORITES</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">PHOTO_NAME</span> <span class="o">=</span> <span class="mi">2056</span>
<span class="n">CAMERA</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 8+ only</span>
<span class="n">DETECTED_TEXT</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 8+ only</span>
<span class="k">class</span> <span class="nc">SearchCategory_Photos8</span><span class="p">(</span><span class="n">SearchCategory</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Search categories for Photos 8&quot;&quot;&quot;</span>
<span class="c1"># Many of the category values changed in Ventura / Photos 8</span>
<span class="c1"># and some new categories were added</span>
<span class="n">LABEL</span> <span class="o">=</span> <span class="mi">1500</span>
<span class="n">MONTH</span> <span class="o">=</span> <span class="mi">1100</span>
<span class="n">YEAR</span> <span class="o">=</span> <span class="mi">1101</span>
<span class="n">HOLIDAY</span> <span class="o">=</span> <span class="mi">1103</span>
<span class="n">SEASON</span> <span class="o">=</span> <span class="mi">1104</span>
<span class="n">KEYWORDS</span> <span class="o">=</span> <span class="mi">1200</span>
<span class="n">TITLE</span> <span class="o">=</span> <span class="mi">1201</span>
<span class="n">DESCRIPTION</span> <span class="o">=</span> <span class="mi">1202</span>
<span class="n">DETECTED_TEXT</span> <span class="o">=</span> <span class="mi">1203</span> <span class="c1"># new in Photos 8</span>
<span class="n">PERSON</span> <span class="o">=</span> <span class="mi">1300</span>
<span class="n">ACTIVITY</span> <span class="o">=</span> <span class="mi">1600</span>
<span class="n">PHOTO_TYPE_FAVORITES</span> <span class="o">=</span> <span class="mi">2000</span>
<span class="n">PHOTO_NAME</span> <span class="o">=</span> <span class="mi">2100</span>
<span class="n">CAMERA</span> <span class="o">=</span> <span class="mi">2300</span> <span class="c1"># new in Photos 8</span>
<span class="k">def</span> <span class="nf">search_category_factory</span><span class="p">(</span><span class="n">version</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">SearchCategory</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return SearchCategory class for Photos version&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">SearchCategory_Photos8</span> <span class="k">if</span> <span class="n">version</span> <span class="o">&gt;=</span> <span class="mi">8</span> <span class="k">else</span> <span class="n">SearchCategory</span>
<span class="c1"># Max filename length on MacOS</span>
@@ -434,13 +474,13 @@
<span class="n">DEFAULT_JPEG_QUALITY</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="c1"># Default suffix to add to edited images</span>
<span class="n">DEFAULT_EDITED_SUFFIX</span> <span class="o">=</span> <span class="s2">"_edited"</span>
<span class="n">DEFAULT_EDITED_SUFFIX</span> <span class="o">=</span> <span class="s2">&quot;_edited&quot;</span>
<span class="c1"># Default suffix to add to original images</span>
<span class="n">DEFAULT_ORIGINAL_SUFFIX</span> <span class="o">=</span> <span class="s2">""</span>
<span class="n">DEFAULT_ORIGINAL_SUFFIX</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="c1"># Default suffix to add to preview images</span>
<span class="n">DEFAULT_PREVIEW_SUFFIX</span> <span class="o">=</span> <span class="s2">"_preview"</span>
<span class="n">DEFAULT_PREVIEW_SUFFIX</span> <span class="o">=</span> <span class="s2">&quot;_preview&quot;</span>
<span class="c1"># Bit masks for --sidecar</span>
<span class="n">SIDECAR_JSON</span> <span class="o">=</span> <span class="mh">0x1</span>
@@ -449,28 +489,27 @@
<span class="c1"># supported attributes for --xattr-template</span>
<span class="n">EXTENDED_ATTRIBUTE_NAMES</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"authors"</span><span class="p">,</span>
<span class="s2">"comment"</span><span class="p">,</span>
<span class="s2">"copyright"</span><span class="p">,</span>
<span class="s2">"creator"</span><span class="p">,</span>
<span class="s2">"description"</span><span class="p">,</span>
<span class="s2">"findercomment"</span><span class="p">,</span>
<span class="s2">"headline"</span><span class="p">,</span>
<span class="s2">"keywords"</span><span class="p">,</span>
<span class="s2">"participants"</span><span class="p">,</span>
<span class="s2">"projects"</span><span class="p">,</span>
<span class="s2">"rating"</span><span class="p">,</span>
<span class="s2">"subject"</span><span class="p">,</span>
<span class="s2">"title"</span><span class="p">,</span>
<span class="s2">"version"</span><span class="p">,</span>
<span class="s2">&quot;authors&quot;</span><span class="p">,</span>
<span class="s2">&quot;comment&quot;</span><span class="p">,</span>
<span class="s2">&quot;copyright&quot;</span><span class="p">,</span>
<span class="s2">&quot;creator&quot;</span><span class="p">,</span>
<span class="s2">&quot;description&quot;</span><span class="p">,</span>
<span class="s2">&quot;findercomment&quot;</span><span class="p">,</span>
<span class="s2">&quot;headline&quot;</span><span class="p">,</span>
<span class="s2">&quot;participants&quot;</span><span class="p">,</span>
<span class="s2">&quot;projects&quot;</span><span class="p">,</span>
<span class="s2">&quot;starrating&quot;</span><span class="p">,</span>
<span class="s2">&quot;subject&quot;</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">,</span>
<span class="s2">&quot;version&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">EXTENDED_ATTRIBUTE_NAMES_QUOTED</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"'</span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2">'"</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">EXTENDED_ATTRIBUTE_NAMES</span><span class="p">]</span>
<span class="n">EXTENDED_ATTRIBUTE_NAMES_QUOTED</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;&#39;</span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2">&#39;&quot;</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">EXTENDED_ATTRIBUTE_NAMES</span><span class="p">]</span>
<span class="c1"># name of export DB</span>
<span class="n">OSXPHOTOS_EXPORT_DB</span> <span class="o">=</span> <span class="s2">".osxphotos_export.db"</span>
<span class="n">OSXPHOTOS_EXPORT_DB</span> <span class="o">=</span> <span class="s2">&quot;.osxphotos_export.db&quot;</span>
<span class="c1"># bit flags for burst images ("burstPickType")</span>
<span class="c1"># bit flags for burst images (&quot;burstPickType&quot;)</span>
<span class="n">BURST_PICK_TYPE_NONE</span> <span class="o">=</span> <span class="mb">0b0</span> <span class="c1"># 0: sometimes used for single images with a burst UUID</span>
<span class="n">BURST_NOT_SELECTED</span> <span class="o">=</span> <span class="mb">0b10</span> <span class="c1"># 2: burst image is not selected</span>
<span class="n">BURST_DEFAULT_PICK</span> <span class="o">=</span> <span class="mb">0b100</span> <span class="c1"># 4: burst image is the one Photos picked to be key image before any selections made</span>
@@ -478,32 +517,32 @@
<span class="n">BURST_KEY</span> <span class="o">=</span> <span class="mb">0b10000</span> <span class="c1"># 16: burst image is the key photo (top of burst stack)</span>
<span class="n">BURST_UNKNOWN</span> <span class="o">=</span> <span class="mb">0b100000</span> <span class="c1"># 32: this is almost always set with BURST_DEFAULT_PICK and never if BURST_DEFAULT_PICK is not set. I think this has something to do with what algorithm Photos used to pick the default image</span>
<span class="n">LIVE_VIDEO_EXTENSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">".mov"</span><span class="p">]</span>
<span class="n">LIVE_VIDEO_EXTENSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;.mov&quot;</span><span class="p">]</span>
<span class="c1"># categories that --post-command can be used with; these map to ExportResults fields</span>
<span class="n">POST_COMMAND_CATEGORIES</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"exported"</span><span class="p">:</span> <span class="s2">"All exported files"</span><span class="p">,</span>
<span class="s2">"new"</span><span class="p">:</span> <span class="s2">"When used with '--update', all newly exported files"</span><span class="p">,</span>
<span class="s2">"updated"</span><span class="p">:</span> <span class="s2">"When used with '--update', all files which were previously exported but updated this time"</span><span class="p">,</span>
<span class="s2">"skipped"</span><span class="p">:</span> <span class="s2">"When used with '--update', all files which were skipped (because they were previously exported and didn't change)"</span><span class="p">,</span>
<span class="s2">"missing"</span><span class="p">:</span> <span class="s2">"All files which were not exported because they were missing from the Photos library"</span><span class="p">,</span>
<span class="s2">"exif_updated"</span><span class="p">:</span> <span class="s2">"When used with '--exiftool', all files on which exiftool updated the metadata"</span><span class="p">,</span>
<span class="s2">"touched"</span><span class="p">:</span> <span class="s2">"When used with '--touch-file', all files where the date was touched"</span><span class="p">,</span>
<span class="s2">"converted_to_jpeg"</span><span class="p">:</span> <span class="s2">"When used with '--convert-to-jpeg', all files which were converted to jpeg"</span><span class="p">,</span>
<span class="s2">"sidecar_json_written"</span><span class="p">:</span> <span class="s2">"When used with '--sidecar json', all JSON sidecar files which were written"</span><span class="p">,</span>
<span class="s2">"sidecar_json_skipped"</span><span class="p">:</span> <span class="s2">"When used with '--sidecar json' and '--update', all JSON sidecar files which were skipped"</span><span class="p">,</span>
<span class="s2">"sidecar_exiftool_written"</span><span class="p">:</span> <span class="s2">"When used with '--sidecar exiftool', all exiftool sidecar files which were written"</span><span class="p">,</span>
<span class="s2">"sidecar_exiftool_skipped"</span><span class="p">:</span> <span class="s2">"When used with '--sidecar exiftool' and '--update, all exiftool sidecar files which were skipped"</span><span class="p">,</span>
<span class="s2">"sidecar_xmp_written"</span><span class="p">:</span> <span class="s2">"When used with '--sidecar xmp', all XMP sidecar files which were written"</span><span class="p">,</span>
<span class="s2">"sidecar_xmp_skipped"</span><span class="p">:</span> <span class="s2">"When used with '--sidecar xmp' and '--update', all XMP sidecar files which were skipped"</span><span class="p">,</span>
<span class="s2">"error"</span><span class="p">:</span> <span class="s2">"All files which produced an error during export"</span><span class="p">,</span>
<span class="c1"># "deleted_files": "When used with '--cleanup', all files deleted during the export",</span>
<span class="c1"># "deleted_directories": "When used with '--cleanup', all directories deleted during the export",</span>
<span class="s2">&quot;exported&quot;</span><span class="p">:</span> <span class="s2">&quot;All exported files&quot;</span><span class="p">,</span>
<span class="s2">&quot;new&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--update&#39;, all newly exported files&quot;</span><span class="p">,</span>
<span class="s2">&quot;updated&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--update&#39;, all files which were previously exported but updated this time&quot;</span><span class="p">,</span>
<span class="s2">&quot;skipped&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--update&#39;, all files which were skipped (because they were previously exported and didn&#39;t change)&quot;</span><span class="p">,</span>
<span class="s2">&quot;missing&quot;</span><span class="p">:</span> <span class="s2">&quot;All files which were not exported because they were missing from the Photos library&quot;</span><span class="p">,</span>
<span class="s2">&quot;exif_updated&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--exiftool&#39;, all files on which exiftool updated the metadata&quot;</span><span class="p">,</span>
<span class="s2">&quot;touched&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--touch-file&#39;, all files where the date was touched&quot;</span><span class="p">,</span>
<span class="s2">&quot;converted_to_jpeg&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--convert-to-jpeg&#39;, all files which were converted to jpeg&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_json_written&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--sidecar json&#39;, all JSON sidecar files which were written&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_json_skipped&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--sidecar json&#39; and &#39;--update&#39;, all JSON sidecar files which were skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_exiftool_written&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--sidecar exiftool&#39;, all exiftool sidecar files which were written&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_exiftool_skipped&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--sidecar exiftool&#39; and &#39;--update, all exiftool sidecar files which were skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_xmp_written&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--sidecar xmp&#39;, all XMP sidecar files which were written&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_xmp_skipped&quot;</span><span class="p">:</span> <span class="s2">&quot;When used with &#39;--sidecar xmp&#39; and &#39;--update&#39;, all XMP sidecar files which were skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;error&quot;</span><span class="p">:</span> <span class="s2">&quot;All files which produced an error during export&quot;</span><span class="p">,</span>
<span class="c1"># &quot;deleted_files&quot;: &quot;When used with &#39;--cleanup&#39;, all files deleted during the export&quot;,</span>
<span class="c1"># &quot;deleted_directories&quot;: &quot;When used with &#39;--cleanup&#39;, all directories deleted during the export&quot;,</span>
<span class="p">}</span>
<div class="viewcode-block" id="AlbumSortOrder"><a class="viewcode-back" href="../../reference.html#osxphotos.AlbumSortOrder">[docs]</a><span class="k">class</span> <span class="nc">AlbumSortOrder</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="sd">"""Album Sort Order"""</span>
<span class="sd">&quot;&quot;&quot;Album Sort Order&quot;&quot;&quot;</span>
<span class="n">UNKNOWN</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">MANUAL</span> <span class="o">=</span> <span class="mi">1</span>
@@ -516,20 +555,20 @@
<span class="c1"># stat sort order for cProfile: https://docs.python.org/3/library/profile.html#pstats.Stats.sort_stats</span>
<span class="n">PROFILE_SORT_KEYS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"calls"</span><span class="p">,</span>
<span class="s2">"cumulative"</span><span class="p">,</span>
<span class="s2">"cumtime"</span><span class="p">,</span>
<span class="s2">"file"</span><span class="p">,</span>
<span class="s2">"filename"</span><span class="p">,</span>
<span class="s2">"module"</span><span class="p">,</span>
<span class="s2">"ncalls"</span><span class="p">,</span>
<span class="s2">"pcalls"</span><span class="p">,</span>
<span class="s2">"line"</span><span class="p">,</span>
<span class="s2">"name"</span><span class="p">,</span>
<span class="s2">"nfl"</span><span class="p">,</span>
<span class="s2">"stdname"</span><span class="p">,</span>
<span class="s2">"time"</span><span class="p">,</span>
<span class="s2">"tottime"</span><span class="p">,</span>
<span class="s2">&quot;calls&quot;</span><span class="p">,</span>
<span class="s2">&quot;cumulative&quot;</span><span class="p">,</span>
<span class="s2">&quot;cumtime&quot;</span><span class="p">,</span>
<span class="s2">&quot;file&quot;</span><span class="p">,</span>
<span class="s2">&quot;filename&quot;</span><span class="p">,</span>
<span class="s2">&quot;module&quot;</span><span class="p">,</span>
<span class="s2">&quot;ncalls&quot;</span><span class="p">,</span>
<span class="s2">&quot;pcalls&quot;</span><span class="p">,</span>
<span class="s2">&quot;line&quot;</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">,</span>
<span class="s2">&quot;nfl&quot;</span><span class="p">,</span>
<span class="s2">&quot;stdname&quot;</span><span class="p">,</span>
<span class="s2">&quot;time&quot;</span><span class="p">,</span>
<span class="s2">&quot;tottime&quot;</span><span class="p">,</span>
<span class="p">]</span>
</pre></div>
</article>
@@ -568,7 +607,9 @@
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>

View File

@@ -1,36 +1,200 @@
<!doctype html>
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<!DOCTYPE html>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.albuminfo - osxphotos 0.54.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.albuminfo &#8212; osxphotos 0.47.9 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>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<span class="sidebar-brand-text">osxphotos 0.54.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
</ul>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
</div>
</div>
<div class="body" role="main">
<h1>Source code for osxphotos.albuminfo</h1><div class="highlight"><pre>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.albuminfo</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">AlbumInfo and FolderInfo classes for dealing with albums and folders</span>
@@ -333,8 +497,14 @@
<span class="p">)</span>
<span class="n">sorted_uuid</span> <span class="o">=</span> <span class="n">sort_list_by_keys</span><span class="p">(</span><span class="n">uuid_list</span><span class="p">,</span> <span class="n">sort_order</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_photos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_by_uuid</span><span class="p">(</span><span class="n">sorted_uuid</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photos</span></div>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photos</span>
<span class="k">def</span> <span class="fm">__bool__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Always returns True</span>
<span class="sd"> A photo without an import session will return None for import_info,</span>
<span class="sd"> thus if import_info is not None, it must be a valid import_info object (#820)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="ProjectInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.ProjectInfo">[docs]</a><span class="k">class</span> <span class="nc">ProjectInfo</span><span class="p">(</span><span class="n">AlbumInfo</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
@@ -345,7 +515,7 @@
<span class="o">...</span></div>
<span class="k">class</span> <span class="nc">FolderInfo</span><span class="p">:</span>
<div class="viewcode-block" id="FolderInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.FolderInfo">[docs]</a><span class="k">class</span> <span class="nc">FolderInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Info about a specific folder, contains all the details about the folder</span>
<span class="sd"> including folders, albums, etc</span>
@@ -445,74 +615,49 @@
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns count of folders + albums contained in the folder&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">subfolders</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">subfolders</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span></div>
</pre></div>
</div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../index.html">osxphotos</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">osxphotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">osxphotos command line interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">osxphotos package</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../index.html">Documentation overview</a><ul>
<li><a href="../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</footer>
</div>
<div class="footer">
&copy;2021, Rhet Turnbull.
<aside class="toc-drawer no-toc">
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</aside>
</div>
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.exiftool - osxphotos 0.49.0 documentation</title>
<title>osxphotos.exiftool - osxphotos 0.50.13 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.49.0 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.49.0 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -202,7 +202,9 @@
<span class="sd"> If these aren't important to you, I highly recommend you use Sven Marnach's excellent </span>
<span class="sd"> pyexiftool: https://github.com/smarnach/pyexiftool which provides more functionality """</span>
<span class="kn">import</span> <span class="nn">atexit</span>
<span class="kn">import</span> <span class="nn">contextlib</span>
<span class="kn">import</span> <span class="nn">html</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">logging</span>
@@ -300,9 +302,12 @@
<span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">large_file_support</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="sd">"""construct _ExifToolProc singleton object or return instance of already created object</span>
<span class="sd"> exiftool: optional path to exiftool binary (if not provided, will search path to find it)</span>
<span class="sd"> Args:</span>
<span class="sd"> exiftool: optional path to exiftool binary (if not provided, will search path to find it)</span>
<span class="sd"> large_file_support: if True, enables large file support (&gt;4GB) via `-api largefilesupport=1`</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"_process_running"</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
@@ -315,16 +320,14 @@
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span> <span class="o">=</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span> <span class="o">=</span> <span class="n">exiftool</span> <span class="ow">or</span> <span class="n">get_exiftool_path</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_start_proc</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_start_proc</span><span class="p">(</span><span class="n">large_file_support</span><span class="o">=</span><span class="n">large_file_support</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""return the exiftool subprocess"""</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_start_proc</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">pid</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@@ -336,7 +339,7 @@
<span class="sd">"""return path to exiftool process"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span>
<span class="k">def</span> <span class="nf">_start_proc</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">_start_proc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">large_file_support</span><span class="p">):</span>
<span class="sd">"""start exiftool in batch mode"""</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
@@ -347,11 +350,13 @@
<span class="c1"># make sure /usr/bin at start of path so exiftool can find xattr (see #636)</span>
<span class="n">env</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">env</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'/usr/bin/:</span><span class="si">{</span><span class="n">env</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span><span class="si">}</span><span class="s1">'</span>
<span class="n">large_file_args</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"-api"</span><span class="p">,</span> <span class="s2">"largefilesupport=1"</span><span class="p">]</span> <span class="k">if</span> <span class="n">large_file_support</span> <span class="k">else</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span>
<span class="p">[</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span><span class="p">,</span>
<span class="s2">"-stay_open"</span><span class="p">,</span> <span class="c1"># keep process open in batch mode</span>
<span class="s2">"True"</span><span class="p">,</span> <span class="c1"># -stay_open=True, keep process open in batch mode</span>
<span class="o">*</span><span class="n">large_file_args</span><span class="p">,</span>
<span class="s2">"-@"</span><span class="p">,</span> <span class="c1"># read command-line arguments from file</span>
<span class="s2">"-"</span><span class="p">,</span> <span class="c1"># read from stdin</span>
<span class="s2">"-common_args"</span><span class="p">,</span> <span class="c1"># specifies args common to all commands subsequently run</span>
@@ -375,13 +380,10 @@
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">with</span> <span class="n">contextlib</span><span class="o">.</span><span class="n">suppress</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">"-stay_open</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">"False</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">flush</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>
<span class="k">pass</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">communicate</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
<span class="k">except</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">TimeoutExpired</span><span class="p">:</span>
@@ -395,7 +397,14 @@
<div class="viewcode-block" id="ExifTool"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool">[docs]</a><span class="k">class</span> <span class="nc">ExifTool</span><span class="p">:</span>
<span class="sd">"""Basic exiftool interface for reading and writing EXIF tags"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepath</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">overwrite</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">flags</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">filepath</span><span class="p">,</span>
<span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">overwrite</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">flags</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">large_file_support</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">"""Create ExifTool object</span>
<span class="sd"> Args:</span>
@@ -403,6 +412,7 @@
<span class="sd"> exiftool: path to exiftool, if not specified will look in path</span>
<span class="sd"> overwrite: if True, will overwrite image file without creating backup, default=False</span>
<span class="sd"> flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F)</span>
<span class="sd"> large_file_support: if True, enables large file support in exiftool (`-api largefilesupport=1`)</span>
<span class="sd"> Returns:</span>
<span class="sd"> ExifTool instance</span>
@@ -415,7 +425,9 @@
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># if running as a context manager, self._context_mgr will be True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span> <span class="o">=</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftoolproc</span> <span class="o">=</span> <span class="n">_ExifToolProc</span><span class="p">(</span><span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftoolproc</span> <span class="o">=</span> <span class="n">_ExifToolProc</span><span class="p">(</span>
<span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool</span><span class="p">,</span> <span class="n">large_file_support</span><span class="o">=</span><span class="n">large_file_support</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_read_exif</span><span class="p">()</span>
<span class="nd">@property</span>
@@ -523,7 +535,7 @@
<span class="n">commands</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">commands</span><span class="p">)</span>
<span class="n">commands</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
<span class="n">filename</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fsencode</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">no_file</span> <span class="k">else</span> <span class="sa">b</span><span class="s2">""</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span> <span class="k">if</span> <span class="n">no_file</span> <span class="k">else</span> <span class="n">os</span><span class="o">.</span><span class="n">fsencode</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">flags</span><span class="p">:</span>
<span class="c1"># need to split flags, e.g. so "--ext AVI" becomes ["--ext", "AVI"]</span>
@@ -619,8 +631,7 @@
<span class="k">def</span> <span class="nf">_read_exif</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""read exif data from file"""</span>
<span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">data</span><span class="o">.</span><span class="n">items</span><span class="p">()}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"file: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="si">}</span><span class="se">\n</span><span class="s2">exiftool: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_exiftoolproc</span><span class="o">.</span><span class="n">_exiftool</span><span class="si">}</span><span class="s2">"</span>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.export_db - osxphotos 0.50.3 documentation</title>
<title>osxphotos.export_db - osxphotos 0.51.8 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.50.3 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.51.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.3 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.51.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -936,10 +936,16 @@
<span class="k">def</span> <span class="nf">_migrate_7_0_to_7_1</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conn</span><span class="p">):</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">"""ALTER TABLE export_data ADD COLUMN timestamp DATETIME;"""</span><span class="p">)</span>
<span class="c1"># timestamp column should not exist but this prevents error if migration is run on an already migrated database</span>
<span class="c1"># reference #794</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="s2">"SELECT COUNT(*) FROM pragma_table_info('export_data') WHERE name='timestamp';"</span>
<span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
<span class="k">if</span> <span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">"""ALTER TABLE export_data ADD COLUMN timestamp DATETIME;"""</span><span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sd">"""</span>
<span class="sd"> CREATE TRIGGER insert_timestamp_trigger</span>
<span class="sd"> CREATE TRIGGER IF NOT EXISTS insert_timestamp_trigger</span>
<span class="sd"> AFTER INSERT ON export_data</span>
<span class="sd"> BEGIN</span>
<span class="sd"> UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;</span>
@@ -948,7 +954,7 @@
<span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sd">"""</span>
<span class="sd"> CREATE TRIGGER update_timestamp_trigger</span>
<span class="sd"> CREATE TRIGGER IF NOT EXISTS update_timestamp_trigger</span>
<span class="sd"> AFTER UPDATE On export_data</span>
<span class="sd"> BEGIN</span>
<span class="sd"> UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;</span>

View File

@@ -1,40 +1,205 @@
<!doctype html>
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<!DOCTYPE html>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.fileutil - osxphotos 0.54.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.fileutil &#8212; osxphotos 0.47.9 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>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<span class="sidebar-brand-text">osxphotos 0.54.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
</ul>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
</div>
</div>
<div class="body" role="main">
<h1>Source code for osxphotos.fileutil</h1><div class="highlight"><pre>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.fileutil</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot; FileUtil class with methods for copy, hardlink, unlink, etc. &quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">shutil</span>
<span class="kn">import</span> <span class="nn">stat</span>
<span class="kn">import</span> <span class="nn">tempfile</span>
<span class="kn">import</span> <span class="nn">typing</span> <span class="k">as</span> <span class="nn">t</span>
@@ -45,7 +210,7 @@
<span class="kn">from</span> <span class="nn">.imageconverter</span> <span class="kn">import</span> <span class="n">ImageConverter</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;FileUtilABC&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtilMacOS&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtil&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtilNoOp&quot;</span><span class="p">]</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;FileUtilABC&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtilMacOS&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtilShUtil&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtil&quot;</span><span class="p">,</span> <span class="s2">&quot;FileUtilNoOp&quot;</span><span class="p">]</span>
<span class="k">class</span> <span class="nc">FileUtilABC</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
@@ -58,7 +223,7 @@
<span class="nd">@classmethod</span>
<span class="nd">@abstractmethod</span>
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">norsrc</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">):</span>
<span class="k">pass</span>
<span class="nd">@classmethod</span>
@@ -283,6 +448,43 @@
<span class="k">return</span> <span class="p">(</span><span class="n">stat</span><span class="o">.</span><span class="n">S_IFMT</span><span class="p">(</span><span class="n">st</span><span class="o">.</span><span class="n">st_mode</span><span class="p">),</span> <span class="n">st</span><span class="o">.</span><span class="n">st_size</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">st</span><span class="o">.</span><span class="n">st_mtime</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">FileUtilShUtil</span><span class="p">(</span><span class="n">FileUtilMacOS</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Various file utilities, uses shutil.copy to copy files instead of NSFileManager (#807)&quot;&quot;&quot;</span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Copies a file from src path to dest path using shutil.copy</span>
<span class="sd"> Args:</span>
<span class="sd"> src: source path as string; must be a valid file path</span>
<span class="sd"> dest: destination path as string</span>
<span class="sd"> dest may be either directory or file; in either case, src file must not exist in dest</span>
<span class="sd"> Note: src and dest may be either a string or a pathlib.Path object</span>
<span class="sd"> Returns:</span>
<span class="sd"> True if copy succeeded</span>
<span class="sd"> Raises:</span>
<span class="sd"> OSError if copy fails</span>
<span class="sd"> TypeError if either path is None</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">):</span>
<span class="n">src</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">):</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="k">if</span> <span class="n">dest</span><span class="o">.</span><span class="n">is_dir</span><span class="p">():</span>
<span class="n">dest</span> <span class="o">/=</span> <span class="n">src</span><span class="o">.</span><span class="n">name</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">shutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">src</span><span class="p">),</span> <span class="nb">str</span><span class="p">(</span><span class="n">dest</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>
<span class="k">raise</span> <span class="ne">OSError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error copying </span><span class="si">{</span><span class="n">src</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
<span class="k">return</span> <span class="kc">True</span>
<div class="viewcode-block" id="FileUtil"><a class="viewcode-back" href="../../reference.html#osxphotos.FileUtil">[docs]</a><span class="k">class</span> <span class="nc">FileUtil</span><span class="p">(</span><span class="n">FileUtilMacOS</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Various file utilities&quot;&quot;&quot;</span>
@@ -313,7 +515,7 @@
<span class="k">pass</span></div>
<div class="viewcode-block" id="FileUtilNoOp.copy"><a class="viewcode-back" href="../../reference.html#osxphotos.FileUtilNoOp.copy">[docs]</a> <span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">norsrc</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">):</span>
<span class="k">pass</span></div>
<div class="viewcode-block" id="FileUtilNoOp.unlink"><a class="viewcode-back" href="../../reference.html#osxphotos.FileUtilNoOp.unlink">[docs]</a> <span class="nd">@classmethod</span>
@@ -351,72 +553,47 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">TemporaryDirectory</span><span class="p">(</span><span class="n">prefix</span><span class="o">=</span><span class="n">prefix</span><span class="p">,</span> <span class="nb">dir</span><span class="o">=</span><span class="nb">dir</span><span class="p">)</span></div></div>
</pre></div>
</div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../index.html">osxphotos</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">osxphotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">osxphotos command line interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">osxphotos package</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../index.html">Documentation overview</a><ul>
<li><a href="../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</footer>
</div>
<div class="footer">
&copy;2021, Rhet Turnbull.
<aside class="toc-drawer no-toc">
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</aside>
</div>
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photoexporter - osxphotos 0.50.1 documentation</title>
<title>osxphotos.photoexporter - osxphotos 0.51.7 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.50.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.51.7 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.51.7 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -332,6 +332,7 @@
<span class="sd"> use_photokit (bool, default=False): if True, will use photokit to export photos when use_photos_export is True</span>
<span class="sd"> verbose (callable): optional callable function to use for printing verbose text during processing; if None (default), does not print output.</span>
<span class="sd"> tmpdir: (str, default=None): Optional directory to use for temporary files, if None (default) uses system tmp directory</span>
<span class="sd"> favorite_rating (bool): if True, set XMP:Rating=5 for favorite images and XMP:Rating=0 for non-favorites</span>
<span class="sd"> """</span>
@@ -377,6 +378,7 @@
<span class="n">use_photos_export</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">verbose</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">Callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">tmpdir</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">favorite_rating</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
@@ -439,6 +441,36 @@
<div class="viewcode-block" id="ExportResults"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults">[docs]</a><span class="k">class</span> <span class="nc">ExportResults</span><span class="p">:</span>
<span class="sd">"""Results class which holds export results for export"""</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"_datetime"</span><span class="p">,</span>
<span class="s2">"converted_to_jpeg"</span><span class="p">,</span>
<span class="s2">"deleted_directories"</span><span class="p">,</span>
<span class="s2">"deleted_files"</span><span class="p">,</span>
<span class="s2">"error"</span><span class="p">,</span>
<span class="s2">"exif_updated"</span><span class="p">,</span>
<span class="s2">"exiftool_error"</span><span class="p">,</span>
<span class="s2">"exiftool_warning"</span><span class="p">,</span>
<span class="s2">"exported"</span><span class="p">,</span>
<span class="s2">"exported_album"</span><span class="p">,</span>
<span class="s2">"metadata_changed"</span><span class="p">,</span>
<span class="s2">"missing"</span><span class="p">,</span>
<span class="s2">"missing_album"</span><span class="p">,</span>
<span class="s2">"new"</span><span class="p">,</span>
<span class="s2">"sidecar_exiftool_skipped"</span><span class="p">,</span>
<span class="s2">"sidecar_exiftool_written"</span><span class="p">,</span>
<span class="s2">"sidecar_json_skipped"</span><span class="p">,</span>
<span class="s2">"sidecar_json_written"</span><span class="p">,</span>
<span class="s2">"sidecar_xmp_skipped"</span><span class="p">,</span>
<span class="s2">"sidecar_xmp_written"</span><span class="p">,</span>
<span class="s2">"skipped"</span><span class="p">,</span>
<span class="s2">"skipped_album"</span><span class="p">,</span>
<span class="s2">"to_touch"</span><span class="p">,</span>
<span class="s2">"touched"</span><span class="p">,</span>
<span class="s2">"updated"</span><span class="p">,</span>
<span class="s2">"xattr_skipped"</span><span class="p">,</span>
<span class="s2">"xattr_written"</span><span class="p">,</span>
<span class="p">]</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">converted_to_jpeg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
@@ -448,11 +480,11 @@
<span class="n">exif_updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exiftool_error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exiftool_warning</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exported_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exported</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exported_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">metadata_changed</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">missing_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">missing</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">missing_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">new</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_exiftool_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_exiftool_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
@@ -460,8 +492,8 @@
<span class="n">sidecar_json_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_xmp_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_xmp_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">skipped_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">skipped_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">to_touch</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">touched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
@@ -469,36 +501,22 @@
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="n">local_vars</span> <span class="o">=</span> <span class="nb">locals</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">:</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">local_vars</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">attr</span><span class="p">)</span> <span class="ow">or</span> <span class="p">[])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">converted_to_jpeg</span> <span class="o">=</span> <span class="n">converted_to_jpeg</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span> <span class="o">=</span> <span class="n">deleted_directories</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span> <span class="o">=</span> <span class="n">deleted_files</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">=</span> <span class="n">error</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exif_updated</span> <span class="o">=</span> <span class="n">exif_updated</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span> <span class="o">=</span> <span class="n">exiftool_error</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span> <span class="o">=</span> <span class="n">exiftool_warning</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span> <span class="o">=</span> <span class="n">exported</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exported_album</span> <span class="o">=</span> <span class="n">exported_album</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">metadata_changed</span> <span class="o">=</span> <span class="n">metadata_changed</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">missing</span> <span class="o">=</span> <span class="n">missing</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">missing_album</span> <span class="o">=</span> <span class="n">missing_album</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">new</span> <span class="o">=</span> <span class="n">new</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span> <span class="o">=</span> <span class="n">sidecar_exiftool_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span> <span class="o">=</span> <span class="n">sidecar_exiftool_written</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_skipped</span> <span class="o">=</span> <span class="n">sidecar_json_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_written</span> <span class="o">=</span> <span class="n">sidecar_json_written</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span> <span class="o">=</span> <span class="n">sidecar_xmp_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span> <span class="o">=</span> <span class="n">sidecar_xmp_written</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">skipped</span> <span class="o">=</span> <span class="n">skipped</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">skipped_album</span> <span class="o">=</span> <span class="n">skipped_album</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">to_touch</span> <span class="o">=</span> <span class="n">to_touch</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">touched</span> <span class="o">=</span> <span class="n">touched</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">updated</span> <span class="o">=</span> <span class="n">updated</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_skipped</span> <span class="o">=</span> <span class="n">xattr_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_written</span> <span class="o">=</span> <span class="n">xattr_written</span> <span class="ow">or</span> <span class="p">[]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">attributes</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">"""Return list of attributes tracked by ExportResults"""</span>
<span class="k">return</span> <span class="p">[</span><span class="n">attr</span> <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__slots__</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">attr</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"_"</span><span class="p">)]</span>
<div class="viewcode-block" id="ExportResults.all_files"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults.all_files">[docs]</a> <span class="k">def</span> <span class="nf">all_files</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">datetime</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">"""Return datetime when ExportResults was created"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span>
<div class="viewcode-block" id="ExportResults.all_files"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults.all_files">[docs]</a> <span class="k">def</span> <span class="nf">all_files</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">"""return all filenames contained in results"""</span>
<span class="n">files</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span>
@@ -520,65 +538,23 @@
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span><span class="p">]</span>
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="p">]</span>
<span class="n">files</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">files</span><span class="p">))</span>
<span class="k">return</span> <span class="n">files</span></div>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">files</span><span class="p">))</span></div>
<span class="k">def</span> <span class="fm">__iadd__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exported</span>
<span class="bp">self</span><span class="o">.</span><span class="n">new</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">new</span>
<span class="bp">self</span><span class="o">.</span><span class="n">updated</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">updated</span>
<span class="bp">self</span><span class="o">.</span><span class="n">skipped</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">skipped</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exif_updated</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exif_updated</span>
<span class="bp">self</span><span class="o">.</span><span class="n">touched</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">touched</span>
<span class="bp">self</span><span class="o">.</span><span class="n">to_touch</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">to_touch</span>
<span class="bp">self</span><span class="o">.</span><span class="n">converted_to_jpeg</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">converted_to_jpeg</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_written</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">sidecar_json_written</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_skipped</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">sidecar_json_skipped</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">sidecar_xmp_written</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span>
<span class="bp">self</span><span class="o">.</span><span class="n">missing</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">missing</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">error</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exiftool_warning</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exiftool_error</span>
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">deleted_files</span>
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">deleted_directories</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exported_album</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exported_album</span>
<span class="bp">self</span><span class="o">.</span><span class="n">skipped_album</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">skipped_album</span>
<span class="bp">self</span><span class="o">.</span><span class="n">missing_album</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">missing_album</span>
<span class="bp">self</span><span class="o">.</span><span class="n">metadata_changed</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">metadata_changed</span>
<span class="k">def</span> <span class="fm">__iadd__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">"ExportResults"</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">other</span><span class="p">)</span> <span class="o">!=</span> <span class="n">ExportResults</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"Can only add ExportResults to ExportResults"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">attribute</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">:</span>
<span class="nb">setattr</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span> <span class="n">attribute</span><span class="p">,</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attribute</span><span class="p">)</span> <span class="o">+</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">attribute</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="k">return</span> <span class="p">(</span>
<span class="s2">"ExportResults("</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">"datetime=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">datetime</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exported=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exported</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",new=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">new</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",updated=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">updated</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",skipped=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">skipped</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exif_updated=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exif_updated</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",touched=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">touched</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",to_touch=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">to_touch</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",converted_to_jpeg=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">converted_to_jpeg</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",sidecar_json_written=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_written</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",sidecar_json_skipped=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_skipped</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",sidecar_exiftool_written=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",sidecar_exiftool_skipped=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",sidecar_xmp_written=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",sidecar_xmp_skipped=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",missing=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">missing</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",error=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exiftool_warning=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exiftool_error=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",deleted_files=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",deleted_directories=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exported_album=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exported_album</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",skipped_album=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">skipped_album</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",missing_album=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">missing_album</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">",metadata_changed=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">metadata_changed</span><span class="si">}</span><span class="s2">"</span>
<span class="o">+</span> <span class="sa">f</span><span class="s2">"datetime=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span><span class="si">}</span><span class="s2">, "</span>
<span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">attr</span><span class="si">}</span><span class="s2">=</span><span class="si">{</span><span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span> <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">])</span>
<span class="o">+</span> <span class="s2">")"</span>
<span class="p">)</span></div>
@@ -1145,7 +1121,7 @@
<span class="c1"># export live_photo .mov file?</span>
<span class="n">live_photo</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">)</span>
<span class="n">overwrite</span> <span class="o">=</span> <span class="nb">any</span><span class="p">([</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">])</span>
<span class="n">edited_version</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">shared</span>
<span class="n">edited_version</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">shared</span><span class="p">)</span>
<span class="c1"># shared photos (in shared albums) show up as not having adjustments (not edited)</span>
<span class="c1"># but Photos is unable to export the "original" as only a jpeg copy is shared in iCloud</span>
<span class="c1"># so tell Photos to export the current version in this case</span>
@@ -1782,6 +1758,7 @@
<span class="sd"> QuickTime:ModifyDate (UTC)</span>
<span class="sd"> QuickTime:GPSCoordinates</span>
<span class="sd"> UserData:GPSCoordinates</span>
<span class="sd"> XMP:Rating</span>
<span class="sd"> Reference:</span>
<span class="sd"> https://iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201610_1.pdf</span>
@@ -1897,8 +1874,8 @@
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">face_regions</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">face_info</span><span class="p">:</span>
<span class="n">exif</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_mwg_face_regions_exiftool</span><span class="p">())</span>
<span class="c1"># if self.favorite():</span>
<span class="c1"># exif["Rating"] = 5</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">favorite_rating</span><span class="p">:</span>
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Rating"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">favorite</span> <span class="k">else</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="p">(</span><span class="n">lat</span><span class="p">,</span> <span class="n">lon</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">location</span>
@@ -2205,6 +2182,11 @@
<span class="n">latlon</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">location</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">location</span> <span class="k">else</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">favorite_rating</span><span class="p">:</span>
<span class="n">rating</span> <span class="o">=</span> <span class="mi">5</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">favorite</span> <span class="k">else</span> <span class="mi">0</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">rating</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">xmp_str</span> <span class="o">=</span> <span class="n">xmp_template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span>
<span class="n">photo</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="n">description</span><span class="p">,</span>
@@ -2214,6 +2196,7 @@
<span class="n">extension</span><span class="o">=</span><span class="n">extension</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="n">latlon</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="n">__version__</span><span class="p">,</span>
<span class="n">rating</span><span class="o">=</span><span class="n">rating</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># remove extra lines that mako inserts from template</span>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,385 @@
<!doctype html>
<html class="no-js">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosalbum - osxphotos 0.51.7 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.51.7 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.51.7 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.photosalbum</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosAlbum class to create an album in default Photos library and add photos to it """</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">more_itertools</span> <span class="kn">import</span> <span class="n">chunked</span>
<span class="kn">from</span> <span class="nn">photoscript</span> <span class="kn">import</span> <span class="n">Album</span><span class="p">,</span> <span class="n">Folder</span><span class="p">,</span> <span class="n">Photo</span><span class="p">,</span> <span class="n">PhotosLibrary</span>
<span class="kn">from</span> <span class="nn">.photoinfo</span> <span class="kn">import</span> <span class="n">PhotoInfo</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">noop</span><span class="p">,</span> <span class="n">pluralize</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"PhotosAlbum"</span><span class="p">,</span> <span class="s2">"PhotosAlbumPhotoScript"</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">folder_by_path</span><span class="p">(</span><span class="n">folders</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Folder</span><span class="p">:</span>
<span class="sd">"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""</span>
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="n">top_folder_name</span> <span class="o">=</span> <span class="n">folders</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">top_folder</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">top_folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">)</span>
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">top_folder</span>
<span class="k">for</span> <span class="n">folder_name</span> <span class="ow">in</span> <span class="n">folders</span><span class="p">:</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">folder</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">folder</span>
<span class="k">return</span> <span class="n">current_folder</span>
<span class="k">def</span> <span class="nf">album_by_path</span><span class="p">(</span>
<span class="n">folders_album</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Album</span><span class="p">:</span>
<span class="sd">"""Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)"""</span>
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">folders_album</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
<span class="c1"># have folders</span>
<span class="n">album_name</span> <span class="o">=</span> <span class="n">folders_album</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">folder_by_path</span><span class="p">(</span><span class="n">folders_album</span><span class="p">,</span> <span class="n">verbose</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># only have album name</span>
<span class="n">album_name</span> <span class="o">=</span> <span class="n">folders_album</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">return</span> <span class="n">album</span>
<div class="viewcode-block" id="PhotosAlbum"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbum">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbum</span><span class="p">:</span>
<span class="sd">"""Add osxphotos.photoinfo.PhotoInfo objects to album"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
<span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">split_folder</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">rich</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">"""Return a PhotosAlbum object, creating the album if necessary</span>
<span class="sd"> Args:</span>
<span class="sd"> name: Name of album</span>
<span class="sd"> verbose: optional callable to print verbose output</span>
<span class="sd"> split_folder: if set, split album name on value of split_folder to create folders if necessary,</span>
<span class="sd"> e.g. if name = 'folder1/folder2/album' and split_folder='/',</span>
<span class="sd"> then folders 'folder1' and 'folder2' will be created and album 'album' will be created in 'folder2';</span>
<span class="sd"> if not set, album 'folder1/folder2/album' will be created</span>
<span class="sd"> rich: if True, use rich themes for verbose output</span>
<span class="sd"> """</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="bp">self</span><span class="o">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">folders_album</span> <span class="o">=</span> <span class="n">name</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">split_folder</span><span class="p">)</span> <span class="k">if</span> <span class="n">split_folder</span> <span class="k">else</span> <span class="p">[</span><span class="n">name</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span> <span class="o">=</span> <span class="n">album_by_path</span><span class="p">(</span><span class="n">folders_album</span><span class="p">,</span> <span class="n">verbose</span><span class="o">=</span><span class="n">verbose</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="o">=</span> <span class="n">rich</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="n">PhotoInfo</span><span class="p">):</span>
<span class="n">photo_</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo_</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PhotoInfo</span><span class="p">]):</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photo_list</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">photos</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</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>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Error creating Photo object for photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">photolist</span> <span class="ow">in</span> <span class="n">chunked</span><span class="p">(</span><span class="n">photos</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">photos</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_format_uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format uuid for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[uuid]</span><span class="si">{</span><span class="n">uuid</span><span class="si">}</span><span class="s2">[/uuid]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">uuid</span>
<span class="k">def</span> <span class="nf">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">album</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format album name for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[filepath]</span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">[/filepath]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">album</span>
<span class="k">def</span> <span class="nf">_format_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format name for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[filename]</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">[/filename]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">name</span>
<span class="k">def</span> <span class="nf">_format_num</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">num</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format number for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[num]</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">[/num]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">num</span><span class="p">)</span></div>
<div class="viewcode-block" id="PhotosAlbumPhotoScript"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbumPhotoScript">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbumPhotoScript</span><span class="p">(</span><span class="n">PhotosAlbum</span><span class="p">):</span>
<span class="sd">"""Add photoscript.Photo objects to album"""</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="n">Photo</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Photo</span><span class="p">]):</span>
<span class="k">for</span> <span class="n">photolist</span> <span class="ow">in</span> <span class="n">chunked</span><span class="p">(</span><span class="n">photo_list</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>

View File

@@ -1,52 +1,216 @@
<!doctype html>
<html class="no-js">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<!DOCTYPE html>
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.50.13 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.photosdb._photosdb_process_comments &#8212; osxphotos 0.47.9 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>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="stylesheet" href="../../../_static/custom.css" type="text/css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">OSXPhotos python API</a></li>
</ul>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
</div>
</div>
<div class="body" role="main">
<h1>Source code for osxphotos.photosdb._photosdb_process_comments</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot; PhotosDB method for processing comments and likes on shared photos.</span>
<span class="sd"> Do not import this module directly &quot;&quot;&quot;</span>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.photosdb._photosdb_process_comments</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosDB method for processing comments and likes on shared photos.</span>
<span class="sd"> Do not import this module directly """</span>
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">datetime</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="nn">.._constants</span> <span class="kn">import</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">,</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">,</span> <span class="n">TIME_DELTA</span>
<span class="kn">from</span> <span class="nn">..utils</span> <span class="kn">import</span> <span class="n">_open_sql_file</span><span class="p">,</span> <span class="n">normalize_unicode</span>
<span class="kn">from</span> <span class="nn">..utils</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
<span class="kn">from</span> <span class="nn">..sqlite_utils</span> <span class="kn">import</span> <span class="n">sqlite_open_ro</span>
<span class="k">def</span> <span class="nf">_process_comments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;load the comments and likes data from the database</span>
<span class="sd">"""load the comments and likes data from the database</span>
<span class="sd"> this is a PhotosDB method that should be imported in</span>
<span class="sd"> the PhotosDB class definition in photosdb.py</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="sd"> """</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_hashed_person_id</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_comments_uuid</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
@@ -57,7 +221,7 @@
<div class="viewcode-block" id="CommentInfo"><a class="viewcode-back" href="../../../reference.html#osxphotos.CommentInfo">[docs]</a><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">CommentInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Class for shared photo comments&quot;&quot;&quot;</span>
<span class="sd">"""Class for shared photo comments"""</span>
<span class="n">datetime</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span>
<span class="n">user</span><span class="p">:</span> <span class="nb">str</span>
@@ -70,7 +234,7 @@
<div class="viewcode-block" id="LikeInfo"><a class="viewcode-back" href="../../../reference.html#osxphotos.LikeInfo">[docs]</a><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">LikeInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Class for shared photo likes&quot;&quot;&quot;</span>
<span class="sd">"""Class for shared photo likes"""</span>
<span class="n">datetime</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span>
<span class="n">user</span><span class="p">:</span> <span class="nb">str</span>
@@ -83,25 +247,25 @@
<span class="c1"># The following methods do not get imported into PhotosDB</span>
<span class="c1"># but will get called by _process_comments</span>
<span class="k">def</span> <span class="nf">_process_comments_4</span><span class="p">(</span><span class="n">photosdb</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;process comments and likes info for Photos &lt;= 4</span>
<span class="sd"> photosdb: PhotosDB instance&quot;&quot;&quot;</span>
<span class="sd">"""process comments and likes info for Photos &lt;= 4</span>
<span class="sd"> photosdb: PhotosDB instance"""</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Not implemented for database version </span><span class="si">{</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_db_version</span><span class="si">}</span><span class="s2">.&quot;</span>
<span class="sa">f</span><span class="s2">"Not implemented for database version </span><span class="si">{</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_db_version</span><span class="si">}</span><span class="s2">."</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_comments_5</span><span class="p">(</span><span class="n">photosdb</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;process comments and likes info for Photos &gt;= 5</span>
<span class="sd"> photosdb: PhotosDB instance&quot;&quot;&quot;</span>
<span class="sd">"""process comments and likes info for Photos &gt;= 5</span>
<span class="sd"> photosdb: PhotosDB instance"""</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_tmp_db</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">&quot;ASSET&quot;</span><span class="p">]</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">"ASSET"</span><span class="p">]</span>
<span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">=</span> <span class="n">_open_sql_file</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
<span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">=</span> <span class="n">sqlite_open_ro</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd">"""</span>
<span class="sd"> SELECT DISTINCT</span>
<span class="sd"> ZINVITEEHASHEDPERSONID AS HASHEDPERSONID,</span>
<span class="sd"> ZINVITEEFIRSTNAME AS FIRSTNAME,</span>
@@ -109,7 +273,7 @@
<span class="sd"> ZINVITEEFULLNAME AS FULLNAME</span>
<span class="sd"> FROM ZCLOUDSHAREDALBUMINVITATIONRECORD</span>
<span class="sd"> WHERE HASHEDPERSONID IS NOT NULL</span>
<span class="sd"> AND HASHEDPERSONID != &quot;&quot;</span>
<span class="sd"> AND HASHEDPERSONID != ""</span>
<span class="sd"> AND NOT (FIRSTNAME IS NULL AND LASTNAME IS NULL)</span>
<span class="sd"> UNION</span>
<span class="sd"> SELECT DISTINCT</span>
@@ -119,9 +283,9 @@
<span class="sd"> ZCLOUDOWNERFULLNAME AS FULLNAME</span>
<span class="sd"> FROM ZGENERICALBUM</span>
<span class="sd"> WHERE HASHEDPERSONID IS NOT NULL</span>
<span class="sd"> AND HASHEDPERSONID != &quot;&quot;</span>
<span class="sd"> AND HASHEDPERSONID != ""</span>
<span class="sd"> AND NOT (FIRSTNAME IS NULL AND LASTNAME IS NULL)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="sd"> """</span>
<span class="p">)</span>
<span class="c1"># order of results</span>
@@ -134,35 +298,35 @@
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">results</span><span class="o">.</span><span class="n">fetchall</span><span class="p">():</span>
<span class="n">person_id</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">person_id</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;first_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span>
<span class="s2">&quot;last_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span>
<span class="s2">&quot;full_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span>
<span class="s2">"first_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span>
<span class="s2">"last_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span>
<span class="s2">"full_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span>
<span class="p">}</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="sa">f</span><span class="s2">"""</span>
<span class="s2"> SELECT </span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID, -- UUID of the photo</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a &quot;like&quot;</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a "like"</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTDATE, -- date of comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTTEXT, -- text of comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTERHASHEDPERSONID, -- hashed ID of person who made comment/like</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user&#39;s) comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user's) comment</span>
<span class="s2"> FROM ZCLOUDSHAREDCOMMENT</span>
<span class="s2"> JOIN </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"> ON</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK = ZCLOUDSHAREDCOMMENT.ZCOMMENTEDASSET</span>
<span class="s2"> OR</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK = ZCLOUDSHAREDCOMMENT.ZLIKEDASSET</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="s2"> """</span>
<span class="p">)</span>
<span class="c1"># order of results</span>
<span class="c1"># 0: ZGENERICASSET.ZUUID, -- UUID of the photo</span>
<span class="c1"># 1: ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a &quot;like&quot;</span>
<span class="c1"># 1: ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a "like"</span>
<span class="c1"># 2: ZCLOUDSHAREDCOMMENT.ZCOMMENTDATE, -- date of comment</span>
<span class="c1"># 3: ZCLOUDSHAREDCOMMENT.ZCOMMENTTEXT, -- text of comment</span>
<span class="c1"># 4: ZCLOUDSHAREDCOMMENT.ZCOMMENTERHASHEDPERSONID, -- hashed ID of person who made comment/like</span>
<span class="c1"># 5: ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user&#39;s) comment</span>
<span class="c1"># 5: ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user's) comment</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
@@ -170,7 +334,7 @@
<span class="n">is_like</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]][</span><span class="s2">&quot;full_name&quot;</span><span class="p">]</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]][</span><span class="s2">"full_name"</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="kc">None</span>
@@ -184,89 +348,62 @@
<span class="k">try</span><span class="p">:</span>
<span class="n">db_comments</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;likes&quot;</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">&quot;comments&quot;</span><span class="p">:</span> <span class="p">[]}</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"likes"</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">"comments"</span><span class="p">:</span> <span class="p">[]}</span>
<span class="n">db_comments</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
<span class="k">if</span> <span class="n">is_like</span><span class="p">:</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">LikeInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">))</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">"likes"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">LikeInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">text</span><span class="p">:</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">CommentInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">"comments"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">CommentInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span>
<span class="c1"># sort results</span>
<span class="k">for</span> <span class="n">uuid</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;comments&quot;</span><span class="p">]:</span>
<span class="n">value</span><span class="p">[</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"likes"</span><span class="p">]:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"likes"</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"comments"</span><span class="p">]:</span>
<span class="n">value</span><span class="p">[</span><span class="s2">"comments"</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
</div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../../index.html">osxphotos</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../../overview.html">osxphotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../cli.html">osxphotos command line interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">osxphotos package</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../../index.html">Documentation overview</a><ul>
<li><a href="../../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</footer>
</div>
<div class="footer">
&copy;2021, Rhet Turnbull.
<aside class="toc-drawer no-toc">
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</aside>
</div>
</div><script data-url_root="../../../" id="documentation_options" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/scripts/furo.js"></script>
<script src="../../../_static/clipboard.min.js"></script>
<script src="../../../_static/copybutton.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +1,237 @@
<!doctype html>
<html class="no-js">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<!DOCTYPE html>
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.placeinfo - osxphotos 0.51.7 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.placeinfo &#8212; osxphotos 0.47.9 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>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.51.7 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<span class="sidebar-brand-text">osxphotos 0.51.7 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
</ul>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
</div>
</div>
<div class="body" role="main">
<h1>Source code for osxphotos.placeinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot; </span>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.placeinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">""" </span>
<span class="sd"> PlaceInfo class</span>
<span class="sd"> Provides reverse geolocation info for photos </span>
<span class="sd"> </span>
<span class="sd"> See https://developer.apple.com/documentation/corelocation/clplacemark</span>
<span class="sd"> for additional documentation on reverse geolocation data</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd">"""</span>
<span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span> <span class="c1"># pylint: disable=syntax-error</span>
<span class="kn">import</span> <span class="nn">yaml</span>
<span class="kn">from</span> <span class="nn">bpylist</span> <span class="kn">import</span> <span class="n">archiver</span>
<span class="kn">from</span> <span class="nn">bpylist2</span> <span class="kn">import</span> <span class="n">archiver</span>
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="n">UNICODE_FORMAT</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;PLRevGeoLocationInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoMapItem&quot;</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoMapItemAdditionalPlaceInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;CNPostalAddress&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo4&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo5&quot;</span><span class="p">,</span>
<span class="s2">"PLRevGeoLocationInfo"</span><span class="p">,</span>
<span class="s2">"PLRevGeoMapItem"</span><span class="p">,</span>
<span class="s2">"PLRevGeoMapItemAdditionalPlaceInfo"</span><span class="p">,</span>
<span class="s2">"CNPostalAddress"</span><span class="p">,</span>
<span class="s2">"PlaceInfo"</span><span class="p">,</span>
<span class="s2">"PlaceInfo4"</span><span class="p">,</span>
<span class="s2">"PlaceInfo5"</span><span class="p">,</span>
<span class="p">]</span>
<span class="c1"># postal address information, returned by PlaceInfo.address</span>
<span class="n">PostalAddress</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span>
<span class="s2">&quot;PostalAddress&quot;</span><span class="p">,</span>
<span class="s2">"PostalAddress"</span><span class="p">,</span>
<span class="p">[</span>
<span class="s2">&quot;street&quot;</span><span class="p">,</span>
<span class="s2">&quot;sub_locality&quot;</span><span class="p">,</span>
<span class="s2">&quot;city&quot;</span><span class="p">,</span>
<span class="s2">&quot;sub_administrative_area&quot;</span><span class="p">,</span>
<span class="s2">&quot;state_province&quot;</span><span class="p">,</span>
<span class="s2">&quot;postal_code&quot;</span><span class="p">,</span>
<span class="s2">&quot;country&quot;</span><span class="p">,</span>
<span class="s2">&quot;iso_country_code&quot;</span><span class="p">,</span>
<span class="s2">"street"</span><span class="p">,</span>
<span class="s2">"sub_locality"</span><span class="p">,</span>
<span class="s2">"city"</span><span class="p">,</span>
<span class="s2">"sub_administrative_area"</span><span class="p">,</span>
<span class="s2">"state_province"</span><span class="p">,</span>
<span class="s2">"postal_code"</span><span class="p">,</span>
<span class="s2">"country"</span><span class="p">,</span>
<span class="s2">"iso_country_code"</span><span class="p">,</span>
<span class="p">],</span>
<span class="p">)</span>
@@ -77,30 +240,30 @@
<span class="c1"># PLRevGeoLocationInfo.mapInfo.sortedPlaceInfos</span>
<span class="c1"># field 18 is combined bodies of water (ocean + inland_water)</span>
<span class="c1"># and maps to Photos &lt;= 4, RKPlace.type == 44</span>
<span class="c1"># (Photos &lt;= 4 doesn&#39;t have ocean or inland_water types)</span>
<span class="c1"># The fields named &quot;field0&quot;, etc. appear to be unused</span>
<span class="c1"># (Photos &lt;= 4 doesn't have ocean or inland_water types)</span>
<span class="c1"># The fields named "field0", etc. appear to be unused</span>
<span class="n">PlaceNames</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span>
<span class="s2">&quot;PlaceNames&quot;</span><span class="p">,</span>
<span class="s2">"PlaceNames"</span><span class="p">,</span>
<span class="p">[</span>
<span class="s2">&quot;field0&quot;</span><span class="p">,</span>
<span class="s2">&quot;country&quot;</span><span class="p">,</span> <span class="c1"># The name of the country associated with the placemark.</span>
<span class="s2">&quot;state_province&quot;</span><span class="p">,</span> <span class="c1"># administrativeArea, The state or province associated with the placemark.</span>
<span class="s2">&quot;sub_administrative_area&quot;</span><span class="p">,</span> <span class="c1"># Additional administrative area information for the placemark.</span>
<span class="s2">&quot;city&quot;</span><span class="p">,</span> <span class="c1"># locality, The city associated with the placemark.</span>
<span class="s2">&quot;field5&quot;</span><span class="p">,</span>
<span class="s2">&quot;additional_city_info&quot;</span><span class="p">,</span> <span class="c1"># subLocality, Additional city-level information for the placemark.</span>
<span class="s2">&quot;ocean&quot;</span><span class="p">,</span> <span class="c1"># The name of the ocean associated with the placemark.</span>
<span class="s2">&quot;area_of_interest&quot;</span><span class="p">,</span> <span class="c1"># areasOfInterest, The relevant areas of interest associated with the placemark.</span>
<span class="s2">&quot;inland_water&quot;</span><span class="p">,</span> <span class="c1"># The name of the inland water body associated with the placemark.</span>
<span class="s2">&quot;field10&quot;</span><span class="p">,</span>
<span class="s2">&quot;region&quot;</span><span class="p">,</span> <span class="c1"># The geographic region associated with the placemark.</span>
<span class="s2">&quot;sub_throughfare&quot;</span><span class="p">,</span> <span class="c1"># Additional street-level information for the placemark.</span>
<span class="s2">&quot;field13&quot;</span><span class="p">,</span>
<span class="s2">&quot;postal_code&quot;</span><span class="p">,</span> <span class="c1"># The postal code associated with the placemark.</span>
<span class="s2">&quot;field15&quot;</span><span class="p">,</span>
<span class="s2">&quot;field16&quot;</span><span class="p">,</span>
<span class="s2">&quot;street_address&quot;</span><span class="p">,</span> <span class="c1"># throughfare, The street address associated with the placemark.</span>
<span class="s2">&quot;body_of_water&quot;</span><span class="p">,</span> <span class="c1"># RKPlace.type == 44, appears to be any body of water (ocean or inland)</span>
<span class="s2">"field0"</span><span class="p">,</span>
<span class="s2">"country"</span><span class="p">,</span> <span class="c1"># The name of the country associated with the placemark.</span>
<span class="s2">"state_province"</span><span class="p">,</span> <span class="c1"># administrativeArea, The state or province associated with the placemark.</span>
<span class="s2">"sub_administrative_area"</span><span class="p">,</span> <span class="c1"># Additional administrative area information for the placemark.</span>
<span class="s2">"city"</span><span class="p">,</span> <span class="c1"># locality, The city associated with the placemark.</span>
<span class="s2">"field5"</span><span class="p">,</span>
<span class="s2">"additional_city_info"</span><span class="p">,</span> <span class="c1"># subLocality, Additional city-level information for the placemark.</span>
<span class="s2">"ocean"</span><span class="p">,</span> <span class="c1"># The name of the ocean associated with the placemark.</span>
<span class="s2">"area_of_interest"</span><span class="p">,</span> <span class="c1"># areasOfInterest, The relevant areas of interest associated with the placemark.</span>
<span class="s2">"inland_water"</span><span class="p">,</span> <span class="c1"># The name of the inland water body associated with the placemark.</span>
<span class="s2">"field10"</span><span class="p">,</span>
<span class="s2">"region"</span><span class="p">,</span> <span class="c1"># The geographic region associated with the placemark.</span>
<span class="s2">"sub_throughfare"</span><span class="p">,</span> <span class="c1"># Additional street-level information for the placemark.</span>
<span class="s2">"field13"</span><span class="p">,</span>
<span class="s2">"postal_code"</span><span class="p">,</span> <span class="c1"># The postal code associated with the placemark.</span>
<span class="s2">"field15"</span><span class="p">,</span>
<span class="s2">"field16"</span><span class="p">,</span>
<span class="s2">"street_address"</span><span class="p">,</span> <span class="c1"># throughfare, The street address associated with the placemark.</span>
<span class="s2">"body_of_water"</span><span class="p">,</span> <span class="c1"># RKPlace.type == 44, appears to be any body of water (ocean or inland)</span>
<span class="p">],</span>
<span class="p">)</span>
@@ -108,7 +271,7 @@
<span class="c1"># in ZADDITIONALASSETATTRIBUTES.ZREVERSELOCATIONDATA</span>
<span class="c1"># These classes are used by bpylist.archiver to unarchive the serialized objects</span>
<span class="k">class</span> <span class="nc">PLRevGeoLocationInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;The top level reverse geolocation object&quot;&quot;&quot;</span>
<span class="sd">"""The top level reverse geolocation object"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -136,14 +299,14 @@
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">&quot;addressString&quot;</span><span class="p">,</span>
<span class="s2">&quot;countryCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;isHome&quot;</span><span class="p">,</span>
<span class="s2">&quot;compoundNames&quot;</span><span class="p">,</span>
<span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">,</span>
<span class="s2">&quot;version&quot;</span><span class="p">,</span>
<span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">,</span>
<span class="s2">&quot;postalAddress&quot;</span><span class="p">,</span>
<span class="s2">"addressString"</span><span class="p">,</span>
<span class="s2">"countryCode"</span><span class="p">,</span>
<span class="s2">"isHome"</span><span class="p">,</span>
<span class="s2">"compoundNames"</span><span class="p">,</span>
<span class="s2">"compoundSecondaryNames"</span><span class="p">,</span>
<span class="s2">"version"</span><span class="p">,</span>
<span class="s2">"geoServiceProvider"</span><span class="p">,</span>
<span class="s2">"postalAddress"</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
@@ -151,31 +314,31 @@
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;addressString: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">addressString</span><span class="si">}</span><span class="s2">, countryCode: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">countryCode</span><span class="si">}</span><span class="s2">, isHome: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">isHome</span><span class="si">}</span><span class="s2">, mapItem: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">mapItem</span><span class="si">}</span><span class="s2">, postalAddress: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">postalAddress</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"addressString: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">addressString</span><span class="si">}</span><span class="s2">, countryCode: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">countryCode</span><span class="si">}</span><span class="s2">, isHome: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">isHome</span><span class="si">}</span><span class="s2">, mapItem: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">mapItem</span><span class="si">}</span><span class="s2">, postalAddress: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">postalAddress</span><span class="si">}</span><span class="s2">"</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;addressString&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">addressString</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;countryCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">countryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;mapItem&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">mapItem</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;isHome&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">isHome</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;compoundNames&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundSecondaryNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;version&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">version</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">geoServiceProvider</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;postalAddress&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">postalAddress</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"addressString"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">addressString</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"countryCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">countryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"mapItem"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">mapItem</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"isHome"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">isHome</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"compoundNames"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"compoundSecondaryNames"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundSecondaryNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"version"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">version</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"geoServiceProvider"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">geoServiceProvider</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"postalAddress"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">postalAddress</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">addressString</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;addressString&quot;</span><span class="p">)</span>
<span class="n">countryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;countryCode&quot;</span><span class="p">)</span>
<span class="n">mapItem</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;mapItem&quot;</span><span class="p">)</span>
<span class="n">isHome</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;isHome&quot;</span><span class="p">)</span>
<span class="n">compoundNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;compoundNames&quot;</span><span class="p">)</span>
<span class="n">compoundSecondaryNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">)</span>
<span class="n">version</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;version&quot;</span><span class="p">)</span>
<span class="n">geoServiceProvider</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">)</span>
<span class="n">postalAddress</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;postalAddress&quot;</span><span class="p">)</span>
<span class="n">addressString</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"addressString"</span><span class="p">)</span>
<span class="n">countryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"countryCode"</span><span class="p">)</span>
<span class="n">mapItem</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"mapItem"</span><span class="p">)</span>
<span class="n">isHome</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"isHome"</span><span class="p">)</span>
<span class="n">compoundNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"compoundNames"</span><span class="p">)</span>
<span class="n">compoundSecondaryNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"compoundSecondaryNames"</span><span class="p">)</span>
<span class="n">version</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"version"</span><span class="p">)</span>
<span class="n">geoServiceProvider</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"geoServiceProvider"</span><span class="p">)</span>
<span class="n">postalAddress</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"postalAddress"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">(</span>
<span class="n">addressString</span><span class="p">,</span>
<span class="n">countryCode</span><span class="p">,</span>
@@ -190,7 +353,7 @@
<span class="k">class</span> <span class="nc">PLRevGeoMapItem</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Stores the list of place names, organized by area&quot;&quot;&quot;</span>
<span class="sd">"""Stores the list of place names, organized by area"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sortedPlaceInfos</span><span class="p">,</span> <span class="n">finalPlaceInfos</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">sortedPlaceInfos</span>
@@ -199,7 +362,7 @@
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">,</span> <span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">]</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"sortedPlaceInfos"</span><span class="p">,</span> <span class="s2">"finalPlaceInfos"</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
@@ -209,23 +372,23 @@
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">place</span><span class="p">)</span> <span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">]</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">place</span><span class="p">)</span> <span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">]</span>
<span class="k">return</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;finalPlaceInfos: </span><span class="si">{</span><span class="n">finalPlaceInfos</span><span class="si">}</span><span class="s2">, sortedPlaceInfos: </span><span class="si">{</span><span class="n">sortedPlaceInfos</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="sa">f</span><span class="s2">"finalPlaceInfos: </span><span class="si">{</span><span class="n">finalPlaceInfos</span><span class="si">}</span><span class="s2">, sortedPlaceInfos: </span><span class="si">{</span><span class="n">sortedPlaceInfos</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"sortedPlaceInfos"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"finalPlaceInfos"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">)</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">)</span>
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"sortedPlaceInfos"</span><span class="p">)</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"finalPlaceInfos"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoMapItem</span><span class="p">(</span><span class="n">sortedPlaceInfos</span><span class="p">,</span> <span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Additional info about individual places&quot;&quot;&quot;</span>
<span class="sd">"""Additional info about individual places"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">area</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">placeType</span><span class="p">,</span> <span class="n">dominantOrderType</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">area</span> <span class="o">=</span> <span class="n">area</span>
@@ -236,35 +399,35 @@
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;area&quot;</span><span class="p">,</span> <span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;placeType&quot;</span><span class="p">,</span> <span class="s2">&quot;dominantOrderType&quot;</span><span class="p">]</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"area"</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">,</span> <span class="s2">"placeType"</span><span class="p">,</span> <span class="s2">"dominantOrderType"</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;area: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">area</span><span class="si">}</span><span class="s2">, name: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">, placeType: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">placeType</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"area: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">area</span><span class="si">}</span><span class="s2">, name: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">, placeType: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">placeType</span><span class="si">}</span><span class="s2">"</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;area&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">area</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;placeType&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">placeType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;dominantOrderType&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">dominantOrderType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"area"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">area</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"name"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"placeType"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">placeType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"dominantOrderType"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">dominantOrderType</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">area</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;area&quot;</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">)</span>
<span class="n">placeType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;placeType&quot;</span><span class="p">)</span>
<span class="n">dominantOrderType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;dominantOrderType&quot;</span><span class="p">)</span>
<span class="n">area</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"area"</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"name"</span><span class="p">)</span>
<span class="n">placeType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"placeType"</span><span class="p">)</span>
<span class="n">dominantOrderType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"dominantOrderType"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">(</span>
<span class="n">area</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">placeType</span><span class="p">,</span> <span class="n">dominantOrderType</span>
<span class="p">)</span>
<span class="k">class</span> <span class="nc">CNPostalAddress</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;postal address for the reverse geolocation info&quot;&quot;&quot;</span>
<span class="sd">"""postal address for the reverse geolocation info"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -290,14 +453,14 @@
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;_city&quot;</span><span class="p">,</span>
<span class="s2">&quot;_country&quot;</span><span class="p">,</span>
<span class="s2">&quot;_postalCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;_state&quot;</span><span class="p">,</span>
<span class="s2">&quot;_street&quot;</span><span class="p">,</span>
<span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">,</span>
<span class="s2">&quot;_subLocality&quot;</span><span class="p">,</span>
<span class="s2">"_ISOCountryCode"</span><span class="p">,</span>
<span class="s2">"_city"</span><span class="p">,</span>
<span class="s2">"_country"</span><span class="p">,</span>
<span class="s2">"_postalCode"</span><span class="p">,</span>
<span class="s2">"_state"</span><span class="p">,</span>
<span class="s2">"_street"</span><span class="p">,</span>
<span class="s2">"_subAdministrativeArea"</span><span class="p">,</span>
<span class="s2">"_subLocality"</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
@@ -305,7 +468,7 @@
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="k">return</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="nb">map</span><span class="p">(</span>
<span class="nb">str</span><span class="p">,</span>
<span class="p">[</span>
@@ -323,25 +486,25 @@
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_ISOCountryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_country&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_country</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_city&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_city</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_postalCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_postalCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_state&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_state</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_street&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_street</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subAdministrativeArea</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_subLocality&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subLocality</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_ISOCountryCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_ISOCountryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_country"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_country</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_city"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_city</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_postalCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_postalCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_state"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_state</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_street"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_street</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_subAdministrativeArea"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subAdministrativeArea</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_subLocality"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subLocality</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">_ISOCountryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">)</span>
<span class="n">_country</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_country&quot;</span><span class="p">)</span>
<span class="n">_city</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_city&quot;</span><span class="p">)</span>
<span class="n">_postalCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_postalCode&quot;</span><span class="p">)</span>
<span class="n">_state</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_state&quot;</span><span class="p">)</span>
<span class="n">_street</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_street&quot;</span><span class="p">)</span>
<span class="n">_subAdministrativeArea</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">)</span>
<span class="n">_subLocality</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_subLocality&quot;</span><span class="p">)</span>
<span class="n">_ISOCountryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_ISOCountryCode"</span><span class="p">)</span>
<span class="n">_country</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_country"</span><span class="p">)</span>
<span class="n">_city</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_city"</span><span class="p">)</span>
<span class="n">_postalCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_postalCode"</span><span class="p">)</span>
<span class="n">_state</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_state"</span><span class="p">)</span>
<span class="n">_street</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_street"</span><span class="p">)</span>
<span class="n">_subAdministrativeArea</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_subAdministrativeArea"</span><span class="p">)</span>
<span class="n">_subLocality</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_subLocality"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">CNPostalAddress</span><span class="p">(</span>
<span class="n">_ISOCountryCode</span><span class="p">,</span>
@@ -356,12 +519,12 @@
<span class="c1"># register the classes with bpylist.archiver</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;CNPostalAddress&quot;</span><span class="p">:</span> <span class="n">CNPostalAddress</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"CNPostalAddress"</span><span class="p">:</span> <span class="n">CNPostalAddress</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">(</span>
<span class="p">{</span><span class="s2">&quot;PLRevGeoMapItemAdditionalPlaceInfo&quot;</span><span class="p">:</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">}</span>
<span class="p">{</span><span class="s2">"PLRevGeoMapItemAdditionalPlaceInfo"</span><span class="p">:</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">}</span>
<span class="p">)</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;PLRevGeoMapItem&quot;</span><span class="p">:</span> <span class="n">PLRevGeoMapItem</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;PLRevGeoLocationInfo&quot;</span><span class="p">:</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"PLRevGeoMapItem"</span><span class="p">:</span> <span class="n">PLRevGeoMapItem</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"PLRevGeoLocationInfo"</span><span class="p">:</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">})</span>
<div class="viewcode-block" id="PlaceInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.PlaceInfo">[docs]</a><span class="k">class</span> <span class="nc">PlaceInfo</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
@@ -397,18 +560,18 @@
<span class="k">class</span> <span class="nc">PlaceInfo4</span><span class="p">(</span><span class="n">PlaceInfo</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Reverse geolocation place info for a photo (Photos &lt;= 4)&quot;&quot;&quot;</span>
<span class="sd">"""Reverse geolocation place info for a photo (Photos &lt;= 4)"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">place_names</span><span class="p">,</span> <span class="n">country_code</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;place_names: list of place name tuples in ascending order by area</span>
<span class="sd">"""place_names: list of place name tuples in ascending order by area</span>
<span class="sd"> tuple fields are: modelID, place name, place type, area, e.g.</span>
<span class="sd"> [(5, &quot;St James&#39;s Park&quot;, 45, 0),</span>
<span class="sd"> (4, &#39;Westminster&#39;, 16, 22097376),</span>
<span class="sd"> (3, &#39;London&#39;, 4, 1596146816),</span>
<span class="sd"> (2, &#39;England&#39;, 2, 180406091776),</span>
<span class="sd"> (1, &#39;United Kingdom&#39;, 1, 414681432064)]</span>
<span class="sd"> [(5, "St James's Park", 45, 0),</span>
<span class="sd"> (4, 'Westminster', 16, 22097376),</span>
<span class="sd"> (3, 'London', 4, 1596146816),</span>
<span class="sd"> (2, 'England', 2, 180406091776),</span>
<span class="sd"> (1, 'United Kingdom', 1, 414681432064)]</span>
<span class="sd"> country_code: two letter country code for the country</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="sd"> """</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place_names</span> <span class="o">=</span> <span class="n">place_names</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_country_code</span> <span class="o">=</span> <span class="n">country_code</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_place_info</span><span class="p">()</span>
@@ -447,14 +610,14 @@
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_place_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Process place_names to set self._name and self._names&quot;&quot;&quot;</span>
<span class="sd">"""Process place_names to set self._name and self._names"""</span>
<span class="n">places</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place_names</span>
<span class="c1"># build a dictionary where key is placetype</span>
<span class="n">places_dict</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">places</span><span class="p">:</span>
<span class="c1"># places in format:</span>
<span class="c1"># [(5, &quot;St James&#39;s Park&quot;, 45, 0), ]</span>
<span class="c1"># [(5, "St James's Park", 45, 0), ]</span>
<span class="c1"># 0: modelID</span>
<span class="c1"># 1: name</span>
<span class="c1"># 2: type</span>
@@ -496,7 +659,7 @@
<span class="c1"># build the name as it appears in Photos</span>
<span class="c1"># the length of the name is at most 3 fields and appears to be based on available</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by &#39;,&#39;)</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by ',')</span>
<span class="c1"># always has country if available then either area of interest and city OR</span>
<span class="c1"># city and state</span>
<span class="c1"># e.g. 4, 2, 1 OR 8, 4, 1</span>
@@ -520,61 +683,61 @@
<span class="k">if</span> <span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
<span class="n">name_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">name_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">name_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">""</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">&quot;PlaceInfo(&quot;</span> <span class="o">+</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">&#39;&quot;</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
<span class="k">return</span> <span class="s2">"PlaceInfo("</span> <span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">='</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">'"</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">")"</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">PlaceInfo5</span><span class="p">(</span><span class="n">PlaceInfo</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Reverse geolocation place info for a photo (Photos &gt;= 5)&quot;&quot;&quot;</span>
<span class="sd">"""Reverse geolocation place info for a photo (Photos &gt;= 5)"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">revgeoloc_bplist</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;revgeoloc_bplist: a binary plist blob containing</span>
<span class="sd"> a serialized PLRevGeoLocationInfo object&quot;&quot;&quot;</span>
<span class="sd">"""revgeoloc_bplist: a binary plist blob containing</span>
<span class="sd"> a serialized PLRevGeoLocationInfo object"""</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_bplist</span> <span class="o">=</span> <span class="n">revgeoloc_bplist</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span> <span class="o">=</span> <span class="n">archiver</span><span class="o">.</span><span class="n">unarchive</span><span class="p">(</span><span class="n">revgeoloc_bplist</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_place_info</span><span class="p">()</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">address_str</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns the postal address as a string&quot;&quot;&quot;</span>
<span class="sd">"""returns the postal address as a string"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">addressString</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">country_code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns the country code&quot;&quot;&quot;</span>
<span class="sd">"""returns the country code"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">countryCode</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">ishome</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns True if place is user&#39;s home address&quot;&quot;&quot;</span>
<span class="sd">"""returns True if place is user's home address"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">isHome</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns local place name&quot;&quot;&quot;</span>
<span class="sd">"""returns local place name"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns PlaceNames tuple with detailed reverse geolocation place names&quot;&quot;&quot;</span>
<span class="sd">"""returns PlaceNames tuple with detailed reverse geolocation place names"""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_names</span>
<span class="nd">@property</span>
@@ -599,7 +762,7 @@
<span class="k">return</span> <span class="n">postal_address</span>
<span class="k">def</span> <span class="nf">_process_place_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Process sortedPlaceInfos to set self._name and self._names&quot;&quot;&quot;</span>
<span class="sd">"""Process sortedPlaceInfos to set self._name and self._names"""</span>
<span class="n">places</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">mapItem</span><span class="o">.</span><span class="n">sortedPlaceInfos</span>
<span class="c1"># build a dictionary where key is placetype</span>
@@ -632,20 +795,20 @@
<span class="c1"># build the name as it appears in Photos</span>
<span class="c1"># the length of the name is variable and appears to be based on available</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by &#39;,&#39;)</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by ',')</span>
<span class="c1"># 8: area_of_interest</span>
<span class="c1"># 11: region (I&#39;ve only seen this applied to islands)</span>
<span class="c1"># 11: region (I've only seen this applied to islands)</span>
<span class="c1"># 4: locality / city</span>
<span class="c1"># 2: administrative area (state/province)</span>
<span class="c1"># 1: country</span>
<span class="c1"># 9: inland_water</span>
<span class="c1"># 7: ocean</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="p">[</span>
<span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="p">[</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">8</span><span class="p">],</span> <span class="c1"># area of interest</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span> <span class="c1"># region (I&#39;ve only seen this applied to islands)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span> <span class="c1"># region (I've only seen this applied to islands)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="c1"># locality / city</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="c1"># administrative area (state/province)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="c1"># country</span>
@@ -655,7 +818,7 @@
<span class="k">if</span> <span class="n">p</span> <span class="ow">and</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="p">]</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">""</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)):</span>
@@ -668,91 +831,64 @@
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;ishome&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">&quot;address_str&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">&quot;address&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">),</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"ishome"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">"address_str"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">"address"</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">&quot;PlaceInfo(&quot;</span> <span class="o">+</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">&#39;&quot;</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
<span class="k">return</span> <span class="s2">"PlaceInfo("</span> <span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">='</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">'"</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">")"</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;ishome&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">&quot;address_str&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">&quot;address&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"ishome"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">"address_str"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">"address"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../index.html">osxphotos</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">osxphotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">osxphotos command line interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">osxphotos package</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../index.html">Documentation overview</a><ul>
<li><a href="../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</footer>
</div>
<div class="footer">
&copy;2021, Rhet Turnbull.
<aside class="toc-drawer no-toc">
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</aside>
</div>
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.queryoptions - osxphotos 0.48.7 documentation</title>
<title>osxphotos.queryoptions - osxphotos 0.50.13 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.48.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.48.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -266,6 +266,7 @@
<span class="sd"> not_missing: search for non-missing photos</span>
<span class="sd"> not_panorama: search for non-panorama photos</span>
<span class="sd"> not_portrait: search for non-portrait photos</span>
<span class="sd"> not_reference: search for photos not stored by reference (that is, they are managed by Photos)</span>
<span class="sd"> not_screenshot: search for non-screenshot photos</span>
<span class="sd"> not_selfie: search for non-selfie photos</span>
<span class="sd"> not_shared: search for non-shared photos</span>
@@ -347,6 +348,7 @@
<span class="n">not_missing</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_panorama</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_portrait</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_reference</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_screenshot</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_selfie</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_shared</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>

View File

@@ -1,60 +1,204 @@
<!doctype html>
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<!DOCTYPE html>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.searchinfo - osxphotos 0.54.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.searchinfo &#8212; osxphotos 0.47.9 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>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<span class="sidebar-brand-text">osxphotos 0.54.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
</ul>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
</div>
</div>
<div class="body" role="main">
<h1>Source code for osxphotos.searchinfo</h1><div class="highlight"><pre>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.searchinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot; class for PhotoInfo exposing SearchInfo data such as labels </span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">_PHOTOS_4_VERSION</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_ACTIVITY</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_ALL_LOCALITY</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_BODY_OF_WATER</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_CITY</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_COUNTRY</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_HOLIDAY</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_LABEL</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_MEDIA_TYPES</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_MONTH</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_NEIGHBORHOOD</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_PLACE_NAME</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_SEASON</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_STATE</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_STATE_ABBREVIATION</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_STREET</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_VENUE</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_VENUE_TYPE</span><span class="p">,</span>
<span class="n">SEARCH_CATEGORY_YEAR</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">,</span> <span class="n">search_category_factory</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;SearchInfo&quot;</span><span class="p">]</span>
@@ -71,6 +215,7 @@
<span class="s2">&quot;search info not implemented for this database version&quot;</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_categories</span> <span class="o">=</span> <span class="n">search_category_factory</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_photo</span> <span class="o">=</span> <span class="n">photo</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_normalized</span> <span class="o">=</span> <span class="n">normalized</span>
<span class="bp">self</span><span class="o">.</span><span class="n">uuid</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">uuid</span>
@@ -84,106 +229,121 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">labels</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return list of labels associated with Photo&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_LABEL</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">LABEL</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">place_names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of place names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_PLACE_NAME</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">PLACE_NAME</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">streets</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of street names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_STREET</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">STREET</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">neighborhoods</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of neighborhoods&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_NEIGHBORHOOD</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">NEIGHBORHOOD</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">locality_names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of other locality names&quot;&quot;&quot;</span>
<span class="n">locality</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">category</span> <span class="ow">in</span> <span class="n">SEARCH_CATEGORY_ALL_LOCALITY</span><span class="p">:</span>
<span class="k">for</span> <span class="n">category</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">ALL_LOCALITY</span><span class="p">:</span>
<span class="n">locality</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">category</span><span class="p">)</span>
<span class="k">return</span> <span class="n">locality</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">city</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns city/town&quot;&quot;&quot;</span>
<span class="n">city</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_CITY</span><span class="p">)</span>
<span class="n">city</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">CITY</span><span class="p">)</span>
<span class="k">return</span> <span class="n">city</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">city</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">state</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns state name&quot;&quot;&quot;</span>
<span class="n">state</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_STATE</span><span class="p">)</span>
<span class="n">state</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">STATE</span><span class="p">)</span>
<span class="k">return</span> <span class="n">state</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">state</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">state_abbreviation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns state abbreviation&quot;&quot;&quot;</span>
<span class="n">abbrev</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_STATE_ABBREVIATION</span><span class="p">)</span>
<span class="n">abbrev</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">STATE_ABBREVIATION</span><span class="p">)</span>
<span class="k">return</span> <span class="n">abbrev</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">abbrev</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">country</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns country name&quot;&quot;&quot;</span>
<span class="n">country</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_COUNTRY</span><span class="p">)</span>
<span class="n">country</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">COUNTRY</span><span class="p">)</span>
<span class="k">return</span> <span class="n">country</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">country</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">month</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns month name&quot;&quot;&quot;</span>
<span class="n">month</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_MONTH</span><span class="p">)</span>
<span class="n">month</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">MONTH</span><span class="p">)</span>
<span class="k">return</span> <span class="n">month</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">month</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">year</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns year&quot;&quot;&quot;</span>
<span class="n">year</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_YEAR</span><span class="p">)</span>
<span class="n">year</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">YEAR</span><span class="p">)</span>
<span class="k">return</span> <span class="n">year</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">year</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">bodies_of_water</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of body of water names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_BODY_OF_WATER</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">BODY_OF_WATER</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">holidays</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of holiday names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_HOLIDAY</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">HOLIDAY</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">activities</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of activity names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_ACTIVITY</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">ACTIVITY</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">season</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns season name&quot;&quot;&quot;</span>
<span class="n">season</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_SEASON</span><span class="p">)</span>
<span class="n">season</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">SEASON</span><span class="p">)</span>
<span class="k">return</span> <span class="n">season</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">season</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">venues</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of venue names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_VENUE</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">VENUE</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">venue_types</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of venue types&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">SEARCH_CATEGORY_VENUE_TYPE</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">VENUE_TYPE</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">media_types</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of media types (photo, video, panorama, etc)&quot;&quot;&quot;</span>
<span class="n">types</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">category</span> <span class="ow">in</span> <span class="n">SEARCH_CATEGORY_MEDIA_TYPES</span><span class="p">:</span>
<span class="k">for</span> <span class="n">category</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">MEDIA_TYPES</span><span class="p">:</span>
<span class="n">types</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="n">category</span><span class="p">)</span>
<span class="k">return</span> <span class="n">types</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">detected_text</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns text detected in the photo (macOS 13+ / Photos 8+ only)&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photo</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">DETECTED_TEXT</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">camera</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns camera name (macOS 13+ / Photos 8+ only)&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photo</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;&quot;</span>
<span class="n">camera</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_categories</span><span class="o">.</span><span class="n">CAMERA</span><span class="p">)</span>
<span class="k">return</span> <span class="n">camera</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">camera</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return all search info properties in a single list&quot;&quot;&quot;</span>
@@ -199,6 +359,7 @@
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">venues</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">venue_types</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">media_types</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">detected_text</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">city</span><span class="p">:</span>
<span class="nb">all</span> <span class="o">+=</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">city</span><span class="p">]</span>
@@ -214,6 +375,8 @@
<span class="nb">all</span> <span class="o">+=</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">year</span><span class="p">]</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">season</span><span class="p">:</span>
<span class="nb">all</span> <span class="o">+=</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">season</span><span class="p">]</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">camera</span><span class="p">:</span>
<span class="nb">all</span> <span class="o">+=</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">camera</span><span class="p">]</span>
<span class="k">return</span> <span class="nb">all</span>
@@ -238,6 +401,8 @@
<span class="s2">&quot;venues&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">venues</span><span class="p">,</span>
<span class="s2">&quot;venue_types&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">venue_types</span><span class="p">,</span>
<span class="s2">&quot;media_types&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">media_types</span><span class="p">,</span>
<span class="s2">&quot;detected_text&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">detected_text</span><span class="p">,</span>
<span class="s2">&quot;camera&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">camera</span><span class="p">,</span>
<span class="p">}</span></div>
<span class="k">def</span> <span class="nf">_get_text_for_category</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">category</span><span class="p">):</span>
@@ -254,72 +419,47 @@
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span></div>
</pre></div>
</div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../index.html">osxphotos</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">osxphotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">osxphotos command line interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">osxphotos package</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../index.html">Documentation overview</a><ul>
<li><a href="../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</footer>
</div>
<div class="footer">
&copy;2021, Rhet Turnbull.
<aside class="toc-drawer no-toc">
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</aside>
</div>
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>

View File

@@ -61,6 +61,9 @@ Valid filters are:
* `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
* `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
* `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
* ``filter(x)``\ : Filter list of values using predicate x; for example, ``{folder_album|filter(contains Events)}`` returns only folders/albums containing the word 'Events' in their path.
* ``int``\ : Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float.
* ``float``\ : Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int.
e.g. if Photo keywords are ``["FOO","bar"]``\ :
@@ -193,6 +196,8 @@ Template Substitutions
- True if template is being rendered for the edited version of a photo, otherwise False.
* - {favorite}
- Photo has been marked as favorite?; True/False value, use in format '{favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}'
* - {created}
- Photo's creation date in ISO format, e.g. '2020-03-22'
* - {created.date}
- Photo's creation date in ISO format, e.g. '2020-03-22'
* - {created.year}
@@ -219,6 +224,8 @@ Template Substitutions
- 2-digit second of the photo creation time
* - {created.strftime}
- Apply strftime template to file creation date/time. Should be used in form {created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {created.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. See https://strftime.org/ for help on strftime templates.
* - {modified}
- Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified
* - {modified.date}
- Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified
* - {modified.year}
@@ -245,6 +252,8 @@ Template Substitutions
- 2-digit second of the photo modification time; uses creation date if photo is not modified
* - {modified.strftime}
- Apply strftime template to file modification date/time. Should be used in form {modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. Uses creation date if photo is not modified. See https://strftime.org/ for help on strftime templates.
* - {today}
- Current date in iso format, e.g. '2020-03-22'
* - {today.date}
- Current date in iso format, e.g. '2020-03-22'
* - {today.year}
@@ -344,9 +353,11 @@ Template Substitutions
* - {cr}
- A carriage return: '\r'
* - {crlf}
- a carriage return + line feed: '\r\n'
- A carriage return + line feed: '\r\n'
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.50.4'
- The osxphotos version, e.g. '0.55.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

View File

@@ -73,6 +73,22 @@ the exported files would be:
/path/to/export/Vacation/IMG_1234.JPG
If your photos are organized in folders and albums in Photos you can preserve this structure on export by using the ``{folder_album}`` template field with the ``--directory`` option. For example, if you have a photo in the album ``Vacation`` which is in the ``Travel`` folder, the following command would export the photo to the ``Travel/Vacation`` directory:
``osxphotos export /path/to/export --directory "{folder_album}"``
Photos can belong to more than one album. In this case, the template field ``{folder_album}`` will expand to all the album names that the photo belongs to. For example, if a photo belongs to the albums ``Vacation`` and ``Travel``\ , the template field ``{folder_album}`` would expand to ``Vacation``\ , ``Travel``. If the photo belongs to no albums, the template field ``{folder_album}`` would expand to "_" (the default value).
All template fields including ``{folder_album}`` can be further filtered using a number of different filters. To convert all directory names to lower case for example, use the ``lower`` filter:
``osxphotos export /path/to/export --directory "{folder_album|lower}"``
If all your photos were organized into various albums under a folder named ``Events`` but some where also included in other top-level albums and you wanted to export only the ``Events`` folder, you could use the ``filter`` option to filter out the other top-level albums by selecting only those folder/album paths that start with ``Events``\ :
``osxphotos export /path/to/export --directory "{folder_album|filter(startswith Events)}"``
You can learn more about the other filters using ``osxphotos help export``.
Specify exported filename
-------------------------
@@ -275,6 +291,14 @@ You can use the ``--report`` option to create a report, in comma-separated value
``osxphotos export /path/to/export --report export.csv``
You can also create reports in JSON or SQLite format by changing the extension of the report filename. For example, to create a JSON report:
``osxphotos export /path/to/export --report export.json``
And to create a SQLite report:
``osxphotos export /path/to/export --report export.sqlite``
Exporting only certain photos
-----------------------------

View File

@@ -0,0 +1,134 @@
/*
* _sphinx_javascript_frameworks_compat.js
* ~~~~~~~~~~
*
* Compatability shim for jQuery and underscores.js.
*
* WILL BE REMOVED IN Sphinx 6.0
* xref RemovedInSphinx60Warning
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}

View File

@@ -222,7 +222,7 @@ table.modindextable td {
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 450px;
min-width: 360px;
max-width: 800px;
}
@@ -237,16 +237,6 @@ a.headerlink {
visibility: hidden;
}
a.brackets:before,
span.brackets > a:before{
content: "[";
}
a.brackets:after,
span.brackets > a:after {
content: "]";
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
@@ -334,13 +324,15 @@ aside.sidebar {
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
@@ -379,6 +371,8 @@ div.body p.centered {
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
@@ -386,6 +380,8 @@ div.admonition > :last-child {
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
@@ -428,10 +424,6 @@ table.docutils td, table.docutils th {
border-bottom: 1px solid #aaa;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
@@ -614,20 +606,26 @@ ol.simple p,
ul.simple p {
margin-bottom: 0;
}
dl.footnote > dt,
dl.citation > dt {
aside.footnote > span,
div.citation > span {
float: left;
margin-right: 0.5em;
}
dl.footnote > dd,
dl.citation > dd {
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
dl.footnote > dd:after,
dl.citation > dd:after {
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
@@ -644,10 +642,6 @@ dl.field-list > dt {
padding-right: 5px;
}
dl.field-list > dt:after {
content: ":";
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;

View File

@@ -35,7 +35,8 @@ div.highlight {
position: relative;
}
.highlight:hover button.copybtn {
/* Show the copybutton */
.highlight:hover button.copybtn, button.copybtn.success {
opacity: 1;
}

View File

@@ -102,18 +102,25 @@ const clearSelection = () => {
}
}
// Changes tooltip text for two seconds, then changes it back
// Changes tooltip text for a moment, then changes it back
// We want the timeout of our `success` class to be a bit shorter than the
// tooltip and icon change, so that we can hide the icon before changing back.
var timeoutIcon = 2000;
var timeoutSuccessClass = 1500;
const temporarilyChangeTooltip = (el, oldText, newText) => {
el.setAttribute('data-tooltip', newText)
el.classList.add('success')
setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000)
setTimeout(() => el.classList.remove('success'), 2000)
// Remove success a little bit sooner than we change the tooltip
// So that we can use CSS to hide the copybutton first
setTimeout(() => el.classList.remove('success'), timeoutSuccessClass)
setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon)
}
// Changes the copy button icon for two seconds, then changes it back
const temporarilyChangeIcon = (el) => {
el.innerHTML = iconCheck;
setTimeout(() => {el.innerHTML = iconCopy}, 2000)
setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon)
}
const addCopyButtonToCodeCells = () => {
@@ -125,7 +132,8 @@ const addCopyButtonToCodeCells = () => {
}
// Add copybuttons to all of our code cells
const codeCells = document.querySelectorAll('div.highlight pre')
const COPYBUTTON_SELECTOR = 'div.highlight pre';
const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR)
codeCells.forEach((codeCell, index) => {
const id = codeCellId(index)
codeCell.setAttribute('id', id)
@@ -141,10 +149,25 @@ function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/**
* Removes excluded text from a Node.
*
* @param {Node} target Node to filter.
* @param {string} exclude CSS selector of nodes to exclude.
* @returns {DOMString} Text from `target` with text removed.
*/
function filterText(target, exclude) {
const clone = target.cloneNode(true); // clone as to not modify the live DOM
if (exclude) {
// remove excluded nodes
clone.querySelectorAll(exclude).forEach(node => node.remove());
}
return clone.innerText;
}
// Callback when a copy button is clicked. Will be passed the node that was clicked
// should then grab the text and replace pieces of text that shouldn't be used in output
function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
var regexp;
var match;
@@ -199,7 +222,12 @@ function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onl
var copyTargetText = (trigger) => {
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
return formatCopyText(target.innerText, '', false, true, true, true, '', '')
// get filtered text
let exclude = '.linenos, .gp';
let text = filterText(target, exclude);
return formatCopyText(text, '', false, true, true, true, '', '')
}
// Initialize with a callback so we can modify the text before copy

View File

@@ -2,10 +2,25 @@ function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/**
* Removes excluded text from a Node.
*
* @param {Node} target Node to filter.
* @param {string} exclude CSS selector of nodes to exclude.
* @returns {DOMString} Text from `target` with text removed.
*/
export function filterText(target, exclude) {
const clone = target.cloneNode(true); // clone as to not modify the live DOM
if (exclude) {
// remove excluded nodes
clone.querySelectorAll(exclude).forEach(node => node.remove());
}
return clone.innerText;
}
// Callback when a copy button is clicked. Will be passed the node that was clicked
// should then grab the text and replace pieces of text that shouldn't be used in output
export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
var regexp;
var match;

69
docs/_static/debug.css vendored Normal file
View File

@@ -0,0 +1,69 @@
/*
This CSS file should be overridden by the theme authors. It's
meant for debugging and developing the skeleton that this theme provides.
*/
body {
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji";
background: lavender;
}
.sb-announcement {
background: rgb(131, 131, 131);
}
.sb-announcement__inner {
background: black;
color: white;
}
.sb-header {
background: lightskyblue;
}
.sb-header__inner {
background: royalblue;
color: white;
}
.sb-header-secondary {
background: lightcyan;
}
.sb-header-secondary__inner {
background: cornflowerblue;
color: white;
}
.sb-sidebar-primary {
background: lightgreen;
}
.sb-main {
background: blanchedalmond;
}
.sb-main__inner {
background: antiquewhite;
}
.sb-header-article {
background: lightsteelblue;
}
.sb-article-container {
background: snow;
}
.sb-article-main {
background: white;
}
.sb-footer-article {
background: lightpink;
}
.sb-sidebar-secondary {
background: lightgoldenrodyellow;
}
.sb-footer-content {
background: plum;
}
.sb-footer-content__inner {
background: palevioletred;
}
.sb-footer {
background: pink;
}
.sb-footer__inner {
background: salmon;
}
.sb-article {
background: white;
}

View File

@@ -2,325 +2,155 @@
* doctools.js
* ~~~~~~~~~~~
*
* Sphinx JavaScript utilities for all documentation.
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
/**
* make the code below compatible with browsers without
* an installed firebug like debugger
if (!window.console || !console.firebug) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
"profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {};
}
*/
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}
/**
* Small JavaScript module for the documentation.
*/
var Documentation = {
init : function() {
this.fixFirefoxAnchorBug();
this.highlightSearchWords();
this.initIndexTable();
if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
this.initOnKeyListeners();
}
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS : {},
PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
LOCALE : 'unknown',
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext : function(string) {
var translated = Documentation.TRANSLATIONS[string];
if (typeof translated === 'undefined')
return string;
return (typeof translated === 'string') ? translated : translated[0];
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext : function(singular, plural, n) {
var translated = Documentation.TRANSLATIONS[singular];
if (typeof translated === 'undefined')
return (n == 1) ? singular : plural;
return translated[Documentation.PLURALEXPR(n)];
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations : function(catalog) {
for (var key in catalog.messages)
this.TRANSLATIONS[key] = catalog.messages[key];
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
this.LOCALE = catalog.locale;
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* add context elements like header anchor links
* helper function to focus on search bar
*/
addContextElements : function() {
$('div[id] > :header:first').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this headline')).
appendTo(this);
});
$('dt[id]').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this definition')).
appendTo(this);
});
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* workaround a firefox stupidity
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
* Initialise the domain index toggle buttons
*/
fixFirefoxAnchorBug : function() {
if (document.location.hash && $.browser.mozilla)
window.setTimeout(function() {
document.location.href += '';
}, 10);
},
/**
* highlight the search words provided in the url in the text
*/
highlightSearchWords : function() {
var params = $.getQueryParameters();
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
if (terms.length) {
var body = $('div.body');
if (!body.length) {
body = $('body');
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
window.setTimeout(function() {
$.each(terms, function() {
body.highlightText(this.toLowerCase(), 'highlighted');
});
}, 10);
$('<p class="highlight-link"><a href="javascript:Documentation.' +
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
.appendTo($('#searchbox'));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
/**
* init the domain index toggle buttons
*/
initIndexTable : function() {
var togglers = $('img.toggler').click(function() {
var src = $(this).attr('src');
var idnum = $(this).attr('id').substr(7);
$('tr.cg-' + idnum).toggle();
if (src.substr(-9) === 'minus.png')
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
else
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
}).css('display', '');
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
togglers.click();
}
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
/**
* helper function to hide the search marks again
*/
hideSearchWords : function() {
$('#searchbox .highlight-link').fadeOut(300);
$('span.highlighted').removeClass('highlighted');
var url = new URL(window.location);
url.searchParams.delete('highlight');
window.history.replaceState({}, '', url);
},
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.altKey || event.ctrlKey || event.metaKey) return;
/**
* make the url absolute
*/
makeURL : function(relativeURL) {
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
},
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
/**
* get the current relative url
*/
getCurrentURL : function() {
var path = document.location.pathname;
var parts = path.split(/\//);
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
if (this === '..')
parts.pop();
});
var url = parts.join('/');
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
},
initOnKeyListeners: function() {
$(document).keydown(function(event) {
var activeElementType = document.activeElement.tagName;
// don't navigate when in search box, textarea, dropdown or button
if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
&& activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey
&& !event.shiftKey) {
switch (event.keyCode) {
case 37: // left
var prevHref = $('link[rel="prev"]').prop('href');
if (prevHref) {
window.location.href = prevHref;
return false;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case 39: // right
var nextHref = $('link[rel="next"]').prop('href');
if (nextHref) {
window.location.href = nextHref;
return false;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
}
},
};
// quick alias for translations
_ = Documentation.gettext;
const _ = Documentation.gettext;
$(document).ready(function() {
Documentation.init();
});
_ready(Documentation.init);

View File

@@ -1,12 +1,14 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.50.4',
LANGUAGE: 'None',
VERSION: '0.55.2',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};

10881
docs/_static/jquery-3.6.0.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,7 @@
*
*/
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, is available */
@@ -197,101 +197,3 @@ var Stemmer = function() {
}
}
var splitChars = (function() {
var result = {};
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
var i, j, start, end;
for (i = 0; i < singles.length; i++) {
result[singles[i]] = true;
}
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
for (i = 0; i < ranges.length; i++) {
start = ranges[i][0];
end = ranges[i][1];
for (j = start; j <= end; j++) {
result[j] = true;
}
}
return result;
})();
function splitQuery(query) {
var result = [];
var start = -1;
for (var i = 0; i < query.length; i++) {
if (splitChars[query.charCodeAt(i)]) {
if (start !== -1) {
result.push(query.slice(start, i));
start = -1;
}
} else if (start === -1) {
start = i;
}
}
if (start !== -1) {
result.push(query.slice(start));
}
return result;
}

View File

@@ -54,6 +54,7 @@
.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */
.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */
.highlight .w { color: #f8f8f8 } /* Text.Whitespace */
.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */
@@ -88,21 +89,21 @@ body[data-theme="dark"] .highlight td.linenos .special { color: #000000; backgro
body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
body[data-theme="dark"] .highlight .hll { background-color: #404040 }
body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 }
body[data-theme="dark"] .highlight .c { color: #999999; font-style: italic } /* Comment */
body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */
body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */
body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */
body[data-theme="dark"] .highlight .k { color: #6ab825; font-weight: bold } /* Keyword */
body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */
body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */
body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */
body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */
body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */
body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */
body[data-theme="dark"] .highlight .ch { color: #999999; font-style: italic } /* Comment.Hashbang */
body[data-theme="dark"] .highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */
body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
body[data-theme="dark"] .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */
body[data-theme="dark"] .highlight .cpf { color: #999999; font-style: italic } /* Comment.PreprocFile */
body[data-theme="dark"] .highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */
body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */
body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
@@ -114,36 +115,37 @@ body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */
body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */
body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */
body[data-theme="dark"] .highlight .gt { color: #d22323 } /* Generic.Traceback */
body[data-theme="dark"] .highlight .kc { color: #6ab825; font-weight: bold } /* Keyword.Constant */
body[data-theme="dark"] .highlight .kd { color: #6ab825; font-weight: bold } /* Keyword.Declaration */
body[data-theme="dark"] .highlight .kn { color: #6ab825; font-weight: bold } /* Keyword.Namespace */
body[data-theme="dark"] .highlight .kp { color: #6ab825 } /* Keyword.Pseudo */
body[data-theme="dark"] .highlight .kr { color: #6ab825; font-weight: bold } /* Keyword.Reserved */
body[data-theme="dark"] .highlight .kt { color: #6ab825; font-weight: bold } /* Keyword.Type */
body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */
body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */
body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */
body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */
body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */
body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */
body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */
body[data-theme="dark"] .highlight .m { color: #3677a9 } /* Literal.Number */
body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */
body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */
body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */
body[data-theme="dark"] .highlight .nb { color: #24909d } /* Name.Builtin */
body[data-theme="dark"] .highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */
body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */
body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */
body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */
body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */
body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */
body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */
body[data-theme="dark"] .highlight .nf { color: #447fcf } /* Name.Function */
body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */
body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */
body[data-theme="dark"] .highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */
body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */
body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */
body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */
body[data-theme="dark"] .highlight .nt { color: #6ab825; font-weight: bold } /* Name.Tag */
body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */
body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */
body[data-theme="dark"] .highlight .ow { color: #6ab825; font-weight: bold } /* Operator.Word */
body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */
body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */
body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */
body[data-theme="dark"] .highlight .mb { color: #3677a9 } /* Literal.Number.Bin */
body[data-theme="dark"] .highlight .mf { color: #3677a9 } /* Literal.Number.Float */
body[data-theme="dark"] .highlight .mh { color: #3677a9 } /* Literal.Number.Hex */
body[data-theme="dark"] .highlight .mi { color: #3677a9 } /* Literal.Number.Integer */
body[data-theme="dark"] .highlight .mo { color: #3677a9 } /* Literal.Number.Oct */
body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */
body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */
body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */
body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */
body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */
body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */
body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */
body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */
@@ -157,13 +159,13 @@ body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Othe
body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */
body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */
body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */
body[data-theme="dark"] .highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */
body[data-theme="dark"] .highlight .fm { color: #447fcf } /* Name.Function.Magic */
body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */
body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */
body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */
body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */
body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */
body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */
body[data-theme="dark"] .highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */
body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) .highlight pre { line-height: 125%; }
body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
@@ -172,21 +174,21 @@ body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000;
body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
body:not([data-theme="light"]) .highlight .hll { background-color: #404040 }
body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 }
body:not([data-theme="light"]) .highlight .c { color: #999999; font-style: italic } /* Comment */
body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */
body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */
body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */
body:not([data-theme="light"]) .highlight .k { color: #6ab825; font-weight: bold } /* Keyword */
body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */
body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */
body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */
body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */
body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */
body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */
body:not([data-theme="light"]) .highlight .ch { color: #999999; font-style: italic } /* Comment.Hashbang */
body:not([data-theme="light"]) .highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */
body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
body:not([data-theme="light"]) .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */
body:not([data-theme="light"]) .highlight .cpf { color: #999999; font-style: italic } /* Comment.PreprocFile */
body:not([data-theme="light"]) .highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */
body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */
body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
@@ -198,36 +200,37 @@ body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prom
body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */
body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */
body:not([data-theme="light"]) .highlight .gt { color: #d22323 } /* Generic.Traceback */
body:not([data-theme="light"]) .highlight .kc { color: #6ab825; font-weight: bold } /* Keyword.Constant */
body:not([data-theme="light"]) .highlight .kd { color: #6ab825; font-weight: bold } /* Keyword.Declaration */
body:not([data-theme="light"]) .highlight .kn { color: #6ab825; font-weight: bold } /* Keyword.Namespace */
body:not([data-theme="light"]) .highlight .kp { color: #6ab825 } /* Keyword.Pseudo */
body:not([data-theme="light"]) .highlight .kr { color: #6ab825; font-weight: bold } /* Keyword.Reserved */
body:not([data-theme="light"]) .highlight .kt { color: #6ab825; font-weight: bold } /* Keyword.Type */
body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */
body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */
body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */
body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */
body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */
body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */
body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */
body:not([data-theme="light"]) .highlight .m { color: #3677a9 } /* Literal.Number */
body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */
body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */
body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */
body:not([data-theme="light"]) .highlight .nb { color: #24909d } /* Name.Builtin */
body:not([data-theme="light"]) .highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */
body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */
body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */
body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */
body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */
body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */
body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */
body:not([data-theme="light"]) .highlight .nf { color: #447fcf } /* Name.Function */
body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */
body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */
body:not([data-theme="light"]) .highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */
body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */
body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */
body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */
body:not([data-theme="light"]) .highlight .nt { color: #6ab825; font-weight: bold } /* Name.Tag */
body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */
body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */
body:not([data-theme="light"]) .highlight .ow { color: #6ab825; font-weight: bold } /* Operator.Word */
body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */
body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */
body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */
body:not([data-theme="light"]) .highlight .mb { color: #3677a9 } /* Literal.Number.Bin */
body:not([data-theme="light"]) .highlight .mf { color: #3677a9 } /* Literal.Number.Float */
body:not([data-theme="light"]) .highlight .mh { color: #3677a9 } /* Literal.Number.Hex */
body:not([data-theme="light"]) .highlight .mi { color: #3677a9 } /* Literal.Number.Integer */
body:not([data-theme="light"]) .highlight .mo { color: #3677a9 } /* Literal.Number.Oct */
body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */
body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */
body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */
body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */
body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */
body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */
body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */
body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */
@@ -241,12 +244,12 @@ body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.Stri
body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */
body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */
body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */
body:not([data-theme="light"]) .highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */
body:not([data-theme="light"]) .highlight .fm { color: #447fcf } /* Name.Function.Magic */
body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */
body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */
body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */
body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */
body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */
body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */
body:not([data-theme="light"]) .highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */
body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */
}
}

File diff suppressed because one or more lines are too long

View File

@@ -8,18 +8,20 @@
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
if (!Scorer) {
/**
* Simple result scoring code.
*/
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [filename, title, anchor, descr, score]
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: function(result) {
return result[4];
score: result => {
const [docname, title, anchor, descr, score, filename] = result
return score
},
*/
@@ -28,9 +30,11 @@ if (!Scorer) {
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5}, // used to be unimportantResults
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
@@ -39,456 +43,495 @@ if (!Scorer) {
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2
partialTerm: 2,
};
}
if (!splitQuery) {
function splitQuery(query) {
return query.split(/\s+/);
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, searchTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const [docName, title, anchor, descr, score, _filename] = item;
let listItem = document.createElement("li");
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = docUrlRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = docUrlRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr)
listItem.appendChild(document.createElement("span")).innerHTML =
" (" + descr + ")";
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms)
);
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = _(
`Search finished, found ${resultCount} page(s) matching the search query.`
);
};
const _displayNextItem = (
results,
resultCount,
searchTerms
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
var Search = {
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
_index : null,
_queued_query : null,
_pulse_status : -1,
htmlToText : function(htmlString) {
var virtualDocument = document.implementation.createHTMLDocument('virtual');
var htmlElement = $(htmlString, virtualDocument);
htmlElement.find('.headerlink').remove();
docContent = htmlElement.find('[role=main]')[0];
if(docContent === undefined) {
console.warn("Content block not found. Sphinx search tries to obtain it " +
"via '[role=main]'. Could you check your theme or template.");
return "";
}
return docContent.textContent || docContent.innerText;
htmlToText: (htmlString) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() });
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent !== undefined) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
);
return "";
},
init : function() {
var params = $.getQueryParameters();
if (params.q) {
var query = params.q[0];
$('input[name="q"]')[0].value = query;
this.performSearch(query);
}
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex : function(url) {
$.ajax({type: "GET", url: url, data: null,
dataType: "script", cache: true,
complete: function(jqxhr, textstatus) {
if (textstatus != "success") {
document.getElementById("searchindexloader").src = url;
}
}});
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex : function(index) {
var q;
this._index = index;
if ((q = this._queued_query) !== null) {
this._queued_query = null;
Search.query(q);
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex : function() {
return this._index !== null;
},
hasIndex: () => Search._index !== null,
deferQuery : function(query) {
this._queued_query = query;
},
deferQuery: (query) => (Search._queued_query = query),
stopPulse : function() {
this._pulse_status = 0;
},
stopPulse: () => (Search._pulse_status = -1),
startPulse : function() {
if (this._pulse_status >= 0)
return;
function pulse() {
var i;
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
var dotString = '';
for (i = 0; i < Search._pulse_status; i++)
dotString += '.';
Search.dots.text(dotString);
if (Search._pulse_status > -1)
window.setTimeout(pulse, 500);
}
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch : function(query) {
performSearch: (query) => {
// create the required interface elements
this.out = $('#search-results');
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
this.dots = $('<span></span>').appendTo(this.title);
this.status = $('<p class="search-summary">&nbsp;</p>').appendTo(this.out);
this.output = $('<ul class="search"/>').appendTo(this.out);
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.classList.add("search");
$('#search-progress').text(_('Preparing search...'));
this.startPulse();
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (this.hasIndex())
this.query(query);
else
this.deferQuery(query);
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query : function(query) {
var i;
query: (query) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// stem the searchterms and add them to the correct list
var stemmer = new Stemmer();
var searchterms = [];
var excluded = [];
var hlterms = [];
var tmp = splitQuery(query);
var objectterms = [];
for (i = 0; i < tmp.length; i++) {
if (tmp[i] !== "") {
objectterms.push(tmp[i].toLowerCase());
}
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {
// skip this "word"
continue;
}
// stem the word
var word = stemmer.stemWord(tmp[i].toLowerCase());
// prevent stemmer from cutting word smaller than two chars
if(word.length < 3 && tmp[i].length >= 3) {
word = tmp[i];
}
var toAppend;
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] == '-') {
toAppend = excluded;
word = word.substr(1);
}
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
toAppend = searchterms;
hlterms.push(tmp[i].toLowerCase());
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
// only add if not already in the list
if (!$u.contains(toAppend, word))
toAppend.push(word);
});
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
// console.debug('SEARCH: searching for:');
// console.info('required: ', searchterms);
// console.info('excluded: ', excluded);
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
// prepare search
var terms = this._index.terms;
var titleterms = this._index.titleterms;
// array of [docname, title, anchor, descr, score, filename]
let results = [];
_removeChildren(document.getElementById("search-progress"));
// array of [filename, title, anchor, descr, score]
var results = [];
$('#search-progress').empty();
const queryLower = query.toLowerCase();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
let score = Math.round(100 * queryLower.length / title.length)
results.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
for (const [file, id] of foundEntries) {
let score = Math.round(100 * queryLower.length / entry.length)
results.push([
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// lookup as object
for (i = 0; i < objectterms.length; i++) {
var others = [].concat(objectterms.slice(0, i),
objectterms.slice(i+1, objectterms.length));
results = results.concat(this.performObjectSearch(objectterms[i], others));
}
objectTerms.forEach((term) =>
results.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
for (i = 0; i < results.length; i++)
results[i][4] = Scorer.score(results[i]);
}
if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort(function(a, b) {
var left = a[4];
var right = b[4];
if (left > right) {
return 1;
} else if (left < right) {
return -1;
} else {
results.sort((a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
left = a[1].toLowerCase();
right = b[1].toLowerCase();
return (left > right) ? -1 : ((left < right) ? 1 : 0);
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
});
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
results = results.reverse();
// for debugging
//Search.lastresults = results.slice(); // a copy
//console.info('search results:', Search.lastresults);
// console.info("search results:", Search.lastresults);
// print the results
var resultCount = results.length;
function displayNextItem() {
// results left, load the summary and display it
if (results.length) {
var item = results.pop();
var listItem = $('<li></li>');
var requestUrl = "";
var linkUrl = "";
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
// dirhtml builder
var dirname = item[0] + '/';
if (dirname.match(/\/index\/$/)) {
dirname = dirname.substring(0, dirname.length-6);
} else if (dirname == 'index/') {
dirname = '';
}
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
}
listItem.append($('<a/>').attr('href',
linkUrl +
highlightstring + item[2]).html(item[1]));
if (item[3]) {
listItem.append($('<span> (' + item[3] + ')</span>'));
Search.output.append(listItem);
setTimeout(function() {
displayNextItem();
}, 5);
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
$.ajax({url: requestUrl,
dataType: "text",
complete: function(jqxhr, textstatus) {
var data = jqxhr.responseText;
if (data !== '' && data !== undefined) {
var summary = Search.makeSearchSummary(data, searchterms, hlterms);
if (summary) {
listItem.append(summary);
}
}
Search.output.append(listItem);
setTimeout(function() {
displayNextItem();
}, 5);
}});
} else {
// no source available, just display title
Search.output.append(listItem);
setTimeout(function() {
displayNextItem();
}, 5);
}
}
// search finished, update title and status message
else {
Search.stopPulse();
Search.title.text(_('Search Results'));
if (!resultCount)
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
else
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
Search.status.fadeIn(500);
}
}
displayNextItem();
_displayNextItem(results, results.length, searchTerms);
},
/**
* search for object names
*/
performObjectSearch : function(object, otherterms) {
var filenames = this._index.filenames;
var docnames = this._index.docnames;
var objects = this._index.objects;
var objnames = this._index.objnames;
var titles = this._index.titles;
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
var i;
var results = [];
const results = [];
for (var prefix in objects) {
for (var iMatch = 0; iMatch != objects[prefix].length; ++iMatch) {
var match = objects[prefix][iMatch];
var name = match[4];
var fullname = (prefix ? prefix + '.' : '') + name;
var fullnameLower = fullname.toLowerCase()
if (fullnameLower.indexOf(object) > -1) {
var score = 0;
var parts = fullnameLower.split('.');
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower == object || parts[parts.length - 1] == object) {
score += Scorer.objNameMatch;
// matches in last name
} else if (parts[parts.length - 1].indexOf(object) > -1) {
score += Scorer.objPartialMatch;
}
var objname = objnames[match[1]][2];
var title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
if (otherterms.length > 0) {
var haystack = (prefix + ' ' + name + ' ' +
objname + ' ' + title).toLowerCase();
var allfound = true;
for (i = 0; i < otherterms.length; i++) {
if (haystack.indexOf(otherterms[i]) == -1) {
allfound = false;
break;
}
}
if (!allfound) {
continue;
}
}
var descr = objname + _(', in ') + title;
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
var anchor = match[3];
if (anchor === '')
anchor = fullname;
else if (anchor == '-')
anchor = objnames[match[1]][1] + '-' + fullname;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2])) {
score += Scorer.objPrio[match[2]];
} else {
score += Scorer.objPrioDefault;
}
results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
}
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
*/
escapeRegExp : function(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
},
/**
* search for full-text terms in the index
*/
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
var docnames = this._index.docnames;
var filenames = this._index.filenames;
var titles = this._index.titles;
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
var i, j, file;
var fileMap = {};
var scoreMap = {};
var results = [];
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
for (i = 0; i < searchterms.length; i++) {
var word = searchterms[i];
var files = [];
var _o = [
{files: terms[word], score: Scorer.term},
{files: titleterms[word], score: Scorer.title}
searchTerms.forEach((word) => {
const files = [];
const arr = [
{ files: terms[word], score: Scorer.term },
{ files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
var word_regex = this.escapeRegExp(word);
for (var w in terms) {
if (w.match(word_regex) && !terms[word]) {
_o.push({files: terms[w], score: Scorer.partialTerm})
}
}
for (var w in titleterms) {
if (w.match(word_regex) && !titleterms[word]) {
_o.push({files: titleterms[w], score: Scorer.partialTitle})
}
}
const escapedWord = _escapeRegExp(word);
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord) && !terms[word])
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord) && !titleTerms[word])
arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
});
}
// no match but word was a required one
if ($u.every(_o, function(o){return o.files === undefined;})) {
break;
}
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
$u.each(_o, function(o) {
var _files = o.files;
if (_files === undefined)
return
arr.forEach((record) => {
if (record.files === undefined) return;
if (_files.length === undefined)
_files = [_files];
files = files.concat(_files);
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file to Scorer.term
for (j = 0; j < _files.length; j++) {
file = _files[j];
if (!(file in scoreMap))
scoreMap[file] = {};
scoreMap[file][word] = o.score;
}
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, {});
scoreMap.get(file)[word] = record.score;
});
});
// create the mapping
for (j = 0; j < files.length; j++) {
file = files[j];
if (file in fileMap && fileMap[file].indexOf(word) === -1)
fileMap[file].push(word);
else
fileMap[file] = [word];
}
}
files.forEach((file) => {
if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
fileMap.get(file).push(word);
else fileMap.set(file, [word]);
});
});
// now check if the files don't contain excluded terms
for (file in fileMap) {
var valid = true;
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
var filteredTermCount = // as search terms with length < 3 are discarded: ignore
searchterms.filter(function(term){return term.length > 2}).length
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
fileMap[file].length != searchterms.length &&
fileMap[file].length != filteredTermCount
) continue;
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
for (i = 0; i < excluded.length; i++) {
if (terms[excluded[i]] == file ||
titleterms[excluded[i]] == file ||
$u.contains(terms[excluded[i]] || [], file) ||
$u.contains(titleterms[excluded[i]] || [], file)) {
valid = false;
break;
}
}
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// if we have still a valid result we can add it to the result list
if (valid) {
// select one (max) score for the file.
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
}
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
]);
}
return results;
},
@@ -496,34 +539,28 @@ var Search = {
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, hlwords is the list of normal, unstemmed
* words. the first one is used to find the occurrence, the
* latter for highlighting it.
* of stemmed words.
*/
makeSearchSummary : function(htmlText, keywords, hlwords) {
var text = Search.htmlToText(htmlText);
if (text == "") {
return null;
}
var textLower = text.toLowerCase();
var start = 0;
$.each(keywords, function() {
var i = textLower.indexOf(this.toLowerCase());
if (i > -1)
start = i;
});
start = Math.max(start - 120, 0);
var excerpt = ((start > 0) ? '...' : '') +
$.trim(text.substr(start, 240)) +
((start + 240 - text.length) ? '...' : '');
var rv = $('<p class="context"></p>').text(excerpt);
$.each(hlwords, function() {
rv = rv.highlightText(this, 'highlighted');
});
return rv;
}
makeSearchSummary: (htmlText, keywords) => {
const text = Search.htmlToText(htmlText);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
$(document).ready(function() {
Search.init();
});
_ready(Search.init);

296
docs/_static/skeleton.css vendored Normal file
View File

@@ -0,0 +1,296 @@
/* Some sane resets. */
html {
height: 100%;
}
body {
margin: 0;
min-height: 100%;
}
/* All the flexbox magic! */
body,
.sb-announcement,
.sb-content,
.sb-main,
.sb-container,
.sb-container__inner,
.sb-article-container,
.sb-footer-content,
.sb-header,
.sb-header-secondary,
.sb-footer {
display: flex;
}
/* These order things vertically */
body,
.sb-main,
.sb-article-container {
flex-direction: column;
}
/* Put elements in the center */
.sb-header,
.sb-header-secondary,
.sb-container,
.sb-content,
.sb-footer,
.sb-footer-content {
justify-content: center;
}
/* Put elements at the ends */
.sb-article-container {
justify-content: space-between;
}
/* These elements grow. */
.sb-main,
.sb-content,
.sb-container,
article {
flex-grow: 1;
}
/* Because padding making this wider is not fun */
article {
box-sizing: border-box;
}
/* The announcements element should never be wider than the page. */
.sb-announcement {
max-width: 100%;
}
.sb-sidebar-primary,
.sb-sidebar-secondary {
flex-shrink: 0;
width: 17rem;
}
.sb-announcement__inner {
justify-content: center;
box-sizing: border-box;
height: 3rem;
overflow-x: auto;
white-space: nowrap;
}
/* Sidebars, with checkbox-based toggle */
.sb-sidebar-primary,
.sb-sidebar-secondary {
position: fixed;
height: 100%;
top: 0;
}
.sb-sidebar-primary {
left: -17rem;
transition: left 250ms ease-in-out;
}
.sb-sidebar-secondary {
right: -17rem;
transition: right 250ms ease-in-out;
}
.sb-sidebar-toggle {
display: none;
}
.sb-sidebar-overlay {
position: fixed;
top: 0;
width: 0;
height: 0;
transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease;
opacity: 0;
background-color: rgba(0, 0, 0, 0.54);
}
#sb-sidebar-toggle--primary:checked
~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"],
#sb-sidebar-toggle--secondary:checked
~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] {
width: 100%;
height: 100%;
opacity: 1;
transition: width 0ms ease, height 0ms ease, opacity 250ms ease;
}
#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary {
left: 0;
}
#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary {
right: 0;
}
/* Full-width mode */
.drop-secondary-sidebar-for-full-width-content
.hide-when-secondary-sidebar-shown {
display: none !important;
}
.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary {
display: none !important;
}
/* Mobile views */
.sb-page-width {
width: 100%;
}
.sb-article-container,
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 100vw;
}
.sb-article,
.match-content-width {
padding: 0 1rem;
box-sizing: border-box;
}
@media (min-width: 32rem) {
.sb-article,
.match-content-width {
padding: 0 2rem;
}
}
/* Tablet views */
@media (min-width: 42rem) {
.sb-article-container {
width: auto;
}
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 42rem;
}
.sb-article,
.match-content-width {
width: 42rem;
}
}
@media (min-width: 46rem) {
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 46rem;
}
.sb-article,
.match-content-width {
width: 46rem;
}
}
@media (min-width: 50rem) {
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 50rem;
}
.sb-article,
.match-content-width {
width: 50rem;
}
}
/* Tablet views */
@media (min-width: 59rem) {
.sb-sidebar-secondary {
position: static;
}
.hide-when-secondary-sidebar-shown {
display: none !important;
}
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 59rem;
}
.sb-article,
.match-content-width {
width: 42rem;
}
}
@media (min-width: 63rem) {
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 63rem;
}
.sb-article,
.match-content-width {
width: 46rem;
}
}
@media (min-width: 67rem) {
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 67rem;
}
.sb-article,
.match-content-width {
width: 50rem;
}
}
/* Desktop views */
@media (min-width: 76rem) {
.sb-sidebar-primary {
position: static;
}
.hide-when-primary-sidebar-shown {
display: none !important;
}
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 59rem;
}
.sb-article,
.match-content-width {
width: 42rem;
}
}
/* Full desktop views */
@media (min-width: 80rem) {
.sb-article,
.match-content-width {
width: 46rem;
}
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 63rem;
}
}
@media (min-width: 84rem) {
.sb-article,
.match-content-width {
width: 50rem;
}
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 67rem;
}
}
@media (min-width: 88rem) {
.sb-footer-content__inner,
.drop-secondary-sidebar-for-full-width-content .sb-article,
.drop-secondary-sidebar-for-full-width-content .match-content-width {
width: 67rem;
}
.sb-page-width {
width: 88rem;
}
}

144
docs/_static/sphinx_highlight.js vendored Normal file
View File

@@ -0,0 +1,144 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";
const SPHINX_HIGHLIGHT_ENABLED = true
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
parent.insertBefore(
span,
parent.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const SphinxHighlight = {
/**
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms")
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
// get individual terms from highlight string
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
localStorage.removeItem("sphinx_highlight_terms")
},
initEscapeListener: () => {
// only install a listener if it is really needed
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};
_ready(SphinxHighlight.highlightSearchWords);
_ready(SphinxHighlight.initEscapeListener);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -145,7 +145,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -178,7 +178,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -269,6 +270,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-album">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-a">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-album">osxphotos-query command line option</a>
</li>
@@ -282,6 +285,13 @@
<li><a href="cli.html#cmdoption-osxphotos-exiftool-album-keyword">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-album-keyword">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
--alt-copy
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-alt-copy">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
@@ -293,6 +303,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-append">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-append">osxphotos-exportdb command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-append">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -311,6 +323,13 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-check-signatures">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
--check-templates
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-check-templates">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -318,6 +337,20 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-cleanup">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
--clear-location
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-L">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
--clear-metadata
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-C">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -408,6 +441,8 @@
<li><a href="cli.html#cmdoption-osxphotos-keywords-db">osxphotos-keywords command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-db">osxphotos-labels command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-db">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-db">osxphotos-persons command line option</a>
</li>
@@ -486,6 +521,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-description">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-d">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-description">osxphotos-query command line option</a>
</li>
@@ -531,6 +568,13 @@
<li><a href="cli.html#cmdoption-osxphotos-export-dry-run">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-dry-run">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
--dup-check
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-D">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -592,6 +636,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-e">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -628,8 +674,17 @@
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-path">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-path">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-0">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-e">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
--export
<ul>
<li><a href="cli.html#cmdoption-osxphotos-orphans-export">osxphotos-orphans command line option</a>
</li>
</ul></li>
<li>
@@ -682,6 +737,22 @@
<li><a href="cli.html#cmdoption-osxphotos-query-favorite">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-favorite">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--favorite-rating
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-favorite-rating">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
--field
<ul>
<li><a href="cli.html#cmdoption-osxphotos-dump-f">osxphotos-dump command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-f">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
@@ -759,6 +830,13 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-F">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
--glob
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-g">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -932,6 +1010,13 @@
<li><a href="cli.html#cmdoption-osxphotos-places-json">osxphotos-places command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-json">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
--keep
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-keep">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
@@ -939,6 +1024,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-keyword">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-k">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-keyword">osxphotos-query command line option</a>
</li>
@@ -1018,6 +1105,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-location">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-l">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-location">osxphotos-query command line option</a>
</li>
@@ -1040,6 +1129,13 @@
<li><a href="cli.html#cmdoption-osxphotos-query-max-size">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-max-size">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--merge-keywords
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-m">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1153,6 +1249,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-no-progress">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-no-progress">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1166,8 +1264,6 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-no-title">osxphotos-repl command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-burst
@@ -1210,6 +1306,8 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-not-hdr">osxphotos-repl command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-hidden
@@ -1281,6 +1379,17 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-portrait">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-portrait">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--not-reference
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-not-reference">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-reference">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-reference">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
@@ -1397,6 +1506,13 @@
<li><a href="cli.html#cmdoption-osxphotos-query-panorama">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-panorama">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--parse-date
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-P">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1460,6 +1576,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-post-function">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-post-function">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1483,6 +1601,17 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-preview-suffix">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
--print
<ul>
<li><a href="cli.html#cmdoption-osxphotos-dump-print">osxphotos-dump command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-print">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-print">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
@@ -1519,6 +1648,13 @@
<li><a href="cli.html#cmdoption-osxphotos-query-query-function">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-query-function">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--quiet
<ul>
<li><a href="cli.html#cmdoption-osxphotos-query-quiet">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
@@ -1544,6 +1680,13 @@
<li><a href="cli.html#cmdoption-osxphotos-query-regex">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-regex">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--relative-to
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-r">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1564,6 +1707,15 @@
<li><a href="cli.html#cmdoption-osxphotos-export-report">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-report">osxphotos-exportdb command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-report">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
--resume
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-R">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1707,6 +1859,13 @@
<li><a href="cli.html#cmdoption-osxphotos-query-slow-mo">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-slow-mo">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--split-folder
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-f">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1744,8 +1903,12 @@
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-theme">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-theme">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-theme">osxphotos-inspect command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-theme">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-theme">osxphotos-timewarp command line option</a>
</li>
@@ -1782,6 +1945,10 @@
<li><a href="cli.html#cmdoption-osxphotos-exiftool-timestamp">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-timestamp">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-T">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-timestamp">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-timestamp">osxphotos-timewarp command line option</a>
</li>
@@ -1798,6 +1965,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-title">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-t">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-title">osxphotos-query command line option</a>
</li>
@@ -1949,6 +2118,10 @@
<li><a href="cli.html#cmdoption-osxphotos-export-V">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-V">osxphotos-exportdb command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-V">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-V">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-V">osxphotos-timewarp command line option</a>
</li>
@@ -1960,6 +2133,13 @@
<li><a href="cli.html#cmdoption-osxphotos-v">osxphotos command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-version">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
--walk
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-w">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -1991,7 +2171,16 @@
-a
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-a">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-a">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
-C
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-C">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -2005,6 +2194,8 @@
-D
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-D">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-D">osxphotos-timewarp command line option</a>
</li>
</ul></li>
@@ -2012,6 +2203,8 @@
-d
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-d">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-d">osxphotos-timewarp command line option</a>
</li>
</ul></li>
@@ -2019,6 +2212,8 @@
-e
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-e">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-e">osxphotos-timewarp command line option</a>
</li>
</ul></li>
@@ -2033,9 +2228,22 @@
-f
<ul>
<li><a href="cli.html#cmdoption-osxphotos-dump-f">osxphotos-dump command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-f">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-f">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-0">osxphotos-timewarp command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-uuid-f">osxphotos-uuid command line option</a>
</li>
</ul></li>
<li>
-g
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-g">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -2056,19 +2264,37 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-i">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-i">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
-k
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-k">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
-L
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-L">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-L">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
-l
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-l">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
-m
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-m">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-m">osxphotos-timewarp command line option</a>
</li>
</ul></li>
@@ -2083,6 +2309,8 @@
-P
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-P">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-P">osxphotos-timewarp command line option</a>
</li>
</ul></li>
@@ -2090,7 +2318,16 @@
-p
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-0">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-p">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
-R
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-R">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -2098,6 +2335,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-diff-r">osxphotos-diff command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-r">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -2111,6 +2350,8 @@
-T
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-T">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-T">osxphotos-inspect command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-T">osxphotos-timewarp command line option</a>
@@ -2120,6 +2361,8 @@
-t
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-t">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-t">osxphotos-inspect command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-t">osxphotos-timewarp command line option</a>
@@ -2143,6 +2386,10 @@
<li><a href="cli.html#cmdoption-osxphotos-export-V">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-V">osxphotos-exportdb command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-V">osxphotos-import command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-V">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-V">osxphotos-timewarp command line option</a>
</li>
@@ -2152,6 +2399,13 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-v">osxphotos command line option</a>
</li>
</ul></li>
<li>
-w
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-w">osxphotos-import command line option</a>
</li>
</ul></li>
<li>
@@ -2241,6 +2495,8 @@
<li><a href="reference.html#osxphotos.SearchInfo.asdict">(osxphotos.SearchInfo method)</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.ExportResults.attributes">attributes (osxphotos.ExportResults property)</a>
</li>
</ul></td>
</tr></table>
</section>
@@ -2285,6 +2541,8 @@
<h2>C</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.SearchInfo.camera">camera (osxphotos.SearchInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.SearchInfo.city">city (osxphotos.SearchInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.ExportDB.close">close() (osxphotos.ExportDB method)</a>
@@ -2294,11 +2552,11 @@
<li><a href="reference.html#osxphotos.QueryOptions.cloudasset">cloudasset (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.CommentInfo">CommentInfo (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.comments">comments (osxphotos.PhotoInfo property)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotoInfo.comments">comments (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.ExportOptions.convert_to_jpeg">convert_to_jpeg (osxphotos.ExportOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.FileUtilNoOp.convert_to_jpeg">convert_to_jpeg() (osxphotos.FileUtilNoOp class method)</a>
@@ -2330,6 +2588,8 @@
<li><a href="reference.html#osxphotos.PhotoInfo.date_modified">date_modified (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.date_trashed">date_trashed (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.ExportResults.datetime">datetime (osxphotos.ExportResults property)</a>
</li>
<li>
DB2
@@ -2367,6 +2627,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-arg-DEST">osxphotos-export command line option</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.SearchInfo.detected_text">detected_text (osxphotos.SearchInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.detected_text">detected_text() (osxphotos.PhotoInfo method)</a>
</li>
<li><a href="reference.html#osxphotos.ExportOptions.download_missing">download_missing (osxphotos.ExportOptions attribute)</a>
@@ -2481,17 +2743,28 @@
<li><a href="reference.html#osxphotos.QueryOptions.favorite">(osxphotos.QueryOptions attribute)</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.ExportOptions.favorite_rating">favorite_rating (osxphotos.ExportOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.FileUtilNoOp.file_sig">file_sig() (osxphotos.FileUtilNoOp class method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.filename">filename (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.FileUtil">FileUtil (class in osxphotos)</a>
<li>
FILES
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-arg-FILES">osxphotos-import command line option</a>
</li>
<li><a href="reference.html#osxphotos.ExportOptions.fileutil">fileutil (osxphotos.ExportOptions attribute)</a>
</ul></li>
<li><a href="reference.html#osxphotos.FileUtil">FileUtil (class in osxphotos)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.ExportOptions.fileutil">fileutil (osxphotos.ExportOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.FileUtilNoOp">FileUtilNoOp (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.filter_predicate">filter_predicate() (osxphotos.PhotoTemplate method)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.folder">folder (osxphotos.QueryOptions attribute)</a>
</li>
@@ -2832,10 +3105,10 @@
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_favorite">not_favorite (osxphotos.QueryOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.QueryOptions.not_hdr">not_hdr (osxphotos.QueryOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.QueryOptions.not_hidden">not_hidden (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_in_album">not_in_album (osxphotos.QueryOptions attribute)</a>
@@ -2849,6 +3122,8 @@
<li><a href="reference.html#osxphotos.QueryOptions.not_panorama">not_panorama (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_portrait">not_portrait (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_reference">not_reference (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_screenshot">not_screenshot (osxphotos.QueryOptions attribute)</a>
</li>
@@ -2941,8 +3216,14 @@
<li><a href="cli.html#cmdoption-osxphotos-dump-deleted">--deleted</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-deleted-only">--deleted-only</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-f">--field</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-print">--print</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-f">-f</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
@@ -3017,6 +3298,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-album">--album</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-album-keyword">--album-keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-alt-copy">--alt-copy</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-append">--append</a>
</li>
@@ -3073,6 +3356,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-external-edit">--external-edit</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-favorite">--favorite</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-favorite-rating">--favorite-rating</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-filename">--filename</a>
</li>
@@ -3111,6 +3396,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-jpeg-ext">--jpeg-ext</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-jpeg-quality">--jpeg-quality</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-keep">--keep</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-keyword">--keyword</a>
</li>
@@ -3165,6 +3452,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-not-panorama">--not-panorama</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-portrait">--not-portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-reference">--not-reference</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-screenshot">--not-screenshot</a>
</li>
@@ -3205,6 +3494,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-preview-if-missing">--preview-if-missing</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-preview-suffix">--preview-suffix</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-print">--print</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-query-eval">--query-eval</a>
</li>
@@ -3352,6 +3643,103 @@
</li>
</ul></li>
<li>
osxphotos-import command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-a">--album</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-append">--append</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-check-templates">--check-templates</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-L">--clear-location</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-C">--clear-metadata</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-d">--description</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-D">--dup-check</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-e">--exiftool</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-0">--exiftool-path</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-g">--glob</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-k">--keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-l">--location</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-m">--merge-keywords</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-no-progress">--no-progress</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-P">--parse-date</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-post-function">--post-function</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-r">--relative-to</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-report">--report</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-R">--resume</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-f">--split-folder</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-theme">--theme</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-T">--timestamp</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-t">--title</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-V">--verbose</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-w">--walk</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-a">-a</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-C">-C</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-d">-d</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-D">-D</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-e">-e</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-f">-f</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-g">-g</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-k">-k</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-l">-l</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-L">-L</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-m">-m</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-P">-P</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-0">-p</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-r">-r</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-R">-R</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-t">-t</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-T">-T</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-V">-V</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-w">-w</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-arg-FILES">FILES</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-info command line option
<ul>
@@ -3412,13 +3800,28 @@
<li><a href="cli.html#cmdoption-osxphotos-labels-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-list command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-list-json">--json</a>
</li>
</ul></li>
<li>
osxphotos-orphans command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-orphans-db">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-export">--export</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-theme">--theme</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-timestamp">--timestamp</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-V">--verbose</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-V">-V</a>
</li>
</ul></li>
<li>
@@ -3478,6 +3881,8 @@
<li><a href="cli.html#cmdoption-osxphotos-query-external-edit">--external-edit</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-favorite">--favorite</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-f">--field</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-folder">--folder</a>
</li>
@@ -3556,6 +3961,8 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-panorama">--not-panorama</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-portrait">--not-portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-reference">--not-reference</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-screenshot">--not-screenshot</a>
</li>
@@ -3578,10 +3985,14 @@
<li><a href="cli.html#cmdoption-osxphotos-query-place">--place</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-portrait">--portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-print">--print</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-query-eval">--query-eval</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-query-function">--query-function</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-quiet">--quiet</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-regex">--regex</a>
</li>
@@ -3610,6 +4021,8 @@
<li><a href="cli.html#cmdoption-osxphotos-query-uuid-from-file">--uuid-from-file</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-year">--year</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-f">-f</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-i">-i</a>
</li>
@@ -3727,6 +4140,8 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-not-panorama">--not-panorama</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-portrait">--not-portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-reference">--not-reference</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-screenshot">--not-screenshot</a>
</li>
@@ -4055,6 +4470,10 @@
<li><a href="cli.html#cmdoption-osxphotos-query-arg-PHOTOS_LIBRARY">osxphotos-query command line option</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.PhotosAlbum">PhotosAlbum (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosAlbumPhotoScript">PhotosAlbumPhotoScript (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosDB">PhotosDB (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate">PhotoTemplate (class in osxphotos)</a>
@@ -4418,7 +4837,9 @@
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

View File

@@ -1,14 +1,14 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="#"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -135,7 +135,7 @@
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon" for="__toc">
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -180,7 +180,9 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -188,14 +190,14 @@
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon" for="__toc">
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<section id="welcome-to-osxphotos-s-documentation">
<h1>Welcome to OSXPhotoss documentation!<a class="headerlink" href="#welcome-to-osxphotos-s-documentation" title="Permalink to this headline">#</a></h1>
<h1>Welcome to OSXPhotoss documentation!<a class="headerlink" href="#welcome-to-osxphotos-s-documentation" title="Permalink to this heading">#</a></h1>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="overview.html">OSXPhotos</a><ul>
@@ -242,12 +244,14 @@
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-export">export</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-exportdb">exportdb</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-help">help</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-import">import</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-info">info</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-inspect">inspect</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-install">install</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-keywords">keywords</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-labels">labels</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-list">list</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-orphans">orphans</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-persons">persons</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-places">places</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-query">query</a></li>
@@ -274,12 +278,419 @@
<li class="toctree-l2"><a class="reference internal" href="package_overview.html#using-the-osxphotos-cli-to-run-python-code">Using the osxphotos CLI to run python code</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos python API</a><ul>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo"><code class="docutils literal notranslate"><span class="pre">AlbumInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.folder_list"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.folder_list</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.folder_names"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.folder_names</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.parent"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.parent</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.photo_index"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.photo_index()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.photos"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.photos</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.sort_order"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.sort_order</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.title"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.title</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.AlbumSortOrder"><code class="docutils literal notranslate"><span class="pre">AlbumSortOrder</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.CommentInfo"><code class="docutils literal notranslate"><span class="pre">CommentInfo</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ExifInfo"><code class="docutils literal notranslate"><span class="pre">ExifInfo</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ExifTool"><code class="docutils literal notranslate"><span class="pre">ExifTool</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.addvalues"><code class="docutils literal notranslate"><span class="pre">ExifTool.addvalues()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.asdict"><code class="docutils literal notranslate"><span class="pre">ExifTool.asdict()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.json"><code class="docutils literal notranslate"><span class="pre">ExifTool.json()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.pid"><code class="docutils literal notranslate"><span class="pre">ExifTool.pid</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.run_commands"><code class="docutils literal notranslate"><span class="pre">ExifTool.run_commands()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.setvalue"><code class="docutils literal notranslate"><span class="pre">ExifTool.setvalue()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExifTool.version"><code class="docutils literal notranslate"><span class="pre">ExifTool.version</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ExportDB"><code class="docutils literal notranslate"><span class="pre">ExportDB</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.close"><code class="docutils literal notranslate"><span class="pre">ExportDB.close()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.create_file_record"><code class="docutils literal notranslate"><span class="pre">ExportDB.create_file_record()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.create_or_get_file_record"><code class="docutils literal notranslate"><span class="pre">ExportDB.create_or_get_file_record()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.delete_data_for_filepath"><code class="docutils literal notranslate"><span class="pre">ExportDB.delete_data_for_filepath()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.delete_data_for_uuid"><code class="docutils literal notranslate"><span class="pre">ExportDB.delete_data_for_uuid()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.export_dir"><code class="docutils literal notranslate"><span class="pre">ExportDB.export_dir</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_export_results"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_export_results()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_exported_files"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_exported_files()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_file_record"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_file_record()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_files_for_uuid"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_files_for_uuid()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_photoinfo_for_uuid"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_photoinfo_for_uuid()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_previous_uuids"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_previous_uuids()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_target_for_file"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_target_for_file()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.get_uuid_for_file"><code class="docutils literal notranslate"><span class="pre">ExportDB.get_uuid_for_file()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.path"><code class="docutils literal notranslate"><span class="pre">ExportDB.path</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.set_config"><code class="docutils literal notranslate"><span class="pre">ExportDB.set_config()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.set_export_results"><code class="docutils literal notranslate"><span class="pre">ExportDB.set_export_results()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportDB.set_photoinfo_for_uuid"><code class="docutils literal notranslate"><span class="pre">ExportDB.set_photoinfo_for_uuid()</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ExportDBTemp"><code class="docutils literal notranslate"><span class="pre">ExportDBTemp</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ExportOptions"><code class="docutils literal notranslate"><span class="pre">ExportOptions</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.convert_to_jpeg"><code class="docutils literal notranslate"><span class="pre">ExportOptions.convert_to_jpeg</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.description_template"><code class="docutils literal notranslate"><span class="pre">ExportOptions.description_template</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.download_missing"><code class="docutils literal notranslate"><span class="pre">ExportOptions.download_missing</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.dry_run"><code class="docutils literal notranslate"><span class="pre">ExportOptions.dry_run</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.edited"><code class="docutils literal notranslate"><span class="pre">ExportOptions.edited</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.exiftool_flags"><code class="docutils literal notranslate"><span class="pre">ExportOptions.exiftool_flags</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.exiftool"><code class="docutils literal notranslate"><span class="pre">ExportOptions.exiftool</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.export_as_hardlink"><code class="docutils literal notranslate"><span class="pre">ExportOptions.export_as_hardlink</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.export_db"><code class="docutils literal notranslate"><span class="pre">ExportOptions.export_db</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.face_regions"><code class="docutils literal notranslate"><span class="pre">ExportOptions.face_regions</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.fileutil"><code class="docutils literal notranslate"><span class="pre">ExportOptions.fileutil</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.force_update"><code class="docutils literal notranslate"><span class="pre">ExportOptions.force_update</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.ignore_date_modified"><code class="docutils literal notranslate"><span class="pre">ExportOptions.ignore_date_modified</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.ignore_signature"><code class="docutils literal notranslate"><span class="pre">ExportOptions.ignore_signature</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.increment"><code class="docutils literal notranslate"><span class="pre">ExportOptions.increment</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.jpeg_ext"><code class="docutils literal notranslate"><span class="pre">ExportOptions.jpeg_ext</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.jpeg_quality"><code class="docutils literal notranslate"><span class="pre">ExportOptions.jpeg_quality</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.keyword_template"><code class="docutils literal notranslate"><span class="pre">ExportOptions.keyword_template</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.live_photo"><code class="docutils literal notranslate"><span class="pre">ExportOptions.live_photo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.location"><code class="docutils literal notranslate"><span class="pre">ExportOptions.location</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.merge_exif_keywords"><code class="docutils literal notranslate"><span class="pre">ExportOptions.merge_exif_keywords</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.merge_exif_persons"><code class="docutils literal notranslate"><span class="pre">ExportOptions.merge_exif_persons</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.overwrite"><code class="docutils literal notranslate"><span class="pre">ExportOptions.overwrite</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.persons"><code class="docutils literal notranslate"><span class="pre">ExportOptions.persons</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.preview_suffix"><code class="docutils literal notranslate"><span class="pre">ExportOptions.preview_suffix</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.preview"><code class="docutils literal notranslate"><span class="pre">ExportOptions.preview</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.raw_photo"><code class="docutils literal notranslate"><span class="pre">ExportOptions.raw_photo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.render_options"><code class="docutils literal notranslate"><span class="pre">ExportOptions.render_options</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.replace_keywords"><code class="docutils literal notranslate"><span class="pre">ExportOptions.replace_keywords</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.rich"><code class="docutils literal notranslate"><span class="pre">ExportOptions.rich</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.sidecar_drop_ext"><code class="docutils literal notranslate"><span class="pre">ExportOptions.sidecar_drop_ext</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.sidecar"><code class="docutils literal notranslate"><span class="pre">ExportOptions.sidecar</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.strip"><code class="docutils literal notranslate"><span class="pre">ExportOptions.strip</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.timeout"><code class="docutils literal notranslate"><span class="pre">ExportOptions.timeout</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.touch_file"><code class="docutils literal notranslate"><span class="pre">ExportOptions.touch_file</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.update"><code class="docutils literal notranslate"><span class="pre">ExportOptions.update</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.use_albums_as_keywords"><code class="docutils literal notranslate"><span class="pre">ExportOptions.use_albums_as_keywords</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.use_persons_as_keywords"><code class="docutils literal notranslate"><span class="pre">ExportOptions.use_persons_as_keywords</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.use_photos_export"><code class="docutils literal notranslate"><span class="pre">ExportOptions.use_photos_export</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.use_photokit"><code class="docutils literal notranslate"><span class="pre">ExportOptions.use_photokit</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.verbose"><code class="docutils literal notranslate"><span class="pre">ExportOptions.verbose</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.tmpdir"><code class="docutils literal notranslate"><span class="pre">ExportOptions.tmpdir</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.favorite_rating"><code class="docutils literal notranslate"><span class="pre">ExportOptions.favorite_rating</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportOptions.bit_flags"><code class="docutils literal notranslate"><span class="pre">ExportOptions.bit_flags</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ExportResults"><code class="docutils literal notranslate"><span class="pre">ExportResults</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportResults.all_files"><code class="docutils literal notranslate"><span class="pre">ExportResults.all_files()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportResults.attributes"><code class="docutils literal notranslate"><span class="pre">ExportResults.attributes</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ExportResults.datetime"><code class="docutils literal notranslate"><span class="pre">ExportResults.datetime</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.FileUtil"><code class="docutils literal notranslate"><span class="pre">FileUtil</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.convert_to_jpeg"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.convert_to_jpeg()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.copy"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.copy()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.file_sig"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.file_sig()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.hardlink"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.hardlink()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.rename"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.rename()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.rmdir"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.rmdir()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.tmpdir"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.tmpdir()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.unlink"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.unlink()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FileUtilNoOp.utime"><code class="docutils literal notranslate"><span class="pre">FileUtilNoOp.utime()</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.FolderInfo"><code class="docutils literal notranslate"><span class="pre">FolderInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FolderInfo.album_info"><code class="docutils literal notranslate"><span class="pre">FolderInfo.album_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FolderInfo.parent"><code class="docutils literal notranslate"><span class="pre">FolderInfo.parent</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FolderInfo.subfolders"><code class="docutils literal notranslate"><span class="pre">FolderInfo.subfolders</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FolderInfo.title"><code class="docutils literal notranslate"><span class="pre">FolderInfo.title</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.FolderInfo.uuid"><code class="docutils literal notranslate"><span class="pre">FolderInfo.uuid</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ImportInfo"><code class="docutils literal notranslate"><span class="pre">ImportInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ImportInfo.photos"><code class="docutils literal notranslate"><span class="pre">ImportInfo.photos</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.LikeInfo"><code class="docutils literal notranslate"><span class="pre">LikeInfo</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.MomentInfo"><code class="docutils literal notranslate"><span class="pre">MomentInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.asdict"><code class="docutils literal notranslate"><span class="pre">MomentInfo.asdict()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.date"><code class="docutils literal notranslate"><span class="pre">MomentInfo.date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.end_date"><code class="docutils literal notranslate"><span class="pre">MomentInfo.end_date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.location"><code class="docutils literal notranslate"><span class="pre">MomentInfo.location</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.modification_date"><code class="docutils literal notranslate"><span class="pre">MomentInfo.modification_date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.photos"><code class="docutils literal notranslate"><span class="pre">MomentInfo.photos</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.pk"><code class="docutils literal notranslate"><span class="pre">MomentInfo.pk</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.start_date"><code class="docutils literal notranslate"><span class="pre">MomentInfo.start_date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.subtitle"><code class="docutils literal notranslate"><span class="pre">MomentInfo.subtitle</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.MomentInfo.title"><code class="docutils literal notranslate"><span class="pre">MomentInfo.title</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PersonInfo"><code class="docutils literal notranslate"><span class="pre">PersonInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PersonInfo.asdict"><code class="docutils literal notranslate"><span class="pre">PersonInfo.asdict()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PersonInfo.face_info"><code class="docutils literal notranslate"><span class="pre">PersonInfo.face_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PersonInfo.json"><code class="docutils literal notranslate"><span class="pre">PersonInfo.json()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PersonInfo.photos"><code class="docutils literal notranslate"><span class="pre">PersonInfo.photos</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PhotoExporter"><code class="docutils literal notranslate"><span class="pre">PhotoExporter</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar"><code class="docutils literal notranslate"><span class="pre">PhotoExporter.exiftool_json_sidecar()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoExporter.export"><code class="docutils literal notranslate"><span class="pre">PhotoExporter.export()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoExporter.write_exiftool_metadata_to_file"><code class="docutils literal notranslate"><span class="pre">PhotoExporter.write_exiftool_metadata_to_file()</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.adjustments"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.adjustments</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.album_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.album_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.albums"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.albums</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.asdict"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.asdict()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst_album_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_album_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst_albums"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_albums</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst_default_pick"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_default_pick</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst_key"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_key</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst_photos"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_photos</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.burst_selected"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_selected</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.cloud_metadata"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.cloud_metadata</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.comments"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.comments</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.date"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.date_added"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.date_added</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.date_modified"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.date_modified</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.date_trashed"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.date_trashed</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.description"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.description</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.detected_text"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.detected_text()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.duplicates"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.duplicates</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.exif_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.exif_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.exiftool"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.exiftool</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.export"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.export()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.external_edit"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.external_edit</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.face_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.face_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.favorite"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.favorite</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.filename"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.filename</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.has_raw"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.has_raw</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.hasadjustments"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.hasadjustments</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.hdr"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.hdr</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.height"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.height</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.hexdigest"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.hexdigest</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.hidden"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.hidden</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.import_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.import_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.incloud"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.incloud</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.intrash"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.intrash</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.iscloudasset"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.iscloudasset</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.ismissing"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.ismissing</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.ismovie"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.ismovie</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.isphoto"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.isphoto</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.israw"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.israw</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.isreference"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.isreference</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.json"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.json()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.keywords"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.keywords</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.labels"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.labels</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.labels_normalized"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.labels_normalized</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.likes"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.likes</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.live_photo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.live_photo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.location"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.location</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.moment_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.moment_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.orientation"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.orientation</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.original_filename"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.original_filename</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.original_filesize"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.original_filesize</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.original_height"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.original_height</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.original_orientation"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.original_orientation</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.original_width"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.original_width</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.owner"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.owner</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.panorama"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.panorama</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.path"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.path</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.path_derivatives"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.path_derivatives</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.path_edited"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.path_edited</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.path_edited_live_photo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.path_edited_live_photo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.path_live_photo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.path_live_photo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.path_raw"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.path_raw</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.person_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.person_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.persons"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.persons</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.place"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.place</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.portrait"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.portrait</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.project_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.project_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.raw_original"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.raw_original</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.render_template"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.render_template()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.score"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.score</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.screenshot"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.screenshot</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.search_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.search_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.search_info_normalized"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.search_info_normalized</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.selfie"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.selfie</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.shared"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.shared</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.slow_mo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.slow_mo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.time_lapse"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.time_lapse</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.title"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.title</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.tzoffset"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.tzoffset</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.uti"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.uti</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.uti_edited"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.uti_edited</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.uti_original"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.uti_original</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.uti_raw"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.uti_raw</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.uuid"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.uuid</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.visible"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.visible</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.width"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.width</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.expand_variables"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.expand_variables()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.expand_variables_to_str"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.expand_variables_to_str()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.filter_predicate"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.filter_predicate()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_field_values"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_field_values()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_filter_values"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_filter_values()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_format_values"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_format_values()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_media_type"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_media_type()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_photo_bool_attribute"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_photo_bool_attribute()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_photo_video_type"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_photo_video_type()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_template_value"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_template_value()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_template_value_exiftool"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_template_value_exiftool()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_template_value_filter_function"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_template_value_filter_function()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_template_value_function"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_template_value_function()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_template_value_multi"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_template_value_multi()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.get_template_value_pathlib"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.get_template_value_pathlib()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoTemplate.render"><code class="docutils literal notranslate"><span class="pre">PhotoTemplate.render()</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PhotosAlbum"><code class="docutils literal notranslate"><span class="pre">PhotosAlbum</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PhotosAlbumPhotoScript"><code class="docutils literal notranslate"><span class="pre">PhotosAlbumPhotoScript</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PhotosDB"><code class="docutils literal notranslate"><span class="pre">PhotosDB</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.album_info"><code class="docutils literal notranslate"><span class="pre">PhotosDB.album_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.album_info_shared"><code class="docutils literal notranslate"><span class="pre">PhotosDB.album_info_shared</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.albums"><code class="docutils literal notranslate"><span class="pre">PhotosDB.albums</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.albums_as_dict"><code class="docutils literal notranslate"><span class="pre">PhotosDB.albums_as_dict</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.albums_shared"><code class="docutils literal notranslate"><span class="pre">PhotosDB.albums_shared</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.albums_shared_as_dict"><code class="docutils literal notranslate"><span class="pre">PhotosDB.albums_shared_as_dict</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.db_path"><code class="docutils literal notranslate"><span class="pre">PhotosDB.db_path</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.db_version"><code class="docutils literal notranslate"><span class="pre">PhotosDB.db_version</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.execute"><code class="docutils literal notranslate"><span class="pre">PhotosDB.execute()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.folder_info"><code class="docutils literal notranslate"><span class="pre">PhotosDB.folder_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.folders"><code class="docutils literal notranslate"><span class="pre">PhotosDB.folders</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.get_db_connection"><code class="docutils literal notranslate"><span class="pre">PhotosDB.get_db_connection()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.get_photo"><code class="docutils literal notranslate"><span class="pre">PhotosDB.get_photo()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.import_info"><code class="docutils literal notranslate"><span class="pre">PhotosDB.import_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.keywords"><code class="docutils literal notranslate"><span class="pre">PhotosDB.keywords</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.keywords_as_dict"><code class="docutils literal notranslate"><span class="pre">PhotosDB.keywords_as_dict</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.labels"><code class="docutils literal notranslate"><span class="pre">PhotosDB.labels</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.labels_as_dict"><code class="docutils literal notranslate"><span class="pre">PhotosDB.labels_as_dict</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.labels_normalized"><code class="docutils literal notranslate"><span class="pre">PhotosDB.labels_normalized</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.labels_normalized_as_dict"><code class="docutils literal notranslate"><span class="pre">PhotosDB.labels_normalized_as_dict</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.library_path"><code class="docutils literal notranslate"><span class="pre">PhotosDB.library_path</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.person_info"><code class="docutils literal notranslate"><span class="pre">PhotosDB.person_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.persons"><code class="docutils literal notranslate"><span class="pre">PhotosDB.persons</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.persons_as_dict"><code class="docutils literal notranslate"><span class="pre">PhotosDB.persons_as_dict</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.photos"><code class="docutils literal notranslate"><span class="pre">PhotosDB.photos()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.photos_by_uuid"><code class="docutils literal notranslate"><span class="pre">PhotosDB.photos_by_uuid()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.project_info"><code class="docutils literal notranslate"><span class="pre">PhotosDB.project_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotosDB.query"><code class="docutils literal notranslate"><span class="pre">PhotosDB.query()</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.PlaceInfo"><code class="docutils literal notranslate"><span class="pre">PlaceInfo</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ProjectInfo"><code class="docutils literal notranslate"><span class="pre">ProjectInfo</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.QueryOptions"><code class="docutils literal notranslate"><span class="pre">QueryOptions</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.added_after"><code class="docutils literal notranslate"><span class="pre">QueryOptions.added_after</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.added_before"><code class="docutils literal notranslate"><span class="pre">QueryOptions.added_before</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.added_in_last"><code class="docutils literal notranslate"><span class="pre">QueryOptions.added_in_last</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.album"><code class="docutils literal notranslate"><span class="pre">QueryOptions.album</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.burst_photos"><code class="docutils literal notranslate"><span class="pre">QueryOptions.burst_photos</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.burst"><code class="docutils literal notranslate"><span class="pre">QueryOptions.burst</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.cloudasset"><code class="docutils literal notranslate"><span class="pre">QueryOptions.cloudasset</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.deleted_only"><code class="docutils literal notranslate"><span class="pre">QueryOptions.deleted_only</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.deleted"><code class="docutils literal notranslate"><span class="pre">QueryOptions.deleted</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.description"><code class="docutils literal notranslate"><span class="pre">QueryOptions.description</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.duplicate"><code class="docutils literal notranslate"><span class="pre">QueryOptions.duplicate</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.edited"><code class="docutils literal notranslate"><span class="pre">QueryOptions.edited</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.exif"><code class="docutils literal notranslate"><span class="pre">QueryOptions.exif</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.external_edit"><code class="docutils literal notranslate"><span class="pre">QueryOptions.external_edit</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.favorite"><code class="docutils literal notranslate"><span class="pre">QueryOptions.favorite</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.folder"><code class="docutils literal notranslate"><span class="pre">QueryOptions.folder</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.from_date"><code class="docutils literal notranslate"><span class="pre">QueryOptions.from_date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.function"><code class="docutils literal notranslate"><span class="pre">QueryOptions.function</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.has_comment"><code class="docutils literal notranslate"><span class="pre">QueryOptions.has_comment</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.has_likes"><code class="docutils literal notranslate"><span class="pre">QueryOptions.has_likes</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.has_raw"><code class="docutils literal notranslate"><span class="pre">QueryOptions.has_raw</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.hdr"><code class="docutils literal notranslate"><span class="pre">QueryOptions.hdr</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.hidden"><code class="docutils literal notranslate"><span class="pre">QueryOptions.hidden</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.ignore_case"><code class="docutils literal notranslate"><span class="pre">QueryOptions.ignore_case</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.in_album"><code class="docutils literal notranslate"><span class="pre">QueryOptions.in_album</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.incloud"><code class="docutils literal notranslate"><span class="pre">QueryOptions.incloud</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.is_reference"><code class="docutils literal notranslate"><span class="pre">QueryOptions.is_reference</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.keyword"><code class="docutils literal notranslate"><span class="pre">QueryOptions.keyword</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.label"><code class="docutils literal notranslate"><span class="pre">QueryOptions.label</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.live"><code class="docutils literal notranslate"><span class="pre">QueryOptions.live</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.location"><code class="docutils literal notranslate"><span class="pre">QueryOptions.location</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.max_size"><code class="docutils literal notranslate"><span class="pre">QueryOptions.max_size</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.min_size"><code class="docutils literal notranslate"><span class="pre">QueryOptions.min_size</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.missing_bursts"><code class="docutils literal notranslate"><span class="pre">QueryOptions.missing_bursts</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.missing"><code class="docutils literal notranslate"><span class="pre">QueryOptions.missing</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.movies"><code class="docutils literal notranslate"><span class="pre">QueryOptions.movies</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.name"><code class="docutils literal notranslate"><span class="pre">QueryOptions.name</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_comment"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_comment</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_description"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_description</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_likes"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_likes</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_location"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_location</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_keyword"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_keyword</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_place"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_place</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.no_title"><code class="docutils literal notranslate"><span class="pre">QueryOptions.no_title</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_burst"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_burst</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_cloudasset"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_cloudasset</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_favorite"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_favorite</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_hdr"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_hdr</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_hidden"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_hidden</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_in_album"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_in_album</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_incloud"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_incloud</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_live"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_live</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_missing"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_missing</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_panorama"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_panorama</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_portrait"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_portrait</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_reference"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_reference</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_screenshot"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_screenshot</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_selfie"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_selfie</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_shared"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_shared</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_slow_mo"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_slow_mo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_time_lapse"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_time_lapse</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.panorama"><code class="docutils literal notranslate"><span class="pre">QueryOptions.panorama</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.person"><code class="docutils literal notranslate"><span class="pre">QueryOptions.person</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.photos"><code class="docutils literal notranslate"><span class="pre">QueryOptions.photos</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.place"><code class="docutils literal notranslate"><span class="pre">QueryOptions.place</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.portrait"><code class="docutils literal notranslate"><span class="pre">QueryOptions.portrait</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.query_eval"><code class="docutils literal notranslate"><span class="pre">QueryOptions.query_eval</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.regex"><code class="docutils literal notranslate"><span class="pre">QueryOptions.regex</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.screenshot"><code class="docutils literal notranslate"><span class="pre">QueryOptions.screenshot</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.selected"><code class="docutils literal notranslate"><span class="pre">QueryOptions.selected</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.selfie"><code class="docutils literal notranslate"><span class="pre">QueryOptions.selfie</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.shared"><code class="docutils literal notranslate"><span class="pre">QueryOptions.shared</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.slow_mo"><code class="docutils literal notranslate"><span class="pre">QueryOptions.slow_mo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.time_lapse"><code class="docutils literal notranslate"><span class="pre">QueryOptions.time_lapse</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.title"><code class="docutils literal notranslate"><span class="pre">QueryOptions.title</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.to_date"><code class="docutils literal notranslate"><span class="pre">QueryOptions.to_date</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.uti"><code class="docutils literal notranslate"><span class="pre">QueryOptions.uti</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.uuid"><code class="docutils literal notranslate"><span class="pre">QueryOptions.uuid</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.year"><code class="docutils literal notranslate"><span class="pre">QueryOptions.year</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ScoreInfo"><code class="docutils literal notranslate"><span class="pre">ScoreInfo</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.SearchInfo"><code class="docutils literal notranslate"><span class="pre">SearchInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.activities"><code class="docutils literal notranslate"><span class="pre">SearchInfo.activities</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.all"><code class="docutils literal notranslate"><span class="pre">SearchInfo.all</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.asdict"><code class="docutils literal notranslate"><span class="pre">SearchInfo.asdict()</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.bodies_of_water"><code class="docutils literal notranslate"><span class="pre">SearchInfo.bodies_of_water</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.camera"><code class="docutils literal notranslate"><span class="pre">SearchInfo.camera</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.city"><code class="docutils literal notranslate"><span class="pre">SearchInfo.city</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.country"><code class="docutils literal notranslate"><span class="pre">SearchInfo.country</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.detected_text"><code class="docutils literal notranslate"><span class="pre">SearchInfo.detected_text</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.holidays"><code class="docutils literal notranslate"><span class="pre">SearchInfo.holidays</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.labels"><code class="docutils literal notranslate"><span class="pre">SearchInfo.labels</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.locality_names"><code class="docutils literal notranslate"><span class="pre">SearchInfo.locality_names</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.media_types"><code class="docutils literal notranslate"><span class="pre">SearchInfo.media_types</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.month"><code class="docutils literal notranslate"><span class="pre">SearchInfo.month</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.neighborhoods"><code class="docutils literal notranslate"><span class="pre">SearchInfo.neighborhoods</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.place_names"><code class="docutils literal notranslate"><span class="pre">SearchInfo.place_names</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.season"><code class="docutils literal notranslate"><span class="pre">SearchInfo.season</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.state"><code class="docutils literal notranslate"><span class="pre">SearchInfo.state</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.state_abbreviation"><code class="docutils literal notranslate"><span class="pre">SearchInfo.state_abbreviation</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.streets"><code class="docutils literal notranslate"><span class="pre">SearchInfo.streets</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.venue_types"><code class="docutils literal notranslate"><span class="pre">SearchInfo.venue_types</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.venues"><code class="docutils literal notranslate"><span class="pre">SearchInfo.venues</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.SearchInfo.year"><code class="docutils literal notranslate"><span class="pre">SearchInfo.year</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.is_debug"><code class="docutils literal notranslate"><span class="pre">is_debug()</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.set_debug"><code class="docutils literal notranslate"><span class="pre">set_debug()</span></code></a></li>
</ul>
</li>
</ul>
</div>
</section>
<section id="indices-and-tables">
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">#</a></h1>
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this heading">#</a></h1>
<ul class="simple">
<li><p><a class="reference internal" href="genindex.html"><span class="std std-ref">Index</span></a></p></li>
<li><p><a class="reference internal" href="py-modindex.html"><span class="std std-ref">Module Index</span></a></p></li>
@@ -299,7 +710,7 @@
</div>
<div class="title">OSXPhotos</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
</div>
@@ -322,33 +733,18 @@
</footer>
</div>
<aside class="toc-drawer">
<aside class="toc-drawer no-toc">
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
Contents
</span>
</div>
<div class="toc-tree-container">
<div class="toc-tree">
<ul>
<li><a class="reference internal" href="#">Welcome to OSXPhotoss documentation!</a></li>
<li><a class="reference internal" href="#indices-and-tables">Indices and tables</a></li>
</ul>
</div>
</div>
</div>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

Binary file not shown.

View File

@@ -1,14 +1,14 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotoss documentation!" href="index.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -180,7 +180,9 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -195,23 +197,23 @@
</div>
<article role="main">
<section id="osxphotos">
<h1>OSXPhotos<a class="headerlink" href="#osxphotos" title="Permalink to this headline">#</a></h1>
<h1>OSXPhotos<a class="headerlink" href="#osxphotos" title="Permalink to this heading">#</a></h1>
<section id="what-is-osxphotos">
<h2>What is OSXPhotos?<a class="headerlink" href="#what-is-osxphotos" title="Permalink to this headline">#</a></h2>
<h2>What is OSXPhotos?<a class="headerlink" href="#what-is-osxphotos" title="Permalink to this heading">#</a></h2>
<p>OSXPhotos provides both the ability to interact with and query Apples Photos.app library on macOS directly from your python code
as well as a very flexible command line interface (CLI) app for exporting photos.
You can query the Photos library database for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc.
You can also easily export both the original and edited photos.</p>
</section>
<section id="supported-operating-systems">
<h2>Supported operating systems<a class="headerlink" href="#supported-operating-systems" title="Permalink to this headline">#</a></h2>
<h2>Supported operating systems<a class="headerlink" href="#supported-operating-systems" title="Permalink to this heading">#</a></h2>
<p>Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) through macOS Monterey (12.3).</p>
<p>This package will read Photos databases for any supported version on any supported macOS version.
E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine running macOS 10.12 and vice versa.</p>
<p>Requires python &gt;= <code class="docutils literal notranslate"><span class="pre">3.8</span></code>.</p>
</section>
<section id="installation">
<h2>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">#</a></h2>
<h2>Installation<a class="headerlink" href="#installation" title="Permalink to this heading">#</a></h2>
<p>The recommended way of installing <code class="docutils literal notranslate"><span class="pre">osxphotos</span></code> is with <a class="reference external" href="https://github.com/pipxproject/pipx">pipx</a>. The easiest way to do this on a Mac is to use <a class="reference external" href="https://brew.sh/">homebrew</a>:</p>
<ul class="simple">
<li><p>Open <code class="docutils literal notranslate"><span class="pre">Terminal</span></code> (search for <code class="docutils literal notranslate"><span class="pre">Terminal</span></code> in Spotlight or look in <code class="docutils literal notranslate"><span class="pre">Applications/Utilities</span></code>)</p></li>
@@ -222,7 +224,7 @@ E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine
</ul>
</section>
<section id="command-line-usage">
<h2>Command Line Usage<a class="headerlink" href="#command-line-usage" title="Permalink to this headline">#</a></h2>
<h2>Command Line Usage<a class="headerlink" href="#command-line-usage" title="Permalink to this heading">#</a></h2>
<p>This package will install a command line utility called <code class="docutils literal notranslate"><span class="pre">osxphotos</span></code> that allows you to query the Photos database and export photos.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">&gt;</span> <span class="n">osxphotos</span>
<span class="n">Usage</span><span class="p">:</span> <span class="n">osxphotos</span> <span class="p">[</span><span class="n">OPTIONS</span><span class="p">]</span> <span class="n">COMMAND</span> <span class="p">[</span><span class="n">ARGS</span><span class="p">]</span><span class="o">...</span>
@@ -283,10 +285,10 @@ E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine
</div>
<div class="title">OSXPhotos Tutorial</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="index.html">
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
@@ -322,7 +324,7 @@ E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
Contents
On this page
</span>
</div>
<div class="toc-tree-container">
@@ -347,7 +349,9 @@ E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

View File

@@ -1,14 +1,14 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos python API" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Python Package Overview - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Python Package Overview - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -180,7 +180,9 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -195,10 +197,10 @@
</div>
<article role="main">
<section id="osxphotos-python-package-overview">
<h1>OSXPhotos Python Package Overview<a class="headerlink" href="#osxphotos-python-package-overview" title="Permalink to this headline">#</a></h1>
<h1>OSXPhotos Python Package Overview<a class="headerlink" href="#osxphotos-python-package-overview" title="Permalink to this heading">#</a></h1>
<section id="example-uses-of-the-osxphotos-python-package">
<h2>Example uses of the OSXPhotos python package<a class="headerlink" href="#example-uses-of-the-osxphotos-python-package" title="Permalink to this headline">#</a></h2>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">""" Simple usage of the package """</span>
<h2>Example uses of the OSXPhotos python package<a class="headerlink" href="#example-uses-of-the-osxphotos-python-package" title="Permalink to this heading">#</a></h2>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">&quot;&quot;&quot; Simple usage of the package &quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">osxphotos</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
@@ -212,11 +214,11 @@
<span class="nb">print</span><span class="p">(</span><span class="n">photosdb</span><span class="o">.</span><span class="n">albums_as_dict</span><span class="p">)</span>
<span class="c1"># find all photos with Keyword = Foo and containing John Smith</span>
<span class="n">photos</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">photos</span><span class="p">(</span><span class="n">keywords</span><span class="o">=</span><span class="p">[</span><span class="s2">"Foo"</span><span class="p">],</span><span class="n">persons</span><span class="o">=</span><span class="p">[</span><span class="s2">"John Smith"</span><span class="p">])</span>
<span class="n">photos</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">photos</span><span class="p">(</span><span class="n">keywords</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;Foo&quot;</span><span class="p">],</span><span class="n">persons</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;John Smith&quot;</span><span class="p">])</span>
<span class="c1"># find all photos that include Alice Smith but do not contain the keyword Bar</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">photos</span><span class="p">(</span><span class="n">persons</span><span class="o">=</span><span class="p">[</span><span class="s2">"Alice Smith"</span><span class="p">])</span>
<span class="k">if</span> <span class="n">p</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">photos</span><span class="p">(</span><span class="n">keywords</span><span class="o">=</span><span class="p">[</span><span class="s2">"Bar"</span><span class="p">])</span> <span class="p">]</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">photos</span><span class="p">(</span><span class="n">persons</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;Alice Smith&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">p</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">photos</span><span class="p">(</span><span class="n">keywords</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;Bar&quot;</span><span class="p">])</span> <span class="p">]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span>
<span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
@@ -231,14 +233,14 @@
<span class="n">p</span><span class="o">.</span><span class="n">path</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">""" Export all photos to specified directory using album names as folders</span>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">&quot;&quot;&quot; Export all photos to specified directory using album names as folders</span>
<span class="sd"> If file has been edited, also export the edited version,</span>
<span class="sd"> otherwise, export the original version</span>
<span class="sd"> This will result in duplicate photos if photo is in more than album """</span>
<span class="sd"> This will result in duplicate photos if photo is in more than album &quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
@@ -251,15 +253,15 @@
<span class="nd">@click</span><span class="o">.</span><span class="n">command</span><span class="p">()</span>
<span class="nd">@click</span><span class="o">.</span><span class="n">argument</span><span class="p">(</span><span class="s2">"export_path"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">click</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">exists</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
<span class="nd">@click</span><span class="o">.</span><span class="n">argument</span><span class="p">(</span><span class="s2">&quot;export_path&quot;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">click</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">exists</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
<span class="nd">@click</span><span class="o">.</span><span class="n">option</span><span class="p">(</span>
<span class="s2">"--default-album"</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">"Default folder for photos with no album. Defaults to 'unfiled'"</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="s2">"unfiled"</span><span class="p">,</span>
<span class="s2">&quot;--default-album&quot;</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;Default folder for photos with no album. Defaults to &#39;unfiled&#39;&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="s2">&quot;unfiled&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="nd">@click</span><span class="o">.</span><span class="n">option</span><span class="p">(</span>
<span class="s2">"--library-path"</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">"Path to Photos library, default to last used library"</span><span class="p">,</span>
<span class="s2">&quot;--library-path&quot;</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;Path to Photos library, default to last used library&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">export</span><span class="p">(</span><span class="n">export_path</span><span class="p">,</span> <span class="n">default_album</span><span class="p">,</span> <span class="n">library_path</span><span class="p">):</span>
@@ -279,17 +281,17 @@
<span class="k">if</span> <span class="ow">not</span> <span class="n">albums</span><span class="p">:</span>
<span class="n">albums</span> <span class="o">=</span> <span class="p">[</span><span class="n">default_album</span><span class="p">]</span>
<span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">albums</span><span class="p">:</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">"exporting </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2"> in album </span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;exporting </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2"> in album </span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># make sure no invalid characters in destination path (could be in album name)</span>
<span class="n">album_name</span> <span class="o">=</span> <span class="n">sanitize_filepath</span><span class="p">(</span><span class="n">album</span><span class="p">,</span> <span class="n">platform</span><span class="o">=</span><span class="s2">"auto"</span><span class="p">)</span>
<span class="n">album_name</span> <span class="o">=</span> <span class="n">sanitize_filepath</span><span class="p">(</span><span class="n">album</span><span class="p">,</span> <span class="n">platform</span><span class="o">=</span><span class="s2">&quot;auto&quot;</span><span class="p">)</span>
<span class="c1"># create destination folder, if necessary, based on album name</span>
<span class="n">dest_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">export_path</span><span class="p">,</span> <span class="n">album_name</span><span class="p">)</span>
<span class="c1"># verify path is a valid path</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">is_valid_filepath</span><span class="p">(</span><span class="n">dest_dir</span><span class="p">,</span> <span class="n">platform</span><span class="o">=</span><span class="s2">"auto"</span><span class="p">):</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid filepath </span><span class="si">{</span><span class="n">dest_dir</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">is_valid_filepath</span><span class="p">(</span><span class="n">dest_dir</span><span class="p">,</span> <span class="n">platform</span><span class="o">=</span><span class="s2">&quot;auto&quot;</span><span class="p">):</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid filepath </span><span class="si">{</span><span class="n">dest_dir</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># create destination dir if needed</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">dest_dir</span><span class="p">):</span>
@@ -300,21 +302,21 @@
<span class="c1"># export edited version</span>
<span class="n">exported</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">export</span><span class="p">(</span><span class="n">dest_dir</span><span class="p">,</span> <span class="n">edited</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">edited_name</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">path_edited</span><span class="p">)</span><span class="o">.</span><span class="n">name</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Exported </span><span class="si">{</span><span class="n">edited_name</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">exported</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Exported </span><span class="si">{</span><span class="n">edited_name</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">exported</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># export unedited version</span>
<span class="n">exported</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">export</span><span class="p">(</span><span class="n">dest_dir</span><span class="p">)</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Exported </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">exported</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Exported </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">exported</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Skipping missing photo: </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Skipping missing photo: </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">export</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="osxphotos-repl">
<h2>OSXPhotos REPL<a class="headerlink" href="#osxphotos-repl" title="Permalink to this headline">#</a></h2>
<h2>OSXPhotos REPL<a class="headerlink" href="#osxphotos-repl" title="Permalink to this heading">#</a></h2>
<p>The osxphotos command line interface includes a REPL (Run-Evaluate-Print Loop) for testing and development.</p>
<p>The REPL is started with the command: <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">repl</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ osxphotos repl
@@ -349,16 +351,16 @@ The following classes have been imported from osxphotos:
- AlbumInfo, ExifTool, PhotoInfo, PhotoExporter, ExportOptions, ExportResults, PhotosDB, PlaceInfo, QueryOptions, MomentInfo, ScoreInfo, SearchInfo
The following variables are defined:
- photosdb: PhotosDB() instance for '/Users/user/Pictures/Photos Library.photoslibrary'
- photosdb: PhotosDB() instance for &#39;/Users/user/Pictures/Photos Library.photoslibrary&#39;
- photos: list of PhotoInfo objects for all photos filtered with any query options passed on command line (len=31581)
- all_photos: list of PhotoInfo objects for all photos in photosdb, including those in the trash (len=31581)
- selected: list of PhotoInfo objects for any photos selected in Photos (len=0)
The following functions may be helpful:
- get_photo(uuid): return a PhotoInfo object for photo with uuid; e.g. get_photo('B13F4485-94E0-41CD-AF71-913095D62E31')
- get_photo(uuid): return a PhotoInfo object for photo with uuid; e.g. get_photo(&#39;B13F4485-94E0-41CD-AF71-913095D62E31&#39;)
- get_selected(); return list of PhotoInfo objects for photos selected in Photos
- show(photo): open a photo object in the default viewer; e.g. show(selected[0])
- show(path): open a file at path in the default viewer; e.g. show('/path/to/photo.jpg')
- show(path): open a file at path in the default viewer; e.g. show(&#39;/path/to/photo.jpg&#39;)
- spotlight(photo): open a photo and spotlight it in Photos
- inspect(object): print information about an object; e.g. inspect(PhotoInfo)
- explore(object): interactively explore an object with objexplore; e.g. explore(PhotoInfo)
@@ -369,7 +371,7 @@ The following functions may be helpful:
</div>
</section>
<section id="using-the-osxphotos-cli-to-run-python-code">
<h2>Using the osxphotos CLI to run python code<a class="headerlink" href="#using-the-osxphotos-cli-to-run-python-code" title="Permalink to this headline">#</a></h2>
<h2>Using the osxphotos CLI to run python code<a class="headerlink" href="#using-the-osxphotos-cli-to-run-python-code" title="Permalink to this heading">#</a></h2>
<p>The osxphotos CLI can also be used to run your own python code using the <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">run</span></code> command.</p>
<p>This is useful if you have installed the CLI using <code class="docutils literal notranslate"><span class="pre">pipx</span></code> but want to use the osxphotos programmatic interface in your own scripts.</p>
<p>If you need to install any additional python packages to use in your own scripts, you can use the <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">install</span></code> command
@@ -392,10 +394,10 @@ as well as <code class="docutils literal notranslate"><span class="pre">{functio
</div>
<div class="title">OSXPhotos python API</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="template_help.html">
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
@@ -431,7 +433,7 @@ as well as <code class="docutils literal notranslate"><span class="pre">{functio
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
Contents
On this page
</span>
</div>
<div class="toc-tree-container">
@@ -455,7 +457,9 @@ as well as <code class="docutils literal notranslate"><span class="pre">{functio
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

View File

@@ -1,12 +1,12 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -145,7 +145,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -178,7 +178,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -248,7 +249,9 @@
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,11 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.50.4 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.55.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -121,7 +121,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -144,7 +144,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -177,7 +177,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -239,7 +240,9 @@
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

File diff suppressed because one or more lines are too long

View File

@@ -1,14 +1,14 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Template System - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Template System - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -180,7 +180,9 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -195,21 +197,21 @@
</div>
<article role="main">
<section id="osxphotos-template-system">
<h1>OSXPhotos Template System<a class="headerlink" href="#osxphotos-template-system" title="Permalink to this headline">#</a></h1>
<h1>OSXPhotos Template System<a class="headerlink" href="#osxphotos-template-system" title="Permalink to this heading">#</a></h1>
<p>The templating system converts one or template statements, written in osxphotos metadata templating language, to one or more rendered values using information from the photo being processed.</p>
<p>In its simplest form, a template statement has the form: <code class="docutils literal notranslate"><span class="pre">"{template_field}"</span></code>, for example <code class="docutils literal notranslate"><span class="pre">"{title}"</span></code> which would resolve to the title of the photo.</p>
<p>In its simplest form, a template statement has the form: <code class="docutils literal notranslate"><span class="pre">&quot;{template_field}&quot;</span></code>, for example <code class="docutils literal notranslate"><span class="pre">&quot;{title}&quot;</span></code> which would resolve to the title of the photo.</p>
<p>Template statements may contain one or more modifiers. The full syntax is:</p>
<p><code class="docutils literal notranslate"><span class="pre">"pretext{delim+template_field:subfield(field_arg)|filter[find,replace]</span> <span class="pre">conditional?bool_value,default}posttext"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">&quot;pretext{delim+template_field:subfield(field_arg)|filter[find,replace]</span> <span class="pre">conditional?bool_value,default}posttext&quot;</span></code></p>
<p>Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.</p>
<p><code class="docutils literal notranslate"><span class="pre">pretext</span></code> and <code class="docutils literal notranslate"><span class="pre">posttext</span></code> are free form text. For example, if a photo has title “My Photo Title” the template statement <code class="docutils literal notranslate"><span class="pre">"The</span> <span class="pre">title</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">{title}"</span></code>, resolves to <code class="docutils literal notranslate"><span class="pre">"The</span> <span class="pre">title</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">My</span> <span class="pre">Photo</span> <span class="pre">Title"</span></code>. The <code class="docutils literal notranslate"><span class="pre">pretext</span></code> in this example is <code class="docutils literal notranslate"><span class="pre">"The</span> <span class="pre">title</span> <span class="pre">if</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">"</span></code> and the template_field is <code class="docutils literal notranslate"><span class="pre">{title}</span></code>.</p>
<p><code class="docutils literal notranslate"><span class="pre">pretext</span></code> and <code class="docutils literal notranslate"><span class="pre">posttext</span></code> are free form text. For example, if a photo has title “My Photo Title” the template statement <code class="docutils literal notranslate"><span class="pre">&quot;The</span> <span class="pre">title</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">{title}&quot;</span></code>, resolves to <code class="docutils literal notranslate"><span class="pre">&quot;The</span> <span class="pre">title</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">My</span> <span class="pre">Photo</span> <span class="pre">Title&quot;</span></code>. The <code class="docutils literal notranslate"><span class="pre">pretext</span></code> in this example is <code class="docutils literal notranslate"><span class="pre">&quot;The</span> <span class="pre">title</span> <span class="pre">if</span> <span class="pre">the</span> <span class="pre">photo</span> <span class="pre">is</span> <span class="pre">&quot;</span></code> and the template_field is <code class="docutils literal notranslate"><span class="pre">{title}</span></code>.</p>
<p><code class="docutils literal notranslate"><span class="pre">delim</span></code>: optional delimiter string to use when expanding multi-valued template values in-place</p>
<p><code class="docutils literal notranslate"><span class="pre">+</span></code>: If present before template <code class="docutils literal notranslate"><span class="pre">name</span></code>, expands the template in place. If <code class="docutils literal notranslate"><span class="pre">delim</span></code> not provided, values are joined with no delimiter.</p>
<p>e.g. if Photo keywords are <code class="docutils literal notranslate"><span class="pre">["foo","bar"]</span></code>:</p>
<p>e.g. if Photo keywords are <code class="docutils literal notranslate"><span class="pre">[&quot;foo&quot;,&quot;bar&quot;]</span></code>:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{keyword}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"foo",</span> <span class="pre">"bar"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{,+keyword}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"foo,bar"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{;</span> <span class="pre">+keyword}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"foo;</span> <span class="pre">bar"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{+keyword}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"foobar"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{keyword}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;foo&quot;,</span> <span class="pre">&quot;bar&quot;</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{,+keyword}&quot;</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">&quot;foo,bar&quot;</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{;</span> <span class="pre">+keyword}&quot;</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">&quot;foo;</span> <span class="pre">bar&quot;</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{+keyword}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;foobar&quot;</span></code></p></li>
</ul>
<p><code class="docutils literal notranslate"><span class="pre">template_field</span></code>: The template field to resolve. See <a class="reference external" href="#template-substitutions">Template Substitutions</a> for full list of template fields.</p>
<p><cite>:subfield</cite>: Some templates have sub-fields, For example, <cite>{exiftool:IPTC:Make}`</cite>; the template_field is``exiftool<code class="docutils literal notranslate"><span class="pre">and</span> <span class="pre">the</span> <span class="pre">sub-field</span> <span class="pre">is</span></code>IPTC:Make`.</p>
@@ -241,25 +243,28 @@
<li><p><cite>remove(x)</cite>: Remove x from list of values, e.g. remove(b): [a, b, c] =&gt; [a, c].</p></li>
<li><p><cite>slice(start:stop:step)</cite>: Slice list using same semantics as Pythons list slicing, e.g. slice(1:3): [a, b, c, d] =&gt; [b, c]; slice(1:4:2): [a, b, c, d] =&gt; [b, d]; slice(1:): [a, b, c, d] =&gt; [b, c, d]; slice(:-1): [a, b, c, d] =&gt; [a, b, c]; slice(::-1): [a, b, c, d] =&gt; [d, c, b, a]. See also sslice().</p></li>
<li><p><cite>sslice(start:stop:step)</cite>: [s(tring) slice] Slice values in a list using same semantics as Pythons string slicing, e.g. sslice(1:3):abcd =&gt; bc; sslice(1:4:2): abcd =&gt; bd, etc. See also slice().</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">filter(x)</span></code>: Filter list of values using predicate x; for example, <code class="docutils literal notranslate"><span class="pre">{folder_album|filter(contains</span> <span class="pre">Events)}</span></code> returns only folders/albums containing the word Events in their path.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">int</span></code>: Convert values in list to integer, e.g. 1.0 =&gt; 1. If value cannot be converted to integer, remove value from list. [1.1, x] =&gt; [1]. See also float.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">float</span></code>: Convert values in list to floating point number, e.g. 1 =&gt; 1.0. If value cannot be converted to float, remove value from list. [1, x] =&gt; [1.0]. See also int.</p></li>
</ul>
<p>e.g. if Photo keywords are <code class="docutils literal notranslate"><span class="pre">["FOO","bar"]</span></code>:</p>
<p>e.g. if Photo keywords are <code class="docutils literal notranslate"><span class="pre">[&quot;FOO&quot;,&quot;bar&quot;]</span></code>:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{keyword|lower}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"foo",</span> <span class="pre">"bar"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{keyword|upper}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"FOO",</span> <span class="pre">"BAR"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{keyword|capitalize}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"Foo",</span> <span class="pre">"Bar"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{keyword|lower|parens}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"(foo)",</span> <span class="pre">"(bar)"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{keyword|lower}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;foo&quot;,</span> <span class="pre">&quot;bar&quot;</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{keyword|upper}&quot;</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">&quot;FOO&quot;,</span> <span class="pre">&quot;BAR&quot;</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{keyword|capitalize}&quot;</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">&quot;Foo&quot;,</span> <span class="pre">&quot;Bar&quot;</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{keyword|lower|parens}&quot;</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">&quot;(foo)&quot;,</span> <span class="pre">&quot;(bar)&quot;</span></code></p></li>
</ul>
<p>e.g. if Photo description is “my description”:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{descr|titlecase}"</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">"My</span> <span class="pre">Description"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{descr|titlecase}&quot;</span></code> renders to: <code class="docutils literal notranslate"><span class="pre">&quot;My</span> <span class="pre">Description&quot;</span></code></p></li>
</ul>
<p>e.g. If Photo is in <code class="docutils literal notranslate"><span class="pre">Album1</span></code> in <code class="docutils literal notranslate"><span class="pre">Folder1</span></code>:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{folder_album}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">["Folder1/Album1"]</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{folder_album(&gt;)}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">["Folder1&gt;Album1"]</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{folder_album()}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">["Folder1Album1"]</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{folder_album}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">[&quot;Folder1/Album1&quot;]</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{folder_album(&gt;)}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">[&quot;Folder1&gt;Album1&quot;]</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{folder_album()}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">[&quot;Folder1Album1&quot;]</span></code></p></li>
</ul>
<p><cite>[find,replace]</cite>: optional text replacement to perform on rendered template value. For example, to replace “/” in an album name, you could use the template <cite>“{album[/,-]}”</cite>. Multiple replacements can be made by appending “|” and adding another find|replace pair. e.g. to replace both “/” and “:” in album name: <code class="docutils literal notranslate"><span class="pre">"{album[/,-|:,-]}"</span></code>. find/replace pairs are not limited to single characters. The “|” character cannot be used in a find/replace pair.</p>
<p><cite>[find,replace]</cite>: optional text replacement to perform on rendered template value. For example, to replace “/” in an album name, you could use the template <cite>“{album[/,-]}”</cite>. Multiple replacements can be made by appending “|” and adding another find|replace pair. e.g. to replace both “/” and “:” in album name: <code class="docutils literal notranslate"><span class="pre">&quot;{album[/,-|:,-]}&quot;</span></code>. find/replace pairs are not limited to single characters. The “|” character cannot be used in a find/replace pair.</p>
<p><cite>conditional</cite>: optional conditional expression that is evaluated as boolean (True/False) for use with the <cite>?bool_value</cite> modifier. Conditional expressions take the form <cite>not operator value</cite> where <cite>not</cite> is an optional modifier that negates the <cite>operator</cite>. Note: the space before the conditional expression is required if you use a conditional expression. Valid comparison operators are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">contains</span></code>: template field contains value, similar to pythons <code class="docutils literal notranslate"><span class="pre">in</span></code></p></li>
@@ -283,47 +288,44 @@
<li><p><code class="docutils literal notranslate"><span class="pre">{keyword|lower</span> <span class="pre">not</span> <span class="pre">contains</span> <span class="pre">beach}</span></code> uses the <code class="docutils literal notranslate"><span class="pre">not</span></code> modifier to negate the comparison so this resolves to True if there is no keyword that matches beach.</p></li>
</ul>
<p>Examples: to export photos that contain certain keywords with the <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span></code> commands <code class="docutils literal notranslate"><span class="pre">--directory</span></code> option:</p>
<p><code class="docutils literal notranslate"><span class="pre">--directory</span> <span class="pre">"{keyword|lower</span> <span class="pre">matches</span> <span class="pre">travel|vacation?Travel-Photos,Not-Travel-Photos}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">--directory</span> <span class="pre">&quot;{keyword|lower</span> <span class="pre">matches</span> <span class="pre">travel|vacation?Travel-Photos,Not-Travel-Photos}&quot;</span></code></p>
<p>This exports any photo that has keywords travel or vacation into a directory Travel-Photos and all other photos into directory Not-Travel-Photos.</p>
<p>This can be used to rename files as well, for example:
<code class="docutils literal notranslate"><span class="pre">--filename</span> <span class="pre">"{favorite?Favorite-{original_name},{original_name}}"</span></code></p>
<code class="docutils literal notranslate"><span class="pre">--filename</span> <span class="pre">&quot;{favorite?Favorite-{original_name},{original_name}}&quot;</span></code></p>
<p>This renames any photo that is a favorite as Favorite-ImageName.jpg (where ImageName.jpg is the original name of the photo) and all other photos with the unmodified original name.</p>
<p><code class="docutils literal notranslate"><span class="pre">?bool_value</span></code>: Template fields may be evaluated as boolean (True/False) by appending “?” after the field name (and following “(field_arg)” or “[find/replace]”. If a field is True (e.g. photo is HDR and field is <code class="docutils literal notranslate"><span class="pre">"{hdr}"</span></code>) or has any value, the value following the “?” will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is <code class="docutils literal notranslate"><span class="pre">"{title}"</span></code>) then the default value following a “,” will be used.</p>
<p><code class="docutils literal notranslate"><span class="pre">?bool_value</span></code>: Template fields may be evaluated as boolean (True/False) by appending “?” after the field name (and following “(field_arg)” or “[find/replace]”. If a field is True (e.g. photo is HDR and field is <code class="docutils literal notranslate"><span class="pre">&quot;{hdr}&quot;</span></code>) or has any value, the value following the “?” will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is <code class="docutils literal notranslate"><span class="pre">&quot;{title}&quot;</span></code>) then the default value following a “,” will be used.</p>
<p>e.g. if photo is an HDR image,</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{hdr?ISHDR,NOTHDR}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"ISHDR"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{hdr?ISHDR,NOTHDR}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;ISHDR&quot;</span></code></p></li>
</ul>
<p>and if it is not an HDR image,</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{hdr?ISHDR,NOTHDR}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"NOTHDR"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{hdr?ISHDR,NOTHDR}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;NOTHDR&quot;</span></code></p></li>
</ul>
<p><code class="docutils literal notranslate"><span class="pre">,default</span></code>: optional default value to use if the template name has no value. This modifier is also used for the value if False for boolean-type fields (see above) as well as to hold a sub-template for values like <code class="docutils literal notranslate"><span class="pre">{created.strftime}</span></code>. If no default value provided, “_” is used.</p>
<p>e.g., if photo has no title set,</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{title}"</span></code> renders to “_”</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">"{title,I</span> <span class="pre">have</span> <span class="pre">no</span> <span class="pre">title}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"I</span> <span class="pre">have</span> <span class="pre">no</span> <span class="pre">title"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{title}&quot;</span></code> renders to “_”</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{title,I</span> <span class="pre">have</span> <span class="pre">no</span> <span class="pre">title}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;I</span> <span class="pre">have</span> <span class="pre">no</span> <span class="pre">title&quot;</span></code></p></li>
</ul>
<p>Template fields such as <code class="docutils literal notranslate"><span class="pre">created.strftime</span></code> use the default value to pass the template to use for <code class="docutils literal notranslate"><span class="pre">strftime</span></code>.</p>
<p>e.g., if photo date is 4 February 2020, 19:07:38,</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">"{created.strftime,%Y-%m-%d-%H%M%S}"</span></code> renders to <code class="docutils literal notranslate"><span class="pre">"2020-02-04-190738"</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;{created.strftime,%Y-%m-%d-%H%M%S}&quot;</span></code> renders to <code class="docutils literal notranslate"><span class="pre">&quot;2020-02-04-190738&quot;</span></code></p></li>
</ul>
<p>Some template fields such as <code class="docutils literal notranslate"><span class="pre">"{media_type}"</span></code> use the default value to allow customization of the output. For example, <code class="docutils literal notranslate"><span class="pre">"{media_type}"</span></code> resolves to the special media type of the photo such as <code class="docutils literal notranslate"><span class="pre">panorama</span></code> or <code class="docutils literal notranslate"><span class="pre">selfie</span></code>. You may use the default value to override these in form: <code class="docutils literal notranslate"><span class="pre">"{media_type,video=vidéo;time_lapse=vidéo_accélérée}"</span></code>. In this example, if photo was a time_lapse photo, <code class="docutils literal notranslate"><span class="pre">media_type</span></code> would resolve to <code class="docutils literal notranslate"><span class="pre">vidéo_accélérée</span></code> instead of <code class="docutils literal notranslate"><span class="pre">time_lapse</span></code>.</p>
<p>Either or both bool_value or default (False value) may be empty which would result in empty string <code class="docutils literal notranslate"><span class="pre">""</span></code> when rendered.</p>
<p>Some template fields such as <code class="docutils literal notranslate"><span class="pre">&quot;{media_type}&quot;</span></code> use the default value to allow customization of the output. For example, <code class="docutils literal notranslate"><span class="pre">&quot;{media_type}&quot;</span></code> resolves to the special media type of the photo such as <code class="docutils literal notranslate"><span class="pre">panorama</span></code> or <code class="docutils literal notranslate"><span class="pre">selfie</span></code>. You may use the default value to override these in form: <code class="docutils literal notranslate"><span class="pre">&quot;{media_type,video=vidéo;time_lapse=vidéo_accélérée}&quot;</span></code>. In this example, if photo was a time_lapse photo, <code class="docutils literal notranslate"><span class="pre">media_type</span></code> would resolve to <code class="docutils literal notranslate"><span class="pre">vidéo_accélérée</span></code> instead of <code class="docutils literal notranslate"><span class="pre">time_lapse</span></code>.</p>
<p>Either or both bool_value or default (False value) may be empty which would result in empty string <code class="docutils literal notranslate"><span class="pre">&quot;&quot;</span></code> when rendered.</p>
<p>If you want to include “{” or “}” in the output, use “{openbrace}” or “{closebrace}” template substitution.</p>
<p>e.g. <code class="docutils literal notranslate"><span class="pre">"{created.year}/{openbrace}{title}{closebrace}"</span></code> would result in <code class="docutils literal notranslate"><span class="pre">"2020/{Photo</span> <span class="pre">Title}"</span></code>.</p>
<p>e.g. <code class="docutils literal notranslate"><span class="pre">&quot;{created.year}/{openbrace}{title}{closebrace}&quot;</span></code> would result in <code class="docutils literal notranslate"><span class="pre">&quot;2020/{Photo</span> <span class="pre">Title}&quot;</span></code>.</p>
<p><strong>Variables</strong></p>
<p>You can define variables for later use in the template string using the format <code class="docutils literal notranslate"><span class="pre">{var:NAME,VALUE}</span></code>. Variables may then be referenced using the format <code class="docutils literal notranslate"><span class="pre">%NAME</span></code>. For example: <code class="docutils literal notranslate"><span class="pre">{var:foo,bar}</span></code> defines the variable <code class="docutils literal notranslate"><span class="pre">%foo</span></code> to have value <code class="docutils literal notranslate"><span class="pre">bar</span></code>. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the “pipe” (<code class="docutils literal notranslate"><span class="pre">|</span></code>) character is not allowed in a find/replace pair but you can get around this limitation like so: <code class="docutils literal notranslate"><span class="pre">{var:pipe,{pipe}}{title[-,%pipe]}</span></code> which replaces the <code class="docutils literal notranslate"><span class="pre">-</span></code> character with <code class="docutils literal notranslate"><span class="pre">|</span></code> (the value of <code class="docutils literal notranslate"><span class="pre">%pipe</span></code>).</p>
<p>Variables can also be referenced as fields in the template string, for example: <code class="docutils literal notranslate"><span class="pre">{var:year,created.year}{original_name}-{%year}</span></code>. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: <code class="docutils literal notranslate"><span class="pre">{var:name,Katie}{person</span> <span class="pre">contains</span> <span class="pre">{%name}?{%name},Not-{%name}}</span></code>.</p>
<p>If you need to use a <code class="docutils literal notranslate"><span class="pre">%</span></code> (percent sign character), you can escape the percent sign by using <code class="docutils literal notranslate"><span class="pre">%%</span></code>. You can also use the <code class="docutils literal notranslate"><span class="pre">{percent}</span></code> template field where a template field is required. For example:</p>
<p><code class="docutils literal notranslate"><span class="pre">{title[:,%%]}</span></code> replaces the <code class="docutils literal notranslate"><span class="pre">:</span></code> with <code class="docutils literal notranslate"><span class="pre">%</span></code> and <code class="docutils literal notranslate"><span class="pre">{title</span> <span class="pre">contains</span> <span class="pre">Foo?{title}{percent},{title}}</span></code> adds <code class="docutils literal notranslate"><span class="pre">%</span></code> to the title if it contains <code class="docutils literal notranslate"><span class="pre">Foo</span></code>.</p>
<section id="id1">
<h2>Template Substitutions<a class="headerlink" href="#id1" title="Permalink to this headline">#</a></h2>
<div class="table-wrapper"><table class="docutils align-default">
<colgroup>
<col style="width: 50%"/>
<col style="width: 50%"/>
</colgroup>
<h2>Template Substitutions<a class="headerlink" href="#id1" title="Permalink to this heading">#</a></h2>
<div class="table-wrapper docutils container">
<table class="docutils align-default">
<thead>
<tr class="row-odd"><th class="head"><p>Field</p></th>
<th class="head"><p>Description</p></th>
@@ -360,45 +362,51 @@
<tr class="row-odd"><td><p>{favorite}</p></td>
<td><p>Photo has been marked as favorite?; True/False value, use in format {favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}</p></td>
</tr>
<tr class="row-even"><td><p>{created.date}</p></td>
<tr class="row-even"><td><p>{created}</p></td>
<td><p>Photos creation date in ISO format, e.g. 2020-03-22</p></td>
</tr>
<tr class="row-odd"><td><p>{created.year}</p></td>
<tr class="row-odd"><td><p>{created.date}</p></td>
<td><p>Photos creation date in ISO format, e.g. 2020-03-22</p></td>
</tr>
<tr class="row-even"><td><p>{created.year}</p></td>
<td><p>4-digit year of photo creation time</p></td>
</tr>
<tr class="row-even"><td><p>{created.yy}</p></td>
<tr class="row-odd"><td><p>{created.yy}</p></td>
<td><p>2-digit year of photo creation time</p></td>
</tr>
<tr class="row-odd"><td><p>{created.mm}</p></td>
<tr class="row-even"><td><p>{created.mm}</p></td>
<td><p>2-digit month of the photo creation time (zero padded)</p></td>
</tr>
<tr class="row-even"><td><p>{created.month}</p></td>
<tr class="row-odd"><td><p>{created.month}</p></td>
<td><p>Month name in users locale of the photo creation time</p></td>
</tr>
<tr class="row-odd"><td><p>{created.mon}</p></td>
<tr class="row-even"><td><p>{created.mon}</p></td>
<td><p>Month abbreviation in the users locale of the photo creation time</p></td>
</tr>
<tr class="row-even"><td><p>{created.dd}</p></td>
<tr class="row-odd"><td><p>{created.dd}</p></td>
<td><p>2-digit day of the month (zero padded) of photo creation time</p></td>
</tr>
<tr class="row-odd"><td><p>{created.dow}</p></td>
<tr class="row-even"><td><p>{created.dow}</p></td>
<td><p>Day of week in users locale of the photo creation time</p></td>
</tr>
<tr class="row-even"><td><p>{created.doy}</p></td>
<tr class="row-odd"><td><p>{created.doy}</p></td>
<td><p>3-digit day of year (e.g Julian day) of photo creation time, starting from 1 (zero padded)</p></td>
</tr>
<tr class="row-odd"><td><p>{created.hour}</p></td>
<tr class="row-even"><td><p>{created.hour}</p></td>
<td><p>2-digit hour of the photo creation time</p></td>
</tr>
<tr class="row-even"><td><p>{created.min}</p></td>
<tr class="row-odd"><td><p>{created.min}</p></td>
<td><p>2-digit minute of the photo creation time</p></td>
</tr>
<tr class="row-odd"><td><p>{created.sec}</p></td>
<tr class="row-even"><td><p>{created.sec}</p></td>
<td><p>2-digit second of the photo creation time</p></td>
</tr>
<tr class="row-even"><td><p>{created.strftime}</p></td>
<tr class="row-odd"><td><p>{created.strftime}</p></td>
<td><p>Apply strftime template to file creation date/time. Should be used in form {created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {created.strftime,%Y-%U} would result in year-week number of year: 2020-23. If used with no template will return null value. See <a class="reference external" href="https://strftime.org/">https://strftime.org/</a> for help on strftime templates.</p></td>
</tr>
<tr class="row-even"><td><p>{modified}</p></td>
<td><p>Photos modification date in ISO format, e.g. 2020-03-22; uses creation date if photo is not modified</p></td>
</tr>
<tr class="row-odd"><td><p>{modified.date}</p></td>
<td><p>Photos modification date in ISO format, e.g. 2020-03-22; uses creation date if photo is not modified</p></td>
</tr>
@@ -438,158 +446,169 @@
<tr class="row-odd"><td><p>{modified.strftime}</p></td>
<td><p>Apply strftime template to file modification date/time. Should be used in form {modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {modified.strftime,%Y-%U} would result in year-week number of year: 2020-23. If used with no template will return null value. Uses creation date if photo is not modified. See <a class="reference external" href="https://strftime.org/">https://strftime.org/</a> for help on strftime templates.</p></td>
</tr>
<tr class="row-even"><td><p>{today.date}</p></td>
<tr class="row-even"><td><p>{today}</p></td>
<td><p>Current date in iso format, e.g. 2020-03-22</p></td>
</tr>
<tr class="row-odd"><td><p>{today.year}</p></td>
<tr class="row-odd"><td><p>{today.date}</p></td>
<td><p>Current date in iso format, e.g. 2020-03-22</p></td>
</tr>
<tr class="row-even"><td><p>{today.year}</p></td>
<td><p>4-digit year of current date</p></td>
</tr>
<tr class="row-even"><td><p>{today.yy}</p></td>
<tr class="row-odd"><td><p>{today.yy}</p></td>
<td><p>2-digit year of current date</p></td>
</tr>
<tr class="row-odd"><td><p>{today.mm}</p></td>
<tr class="row-even"><td><p>{today.mm}</p></td>
<td><p>2-digit month of the current date (zero padded)</p></td>
</tr>
<tr class="row-even"><td><p>{today.month}</p></td>
<tr class="row-odd"><td><p>{today.month}</p></td>
<td><p>Month name in users locale of the current date</p></td>
</tr>
<tr class="row-odd"><td><p>{today.mon}</p></td>
<tr class="row-even"><td><p>{today.mon}</p></td>
<td><p>Month abbreviation in the users locale of the current date</p></td>
</tr>
<tr class="row-even"><td><p>{today.dd}</p></td>
<tr class="row-odd"><td><p>{today.dd}</p></td>
<td><p>2-digit day of the month (zero padded) of current date</p></td>
</tr>
<tr class="row-odd"><td><p>{today.dow}</p></td>
<tr class="row-even"><td><p>{today.dow}</p></td>
<td><p>Day of week in users locale of the current date</p></td>
</tr>
<tr class="row-even"><td><p>{today.doy}</p></td>
<tr class="row-odd"><td><p>{today.doy}</p></td>
<td><p>3-digit day of year (e.g Julian day) of current date, starting from 1 (zero padded)</p></td>
</tr>
<tr class="row-odd"><td><p>{today.hour}</p></td>
<tr class="row-even"><td><p>{today.hour}</p></td>
<td><p>2-digit hour of the current date</p></td>
</tr>
<tr class="row-even"><td><p>{today.min}</p></td>
<tr class="row-odd"><td><p>{today.min}</p></td>
<td><p>2-digit minute of the current date</p></td>
</tr>
<tr class="row-odd"><td><p>{today.sec}</p></td>
<tr class="row-even"><td><p>{today.sec}</p></td>
<td><p>2-digit second of the current date</p></td>
</tr>
<tr class="row-even"><td><p>{today.strftime}</p></td>
<tr class="row-odd"><td><p>{today.strftime}</p></td>
<td><p>Apply strftime template to current date/time. Should be used in form {today.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {today.strftime,%Y-%U} would result in year-week number of year: 2020-23. If used with no template will return null value. See <a class="reference external" href="https://strftime.org/">https://strftime.org/</a> for help on strftime templates.</p></td>
</tr>
<tr class="row-odd"><td><p>{place.name}</p></td>
<tr class="row-even"><td><p>{place.name}</p></td>
<td><p>Place name from the photos reverse geolocation data, as displayed in Photos</p></td>
</tr>
<tr class="row-even"><td><p>{place.country_code}</p></td>
<tr class="row-odd"><td><p>{place.country_code}</p></td>
<td><p>The ISO country code from the photos reverse geolocation data</p></td>
</tr>
<tr class="row-odd"><td><p>{place.name.country}</p></td>
<tr class="row-even"><td><p>{place.name.country}</p></td>
<td><p>Country name from the photos reverse geolocation data</p></td>
</tr>
<tr class="row-even"><td><p>{place.name.state_province}</p></td>
<tr class="row-odd"><td><p>{place.name.state_province}</p></td>
<td><p>State or province name from the photos reverse geolocation data</p></td>
</tr>
<tr class="row-odd"><td><p>{place.name.city}</p></td>
<tr class="row-even"><td><p>{place.name.city}</p></td>
<td><p>City or locality name from the photos reverse geolocation data</p></td>
</tr>
<tr class="row-even"><td><p>{place.name.area_of_interest}</p></td>
<tr class="row-odd"><td><p>{place.name.area_of_interest}</p></td>
<td><p>Area of interest name (e.g. landmark or public place) from the photos reverse geolocation data</p></td>
</tr>
<tr class="row-odd"><td><p>{place.address}</p></td>
<tr class="row-even"><td><p>{place.address}</p></td>
<td><p>Postal address from the photos reverse geolocation data, e.g. 2007 18th St NW, Washington, DC 20009, United States</p></td>
</tr>
<tr class="row-even"><td><p>{place.address.street}</p></td>
<tr class="row-odd"><td><p>{place.address.street}</p></td>
<td><p>Street part of the postal address, e.g. 2007 18th St NW</p></td>
</tr>
<tr class="row-odd"><td><p>{place.address.city}</p></td>
<tr class="row-even"><td><p>{place.address.city}</p></td>
<td><p>City part of the postal address, e.g. Washington</p></td>
</tr>
<tr class="row-even"><td><p>{place.address.state_province}</p></td>
<tr class="row-odd"><td><p>{place.address.state_province}</p></td>
<td><p>State/province part of the postal address, e.g. DC</p></td>
</tr>
<tr class="row-odd"><td><p>{place.address.postal_code}</p></td>
<tr class="row-even"><td><p>{place.address.postal_code}</p></td>
<td><p>Postal code part of the postal address, e.g. 20009</p></td>
</tr>
<tr class="row-even"><td><p>{place.address.country}</p></td>
<tr class="row-odd"><td><p>{place.address.country}</p></td>
<td><p>Country name of the postal address, e.g. United States</p></td>
</tr>
<tr class="row-odd"><td><p>{place.address.country_code}</p></td>
<tr class="row-even"><td><p>{place.address.country_code}</p></td>
<td><p>ISO country code of the postal address, e.g. US</p></td>
</tr>
<tr class="row-even"><td><p>{searchinfo.season}</p></td>
<tr class="row-odd"><td><p>{searchinfo.season}</p></td>
<td><p>Season of the year associated with a photo, e.g. Summer; (Photos 5+ only, applied automatically by Photos image categorization algorithms).</p></td>
</tr>
<tr class="row-odd"><td><p>{exif.camera_make}</p></td>
<tr class="row-even"><td><p>{exif.camera_make}</p></td>
<td><p>Camera make from original photos EXIF information as imported by Photos, e.g. Apple</p></td>
</tr>
<tr class="row-even"><td><p>{exif.camera_model}</p></td>
<tr class="row-odd"><td><p>{exif.camera_model}</p></td>
<td><p>Camera model from original photos EXIF information as imported by Photos, e.g. iPhone 6s</p></td>
</tr>
<tr class="row-odd"><td><p>{exif.lens_model}</p></td>
<tr class="row-even"><td><p>{exif.lens_model}</p></td>
<td><p>Lens model from original photos EXIF information as imported by Photos, e.g. iPhone 6s back camera 4.15mm f/2.2</p></td>
</tr>
<tr class="row-even"><td><p>{moment}</p></td>
<tr class="row-odd"><td><p>{moment}</p></td>
<td><p>The moment title of the photo</p></td>
</tr>
<tr class="row-odd"><td><p>{uuid}</p></td>
<tr class="row-even"><td><p>{uuid}</p></td>
<td><p>Photos internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. 128FB4C6-0B16-4E7D-9108-FB2E90DA1546</p></td>
</tr>
<tr class="row-even"><td><p>{shortuuid}</p></td>
<tr class="row-odd"><td><p>{shortuuid}</p></td>
<td><p>A shorter representation of photos internal universally unique identifier (UUID) for the photo, a 22-character string unique to the photo, e.g. JYsxugP9UjetmCbBCHXcmu</p></td>
</tr>
<tr class="row-odd"><td><p>{id}</p></td>
<tr class="row-even"><td><p>{id}</p></td>
<td><p>A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3…etc. Each asset associated with a photo (e.g. an image and Live Photo preview) will share the same id. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {id:05d} which results in 00001, 00002, 00003…etc.</p></td>
</tr>
<tr class="row-even"><td><p>{album_seq}</p></td>
<tr class="row-odd"><td><p>{album_seq}</p></td>
<td><p>An integer, starting at 0, indicating the photos index (sequence) in the containing album. Only valid when used in a filename template and only when {album} or {folder_album} is used in the directory template. For example directory “{folder_album}” filename “{album<em>seq}</em>{original_name}”’. To start counting at a value other than 0, append append (starting_value) to the field name. For example, to start counting at 1 instead of 0: {album_seq(1)}. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {album_seq:05d} which results in 00000, 00001, 00002…etc. To format while also using a starting value: {album_seq:05d(1)} which results in 0001, 00002…etc.This may result in incorrect sequences if you have duplicate albums with the same name; see also {folder_album_seq}.</p></td>
</tr>
<tr class="row-odd"><td><p>{folder_album_seq}</p></td>
<tr class="row-even"><td><p>{folder_album_seq}</p></td>
<td><p>An integer, starting at 0, indicating the photos index (sequence) in the containing album and folder path. Only valid when used in a filename template and only when {folder_album} is used in the directory template. For example directory “{folder_album}” filename “{folder_album<em>seq}</em>{original_name}”’. To start counting at a value other than 0, append (starting_value) to the field name. For example, to start counting at 1 instead of 0: {folder_album_seq(1)} May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use {folder_album_seq:05d} which results in 00000, 00001, 00002…etc. To format while also using a starting value: {folder_album_seq:05d(1)} which results in 0001, 00002…etc.This may result in incorrect sequences if you have duplicate albums with the same name in the same folder; see also {album_seq}.</p></td>
</tr>
<tr class="row-even"><td><p>{comma}</p></td>
<tr class="row-odd"><td><p>{comma}</p></td>
<td><p>A comma: ,</p></td>
</tr>
<tr class="row-odd"><td><p>{semicolon}</p></td>
<tr class="row-even"><td><p>{semicolon}</p></td>
<td><p>A semicolon: ;</p></td>
</tr>
<tr class="row-even"><td><p>{questionmark}</p></td>
<tr class="row-odd"><td><p>{questionmark}</p></td>
<td><p>A question mark: ?</p></td>
</tr>
<tr class="row-odd"><td><p>{pipe}</p></td>
<tr class="row-even"><td><p>{pipe}</p></td>
<td><p>A vertical pipe: |</p></td>
</tr>
<tr class="row-even"><td><p>{openbrace}</p></td>
<tr class="row-odd"><td><p>{openbrace}</p></td>
<td><p>An open brace: {</p></td>
</tr>
<tr class="row-odd"><td><p>{closebrace}</p></td>
<tr class="row-even"><td><p>{closebrace}</p></td>
<td><p>A close brace: }</p></td>
</tr>
<tr class="row-even"><td><p>{openparens}</p></td>
<tr class="row-odd"><td><p>{openparens}</p></td>
<td><p>An open parentheses: (</p></td>
</tr>
<tr class="row-odd"><td><p>{closeparens}</p></td>
<tr class="row-even"><td><p>{closeparens}</p></td>
<td><p>A close parentheses: )</p></td>
</tr>
<tr class="row-even"><td><p>{openbracket}</p></td>
<tr class="row-odd"><td><p>{openbracket}</p></td>
<td><p>An open bracket: [</p></td>
</tr>
<tr class="row-odd"><td><p>{closebracket}</p></td>
<tr class="row-even"><td><p>{closebracket}</p></td>
<td><p>A close bracket: ]</p></td>
</tr>
<tr class="row-even"><td><p>{newline}</p></td>
<tr class="row-odd"><td><p>{newline}</p></td>
<td><p>A newline: n</p></td>
</tr>
<tr class="row-odd"><td><p>{lf}</p></td>
<tr class="row-even"><td><p>{lf}</p></td>
<td><p>A line feed: n, alias for {newline}</p></td>
</tr>
<tr class="row-even"><td><p>{cr}</p></td>
<tr class="row-odd"><td><p>{cr}</p></td>
<td><p>A carriage return: r</p></td>
</tr>
<tr class="row-odd"><td><p>{crlf}</p></td>
<td><p>a carriage return + line feed: rn</p></td>
<tr class="row-even"><td><p>{crlf}</p></td>
<td><p>A carriage return + line feed: rn</p></td>
</tr>
<tr class="row-odd"><td><p>{tab}</p></td>
<td><dl class="field-list simple">
<dt class="field-odd">A tab<span class="colon">:</span></dt>
<dd class="field-odd"><p>t</p>
</dd>
</dl>
</td>
</tr>
<tr class="row-even"><td><p>{osxphotos_version}</p></td>
<td><p>The osxphotos version, e.g. 0.50.4</p></td>
<td><p>The osxphotos version, e.g. 0.55.2</p></td>
</tr>
<tr class="row-odd"><td><p>{osxphotos_cmd_line}</p></td>
<td><p>The full command line used to run osxphotos</p></td>
@@ -658,7 +677,8 @@
<td><p>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.</p></td>
</tr>
</tbody>
</table></div>
</table>
</div>
</section>
</section>
@@ -674,10 +694,10 @@
</div>
<div class="title">OSXPhotos Python Package Overview</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="cli.html">
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
@@ -713,7 +733,7 @@
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
Contents
On this page
</span>
</div>
<div class="toc-tree-container">
@@ -735,7 +755,9 @@
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

View File

@@ -1,14 +1,14 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Tutorial - osxphotos 0.50.4 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Tutorial - osxphotos 0.55.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.50.4 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.55.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.50.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.55.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -180,7 +180,9 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -195,50 +197,58 @@
</div>
<article role="main">
<!-- OSXPHOTOS-TUTORIAL-HEADER:START --><section id="osxphotos-tutorial">
<h1>OSXPhotos Tutorial<a class="headerlink" href="#osxphotos-tutorial" title="Permalink to this headline">#</a></h1>
<h1>OSXPhotos Tutorial<a class="headerlink" href="#osxphotos-tutorial" title="Permalink to this heading">#</a></h1>
<section id="overview">
<h2>Overview<a class="headerlink" href="#overview" title="Permalink to this headline">#</a></h2>
<h2>Overview<a class="headerlink" href="#overview" title="Permalink to this heading">#</a></h2>
<!-- OSXPHOTOS-TUTORIAL-HEADER:END --><p>The design philosophy for osxphotos is “make the easy things easy and make the hard things possible”. To “make the hard things possible”, osxphotos is very flexible and has many, many configuration options the <code class="docutils literal notranslate"><span class="pre">export</span></code> command for example, has over 100 command line options. Thus, osxphotos may seem daunting at first. The purpose of this tutorial is to explain a number of common use cases with examples and, hopefully, make osxphotos less daunting to use. osxphotos includes several commands for retrieving information from your Photos library but the one most users are interested in is the <code class="docutils literal notranslate"><span class="pre">export</span></code> command which exports photos from the library so thats the focus of this tutorial.</p>
</section>
<section id="export-your-photos">
<h2>Export your photos<a class="headerlink" href="#export-your-photos" title="Permalink to this headline">#</a></h2>
<h2>Export your photos<a class="headerlink" href="#export-your-photos" title="Permalink to this heading">#</a></h2>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span></code></p>
<p>This command exports all your photos to the <code class="docutils literal notranslate"><span class="pre">/path/to/export</span></code> directory.</p>
<p><strong>Note</strong>: osxphotos uses the term photo to refer to a generic media asset in your Photos Library. A photo may be an image, a video file, a combination of still image and video file (e.g. an Apple “Live Photo” which is an image and an associated “live preview” video file), a JPEG image with an associated RAW image, etc.</p>
</section>
<section id="export-by-date">
<h2>Export by date<a class="headerlink" href="#export-by-date" title="Permalink to this headline">#</a></h2>
<h2>Export by date<a class="headerlink" href="#export-by-date" title="Permalink to this heading">#</a></h2>
<p>While the previous command will export all your photos (and videossee note above), it probably doesnt do exactly what you want. In the previous example, all the photos will be exported to a single folder: <code class="docutils literal notranslate"><span class="pre">/path/to/export</span></code>. If you have a large library with thousands of images and videos, this likely isnt very useful. You can use the <code class="docutils literal notranslate"><span class="pre">--export-by-date</span></code> option to export photos to a folder structure organized by year, month, day, e.g. <code class="docutils literal notranslate"><span class="pre">2021/04/21</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--export-by-date</span></code></p>
<p>With this command, a photo that was created on 31 May 2015 would be exported to: <code class="docutils literal notranslate"><span class="pre">/path/to/export/2015/05/31</span></code></p>
</section>
<section id="specify-directory-structure">
<h2>Specify directory structure<a class="headerlink" href="#specify-directory-structure" title="Permalink to this headline">#</a></h2>
<h2>Specify directory structure<a class="headerlink" href="#specify-directory-structure" title="Permalink to this heading">#</a></h2>
<p>If you prefer a different directory structure for your exported images, osxphotos provides a very flexible <span class="raw-html-m2r"><!-- OSXPHOTOS-TEMPLATE-SYSTEM-LINK:START --></span>template system<span class="raw-html-m2r"><!-- OSXPHOTOS-TEMPLATE-SYSTEM-LINK:END --></span> that allows you to specify the directory structure using the <code class="docutils literal notranslate"><span class="pre">--directory</span></code> option. For example, this command exported to a directory structure that looks like: <code class="docutils literal notranslate"><span class="pre">2015/May</span></code> (4-digit year / month name):</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">"{created.year}/{created.month}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{created.year}/{created.month}&quot;</span></code></p>
<p>The string following <code class="docutils literal notranslate"><span class="pre">--directory</span></code> is an <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">template</span> <span class="pre">string</span></code>. Template strings are widely used throughout osxphotos and its worth your time to learn more about them. In a template string, the values between the curly braces, e.g. <code class="docutils literal notranslate"><span class="pre">{created.year}</span></code> are replaced with metadata from the photo being exported. In this case, <code class="docutils literal notranslate"><span class="pre">{created.year}</span></code> is the 4-digit year of the photos creation date and <code class="docutils literal notranslate"><span class="pre">{created.month}</span></code> is the full month name in the users locale (e.g. <code class="docutils literal notranslate"><span class="pre">May</span></code>, <code class="docutils literal notranslate"><span class="pre">mai</span></code>, etc.). In the osxphotos template system these are referred to as template fields. The text not included between <code class="docutils literal notranslate"><span class="pre">{}</span></code> pairs is interpreted literally, in this case <code class="docutils literal notranslate"><span class="pre">/</span></code>, is a directory separator.</p>
<p>osxphotos provides access to almost all the metadata known to Photos about your images. For example, Photos performs reverse geolocation lookup on photos that contain GPS coordinates to assign place names to the photo. Using the <code class="docutils literal notranslate"><span class="pre">--directory</span></code> template, you could thus export photos organized by country name:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">"{created.year}/{place.name.country}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{created.year}/{place.name.country}&quot;</span></code></p>
<p>Of course, some photos might not have an associated place name so the template system allows you specify a default value to use if a template field is null (has no value).</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">"{created.year}/{place.name.country,No-Country}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{created.year}/{place.name.country,No-Country}&quot;</span></code></p>
<p>The value after the , in the template string is the default value, in this case No-Country. <strong>Note</strong>: If you dont specify a default value and a template field is null, osxphotos will use “_” (underscore character) as the default.</p>
<p>Some template fields, such as <code class="docutils literal notranslate"><span class="pre">{keyword}</span></code>, may expand to more than one value. For example, if a photo has keywords of “Travel” and “Vacation”, <code class="docutils literal notranslate"><span class="pre">{keyword}</span></code> would expand to “Travel”, “Vacation”. When used with <code class="docutils literal notranslate"><span class="pre">--directory</span></code>, this would result in the photo being exported to more than one directory (thus more than one copy of the photo would be exported). For example, if <code class="docutils literal notranslate"><span class="pre">IMG_1234.JPG</span></code> has keywords <code class="docutils literal notranslate"><span class="pre">Travel</span></code>, and <code class="docutils literal notranslate"><span class="pre">Vacation</span></code> and you run the following command:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">"{keyword}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{keyword}&quot;</span></code></p>
<p>the exported files would be:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="n">to</span><span class="o">/</span><span class="n">export</span><span class="o">/</span><span class="n">Travel</span><span class="o">/</span><span class="n">IMG_1234</span><span class="o">.</span><span class="n">JPG</span>
<span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="n">to</span><span class="o">/</span><span class="n">export</span><span class="o">/</span><span class="n">Vacation</span><span class="o">/</span><span class="n">IMG_1234</span><span class="o">.</span><span class="n">JPG</span>
</pre></div>
</div>
<p>If your photos are organized in folders and albums in Photos you can preserve this structure on export by using the <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> template field with the <code class="docutils literal notranslate"><span class="pre">--directory</span></code> option. For example, if you have a photo in the album <code class="docutils literal notranslate"><span class="pre">Vacation</span></code> which is in the <code class="docutils literal notranslate"><span class="pre">Travel</span></code> folder, the following command would export the photo to the <code class="docutils literal notranslate"><span class="pre">Travel/Vacation</span></code> directory:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{folder_album}&quot;</span></code></p>
<p>Photos can belong to more than one album. In this case, the template field <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> will expand to all the album names that the photo belongs to. For example, if a photo belongs to the albums <code class="docutils literal notranslate"><span class="pre">Vacation</span></code> and <code class="docutils literal notranslate"><span class="pre">Travel</span></code>, the template field <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> would expand to <code class="docutils literal notranslate"><span class="pre">Vacation</span></code>, <code class="docutils literal notranslate"><span class="pre">Travel</span></code>. If the photo belongs to no albums, the template field <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> would expand to “_” (the default value).</p>
<p>All template fields including <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> can be further filtered using a number of different filters. To convert all directory names to lower case for example, use the <code class="docutils literal notranslate"><span class="pre">lower</span></code> filter:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{folder_album|lower}&quot;</span></code></p>
<p>If all your photos were organized into various albums under a folder named <code class="docutils literal notranslate"><span class="pre">Events</span></code> but some where also included in other top-level albums and you wanted to export only the <code class="docutils literal notranslate"><span class="pre">Events</span></code> folder, you could use the <code class="docutils literal notranslate"><span class="pre">filter</span></code> option to filter out the other top-level albums by selecting only those folder/album paths that start with <code class="docutils literal notranslate"><span class="pre">Events</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--directory</span> <span class="pre">&quot;{folder_album|filter(startswith</span> <span class="pre">Events)}&quot;</span></code></p>
<p>You can learn more about the other filters using <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">help</span> <span class="pre">export</span></code>.</p>
</section>
<section id="specify-exported-filename">
<h2>Specify exported filename<a class="headerlink" href="#specify-exported-filename" title="Permalink to this headline">#</a></h2>
<h2>Specify exported filename<a class="headerlink" href="#specify-exported-filename" title="Permalink to this heading">#</a></h2>
<p>By default, osxphotos will use the original filename of the photo when exporting. That is, the filename the photo had when it was taken or imported into Photos. This is often something like <code class="docutils literal notranslate"><span class="pre">IMG_1234.JPG</span></code> or <code class="docutils literal notranslate"><span class="pre">DSC05678.dng</span></code>. osxphotos allows you to specify a custom filename template using the <code class="docutils literal notranslate"><span class="pre">--filename</span></code> option in the same way as <code class="docutils literal notranslate"><span class="pre">--directory</span></code> allows you to specify a custom directory name. For example, Photos allows you specify a title or caption for a photo and you can use this in place of the original filename:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--filename</span> <span class="pre">"{title}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--filename</span> <span class="pre">&quot;{title}&quot;</span></code></p>
<p>The above command will export photos using the title. Note that you dont need to specify the extension as part of the <code class="docutils literal notranslate"><span class="pre">--filename</span></code> template as osxphotos will automatically add the correct file extension. Some photos might not have a title so in this case, you could use the default value feature to specify a different name for these photos. For example, to use the title as the filename, but if no title is specified, use the original filename instead:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --filename "{title,{original_name}}"
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --filename &quot;{title,{original_name}}&quot;
│ ││ │
│ ││ │
Use photo's title as the filename &lt;──────┘ ││ │
Use photo&#39;s title as the filename &lt;──────┘ ││ │
││ │
Value after comma will be used &lt;───────┘│ │
if title is blank │ │
@@ -246,30 +256,30 @@
The default value can be &lt;────┘ │
another template field │
Use photo's original name if no title &lt;──────┘
Use photo&#39;s original name if no title &lt;──────┘
</pre></div>
</div>
<p>The osxphotos template system also allows for limited conditional logic of the type “If a condition is true then do one thing, otherwise, do a different thing”. For example, you can use the <code class="docutils literal notranslate"><span class="pre">--filename</span></code> option to name files that are marked as “Favorites” in Photos differently than other files. For example, to add a “#” to the name of every photo thats a favorite:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --filename "{original_name}{favorite?#,}"
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --filename &quot;{original_name}{favorite?#,}&quot;
│ │ │││
│ │ │││
Use photo's original name as filename &lt;──┘ │ │││
Use photo&#39;s original name as filename &lt;──┘ │ │││
│ │││
'favorite' is True if photo is a Favorite, &lt;───────┘ │││
&#39;favorite&#39; is True if photo is a Favorite, &lt;───────┘ │││
otherwise, False │││
│││
'?' specifies a conditional &lt;─────────────┘││
&#39;?&#39; specifies a conditional &lt;─────────────┘││
││
Value immediately following ? will be used if &lt;──────┘│
preceding template field is True or non-blank │
Value immediately following comma will be used if &lt;──────┘
template field is False or blank (null); in this case
no value is specified so a blank string "" will be used
no value is specified so a blank string &quot;&quot; will be used
</pre></div>
</div>
<p>Like with <code class="docutils literal notranslate"><span class="pre">--directory</span></code>, using a multi-valued template field such as <code class="docutils literal notranslate"><span class="pre">{keyword}</span></code> may result in more than one copy of a photo being exported. For example, if <code class="docutils literal notranslate"><span class="pre">IMG_1234.JPG</span></code> has keywords <code class="docutils literal notranslate"><span class="pre">Travel</span></code>, and <code class="docutils literal notranslate"><span class="pre">Vacation</span></code> and you run the following command:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--filename</span> <span class="pre">"{keyword}-{original_name}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--filename</span> <span class="pre">&quot;{keyword}-{original_name}&quot;</span></code></p>
<p>the exported files would be:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="n">to</span><span class="o">/</span><span class="n">export</span><span class="o">/</span><span class="n">Travel</span><span class="o">-</span><span class="n">IMG_1234</span><span class="o">.</span><span class="n">JPG</span>
<span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="n">to</span><span class="o">/</span><span class="n">export</span><span class="o">/</span><span class="n">Vacation</span><span class="o">-</span><span class="n">IMG_1234</span><span class="o">.</span><span class="n">JPG</span>
@@ -277,11 +287,11 @@
</div>
</section>
<section id="edited-photos">
<h2>Edited photos<a class="headerlink" href="#edited-photos" title="Permalink to this headline">#</a></h2>
<h2>Edited photos<a class="headerlink" href="#edited-photos" title="Permalink to this heading">#</a></h2>
<p>If a photo has been edited in Photos (e.g. cropped, adjusted, etc.) there will be both an original image and an edited image in the Photos Library. By default, osxphotos will export both the original and the edited image. To distinguish between them, osxphotos will append “_edited” to the edited image. For example, if the original image was named <code class="docutils literal notranslate"><span class="pre">IMG_1234.JPG</span></code>, osxphotos will export the original as <code class="docutils literal notranslate"><span class="pre">IMG_1234.JPG</span></code> and the edited version as <code class="docutils literal notranslate"><span class="pre">IMG_1234_edited.jpeg</span></code>. <strong>Note:</strong> Photos changes the extension of edited images to “.jpeg” even if the original was named “.JPG”. You can change the suffix appended to edited images using the <code class="docutils literal notranslate"><span class="pre">--edited-suffix</span></code> option:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--edited-suffix</span> <span class="pre">"_EDIT"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--edited-suffix</span> <span class="pre">&quot;_EDIT&quot;</span></code></p>
<p>In this example, the edited image would be named <code class="docutils literal notranslate"><span class="pre">IMG_1234_EDIT.jpeg</span></code>. Like many options in osxphotos, the <code class="docutils literal notranslate"><span class="pre">--edited-suffix</span></code> option can evaluate an osxphotos template string so you could append the modification date (the date the photo was edited) to all edited photos using this command:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--edited-suffix</span> <span class="pre">"_{modified.year}-{modified.mm}-{modified.dd}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--edited-suffix</span> <span class="pre">&quot;_{modified.year}-{modified.mm}-{modified.dd}&quot;</span></code></p>
<p>In this example, if the photo was edited on 21 April 2021, the name of the exported file would be: <code class="docutils literal notranslate"><span class="pre">IMG_1234_2021-04-21.jpeg</span></code>.</p>
<p>You can tell osxphotos to not export edited photos (that is, only export the original unedited photos) using <code class="docutils literal notranslate"><span class="pre">--skip-edited</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--skip-edited</span></code></p>
@@ -291,19 +301,19 @@
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--jpeg-ext</span> <span class="pre">jpg</span></code></p>
</section>
<section id="specifying-the-photos-library">
<h2>Specifying the Photos library<a class="headerlink" href="#specifying-the-photos-library" title="Permalink to this headline">#</a></h2>
<h2>Specifying the Photos library<a class="headerlink" href="#specifying-the-photos-library" title="Permalink to this heading">#</a></h2>
<p>All the above commands operate on the default Photos library. Most users only use a single Photos library which is also known as the System Photo Library. It is possible to use Photos with more than one library. For example, if you hold down the “Option” key while opening Photos, you can select an alternate Photos library. If you dont specify which library to use, osxphotos will try find the last opened library. Occasionally it cant determine this and in that case, it will use the System Photos Library. If you use more than one Photos library and want to explicitly specify which library to use, you can do so with the <code class="docutils literal notranslate"><span class="pre">--db</span></code> option. (db is short for database and is so named because osxphotos operates on the database that Photos uses to manage your Photos library).</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--db</span> <span class="pre">~/Pictures/MyAlternateLibrary.photoslibrary</span></code></p>
</section>
<section id="missing-photos">
<h2>Missing photos<a class="headerlink" href="#missing-photos" title="Permalink to this headline">#</a></h2>
<h2>Missing photos<a class="headerlink" href="#missing-photos" title="Permalink to this heading">#</a></h2>
<p>osxphotos works by copying photos out of the Photos library folder to export them. You may see osxphotos report that one or more photos are missing and thus could not be exported. One possible reason for this is that you are using iCloud to synch your Photos library and Photos either hasnt yet synched the cloud library to the local Mac or you have Photos configured to “Optimize Mac Storage” in Photos Preferences. Another reason is that even if you have Photos configured to download originals to the Mac, Photos does not always download photos from shared albums or original screenshots to the Mac.</p>
<p>If you encounter missing photos you can tell osxphotos to download the missing photos from iCloud using the <code class="docutils literal notranslate"><span class="pre">--download-missing</span></code> option. <code class="docutils literal notranslate"><span class="pre">--download-missing</span></code> uses AppleScript to communicate with Photos and tell it to download the missing photos. Photos AppleScript interface is somewhat buggy and you may find that Photos crashes. In this case, osxphotos will attempt to restart Photos to resume the download process. Theres also an experimental <code class="docutils literal notranslate"><span class="pre">--use-photokit</span></code> option that will communicate with Photos using a different “PhotoKit” interface. This option must be used together with <code class="docutils literal notranslate"><span class="pre">--download-missing</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--download-missing</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--download-missing</span> <span class="pre">--use-photokit</span></code></p>
</section>
<section id="exporting-to-external-disks">
<h2>Exporting to external disks<a class="headerlink" href="#exporting-to-external-disks" title="Permalink to this headline">#</a></h2>
<h2>Exporting to external disks<a class="headerlink" href="#exporting-to-external-disks" title="Permalink to this heading">#</a></h2>
<p>If you are exporting to an external network attached storage (NAS) device, you may encounter errors if the network connection is unreliable. In this case, you can use the <code class="docutils literal notranslate"><span class="pre">--retry</span></code> option so that osxphotos will automatically retry the export. Use <code class="docutils literal notranslate"><span class="pre">--retry</span></code> with a number that specifies the number of times to retry the export:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--retry</span> <span class="pre">3</span></code></p>
<p>In this example, osxphotos will attempt to export a photo up to 3 times if it encounters an error.</p>
@@ -311,11 +321,11 @@
<p>Another alternative to using <code class="docutils literal notranslate"><span class="pre">--exportdb</span></code> is to use <code class="docutils literal notranslate"><span class="pre">--ramdb</span></code>. This option instructs osxphotos to use a RAM database instead of a file on disk. The RAM database is much faster than the file on disk and doesnt require osxphotos to access the network drive to query or write to the database. When osxphotos completes the export it will write the RAM database to the export location. This can offer a significant performance boost but you will lose state information if osxphotos crashes or is interrupted during export.</p>
</section>
<section id="exporting-metadata-with-exported-photos">
<h2>Exporting metadata with exported photos<a class="headerlink" href="#exporting-metadata-with-exported-photos" title="Permalink to this headline">#</a></h2>
<h2>Exporting metadata with exported photos<a class="headerlink" href="#exporting-metadata-with-exported-photos" title="Permalink to this heading">#</a></h2>
<p>Photos tracks a tremendous amount of metadata associated with photos in the library such as keywords, faces and persons, reverse geolocation data, and image classification labels. Photos native export capability does not preserve most of this metadata. osxphotos can, however, access and preserve almost all the metadata associated with photos. Using the free <cite>``exiftool`</cite> &lt;<a class="reference external" href="https://exiftool.org/">https://exiftool.org/</a>&gt;`_ app, osxphotos can write metadata to exported photos. Follow the instructions on the exiftool website to install exiftool then you can use the <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> option to write metadata to exported photos:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span></code></p>
<p>This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> to modify the metadata that is written by <code class="docutils literal notranslate"><span class="pre">exiftool</span></code>. For example, you can use the <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code> option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchical keywords in the format used by Lightroom Classic:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --exiftool --keyword-template "{folder_album(&gt;)}"
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --exiftool --keyword-template &quot;{folder_album(&gt;)}&quot;
│ │
│ │
folder_album results in the folder(s) &lt;──┘ │
@@ -324,24 +334,24 @@
The value in () is used as the path separator &lt;───────┘
for joining the folders and albums. For example,
if photo is in Folder1/Folder2/Album, (&gt;) produces
"Folder1&gt;Folder2&gt;Album" which some programs, such as
&quot;Folder1&gt;Folder2&gt;Album&quot; which some programs, such as
Lightroom Classic, treat as hierarchical keywords
</pre></div>
</div>
<p>The above command will write all the regular metadata that <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form “Folder1&gt;Folder2&gt;Album”. If you did not include the <code class="docutils literal notranslate"><span class="pre">(&gt;)</span></code> in the template string (e.g. <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code>), folder_album would render in form “Folder1/Folder2/Album”.</p>
<p>A powerful feature of Photos is that it uses machine learning algorithms to automatically classify or label photos. These labels are used when you search for images in Photos but are not otherwise available to the user. osxphotos is able to read all the labels associated with a photo and makes those available through the template system via the <code class="docutils literal notranslate"><span class="pre">{label}</span></code>. Think of these as automatic keywords as opposed to the keywords you assign manually in Photos. One common use case is to use the automatic labels to create new keywords when exporting images so that these labels are embedded in the images metadata:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">"{label}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">&quot;{label}&quot;</span></code></p>
</section>
<section id="removing-a-keyword-during-export">
<h2>Removing a keyword during export<a class="headerlink" href="#removing-a-keyword-during-export" title="Permalink to this headline">#</a></h2>
<h2>Removing a keyword during export<a class="headerlink" href="#removing-a-keyword-during-export" title="Permalink to this heading">#</a></h2>
<p>If some of your photos contain a keyword you do not want to be added to the exported file with <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code>, you can use the template system to remove the keyword from the exported file. For example, if you want to remove the keyword “MyKeyword” from all your photos:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">"{keyword|remove(MyKeyword)}"</span> <span class="pre">--replace-keywords</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">&quot;{keyword|remove(MyKeyword)}&quot;</span> <span class="pre">--replace-keywords</span></code></p>
<p>In this example, <code class="docutils literal notranslate"><span class="pre">|remove(MyKeyword)</span></code> is a filter which removes <code class="docutils literal notranslate"><span class="pre">MyKeyword</span></code> from the keyword list of every photo being processed. The <code class="docutils literal notranslate"><span class="pre">--replace-keywords</span></code> option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code>.</p>
<p><strong>Note</strong>: When evaluating templates for <code class="docutils literal notranslate"><span class="pre">--directory</span></code> and <code class="docutils literal notranslate"><span class="pre">--filename</span></code>, osxphotos inserts the automatic default value “_” for any template field which is null (empty or blank). This is to ensure that theres never a null directory or filename created. For metadata templates such as <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code>, osxphotos does not provide an automatic default value thus if the template field is null, no keyword would be created. Of course, you can provide a default value if desired and osxphotos will use this. For example, to add “nolabel” as a keyword for any photo that doesnt have labels:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">"{label,nolabel}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span> <span class="pre">--keyword-template</span> <span class="pre">&quot;{label,nolabel}&quot;</span></code></p>
</section>
<section id="sidecar-files">
<h2>Sidecar files<a class="headerlink" href="#sidecar-files" title="Permalink to this headline">#</a></h2>
<h2>Sidecar files<a class="headerlink" href="#sidecar-files" title="Permalink to this heading">#</a></h2>
<p>Another way to export metadata about your photos is through the use of sidecar files. These are files that have the same name as your photo (but with a different extension) and carry the metadata. Many digital asset management applications (for example, PhotoPrism, Lightroom, Digikam, etc.) can read or write sidecar files. osxphotos can export metadata in exiftool compatible JSON and XMP formats using the <code class="docutils literal notranslate"><span class="pre">--sidecar</span></code> option. For example, to output metadata to XMP sidecars:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--sidecar</span> <span class="pre">XMP</span></code></p>
<p>Unlike <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code>, you do not need to install exiftool to use the <code class="docutils literal notranslate"><span class="pre">--sidecar</span></code> feature. Many of the same configuration options that apply to <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> to modify metadata, for example, <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code> can also be used with <code class="docutils literal notranslate"><span class="pre">--sidecar</span></code>.</p>
@@ -349,7 +359,7 @@
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--sidecar</span> <span class="pre">XMP</span> <span class="pre">-sidecar-drop-ext</span></code></p>
</section>
<section id="updating-a-previous-export">
<h2>Updating a previous export<a class="headerlink" href="#updating-a-previous-export" title="Permalink to this headline">#</a></h2>
<h2>Updating a previous export<a class="headerlink" href="#updating-a-previous-export" title="Permalink to this heading">#</a></h2>
<p>If you want to use osxphotos to perform periodic backups of your Photos library rather than a one-time export, use the <code class="docutils literal notranslate"><span class="pre">--update</span></code> option. When <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span></code> is run, it creates a database file named <code class="docutils literal notranslate"><span class="pre">.osxphotos_export.db</span></code> in the export folder. (<strong>Note</strong> Because the filename starts with a “.”, you wont see it in Finder which treats “dot-files” like this as hidden. You will see the file in the Terminal.) . If you run osxphotos with the <code class="docutils literal notranslate"><span class="pre">--update</span></code> option, it will look for this database file and, if found, use it to retrieve state information from the last time it was run to only export new or changed files. For example:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--update</span></code></p>
<p>will read the export database located in <code class="docutils literal notranslate"><span class="pre">/path/to/export/.osxphotos_export.db</span></code> and only export photos that have been added or changed since the last time osxphotos was run. You can run osxphotos with the <code class="docutils literal notranslate"><span class="pre">--update</span></code> option even if its never been run before. If the database isnt found, osxphotos will create it. If you run <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span></code> without <code class="docutils literal notranslate"><span class="pre">--update</span></code> in a folder where you had previously exported photos, it will re-export all the photos. If your intent is to keep a periodic backup of your Photos Library up to date with osxphotos, you should always use <code class="docutils literal notranslate"><span class="pre">--update</span></code>.</p>
@@ -359,51 +369,55 @@
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--update</span> <span class="pre">--ignore-signature</span></code></p>
</section>
<section id="dry-run">
<h2>Dry Run<a class="headerlink" href="#dry-run" title="Permalink to this headline">#</a></h2>
<h2>Dry Run<a class="headerlink" href="#dry-run" title="Permalink to this heading">#</a></h2>
<p>You can use the <code class="docutils literal notranslate"><span class="pre">--dry-run</span></code> option to have osxphotos “dry run” or test an export without actually exporting any files. When combined with the <code class="docutils literal notranslate"><span class="pre">--verbose</span></code> option, which causes osxphotos to print out details of every file being exported, this can be a useful tool for testing your export options before actually running a full export. For example, if you are learning the template system and want to verify that your <code class="docutils literal notranslate"><span class="pre">--directory</span></code> and <code class="docutils literal notranslate"><span class="pre">--filename</span></code> templates are correct, <code class="docutils literal notranslate"><span class="pre">--dry-run</span> <span class="pre">--verbose</span></code> will print out the name of each file being exported.</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--dry-run</span> <span class="pre">--verbose</span></code></p>
</section>
<section id="creating-a-report-of-all-exported-files">
<h2>Creating a report of all exported files<a class="headerlink" href="#creating-a-report-of-all-exported-files" title="Permalink to this headline">#</a></h2>
<h2>Creating a report of all exported files<a class="headerlink" href="#creating-a-report-of-all-exported-files" title="Permalink to this heading">#</a></h2>
<p>You can use the <code class="docutils literal notranslate"><span class="pre">--report</span></code> option to create a report, in comma-separated values (CSV) format that will list the details of all files that were exported, skipped, missing, etc. This file format is compatible with programs such as Microsoft Excel. Provide the name of the report after the <code class="docutils literal notranslate"><span class="pre">--report</span></code> option:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--report</span> <span class="pre">export.csv</span></code></p>
<p>You can also create reports in JSON or SQLite format by changing the extension of the report filename. For example, to create a JSON report:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--report</span> <span class="pre">export.json</span></code></p>
<p>And to create a SQLite report:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--report</span> <span class="pre">export.sqlite</span></code></p>
</section>
<section id="exporting-only-certain-photos">
<h2>Exporting only certain photos<a class="headerlink" href="#exporting-only-certain-photos" title="Permalink to this headline">#</a></h2>
<h2>Exporting only certain photos<a class="headerlink" href="#exporting-only-certain-photos" title="Permalink to this heading">#</a></h2>
<p>By default, osxphotos will export your entire Photos library. If you want to export only certain photos, osxphotos provides a rich set of “query options” that allow you to query the Photos database to filter out only certain photos that match your query criteria. The tutorial does not cover all the query options as there are over 50 of themread the help text (<code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">help</span> <span class="pre">export</span></code>) to better understand the available query options. No matter which subset of photos you would like to export, there is almost certainly a way for osxphotos to filter these. For example, you can filter for only images that contain certain keywords or images without a title, images from a specific time of day or specific date range, images contained in specific albums, etc.</p>
<p>For example, to export only photos with keyword <code class="docutils literal notranslate"><span class="pre">Travel</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--keyword</span> <span class="pre">"Travel"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--keyword</span> <span class="pre">&quot;Travel&quot;</span></code></p>
<p>Like many options in osxphotos, <code class="docutils literal notranslate"><span class="pre">--keyword</span></code> (and most other query options) can be repeated to search for more than one term. For example, to find photos with keyword <code class="docutils literal notranslate"><span class="pre">Travel</span></code> <em>or</em> keyword <code class="docutils literal notranslate"><span class="pre">Vacation</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--keyword</span> <span class="pre">"Travel"</span> <span class="pre">--keyword</span> <span class="pre">"Vacation"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--keyword</span> <span class="pre">&quot;Travel&quot;</span> <span class="pre">--keyword</span> <span class="pre">&quot;Vacation&quot;</span></code></p>
<p>To export only photos contained in the album “Summer Vacation”:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--album</span> <span class="pre">"Summer</span> <span class="pre">Vacation"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--album</span> <span class="pre">&quot;Summer</span> <span class="pre">Vacation&quot;</span></code></p>
<p>In Photos, its possible to have multiple albums with the same name. In this case, osxphotos would export photos from all albums matching the value passed to <code class="docutils literal notranslate"><span class="pre">--album</span></code>. If you wanted to export only one of the albums and this album is in a folder, the <code class="docutils literal notranslate"><span class="pre">--regex</span></code> option (short for “regular expression”), which does pattern matching, could be used with the <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code> template to match the specific album. For example, if you had a “Summer Vacation” album inside the folder “2018” and also one with the same name inside the folder “2019”, you could export just the album “2018/Summer Vacation” using this command:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--regex</span> <span class="pre">"2018/Summer</span> <span class="pre">Vacation"</span> <span class="pre">"{folder_album}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--regex</span> <span class="pre">&quot;2018/Summer</span> <span class="pre">Vacation&quot;</span> <span class="pre">&quot;{folder_album}&quot;</span></code></p>
<p>This command matches the pattern “2018/Summer Vacation” against the full folder/album path for every photo.</p>
<p>There are also a number of query options to export only certain types of photos. For example, to export only photos taken with iPhone “Portrait Mode”:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--portrait</span></code></p>
<p>You can also export photos in a certain date range:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--from-date</span> <span class="pre">"2020-01-01"</span> <span class="pre">--to-date</span> <span class="pre">"2020-02-28"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--from-date</span> <span class="pre">&quot;2020-01-01&quot;</span> <span class="pre">--to-date</span> <span class="pre">&quot;2020-02-28&quot;</span></code></p>
<p>or photos added to the library in the last week:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--added-in-last</span> <span class="pre">"1</span> <span class="pre">week"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--added-in-last</span> <span class="pre">&quot;1</span> <span class="pre">week&quot;</span></code></p>
</section>
<section id="converting-images-to-jpeg-on-export">
<h2>Converting images to JPEG on export<a class="headerlink" href="#converting-images-to-jpeg-on-export" title="Permalink to this headline">#</a></h2>
<h2>Converting images to JPEG on export<a class="headerlink" href="#converting-images-to-jpeg-on-export" title="Permalink to this heading">#</a></h2>
<p>Photos can store images in many different formats. osxphotos can convert non-JPEG images (for example, RAW photos) to JPEG on export using the <code class="docutils literal notranslate"><span class="pre">--convert-to-jpeg</span></code> option. You can specify the JPEG quality (0: worst, 1.0: best) using <code class="docutils literal notranslate"><span class="pre">--jpeg-quality</span></code>. For example:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--convert-to-jpeg</span> <span class="pre">--jpeg-quality</span> <span class="pre">0.9</span></code></p>
</section>
<section id="finder-attributes">
<h2>Finder attributes<a class="headerlink" href="#finder-attributes" title="Permalink to this headline">#</a></h2>
<h2>Finder attributes<a class="headerlink" href="#finder-attributes" title="Permalink to this heading">#</a></h2>
<p>In addition to using <code class="docutils literal notranslate"><span class="pre">exiftool</span></code> to write metadata directly to the image metadata, osxphotos can write certain metadata that is available to the Finder and Spotlight but does not modify the actual image file. This is done through something called extended attributes which are stored in the filesystem with a file but do not actually modify the file itself. Finder tags and Finder comments are common examples of these.</p>
<p>osxphotos can, for example, write any keywords in the image to Finder tags so that you can search for images in Spotlight or the Finder using the <code class="docutils literal notranslate"><span class="pre">tag:tagname</span></code> syntax:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--finder-tag-keywords</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">--finder-tag-keywords</span></code> also works with <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code> as described above in the section on <code class="docutils literal notranslate"><span class="pre">exiftool</span></code>:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--finder-tag-keywords</span> <span class="pre">--keyword-template</span> <span class="pre">"{label}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--finder-tag-keywords</span> <span class="pre">--keyword-template</span> <span class="pre">&quot;{label}&quot;</span></code></p>
<p>The <code class="docutils literal notranslate"><span class="pre">--xattr-template</span></code> option allows you to set a variety of other extended attributes. It is used in the format <code class="docutils literal notranslate"><span class="pre">--xattr-template</span> <span class="pre">ATTRIBUTE</span> <span class="pre">TEMPLATE</span></code> where ATTRIBUTE is one of authors,comment, copyright, description, findercomment, headline, keywords.</p>
<p>For example, to set Finder comment to the photos title and description:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--xattr-template</span> <span class="pre">findercomment</span> <span class="pre">"{title}{newline}{descr}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--xattr-template</span> <span class="pre">findercomment</span> <span class="pre">&quot;{title}{newline}{descr}&quot;</span></code></p>
<p>In the template string above, <code class="docutils literal notranslate"><span class="pre">{newline}</span></code> instructs osxphotos to insert a new line character (”n”) between the title and description. In this example, if <code class="docutils literal notranslate"><span class="pre">{title}</span></code> or <code class="docutils literal notranslate"><span class="pre">{descr}</span></code> is empty, youll get “titlen” or “ndescription” which may not be desired so you can use more advanced features of the template system to handle these cases:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--xattr-template</span> <span class="pre">findercomment</span> <span class="pre">"{title,}{title?{descr?{newline},},}{descr,}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--xattr-template</span> <span class="pre">findercomment</span> <span class="pre">&quot;{title,}{title?{descr?{newline},},}{descr,}&quot;</span></code></p>
<p>Explanation of the template string:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>{title,}{title?{descr?{newline},},}{descr,}
│ │ │ │ │ │ │
@@ -429,7 +443,7 @@
<p>See Extended Attributes section in the help for <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span></code> for additional information about this feature.</p>
</section>
<section id="saving-and-loading-options">
<h2>Saving and loading options<a class="headerlink" href="#saving-and-loading-options" title="Permalink to this headline">#</a></h2>
<h2>Saving and loading options<a class="headerlink" href="#saving-and-loading-options" title="Permalink to this heading">#</a></h2>
<p>If you repeatedly run a complex osxphotos export command (for example, to regularly back-up your Photos library), you can save all the options to a configuration file for future use (<code class="docutils literal notranslate"><span class="pre">--save-config</span> <span class="pre">FILE</span></code>) and then load them (<code class="docutils literal notranslate"><span class="pre">--load-config</span> <span class="pre">FILE</span></code>) instead of repeating each option on the command line.</p>
<p>To save the configuration:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">&lt;all</span> <span class="pre">your</span> <span class="pre">options</span> <span class="pre">here&gt;</span> <span class="pre">--update</span> <span class="pre">--save-config</span> <span class="pre">osxphotos.toml</span></code></p>
@@ -438,10 +452,10 @@
<p>The configuration file is a plain text file in <a class="reference external" href="https://toml.io/en/">TOML</a> format so the <code class="docutils literal notranslate"><span class="pre">.toml</span></code> extension is standard but you can name the file anything you like.</p>
</section>
<section id="run-commands-on-exported-photos-for-post-processing">
<h2>Run commands on exported photos for post-processing<a class="headerlink" href="#run-commands-on-exported-photos-for-post-processing" title="Permalink to this headline">#</a></h2>
<h2>Run commands on exported photos for post-processing<a class="headerlink" href="#run-commands-on-exported-photos-for-post-processing" title="Permalink to this heading">#</a></h2>
<p>You can use the <code class="docutils literal notranslate"><span class="pre">--post-command</span></code> option to run one or more commands against exported files. The <code class="docutils literal notranslate"><span class="pre">--post-command</span></code> option takes two arguments: CATEGORY and COMMAND. CATEGORY is a string that describes which category of file to run the command against. The available categories are described in the help text available via: <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">help</span> <span class="pre">export</span></code>. For example, the <code class="docutils literal notranslate"><span class="pre">exported</span></code> category includes all exported photos and the <code class="docutils literal notranslate"><span class="pre">skipped</span></code> category includes all photos that were skipped when running export with <code class="docutils literal notranslate"><span class="pre">--update</span></code>. COMMAND is an osxphotos template string which will be rendered then passed to the shell for execution.</p>
<p>For example, the following command generates a log of all exported files and their associated keywords:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--post-command</span> <span class="pre">exported</span> <span class="pre">"echo</span> <span class="pre">{shell_quote,{filepath}{comma}{,+keyword,}}</span> <span class="pre">&gt;&gt;</span> <span class="pre">{shell_quote,{export_dir}/exported.txt}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--post-command</span> <span class="pre">exported</span> <span class="pre">&quot;echo</span> <span class="pre">{shell_quote,{filepath}{comma}{,+keyword,}}</span> <span class="pre">&gt;&gt;</span> <span class="pre">{shell_quote,{export_dir}/exported.txt}&quot;</span></code></p>
<p>The special template field <code class="docutils literal notranslate"><span class="pre">{shell_quote}</span></code> ensures a string is properly quoted for execution in the shell. For example, its possible that a file path or keyword in this example has a space in the value and if not properly quoted, this would cause an error in the execution of the command. When running commands, the template <code class="docutils literal notranslate"><span class="pre">{filepath}</span></code> is set to the full path of the exported file and <code class="docutils literal notranslate"><span class="pre">{export_dir}</span></code> is set to the full path of the base export directory.</p>
<p>Explanation of the template string:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>{shell_quote,{filepath}{comma}{,+keyword,}}
@@ -453,17 +467,17 @@
│ │ │
└───&gt; insert a comma
│ │
└───&gt; join the list of keywords together with a ","
└───&gt; join the list of keywords together with a &quot;,&quot;
└───&gt; if no keywords, insert nothing (empty string: "")
└───&gt; if no keywords, insert nothing (empty string: &quot;&quot;)
</pre></div>
</div>
<p>Another example: if you had <code class="docutils literal notranslate"><span class="pre">exiftool</span></code> installed and wanted to wipe all metadata from all exported files, you could use the following:</p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--post-command</span> <span class="pre">exported</span> <span class="pre">"/usr/local/bin/exiftool</span> <span class="pre">-all=</span> <span class="pre">{filepath|shell_quote}"</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--post-command</span> <span class="pre">exported</span> <span class="pre">&quot;/usr/local/bin/exiftool</span> <span class="pre">-all=</span> <span class="pre">{filepath|shell_quote}&quot;</span></code></p>
<p>This command uses the <code class="docutils literal notranslate"><span class="pre">|shell_quote</span></code> template filter instead of the <code class="docutils literal notranslate"><span class="pre">{shell_quote}</span></code> template because the only thing that needs to be quoted is the path to the exported file. Template filters filter the value of the rendered template field. A number of other filters are available and are described in the help text.</p>
</section>
<section id="an-example-from-an-actual-osxphotos-user">
<h2>An example from an actual osxphotos user<a class="headerlink" href="#an-example-from-an-actual-osxphotos-user" title="Permalink to this headline">#</a></h2>
<h2>An example from an actual osxphotos user<a class="headerlink" href="#an-example-from-an-actual-osxphotos-user" title="Permalink to this heading">#</a></h2>
<p>Heres a comprehensive use case from an actual osxphotos user that integrates many of the concepts discussed in this tutorial (thank-you Philippe for contributing this!):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>I usually import my iPhones photo roll on a more or less regular basis, and it
includes photos and videos. As a result, the size ot my Photos library may rise
@@ -471,12 +485,12 @@ very quickly. Nevertheless, I will tag and geolocate everything as Photos has a
quite good keyword management system.
After a while, I want to take most of the videos out of the library and move them
to a separate "videos" folder on a different folder / volume. As I might want to
to a separate &quot;videos&quot; folder on a different folder / volume. As I might want to
use them in Final Cut Pro, and since Final Cut is able to import Finder tags into
its internal library tagging system, I will use osxphotos to do just this.
Picking the videos can be left to Photos, using a smart folder for instance. Then
just add a keyword to all videos to be processed. Here I chose "Quik" as I wanted
just add a keyword to all videos to be processed. Here I chose &quot;Quik&quot; as I wanted
to spot all videos created on my iPhone using the Quik application (now part of
GoPro).
@@ -494,14 +508,14 @@ Finally, use `--strip` to remove any leading or trailing whitespace from process
template fields.
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">~/Desktop/folder</span> <span class="pre">for</span> <span class="pre">exported</span> <span class="pre">videos/</span> <span class="pre">--keyword</span> <span class="pre">Quik</span> <span class="pre">--only-movies</span> <span class="pre">--db</span> <span class="pre">/path</span> <span class="pre">to</span> <span class="pre">my.photoslibrary</span> <span class="pre">--touch-file</span> <span class="pre">--finder-tag-keywords</span> <span class="pre">--person-keyword</span> <span class="pre">--xattr-template</span> <span class="pre">findercomment</span> <span class="pre">"{title}{title?{descr?{newline},},}{descr}"</span> <span class="pre">--exiftool-merge-keywords</span> <span class="pre">--exiftool-merge-persons</span> <span class="pre">--exiftool</span> <span class="pre">--strip</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">~/Desktop/folder</span> <span class="pre">for</span> <span class="pre">exported</span> <span class="pre">videos/</span> <span class="pre">--keyword</span> <span class="pre">Quik</span> <span class="pre">--only-movies</span> <span class="pre">--db</span> <span class="pre">/path</span> <span class="pre">to</span> <span class="pre">my.photoslibrary</span> <span class="pre">--touch-file</span> <span class="pre">--finder-tag-keywords</span> <span class="pre">--person-keyword</span> <span class="pre">--xattr-template</span> <span class="pre">findercomment</span> <span class="pre">&quot;{title}{title?{descr?{newline},},}{descr}&quot;</span> <span class="pre">--exiftool-merge-keywords</span> <span class="pre">--exiftool-merge-persons</span> <span class="pre">--exiftool</span> <span class="pre">--strip</span></code></p>
</section>
<section id="color-themes">
<h2>Color Themes<a class="headerlink" href="#color-themes" title="Permalink to this headline">#</a></h2>
<h2>Color Themes<a class="headerlink" href="#color-themes" title="Permalink to this heading">#</a></h2>
<p>Some osxphotos commands such as export use color themes to colorize the output to make it more legible. The theme may be specified with the <code class="docutils literal notranslate"><span class="pre">--theme</span></code> option. For example: <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--verbose</span> <span class="pre">--theme</span> <span class="pre">dark</span></code> uses a theme suited for dark terminals. If you dont specify the color theme, osxphotos will select a default theme based on the current terminal settings. You can also specify your own default theme. See <code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">help</span> <span class="pre">theme</span></code> for more information on themes and for commands to help manage themes. Themes are defined in <code class="docutils literal notranslate"><span class="pre">.theme</span></code> files in the <code class="docutils literal notranslate"><span class="pre">~/.osxphotos/themes</span></code> directory and use style specifications compatible with the <a class="reference external" href="https://rich.readthedocs.io/en/stable/style.html">rich</a> library.</p>
</section>
<section id="conclusion">
<h2>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline">#</a></h2>
<h2>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this heading">#</a></h2>
<p>osxphotos is very flexible. If you merely want to backup your Photos library, then spending a few minutes to understand the <code class="docutils literal notranslate"><span class="pre">--directory</span></code> option is likely all you need and you can be up and running in minutes. However, if you have a more complex workflow, osxphotos likely provides options to implement your workflow. This tutorial does not attempt to cover every option offered by osxphotos but hopefully it provides a good understanding of what kinds of things are possible and where to explore if you want to learn more.</p>
</section>
</section>
@@ -518,10 +532,10 @@ template fields.
</div>
<div class="title">OSXPhotos Command Line Interface (CLI)</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="overview.html">
<svg><use href="#svg-arrow-right"></use></svg>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
@@ -557,7 +571,7 @@ template fields.
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
Contents
On this page
</span>
</div>
<div class="toc-tree-container">
@@ -601,7 +615,9 @@ template fields.
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>

View File

@@ -61,6 +61,9 @@ Valid filters are:
* `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
* `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
* `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
* ``filter(x)``\ : Filter list of values using predicate x; for example, ``{folder_album|filter(contains Events)}`` returns only folders/albums containing the word 'Events' in their path.
* ``int``\ : Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float.
* ``float``\ : Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int.
e.g. if Photo keywords are ``["FOO","bar"]``\ :
@@ -193,6 +196,8 @@ Template Substitutions
- True if template is being rendered for the edited version of a photo, otherwise False.
* - {favorite}
- Photo has been marked as favorite?; True/False value, use in format '{favorite?VALUE_IF_TRUE,VALUE_IF_FALSE}'
* - {created}
- Photo's creation date in ISO format, e.g. '2020-03-22'
* - {created.date}
- Photo's creation date in ISO format, e.g. '2020-03-22'
* - {created.year}
@@ -219,6 +224,8 @@ Template Substitutions
- 2-digit second of the photo creation time
* - {created.strftime}
- Apply strftime template to file creation date/time. Should be used in form {created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {created.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. See https://strftime.org/ for help on strftime templates.
* - {modified}
- Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified
* - {modified.date}
- Photo's modification date in ISO format, e.g. '2020-03-22'; uses creation date if photo is not modified
* - {modified.year}
@@ -245,6 +252,8 @@ Template Substitutions
- 2-digit second of the photo modification time; uses creation date if photo is not modified
* - {modified.strftime}
- Apply strftime template to file modification date/time. Should be used in form {modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. Uses creation date if photo is not modified. See https://strftime.org/ for help on strftime templates.
* - {today}
- Current date in iso format, e.g. '2020-03-22'
* - {today.date}
- Current date in iso format, e.g. '2020-03-22'
* - {today.year}
@@ -344,9 +353,11 @@ Template Substitutions
* - {cr}
- A carriage return: '\r'
* - {crlf}
- a carriage return + line feed: '\r\n'
- A carriage return + line feed: '\r\n'
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.50.4'
- The osxphotos version, e.g. '0.55.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

View File

@@ -73,6 +73,22 @@ the exported files would be:
/path/to/export/Vacation/IMG_1234.JPG
If your photos are organized in folders and albums in Photos you can preserve this structure on export by using the ``{folder_album}`` template field with the ``--directory`` option. For example, if you have a photo in the album ``Vacation`` which is in the ``Travel`` folder, the following command would export the photo to the ``Travel/Vacation`` directory:
``osxphotos export /path/to/export --directory "{folder_album}"``
Photos can belong to more than one album. In this case, the template field ``{folder_album}`` will expand to all the album names that the photo belongs to. For example, if a photo belongs to the albums ``Vacation`` and ``Travel``\ , the template field ``{folder_album}`` would expand to ``Vacation``\ , ``Travel``. If the photo belongs to no albums, the template field ``{folder_album}`` would expand to "_" (the default value).
All template fields including ``{folder_album}`` can be further filtered using a number of different filters. To convert all directory names to lower case for example, use the ``lower`` filter:
``osxphotos export /path/to/export --directory "{folder_album|lower}"``
If all your photos were organized into various albums under a folder named ``Events`` but some where also included in other top-level albums and you wanted to export only the ``Events`` folder, you could use the ``filter`` option to filter out the other top-level albums by selecting only those folder/album paths that start with ``Events``\ :
``osxphotos export /path/to/export --directory "{folder_album|filter(startswith Events)}"``
You can learn more about the other filters using ``osxphotos help export``.
Specify exported filename
-------------------------
@@ -275,6 +291,14 @@ You can use the ``--report`` option to create a report, in comma-separated value
``osxphotos export /path/to/export --report export.csv``
You can also create reports in JSON or SQLite format by changing the extension of the report filename. For example, to create a JSON report:
``osxphotos export /path/to/export --report export.json``
And to create a SQLite report:
``osxphotos export /path/to/export --report export.sqlite``
Exporting only certain photos
-----------------------------

43
examples/bad_photos.py Normal file
View File

@@ -0,0 +1,43 @@
""" Find 'bad photos' and add them to an album
This is inspired by this blog post: https://www.muchen.ca/blog/2022/cleanup-photos/
This is an osxphotos query function, that when run as follows,
will add all photos with low quality scores to the album 'Bad Photos'
osxphotos query --query-function bad_photos.py::bad_photos --add-to-album "Bad Photos"
"""
from typing import List
from osxphotos import PhotoInfo
# Call this with: osxphotos query --query-function bad_photos.py::bad_photos --add-to-album "Bad Photos"
def bad_photos(photos: List[PhotoInfo]) -> List[PhotoInfo]:
"""your query function should take a list of PhotoInfo objects and return a list of PhotoInfo objects (or empty list)"""
# this example finds bad photos (as measured by Photos' own scoring system)
# don't include screenshots as Photos tends to give low scores to screenshots
return [p for p in photos if not p.screenshot and is_bad_photo(p)]
def is_bad_photo(p: PhotoInfo) -> bool:
"""Look at photo's ScoreInfo to find photos that have low scores
(and hence might be considered bad photos)
"""
return any(
[
p.score.failure < -0.1,
p.score.harmonious_color < -0.1,
p.score.interesting_subject < -0.7,
p.score.intrusive_object_presence < -0.999,
p.score.noise < -0.75,
p.score.pleasant_composition < -0.8,
p.score.pleasant_lighting < -0.7,
p.score.pleasant_perspective < -0.6,
p.score.tastefully_blurred < -0.9,
p.score.well_framed_subject < -0.7,
p.score.well_timed_shot < -0.7,
]
)

View File

@@ -0,0 +1,42 @@
"""Compare two albums in Photos and find the differences."""
import sys
import osxphotos
import click
@click.command()
@click.argument("album1")
@click.argument("album2")
def compare_albums(album1, album2):
print("Loading Photos library...")
photosdb = osxphotos.PhotosDB()
album1_ = None
album2_ = None
for album in photosdb.album_info:
if album.title == album1:
album1_ = album
if album.title == album2:
album2_ = album
if album1_ is None:
print("Album 1 not found:", album1)
sys.exit(1)
if album2_ is None:
print("Album 2 not found:", album2)
sys.exit(1)
print(f"Comparing albums: '{album1}' '{album2}'")
not_in_album2 = [photo for photo in album1_.photos if photo not in album2_.photos]
not_in_album1 = [photo for photo in album2_.photos if photo not in album1_.photos]
print(f"Photos in '{album1}' but not in '{album2}':")
for photo in not_in_album2:
print(f" {photo.original_filename}, {photo.date}, {photo.uuid}")
print(f"Photos in '{album2}' but not in '{album1}':")
for photo in not_in_album1:
print(f" {photo.original_filename}, {photo.date}, {photo.uuid}")
if __name__ == "__main__":
compare_albums()

198
examples/detect_qrcodes.py Normal file
View File

@@ -0,0 +1,198 @@
"""Detect QR Codes in photos in Apple Photos and add qrcode tag/keyword to photos contain a QR Code
Run with `osxphotos run detect_qrcodes.py`
Run with `osxphotos run detect_qrcodes.py --help` for help
All dependencies are already installed as part of a standard osxphotos install.
"""
import datetime
import json
import os
import os.path
from typing import List
import click
import objc
import Quartz
from Cocoa import NSURL
from Foundation import NSDictionary
from photoscript import Photo, PhotosLibrary
from rich import print
from rich.progress import Progress
from osxphotos import PhotosDB
from osxphotos.cli.common import get_data_dir
from osxphotos.sqlitekvstore import SQLiteKVStore
QRCODE_KEYWORD = "qrcode"
def detect_qrcodes_in_image(filepath: str) -> List[str]:
"""Detect QR Codes in images using CIDetector and return text of the found QR Codes"""
with objc.autorelease_pool():
context = Quartz.CIContext.contextWithOptions_(None)
options = NSDictionary.dictionaryWithDictionary_(
{"CIDetectorAccuracy": Quartz.CIDetectorAccuracyHigh}
)
detector = Quartz.CIDetector.detectorOfType_context_options_(
Quartz.CIDetectorTypeQRCode, context, options
)
results = []
input_url = NSURL.fileURLWithPath_(filepath)
input_image = Quartz.CIImage.imageWithContentsOfURL_(input_url)
features = detector.featuresInImage_(input_image)
if not features:
return []
for idx in range(features.count()):
feature = features.objectAtIndex_(idx)
results.append(feature.messageString())
return results
@click.command()
@click.option(
"--keyword",
"-k",
default=QRCODE_KEYWORD,
help=f"Keyword to add to photos with QR Code; default = '{QRCODE_KEYWORD}'.",
)
@click.option(
"--description",
"-d",
is_flag=True,
help="Set the description of the photo to the decoded QR Code text.",
)
@click.option(
"--verbose",
"-V",
"verbose_mode",
is_flag=True,
help="Print verbose output.",
)
@click.option(
"--dry-run",
"-n",
is_flag=True,
help="Dry run mode: don't actually update keywords in Photos library.",
)
@click.option(
"--selected",
"-s",
is_flag=True,
help="Only process selected photos.",
)
@click.option(
"--reset",
"-R",
help="Reset the database of previously processed photos.",
is_flag=True,
)
def detect_qrcodes(keyword, description, verbose_mode, dry_run, selected, reset):
"""Detect QR Codes in your photos and add a tag/keyword to photos containing a QR Code."""
# osxphotos includes a simple sqlite-based key-value store for storing data
# Use this to store photos that have already been processed so if the script is re-run
# it doesn't re-process photos that have already been processed
# get_data_dir() returns the path to the user's XDG data directory, usually ~/.local/share/osxphotos
# reference: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
def verbose(msg):
if not verbose_mode:
return
print(msg)
db_path = os.path.join(get_data_dir(), "qrcodes.db")
if reset:
verbose(f"Resetting database: {db_path}")
if os.path.exists(db_path):
os.remove(db_path)
verbose(f"Using database {db_path}")
# enable write-ahead logging for performance, serialize/deserialize data as JSON
kvstore = SQLiteKVStore(
db_path, wal=True, serialize=json.dumps, deserialize=json.loads
)
# get list of photos to process
verbose("Getting list of photos to process...")
# Capture selection before loading the Photos database it can take a while to process the database
# and the selection may change while the database is being processed
selection = PhotosLibrary().selection if selected else []
# the QR detection doesn't work on movies so exclude movies
photos = PhotosDB().photos(movies=False)
if selected:
selected_uuid = [p.uuid for p in selection]
photos = [p for p in photos if p.uuid in selected_uuid]
# track number of photos processed for reporting at the end
num_photos = len(photos)
num_processed = 0
num_previously_processed = 0
num_skipped = 0
num_qrcodes = 0
# process all the photos
with Progress() as progress:
task = progress.add_task(f"Processing {num_photos} photos", total=num_photos)
for photo in photos:
# check if photo has already been processed
if kvstore.get(photo.uuid):
verbose(
f"Skipping previously processed photo {photo.original_filename} ({photo.uuid})"
)
num_previously_processed += 1
continue
# cv2.imread() doesn't work on some file types like HEIC but every photo has a
# JPEG preview image ("derivative" in Photos) so use that. The preview image is
# smaller and lower-resolution, but in my testing, good enough for QR detection
if not photo.path_derivatives:
verbose(
f"Skipping {photo.original_filename} ({photo.uuid}), could not find photo path"
)
num_skipped += 1
continue
photo_path = photo.path_derivatives[0]
# record that will be stored in the kvstore database
record = {
"datetime": datetime.datetime.now().isoformat(),
"uuid": photo.uuid,
"original_filename": photo.original_filename,
"qrcode": None,
}
verbose(f"Processing {photo.original_filename} ({photo.uuid})")
num_processed += 1
if qrcode_text := detect_qrcodes_in_image(photo_path):
# add qrcode tag/keyword to photo
# osxphotos PhotoInfo objects are read-only but you can get a photoscript Photo object
# that allows you to modify certain data about the Photo via the Photos app AppleScript interface
photo_ = Photo(photo.uuid)
if not dry_run:
photo_.keywords = list(set(photo_.keywords + [keyword]))
if description:
photo_.description = ", ".join(qrcode_text)
record["qrcode"] = qrcode_text
verbose(
f"Added {keyword} to {photo.original_filename} ({photo.uuid}), detected QR Code: {qrcode_text}"
)
num_qrcodes += 1
# store photo in kvstore to indicate it's been processed
if not dry_run:
kvstore.set(photo.uuid, record)
progress.advance(task)
print(f"Processed {num_photos} photos")
print(f"Previously processed {num_previously_processed} photos")
print(f"Skipped {num_skipped} missing photos")
print(f"Processed {num_processed} photos this time")
print(f"Detected {num_qrcodes} QR Codes")
if __name__ == "__main__":
detect_qrcodes()

View File

@@ -55,9 +55,6 @@ def export(export_path, library_path, uuid):
coords = face.face_rect()
draw.rectangle(coords, width=3)
draw.ellipse(get_circle_points(face.center, 3), width=1)
draw.text(face.mouth, "M", fill=(255, 255, 255, 255))
draw.text(face.left_eye, "L", fill=(255, 255, 255, 255))
draw.text(face.right_eye, "R", fill=(255, 255, 255, 255))
im.save(export[0])
else:
print(f"no photos exported for {p.uuid}")

View File

@@ -0,0 +1,28 @@
""" Example function for use with osxphotos import --post-function option """
import typing as t
import photoscript
import pathlib
def post_function(
photo: photoscript.Photo, filepath: pathlib.Path, verbose: t.Callable, **kwargs
):
"""Call this with osxphotos import /file/to/import --post-function post_function.py::post_function
This will get called immediately after the photo has been imported into Photos
and all metadata been set (e.g. --exiftool, --title, etc.)
Args:
photo: photoscript.Photo instance for the photo that's just been imported
filepath: pathlib.Path to the file that was imported (this is the path to the source file, not the path inside the Photos library)
verbose: A function to print verbose output if --verbose is set; if --verbose is not set, acts as a no-op (nothing gets printed)
**kwargs: reserved for future use; recommend you include **kwargs so your function still works if additional arguments are added in future versions
Notes:
Use verbose(str) instead of print if you want your function to conditionally output text depending on --verbose flag
Any string printed with verbose that contains "warning" or "error" (case-insensitive) will be printed with the appropriate warning or error color
See https://rhettbull.github.io/PhotoScript/ for documentation on photoscript
"""
# add a note to the photo's description
verbose("Adding note to description")
photo.description = f"{photo.description} (imported with osxphotos)"

196
examples/strip_live.py Normal file
View File

@@ -0,0 +1,196 @@
"""Export selected Live photos and re-import just the image portion"""
import pathlib
import sys
import time
from tempfile import TemporaryDirectory
from typing import List
import click
from photoscript import Album, Photo, PhotosLibrary
from rich import print
from osxphotos import PhotoInfo, PhotosDB
DEFAULT_DELETE_ALBUM = "Live Photos to Delete"
DEFAULT_NEW_ALBUM = "Imported Live Photos"
def rename_photos(photo_paths: List[str]) -> List[str]:
"""Given a list of photo paths, rename the photos so names don't clash as duplicated on re-import"""
# use perf_counter_ns as a simple unique ID to ensure each photo has a different name
new_paths = []
for path in photo_paths:
path = pathlib.Path(path)
stem = f"{path.stem}_{time.perf_counter_ns()}"
new_path = path.rename(path.parent / f"{stem}{path.suffix}")
new_paths.append(str(new_path))
return new_paths
def set_metadata_from_photo(source_photo: PhotoInfo, dest_photos: List[Photo]):
"""Set metadata (keywords, albums, title, description, favorite) for dest_photos from source_photo"""
title = source_photo.title
description = source_photo.description
keywords = source_photo.keywords
favorite = source_photo.favorite
# apply metadata to each photo
for dest_photo in dest_photos:
dest_photo.title = title
dest_photo.description = description
dest_photo.keywords = keywords
dest_photo.favorite = favorite
# add photos to albums
album_ids = [a.uuid for a in source_photo.album_info]
for album_id in album_ids:
album = Album(album_id)
album.add(dest_photos)
def process_photo(
photo: Photo,
photosdb: PhotosDB,
keep_originals: bool,
download_missing: bool,
new_album: Album,
delete_album: Album,
):
"""Process each Live Photo to export/re-import it"""
with TemporaryDirectory() as tempdir:
p = photosdb.get_photo(photo.uuid)
if not p.live_photo:
print(
f"[yellow]Skipping non-Live photo {p.original_filename} ({p.uuid})[/]"
)
return
# versions to download (True for edited, False for original)
versions = []
# use photos_export to download from iCloud
photos_export = False
# try to download missing photos only if photo is missing and --download-missing
if keep_originals or not p.hasadjustments:
# export original photo
if not p.path and not download_missing:
print(
f"[yellow]Skipping missing original version of photo {p.original_filename} ({p.uuid}) (you may want to try --download-missing)[/]"
)
return
photos_export = download_missing and not p.path
versions.append(False)
if p.hasadjustments:
if not p.path_edited and not download_missing:
print(
f"[yellow]Skipping missing edited version of photo {p.original_filename} ({p.uuid}) (you may want to try --download-missing)[/]"
)
return
photos_export = photos_export or (download_missing and not p.path_edited)
versions.append(True)
exported = []
for version in versions:
# export the actual photo (without the Live video)
print(
f"Exporting {'edited' if version else 'original'} photo {p.original_filename} ({p.uuid})"
)
if exports := p.export(
tempdir,
live_photo=False,
edited=version,
use_photos_export=photos_export,
):
exported.extend(exports)
else:
print(
f"[red]Error exporting photo {p.original_filename} ({p.uuid})[/]",
file=sys.stderr,
)
if not exported:
return
exported = rename_photos(exported)
print(
f"Re-importing {', '.join([pathlib.Path(p).name for p in exported])} to album '{new_album.name}'"
)
new_photos = new_album.import_photos(exported)
print("Applying metadata to newly imported photos")
set_metadata_from_photo(p, new_photos)
print(f"Moving {p.original_filename} to album '{delete_album.name}'")
delete_album.add([Photo(p.uuid)])
@click.command()
@click.option(
"--download-missing", is_flag=True, help="Download missing files from iCloud."
)
@click.option(
"--keep-originals",
is_flag=True,
help="If photo is edited, also keep the original, unedited photo. "
"Without --keep-originals, only the edited version of a Live photo that has been edited will be kept.",
)
@click.option(
"--delete-album",
"delete_album_name",
default=DEFAULT_DELETE_ALBUM,
help="Album to put Live photos in when they're ready to be deleted; "
f"default = '{DEFAULT_DELETE_ALBUM}'",
)
@click.option(
"--new-album",
"new_album_name",
default=DEFAULT_NEW_ALBUM,
help="Album to put Live photos in when they've been re-imported after stripping the video component; "
f"default = '{DEFAULT_NEW_ALBUM}'",
)
def strip_live_photos(
download_missing, keep_originals, delete_album_name, new_album_name
):
"""Export selected Live photos and re-import just the image portion.
This script can be used to free space in your Photos library by allowing you
to effectively delete just the Live video portion of a Live photo.
The photo part of the Live photo will be exported to a temporary directory then
reimported into Photos. Albums, keywords, title/caption, favorite, and description
will be preserved. Unfortunately person/face data cannot be preserved.
After export Live photos will be moved to an album (which can be set using
--delete-album) so they can be deleted. You can use Command + Delete to put the
photos in the trash after selecting them in the album.
"""
photoslib = PhotosLibrary()
selected = photoslib.selection
if not selected:
print("No photos selected...nothing to do", file=sys.stderr)
sys.exit(1)
print(f"Processing {len(selected)} photo(s)")
print("Loading Photos database")
photosdb = PhotosDB()
new_album = photoslib.album(
new_album_name, top_level=True
) or photoslib.create_album(new_album_name)
delete_album = photoslib.album(
delete_album_name, top_level=True
) or photoslib.create_album(delete_album_name)
for photo in selected:
process_photo(
photo, photosdb, keep_originals, download_missing, new_album, delete_album
)
new_album.spotlight()
if __name__ == "__main__":
strip_live_photos()

View File

@@ -0,0 +1,84 @@
""" Example showing how to use a custom function for osxphotos {function} template
Returns expected path for a missing photos
Use: osxphotos query --missing --field original_path "{function:photopath.py::original}"
or for edited photos: osxphotos query --missing --field edited_path "{function:photopath.py::edited}"
"""
from __future__ import annotations
import os
from typing import List, Optional, Union
from osxphotos import ExportOptions, PhotoInfo
from osxphotos._constants import _MOVIE_TYPE, _PHOTO_TYPE, _PHOTOS_5_SHARED_PHOTO_PATH
def original(
photo: PhotoInfo, options: ExportOptions, args: Optional[str] = None, **kwargs
) -> Union[list[str], str]:
"""returns expected path for original photo or None if path cannot be determined
Args:
photo: osxphotos.PhotoInfo object
options: osxphotos.ExportOptions object
args: optional str of arguments passed to template function
**kwargs: not currently used, placeholder to keep functions compatible with possible changes to {function}
Returns:
str or list of str of values that should be substituted for the {function} template
"""
if photo._info["shared"]:
# shared photo
return os.path.join(
photo._db._library_path,
_PHOTOS_5_SHARED_PHOTO_PATH,
photo._info["directory"],
photo._info["filename"],
)
elif photo._info["directory"].startswith("/"):
# referenced photo
return os.path.join(photo._info["directory"], photo._info["filename"])
else:
# regular photo
return os.path.join(
photo._db._masters_path,
photo._info["directory"],
photo._info["filename"],
)
def edited(
photo: PhotoInfo, options: ExportOptions, args: Optional[str] = None, **kwargs
) -> Union[list[str], str]:
"""returns expected path for edited photo or None if path cannot be determined
Args:
photo: osxphotos.PhotoInfo object
options: osxphotos.ExportOptions object
args: optional str of arguments passed to template function
**kwargs: not currently used, placeholder to keep functions compatible with possible changes to {function}
Returns:
str or list of str of values that should be substituted for the {function} template
"""
if not photo._info["hasAdjustments"]:
return []
library = photo._db._library_path
directory = photo._uuid[0] # first char of uuid
filename = None
if photo._info["type"] == _PHOTO_TYPE:
# it's a photo
if photo._db._photos_ver != 5 and photo.uti == "public.heic":
filename = f"{photo._uuid}_1_201_a.heic"
else:
filename = f"{photo._uuid}_1_201_a.jpeg"
elif photo._info["type"] == _MOVIE_TYPE:
# it's a movie
filename = f"{photo._uuid}_2_0_a.mov"
else:
return []
return os.path.join(library, "resources", "renders", directory, filename)

View File

@@ -0,0 +1,27 @@
""" Example showing how to use a custom function for osxphotos {function} template with the `osxphotos import` command
Use: osxphotos import /path/to/import/*.jpg --album "{function:/path/to/template_function_import.py::example}"
You may place more than one template function in a single file as each is called by name using the {function:file.py::function_name} format
"""
import pathlib
from typing import List, Optional, Union
def example(
filepath: pathlib.Path, args: Optional[str] = None, **kwargs
) -> Union[List, str]:
"""example function for {function} template for use with `osxphotos import`
This example parses filenames in format album_img_123.jpg and returns the album name
Args:
filepath: pathlib.Path object of file being imported
args: optional str of arguments passed to template function
**kwargs: not currently used, placeholder to keep functions compatible with possible changes to {function}
Returns:
str or list of str of values that should be substituted for the {function} template
"""
filename = filepath.stem
fields = filename.split("_")
return fields[0] if len(fields) > 1 else ""

View File

@@ -0,0 +1,39 @@
"""Example function for use with `osxphotos timewarp --function`
Call this as: `osxphotos timewarp --function timewarp_filename.py::parse_date_time_from_filename`
"""
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Callable
from photoscript import Photo
from strpdatetime import strpdatetime
def parse_date_time_from_filename(
photo: Photo, path: str | None, tz_sec: int, tz_name: str, verbose: Callable
) -> tuple[datetime, int]:
"""Function for use with `osxphotos timewarp --function` that parses date/time from filename in format "YYYY-MM-DD FILENAME.jpg"
Args:
photo: Photo object
path: path to photo, which may be None if photo is not on disk
tz_sec: timezone offset from UTC in seconds
tz_name: timezone name
verbose: function to print verbose messages
Returns:
tuple of (new date/time as datetime.datetime, and new timezone offset from UTC in seconds as int)
"""
filename = photo.filename
try:
datetime = strpdatetime(filename, "^%Y-%m-%d")
except ValueError:
verbose(f"Unable to parse date/time from {filename}")
return photo.date, tz_sec
verbose(f"Updating {photo.filename} date/time: {datetime}")
return datetime, tz_sec

93
examples/xmp_rating.py Normal file
View File

@@ -0,0 +1,93 @@
""" Example function for use with osxphotos export --post-function option to set custom XMP:Rating value"""
# See this Reddit post for context: https://www.reddit.com/r/osxphotos/comments/wo4xra/can_i_set_xmprating_based_on_keywords/
import sys
from typing import Callable
from osxphotos import ExportResults, PhotoInfo
from osxphotos.exiftool import ExifTool
from osxphotos.utils import normalize_unicode
# Update this for your custom keyword to rating mapping
RATINGS = {
"★⭐︎⭐︎⭐︎⭐︎": 1,
"★★︎⭐︎⭐︎⭐︎": 2,
"★★★︎⭐︎⭐︎": 3,
"★★★★︎⭐︎": 4,
"★★★★★︎": 5,
}
# normalize the unicode to match what osxphotos uses internally
RATINGS = {normalize_unicode(k): v for k, v in RATINGS.items()}
def rating(photo: PhotoInfo, results: ExportResults, verbose: Callable, **kwargs):
"""Call this with `osxphotos export /path/to/export --post-function xmp_rating.py::rating`
This will get called immediately after the photo has been exported
Args:
photo: PhotoInfo instance for the photo that's just been exported
results: ExportResults instance with information about the files associated with the exported photo
verbose: A function to print verbose output if --verbose is set; if --verbose is not set, acts as a no-op (nothing gets printed)
**kwargs: reserved for future use; recommend you include **kwargs so your function still works if additional arguments are added in future versions
Notes:
Use verbose(str) instead of print if you want your function to conditionally output text depending on --verbose flag
Any string printed with verbose that contains "warning" or "error" (case-insensitive) will be printed with the appropriate warning or error color
Will not be called if --dry-run flag is enabled
Will be called immediately after export and before any --post-command commands are executed
"""
# ExportResults has the following properties
# fields with filenames contain the full path to the file
# exported: list of all files exported
# new: list of all new files exported (--update)
# updated: list of all files updated (--update)
# skipped: list of all files skipped (--update)
# exif_updated: list of all files that were updated with --exiftool
# touched: list of all files that had date updated with --touch-file
# converted_to_jpeg: list of files converted to jpeg with --convert-to-jpeg
# sidecar_json_written: list of all JSON sidecar files written
# sidecar_json_skipped: list of all JSON sidecar files skipped (--update)
# sidecar_exiftool_written: list of all exiftool sidecar files written
# sidecar_exiftool_skipped: list of all exiftool sidecar files skipped (--update)
# sidecar_xmp_written: list of all XMP sidecar files written
# sidecar_xmp_skipped: list of all XMP sidecar files skipped (--update)
# missing: list of all missing files
# error: list tuples of (filename, error) for any errors generated during export
# exiftool_warning: list of tuples of (filename, warning) for any warnings generated by exiftool with --exiftool
# exiftool_error: list of tuples of (filename, error) for any errors generated by exiftool with --exiftool
# xattr_written: list of files that had extended attributes written
# xattr_skipped: list of files that where extended attributes were skipped (--update)
# deleted_files: list of deleted files
# deleted_directories: list of deleted directories
# exported_album: list of tuples of (filename, album_name) for exported files added to album with --add-exported-to-album
# skipped_album: list of tuples of (filename, album_name) for skipped files added to album with --add-skipped-to-album
# missing_album: list of tuples of (filename, album_name) for missing files added to album with --add-missing-to-album
# metadata_changed: list of filenames that had metadata changes since last export
xmp_rating = None
# check to see if there's a rating to apply
for rating in RATINGS:
if rating in photo.keywords:
xmp_rating = RATINGS[rating]
break
if not xmp_rating:
# nothing to do
verbose("No XMP:Rating to set")
return
# update each exported file with the new rating
for filename in results.exported:
verbose(
f"Updating [filepath]{filename}[/] with XMP:Rating=[num]{xmp_rating}[/]"
)
with ExifTool(filename) as exiftool:
if not exiftool.setvalue("XMP:Rating", xmp_rating):
print(
f"Error updating XMP:Rating for file {filename}: {exiftool.error}",
file=sys.stderr,
)

View File

@@ -24,11 +24,32 @@ datas.extend(
]
)
package_imports = [["photoscript", ["photoscript.applescript"]]]
package_imports = [
["photoscript", ["photoscript.applescript"]],
]
for package, files in package_imports:
proot = os.path.dirname(importlib.import_module(package).__file__)
datas.extend((os.path.join(proot, f), package) for f in files)
# Add attribute data files for osxmetadata
# There is probably a better way to do this but this works
proot = os.path.dirname(importlib.import_module("osxmetadata").__file__)
for attribute_data in [
"audio_attributes.json",
"common_attributes.json",
"filesystem_attributes.json",
"image_attributes.json",
"mdimporter_constants.json",
"nsurl_resource_keys.json",
"video_attributes.json",
]:
datas.append(
(
os.path.join(proot, "attribute_data", attribute_data),
"osxmetadata/attribute_data",
)
)
block_cipher = None
a = Analysis(
@@ -63,4 +84,5 @@ exe = EXE(
upx_exclude=[],
runtime_tmpdir=None,
console=True,
target_architecture="universal2",
)

View File

@@ -12,6 +12,7 @@ from .momentinfo import MomentInfo
from .personinfo import PersonInfo
from .photoexporter import ExportOptions, ExportResults, PhotoExporter
from .photoinfo import PhotoInfo
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
from .photosdb import PhotosDB
from .photosdb._photosdb_process_comments import CommentInfo, LikeInfo
from .phototemplate import PhotoTemplate
@@ -43,6 +44,8 @@ __all__ = [
"PhotoExporter",
"PhotoInfo",
"PhotoTemplate",
"PhotosAlbum",
"PhotosAlbumPhotoScript",
"PhotosDB",
"PlaceInfo",
"ProjectInfo",

View File

@@ -113,11 +113,15 @@ _TESTED_OS_VERSIONS = [
("11", "4"),
("11", "5"),
("11", "6"),
("11", "7"),
("12", "0"),
("12", "1"),
("12", "2"),
("12", "3"),
("12", "4"),
("12", "5"),
("12", "6"),
("13", "0"),
]
# Photos 5 has persons who are empty string if unidentified face
@@ -168,64 +172,99 @@ _MAX_IPTC_KEYWORD_LEN = 64
# If anyone has a keyword matching this, then too bad...
_OSXPHOTOS_NONE_SENTINEL = "OSXPhotosXYZZY42_Sentinel$"
# SearchInfo categories for Photos 5, corresponds to categories in database/search/psi.sqlite
SEARCH_CATEGORY_LABEL = 2024
SEARCH_CATEGORY_PLACE_NAME = 1
SEARCH_CATEGORY_STREET = 2
SEARCH_CATEGORY_NEIGHBORHOOD = 3
SEARCH_CATEGORY_LOCALITY_4 = 4
SEARCH_CATEGORY_SUB_LOCALITY_5 = 5
SEARCH_CATEGORY_SUB_LOCALITY_6 = 6
SEARCH_CATEGORY_CITY = 7
SEARCH_CATEGORY_LOCALITY_8 = 8
SEARCH_CATEGORY_NAMED_AREA = 9
SEARCH_CATEGORY_ALL_LOCALITY = [
SEARCH_CATEGORY_LOCALITY_4,
SEARCH_CATEGORY_SUB_LOCALITY_5,
SEARCH_CATEGORY_SUB_LOCALITY_6,
SEARCH_CATEGORY_LOCALITY_8,
SEARCH_CATEGORY_NAMED_AREA,
]
SEARCH_CATEGORY_STATE = 10
SEARCH_CATEGORY_STATE_ABBREVIATION = 11
SEARCH_CATEGORY_COUNTRY = 12
SEARCH_CATEGORY_BODY_OF_WATER = 14
SEARCH_CATEGORY_MONTH = 1014
SEARCH_CATEGORY_YEAR = 1015
SEARCH_CATEGORY_KEYWORDS = 2016
SEARCH_CATEGORY_TITLE = 2017
SEARCH_CATEGORY_DESCRIPTION = 2018
SEARCH_CATEGORY_HOME = 2020
SEARCH_CATEGORY_PERSON = 2021
SEARCH_CATEGORY_ACTIVITY = 2027
SEARCH_CATEGORY_HOLIDAY = 2029
SEARCH_CATEGORY_SEASON = 2030
SEARCH_CATEGORY_WORK = 2036
SEARCH_CATEGORY_VENUE = 2038
SEARCH_CATEGORY_VENUE_TYPE = 2039
SEARCH_CATEGORY_PHOTO_TYPE_VIDEO = 2044
SEARCH_CATEGORY_PHOTO_TYPE_SLOMO = 2045
SEARCH_CATEGORY_PHOTO_TYPE_LIVE = 2046
SEARCH_CATEGORY_PHOTO_TYPE_SCREENSHOT = 2047
SEARCH_CATEGORY_PHOTO_TYPE_PANORAMA = 2048
SEARCH_CATEGORY_PHOTO_TYPE_TIMELAPSE = 2049
SEARCH_CATEGORY_PHOTO_TYPE_BURSTS = 2052
SEARCH_CATEGORY_PHOTO_TYPE_PORTRAIT = 2053
SEARCH_CATEGORY_PHOTO_TYPE_SELFIES = 2054
SEARCH_CATEGORY_PHOTO_TYPE_FAVORITES = 2055
SEARCH_CATEGORY_MEDIA_TYPES = [
SEARCH_CATEGORY_PHOTO_TYPE_VIDEO,
SEARCH_CATEGORY_PHOTO_TYPE_SLOMO,
SEARCH_CATEGORY_PHOTO_TYPE_LIVE,
SEARCH_CATEGORY_PHOTO_TYPE_SCREENSHOT,
SEARCH_CATEGORY_PHOTO_TYPE_PANORAMA,
SEARCH_CATEGORY_PHOTO_TYPE_TIMELAPSE,
SEARCH_CATEGORY_PHOTO_TYPE_BURSTS,
SEARCH_CATEGORY_PHOTO_TYPE_PORTRAIT,
SEARCH_CATEGORY_PHOTO_TYPE_SELFIES,
SEARCH_CATEGORY_PHOTO_TYPE_FAVORITES,
]
SEARCH_CATEGORY_PHOTO_NAME = 2056
class SearchCategory:
"""SearchInfo categories for Photos 5+; corresponds to categories in database/search/psi.sqlite:groups.category
Note: This is a simple enum class; the values are not meant to be changed.
Would be great if Python enums actually let you access the value directly.
"""
LABEL = 2024
PLACE_NAME = 1
STREET = 2
NEIGHBORHOOD = 3
LOCALITY_4 = 4
SUB_LOCALITY_5 = 5
SUB_LOCALITY_6 = 6
CITY = 7
LOCALITY_8 = 8
NAMED_AREA = 9
ALL_LOCALITY = [
LOCALITY_4,
SUB_LOCALITY_5,
SUB_LOCALITY_6,
LOCALITY_8,
NAMED_AREA,
]
STATE = 10
STATE_ABBREVIATION = 11
COUNTRY = 12
BODY_OF_WATER = 14
MONTH = 1014
YEAR = 1015
KEYWORDS = 2016
TITLE = 2017
DESCRIPTION = 2018
HOME = 2020
PERSON = 2021
ACTIVITY = 2027
HOLIDAY = 2029
SEASON = 2030
WORK = 2036
VENUE = 2038
VENUE_TYPE = 2039
PHOTO_TYPE_VIDEO = 2044
PHOTO_TYPE_SLOMO = 2045
PHOTO_TYPE_LIVE = 2046
PHOTO_TYPE_SCREENSHOT = 2047
PHOTO_TYPE_PANORAMA = 2048
PHOTO_TYPE_TIMELAPSE = 2049
PHOTO_TYPE_BURSTS = 2052
PHOTO_TYPE_PORTRAIT = 2053
PHOTO_TYPE_SELFIES = 2054
PHOTO_TYPE_FAVORITES = 2055
MEDIA_TYPES = [
PHOTO_TYPE_VIDEO,
PHOTO_TYPE_SLOMO,
PHOTO_TYPE_LIVE,
PHOTO_TYPE_SCREENSHOT,
PHOTO_TYPE_PANORAMA,
PHOTO_TYPE_TIMELAPSE,
PHOTO_TYPE_BURSTS,
PHOTO_TYPE_PORTRAIT,
PHOTO_TYPE_SELFIES,
PHOTO_TYPE_FAVORITES,
]
PHOTO_NAME = 2056
CAMERA = None # Photos 8+ only
DETECTED_TEXT = None # Photos 8+ only
class SearchCategory_Photos8(SearchCategory):
"""Search categories for Photos 8"""
# Many of the category values changed in Ventura / Photos 8
# and some new categories were added
LABEL = 1500
MONTH = 1100
YEAR = 1101
HOLIDAY = 1103
SEASON = 1104
KEYWORDS = 1200
TITLE = 1201
DESCRIPTION = 1202
DETECTED_TEXT = 1203 # new in Photos 8
PERSON = 1300
ACTIVITY = 1600
PHOTO_TYPE_FAVORITES = 2000
PHOTO_NAME = 2100
CAMERA = 2300 # new in Photos 8
def search_category_factory(version: int) -> SearchCategory:
"""Return SearchCategory class for Photos version"""
return SearchCategory_Photos8 if version >= 8 else SearchCategory
# Max filename length on MacOS
@@ -260,10 +299,9 @@ EXTENDED_ATTRIBUTE_NAMES = [
"description",
"findercomment",
"headline",
"keywords",
"participants",
"projects",
"rating",
"starrating",
"subject",
"title",
"version",

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.50.4"
__version__ = "0.55.2"

View File

@@ -302,6 +302,12 @@ class ImportInfo(AlbumInfoBaseClass):
self._photos = self._db.photos_by_uuid(sorted_uuid)
return self._photos
def __bool__(self):
"""Always returns True
A photo without an import session will return None for import_info,
thus if import_info is not None, it must be a valid import_info object (#820)
"""
return True
class ProjectInfo(AlbumInfo):
"""

View File

@@ -58,6 +58,7 @@ from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
from .labels import labels
from .list import _list_libraries, list_libraries
from .orphans import orphans
from .persons import persons
from .photo_inspect import photo_inspect
from .places import places
@@ -88,6 +89,7 @@ __all__ = [
"list_libraries",
"list_libraries",
"load_uuid_from_file",
"orphans",
"persons",
"photo_inspect",
"places",

View File

@@ -15,11 +15,13 @@ from .export import export
from .exportdb import exportdb
from .grep import grep
from .help import help
from .import_cli import import_cli
from .info import info
from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
from .labels import labels
from .list import list_libraries
from .orphans import orphans
from .persons import persons
from .photo_inspect import photo_inspect
from .places import places
@@ -74,11 +76,13 @@ for command in [
exportdb,
grep,
help,
import_cli,
info,
install,
keywords,
labels,
list_libraries,
orphans,
persons,
photo_inspect,
places,

View File

@@ -35,6 +35,7 @@ __all__ = [
"DB_OPTION",
"DEBUG_OPTIONS",
"DELETED_OPTIONS",
"FIELD_OPTION",
"JSON_OPTION",
"QUERY_OPTIONS",
"THEME_OPTION",
@@ -116,6 +117,19 @@ JSON_OPTION = click.option(
help="Print output in JSON format.",
)
FIELD_OPTION = click.option(
"--field",
"-f",
metavar="FIELD TEMPLATE",
multiple=True,
nargs=2,
help="Output only specified custom fields. "
"FIELD is the name of the field and TEMPLATE is the template to use as the field value. "
"May be repeated to output multiple fields. "
"For example, to output photo uuid, name, and title: "
'`--field uuid "{uuid}" --field name "{original_name}" --field title "{title}"`.',
)
def DELETED_OPTIONS(f):
o = click.option
@@ -420,6 +434,12 @@ def QUERY_OPTIONS(f):
is_flag=True,
help="Search for photos that were imported as referenced files (not copied into Photos library).",
),
o(
"--not-reference",
is_flag=True,
help="Search for photos that are not references, that is, they were copied into the Photos library "
"and are managed by Photos.",
),
o(
"--in-album",
is_flag=True,

View File

@@ -3,24 +3,66 @@
import click
import osxphotos
from osxphotos.cli.click_rich_echo import (
rich_click_echo,
set_rich_console,
set_rich_theme,
)
from osxphotos.phototemplate import RenderOptions
from osxphotos.queryoptions import QueryOptions
from .common import DB_ARGUMENT, DB_OPTION, DELETED_OPTIONS, JSON_OPTION, get_photos_db
from .color_themes import get_default_theme
from .common import (
DB_ARGUMENT,
DB_OPTION,
DELETED_OPTIONS,
FIELD_OPTION,
JSON_OPTION,
get_photos_db,
)
from .list import _list_libraries
from .print_photo_info import print_photo_info
from .print_photo_info import print_photo_fields, print_photo_info
from .verbose import get_verbose_console
@click.command()
@DB_OPTION
@JSON_OPTION
@DELETED_OPTIONS
@FIELD_OPTION
@click.option(
"--print",
"print_template",
metavar="TEMPLATE",
multiple=True,
help="Render TEMPLATE string for each photo queried and print to stdout. "
"TEMPLATE is an osxphotos template string. "
"This may be useful for creating custom reports, etc. "
"If --print TEMPLATE is provided, regular output is suppressed "
"and only the rendered TEMPLATE values are printed. "
"May be repeated to print multiple template strings. ",
)
@DB_ARGUMENT
@click.pass_obj
@click.pass_context
def dump(ctx, cli_obj, db, json_, deleted, deleted_only, photos_library):
def dump(
ctx,
cli_obj,
db,
deleted,
deleted_only,
field,
json_,
photos_library,
print_template,
):
"""Print list of all photos & associated info from the Photos library."""
db = get_photos_db(*photos_library, db, cli_obj.db)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
cli_json = cli_obj.json if cli_obj is not None else None
db = get_photos_db(*photos_library, db, cli_db)
if db is None:
click.echo(ctx.obj.group.commands["dump"].get_help(ctx), err=True)
click.echo("\n\nLocated the following Photos library databases: ", err=True)
@@ -33,6 +75,10 @@ def dump(ctx, cli_obj, db, json_, deleted, deleted_only, photos_library):
click.echo(ctx.obj.group.commands["dump"].get_help(ctx), err=True)
return
# set console for rich_echo to be same as for verbose_
set_rich_console(get_verbose_console())
set_rich_theme(get_default_theme())
photosdb = osxphotos.PhotosDB(dbfile=db)
if deleted or deleted_only:
photos = photosdb.photos(movies=True, intrash=True)
@@ -41,4 +87,28 @@ def dump(ctx, cli_obj, db, json_, deleted, deleted_only, photos_library):
if not deleted_only:
photos += photosdb.photos(movies=True)
print_photo_info(photos, json_ or cli_obj.json)
if not print_template and not field:
# just dump and be done
print_photo_info(photos, cli_json or json_)
return
if field:
print_photo_fields(photos, field, cli_json or json_)
if print_template:
# have print template(s)
options = RenderOptions()
for p in photos:
for template in print_template:
rendered_templates, unmatched = p.render_template(
template,
options,
)
if unmatched:
rich_click_echo(
f"[warning]Unmatched template field: {unmatched}[/]"
)
for rendered_template in rendered_templates:
if not rendered_template:
continue
print(rendered_template)

View File

@@ -11,10 +11,16 @@ import shlex
import subprocess
import sys
import time
from typing import Dict
from typing import Iterable, List, Optional, Tuple
import click
import osxmetadata
from osxmetadata import (
MDITEM_ATTRIBUTE_DATA,
MDITEM_ATTRIBUTE_SHORT_NAMES,
OSXMetaData,
Tag,
)
from osxmetadata.constants import _TAGS_NAMES
import osxphotos
from osxphotos._constants import (
@@ -44,7 +50,7 @@ from osxphotos.datetime_formatter import DateTimeFormatter
from osxphotos.debug import is_debug, set_debug
from osxphotos.exiftool import get_exiftool_path
from osxphotos.export_db import ExportDB, ExportDBInMemory
from osxphotos.fileutil import FileUtil, FileUtilNoOp
from osxphotos.fileutil import FileUtil, FileUtilNoOp, FileUtilShUtil
from osxphotos.path_utils import is_valid_filepath, sanitize_filename, sanitize_filepath
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
from osxphotos.photoinfo import PhotoInfoNone
@@ -86,7 +92,7 @@ from .common import (
from .help import ExportCommand, get_help_msg
from .list import _list_libraries
from .param_types import ExportDBType, FunctionCall, TemplateString
from .report_writer import report_writer_factory, ReportWriterNoOp
from .report_writer import ReportWriterNoOp, report_writer_factory
from .rich_progress import rich_progress
from .verbose import get_verbose_console, time_stamp, verbose_print
@@ -364,6 +370,13 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
is_flag=True,
help="Merge any persons found in the original file with persons used for '--exiftool' and '--sidecar'.",
)
@click.option(
"--favorite-rating",
is_flag=True,
help="When used with --exiftool or --sidecar, "
"set XMP:Rating=5 for photos marked as Favorite and XMP:Rating=0 for non-Favorites. "
"If not specified, XMP:Rating is not set.",
)
@click.option(
"--ignore-date-modified",
is_flag=True,
@@ -539,6 +552,26 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
"for example, your own scripts or other files. Be sure this is what you intend before using "
"--cleanup. Use --dry-run with --cleanup first if you're not certain.",
)
@click.option(
"--keep",
metavar="KEEP_PATH",
nargs=1,
multiple=True,
help="When used with --cleanup, prevents file or directory KEEP_PATH from being deleted "
"when cleanup is run. Use this if there are files in the export directory that you don't "
"want to be deleted when --cleanup is run. "
"KEEP_PATH may be a file path, e.g. '/Volumes/Photos/keep.jpg', "
"or a file path and wild card, e.g. '/Volumes/Photos/*.txt', "
"or a directory, e.g. '/Volumes/Photos/KeepMe'. "
"KEEP_PATH may be an absolute path or a relative path. "
"If it is relative, it must be relative to the export destination. "
"For example if export destination is `/Volumes/Photos` and you want to keep all `.txt` files, "
'you can specify `--keep "/Volumes/Photos/*.txt"` or `--keep "*.txt"`. '
"If wild card is used, KEEP_PATH must be enclosed in quotes to prevent the shell from expanding the wildcard, "
'e.g. `--keep "/Volumes/Photos/*.txt"`. '
"If KEEP_PATH is a directory, all files and directories contained in KEEP_PATH will be kept. "
"--keep may be repeated to keep additional files/directories.",
)
@click.option(
"--add-exported-to-album",
metavar="ALBUM",
@@ -618,6 +651,15 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
"the Photos library.",
type=click.Path(dir_okay=True, file_okay=False, exists=True),
)
@click.option(
"--alt-copy",
is_flag=True,
help="Use alternate copy method that may be more reliable for some "
"network attached storage (NAS) devices. Use --alt-copy if you experience "
"problems exporting to a NAS device or SMB volume. "
"Unlike the default copy method, --alt-copy does not support "
"copy-on-write on APFS volumes nor does it preserve filesystem metadata.",
)
@click.option(
"--load-config",
required=False,
@@ -647,6 +689,17 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
is_flag=True,
help="If specified, saves the config file but does not export any files; must be used with --save-config.",
)
@click.option(
"--print",
"print_template",
metavar="TEMPLATE",
multiple=True,
help="Render TEMPLATE string for each photo being exported and print to stdout. "
"TEMPLATE is an osxphotos template string. "
"This may be useful for creating custom reports, etc. "
"TEMPLATE will be printed after the photo is exported or skipped. "
"May be repeated to print multiple template strings. ",
)
@click.option(
"--beta",
is_flag=True,
@@ -701,6 +754,7 @@ def export(
added_in_last,
album_keyword,
album,
alt_copy,
append,
beta,
burst,
@@ -730,6 +784,7 @@ def export(
exportdb,
external_edit,
favorite,
favorite_rating,
filename_template,
finder_tag_keywords,
finder_tag_template,
@@ -749,6 +804,7 @@ def export(
is_reference,
jpeg_ext,
jpeg_quality,
keep,
keyword_template,
keyword,
label,
@@ -776,6 +832,7 @@ def export(
not_live,
not_panorama,
not_portrait,
not_reference,
not_screenshot,
not_selfie,
not_shared,
@@ -796,6 +853,7 @@ def export(
preview_if_missing,
preview_suffix,
preview,
print_template,
profile_sort,
profile,
query_eval,
@@ -921,6 +979,7 @@ def export(
add_skipped_to_album = cfg.add_skipped_to_album
album = cfg.album
album_keyword = cfg.album_keyword
alt_copy = cfg.alt_copy
append = cfg.append
beta = cfg.beta
burst = cfg.burst
@@ -949,6 +1008,7 @@ def export(
exportdb = cfg.exportdb
external_edit = cfg.external_edit
favorite = cfg.favorite
favorite_rating = cfg.favorite_rating
filename_template = cfg.filename_template
finder_tag_keywords = cfg.finder_tag_keywords
finder_tag_template = cfg.finder_tag_template
@@ -965,8 +1025,10 @@ def export(
ignore_date_modified = cfg.ignore_date_modified
ignore_signature = cfg.ignore_signature
in_album = cfg.in_album
is_reference = cfg.is_reference
jpeg_ext = cfg.jpeg_ext
jpeg_quality = cfg.jpeg_quality
keep = cfg.keep
keyword = cfg.keyword
keyword_template = cfg.keyword_template
label = cfg.label
@@ -993,6 +1055,7 @@ def export(
not_live = cfg.not_live
not_panorama = cfg.not_panorama
not_portrait = cfg.not_portrait
not_reference = cfg.not_reference
not_screenshot = cfg.not_screenshot
not_selfie = cfg.not_selfie
not_shared = cfg.not_shared
@@ -1014,6 +1077,7 @@ def export(
preview = cfg.preview
preview_if_missing = cfg.preview_if_missing
preview_suffix = cfg.preview_suffix
print_template = cfg.print_template
query_eval = cfg.query_eval
query_function = cfg.query_function
ramdb = cfg.ramdb
@@ -1098,13 +1162,16 @@ def export(
("slow_mo", "not_slow_mo"),
("time_lapse", "not_time_lapse"),
("title", "no_title"),
("is_reference", "not_reference"),
]
dependent_options = [
("exiftool_merge_keywords", ("exiftool", "sidecar")),
("exiftool_merge_persons", ("exiftool", "sidecar")),
("favorite_rating", ("exiftool", "sidecar")),
("exiftool_option", ("exiftool")),
("ignore_signature", ("update", "force_update")),
("jpeg_quality", ("convert_to_jpeg")),
("keep", ("cleanup")),
("missing", ("download_missing", "use_photos_export")),
("only_new", ("update", "force_update")),
("append", ("report")),
@@ -1277,7 +1344,7 @@ def export(
if ramdb
else ExportDB(dbfile=export_db_path, export_dir=dest)
)
fileutil = FileUtil
fileutil = FileUtilShUtil if alt_copy else FileUtil
if verbose_:
if export_db.was_created:
@@ -1358,6 +1425,7 @@ def export(
not_missing=None,
not_panorama=not_panorama,
not_portrait=not_portrait,
not_reference=not_reference,
not_screenshot=not_screenshot,
not_selfie=not_selfie,
not_shared=not_shared,
@@ -1470,6 +1538,7 @@ def export(
export_live=export_live,
export_preview=preview,
export_raw=export_raw,
favorite_rating=favorite_rating,
filename_template=filename_template,
fileutil=fileutil,
force_update=force_update,
@@ -1613,6 +1682,22 @@ def export(
report_writer.write(export_results)
if print_template:
options = RenderOptions(export_dir=dest)
for template in print_template:
rendered_templates, unmatched = p.render_template(
template,
options,
)
if unmatched:
rich_click_echo(
f"[warning]Unmatched template field: {unmatched}[/]"
)
for rendered_template in rendered_templates:
if not rendered_template:
continue
rich_click_echo(rendered_template)
progress.advance(task)
# handle limit
@@ -1678,9 +1763,18 @@ def export(
+ [r[0] for r in results.error]
+ db_files
)
# if --report, add report file to keep list to prevent it from being deleted
if report:
all_files.append(report)
dirs_to_keep = []
if keep:
files_to_keep, dirs_to_keep = collect_files_to_keep(keep, dest)
all_files += files_to_keep
rich_echo(f"Cleaning up [filepath]{dest}")
cleaned_files, cleaned_dirs = cleanup_files(
dest, all_files, fileutil, verbose_=verbose_
dest, all_files, dirs_to_keep, fileutil, verbose_=verbose_
)
file_str = "files" if len(cleaned_files) != 1 else "file"
dir_str = "directories" if len(cleaned_dirs) != 1 else "directory"
@@ -1725,6 +1819,7 @@ def export_photo(
exiftool_merge_keywords=False,
exiftool_merge_persons=False,
directory=None,
favorite_rating=False,
filename_template=None,
export_raw=None,
album_keyword=None,
@@ -1778,6 +1873,7 @@ def export_photo(
export_live: bool; also export live video component if photo is a live photo; live video will have same name as photo but with .mov extension
export_preview: export the preview image generated by Photos
export_raw: bool; if True exports raw image associate with the photo
favorite_rating: bool; if True, set XMP:Rating=5 for favorite images and XMP:Rating=0 for non-favorites
filename_template: template use to determine output file
fileutil: file util class compatible with FileUtilABC
force_update: bool, only export updated photos but trigger export even if only metadata has changed
@@ -1943,6 +2039,7 @@ def export_photo(
export_original=export_original,
export_preview=export_preview,
export_raw=export_raw,
favorite_rating=favorite_rating,
filename=original_filename,
fileutil=fileutil,
force_update=force_update,
@@ -2057,6 +2154,7 @@ def export_photo(
export_original=False,
export_preview=not export_original and export_preview,
export_raw=not export_original and export_raw,
favorite_rating=favorite_rating,
filename=edited_filename,
fileutil=fileutil,
force_update=force_update,
@@ -2142,6 +2240,7 @@ def export_photo_to_directory(
export_original,
export_preview,
export_raw,
favorite_rating,
filename,
fileutil,
force_update,
@@ -2203,6 +2302,7 @@ def export_photo_to_directory(
exiftool=exiftool,
export_as_hardlink=export_as_hardlink,
export_db=export_db,
favorite_rating=favorite_rating,
fileutil=fileutil,
force_update=force_update,
ignore_date_modified=ignore_date_modified,
@@ -2436,7 +2536,6 @@ def find_files_in_branch(pathname, filename):
# walk down the tree
for root, _, filenames in os.walk(pathname):
# for directory in directories:
# print(os.path.join(root, directory))
for fname in filenames:
if fname == filename and pathlib.Path(root) != pathname:
files.append(os.path.join(root, fname))
@@ -2453,14 +2552,43 @@ def find_files_in_branch(pathname, filename):
return files
def cleanup_files(dest_path, files_to_keep, fileutil, verbose_):
def collect_files_to_keep(
keep: Iterable[str], export_dir: str
) -> Tuple[List[str], List[str]]:
"""Collect all files to keep for --keep/--cleanup.
Args:
keep: Iterable of filepaths to keep; each path may be a filepath, a filepath/wildcard, or a directory path.
export_dir: the export directory which will be used to resolve paths when paths in keep are relative instead of absolute
Returns:
tuple of [files_to_keep], [dirs_to_keep]
"""
export_dir = pathlib.Path(export_dir)
keepers = []
for k in keep:
keeper = pathlib.Path(k).expanduser()
if not keeper.is_absolute():
# relative path: relative to export_dir
keeper = export_dir / keeper
if keeper.is_dir():
keepers.extend(keeper.glob("**/*"))
keepers.extend(keeper.parent.glob(keeper.name))
files_to_keep = [str(k) for k in keepers if k.is_file()]
dirs_to_keep = [str(k) for k in keepers if k.is_dir()]
return files_to_keep, dirs_to_keep
def cleanup_files(dest_path, files_to_keep, dirs_to_keep, fileutil, verbose_):
"""cleanup dest_path by deleting and files and empty directories
not in files_to_keep
Args:
dest_path: path to directory to clean
files_to_keep: list of full file paths to keep (not delete)
fileutile: FileUtil object
dirs_to_keep: list of full dir paths to keep (not delete if they are empty)
fileutil: FileUtil object
verbose_: verbose callable for printing verbose output
Returns:
tuple of (list of files deleted, list of directories deleted)
@@ -2480,6 +2608,8 @@ def cleanup_files(dest_path, files_to_keep, fileutil, verbose_):
deleted_dirs = []
# walk directory tree bottom up and verify contents are empty
for dirpath, _, _ in os.walk(dest_path, topdown=False):
if dirpath in dirs_to_keep:
continue
if not list(pathlib.Path(dirpath).glob("*")):
# directory and directory is empty
verbose_(f"Deleting empty directory {dirpath}")
@@ -2570,9 +2700,9 @@ def write_finder_tags(
]
tags.extend(rendered_tags)
tags = [osxmetadata.Tag(tag) for tag in set(tags)]
tags = [Tag(tag, 0) for tag in set(tags)]
for f in files:
md = osxmetadata.OSXMetaData(f)
md = OSXMetaData(f)
if sorted(md.tags) != sorted(tags):
verbose_(f"Writing Finder tags to {f}")
md.tags = tags
@@ -2634,24 +2764,24 @@ def write_extended_attributes(
written = set()
skipped = set()
for f in files:
md = osxmetadata.OSXMetaData(f)
md = OSXMetaData(f)
for attr, value in attributes.items():
islist = osxmetadata.ATTRIBUTES[attr].list
attr_type = get_metadata_attribute_type(attr) or "str"
if value:
value = ", ".join(value) if not islist else sorted(value)
file_value = md.get_attribute(attr)
value = sorted(list(value)) if attr_type == "list" else ", ".join(value)
file_value = md.get(attr)
if file_value and islist:
if file_value and attr_type == "lists":
file_value = sorted(file_value)
if (not file_value and not value) or file_value == value:
# if both not set or both equal, nothing to do
# get_attribute returns None if not set and value will be [] if not set so can't directly compare
# get returns None if not set and value will be [] if not set so can't directly compare
verbose_(f"Skipping extended attribute {attr} for {f}: nothing to do")
skipped.add(f)
else:
verbose_(f"Writing extended attribute {attr} to {f}")
md.set_attribute(attr, value)
md.set(attr, value)
written.add(f)
return list(written), [f for f in skipped if f not in written]
@@ -2728,3 +2858,23 @@ def render_and_validate_report(report: str, exiftool_path: str, export_dir: str)
sys.exit(1)
return report
def get_metadata_attribute_type(attr: str) -> Optional[str]:
"""Get the type of a metadata attribute
Args:
attr: attribute name
Returns:
type of attribute as string or None if type is not known
"""
if attr in MDITEM_ATTRIBUTE_SHORT_NAMES:
attr = MDITEM_ATTRIBUTE_SHORT_NAMES[attr]
return (
"list"
if attr in _TAGS_NAMES
else MDITEM_ATTRIBUTE_DATA[attr]["python_type"]
if attr in MDITEM_ATTRIBUTE_DATA
else None
)

View File

@@ -5,7 +5,7 @@ import re
import typing as t
import click
import osxmetadata
from osxmetadata import MDITEM_ATTRIBUTE_DATA, MDITEM_ATTRIBUTE_SHORT_NAMES
from rich.console import Console
from rich.markdown import Markdown
@@ -256,34 +256,46 @@ class ExportCommand(click.Command):
formatter.write_text(
"""
Some options (currently '--finder-tag-template', '--finder-tag-keywords', '-xattr-template') write
additional metadata to extended attributes in the file. These options will only work
if the destination filesystem supports extended attributes (most do).
additional metadata accessible by Spotlight to facilitate searching.
For example, --finder-tag-keyword writes all keywords (including any specified by '--keyword-template'
or other options) to Finder tags that are searchable in Spotlight using the syntax: 'tag:tagname'.
For example, if you have images with keyword "Travel" then using '--finder-tag-keywords' you could quickly
find those images in the Finder by typing 'tag:Travel' in the Spotlight search bar.
Finder tags are written to the 'com.apple.metadata:_kMDItemUserTags' extended attribute.
Unlike EXIF metadata, extended attributes do not modify the actual file. Most cloud storage services
do not synch extended attributes. Dropbox does sync them and any changes to a file's extended attributes
Unlike EXIF metadata, extended attributes do not modify the actual file;
the metadata is written to extended attributes associated with the file and the Spotlight metadata database.
Most cloud storage services do not synch extended attributes.
Dropbox does sync them and any changes to a file's extended attributes
will cause Dropbox to re-sync the files.
The following attributes may be used with '--xattr-template':
"""
)
# build help text from all the attribute names
# passed to click.HelpFormatter.write_dl for formatting
attr_tuples = [
(
rich_text("[bold]Attribute[/bold]", width=formatter.width),
rich_text("[bold]Description[/bold]", width=formatter.width),
),
*[
(
attr,
f"{osxmetadata.ATTRIBUTES[attr].help} ({osxmetadata.ATTRIBUTES[attr].constant})",
)
for attr in EXTENDED_ATTRIBUTE_NAMES
],
)
]
for attr_key in sorted(EXTENDED_ATTRIBUTE_NAMES):
# get short and long name
attr = MDITEM_ATTRIBUTE_SHORT_NAMES[attr_key]
short_name = MDITEM_ATTRIBUTE_DATA[attr]["short_name"]
long_name = MDITEM_ATTRIBUTE_DATA[attr]["name"]
constant = MDITEM_ATTRIBUTE_DATA[attr]["xattr_constant"]
# get help text
description = MDITEM_ATTRIBUTE_DATA[attr]["description"]
type_ = MDITEM_ATTRIBUTE_DATA[attr]["help_type"]
attr_help = f"{long_name}; {constant}; {description}; {type_}"
# add to list
attr_tuples.append((short_name, attr_help))
formatter.write_dl(attr_tuples)
formatter.write("\n")
formatter.write_text(

1565
osxphotos/cli/import_cli.py Normal file

File diff suppressed because it is too large Load Diff

156
osxphotos/cli/orphans.py Normal file
View File

@@ -0,0 +1,156 @@
"""Find orphaned photos in a Photos library"""
import os
import os.path
import re
import sys
# using os.path.join is slightly slower inside loop than directly using the method
from os.path import join as joinpath
from os.path import splitext
from pathlib import Path
from typing import Dict
import click
from osxphotos import PhotosDB
from osxphotos._constants import _PHOTOS_4_VERSION
from osxphotos.fileutil import FileUtil
from osxphotos.utils import increment_filename, pluralize
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import set_rich_console, set_rich_theme, set_rich_timestamp
from .color_themes import get_theme
from .common import DB_OPTION, THEME_OPTION, get_photos_db
from .help import get_help_msg
from .list import _list_libraries
from .verbose import get_verbose_console, verbose_print
@click.command(name="orphans")
@click.option(
"--export",
metavar="EXPORT_PATH",
required=False,
type=click.Path(file_okay=False, writable=True, resolve_path=True, exists=True),
help="Export orphans to directory EXPORT_PATH. If --export not specified, orphans are listed but not exported.",
)
@DB_OPTION
@click.option("--verbose", "-V", "verbose", is_flag=True, help="Print verbose output.")
@click.option("--timestamp", is_flag=True, help="Add time stamp to verbose output")
@THEME_OPTION
@click.pass_obj
@click.pass_context
def orphans(ctx, cli_obj, export, db, verbose, timestamp, theme):
"""Find orphaned photos in a Photos library"""
color_theme = get_theme(theme)
verbose_ = verbose_print(
verbose, timestamp, rich=True, theme=color_theme, highlight=False
)
# set console for rich_echo to be same as for verbose_
set_rich_console(get_verbose_console())
set_rich_theme(color_theme)
set_rich_timestamp(timestamp)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
db = get_photos_db(db, cli_db)
if not db:
echo(get_help_msg(orphans), err=True)
echo("\n\nLocated the following Photos library databases: ", err=True)
_list_libraries()
return
verbose_("Loading Photos database")
photosdb = PhotosDB(dbfile=db, verbose=verbose_, rich=True)
if photosdb.db_version <= _PHOTOS_4_VERSION:
echo(
"[error]Orphans can only be used with Photos libraries > version 5 (MacOS Catalina/10.15)[/]",
err=True,
)
sys.exit(1)
photos = photosdb.photos()
photos += photosdb.photos(intrash=True)
# need to add unselected bursts
burst_photos = [bp for p in photos for bp in p.burst_photos]
# will be some duplicates but those will be removed when converting to dict
photos.extend(burst_photos)
uuids_in_db = {photo.uuid: photo for photo in photos}
# walk the Photos library looking for photos associated with a uuid
uuids_in_library = {}
verbose_("Scanning for orphan files")
# originals
verbose_("Scanning original files")
directory = joinpath(photosdb.library_path, "originals")
scan_for_files(directory, uuids_in_library)
# edited
verbose_("Scanning edited files")
directory = joinpath(photosdb.library_path, "resources", "renders")
scan_for_files(directory, uuids_in_library)
# derivatives
verbose_("Scanning derivative files")
directory = joinpath(photosdb.library_path, "resources", "derivatives")
scan_for_files(directory, uuids_in_library)
# shared iCloud photos
verbose_("Scanning shared iCloud photos")
directory = joinpath(photosdb.library_path, "resources", "cloudsharing", "data")
scan_for_files(directory, uuids_in_library)
# shared derivatives
directory = joinpath(
"resources", "cloudsharing", "resources", "derivatives", "masters"
)
scan_for_files(directory, uuids_in_library)
# find orphans
possible_orphans = []
for uuid, files in uuids_in_library.items():
if uuid not in uuids_in_db:
possible_orphans.extend(files)
echo(
f"Found [num]{len(possible_orphans)}[/] "
f"{pluralize(len(possible_orphans), 'orphan', 'orphans')}"
)
exported = []
for orphan in possible_orphans:
echo(f"[filepath]{orphan}[/]")
if export:
dest = increment_filename(Path(export) / Path(orphan).name)
verbose_(f"Copying [filepath]{Path(orphan).name}[/] to [filepath]{dest}[/]")
FileUtil.copy(orphan, dest)
exported.append(dest)
if export:
echo(
f"Exported [num]{len(exported)}[/] "
f"{pluralize(len(exported), 'file', 'files')}"
)
def scan_for_files(directory: str, uuid_dict: Dict):
"""Walk a directory path finding any files named with UUID in the filename and add to uuid_dict
Note: modifies uuid_dict
"""
uuid_pattern = r"([0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12})"
uuid_regex = re.compile(uuid_pattern)
for dirpath, dirname, filenames in os.walk(directory):
for filename in filenames:
if match := uuid_regex.match(filename):
stem, ext = splitext(filename)
# .plist and .aae files may hold data on adjustments but these
# are not useful by themselves so skip them
if ext.lower() in [".plist", ".aae"]:
continue
filepath = joinpath(dirpath, filename)
try:
uuid_dict[match[0]].append(filepath)
except KeyError:
uuid_dict[match[0]] = [filepath]

View File

@@ -7,6 +7,7 @@ import re
import bitmath
import click
import pytimeparse2
from strpdatetime import strpdatetime
from osxphotos.export_db_utils import export_db_get_version
from osxphotos.photoinfo import PhotoInfoNone
@@ -21,6 +22,7 @@ __all__ = [
"DateTimeISO8601",
"ExportDBType",
"FunctionCall",
"StrpDateTimePattern",
"TemplateString",
"TimeISO8601",
"TimeOffset",
@@ -217,3 +219,24 @@ class UTCOffset(click.ParamType):
f"Invalid timezone format: {value}. "
"Valid format for timezone offset: '±HH:MM', '±H:MM', or '±HHMM'"
)
class StrpDateTimePattern(click.ParamType):
"""A pattern to be used with strpdatetime()"""
name = "STRPDATETIME_PATTERN"
def convert(self, value, param, ctx):
try:
strpdatetime("", value)
return value
except ValueError as e:
# ValueError could be due to no match or invalid pattern
# only want to fail if invalid pattern
if any(
s in str(e)
for s in ["Invalid format string", "bad directive", "stray %"]
):
self.fail(f"Invalid strpdatetime format string: {value}. {e}")
else:
return value

View File

@@ -85,14 +85,32 @@ def inspect_photo(
+ f"[time]{photo.import_info.creation_date.isoformat()}[/]"
)
file_size = (
bold("File size: ")
+ f"[num]{float(bitmath.Byte(photo.original_filesize).to_MB()):.2f} MB[/]"
)
if photo.live_photo and photo.path_live_photo:
file_size += (
" | [num]"
+ f"{float(bitmath.Byte(pathlib.Path(photo.path_live_photo).stat().st_size).to_MB()):.2f}"
+ " MB (Live video)[/]"
)
if photo.has_raw and photo.path_raw:
file_size += (
" | [num]"
+ f"{float(bitmath.Byte(pathlib.Path(photo.path_raw).stat().st_size).to_MB()):.2f}"
+ " MB (RAW photo)[/]"
)
properties.extend(
[
bold("Dimensions: ")
+ f"[num]{photo.width}[/] x [num]{photo.height}[/] "
+ bold("Orientation: ")
+ f"[num]{photo.orientation}[/]",
bold("File size: ")
+ f"[num]{float(bitmath.Byte(photo.original_filesize).to_MB()):.2f} MB[/]",
file_size,
bold("Title: ") + f"{photo.title or '-'}",
bold("Description: ")
+ f"{trim(photo.description or '-', 'Description: ')}",
@@ -106,7 +124,7 @@ def inspect_photo(
bold("Location: ")
+ f"{', '.join(dd_to_dms_str(*photo.location)) if photo.location[0] else '-'}",
bold("Place: ") + f"{photo.place.name if photo.place else '-'}",
bold("Categories: ") + f"{', '.join(photo.labels) or '-'}",
bold("Categories/Labels: ") + f"{', '.join(photo.labels) or '-'}",
]
)
properties.append(format_flags(photo))
@@ -227,10 +245,18 @@ def format_paths(photo: PhotoInfo) -> str:
"""format photo paths for inspect_photo"""
path_str = bold("Path original: ")
path_str += f"[filepath]{format_path_link(photo.path)}[/]" if photo.path else "-"
if photo.path_live_photo:
path_str += "\n"
path_str += bold("Path live video: ")
path_str += f"[filepath]{format_path_link(photo.path_live_photo)}[/]"
if photo.path_edited:
path_str += "\n"
path_str += bold("Path edited: ")
path_str += f"[filepath]{format_path_link(photo.path_edited)}[/]"
if photo.path_edited_live_photo:
path_str += "\n"
path_str += bold("Path edited live video: ")
path_str += f"[filepath]{format_path_link(photo.path_edited_live_photo)}[/]"
if photo.path_raw:
path_str += "\n"
path_str += bold("Path raw: ")

View File

@@ -1,8 +1,9 @@
"""print_photo_info function to print PhotoInfo objects"""
import csv
import json
import sys
from typing import Callable, List
from typing import Callable, List, Tuple
from osxphotos.photoinfo import PhotoInfo
@@ -110,3 +111,44 @@ def print_photo_info(
)
for row in dump:
csv_writer.writerow(row)
def print_photo_fields(
photos: List[PhotoInfo], fields: Tuple[Tuple[str]], json_format: bool
):
"""Output custom field templates from PhotoInfo objects
Args:
photos: List of PhotoInfo objects
fields: Tuple of Tuple of field names/field templates to output"""
keys = [f[0] for f in fields]
data = []
for p in photos:
record = {}
for field in fields:
rendered_value, unmatched = p.render_template(field[1])
if unmatched:
raise ValueError(
f"Unmatched template variables in field {field[0]}: {field[1]}"
)
field_value = (
rendered_value[0]
if len(rendered_value) == 1
else ",".join(rendered_value)
if not json_format
else rendered_value
)
record[field[0]] = field_value
data.append(record)
if json_format:
print(json.dumps(data, indent=4))
else:
# dump as CSV
csv_writer = csv.writer(
sys.stdout, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
)
# add headers
csv_writer.writerow(keys)
for record in data:
csv_writer.writerow(record.values())

View File

@@ -3,16 +3,24 @@
import click
import osxphotos
from osxphotos.cli.click_rich_echo import (
rich_click_echo,
set_rich_console,
set_rich_theme,
)
from osxphotos.debug import set_debug
from osxphotos.photosalbum import PhotosAlbum
from osxphotos.phototemplate import RenderOptions
from osxphotos.queryoptions import QueryOptions
from .color_themes import get_default_theme
from .common import (
CLI_COLOR_ERROR,
CLI_COLOR_WARNING,
DB_ARGUMENT,
DB_OPTION,
DELETED_OPTIONS,
FIELD_OPTION,
JSON_OPTION,
OSXPHOTOS_HIDDEN,
QUERY_OPTIONS,
@@ -20,7 +28,8 @@ from .common import (
load_uuid_from_file,
)
from .list import _list_libraries
from .print_photo_info import print_photo_info
from .print_photo_info import print_photo_fields, print_photo_info
from .verbose import get_verbose_console
@click.command()
@@ -62,6 +71,24 @@ from .print_photo_info import print_photo_info
"This only works if the Photos library being queried is the last-opened (default) library in Photos. "
"This feature is currently experimental. I don't know how well it will work on large query sets.",
)
@click.option(
"--quiet",
is_flag=True,
help="Quiet output; doesn't actually print query results. "
"Useful with --print and --add-to-album if you don't want to see the actual query results.",
)
@FIELD_OPTION
@click.option(
"--print",
"print_template",
metavar="TEMPLATE",
multiple=True,
help="Render TEMPLATE string for each photo queried and print to stdout. "
"TEMPLATE is an osxphotos template string. "
"This may be useful for creating custom reports, etc. "
"Most useful with --quiet. "
"May be repeated to print multiple template strings. ",
)
@click.option(
"--debug", required=False, is_flag=True, default=False, hidden=OSXPHOTOS_HIDDEN
)
@@ -88,6 +115,7 @@ def query(
exif,
external_edit,
favorite,
field,
folder,
from_date,
from_time,
@@ -127,6 +155,7 @@ def query(
not_missing,
not_panorama,
not_portrait,
not_reference,
not_screenshot,
not_selfie,
not_shared,
@@ -138,8 +167,10 @@ def query(
person,
place,
portrait,
print_template,
query_eval,
query_function,
quiet,
regex,
screenshot,
selected,
@@ -176,7 +207,6 @@ def query(
from_date,
from_time,
has_raw,
is_reference,
keyword,
label,
max_size,
@@ -220,6 +250,7 @@ def query(
(shared, not_shared),
(slow_mo, not_slow_mo),
(time_lapse, not_time_lapse),
(is_reference, not_reference),
]
# print help if no non-exclusive term or a double exclusive term is given
if any(all(bb) for bb in exclusive) or not any(
@@ -229,6 +260,10 @@ def query(
click.echo(ctx.obj.group.commands["query"].get_help(ctx), err=True)
return
# set console for rich_echo to be same as for verbose_
set_rich_console(get_verbose_console())
set_rich_theme(get_default_theme())
# actually have something to query
# default searches for everything
photos = True
@@ -309,6 +344,7 @@ def query(
not_missing=not_missing,
not_panorama=not_panorama,
not_portrait=not_portrait,
not_reference=not_reference,
not_screenshot=not_screenshot,
not_selfie=not_selfie,
not_shared=not_shared,
@@ -366,4 +402,25 @@ def query(
err=True,
)
print_photo_info(photos, cli_json or json_, print_func=click.echo)
if field:
print_photo_fields(photos, field, cli_json or json_)
if print_template:
options = RenderOptions()
for p in photos:
for template in print_template:
rendered_templates, unmatched = p.render_template(
template,
options,
)
if unmatched:
rich_click_echo(
f"[warning]Unmatched template field: {unmatched}[/]"
)
for rendered_template in rendered_templates:
if not rendered_template:
continue
print(rendered_template)
if not quiet and not field:
print_photo_info(photos, cli_json or json_, print_func=click.echo)

View File

@@ -253,7 +253,6 @@ def _query_options_from_kwargs(**kwargs) -> QueryOptions:
"from_date",
"from_time",
"has_raw",
"is_reference",
"keyword",
"label",
"max_size",
@@ -294,6 +293,7 @@ def _query_options_from_kwargs(**kwargs) -> QueryOptions:
("shared", "not_shared"),
("slow_mo", "not_slow_mo"),
("time_lapse", "not_time_lapse"),
("is_reference", "not_reference"),
]
# print help if no non-exclusive term or a double exclusive term is given
# TODO: add option to validate requiring at least one query arg

View File

@@ -2,16 +2,18 @@
import csv
import datetime
import json
import os
import os.path
import sqlite3
from abc import ABC, abstractmethod
from contextlib import suppress
from typing import Union, Dict
from typing import Dict, Union
from osxphotos.photoexporter import ExportResults
from osxphotos.export_db import OSXPHOTOS_ABOUT_STRING
from osxphotos.photoexporter import ExportResults
from osxphotos.sqlite_utils import sqlite_columns
__all__ = [
"report_writer_factory",
@@ -166,18 +168,20 @@ class ReportWriterSQLite(ReportWriterABC):
self._conn = sqlite3.connect(self.output_file)
self._create_tables()
self.report_id = self._generate_report_id()
def write(self, export_results: ExportResults):
"""Write results to the output file"""
all_results = prepare_results_for_writing(export_results)
for data in list(all_results.values()):
data["report_id"] = self.report_id
cursor = self._conn.cursor()
cursor.execute(
"INSERT INTO report "
"(datetime, filename, exported, new, updated, skipped, exif_updated, touched, converted_to_jpeg, sidecar_xmp, sidecar_json, sidecar_exiftool, missing, error, exiftool_warning, exiftool_error, extended_attributes_written, extended_attributes_skipped, cleanup_deleted_file, cleanup_deleted_directory, exported_album) "
"(datetime, filename, exported, new, updated, skipped, exif_updated, touched, converted_to_jpeg, sidecar_xmp, sidecar_json, sidecar_exiftool, missing, error, exiftool_warning, exiftool_error, extended_attributes_written, extended_attributes_skipped, cleanup_deleted_file, cleanup_deleted_directory, exported_album, report_id) "
"VALUES "
"(:datetime, :filename, :exported, :new, :updated, :skipped, :exif_updated, :touched, :converted_to_jpeg, :sidecar_xmp, :sidecar_json, :sidecar_exiftool, :missing, :error, :exiftool_warning, :exiftool_error, :extended_attributes_written, :extended_attributes_skipped, :cleanup_deleted_file, :cleanup_deleted_directory, :exported_album);",
"(:datetime, :filename, :exported, :new, :updated, :skipped, :exif_updated, :touched, :converted_to_jpeg, :sidecar_xmp, :sidecar_json, :sidecar_exiftool, :missing, :error, :exiftool_warning, :exiftool_error, :extended_attributes_written, :extended_attributes_skipped, :cleanup_deleted_file, :cleanup_deleted_directory, :exported_album, :report_id);",
data,
)
self._conn.commit()
@@ -191,27 +195,27 @@ class ReportWriterSQLite(ReportWriterABC):
c.execute(
"""
CREATE TABLE IF NOT EXISTS report (
datetime text,
filename text,
exported integer,
new integer,
updated integer,
skipped integer,
exif_updated integer,
touched integer,
converted_to_jpeg integer,
sidecar_xmp integer,
sidecar_json integer,
sidecar_exiftool integer,
missing integer,
error text,
exiftool_warning text,
exiftool_error text,
extended_attributes_written integer,
extended_attributes_skipped integer,
cleanup_deleted_file integer,
cleanup_deleted_directory integer,
exported_album text
datetime TEXT,
filename TEXT,
exported INTEGER,
new INTEGER,
updated INTEGER,
skipped INTEGER,
exif_updated INTEGER,
touched INTEGER,
converted_to_jpeg INTEGER,
sidecar_xmp INTEGER,
sidecar_json INTEGER,
sidecar_exiftool INTEGER,
missing INTEGER,
error TEXT,
exiftool_warning TEXT,
exiftool_error TEXT,
extended_attributes_written INTEGER,
extended_attributes_skipped INTEGER,
cleanup_deleted_file INTEGER,
cleanup_deleted_directory INTEGER,
exported_album TEXT
)
"""
)
@@ -226,9 +230,55 @@ class ReportWriterSQLite(ReportWriterABC):
"INSERT INTO about(about) VALUES (?);",
(f"OSXPhotos Export Report. {OSXPHOTOS_ABOUT_STRING}",),
)
c.execute(
"""
CREATE TABLE IF NOT EXISTS report_id (
report_id INTEGER PRIMARY KEY,
datetime TEXT
);"""
)
self._conn.commit()
# migrate report table to add report_id if needed (#731)
if "report_id" not in sqlite_columns(self._conn, "report"):
self._conn.cursor().execute("ALTER TABLE report ADD COLUMN report_id TEXT;")
self._conn.commit()
# create report_summary view
c.execute(
"""
CREATE VIEW IF NOT EXISTS report_summary AS
SELECT
report_id,
datetime(MIN(datetime)) start_time,
datetime(MAX(datetime)) end_time,
STRFTIME('%s',MAX(datetime)) - STRFTIME('%s',MIN(datetime)) AS duration_s,
SUM(exported) AS exported,
sum(new) as new,
SUM(updated) as updated,
SUM(skipped) as skipped,
SUM(sidecar_xmp) as sidecar_xmp,
SUM(touched) as touched,
SUM(converted_to_jpeg) as converted_to_jpeg,
SUM(missing) as missing,
SUM(CASE WHEN error = "" THEN 0 ELSE 1 END) as error,
SUM(cleanup_deleted_file) as cleanup_deleted_file
FROM report
GROUP BY report_id;"""
)
self._conn.commit()
def _generate_report_id(self) -> int:
"""Get a new report ID for this report"""
c = self._conn.cursor()
c.execute(
"INSERT INTO report_id(datetime) VALUES (?);",
(datetime.datetime.now().isoformat(),),
)
report_id = c.lastrowid
self._conn.commit()
return report_id
def __del__(self):
with suppress(Exception):
self.close()

Binary file not shown.

View File

@@ -6,7 +6,9 @@
If these aren't important to you, I highly recommend you use Sven Marnach's excellent
pyexiftool: https://github.com/smarnach/pyexiftool which provides more functionality """
import atexit
import contextlib
import html
import json
import logging
@@ -104,9 +106,12 @@ class _ExifToolProc:
return cls.instance
def __init__(self, exiftool=None):
def __init__(self, exiftool=None, large_file_support=True):
"""construct _ExifToolProc singleton object or return instance of already created object
exiftool: optional path to exiftool binary (if not provided, will search path to find it)
Args:
exiftool: optional path to exiftool binary (if not provided, will search path to find it)
large_file_support: if True, enables large file support (>4GB) via `-api largefilesupport=1`
"""
if hasattr(self, "_process_running") and self._process_running:
@@ -119,16 +124,14 @@ class _ExifToolProc:
return
self._process_running = False
self._exiftool = exiftool or get_exiftool_path()
self._start_proc()
self._start_proc(large_file_support=large_file_support)
@property
def process(self):
"""return the exiftool subprocess"""
if self._process_running:
return self._process
else:
if not self._process_running:
self._start_proc()
return self._process
return self._process
@property
def pid(self):
@@ -140,7 +143,7 @@ class _ExifToolProc:
"""return path to exiftool process"""
return self._exiftool
def _start_proc(self):
def _start_proc(self, large_file_support):
"""start exiftool in batch mode"""
if self._process_running:
@@ -151,11 +154,13 @@ class _ExifToolProc:
# make sure /usr/bin at start of path so exiftool can find xattr (see #636)
env = os.environ.copy()
env["PATH"] = f'/usr/bin/:{env["PATH"]}'
large_file_args = ["-api", "largefilesupport=1"] if large_file_support else []
self._process = subprocess.Popen(
[
self._exiftool,
"-stay_open", # keep process open in batch mode
"True", # -stay_open=True, keep process open in batch mode
*large_file_args,
"-@", # read command-line arguments from file
"-", # read from stdin
"-common_args", # specifies args common to all commands subsequently run
@@ -179,13 +184,10 @@ class _ExifToolProc:
if not self._process_running:
return
try:
with contextlib.suppress(Exception):
self._process.stdin.write(b"-stay_open\n")
self._process.stdin.write(b"False\n")
self._process.stdin.flush()
except Exception as e:
pass
try:
self._process.communicate(timeout=5)
except subprocess.TimeoutExpired:
@@ -199,7 +201,14 @@ class _ExifToolProc:
class ExifTool:
"""Basic exiftool interface for reading and writing EXIF tags"""
def __init__(self, filepath, exiftool=None, overwrite=True, flags=None):
def __init__(
self,
filepath,
exiftool=None,
overwrite=True,
flags=None,
large_file_support=True,
):
"""Create ExifTool object
Args:
@@ -207,6 +216,7 @@ class ExifTool:
exiftool: path to exiftool, if not specified will look in path
overwrite: if True, will overwrite image file without creating backup, default=False
flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F)
large_file_support: if True, enables large file support in exiftool (`-api largefilesupport=1`)
Returns:
ExifTool instance
@@ -219,7 +229,9 @@ class ExifTool:
self.error = None
# if running as a context manager, self._context_mgr will be True
self._context_mgr = False
self._exiftoolproc = _ExifToolProc(exiftool=exiftool)
self._exiftoolproc = _ExifToolProc(
exiftool=exiftool, large_file_support=large_file_support
)
self._read_exif()
@property
@@ -327,7 +339,7 @@ class ExifTool:
commands = list(commands)
commands.append("-overwrite_original")
filename = os.fsencode(self.file) if not no_file else b""
filename = b"" if no_file else os.fsencode(self.file)
if self.flags:
# need to split flags, e.g. so "--ext AVI" becomes ["--ext", "AVI"]
@@ -423,8 +435,7 @@ class ExifTool:
def _read_exif(self):
"""read exif data from file"""
data = self.asdict()
self.data = {k: v for k, v in data.items()}
self.data = self.asdict().copy()
def __str__(self):
return f"file: {self.file}\nexiftool: {self._exiftoolproc._exiftool}"

View File

@@ -740,10 +740,16 @@ class ExportDB:
def _migrate_7_0_to_7_1(self, conn):
c = conn.cursor()
c.execute("""ALTER TABLE export_data ADD COLUMN timestamp DATETIME;""")
# timestamp column should not exist but this prevents error if migration is run on an already migrated database
# reference #794
results = c.execute(
"SELECT COUNT(*) FROM pragma_table_info('export_data') WHERE name='timestamp';"
).fetchone()
if results[0] == 0:
c.execute("""ALTER TABLE export_data ADD COLUMN timestamp DATETIME;""")
c.execute(
"""
CREATE TRIGGER insert_timestamp_trigger
CREATE TRIGGER IF NOT EXISTS insert_timestamp_trigger
AFTER INSERT ON export_data
BEGIN
UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
@@ -752,7 +758,7 @@ class ExportDB:
)
c.execute(
"""
CREATE TRIGGER update_timestamp_trigger
CREATE TRIGGER IF NOT EXISTS update_timestamp_trigger
AFTER UPDATE On export_data
BEGIN
UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;

View File

@@ -2,6 +2,7 @@
import os
import pathlib
import shutil
import stat
import tempfile
import typing as t
@@ -12,7 +13,7 @@ import Foundation
from .imageconverter import ImageConverter
__all__ = ["FileUtilABC", "FileUtilMacOS", "FileUtil", "FileUtilNoOp"]
__all__ = ["FileUtilABC", "FileUtilMacOS", "FileUtilShUtil", "FileUtil", "FileUtilNoOp"]
class FileUtilABC(ABC):
@@ -25,7 +26,7 @@ class FileUtilABC(ABC):
@classmethod
@abstractmethod
def copy(cls, src, dest, norsrc=False):
def copy(cls, src, dest):
pass
@classmethod
@@ -250,6 +251,43 @@ class FileUtilMacOS(FileUtilABC):
return (stat.S_IFMT(st.st_mode), st.st_size, int(st.st_mtime))
class FileUtilShUtil(FileUtilMacOS):
"""Various file utilities, uses shutil.copy to copy files instead of NSFileManager (#807)"""
@classmethod
def copy(cls, src, dest):
"""Copies a file from src path to dest path using shutil.copy
Args:
src: source path as string; must be a valid file path
dest: destination path as string
dest may be either directory or file; in either case, src file must not exist in dest
Note: src and dest may be either a string or a pathlib.Path object
Returns:
True if copy succeeded
Raises:
OSError if copy fails
TypeError if either path is None
"""
if not isinstance(src, pathlib.Path):
src = pathlib.Path(src)
if not isinstance(dest, pathlib.Path):
dest = pathlib.Path(dest)
if dest.is_dir():
dest /= src.name
try:
shutil.copy(str(src), str(dest))
except Exception as e:
raise OSError(f"Error copying {src} to {dest}: {e}") from e
return True
class FileUtil(FileUtilMacOS):
"""Various file utilities"""
@@ -280,7 +318,7 @@ class FileUtilNoOp(FileUtil):
pass
@classmethod
def copy(cls, src, dest, norsrc=False):
def copy(cls, src, dest):
pass
@classmethod

View File

@@ -136,6 +136,7 @@ class ExportOptions:
use_photokit (bool, default=False): if True, will use photokit to export photos when use_photos_export is True
verbose (callable): optional callable function to use for printing verbose text during processing; if None (default), does not print output.
tmpdir: (str, default=None): Optional directory to use for temporary files, if None (default) uses system tmp directory
favorite_rating (bool): if True, set XMP:Rating=5 for favorite images and XMP:Rating=0 for non-favorites
"""
@@ -181,6 +182,7 @@ class ExportOptions:
use_photos_export: bool = False
verbose: t.Optional[t.Callable] = None
tmpdir: t.Optional[str] = None
favorite_rating: bool = False
def asdict(self):
return asdict(self)
@@ -243,6 +245,36 @@ class StagedFiles:
class ExportResults:
"""Results class which holds export results for export"""
__slots__ = [
"_datetime",
"converted_to_jpeg",
"deleted_directories",
"deleted_files",
"error",
"exif_updated",
"exiftool_error",
"exiftool_warning",
"exported",
"exported_album",
"metadata_changed",
"missing",
"missing_album",
"new",
"sidecar_exiftool_skipped",
"sidecar_exiftool_written",
"sidecar_json_skipped",
"sidecar_json_written",
"sidecar_xmp_skipped",
"sidecar_xmp_written",
"skipped",
"skipped_album",
"to_touch",
"touched",
"updated",
"xattr_skipped",
"xattr_written",
]
def __init__(
self,
converted_to_jpeg=None,
@@ -252,11 +284,11 @@ class ExportResults:
exif_updated=None,
exiftool_error=None,
exiftool_warning=None,
exported_album=None,
exported=None,
exported_album=None,
metadata_changed=None,
missing_album=None,
missing=None,
missing_album=None,
new=None,
sidecar_exiftool_skipped=None,
sidecar_exiftool_written=None,
@@ -264,8 +296,8 @@ class ExportResults:
sidecar_json_written=None,
sidecar_xmp_skipped=None,
sidecar_xmp_written=None,
skipped_album=None,
skipped=None,
skipped_album=None,
to_touch=None,
touched=None,
updated=None,
@@ -273,36 +305,22 @@ class ExportResults:
xattr_written=None,
):
self.datetime = datetime.now().isoformat()
local_vars = locals()
self._datetime = datetime.now().isoformat()
for attr in self.attributes:
setattr(self, attr, local_vars.get(attr) or [])
self.converted_to_jpeg = converted_to_jpeg or []
self.deleted_directories = deleted_directories or []
self.deleted_files = deleted_files or []
self.error = error or []
self.exif_updated = exif_updated or []
self.exiftool_error = exiftool_error or []
self.exiftool_warning = exiftool_warning or []
self.exported = exported or []
self.exported_album = exported_album or []
self.metadata_changed = metadata_changed or []
self.missing = missing or []
self.missing_album = missing_album or []
self.new = new or []
self.sidecar_exiftool_skipped = sidecar_exiftool_skipped or []
self.sidecar_exiftool_written = sidecar_exiftool_written or []
self.sidecar_json_skipped = sidecar_json_skipped or []
self.sidecar_json_written = sidecar_json_written or []
self.sidecar_xmp_skipped = sidecar_xmp_skipped or []
self.sidecar_xmp_written = sidecar_xmp_written or []
self.skipped = skipped or []
self.skipped_album = skipped_album or []
self.to_touch = to_touch or []
self.touched = touched or []
self.updated = updated or []
self.xattr_skipped = xattr_skipped or []
self.xattr_written = xattr_written or []
@property
def attributes(self) -> t.List[str]:
"""Return list of attributes tracked by ExportResults"""
return [attr for attr in self.__slots__ if not attr.startswith("_")]
def all_files(self):
@property
def datetime(self) -> str:
"""Return datetime when ExportResults was created"""
return self._datetime
def all_files(self) -> t.List[str]:
"""return all filenames contained in results"""
files = (
self.exported
@@ -324,65 +342,23 @@ class ExportResults:
files += [x[0] for x in self.exiftool_error]
files += [x[0] for x in self.error]
files = list(set(files))
return files
return list(set(files))
def __iadd__(self, other):
self.exported += other.exported
self.new += other.new
self.updated += other.updated
self.skipped += other.skipped
self.exif_updated += other.exif_updated
self.touched += other.touched
self.to_touch += other.to_touch
self.converted_to_jpeg += other.converted_to_jpeg
self.sidecar_json_written += other.sidecar_json_written
self.sidecar_json_skipped += other.sidecar_json_skipped
self.sidecar_exiftool_written += other.sidecar_exiftool_written
self.sidecar_exiftool_skipped += other.sidecar_exiftool_skipped
self.sidecar_xmp_written += other.sidecar_xmp_written
self.sidecar_xmp_skipped += other.sidecar_xmp_skipped
self.missing += other.missing
self.error += other.error
self.exiftool_warning += other.exiftool_warning
self.exiftool_error += other.exiftool_error
self.deleted_files += other.deleted_files
self.deleted_directories += other.deleted_directories
self.exported_album += other.exported_album
self.skipped_album += other.skipped_album
self.missing_album += other.missing_album
self.metadata_changed += other.metadata_changed
def __iadd__(self, other) -> "ExportResults":
if type(other) != ExportResults:
raise TypeError("Can only add ExportResults to ExportResults")
for attribute in self.attributes:
setattr(
self, attribute, getattr(self, attribute) + getattr(other, attribute)
)
return self
def __str__(self):
def __str__(self) -> str:
return (
"ExportResults("
+ f"datetime={self.datetime}"
+ f",exported={self.exported}"
+ f",new={self.new}"
+ f",updated={self.updated}"
+ f",skipped={self.skipped}"
+ f",exif_updated={self.exif_updated}"
+ f",touched={self.touched}"
+ f",to_touch={self.to_touch}"
+ f",converted_to_jpeg={self.converted_to_jpeg}"
+ f",sidecar_json_written={self.sidecar_json_written}"
+ f",sidecar_json_skipped={self.sidecar_json_skipped}"
+ f",sidecar_exiftool_written={self.sidecar_exiftool_written}"
+ f",sidecar_exiftool_skipped={self.sidecar_exiftool_skipped}"
+ f",sidecar_xmp_written={self.sidecar_xmp_written}"
+ f",sidecar_xmp_skipped={self.sidecar_xmp_skipped}"
+ f",missing={self.missing}"
+ f",error={self.error}"
+ f",exiftool_warning={self.exiftool_warning}"
+ f",exiftool_error={self.exiftool_error}"
+ f",deleted_files={self.deleted_files}"
+ f",deleted_directories={self.deleted_directories}"
+ f",exported_album={self.exported_album}"
+ f",skipped_album={self.skipped_album}"
+ f",missing_album={self.missing_album}"
+ f",metadata_changed={self.metadata_changed}"
+ f"datetime={self._datetime}, "
+ ", ".join([f"{attr}={getattr(self, attr)}" for attr in self.attributes])
+ ")"
)
@@ -949,7 +925,7 @@ class PhotoExporter:
# export live_photo .mov file?
live_photo = bool(options.live_photo and self.photo.live_photo)
overwrite = any([options.overwrite, options.update, options.force_update])
edited_version = options.edited or self.photo.shared
edited_version = bool(options.edited or self.photo.shared)
# shared photos (in shared albums) show up as not having adjustments (not edited)
# but Photos is unable to export the "original" as only a jpeg copy is shared in iCloud
# so tell Photos to export the current version in this case
@@ -1586,6 +1562,7 @@ class PhotoExporter:
QuickTime:ModifyDate (UTC)
QuickTime:GPSCoordinates
UserData:GPSCoordinates
XMP:Rating
Reference:
https://iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201610_1.pdf
@@ -1701,8 +1678,8 @@ class PhotoExporter:
if options.face_regions and self.photo.face_info:
exif.update(self._get_mwg_face_regions_exiftool())
# if self.favorite():
# exif["Rating"] = 5
if options.favorite_rating:
exif["XMP:Rating"] = 5 if self.photo.favorite else 0
if options.location:
(lat, lon) = self.photo.location
@@ -2009,6 +1986,11 @@ class PhotoExporter:
latlon = self.photo.location if options.location else (None, None)
if options.favorite_rating:
rating = 5 if self.photo.favorite else 0
else:
rating = None
xmp_str = xmp_template.render(
photo=self.photo,
description=description,
@@ -2018,6 +2000,7 @@ class PhotoExporter:
extension=extension,
location=latlon,
version=__version__,
rating=rating,
)
# remove extra lines that mako inserts from template

View File

@@ -4,6 +4,8 @@ Represents a single photo in the Photos library and provides access to the photo
PhotosDB.photos() returns a list of PhotoInfo objects
"""
from __future__ import annotations
import contextlib
import dataclasses
import datetime
@@ -277,77 +279,119 @@ class PhotoInfo:
return photopath
def _path_edited_4(self):
"""return path_edited for Photos <= 4"""
if self._db._db_version > _PHOTOS_4_VERSION:
raise RuntimeError("Wrong database format!")
photopath = None
if self._info["hasAdjustments"]:
edit_id = self._info["edit_resource_id"]
if edit_id is not None:
library = self._db._library_path
folder_id, file_id = _get_resource_loc(edit_id)
# todo: is this always true or do we need to search file file_id under folder_id
# figure out what kind it is and build filename
filename = None
if self._info["type"] == _PHOTO_TYPE:
# it's a photo
filename = f"fullsizeoutput_{file_id}.jpeg"
elif self._info["type"] == _MOVIE_TYPE:
# it's a movie
filename = f"fullsizeoutput_{file_id}.mov"
else:
# don't know what it is!
logging.debug(f"WARNING: unknown type {self._info['type']}")
return None
# photopath appears to usually be in "00" subfolder but
# could be elsewhere--I haven't figured out this logic yet
# first see if it's in 00
photopath = os.path.join(
library, "resources", "media", "version", folder_id, "00", filename
def _get_predicted_path_edited_4(self) -> str | None:
"""return predicted path_edited for Photos <= 4"""
edit_id = self._info["edit_resource_id_photo"]
folder_id, file_id, nn_id = _get_resource_loc(edit_id)
# figure out what kind it is and build filename
library = self._db._library_path
if uti_edited := self.uti_edited:
ext = get_preferred_uti_extension(uti_edited)
if ext is not None:
filename = f"fullsizeoutput_{file_id}.{ext}"
return os.path.join(
library, "resources", "media", "version", folder_id, nn_id, filename
)
if not os.path.isfile(photopath):
rootdir = os.path.join(
library, "resources", "media", "version", folder_id
)
# if we get here, we couldn't figure out the extension
# so try to figure out the type and build the filename
type_ = self._info["type"]
if type_ == _PHOTO_TYPE:
# it's a photo
filename = f"fullsizeoutput_{file_id}.jpeg"
elif type_ == _MOVIE_TYPE:
# it's a movie
filename = f"fullsizeoutput_{file_id}.mov"
else:
raise ValueError(f"Unknown type {type_}")
for dirname, _, filelist in os.walk(rootdir):
if filename in filelist:
photopath = os.path.join(dirname, filename)
break
return os.path.join(
library, "resources", "media", "version", folder_id, nn_id, filename
)
# check again to see if we found a valid file
if not os.path.isfile(photopath):
logging.debug(
f"MISSING PATH: edited file for UUID {self._uuid} should be at {photopath} but does not appear to exist"
)
photopath = None
else:
def _path_edited_4(self) -> str | None:
"""return path_edited for Photos <= 4; modified version of code in PhotoInfo to debug #859"""
if not self._info["hasAdjustments"]:
return None
if edit_id := self._info["edit_resource_id"]:
try:
photopath = self._get_predicted_path_edited_4()
except ValueError as e:
logging.debug(f"ERROR: {e}")
photopath = None
if photopath is not None and not os.path.isfile(photopath):
# the heuristic failed, so try to find the file
rootdir = pathlib.Path(photopath).parent.parent
filename = pathlib.Path(photopath).name
for dirname, _, filelist in os.walk(rootdir):
if filename in filelist:
photopath = os.path.join(dirname, filename)
break
# check again to see if we found a valid file
if photopath is not None and not os.path.isfile(photopath):
logging.debug(
f"{self.uuid} hasAdjustments but edit_resource_id is None"
f"MISSING PATH: edited file for UUID {self._uuid} should be at {photopath} but does not appear to exist"
)
photopath = None
else:
logging.debug(f"{self.uuid} hasAdjustments but edit_resource_id is None")
photopath = None
return photopath
@property
def path_edited_live_photo(self):
"""return path to edited version of live photo movie; only valid for Photos 5+"""
if self._db._db_version < _PHOTOS_5_VERSION:
return None
"""return path to edited version of live photo movie"""
try:
return self._path_edited_live_photo
except AttributeError:
self._path_edited_live_photo = self._path_edited_5_live_photo()
if self._db._db_version < _PHOTOS_5_VERSION:
self._path_edited_live_photo = self._path_edited_4_live_photo()
else:
self._path_edited_live_photo = self._path_edited_5_live_photo()
return self._path_edited_live_photo
def _get_predicted_path_edited_live_photo_4(self) -> str | None:
"""return predicted path_edited for Photos <= 4"""
# need the resource id for the video, not the photo (edit_resource_id is for photo)
if edit_id := self._info["edit_resource_id_video"]:
folder_id, file_id, nn_id = _get_resource_loc(edit_id)
# figure out what kind it is and build filename
library = self._db._library_path
filename = f"videocomplementoutput_{file_id}.mov"
return os.path.join(
library, "resources", "media", "version", folder_id, nn_id, filename
)
else:
return None
def _path_edited_4_live_photo(self):
"""return path_edited_live_photo for Photos <= 4"""
if self._db._db_version > _PHOTOS_4_VERSION:
raise RuntimeError("Wrong database format!")
photopath = self._get_predicted_path_edited_live_photo_4()
if photopath is not None and not os.path.isfile(photopath):
# the heuristic failed, so try to find the file
rootdir = pathlib.Path(photopath).parent.parent
filename = pathlib.Path(photopath).name
photopath = next(
(
os.path.join(dirname, filename)
for dirname, _, filelist in os.walk(rootdir)
if filename in filelist
),
None,
)
if photopath is None:
logging.debug(
f"MISSING PATH: edited live photo file for UUID {self._uuid} does not appear to exist"
)
return photopath
def _path_edited_5_live_photo(self):
"""return path_edited_live_photo for Photos >= 5"""
if self._db._db_version < _PHOTOS_5_VERSION:
@@ -863,7 +907,7 @@ class PhotoInfo:
logging.debug(f"missing live_model_id: {self._uuid}")
photopath = None
else:
folder_id, file_id = _get_resource_loc(live_model_id)
folder_id, file_id, nn_id = _get_resource_loc(live_model_id)
library_path = self._db.library_path
photopath = os.path.join(
library_path,
@@ -871,7 +915,7 @@ class PhotoInfo:
"media",
"master",
folder_id,
"00",
nn_id,
f"jpegvideocomplement_{file_id}.mov",
)
if not os.path.isfile(photopath):
@@ -934,17 +978,13 @@ class PhotoInfo:
modelid = self._info["modelID"]
if modelid is None:
return []
folder_id, file_id = _get_resource_loc(modelid)
folder_id, file_id, nn_id = _get_resource_loc(modelid)
derivatives_root = (
pathlib.Path(self._db._library_path)
/ f"resources/proxies/derivatives/{folder_id}"
)
# photos appears to usually be in "00" subfolder but
# could be elsewhere--I haven't figured out this logic yet
# first see if it's in 00
derivatives_path = derivatives_root / "00" / file_id
derivatives_path = derivatives_root / nn_id / file_id
if derivatives_path.is_dir():
files = derivatives_path.glob("*")
files = sorted(files, reverse=True, key=lambda f: f.stat().st_size)
@@ -1440,11 +1480,29 @@ class PhotoInfo:
return []
md = OSXMetaData(path)
detected_text = md.get_attribute("osxphotos_detected_text")
try:
def decoder(val):
"""Decode value from JSON"""
return json.loads(val.decode("utf-8"))
detected_text = md.get_xattr(
"osxphotos.metadata:detected_text", decode=decoder
)
except KeyError:
detected_text = None
if detected_text is None:
orientation = self.orientation or None
detected_text = detect_text(path, orientation)
md.set_attribute("osxphotos_detected_text", detected_text)
def encoder(obj):
"""Encode value as JSON"""
val = json.dumps(obj)
return val.encode("utf-8")
md.set_xattr(
"osxphotos.metadata:detected_text", detected_text, encode=encoder
)
return detected_text
@property

View File

@@ -4,7 +4,7 @@ from typing import List, Optional
import photoscript
from more_itertools import chunked
from photoscript import Photo, PhotosLibrary
from photoscript import Album, Folder, Photo, PhotosLibrary
from .photoinfo import PhotoInfo
from .utils import noop, pluralize
@@ -12,25 +12,84 @@ from .utils import noop, pluralize
__all__ = ["PhotosAlbum", "PhotosAlbumPhotoScript"]
def folder_by_path(folders: List[str], verbose: Optional[callable] = None) -> Folder:
"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""
library = PhotosLibrary()
verbose = verbose or noop
top_folder_name = folders.pop(0)
top_folder = library.folder(top_folder_name, top_level=True)
if not top_folder:
verbose(f"Creating folder '{top_folder_name}'")
top_folder = library.create_folder(top_folder_name)
current_folder = top_folder
for folder_name in folders:
folder = current_folder.folder(folder_name)
if not folder:
verbose(f"Creating folder '{folder_name}'")
folder = current_folder.create_folder(folder_name)
current_folder = folder
return current_folder
def album_by_path(
folders_album: List[str], verbose: Optional[callable] = None
) -> Album:
"""Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)"""
library = PhotosLibrary()
verbose = verbose or noop
if len(folders_album) > 1:
# have folders
album_name = folders_album.pop()
folder = folder_by_path(folders_album, verbose)
album = folder.album(album_name)
if album is None:
verbose(f"Creating album '{album_name}'")
album = folder.create_album(album_name)
else:
# only have album name
album_name = folders_album[0]
album = library.album(album_name, top_level=True)
if album is None:
verbose(f"Creating album '{album_name}'")
album = library.create_album(album_name)
return album
class PhotosAlbum:
"""Add osxphotos.photoinfo.PhotoInfo objects to album"""
def __init__(self, name: str, verbose: Optional[callable] = None):
self.name = name
def __init__(
self,
name: str,
verbose: Optional[callable] = None,
split_folder: Optional[str] = None,
rich: bool = False,
):
"""Return a PhotosAlbum object, creating the album if necessary
Args:
name: Name of album
verbose: optional callable to print verbose output
split_folder: if set, split album name on value of split_folder to create folders if necessary,
e.g. if name = 'folder1/folder2/album' and split_folder='/',
then folders 'folder1' and 'folder2' will be created and album 'album' will be created in 'folder2';
if not set, album 'folder1/folder2/album' will be created
rich: if True, use rich themes for verbose output
"""
self.verbose = verbose or noop
self.library = photoscript.PhotosLibrary()
album = self.library.album(name)
if album is None:
self.verbose(f"Creating Photos album '{self.name}'")
album = self.library.create_album(name)
self.album = album
folders_album = name.split(split_folder) if split_folder else [name]
self.album = album_by_path(folders_album, verbose=verbose)
self.name = name
self.rich = rich
def add(self, photo: PhotoInfo):
photo_ = photoscript.Photo(photo.uuid)
self.album.add([photo_])
self.verbose(
f"Added {photo.original_filename} ({photo.uuid}) to album {self.name}"
f"Added {self._format_name(photo.original_filename)} ({self._format_uuid(photo.uuid)}) to album {self._format_album(self.name)}"
)
def add_list(self, photo_list: List[PhotoInfo]):
@@ -39,43 +98,49 @@ class PhotosAlbum:
try:
photos.append(photoscript.Photo(p.uuid))
except Exception as e:
self.verbose(f"Error creating Photo object for photo {p.uuid}: {e}")
self.verbose(
f"Error creating Photo object for photo {self._format_uuid(p.uuid)}: {e}"
)
for photolist in chunked(photos, 10):
self.album.add(photolist)
photo_len = len(photo_list)
self.verbose(
f"Added {photo_len} {pluralize(photo_len, 'photo', 'photos')} to album {self.name}"
f"Added {self._format_num(photo_len)} {pluralize(photo_len, 'photo', 'photos')} to album {self._format_album(self.name)}"
)
def photos(self):
return self.album.photos()
def _format_uuid(self, uuid: str) -> str:
""" "Format uuid for verbose output"""
return f"[uuid]{uuid}[/uuid]" if self.rich else uuid
class PhotosAlbumPhotoScript:
def _format_album(self, album: str) -> str:
""" "Format album name for verbose output"""
return f"[filepath]{album}[/filepath]" if self.rich else album
def _format_name(self, name: str) -> str:
""" "Format name for verbose output"""
return f"[filename]{name}[/filename]" if self.rich else name
def _format_num(self, num: int) -> str:
""" "Format number for verbose output"""
return f"[num]{num}[/num]" if self.rich else str(num)
class PhotosAlbumPhotoScript(PhotosAlbum):
"""Add photoscript.Photo objects to album"""
def __init__(self, name: str, verbose: Optional[callable] = None):
self.name = name
self.verbose = verbose or noop
self.library = PhotosLibrary()
album = self.library.album(name)
if album is None:
self.verbose(f"Creating Photos album '{self.name}'")
album = self.library.create_album(name)
self.album = album
def add(self, photo: Photo):
self.album.add([photo])
self.verbose(f"Added {photo.filename} ({photo.uuid}) to album {self.name}")
self.verbose(
f"Added {self._format_name(photo.filename)} ({self._format_uuid(photo.uuid)}) to album {self._format_album(self.name)}"
)
def add_list(self, photo_list: List[Photo]):
for photolist in chunked(photo_list, 10):
self.album.add(photolist)
photo_len = len(photo_list)
self.verbose(
f"Added {photo_len} {pluralize(photo_len, 'photo', 'photos')} to album {self.name}"
f"Added {self._format_num(photo_len)} {pluralize(photo_len, 'photo', 'photos')} to album {self._format_album(self.name)}"
)
def photos(self):
return self.album.photos()

View File

@@ -6,7 +6,8 @@ import datetime
from dataclasses import dataclass
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION, TIME_DELTA
from ..utils import _open_sql_file, normalize_unicode
from ..utils import normalize_unicode
from ..sqlite_utils import sqlite_open_ro
def _process_comments(self):
@@ -65,7 +66,7 @@ def _process_comments_5(photosdb):
asset_table = _DB_TABLE_NAMES[photosdb._photos_ver]["ASSET"]
(conn, cursor) = _open_sql_file(db)
(conn, cursor) = sqlite_open_ro(db)
results = conn.execute(
"""

View File

@@ -4,7 +4,7 @@
import logging
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
from ..utils import _db_is_locked, _open_sql_file
from ..sqlite_utils import sqlite_open_ro, sqlite_db_is_locked
from .photosdb_utils import get_db_version
@@ -38,7 +38,7 @@ def _process_exifinfo_5(photosdb):
asset_table = _DB_TABLE_NAMES[photosdb._photos_ver]["ASSET"]
(conn, cursor) = _open_sql_file(db)
(conn, cursor) = sqlite_open_ro(db)
result = conn.execute(
f"""

View File

@@ -3,7 +3,8 @@
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
from ..utils import _open_sql_file, normalize_unicode
from ..sqlite_utils import sqlite_open_ro
from ..utils import normalize_unicode
from .photosdb_utils import get_db_version
"""
@@ -40,7 +41,7 @@ def _process_faceinfo_4(photosdb):
"""
db = photosdb._tmp_db
(conn, cursor) = _open_sql_file(db)
(conn, cursor) = sqlite_open_ro(db)
result = cursor.execute(
"""
@@ -179,7 +180,7 @@ def _process_faceinfo_5(photosdb):
asset_table = _DB_TABLE_NAMES[photosdb._photos_ver]["ASSET"]
(conn, cursor) = _open_sql_file(db)
(conn, cursor) = sqlite_open_ro(db)
result = cursor.execute(
f"""

Some files were not shown because too many files have changed in this diff Show More