Compare commits

...

48 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
285 changed files with 22534 additions and 4062 deletions

View File

@@ -55,7 +55,8 @@
"profile": "http://3e.org/",
"contributions": [
"code",
"bug"
"bug",
"userTesting"
]
},
{
@@ -394,6 +395,25 @@
"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,

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.53.0
current_version = 0.55.2
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
serialize = {major}.{minor}.{patch}

View File

@@ -10,12 +10,12 @@ jobs:
max-parallel: 4
matrix:
os: [macos-latest]
python-version: ['3.8', '3.9', '3.10']
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

@@ -2012,7 +2012,7 @@ cog.out(get_template_field_table())
|{cr}|A carriage return: '\r'|
|{crlf}|A carriage return + line feed: '\r\n'|
|{tab}|:A tab: '\t'|
|{osxphotos_version}|The osxphotos version, e.g. '0.53.0'|
|{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|

View File

@@ -4,6 +4,82 @@ 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
@@ -1327,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)
@@ -1510,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)
@@ -1562,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)
@@ -1598,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)
@@ -1629,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)
@@ -1687,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)
@@ -1833,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)
@@ -1955,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)
@@ -2065,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)

View File

@@ -7,13 +7,11 @@
[![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-41-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.
**NOTE: osxphotos does not currently run on python 3.11 due to issues with some of the dependencies. Please do not report any bugs related to python 3.11. osxphotos does run on python 3.8, 3.9, or 3.10 so install one of those if you want to use osxphotos.**
<p align="center"><img src="docs/screencast/demo.gif?raw=true" width="713" height="430"/></p>
# Table of Contents
@@ -39,21 +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 |
| ----------------- |------------|:-------------------|
| 13.0 | Ventura | 8.0 ? * |
| 12.0 - 12.4 | 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 ✅ |
\* Basic functionality has been tested on a Photos library created with the developer preview of macOS Ventura (13.0). I do not have access to a Mac running Ventura beta to do further testing.
\*\* Some features may not be fully supported on Monterey. 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.
\* 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
@@ -73,11 +69,6 @@ Once you've installed osxphotos with pipx, to upgrade to the latest version:
pipx upgrade osxphotos
If you get an error during the installation, it's possible `pipx` attempted to install osxphotos with python 3.11 which osxphotos is not yet compatible with. In that case, try the following:
* `brew install python@3.10`
* `pipx install --python python3.10 osxphotos`
### Installation using pip
You can also install directly from [pypi](https://pypi.org/project/osxphotos/):
@@ -1331,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
@@ -2009,7 +2008,7 @@ Substitution Description
{cr} A carriage return: '\r'
{crlf} A carriage return + line feed: '\r\n'
{tab} :A tab: '\t'
{osxphotos_version} The osxphotos version, e.g. '0.53.0'
{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
@@ -2493,7 +2492,7 @@ The following template field substitutions are availabe for use the templating s
|{cr}|A carriage return: '\r'|
|{crlf}|A carriage return + line feed: '\r\n'|
|{tab}|:A tab: '\t'|
|{osxphotos_version}|The osxphotos version, e.g. '0.53.0'|
|{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|
@@ -2549,7 +2548,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<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></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>
@@ -2596,6 +2595,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<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>

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

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

@@ -2,7 +2,7 @@ build
bump2version==1.0.1
cogapp>=3.3.0,<4.0.0
furo
m2r2
m2r2==0.3.3
pdbpp
pyinstaller==5.6.2
pytest-cov==4.0.0
@@ -14,5 +14,4 @@ sphinx_rtd_theme
sphinx-copybutton
sphinxcontrib-programoutput
twine
wheel
mistune>=2.0.3 # not directly required, pinned by Snyk to avoid a vulnerability
wheel

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: f3427ad3d443a19a10f261c12c07338c
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.51.8 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.51.8 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.51.8 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>
@@ -250,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.51.7 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.51.7 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.51.7 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,97 +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">"12"</span><span class="p">,</span> <span class="s2">"5"</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"12"</span><span class="p">,</span> <span class="s2">"6"</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>
@@ -351,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>
@@ -436,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>
@@ -451,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>
@@ -480,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>
@@ -518,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>
@@ -570,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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@@ -357,7 +357,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.51.8'
- The osxphotos version, e.g. '0.55.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

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.51.8',
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.51.8 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.51.8 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.51.8 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>
@@ -284,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>
@@ -667,7 +675,7 @@
</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-p">osxphotos-import command line option</a>
<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>
@@ -1498,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>
@@ -1561,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>
@@ -2292,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>
@@ -2299,7 +2318,7 @@
-p
<ul>
<li><a href="cli.html#cmdoption-osxphotos-import-p">osxphotos-import command line option</a>
<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>
@@ -2522,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>
@@ -2531,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>
@@ -2606,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>
@@ -3275,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>
@@ -3637,7 +3662,7 @@
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-e">--exiftool</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-p">--exiftool-path</a>
<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>
@@ -3648,6 +3673,10 @@
<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>
@@ -3689,7 +3718,9 @@
</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><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>
@@ -3706,6 +3737,8 @@
<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
@@ -3717,8 +3750,6 @@
<li><a href="cli.html#cmdoption-osxphotos-info-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-inspect command line option
@@ -4806,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.51.8 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.51.8 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.51.8 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>
@@ -276,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>
@@ -301,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>
@@ -324,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.51.8 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.51.8 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.51.8 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.51.8 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.51.8 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.51.8 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.51.8 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.51.8 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.51.8 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.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" />
<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.51.8 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.51.8 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.51.8 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.51.8 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.51.8 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>
@@ -245,24 +247,24 @@
<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>
@@ -286,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>
@@ -602,14 +601,14 @@
</tr>
<tr class="row-odd"><td><p>{tab}</p></td>
<td><dl class="field-list simple">
<dt class="field-odd">A tab</dt>
<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.51.8</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>
@@ -678,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>
@@ -694,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>
@@ -733,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">
@@ -755,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.51.8 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.51.8 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.51.8 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,58 +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">"{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">--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">"{folder_album|lower}"</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|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">"{folder_album|filter(startswith</span> <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 │ │
@@ -254,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>
@@ -285,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>
@@ -299,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>
@@ -319,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;──┘ │
@@ -332,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>
@@ -357,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>
@@ -367,12 +369,12 @@
<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>
@@ -381,41 +383,41 @@
<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,}
│ │ │ │ │ │ │
@@ -441,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>
@@ -450,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,}}
@@ -465,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
@@ -483,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).
@@ -506,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>
@@ -530,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>
@@ -569,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">
@@ -613,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

@@ -0,0 +1,405 @@
OSXPhotos Template System
=========================
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.
In its simplest form, a template statement has the form: ``"{template_field}"``\ , for example ``"{title}"`` which would resolve to the title of the photo.
Template statements may contain one or more modifiers. The full syntax is:
``"pretext{delim+template_field:subfield(field_arg)|filter[find,replace] conditional?bool_value,default}posttext"``
Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.
``pretext`` and ``posttext`` are free form text. For example, if a photo has title "My Photo Title" the template statement ``"The title of the photo is {title}"``\ , resolves to ``"The title of the photo is My Photo Title"``. The ``pretext`` in this example is ``"The title if the photo is "`` and the template_field is ``{title}``.
``delim``\ : optional delimiter string to use when expanding multi-valued template values in-place
``+``\ : If present before template ``name``\ , expands the template in place. If ``delim`` not provided, values are joined with no delimiter.
e.g. if Photo keywords are ``["foo","bar"]``\ :
* ``"{keyword}"`` renders to ``"foo", "bar"``
* ``"{,+keyword}"`` renders to: ``"foo,bar"``
* ``"{; +keyword}"`` renders to: ``"foo; bar"``
* ``"{+keyword}"`` renders to ``"foobar"``
``template_field``\ : The template field to resolve. See `Template Substitutions <#template-substitutions>`_ for full list of template fields.
`:subfield`: Some templates have sub-fields, For example, `{exiftool:IPTC:Make}\ ``; the template_field is``\ exiftool\ ``and the sub-field is``\ IPTC:Make`.
``(field_arg)``\ : optional arguments to pass to the field; for example, with ``{folder_album}`` this is used to pass the path separator used for joining folders and albums when rendering the field (default is "/" for ``{folder_album}``\ ).
`|filter`: You may optionally append one or more filter commands to the end of the template field using the vertical pipe ('|') symbol. Filters may be combined, separated by '|' as in: ``{keyword|capitalize|parens}``.
Valid filters are:
* ``lower``\ : Convert value to lower case, e.g. 'Value' => 'value'.
* ``upper``\ : Convert value to upper case, e.g. 'Value' => 'VALUE'.
* ``strip``\ : Strip whitespace from beginning/end of value, e.g. ' Value ' => 'Value'.
* ``titlecase``\ : Convert value to title case, e.g. 'my value' => 'My Value'.
* ``capitalize``\ : Capitalize first word of value and convert other words to lower case, e.g. 'MY VALUE' => 'My value'.
* ``braces``\ : Enclose value in curly braces, e.g. 'value => '{value}'.
* ``parens``\ : Enclose value in parentheses, e.g. 'value' => '(value')
* ``brackets``\ : Enclose value in brackets, e.g. 'value' => '[value]'
* ``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.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 split strings delimited by comma, semicolon, or space, e.g. 'value1,value2' => ['value1', 'value2'].
* `chop(x)`: Remove x characters off the end of value, e.g. chop(1): 'Value' => 'Valu'; when applied to a list, chops characters from each list value, e.g. chop(1): ['travel', 'beach']=> ['trave', 'beac'].
* `chomp(x)`: Remove x characters from the beginning of value, e.g. chomp(1): ['Value'] => ['alue']; when applied to a list, removes characters from each list value, e.g. chomp(1): ['travel', 'beach']=> ['ravel', 'each'].
* ``sort``\ : Sort list of values, e.g. ['c', 'b', 'a'] => ['a', 'b', 'c'].
* ``rsort``\ : Sort list of values in reverse order, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
* ``reverse``\ : Reverse order of values, e.g. ['a', 'b', 'c'] => ['c', 'b', 'a'].
* ``uniq``\ : Remove duplicate values, e.g. ['a', 'b', 'c', 'b', 'a'] => ['a', 'b', 'c'].
* `join(x)`: Join list of values with delimiter x, e.g. join(,): ['a', 'b', 'c'] => 'a,b,c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is 'join()' which joins values together with no delimiter. e.g. join(): ['a', 'b', 'c'] => 'abc'.
* `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
* `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
* `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"]``\ :
* ``"{keyword|lower}"`` renders to ``"foo", "bar"``
* ``"{keyword|upper}"`` renders to: ``"FOO", "BAR"``
* ``"{keyword|capitalize}"`` renders to: ``"Foo", "Bar"``
* ``"{keyword|lower|parens}"`` renders to: ``"(foo)", "(bar)"``
e.g. if Photo description is "my description":
* ``"{descr|titlecase}"`` renders to: ``"My Description"``
e.g. If Photo is in ``Album1`` in ``Folder1``\ :
* ``"{folder_album}"`` renders to ``["Folder1/Album1"]``
* ``"{folder_album(>)}"`` renders to ``["Folder1>Album1"]``
* ``"{folder_album()}"`` renders to ``["Folder1Album1"]``
`[find,replace]`: optional text replacement to perform on rendered template value. For example, to replace "/" in an album name, you could use the template `"{album[/,-]}"`. Multiple replacements can be made by appending "|" and adding another find|replace pair. e.g. to replace both "/" and ":" in album name: ``"{album[/,-|:,-]}"``. find/replace pairs are not limited to single characters. The "|" character cannot be used in a find/replace pair.
`conditional`: optional conditional expression that is evaluated as boolean (True/False) for use with the `?bool_value` modifier. Conditional expressions take the form '`not operator value`' where `not` is an optional modifier that negates the `operator`. Note: the space before the conditional expression is required if you use a conditional expression. Valid comparison operators are:
* ``contains``\ : template field contains value, similar to python's ``in``
* `matches`: template field contains exactly value, unlike `contains`: does not match partial matches
* ``startswith``\ : template field starts with value
* ``endswith``\ : template field ends with value
* ``<=``\ : template field is less than or equal to value
* ``>=``\ : template field is greater than or equal to value
* ``<``\ : template field is less than value
* ``>``\ : template field is greater than value
* ``==``\ : template field equals value
* ``!=``\ : template field does not equal value
The ``value`` part of the conditional expression is treated as a bare (unquoted) word/phrase. Multiple values may be separated by '|' (the pipe symbol). ``value`` is itself a template statement so you can use one or more template fields in ``value`` which will be resolved before the comparison occurs.
For example:
* ``{keyword matches Beach}`` resolves to True if 'Beach' is a keyword. It would not match keyword 'BeachDay'.
* ``{keyword contains Beach}`` resolves to True if any keyword contains the word 'Beach' so it would match both 'Beach' and 'BeachDay'.
* ``{photo.score.overall > 0.7}`` resolves to True if the photo's overall aesthetic score is greater than 0.7.
* ``{keyword|lower contains beach}`` uses the lower case filter to do case-insensitive matching to match any keyword that contains the word 'beach'.
* ``{keyword|lower not contains beach}`` uses the ``not`` modifier to negate the comparison so this resolves to True if there is no keyword that matches 'beach'.
Examples: to export photos that contain certain keywords with the ``osxphotos export`` command's ``--directory`` option:
``--directory "{keyword|lower matches travel|vacation?Travel-Photos,Not-Travel-Photos}"``
This exports any photo that has keywords 'travel' or 'vacation' into a directory 'Travel-Photos' and all other photos into directory 'Not-Travel-Photos'.
This can be used to rename files as well, for example:
``--filename "{favorite?Favorite-{original_name},{original_name}}"``
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.
``?bool_value``\ : 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 ``"{hdr}"``\ ) 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 ``"{title}"``\ ) then the default value following a "," will be used.
e.g. if photo is an HDR image,
* ``"{hdr?ISHDR,NOTHDR}"`` renders to ``"ISHDR"``
and if it is not an HDR image,
* ``"{hdr?ISHDR,NOTHDR}"`` renders to ``"NOTHDR"``
``,default``\ : 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 ``{created.strftime}``. If no default value provided, "_" is used.
e.g., if photo has no title set,
* ``"{title}"`` renders to "_"
* ``"{title,I have no title}"`` renders to ``"I have no title"``
Template fields such as ``created.strftime`` use the default value to pass the template to use for ``strftime``.
e.g., if photo date is 4 February 2020, 19:07:38,
* ``"{created.strftime,%Y-%m-%d-%H%M%S}"`` renders to ``"2020-02-04-190738"``
Some template fields such as ``"{media_type}"`` use the default value to allow customization of the output. For example, ``"{media_type}"`` resolves to the special media type of the photo such as ``panorama`` or ``selfie``. You may use the default value to override these in form: ``"{media_type,video=vidéo;time_lapse=vidéo_accélérée}"``. In this example, if photo was a time_lapse photo, ``media_type`` would resolve to ``vidéo_accélérée`` instead of ``time_lapse``.
Either or both bool_value or default (False value) may be empty which would result in empty string ``""`` when rendered.
If you want to include "{" or "}" in the output, use "{openbrace}" or "{closebrace}" template substitution.
e.g. ``"{created.year}/{openbrace}{title}{closebrace}"`` would result in ``"2020/{Photo Title}"``.
**Variables**
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}``. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. 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" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
Variables can also be referenced as fields in the template string, for example: ``{var:year,created.year}{original_name}-{%year}``. 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: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
If you need to use a ``%`` (percent sign character), you can escape the percent sign by using ``%%``. You can also use the ``{percent}`` template field where a template field is required. For example:
``{title[:,%%]}`` replaces the ``:`` with ``%`` and ``{title contains Foo?{title}{percent},{title}}`` adds ``%`` to the title if it contains ``Foo``.
Template Substitutions
----------------------
.. list-table::
:header-rows: 1
* - Field
- Description
* - {name}
- Current filename of the photo
* - {original_name}
- Photo's original filename when imported to Photos
* - {title}
- Title of the photo
* - {descr}
- Description of the photo
* - {media_type}
- Special media type resolved in this precedence: selfie, time_lapse, panorama, slow_mo, screenshot, portrait, live_photo, burst, photo, video. Defaults to 'photo' or 'video' if no special type. Customize one or more media types using format: '{media_type,video=vidéo;time_lapse=vidéo_accélérée}'
* - {photo_or_video}
- 'photo' or 'video' depending on what type the image is. To customize, use default value as in '{photo_or_video,photo=fotos;video=videos}'
* - {hdr}
- Photo is HDR?; True/False value, use in format '{hdr?VALUE_IF_TRUE,VALUE_IF_FALSE}'
* - {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
* - {created.mm}
- 2-digit month of the photo creation time (zero padded)
* - {created.month}
- Month name in user's locale of the photo creation time
* - {created.mon}
- Month abbreviation in the user's locale of the photo creation time
* - {created.dd}
- 2-digit day of the month (zero padded) of photo creation time
* - {created.dow}
- Day of week in user's locale of the photo creation time
* - {created.doy}
- 3-digit day of year (e.g Julian day) of photo creation time, starting from 1 (zero padded)
* - {created.hour}
- 2-digit hour of the photo creation time
* - {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
* - {modified.mm}
- 2-digit month of the photo modification time (zero padded); uses creation date if photo is not modified
* - {modified.month}
- Month name in user's locale of the photo modification time; uses creation date if photo is not modified
* - {modified.mon}
- Month abbreviation in the user's locale of the photo modification time; uses creation date if photo is not modified
* - {modified.dd}
- 2-digit day of the month (zero padded) of the photo modification time; uses creation date if photo is not modified
* - {modified.dow}
- Day of week in user's locale of the photo modification time; uses creation date if photo is not modified
* - {modified.doy}
- 3-digit day of year (e.g Julian day) of photo modification time, starting from 1 (zero padded); uses creation date if photo is not modified
* - {modified.hour}
- 2-digit hour of the photo modification time; uses creation date if photo is not modified
* - {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
* - {today.mm}
- 2-digit month of the current date (zero padded)
* - {today.month}
- Month name in user's locale of the current date
* - {today.mon}
- Month abbreviation in the user's locale of the current date
* - {today.dd}
- 2-digit day of the month (zero padded) of current date
* - {today.dow}
- Day of week in user's locale of the current date
* - {today.doy}
- 3-digit day of year (e.g Julian day) of current date, starting from 1 (zero padded)
* - {today.hour}
- 2-digit hour of the current date
* - {today.min}
- 2-digit minute of the current date
* - {today.sec}
- 2-digit second of the current date
* - {today.strftime}
- 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 https://strftime.org/ for help on strftime templates.
* - {place.name}
- Place name from the photo's reverse geolocation data, as displayed in Photos
* - {place.country_code}
- The ISO country code from the photo's reverse geolocation data
* - {place.name.country}
- Country name from the photo's reverse geolocation data
* - {place.name.state_province}
- State or province name from the photo's reverse geolocation data
* - {place.name.city}
- City or locality name from the photo's reverse geolocation data
* - {place.name.area_of_interest}
- Area of interest name (e.g. landmark or public place) from the photo's reverse geolocation data
* - {place.address}
- Postal address from the photo's reverse geolocation data, e.g. '2007 18th St NW, Washington, DC 20009, United States'
* - {place.address.street}
- Street part of the postal address, e.g. '2007 18th St NW'
* - {place.address.city}
- City part of the postal address, e.g. 'Washington'
* - {place.address.state_province}
- State/province part of the postal address, e.g. 'DC'
* - {place.address.postal_code}
- Postal code part of the postal address, e.g. '20009'
* - {place.address.country}
- Country name of the postal address, e.g. 'United States'
* - {place.address.country_code}
- ISO country code of the postal address, e.g. 'US'
* - {searchinfo.season}
- Season of the year associated with a photo, e.g. 'Summer'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).
* - {exif.camera_make}
- Camera make from original photo's EXIF information as imported by Photos, e.g. 'Apple'
* - {exif.camera_model}
- Camera model from original photo's EXIF information as imported by Photos, e.g. 'iPhone 6s'
* - {exif.lens_model}
- Lens model from original photo's EXIF information as imported by Photos, e.g. 'iPhone 6s back camera 4.15mm f/2.2'
* - {moment}
- The moment title of the photo
* - {uuid}
- Photo's internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. '128FB4C6-0B16-4E7D-9108-FB2E90DA1546'
* - {shortuuid}
- A shorter representation of photo's internal universally unique identifier (UUID) for the photo, a 22-character string unique to the photo, e.g. 'JYsxugP9UjetmCbBCHXcmu'
* - {id}
- 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.
* - {album_seq}
- An integer, starting at 0, indicating the photo's 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\ *seq}*\ {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}'.
* - {folder_album_seq}
- An integer, starting at 0, indicating the photo's 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\ *seq}*\ {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}'.
* - {comma}
- A comma: ','
* - {semicolon}
- A semicolon: ';'
* - {questionmark}
- A question mark: '?'
* - {pipe}
- A vertical pipe: '|'
* - {openbrace}
- An open brace: '{'
* - {closebrace}
- A close brace: '}'
* - {openparens}
- An open parentheses: '('
* - {closeparens}
- A close parentheses: ')'
* - {openbracket}
- An open bracket: '['
* - {closebracket}
- A close bracket: ']'
* - {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'
* - {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
* - {project}
- Project(s) photo is contained in (such as greeting cards, calendars, slideshows)
* - {album_project}
- Album(s) and project(s) photo is contained in; treats projects as regular albums
* - {folder_album_project}
- Folder path + album (includes projects as albums) photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder
* - {keyword}
- Keyword(s) assigned to photo
* - {person}
- Person(s) / face(s) in a photo
* - {label}
- Image categorization label associated with a photo (Photos 5+ only). Labels are added automatically by Photos using machine learning algorithms to categorize images. These are not the same as {keyword} which refers to the user-defined keywords/tags applied in Photos.
* - {label_normalized}
- All lower case version of 'label' (Photos 5+ only)
* - {comment}
- Comment(s) on shared Photos; format is 'Person name: comment text' (Photos 5+ only)
* - {exiftool}
- Format: '{exiftool:GROUP:TAGNAME}'; use exiftool (https://exiftool.org) to extract metadata, in form GROUP:TAGNAME, from image. E.g. '{exiftool:EXIF:Make}' to get camera make, or {exiftool:IPTC:Keywords} to extract keywords. See https://exiftool.org/TagNames/ for list of valid tag names. You must specify group (e.g. EXIF, IPTC, etc) as used in ``exiftool -G``. exiftool must be installed in the path to use this template.
* - {searchinfo.holiday}
- Holiday names associated with a photo, e.g. 'Christmas Day'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).
* - {searchinfo.activity}
- Activities associated with a photo, e.g. 'Sporting Event'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).
* - {searchinfo.venue}
- Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).
* - {searchinfo.venue_type}
- Venue types associated with a photo, e.g. 'Restaurant'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).
* - {photo}
- Provides direct access to the PhotoInfo object for the photo. Must be used in format '{photo.property}' where 'property' represents a PhotoInfo property. For example: '{photo.favorite}' is the same as '{favorite}' and '{photo.place.name}' is the same as '{place.name}'. '{photo}' provides access to properties that are not available as separate template fields but it assumes some knowledge of the underlying PhotoInfo class. See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.
* - {detected_text}
- List of text strings found in the image after performing text detection. Using '{detected_text}' will cause osxphotos to perform text detection on your photos using the built-in macOS text detection algorithms which will slow down your export. The results for each photo will be cached in the export database so that future exports with '--update' do not need to reprocess each photo. You may pass a confidence threshold value between 0.0 and 1.0 after a colon as in '{detected_text:0.5}'; The default confidence threshold is 0.75. '{detected_text}' works only on macOS Catalina (10.15) or later. Note: this feature is not the same thing as Live Text in macOS Monterey, which osxphotos does not yet support.
* - {shell_quote}
- Use in form '{shell_quote,TEMPLATE}'; quotes the rendered TEMPLATE value(s) for safe usage in the shell, e.g. My file.jpeg => 'My file.jpeg'; only adds quotes if needed.
* - {strip}
- Use in form '{strip,TEMPLATE}'; strips whitespace from begining and end of rendered TEMPLATE value(s).
* - {format}
- Use in form, '{format:TYPE:FORMAT,TEMPLATE}'; converts TEMPLATE value to TYPE then formats the value using Python string formatting codes specified by FORMAT; TYPE is one of: 'int', 'float', or 'str'. For example, '{format:float:.1f,{exiftool:EXIF:FocalLength}}' will format focal length to 1 decimal place (e.g. '100.0').
* - {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.

492
docsrc/source/tutorial.rst Normal file
View File

@@ -0,0 +1,492 @@
.. role:: raw-html-m2r(raw)
:format: html
.. raw:: html
<!-- OSXPHOTOS-TUTORIAL-HEADER:START -->
OSXPhotos Tutorial
==================
Overview
--------
.. raw:: html
<!-- OSXPHOTOS-TUTORIAL-HEADER:END -->
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 ``export`` 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 ``export`` command which exports photos from the library so that's the focus of this tutorial.
Export your photos
------------------
``osxphotos export /path/to/export``
This command exports all your photos to the ``/path/to/export`` directory.
**Note**\ : 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.
Export by date
--------------
While the previous command will export all your photos (and videos--see note above), it probably doesn't do exactly what you want. In the previous example, all the photos will be exported to a single folder: ``/path/to/export``. If you have a large library with thousands of images and videos, this likely isn't very useful. You can use the ``--export-by-date`` option to export photos to a folder structure organized by year, month, day, e.g. ``2021/04/21``\ :
``osxphotos export /path/to/export --export-by-date``
With this command, a photo that was created on 31 May 2015 would be exported to: ``/path/to/export/2015/05/31``
Specify directory structure
---------------------------
If you prefer a different directory structure for your exported images, osxphotos provides a very flexible :raw-html-m2r:`<!-- OSXPHOTOS-TEMPLATE-SYSTEM-LINK:START -->`\ template system\ :raw-html-m2r:`<!-- OSXPHOTOS-TEMPLATE-SYSTEM-LINK:END -->` that allows you to specify the directory structure using the ``--directory`` option. For example, this command exported to a directory structure that looks like: ``2015/May`` (4-digit year / month name):
``osxphotos export /path/to/export --directory "{created.year}/{created.month}"``
The string following ``--directory`` is an ``osxphotos template string``. Template strings are widely used throughout osxphotos and it's worth your time to learn more about them. In a template string, the values between the curly braces, e.g. ``{created.year}`` are replaced with metadata from the photo being exported. In this case, ``{created.year}`` is the 4-digit year of the photo's creation date and ``{created.month}`` is the full month name in the user's locale (e.g. ``May``\ , ``mai``\ , etc.). In the osxphotos template system these are referred to as template fields. The text not included between ``{}`` pairs is interpreted literally, in this case ``/``\ , is a directory separator.
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 ``--directory`` template, you could thus export photos organized by country name:
``osxphotos export /path/to/export --directory "{created.year}/{place.name.country}"``
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).
``osxphotos export /path/to/export --directory "{created.year}/{place.name.country,No-Country}"``
The value after the ',' in the template string is the default value, in this case 'No-Country'. **Note**\ : If you don't specify a default value and a template field is null, osxphotos will use "_" (underscore character) as the default.
Some template fields, such as ``{keyword}``\ , may expand to more than one value. For example, if a photo has keywords of "Travel" and "Vacation", ``{keyword}`` would expand to "Travel", "Vacation". When used with ``--directory``\ , 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 ``IMG_1234.JPG`` has keywords ``Travel``\ , and ``Vacation`` and you run the following command:
``osxphotos export /path/to/export --directory "{keyword}"``
the exported files would be:
.. code-block::
/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:
``osxphotos export /path/to/export --filename "{title}"``
The above command will export photos using the title. Note that you don't need to specify the extension as part of the ``--filename`` 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:
.. code-block::
osxphotos export /path/to/export --filename "{title,{original_name}}"
│ ││ │
│ ││ │
Use photo's title as the filename <──────┘ ││ │
││ │
Value after comma will be used <───────┘│ │
if title is blank │ │
│ │
The default value can be <────┘ │
another template field │
Use photo's original name if no title <──────┘
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 ``--filename`` 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 that's a favorite:
.. code-block::
osxphotos export /path/to/export --filename "{original_name}{favorite?#,}"
│ │ │││
│ │ │││
Use photo's original name as filename <──┘ │ │││
│ │││
'favorite' is True if photo is a Favorite, <───────┘ │││
otherwise, False │││
│││
'?' specifies a conditional <─────────────┘││
││
Value immediately following ? will be used if <──────┘│
preceding template field is True or non-blank │
Value immediately following comma will be used if <──────┘
template field is False or blank (null); in this case
no value is specified so a blank string "" will be used
Like with ``--directory``\ , using a multi-valued template field such as ``{keyword}`` may result in more than one copy of a photo being exported. For example, if ``IMG_1234.JPG`` has keywords ``Travel``\ , and ``Vacation`` and you run the following command:
``osxphotos export /path/to/export --filename "{keyword}-{original_name}"``
the exported files would be:
.. code-block::
/path/to/export/Travel-IMG_1234.JPG
/path/to/export/Vacation-IMG_1234.JPG
Edited photos
-------------
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 ``IMG_1234.JPG``\ , osxphotos will export the original as ``IMG_1234.JPG`` and the edited version as ``IMG_1234_edited.jpeg``. **Note:** 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 ``--edited-suffix`` option:
``osxphotos export /path/to/export --edited-suffix "_EDIT"``
In this example, the edited image would be named ``IMG_1234_EDIT.jpeg``. Like many options in osxphotos, the ``--edited-suffix`` 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:
``osxphotos export /path/to/export --edited-suffix "_{modified.year}-{modified.mm}-{modified.dd}"``
In this example, if the photo was edited on 21 April 2021, the name of the exported file would be: ``IMG_1234_2021-04-21.jpeg``.
You can tell osxphotos to not export edited photos (that is, only export the original unedited photos) using ``--skip-edited``\ :
``osxphotos export /path/to/export --skip-edited``
You can also tell osxphotos to export either the original photo (if the photo has not been edited) or the edited photo (if it has been edited), but not both, using the ``--skip-original-if-edited`` option:
``osxphotos export /path/to/export --skip-original-if-edited``
As mentioned above, Photos renames JPEG images that have been edited with the ".jpeg" extension. Some applications use ".JPG" and others use ".jpg" or ".JPEG". You can use the ``--jpeg-ext`` option to have osxphotos rename all JPEG files with the same extension. Valid values are jpeg, jpg, JPEG, JPG; e.g. ``--jpeg-ext jpg`` to use '.jpg' for all JPEGs.
``osxphotos export /path/to/export --jpeg-ext jpg``
Specifying the Photos library
-----------------------------
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 don't specify which library to use, osxphotos will try find the last opened library. Occasionally it can't 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 ``--db`` option. (db is short for database and is so named because osxphotos operates on the database that Photos uses to manage your Photos library).
``osxphotos export /path/to/export --db ~/Pictures/MyAlternateLibrary.photoslibrary``
Missing photos
--------------
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 hasn't 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.
If you encounter missing photos you can tell osxphotos to download the missing photos from iCloud using the ``--download-missing`` option. ``--download-missing`` 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. There's also an experimental ``--use-photokit`` option that will communicate with Photos using a different "PhotoKit" interface. This option must be used together with ``--download-missing``\ :
``osxphotos export /path/to/export --download-missing``
``osxphotos export /path/to/export --download-missing --use-photokit``
Exporting to external disks
---------------------------
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 ``--retry`` option so that osxphotos will automatically retry the export. Use ``--retry`` with a number that specifies the number of times to retry the export:
``osxphotos export /path/to/export --retry 3``
In this example, osxphotos will attempt to export a photo up to 3 times if it encounters an error.
In addition to ``--retry``\ , the ``--exportdb`` and ``--ramdb`` may improve performance when exporting to an external disk or a NAS. When osxphotos exports photos, it creates an export database file named ``.osxphotos_export.db`` in the export folder which osxphotos uses to keep track of which photos have been exported. This allows you to restart and export and to use ``--update`` to update an existing export. If the connection to the export location is slow or flaky, having the export database located on the export disk may decrease performance. In this case, you can use ``--exportdb DBPATH`` to instruct osxphotos to store the export database at DBPATH. If using this option, I recommend putting the export database on your Mac system disk (for example, in your home directory). If you intend to use ``--update`` to update the export in the future, you must remember where the export database is and use the ``--exportdb`` option every time you update the export.
Another alternative to using ``--exportdb`` is to use ``--ramdb``. 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 doesn't 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.
Exporting metadata with exported photos
---------------------------------------
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 `\ ``exiftool`` <https://exiftool.org/>`_ app, osxphotos can write metadata to exported photos. Follow the instructions on the exiftool website to install exiftool then you can use the ``--exiftool`` option to write metadata to exported photos:
``osxphotos export /path/to/export --exiftool``
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 ``--exiftool`` to modify the metadata that is written by ``exiftool``. For example, you can use the ``--keyword-template`` 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:
.. code-block::
osxphotos export /path/to/export --exiftool --keyword-template "{folder_album(>)}"
│ │
│ │
folder_album results in the folder(s) <──┘ │
and album a photo is contained in │
The value in () is used as the path separator <───────┘
for joining the folders and albums. For example,
if photo is in Folder1/Folder2/Album, (>) produces
"Folder1>Folder2>Album" which some programs, such as
Lightroom Classic, treat as hierarchical keywords
The above command will write all the regular metadata that ``--exiftool`` normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form "Folder1>Folder2>Album". If you did not include the ``(>)`` in the template string (e.g. ``{folder_album}``\ ), folder_album would render in form "Folder1/Folder2/Album".
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 ``{label}``. 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 image's metadata:
``osxphotos export /path/to/export --exiftool --keyword-template "{label}"``
Removing a keyword during export
--------------------------------
If some of your photos contain a keyword you do not want to be added to the exported file with ``--exiftool``\ , 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:
``osxphotos export /path/to/export --exiftool --keyword-template "{keyword|remove(MyKeyword)}" --replace-keywords``
In this example, ``|remove(MyKeyword)`` is a filter which removes ``MyKeyword`` from the keyword list of every photo being processed. The ``--replace-keywords`` option instructs osxphotos to replace the keywords in the exported file with the filtered keywords from ``--keyword-template``.
**Note**\ : When evaluating templates for ``--directory`` and ``--filename``\ , osxphotos inserts the automatic default value "_" for any template field which is null (empty or blank). This is to ensure that there's never a null directory or filename created. For metadata templates such as ``--keyword-template``\ , 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 doesn't have labels:
``osxphotos export /path/to/export --exiftool --keyword-template "{label,nolabel}"``
Sidecar files
-------------
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 ``--sidecar`` option. For example, to output metadata to XMP sidecars:
``osxphotos export /path/to/export --sidecar XMP``
Unlike ``--exiftool``\ , you do not need to install exiftool to use the ``--sidecar`` feature. Many of the same configuration options that apply to ``--exiftool`` to modify metadata, for example, ``--keyword-template`` can also be used with ``--sidecar``.
Sidecar files are named "photoname.ext.sidecar_ext". For example, if the photo is named ``IMG_1234.JPG`` and the sidecar format is XMP, the sidecar would be named ``IMG_1234.JPG.XMP``. Some applications expect the sidecar in this case to be named ``IMG_1234.XMP``. You can use the ``-sidecar-drop-ext`` option to force osxphotos to name the sidecar files in this manner:
``osxphotos export /path/to/export --sidecar XMP -sidecar-drop-ext``
Updating a previous export
--------------------------
If you want to use osxphotos to perform periodic backups of your Photos library rather than a one-time export, use the ``--update`` option. When ``osxphotos export`` is run, it creates a database file named ``.osxphotos_export.db`` in the export folder. (\ **Note** Because the filename starts with a ".", you won't 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 ``--update`` 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:
``osxphotos export /path/to/export --update``
will read the export database located in ``/path/to/export/.osxphotos_export.db`` and only export photos that have been added or changed since the last time osxphotos was run. You can run osxphotos with the ``--update`` option even if it's never been run before. If the database isn't found, osxphotos will create it. If you run ``osxphotos export`` without ``--update`` 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 ``--update``.
If your workflow involves moving files out of the export directory (for example, you move them into a digital asset management app) but you want to use the features of ``--update``\ , you can use the ``--only-new`` with ``--update`` to force osxphotos to only export photos that are new (added to the library) since the last update. In this case, osxphotos will ignore the previously exported files that are now missing. Without ``--only-new``\ , osxphotos would see that previously exported files are missing and re-export them.
``osxphotos export /path/to/export --update --only-new``
If your workflow involves editing the images you exported from Photos but you still want to maintain a backup with ``--update``\ , you should use the ``--ignore-signature`` option. ``--ignore-signature`` instructs osxphotos to ignore the file's signature (for example, size and date modified) when deciding which files should be updated with ``--update``. If you edit a file in the export directory and then run ``--update`` without ``--ignore-signature``\ , osxphotos will see that the file is different than the one in the Photos library and re-export it.
``osxphotos export /path/to/export --update --ignore-signature``
Dry Run
-------
You can use the ``--dry-run`` option to have osxphotos "dry run" or test an export without actually exporting any files. When combined with the ``--verbose`` 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 ``--directory`` and ``--filename`` templates are correct, ``--dry-run --verbose`` will print out the name of each file being exported.
``osxphotos export /path/to/export --dry-run --verbose``
Creating a report of all exported files
---------------------------------------
You can use the ``--report`` 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 ``--report`` option:
``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.
For example, to export only photos with keyword ``Travel``\ :
``osxphotos export /path/to/export --keyword "Travel"``
Like many options in osxphotos, ``--keyword`` (and most other query options) can be repeated to search for more than one term. For example, to find photos with keyword ``Travel`` *or* keyword ``Vacation``\ :
``osxphotos export /path/to/export --keyword "Travel" --keyword "Vacation"``
To export only photos contained in the album "Summer Vacation":
``osxphotos export /path/to/export --album "Summer Vacation"``
In Photos, it's possible to have multiple albums with the same name. In this case, osxphotos would export photos from all albums matching the value passed to ``--album``. If you wanted to export only one of the albums and this album is in a folder, the ``--regex`` option (short for "regular expression"), which does pattern matching, could be used with the ``{folder_album}`` 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:
``osxphotos export /path/to/export --regex "2018/Summer Vacation" "{folder_album}"``
This command matches the pattern "2018/Summer Vacation" against the full folder/album path for every photo.
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":
``osxphotos export /path/to/export --portrait``
You can also export photos in a certain date range:
``osxphotos export /path/to/export --from-date "2020-01-01" --to-date "2020-02-28"``
or photos added to the library in the last week:
``osxphotos export /path/to/export --added-in-last "1 week"``
Converting images to JPEG on export
-----------------------------------
Photos can store images in many different formats. osxphotos can convert non-JPEG images (for example, RAW photos) to JPEG on export using the ``--convert-to-jpeg`` option. You can specify the JPEG quality (0: worst, 1.0: best) using ``--jpeg-quality``. For example:
``osxphotos export /path/to/export --convert-to-jpeg --jpeg-quality 0.9``
Finder attributes
-----------------
In addition to using ``exiftool`` 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.
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 ``tag:tagname`` syntax:
``osxphotos export /path/to/export --finder-tag-keywords``
``--finder-tag-keywords`` also works with ``--keyword-template`` as described above in the section on ``exiftool``\ :
``osxphotos export /path/to/export --finder-tag-keywords --keyword-template "{label}"``
The ``--xattr-template`` option allows you to set a variety of other extended attributes. It is used in the format ``--xattr-template ATTRIBUTE TEMPLATE`` where ATTRIBUTE is one of 'authors','comment', 'copyright', 'description', 'findercomment', 'headline', 'keywords'.
For example, to set Finder comment to the photo's title and description:
``osxphotos export /path/to/export --xattr-template findercomment "{title}{newline}{descr}"``
In the template string above, ``{newline}`` instructs osxphotos to insert a new line character ("\n") between the title and description. In this example, if ``{title}`` or ``{descr}`` is empty, you'll get "title\n" or "\ndescription" which may not be desired so you can use more advanced features of the template system to handle these cases:
``osxphotos export /path/to/export --xattr-template findercomment "{title,}{title?{descr?{newline},},}{descr,}"``
Explanation of the template string:
.. code-block::
{title,}{title?{descr?{newline},},}{descr,}
│ │ │ │ │ │ │
│ │ │ │ │ │ │
└──> insert title (or nothing if no title)
│ │ │ │ │ │
└───> is there a title?
│ │ │ │ │
└───> if so, is there a description?
│ │ │ │
└───> if so, insert new line
│ │ │
└───> if descr is blank, insert nothing
│ │
└───> if title is blank, insert nothing
└───> finally, insert description
(or nothing if no description)
In this example, ``title?`` demonstrates use of the boolean (True/False) feature of the template system. ``title?`` is read as "Is the title True (or not blank/empty)? If so, then the value immediately following the ``?`` is used in place of ``title``. If ``title`` is blank, then the value immediately following the comma is used instead. The format for boolean fields is ``field?value if true,value if false``. Either ``value if true`` or ``value if false`` may be blank, in which case a blank string ("") is used for the value and both may also be an entirely new template string as seen in the above example. Using this format, template strings may be nested inside each other to form complex ``if-then-else`` statements.
The above example, while complex to read, shows how flexible the osxphotos template system is. If you invest a little time learning how to use the template system you can easily handle almost any use case you have.
See Extended Attributes section in the help for ``osxphotos export`` for additional information about this feature.
Saving and loading options
--------------------------
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 (\ ``--save-config FILE``\ ) and then load them (\ ``--load-config FILE``\ ) instead of repeating each option on the command line.
To save the configuration:
``osxphotos export /path/to/export <all your options here> --update --save-config osxphotos.toml``
Then the next to you run osxphotos, you can simply do this:
``osxphotos export /path/to/export --load-config osxphotos.toml``
The configuration file is a plain text file in `TOML <https://toml.io/en/>`_ format so the ``.toml`` extension is standard but you can name the file anything you like.
Run commands on exported photos for post-processing
---------------------------------------------------
You can use the ``--post-command`` option to run one or more commands against exported files. The ``--post-command`` 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: ``osxphotos help export``. For example, the ``exported`` category includes all exported photos and the ``skipped`` category includes all photos that were skipped when running export with ``--update``. COMMAND is an osxphotos template string which will be rendered then passed to the shell for execution.
For example, the following command generates a log of all exported files and their associated keywords:
``osxphotos export /path/to/export --post-command exported "echo {shell_quote,{filepath}{comma}{,+keyword,}} >> {shell_quote,{export_dir}/exported.txt}"``
The special template field ``{shell_quote}`` ensures a string is properly quoted for execution in the shell. For example, it's 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 ``{filepath}`` is set to the full path of the exported file and ``{export_dir}`` is set to the full path of the base export directory.
Explanation of the template string:
.. code-block::
{shell_quote,{filepath}{comma}{,+keyword,}}
│ │ │ │ │
│ │ │ | │
└──> quote everything after comma for proper execution in the shell
│ │ │ │
└───> filepath of the exported file
│ │ │
└───> insert a comma
│ │
└───> join the list of keywords together with a ","
└───> if no keywords, insert nothing (empty string: "")
Another example: if you had ``exiftool`` installed and wanted to wipe all metadata from all exported files, you could use the following:
``osxphotos export /path/to/export --post-command exported "/usr/local/bin/exiftool -all= {filepath|shell_quote}"``
This command uses the ``|shell_quote`` template filter instead of the ``{shell_quote}`` 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.
An example from an actual osxphotos user
----------------------------------------
Here's 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!):
.. code-block::
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
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
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
to spot all videos created on my iPhone using the Quik application (now part of
GoPro).
I want to retrieve my keywords only and make sure they populate the Finder tags, as
well as export all the persons identified in the videos by Photos. I also want to
merge any keywords or persons already in the video metadata with the exported
metadata.
Keeping Photos edited titles and descriptions and putting both in the Finder
comments field in a readable manner is also enabled.
And I want to keep the files creation date (using `--touch-file`).
Finally, use `--strip` to remove any leading or trailing whitespace from processed
template fields.
``osxphotos export ~/Desktop/folder for exported videos/ --keyword Quik --only-movies --db /path to my.photoslibrary --touch-file --finder-tag-keywords --person-keyword --xattr-template findercomment "{title}{title?{descr?{newline},},}{descr}" --exiftool-merge-keywords --exiftool-merge-persons --exiftool --strip``
Color Themes
------------
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 ``--theme`` option. For example: ``osxphotos export /path/to/export --verbose --theme dark`` uses a theme suited for dark terminals. If you don't 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 ``osxphotos help theme`` for more information on themes and for commands to help manage themes. Themes are defined in ``.theme`` files in the ``~/.osxphotos/themes`` directory and use style specifications compatible with the `rich <https://rich.readthedocs.io/en/stable/style.html>`_ library.
Conclusion
----------
osxphotos is very flexible. If you merely want to backup your Photos library, then spending a few minutes to understand the ``--directory`` 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.

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

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

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

@@ -121,6 +121,7 @@ _TESTED_OS_VERSIONS = [
("12", "4"),
("12", "5"),
("12", "6"),
("13", "0"),
]
# Photos 5 has persons who are empty string if unidentified face
@@ -236,21 +237,29 @@ class SearchCategory:
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"""
# NOTE: This list is incomplete;
# until I get a test library that's been processed by photoanalysisd on Ventura,
# I can't verify all these are correct
# 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
ACTIVITY = 1600
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:

View File

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

View File

@@ -50,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
@@ -651,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,
@@ -745,6 +754,7 @@ def export(
added_in_last,
album_keyword,
album,
alt_copy,
append,
beta,
burst,
@@ -969,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
@@ -1333,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:

View File

@@ -26,13 +26,20 @@ import click
from photoscript import Photo, PhotosLibrary
from rich.console import Console
from rich.markdown import Markdown
from strpdatetime import strpdatetime
from osxphotos._constants import _OSXPHOTOS_NONE_SENTINEL
from osxphotos._version import __version__
from osxphotos.cli.common import get_data_dir
from osxphotos.cli.help import HELP_WIDTH
from osxphotos.cli.param_types import TemplateString
from osxphotos.datetime_utils import datetime_naive_to_local
from osxphotos.cli.param_types import FunctionCall, StrpDateTimePattern, TemplateString
from osxphotos.datetime_utils import (
datetime_has_tz,
datetime_naive_to_local,
datetime_remove_tz,
datetime_tz_to_utc,
datetime_utc_to_local,
)
from osxphotos.exiftool import ExifToolCaching, get_exiftool_path
from osxphotos.photoinfo import PhotoInfoNone
from osxphotos.photosalbum import PhotosAlbumPhotoScript
@@ -42,6 +49,7 @@ from osxphotos.utils import pluralize
from .click_rich_echo import (
rich_click_echo,
rich_echo_error,
set_rich_console,
set_rich_theme,
set_rich_timestamp,
@@ -89,7 +97,7 @@ class PhotoInfoFromFile:
@property
def date(self):
"""Use file creation date and local timezone"""
"""Use file creation date and local time zone"""
ctime = os.path.getctime(self._path)
dt = datetime.datetime.fromtimestamp(ctime)
return datetime_naive_to_local(dt)
@@ -463,6 +471,33 @@ def set_photo_location(
photo.location = location
def set_photo_date_from_filename(
photo: Photo, filepath: Path, parse_date: str, verbose: Callable[..., None]
):
"""Set date of photo from filename"""
# TODO: handle timezone (use code from timewarp), for now convert timezone to local timezone
try:
date = strpdatetime(filepath.name, parse_date)
# Photo.date must be timezone naive (assumed to local timezone)
if datetime_has_tz(date):
local_date = datetime_remove_tz(
datetime_utc_to_local(datetime_tz_to_utc(date))
)
verbose(
f"Moving date with timezone [time]{date}[/] to local timezone: [time]{local_date.strftime('%Y-%m-%d %H:%M:%S')}[/]"
)
date = local_date
except ValueError:
verbose(
f"[warning]Could not parse date from filename [filename]{filepath.name}[/][/]"
)
return
verbose(
f"Setting date of photo [filename]{filepath.name}[/] to [time]{date.strftime('%Y-%m-%d %H:%M:%S')}[/]"
)
photo.date = date
def get_relative_filepath(filepath: Path, relative_to: Optional[str]) -> Path:
"""Get relative filepath of file relative to relative_to or return filepath if relative_to is None
@@ -499,6 +534,7 @@ def check_templates_and_exit(
album: Tuple[str],
exiftool_path: Optional[str],
exiftool: bool,
parse_date: Optional[str],
):
"""Renders templates against each file so user can verify correctness"""
for file in files:
@@ -539,6 +575,14 @@ def check_templates_and_exit(
)
rendered_album = rendered_album[0] if rendered_album else "None"
echo(f"album: [italic]{al}[/]: {rendered_album}")
if parse_date:
try:
date = strpdatetime(file.name, parse_date)
echo(f"date: [italic]{parse_date}[/]: {date}")
except ValueError:
echo(
f"[warning]Could not parse date from filename [filename]{file.name}[/][/]"
)
sys.exit(0)
@@ -912,7 +956,9 @@ class ImportCommand(click.Command):
and you want to exclude /Volumes/Photos from the folder/album path, you can do this:
`osxphotos import /Volumes/Photos/* --walk --album "{filepath}" --relative-to "/Volumes/Photos"`
`osxphotos import /Volumes/Photos/* --walk --album "{filepath.parent}" --relative-to "/Volumes/Photos" --split-folder "/"`
This will produce folders/albums `2021/Family`, `2021/Travel`, and so on.
Note: in Photos, only albums can contain photos and folders
may contain albums or other folders.
@@ -1040,6 +1086,70 @@ class ImportCommand(click.Command):
but will instead print out the rendered value for each `--title`, `--description`,
`--keyword`, and `--album` option. It will also print out the values extracted by
the `--exiftool` option.
## Parsing Dates/Times from Filenames
The --parse-date option allows you to parse dates/times from the filename of the
file being imported. This is useful if you have a large number of files with
dates/times embedded in the filename but not in the metadata.
The argument to `--parse-date` is a pattern string that is used to parse the date/time
from the filename. The pattern string is a superset of the python `strftime/strptime`
format with the following additions:
- *: Match any number of characters
- ^: Match the beginning of the string
- $: Match the end of the string
- {n}: Match exactly n characters
- {n,}: Match at least n characters
- {n,m}: Match at least n characters and at most m characters
- In addition to `%%` for a literal `%`, the following format codes are supported:
`%^`, `%$`, `%*`, `%|`, `%{`, `%}` for `^`, `$`, `*`, `|`, `{`, `}` respectively
- |: join multiple format codes; each code is tried in order until one matches
- Unlike the standard library, the leading zero is not optional for
%d, %m, %H, %I, %M, %S, %j, %U, %W, and %V
- For optional leading zero, use %-d, %-m, %-H, %-I, %-M, %-S, %-j, %-U, %-W, and %-V
For more information on strptime format codes, see:
https://docs.python.org/3/library/datetime.html?highlight=strptime#strftime-and-strptime-format-codes
**Note**: The time zone of the parsed date/time is assumed to be the local time zone.
If the parse pattern includes a time zone, the photo's time will be converted from
the specified time zone to the local time zone. osxphotos import does not
currently support setting the time zone of imported photos.
See also `osxphotos help timewarp` for more information on the timewarp
command which can be used to change the time zone of photos after import.
### Examples
If you have photos with embedded names in filenames like `IMG_1234_20200322_123456.jpg`
and `12345678_20200322.jpg`, you can parse the dates with the following pattern:
`--parse-date "IMG_*_%Y%m%d_%H%M%S|*_%Y%m%d.*"`. The first pattern matches the first format
and the second pattern matches the second. The `|` character is used to separate the two
patterns. The order is important as the first pattern will be tried first then the second
and so on. If you have multiple formats in your filenames you will want to order the patterns
from most specific to least specific to avoid false matches.
## Post Function
You can run a custom python function after each photo is imported using `--post-function`.
The format is `osxphotos import /file/to/import --post-function post_function.py::post_function`
where `post_function.py` is the name of the python file containing the function and `post_function`
is the name of the function. The function will be called with the following arguments:
`post_function(photo: photoscript.Photo, filepath: pathlib.Path, verbose: t.Callable, **kwargs)`
- 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
The function will get called immediately after the photo has been imported into Photos
and all metadata been set (e.g. --exiftool, --title, etc.)
You may call more than one function by repeating the `--post-function` option.
See https://rhettbull.github.io/PhotoScript/
for documentation on photoscript and the Photo class that is passed to the function.
"""
)
console = Console()
@@ -1111,6 +1221,21 @@ class ImportCommand(click.Command):
"Longitude is a number in the range -180.0 to 180.0; "
"positive longitudes are east of the Prime Meridian; negative longitudes are west of the Prime Meridian.",
)
@click.option(
"--parse-date",
"-P",
metavar="DATE_PATTERN",
type=StrpDateTimePattern(),
help="Parse date from filename using DATE_PATTERN. "
"If file does not match DATE_PATTERN, the date will be set by Photos using Photo's default behavior. "
"DATE_PATTERN is a strptime-compatible pattern with extensions as pattern described below. "
"If DATE_PATTERN matches time zone information, the time will be set to the local time in the timezone "
"as the import command does not yet support setting time zone information. "
"For example, if your photos are named 'IMG_1234_2022_11_23_12_34_56.jpg' where the date/time is "
"'2022-11-23 12:34:56', you could use the pattern '%Y_%m_%d_%H_%M_%S' or "
"'IMG_*_%Y_%m_%d_%H_%M_%S' to further narrow the pattern to only match files with 'IMG_xxxx_' in the name."
"See also --check-templates.",
)
@click.option(
"--clear-metadata",
"-C",
@@ -1215,7 +1340,20 @@ class ImportCommand(click.Command):
"--check-templates",
is_flag=True,
help="Don't actually import anything; "
"renders template strings so you can verify they are correct.",
"renders template strings and date patterns so you can verify they are correct.",
)
@click.option(
"--post-function",
metavar="filename.py::function",
nargs=1,
type=FunctionCall(),
multiple=True,
help="Run python function after importing file."
"Use this in format: --post-function filename.py::function where filename.py is a python "
"file you've created and function is the name of the function in the python file you want to call. "
"The function will be passed a reference to the photo object and the path to the file that was imported. "
"You can run more than one function by repeating the '--post-function' option with different arguments. "
"See Post Function below.",
)
@THEME_OPTION
@click.argument("files", nargs=-1)
@@ -1239,6 +1377,8 @@ def import_cli(
location,
merge_keywords,
no_progress,
parse_date,
post_function,
relative_to,
report,
resume,
@@ -1287,6 +1427,7 @@ def import_cli(
album,
exiftool_path,
exiftool,
parse_date,
)
# initialize report data
@@ -1379,6 +1520,9 @@ def import_cli(
if location:
set_photo_location(photo, filepath, location, verbose)
if parse_date:
set_photo_date_from_filename(photo, filepath, parse_date, verbose)
if album:
add_photo_to_albums(
photo,
@@ -1390,6 +1534,17 @@ def import_cli(
verbose,
)
if post_function:
for function in post_function:
# post function is tuple of (function, filename.py::function_name)
verbose(f"Calling post-function [bold]{function[1]}")
try:
function[0](photo, filepath, verbose)
except Exception as e:
rich_echo_error(
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
)
update_report_record(report_data[filepath], photo, filepath)
import_db.set(str(filepath), report_data[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

@@ -245,14 +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_edited:
path_str += "\n"
path_str += bold("Path edited: ")
path_str += f"[filepath]{format_path_link(photo.path_edited)}[/]"
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: ")

Binary file not shown.

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

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

View File

@@ -1297,7 +1297,9 @@ class PhotosDB:
RKModelResource.resourceTag, RKModelResource.UTI, RKVersion.specialType,
RKModelResource.attachedModelType, RKModelResource.resourceType
FROM RKVersion
JOIN RKModelResource on RKModelResource.attachedModelId = RKVersion.modelId """
JOIN RKModelResource on RKModelResource.attachedModelId = RKVersion.modelId
ORDER BY RKModelResource.modelId
"""
)
# Order of results:
@@ -1307,8 +1309,8 @@ class PhotosDB:
# 3 RKModelResource.resourceTag
# 4 RKModelResource.UTI
# 5 RKVersion.specialType
# 6 RKModelResource.attachedModelType
# 7 RKModelResource.resourceType
# 6 RKModelResource.attachedModelType (2 = edit)
# 7 RKModelResource.resourceType (4 = photo, 8 = video)
for row in c:
uuid = row[0]
@@ -1320,18 +1322,30 @@ class PhotosDB:
and row[1] != "UNADJUSTED"
and row[6] == 2
):
if "edit_resource_id" in self._dbphotos[uuid]:
if is_debug():
logging.debug(
f"WARNING: found more than one edit_resource_id for "
f"UUID {row[0]},adjustmentUUID {row[1]}, modelID {row[2]}"
)
# TODO: I think there should never be more than one edit but
# I've seen this once in my library
# should we return all edits or just most recent one?
# For now, return most recent edit
self._dbphotos[uuid]["edit_resource_id"] = row[2]
self._dbphotos[uuid]["UTI_edited"] = row[4]
resource_type = row[7]
# UTI_edited will be set to the appropriate UTI for the edited resource below
# a live photo that's edited will have both a photo and video resource but the photo
# UTI will be used for the edited live photo, see #859
if resource_type == 4:
# photo
if "edit_resource_id_photo" in self._dbphotos[uuid]:
if is_debug():
logging.debug(
f"WARNING: found more than one edit_resource_id_photo for "
f"UUID {row[0]},adjustmentUUID {row[1]}, modelID {row[2]}"
)
self._dbphotos[uuid]["edit_resource_id_photo"] = row[2]
self._dbphotos[uuid]["UTI_edited_photo"] = row[4]
elif resource_type == 8:
# video
if "edit_resource_id_video" in self._dbphotos[uuid]:
if is_debug():
logging.debug(
f"WARNING: found more than one edit_resource_id_video for "
f"UUID {row[0]},adjustmentUUID {row[1]}, modelID {row[2]}"
)
self._dbphotos[uuid]["edit_resource_id_video"] = row[2]
self._dbphotos[uuid]["UTI_edited_video"] = row[4]
# get details on external edits
c.execute(
@@ -1384,9 +1398,27 @@ class PhotosDB:
)
# init any uuids that had no edits or live photos
# also initialized UTI_edited and edit_resource_id
for uuid in self._dbphotos:
if "edit_resource_id" not in self._dbphotos[uuid]:
self._dbphotos[uuid]["edit_resource_id"] = None
if "edit_resource_id_photo" not in self._dbphotos[uuid]:
self._dbphotos[uuid]["edit_resource_id_photo"] = None
if "edit_resource_id_video" not in self._dbphotos[uuid]:
self._dbphotos[uuid]["edit_resource_id_video"] = None
if "UTI_edited_photo" not in self._dbphotos[uuid]:
self._dbphotos[uuid]["UTI_edited_photo"] = None
if "UTI_edited_video" not in self._dbphotos[uuid]:
self._dbphotos[uuid]["UTI_edited_video"] = None
# UTI_edited will be set to the appropriate UTI for the edited resource below
# a live photo that's edited will have both a photo and video resource but the photo
# UTI will be used for the edited live photo
self._dbphotos[uuid]["UTI_edited"] = (
self._dbphotos[uuid]["UTI_edited_photo"]
or self._dbphotos[uuid]["UTI_edited_video"]
)
self._dbphotos[uuid]["edit_resource_id"] = (
self._dbphotos[uuid]["edit_resource_id_photo"]
or self._dbphotos[uuid]["edit_resource_id_video"]
)
if "live_model_id" not in self._dbphotos[uuid]:
self._dbphotos[uuid]["live_model_id"] = None
self._dbphotos[uuid]["modeResourceIsOnDisk"] = None
@@ -1792,7 +1824,8 @@ class PhotosDB:
"parentfolder": album[7],
"pk": album[8],
"intrash": False if album[9] == 0 else True,
"creation_date": album[10] or 0, # iPhone Photos.sqlite can have null value
"creation_date": album[10]
or 0, # iPhone Photos.sqlite can have null value
"start_date": album[11] or 0,
"end_date": album[12] or 0,
"customsortascending": album[13],
@@ -2169,6 +2202,12 @@ class PhotosDB:
info["alt_master_uuid"] = None # Photos 4
info["raw_info"] = None # Photos 4
# Photos 4 only
info["edit_resource_id_photo"] = None
info["edit_resource_id_video"] = None
info["UTI_edited_photo"] = None
info["UTI_edited_video"] = None
self._dbphotos[uuid] = info
# compute signatures for finding possible duplicates

View File

@@ -21,7 +21,6 @@ __all__ = [
"get_db_version",
"get_model_version",
"get_db_model_version",
"UnknownLibraryVersion",
"get_photos_library_version",
]
@@ -115,10 +114,6 @@ def get_db_model_version(db_file: str) -> int:
return 8
class UnknownLibraryVersion(Exception):
pass
def get_photos_library_version(library_path):
"""Return int indicating which Photos version a library was created with"""
library_path = pathlib.Path(library_path)
@@ -140,4 +135,9 @@ def get_photos_library_version(library_path):
return 6
if _PHOTOS_7_MODEL_VERSION[0] <= model_ver <= _PHOTOS_7_MODEL_VERSION[1]:
return 7
raise UnknownLibraryVersion(f"db_ver = {db_ver}, model_ver = {model_ver}")
if _PHOTOS_8_MODEL_VERSION[0] <= model_ver <= _PHOTOS_8_MODEL_VERSION[1]:
return 8
logging.warning(
f"Unknown db / model version: db_ver={db_ver}, model_ver={model_ver}; assuming Photos 8"
)
return 8

View File

@@ -132,6 +132,21 @@ class SearchInfo:
types += self._get_text_for_category(category)
return types
@property
def detected_text(self):
"""Returns text detected in the photo (macOS 13+ / Photos 8+ only)"""
if self._photo._db._photos_ver < 8:
return []
return self._get_text_for_category(self._categories.DETECTED_TEXT)
@property
def camera(self):
"""returns camera name (macOS 13+ / Photos 8+ only)"""
if self._photo._db._photos_ver < 8:
return ""
camera = self._get_text_for_category(self._categories.CAMERA)
return camera[0] if camera else ""
@property
def all(self):
"""return all search info properties in a single list"""
@@ -147,6 +162,7 @@ class SearchInfo:
+ self.venues
+ self.venue_types
+ self.media_types
+ self.detected_text
)
if self.city:
all += [self.city]
@@ -162,6 +178,8 @@ class SearchInfo:
all += [self.year]
if self.season:
all += [self.season]
if self.camera:
all += [self.camera]
return all
@@ -186,6 +204,8 @@ class SearchInfo:
"venues": self.venues,
"venue_types": self.venue_types,
"media_types": self.media_types,
"detected_text": self.detected_text,
"camera": self.camera,
}
def _get_text_for_category(self, category):

View File

@@ -1,7 +1,6 @@
__all__ = ["get_preferred_uti_extension", "get_uti_for_extension"]
""" get UTI for a given file extension and the preferred extension for a given UTI """
""" get UTI for a given file extension and the preferred extension for a given UTI
""" Implementation note: runs only on macOS
Implementation note: runs only on macOS
On macOS <= 11 (Big Sur), uses objective C CoreServices methods
UTTypeCopyPreferredTagWithClass and UTTypeCreatePreferredIdentifierForTag to retrieve the
@@ -17,6 +16,8 @@ __all__ = ["get_preferred_uti_extension", "get_uti_for_extension"]
It's a bit hacky but best I can think of to make this robust on different versions of macOS. PRs welcome.
"""
from __future__ import annotations
import csv
import re
import subprocess
@@ -27,6 +28,8 @@ import objc
from .utils import _get_os_version
__all__ = ["get_preferred_uti_extension", "get_uti_for_extension"]
# cached values of all the UTIs (< 6 chars long) known to my Mac running macOS 10.15.7
UTI_CSV = """extension,UTI,preferred_extension,MIME_type
c,public.c-source,c,None
@@ -565,7 +568,7 @@ def _get_ext_from_uti_dict(uti):
return None
def get_preferred_uti_extension(uti):
def get_preferred_uti_extension(uti: str) -> str | None:
"""get preferred extension for a UTI type
uti: UTI str, e.g. 'public.jpeg'
returns: preferred extension as str or None if cannot be determined"""
@@ -582,7 +585,7 @@ def get_preferred_uti_extension(uti):
# on MacOS 10.12, HEIC files are not supported and UTTypeCopyPreferredTagWithClass will return None for HEIC
if uti == "public.heic":
return "HEIC"
return "heic"
return None

View File

@@ -101,22 +101,27 @@ def _check_file_exists(filename):
return os.path.exists(filename) and not os.path.isdir(filename)
def _get_resource_loc(model_id):
"""returns folder_id and file_id needed to find location of edited photo"""
""" and live photos for version <= Photos 4.0 """
def _get_resource_loc(model_id) -> tuple[str, str, str]:
"""returns folder_id and file_id needed to find location of edited photo
and live photos for version <= Photos 4.0
modified version of code in utils to debug #859
"""
# determine folder where Photos stores edited version
# edited images are stored in:
# Photos Library.photoslibrary/resources/media/version/XX/00/fullsizeoutput_Y.jpeg
# Photos Library.photoslibrary/resources/media/version/folder_id/nn/fullsizeoutput_file_id.jpeg
# where XX and Y are computed based on RKModelResources.modelId
# file_id (Y in above example) is hex representation of model_id without leading 0x
file_id = hex_id = hex(model_id)[2:]
# folder_id (XX) in above example if first two chars of model_id converted to hex
# folder_id (XX) is digits -4 and -3 of hex representation of model_id
# and left padded with zeros if < 4 digits
folder_id = hex_id.zfill(4)[0:2]
folder_id = hex_id.zfill(4)[-4:-2]
return folder_id, file_id
# find the nn_id which is the hex_id digits minus the last 4 chars (or 00 if len(hex_id) <= 4)
nn_id = hex_id[: len(hex_id) - 4].zfill(2) if len(hex_id) > 4 else "00"
return folder_id, file_id, nn_id
def _dd_to_dms(dd):

View File

@@ -1,33 +1,34 @@
Click>=8.0.4,<9.0
Click>=8.0.4,<9.0s
Mako>=1.2.2,<1.3.0
PyYAML>=6.0.0,<7.0.0
bitmath>=1.3.3.1,<1.4.0.0
bpylist2==4.0.1
more-itertools>=8.8.0,<9.0.0
objexplore>=1.6.3,<2.0.0
osxmetadata>=1.0.0,<2.0.0
osxmetadata>=1.2.0,<2.0.0
packaging>=21.3
pathvalidate>=2.4.1,<2.5.0
photoscript>=0.1.6,<0.2.0
photoscript>=0.2.1,<0.3.0
ptpython>=3.0.20,<3.1.0
pyobjc-core>=7.3,<9.0
pyobjc-framework-AVFoundation>=7.3,<9.0
pyobjc-framework-AppleScriptKit>=7.3,<9.0
pyobjc-framework-AppleScriptObjC>=7.3,<9.0
pyobjc-framework-Cocoa>=7.3,<9.0
pyobjc-framework-CoreServices>=7.2,<9.0
pyobjc-framework-Metal>=7.3,<9.0
pyobjc-framework-Photos>=7.3,<9.0
pyobjc-framework-Quartz>=7.3,<9.0
pyobjc-framework-Vision>=7.3,<9.0
pyobjc-core>=9.0,<10.0
pyobjc-framework-AVFoundation>=9.0,<10.0
pyobjc-framework-AppleScriptKit>=9.0,<10.0
pyobjc-framework-AppleScriptObjC>=9.0,<10.0
pyobjc-framework-Cocoa>=9.0,<10.0
pyobjc-framework-CoreServices>=9.0,<10.0
pyobjc-framework-Metal>=9.0,<10.0
pyobjc-framework-Photos>=9.0,<10.0
pyobjc-framework-Quartz>=9.0,<10.0
pyobjc-framework-Vision>=9.0,<10.0
pytimeparse2==1.4.0
requests>=2.27.1,<3.0.0
rich>=11.2.0,<13.0.0
rich_theme_manager>=0.11.0
shortuuid==1.0.9
strpdatetime>=0.2.0
tenacity>=8.0.1,<9.0.0
textx>=2.3.0,<2.4.0
textx>=3.0.0,<4.0.0
toml>=0.10.2,<0.11.0
wrapt>=1.13.3,<1.14.0
wurlitzer>=2.1.0,<2.2.0
wrapt>=1.14.1,<2.0.0
wurlitzer>=3.0.2,<4.0.0
xdg==5.1.1

View File

@@ -67,9 +67,9 @@ setup(
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries :: Python Modules",
],
install_requires=[
@@ -80,31 +80,32 @@ setup(
"bpylist2==4.0.1",
"more-itertools>=8.8.0,<9.0.0",
"objexplore>=1.6.3,<2.0.0",
"osxmetadata>=1.0.0,<2.0.0",
"osxmetadata>=1.2.0,<2.0.0",
"packaging>=21.3",
"pathvalidate>=2.4.1,<3.0.0",
"photoscript>=0.1.6,<0.2.0",
"photoscript>=0.2.1,<0.3.0",
"ptpython>=3.0.20,<4.0.0",
"pyobjc-core>=7.3,<9.0",
"pyobjc-framework-AVFoundation>=7.3,<9.0",
"pyobjc-framework-AppleScriptKit>=7.3,<9.0",
"pyobjc-framework-AppleScriptObjC>=7.3,<9.0",
"pyobjc-framework-Cocoa>=7.3,<9.0",
"pyobjc-framework-CoreServices>=7.2,<9.0",
"pyobjc-framework-Metal>=7.3,<9.0",
"pyobjc-framework-Photos>=7.3,<9.0",
"pyobjc-framework-Quartz>=7.3,<9.0",
"pyobjc-framework-Vision>=7.3,<9.0",
"pyobjc-core>=9.0,<=10.0",
"pyobjc-framework-AVFoundation>=9.0,<10.0",
"pyobjc-framework-AppleScriptKit>=9.0,<10.0",
"pyobjc-framework-AppleScriptObjC>=9.0,<10.0",
"pyobjc-framework-Cocoa>=9.0,<10.0",
"pyobjc-framework-CoreServices>=9.0,<10.0",
"pyobjc-framework-Metal>=9.0,<10.0",
"pyobjc-framework-Photos>=9.0,<10.0",
"pyobjc-framework-Quartz>=9.0,<10.0",
"pyobjc-framework-Vision>=9.0,<10.0",
"pytimeparse2==1.4.0",
"requests>=2.27.1,<3.0.0",
"rich>=11.2.0,<13.0.0",
"rich_theme_manager>=0.11.0",
"shortuuid==1.0.9",
"strpdatetime>=0.2.0",
"tenacity>=8.0.1,<9.0.0",
"textx>=2.3.0,<3.0.0",
"textx>=3.0.0,<4.0.0",
"toml>=0.10.2,<0.11.0",
"wrapt>=1.13.3,<1.14.0",
"wurlitzer>=2.1.0,<3.0.0",
"wrapt>=1.14.1,<2.0.0",
"wurlitzer>=3.0.2,<4.0.0",
"xdg==5.1.1",
],
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli_main"]},

View File

@@ -30,6 +30,8 @@ A couple of tests require interaction with Photos and configuring a specific tes
## Test Photo Libraries
**Important**: The test code uses several test photo libraries created on various version of MacOS. If you need to inspect one of these or modify one for a test, make a copy of the library (for example, copy it to your ~/Pictures folder) then open the copy in Photos. Once done, copy the revised library back to the tests/ folder. If you do not do this, the Photos background process photoanalysisd will forever try to process the library resulting in updates to the database which will cause git to see changes to the file you didn't intend. I'm not aware of any way to disassociate photoanalysisd from the library once you've opened it in Photos.
Some of the "search_info" tests require data from my personal library on Catalina 10.15.7. The data is generated by running `python3 tests/generate_search_info_test_data.py >tests/search_info_test_data_10_15_7.json`
## Attribution ##
These tests utilize a test Photos library. The test library is populated with photos from [flickr](https://www.flickr.com) and from my own photo library. All images used are licensed under Creative Commons 2.0 Attribution [license](https://creativecommons.org/licenses/by/2.0/).

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LibrarySchemaVersion</key>
<integer>5001</integer>
<key>MetaSchemaVersion</key>
<integer>3</integer>
</dict>
</plist>

Binary file not shown.

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>hostname</key>
<string>ddrucker-mba.local</string>
<key>hostuuid</key>
<string>3C58BD83-C174-52E3-B12D-D7EBDED55622</string>
<key>pid</key>
<integer>940</integer>
<key>processname</key>
<string>photolibraryd</string>
<key>uid</key>
<integer>502</integer>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BlacklistedMeaningsByMeaning</key>
<dict/>
<key>SceneWhitelist</key>
<array>
<string>Art</string>
<string>Flower</string>
<string>Food</string>
<string>Jewelry</string>
<string>Lake</string>
<string>Shore</string>
<string>Sport</string>
<string>Vehicle</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>insertAlbum</key>
<array/>
<key>insertAsset</key>
<array/>
<key>insertHighlight</key>
<array/>
<key>insertMemory</key>
<array/>
<key>insertMoment</key>
<array/>
<key>removeAlbum</key>
<array/>
<key>removeAsset</key>
<array/>
<key>removeHighlight</key>
<array/>
<key>removeMemory</key>
<array/>
<key>removeMoment</key>
<array/>
<key>renamePerson</key>
<array/>
</dict>
</plist>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>embeddingVersion</key>
<string>1</string>
<key>featureFlags</key>
<string>319</string>
<key>featuredContentAllowed</key>
<string>1</string>
<key>localeIdentifier</key>
<string>en_US</string>
<key>sceneTaxonomySHA</key>
<string>64d078bafc0035e1ec26dfa565c2ac0479fcbab329fda1c16cd17e0fdbf2f4c0,4afa5d3c45c08a664cf73cff957aaeeae3a325d2970aada51268407b9ad0f03e</string>
<key>searchIndexVersion</key>
<string>16025</string>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

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