Compare commits

...

109 Commits

Author SHA1 Message Date
Rhet Turnbull
de0b4fa11f Release v0.60.2 (#1089) 2023-06-17 07:18:55 -07:00
Rhet Turnbull
269a2dfa9e Fix download missing when not needed 1086 (#1088)
* Fixed staging to not call download missing if not needed

* Optimizations for #1086

* Memoize compiled XMP template, #1086
2023-06-17 07:14:24 -07:00
Rhet Turnbull
986010ec5f Updated example [skip ci] 2023-06-15 22:11:35 -07:00
Rhet Turnbull
e062582d14 Added export_preview.py example [skip_ci] 2023-05-31 21:41:35 -07:00
allcontributors[bot]
d30314bedc add rajscode as a contributor for doc (#1076)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-05-14 20:26:39 -07:00
rajscode
7dfd24cb96 Update README.md (#1074)
since changed to single
2023-05-14 20:25:00 -07:00
Rhet Turnbull
fcb015dd4a Updated CHANGELOG.md [skip ci] 2023-05-14 08:36:27 -07:00
Rhet Turnbull
d2338992a2 Release 0.60.1 (#1073) 2023-05-14 08:29:49 -07:00
Rhet Turnbull
234db3fb54 Fix for #1071, crash with --dry-run (#1072) 2023-05-14 08:27:51 -07:00
Rhet Turnbull
55d4be6892 Updated CHANGELOG.md [skip ci] 2023-05-07 08:10:16 -07:00
Rhet Turnbull
922634ab3e Updated release v0.60.0 (#1067) 2023-05-07 07:59:51 -07:00
Rhet Turnbull
f0aa69d8bb Ran black (#1066) 2023-05-07 07:33:07 -07:00
Rhet Turnbull
ee8053d867 Fixed tests for ubuntu 2023-05-07 07:25:48 -07:00
Rhet Turnbull
253901281d Fixed requirements.txt for ubuntu 2023-05-07 07:14:18 -07:00
Rhet Turnbull
051da093ea Added platform conditionals to requirements.txt 2023-05-07 07:08:54 -07:00
Rhet Turnbull
fd5b16578c Added ubuntu runners 2023-05-07 07:02:45 -07:00
dvdkon
ca3da647f2 Port to non-MacOS platforms (#1026)
* Port to non-MacOS platforms

* Keep NFD normalization on macOS

* Update locale_util.py

Fix lint error from ruff (runs in CI)

* Update query.py

click.Option first arg needs to be a list (different than click.option)

* Dynamically normalize Unicode paths in test

* Fix missing import

---------

Co-authored-by: Rhet Turnbull <rturnbull@gmail.com>
2023-05-07 06:55:56 -07:00
Rhet Turnbull
0c85298c03 Updated API_README.md to add tables() 2023-04-16 09:19:10 -07:00
Rhet Turnbull
b4b58d3b00 Updated API_README.md to add tables() 2023-04-16 09:17:56 -07:00
Rhet Turnbull
ed543aa2d0 Feature phototables (#1059)
* Added tables() method to PhotoInfo to get access to underlying tables

* This time with the phototables code...
2023-04-16 09:02:59 -07:00
allcontributors[bot]
1b0c91db97 add pekingduck as a contributor for bug (#1058)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-12 20:41:51 -07:00
Rhet Turnbull
dd3914328b Updated CHANGELOG.md [skip ci] 2023-04-10 20:59:09 -07:00
Rhet Turnbull
4b4252a73c Release 0.59.3 (#1053) 2023-04-10 20:49:19 -07:00
Rhet Turnbull
956cecfa30 Bug memory leak 1047 (#1052)
* Fix for memory leak, #1047

* Refactored photos_by_uuid, AlbumInfo.asdict for speed optimization

* Fix for huge crash log, #1048

* Fix for error on export #1046

* add rajscode as a contributor for bug (#1049)

* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>

* add wernerzj as a contributor for bug (#1050)

* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Rhet Turnbull <rturnbull@gmail.com>

* Fixed all-contributors badge

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-10 16:50:17 -07:00
Rhet Turnbull
6f88f19950 Fixed all-contributors badge 2023-04-10 12:06:13 -07:00
allcontributors[bot]
ce4f3c4c0b add wernerzj as a contributor for bug (#1050)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Rhet Turnbull <rturnbull@gmail.com>
2023-04-10 12:02:37 -07:00
allcontributors[bot]
3993cf220d add rajscode as a contributor for bug (#1049)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-10 11:57:10 -07:00
allcontributors[bot]
2ae41a5a7a add oPromessa as a contributor for doc (#1045)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-08 21:58:26 -07:00
oPromessa
519a6b0035 Small typo on Development Environment creation instructions (#1043)
Corrections on READNE_DEV.md and  testes/README.md
2023-04-08 21:56:38 -07:00
Rhet Turnbull
a4b4f1c288 Feature help no selection 1036 (#1042)
* Added validation for --selected

* Added test for #999 (project_info) that I missed on last branch
2023-04-08 14:34:08 -07:00
Rhet Turnbull
f668ecf0c4 Updated CHANGELOG.md [skip ci] 2023-04-08 12:10:43 -07:00
Rhet Turnbull
058b56092f Release 0.59.2 (#1041) 2023-04-08 12:04:54 -07:00
Rhet Turnbull
d2b7783125 Added shallow json() option, #1038 (#1040) 2023-04-08 11:10:29 -07:00
Rhet Turnbull
f4a743468d Fix for json() failing on photos in projects, #999 (#1039) 2023-04-08 09:49:31 -07:00
Rhet Turnbull
adac985309 Fix non-incrementing photo count, #999 2023-04-08 09:09:33 -07:00
allcontributors[bot]
4939324d2f add dvdkon as a contributor for code (#1037)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-07 08:17:00 -07:00
Christian Clauss
1a3b4c2afe Lint Python twice: 1. Whole repo, 2. Exclude tests/ (#1035)
* Lint Python twice: 1. Whole repo, 2. Exclude tests/

Both runs take less than a second so...
`line-length=366` is more reasonable`
```
    - run: pip install --user ruff  # Lint Python twice: 1. Whole repo, 2. Exclude tests/
    - run: ruff --format=github --line-length=7228 --target-version=py39
                --ignore=E402,E712,E721,E722,E741,F401,F403,F405,F541,F601,F811,F822,F841 .
    - run: ruff --format=github --line-length=366 --target-version=py39
                --exclude=tests/ --ignore=E402,E712,E722,E741,F401,F403,F405,F541,F822,F841 .
```

* Update configoptions.py
2023-04-07 08:14:51 -07:00
Rhet Turnbull
1b16a39cef Updated CHANGELOG.md [skip ci] 2023-04-02 22:07:10 -07:00
Rhet Turnbull
0d31a152be Test fix for Ventura 2023-04-02 22:00:35 -07:00
Rhet Turnbull
34fbd87fc6 Release 0.59.1 (#1034) 2023-04-02 21:18:18 -07:00
Christian Clauss
3ddfea6a3d Fix two undefined names in phototemplate.py (#1030)
* Fix two undefined names in phototemplate.py

% `ruff --exit-zero --select=E9,F63,F7,F82,YTT .`
```
osxphotos/cli/report_writer.py:22:1: F822 Undefined name `ExportReportWriterSqlite` in `__all__`
osxphotos/cli/report_writer.py:22:1: F822 Undefined name `SyncReportWriterSqlite` in `__all__`
osxphotos/phototemplate.py:1156:108: F821 Undefined name `vals`
osxphotos/phototemplate.py:1685:29: F821 Undefined name `PhotoInfo`
Found 4 errors.
```

* Avoid cyclic imports
2023-04-02 19:45:33 -07:00
Christian Clauss
8b59615ff7 GitHub Action to lint Python code (#1025)
* GitHub Action to lint Python code

[Ruff](https://beta.ruff.rs/) supports [over 500 lint rules](https://beta.ruff.rs/docs/rules) including bandit, isort, pylint, pyupgrade, and flake8 plus its plugins, and is written in Rust for speed.

The `ruff` Action uses minimal steps to __run in ~5 seconds__, rapidly providing intuitive GitHub Annotations to contributors.

![image](https://user-images.githubusercontent.com/3709715/223758136-afc386d2-70aa-4eff-953a-2c2d82ceea23.png)

* ruff --target-version=py39
2023-04-02 19:45:13 -07:00
allcontributors[bot]
c590a16d70 add cclauss as a contributor for code (#1033)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-02 19:44:49 -07:00
Rhet Turnbull
36ce86c4a6 Removed file locking, performance improvements (#1032) 2023-04-02 19:42:28 -07:00
Rhet Turnbull
93d22c646f Updated example [skip ci] 2023-04-01 13:59:47 -07:00
Rhet Turnbull
77000d85c6 Updated example [skip ci] 2023-04-01 10:26:20 -07:00
Rhet Turnbull
005f821501 Updated CHANGELOG.md [skip ci] 2023-04-01 10:17:16 -07:00
Rhet Turnbull
622f8952b7 Release 0.59.0 2023-04-01 10:09:26 -07:00
Rhet Turnbull
b7816be459 Added concurrent export example 2023-04-01 10:03:05 -07:00
Rhet Turnbull
e7099d250b Concurrency refactor 999 (#1029)
* Working on making export threadsafe, #999

* Working on making export threadsafe, #999

* refactor for concurrent export, #999

* Fixed race condition in ExportRecord context manager
2023-04-01 09:39:08 -07:00
Rhet Turnbull
2c4d0f4546 Fixed help text 2023-03-14 12:26:19 -07:00
Rhet Turnbull
84954f4551 Updated CHANGELOG.md [skip ci] 2023-03-14 12:22:04 -07:00
allcontributors[bot]
10ebe9e02b add pekingduck as a contributor for ideas (#1021)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-03-14 12:07:37 -07:00
Rhet Turnbull
6813e9f2b4 Release files for 0.58.2 (#1020) 2023-03-14 12:06:43 -07:00
Rhet Turnbull
1d1b69601f Added --replace-keywords to batch-edit #1018 (#1019) 2023-03-14 11:58:13 -07:00
Rhet Turnbull
3ad4c7a4cc Updated CHANGELOG.md [skip ci] 2023-03-09 06:59:08 -08:00
allcontributors[bot]
0dd628682f add ianmmoir as a contributor for bug (#1016)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-03-09 06:55:37 -08:00
Rhet Turnbull
8f7d88d8da Release files for 0.58.1 2023-03-09 06:50:23 -08:00
Rhet Turnbull
4fcd381262 Hot fix for null date, #1014 2023-03-09 06:49:05 -08:00
Rhet Turnbull
10d1ea8b2c rebuild pages 2023-03-09 06:32:58 -08:00
Rhet Turnbull
cfb623d19b Feature appends prepends 1010 (#1015)
* Added appends, prepends filters, #1010

* Fixed initialization of field_arg
2023-03-09 06:22:24 -08:00
Rhet Turnbull
a08680ed02 Added python and macOS versions to --version (#1008) 2023-02-28 06:43:06 -08:00
Rhet Turnbull
56ca54ad0d Updated CHANGELOG.md [skip ci] 2023-02-25 20:05:08 -08:00
allcontributors[bot]
2e4046c319 add eecue as a contributor for bug (#1005)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-02-25 14:56:09 -08:00
Rhet Turnbull
0b6be87d15 Release files for 0.58.0 (#1004) 2023-02-25 14:54:48 -08:00
Rhet Turnbull
b7c98aa548 Fixed docstring 2023-02-25 14:49:16 -08:00
Rhet Turnbull
0694662f78 Moved custom param types to param_types 2023-02-25 14:43:37 -08:00
Rhet Turnbull
7eb35b31ac Updated supported versions 2023-02-25 14:40:44 -08:00
Rhet Turnbull
94f484e9ec Feature date added 998 (#1003)
* Implemented --date-added, #998

* Added --date-added-from-photo

* Fixed typehint
2023-02-25 14:38:49 -08:00
Rhet Turnbull
0e1613f134 Fix error on closing export db, #999 (#1002) 2023-02-25 14:38:07 -08:00
Rhet Turnbull
1661cc9f0b Feature batch edit 949 (#1001)
* Initial implementation of batch-edit, #949

* Added tests for batch-edit, #949
2023-02-25 14:37:26 -08:00
Rhet Turnbull
1981340108 Added batch_edit.py example, [skip ci] 2023-02-23 06:43:41 -08:00
Rhet Turnbull
c3ca25fe33 Added batch_edit.py example, [skip ci] 2023-02-22 06:36:53 -08:00
Rhet Turnbull
1e10df26c9 Added batch_edit.py example, [skip ci] 2023-02-22 06:35:00 -08:00
Rhet Turnbull
8fdecd56f5 Added batch_edit.py example, [skip ci] 2023-02-21 21:29:59 -08:00
Rhet Turnbull
b2ed70b00c Added batch_edit.py example, [skip ci] 2023-02-21 21:26:07 -08:00
Rhet Turnbull
3a77a3e5f0 Added batch_edit.py example, [skip ci] 2023-02-21 21:16:53 -08:00
Rhet Turnbull
e89f8a14b4 Added batch_edit.py example, [skip ci] 2023-02-20 21:45:37 -08:00
Rhet Turnbull
82295513fe Added batch_edit.py example, [skip ci] 2023-02-20 21:43:37 -08:00
Rhet Turnbull
e7e3e72f75 Added batch_edit.py example, [skip ci] 2023-02-20 17:50:09 -08:00
Rhet Turnbull
9006708a30 Added batch_edit.py example, [skip ci] 2023-02-20 17:39:02 -08:00
Rhet Turnbull
4c2570a81d Added batch_edit.py example, [skip ci] 2023-02-20 17:18:38 -08:00
Rhet Turnbull
30e81eddb5 Updated CHANGELOG.md [skip ci] 2023-02-20 09:04:19 -08:00
Rhet Turnbull
5aa4a20bb5 Added pip caching 2023-02-20 08:58:08 -08:00
Rhet Turnbull
109917321a Updated docstrings, [skip-ci] 2023-02-20 08:54:44 -08:00
Rhet Turnbull
7fb7a551a8 Release files for 0.57.3 (#996) 2023-02-20 08:46:39 -08:00
Rhet Turnbull
d3267cb0f0 Added ability to show from export filepath to osxphotos show (#995) 2023-02-20 08:44:17 -08:00
Rhet Turnbull
1209d98e6e Updated CHANGELOG.md [skip ci] 2023-02-20 08:14:22 -08:00
Rhet Turnbull
c90c241a36 Release files for 0.57.2 (#994) 2023-02-20 07:58:17 -08:00
Rhet Turnbull
206490cacc Fixed click.confirm to not abort 2023-02-19 23:00:33 -08:00
Rhet Turnbull
31212a1daa Added exportdb test for #990 2023-02-19 22:54:13 -08:00
Rhet Turnbull
a46f8bd244 Fix error during cleanup, #987 (#993) 2023-02-19 22:51:52 -08:00
Rhet Turnbull
06b02b4a23 Feature exportdb update 990 (#992)
* Added cloud_guid, #990

* Added fingerprint, cloud_guid to PhotoInfo.json

* Added stub for --migrate-photos-library

* Added --migrate-photos-library option to exportdb, #990
2023-02-19 22:51:25 -08:00
allcontributors[bot]
30c036a287 add swduncan as a contributor for ideas (#991)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-02-18 16:38:52 -08:00
Rhet Turnbull
715d874306 Updated CHANGELOG.md [skip ci] 2023-02-12 08:41:54 -08:00
Rhet Turnbull
e39424e906 Release files for 0.57.1 (#983) 2023-02-12 08:27:17 -08:00
Rhet Turnbull
b03670dc70 Implemented show command, #964 (#982) 2023-02-12 08:11:38 -08:00
Rhet Turnbull
ce297ced0a Added lock files to export to minimize name collisions (#981) 2023-02-11 18:21:15 -08:00
Rhet Turnbull
c4788c0fd2 Updated textx version 2023-02-11 09:34:56 -08:00
allcontributors[bot]
98e988cd6a add aa599 as a contributor for ideas (#980)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Rhet Turnbull <rturnbull@gmail.com>
2023-02-11 09:31:11 -08:00
allcontributors[bot]
b39f52cc35 add aa599 as a contributor for bug (#977)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-02-11 09:27:12 -08:00
Rhet Turnbull
d990670f72 Bug fix timezone none 976 (#978)
* Allow --uuid-from-file to read from stdin, #965

* Load query options before opening the database

* Fix for timezone in database has value None, #976
2023-02-11 09:26:57 -08:00
Rhet Turnbull
97a0a65d8a Feature UUID from stdin 965 (#979)
* Allow --uuid-from-file to read from stdin, #965

* Load query options before opening the database
2023-02-11 09:26:42 -08:00
Rhet Turnbull
f7ca3977a9 Updated CHANGELOG.md 2023-02-05 18:41:52 -08:00
Rhet Turnbull
fbb05d6795 Release 0.57.0 (#971) 2023-02-05 18:21:18 -08:00
Rhet Turnbull
0982d0c548 Fixes regression for #640 2023-02-05 18:00:04 -08:00
Rhet Turnbull
007f0e0960 Feature add query command (#970)
* Added query_command and example

* Refactored QUERY_OPTIONS, added query_command, refactored verbose, #930, #931

* Added query options to debug-dump, #966

* Refactored query, #602

* Added precedence test for --load-config

* Refactored handling of query options

* Refactored export_photo

* Removed extraneous print

* Updated API_README

* Updated examples
2023-02-05 14:48:42 -08:00
Rhet Turnbull
0f1866e39d Updated CHANGELOG.md [skip ci] 2023-01-28 19:09:57 -08:00
Rhet Turnbull
da6c3b8440 Updated CHANGELOG.md [skip ci] 2023-01-28 19:09:00 -08:00
167 changed files with 17975 additions and 4980 deletions

View File

@@ -262,7 +262,8 @@
"bug",
"ideas",
"test",
"code"
"code",
"doc"
]
},
{
@@ -481,7 +482,8 @@
"profile": "http://eecue.com/",
"contributions": [
"ideas",
"userTesting"
"userTesting",
"bug"
]
},
{
@@ -493,6 +495,81 @@
"bug",
"ideas"
]
},
{
"login": "aa599",
"name": "aa599",
"avatar_url": "https://avatars.githubusercontent.com/u/37746269?v=4",
"profile": "https://github.com/aa599",
"contributions": [
"bug",
"ideas"
]
},
{
"login": "swduncan",
"name": "Steve Duncan",
"avatar_url": "https://avatars.githubusercontent.com/u/2053195?v=4",
"profile": "https://github.com/swduncan",
"contributions": [
"ideas"
]
},
{
"login": "ianmmoir",
"name": "Ian Moir",
"avatar_url": "https://avatars.githubusercontent.com/u/15144745?v=4",
"profile": "http://www.projany.com",
"contributions": [
"bug"
]
},
{
"login": "pekingduck",
"name": "Peking Duck",
"avatar_url": "https://avatars.githubusercontent.com/u/2597142?v=4",
"profile": "https://github.com/pekingduck",
"contributions": [
"ideas",
"bug"
]
},
{
"login": "cclauss",
"name": "Christian Clauss",
"avatar_url": "https://avatars.githubusercontent.com/u/3709715?v=4",
"profile": "https://www.patreon.com/cclauss",
"contributions": [
"code"
]
},
{
"login": "dvdkon",
"name": "dvdkon",
"avatar_url": "https://avatars.githubusercontent.com/u/3526303?v=4",
"profile": "https://github.com/dvdkon",
"contributions": [
"code"
]
},
{
"login": "wernerzj",
"name": "wernerzj",
"avatar_url": "https://avatars.githubusercontent.com/u/130370930?v=4",
"profile": "https://github.com/wernerzj",
"contributions": [
"bug"
]
},
{
"login": "rajscode",
"name": "rajscode",
"avatar_url": "https://avatars.githubusercontent.com/u/99123253?v=4",
"profile": "https://github.com/rajscode",
"contributions": [
"bug",
"doc"
]
}
],
"contributorsPerLine": 7,

View File

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

View File

@@ -3,21 +3,30 @@ name: Tests
on: [push, pull_request]
jobs:
ruff: # https://beta.ruff.rs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: pip install --user ruff # Lint Python twice: 1. Whole repo, 2. Exclude tests/
- run: ruff --format=github --line-length=7228 --target-version=py39
--ignore=E402,E712,E721,E722,E741,F401,F403,F405,F541,F601,F811,F822,F841 .
- run: ruff --format=github --line-length=366 --target-version=py39
--exclude=tests/ --ignore=E402,E722,E741,F401,F403,F405,F541,F822,F841 .
build:
runs-on: ${{ matrix.os }}
if: "!contains(github.event.head_commit.message, '[skip ci]')"
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
strategy:
max-parallel: 4
matrix:
os: [macos-latest]
os: [macos-latest, ubuntu-latest]
python-version: ['3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

111
README.md
View File

@@ -7,10 +7,10 @@
[![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-51-orange.svg?style=flat)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-59-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.
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS and Linux. 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 align="center"><img src="docs/screencast/demo.gif?raw=true" width="713" height="430"/></p>
@@ -33,20 +33,24 @@ OSXPhotos provides the ability to interact with and query Apple's Photos.app lib
## Supported operating systems
Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) through macOS Monterey (12.0.1). Tested on both x86 and Apple silicon (M1).
Tested on Ubuntu Linux and macOS. Many features are only available on macOS.
On Linux, macOS-specific features of the CLI will not be available (these will not be shown in the help output).
The export and query CLI commands as well as the Python API will work on Linux which enables you to export photos
from a Photos library on a Linux machine.
Tested on macOS Sierra (10.12.6) through macOS Ventura (13.3). Tested on both x86 and Apple silicon (M1).
| macOS Version | macOS name | Photos.app version |
| ----------------- |------------|:-------------------|
| 13.0 | Ventura | 8.0 ✅ * |
| 12.0 - 12.6 | Monterey | 7.0 ✅ * |
| 13.0 - 13.3 | Ventura | 8.0 ✅ |
| 12.0 - 12.6 | Monterey | 7.0 ✅ |
| 10.16, 11.0-11.4 | Big Sur | 6.0 ✅ |
| 10.15.1 - 10.15.7 | Catalina | 5.0 ✅ |
| 10.14.5, 10.14.6 | Mojave | 4.0 ✅ |
| 10.13.6 | High Sierra| 3.0 ✅ |
| 10.12.6 | Sierra | 2.0 ✅ |
\* Some features may not be fully supported on Monterey and Ventura. Notably, `--use-photokit` and `--download-missing` may or may not work depending on your configuration; this is a known issue that will be fixed if I can find a solution. Many users successfully use OSXPhotos on Monterey without problem.
This package will read Photos databases for any supported version on any supported macOS version. E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine running macOS 10.12 and vice versa.
Requires python >= `3.9`.
@@ -128,21 +132,21 @@ Usage: osxphotos [OPTIONS] COMMAND [ARGS]...
osxphotos: the multi-tool for your Photos library
Options:
-v, --version Show the version and exit.
--db PHOTOS_LIBRARY_PATH Specify Photos database path. Path to Photos
library/database can be specified using either --db
or directly as PHOTOS_LIBRARY positional argument.
If neither --db or PHOTOS_LIBRARY provided, will
attempt to find the library to use in the following
order: 1. last opened library, 2. system library, 3.
~/Pictures/Photos Library.photoslibrary
--json Print output in JSON format.
-h, --help Show this message and exit.
-v, --version Show the version and exit.
--library, --db PHOTOS_LIBRARY_PATH
Specify path to Photos library. If not
provided, will attempt to find the library to
use in the following order: 1. last opened
library, 2. system library, 3.
~/Pictures/Photos Library.photoslibrary
--json Print output in JSON format.
-h, --help Show this message and exit.
Commands:
about Print information about osxphotos including license.
add-locations Add missing location data to photos in Photos.app using...
albums Print out albums found in the Photos library.
batch-edit Batch edit photo metadata such as title, description,...
diff Compare two Photos databases and print out differences
docs Open osxphotos documentation in your browser.
dump Print list of all photos & associated info from the Photos...
@@ -163,6 +167,7 @@ Commands:
query Query the Photos database using 1 or more search options;...
repl Run interactive osxphotos REPL shell (useful for...
run Run a python file using same environment as osxphotos.
show Show photo, album, or folder in Photos from UUID_OR_NAME
snap Create snapshot of Photos database to use with diff command
sync Sync metadata and albums between Photos libraries.
theme Manage osxphotos color themes.
@@ -685,10 +690,8 @@ Usage: osxphotos export [OPTIONS] [PHOTOS_LIBRARY]... DEST
this behavior.
Options:
--db PHOTOS_LIBRARY_PATH Specify Photos database path. Path to Photos
library/database can be specified using either
--db or directly as PHOTOS_LIBRARY positional
argument. If neither --db or PHOTOS_LIBRARY
--library, --db PHOTOS_LIBRARY_PATH
Specify path to Photos library. If not
provided, will attempt to find the library to
use in the following order: 1. last opened
library, 2. system library, 3.
@@ -719,8 +722,9 @@ Options:
--uuid UUID Search for photos with UUID(s). May be
repeated to include multiple UUIDs.
--uuid-from-file FILE Search for photos with UUID(s) loaded from
FILE. Format is a single UUID per line. Lines
preceded with # are ignored.
FILE. Format is a single UUID per line. Lines
preceded with # are ignored. If FILE is '-',
read UUIDs from stdin.
--title TITLE Search for TITLE in title of photo.
--no-title Search for photos with no title.
--description DESC Search for DESC in description of photo.
@@ -912,10 +916,10 @@ Options:
evaluated. See https://github.com/RhetTbull/os
xphotos/blob/master/examples/query_function.py
for example of how to use this option.
--deleted Include photos from the 'Recently Deleted'
folder.
--deleted-only Include only photos from the 'Recently
Deleted' folder.
--deleted Include photos from the 'Recently Deleted'
folder.
--update Only export new or updated files. See also
--force-update and notes below on export and
--update.
@@ -1440,22 +1444,23 @@ option to re-export the entire library thus rebuilding the
Extended Attributes
Some options (currently '--finder-tag-template', '--finder-tag-keywords',
'-xattr-template') write additional metadata accessible by Spotlight to
facilitate searching. For example, --finder-tag-keyword writes all keywords
(including any specified by '--keyword-template' or other options) to Finder
tags that are searchable in Spotlight using the syntax: 'tag:tagname'. For
example, if you have images with keyword "Travel" then using '--finder-tag-
keywords' you could quickly find those images in the Finder by typing
'tag:Travel' in the Spotlight search bar. Finder tags are written to the
'com.apple.metadata:_kMDItemUserTags' extended attribute. Unlike EXIF
metadata, extended attributes do not modify the actual file; the metadata is
written to extended attributes associated with the file and the Spotlight
metadata database. Most cloud storage services do not synch extended
attributes. Dropbox does sync them and any changes to a file's extended
attributes will cause Dropbox to re-sync the files.
Some options (currently '--finder-tag-template', '--finder-tag-keywords',
'-xattr-template') write additional metadata accessible by Spotlight
to facilitate searching. For example, --finder-tag-keyword writes all
keywords (including any specified by '--keyword-template' or other
options) to Finder tags that are searchable in Spotlight using the syntax:
'tag:tagname'. For example, if you have images with keyword "Travel"
then using '--finder-tag-keywords' you could quickly find those images
in the Finder by typing 'tag:Travel' in the Spotlight search bar.
Finder tags are written to the 'com.apple.metadata:_kMDItemUserTags'
extended attribute. Unlike EXIF metadata, extended attributes do not
modify the actual file; the metadata is written to extended attributes
associated with the file and the Spotlight metadata database. Most
cloud storage services do not synch extended attributes. Dropbox does
sync them and any changes to a file's extended attributes will cause
Dropbox to re-sync the files.
The following attributes may be used with '--xattr-template':
The following attributes may be used with '--xattr-template':
Attribute Description
@@ -1599,6 +1604,10 @@ Valid filters are:
['a', 'b', 'c', 'd'].
• prepend(x): Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c']
=> ['d', 'a', 'b', 'c'].
• appends(x): Append s[tring] Append x to each value of list of values, e.g.
appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
• prepends(x): Prepend s[tring] x to each value of list of values, e.g.
prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
• 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
@@ -2090,7 +2099,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.56.7'
{osxphotos_version} The osxphotos version, e.g. '0.60.2'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified
@@ -2377,6 +2386,8 @@ Valid filters are:
- `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'].
- `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
- `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
- `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().
@@ -2575,7 +2586,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.56.7'|
|{osxphotos_version}|The osxphotos version, e.g. '0.60.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|
@@ -2615,7 +2626,7 @@ I'll gladly consider pull requests for bug fixes or feature implementations.
If you have an interesting example that shows usage of this package, submit an issue or pull request and i'll include it or link to it.
Testing against "real world" Photos libraries would be especially helpful. If you discover issues in testing against your Photos libraries, please open an issue. I've done extensive testing against my own Photos library but that's a since data point and I'm certain there are issues lurking in various edge cases I haven't discovered yet.
Testing against "real world" Photos libraries would be especially helpful. If you discover issues in testing against your Photos libraries, please open an issue. I've done extensive testing against my own Photos library but that's a single data point and I'm certain there are issues lurking in various edge cases I haven't discovered yet.
### Contributors ✨
@@ -2659,7 +2670,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mkirkland4874"><img src="https://avatars.githubusercontent.com/u/36466711?v=4?s=75" width="75px;" alt="mkirkland4874"/><br /><sub><b>mkirkland4874</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Amkirkland4874" title="Bug reports">🐛</a> <a href="#example-mkirkland4874" title="Examples">💡</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jcommisso07"><img src="https://avatars.githubusercontent.com/u/3111054?v=4?s=75" width="75px;" alt="Joseph Commisso"/><br /><sub><b>Joseph Commisso</b></sub></a><br /><a href="#data-jcommisso07" title="Data">🔣</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dssinger"><img src="https://avatars.githubusercontent.com/u/1817903?v=4?s=75" width="75px;" alt="David Singer"/><br /><sub><b>David Singer</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Adssinger" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oPromessa"><img src="https://avatars.githubusercontent.com/u/21261491?v=4?s=75" width="75px;" alt="oPromessa"/><br /><sub><b>oPromessa</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3AoPromessa" title="Bug reports">🐛</a> <a href="#ideas-oPromessa" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Tests">⚠️</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oPromessa"><img src="https://avatars.githubusercontent.com/u/21261491?v=4?s=75" width="75px;" alt="oPromessa"/><br /><sub><b>oPromessa</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3AoPromessa" title="Bug reports">🐛</a> <a href="#ideas-oPromessa" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Tests">⚠️</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=oPromessa" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://spencerchang.me"><img src="https://avatars.githubusercontent.com/u/14796580?v=4?s=75" width="75px;" alt="Spencer Chang"/><br /><sub><b>Spencer Chang</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aspencerc99" title="Bug reports">🐛</a></td>
</tr>
<tr>
@@ -2690,8 +2701,18 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://danielbeadle.net"><img src="https://avatars.githubusercontent.com/u/6235378?v=4?s=75" width="75px;" alt="Daniel Beadle"/><br /><sub><b>Daniel Beadle</b></sub></a><br /><a href="#ideas-djbeadle" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://eecue.com/"><img src="https://avatars.githubusercontent.com/u/532536?v=4?s=75" width="75px;" alt="Dave Bullock"/><br /><sub><b>Dave Bullock</b></sub></a><br /><a href="#ideas-eecue" title="Ideas, Planning, & Feedback">🤔</a> <a href="#userTesting-eecue" title="User Testing">📓</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://eecue.com/"><img src="https://avatars.githubusercontent.com/u/532536?v=4?s=75" width="75px;" alt="Dave Bullock"/><br /><sub><b>Dave Bullock</b></sub></a><br /><a href="#ideas-eecue" title="Ideas, Planning, & Feedback">🤔</a> <a href="#userTesting-eecue" title="User Testing">📓</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aeecue" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pweaver"><img src="https://avatars.githubusercontent.com/u/611620?v=4?s=75" width="75px;" alt="Pweaver"/><br /><sub><b>Pweaver</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Apweaver" title="Bug reports">🐛</a> <a href="#ideas-pweaver" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aa599"><img src="https://avatars.githubusercontent.com/u/37746269?v=4?s=75" width="75px;" alt="aa599"/><br /><sub><b>aa599</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aaa599" title="Bug reports">🐛</a> <a href="#ideas-aa599" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/swduncan"><img src="https://avatars.githubusercontent.com/u/2053195?v=4?s=75" width="75px;" alt="Steve Duncan"/><br /><sub><b>Steve Duncan</b></sub></a><br /><a href="#ideas-swduncan" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.projany.com"><img src="https://avatars.githubusercontent.com/u/15144745?v=4?s=75" width="75px;" alt="Ian Moir"/><br /><sub><b>Ian Moir</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aianmmoir" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pekingduck"><img src="https://avatars.githubusercontent.com/u/2597142?v=4?s=75" width="75px;" alt="Peking Duck"/><br /><sub><b>Peking Duck</b></sub></a><br /><a href="#ideas-pekingduck" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Apekingduck" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.patreon.com/cclauss"><img src="https://avatars.githubusercontent.com/u/3709715?v=4?s=75" width="75px;" alt="Christian Clauss"/><br /><sub><b>Christian Clauss</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=cclauss" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dvdkon"><img src="https://avatars.githubusercontent.com/u/3526303?v=4?s=75" width="75px;" alt="dvdkon"/><br /><sub><b>dvdkon</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dvdkon" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wernerzj"><img src="https://avatars.githubusercontent.com/u/130370930?v=4?s=75" width="75px;" alt="wernerzj"/><br /><sub><b>wernerzj</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Awernerzj" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rajscode"><img src="https://avatars.githubusercontent.com/u/99123253?v=4?s=75" width="75px;" alt="rajscode"/><br /><sub><b>rajscode</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Arajscode" title="Bug reports">🐛</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=rajscode" title="Documentation">📖</a></td>
</tr>
</tbody>
</table>

View File

@@ -7,7 +7,7 @@ These are notes for developers working on osxphotos. They're mostly to help me r
- Clone the repo: `git clone git@github.com:RhetTbull/osxphotos.git`
- Create a virtual environment and activate it: `python3 -m venv venv` then `source venv/bin/activate`. I use [pyenv](https://github.com/pyenv/pyenv) with [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv) to manage my virtual environments
- Install the requirements: `pip install -r requirements.txt`
- Install the development requirements: `pip install -r requirements-dev.txt`
- Install the development requirements: `pip install -r dev_requirements.txt`
- Install osxphotos: `pip install -e .`
## Running tests

View File

@@ -2,7 +2,7 @@ build
bump2version==1.0.1
cogapp>=3.3.0,<4.0.0
furo
m2r2==0.3.3
m2r2==0.3.3.post2
pdbpp
pyinstaller==5.6.2
pytest-cov==4.0.0

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: e817dae2cad3fa76bd7234426f649ff1
config: c65b2fa3d733cf8df20a933bdfcfebb7
tags: 645f666f9bcd5a90fca523b33c5a78b7

3073
docs/API_README.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>Overview: module code - osxphotos 0.56.7 documentation</title>
<title>Overview: module code - osxphotos 0.60.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos._constants - osxphotos 0.56.4 documentation</title>
<title>osxphotos._constants - osxphotos 0.60.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.60.1 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.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -199,10 +199,14 @@
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="kn">import</span> <span class="nn">sqlite3</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">logger</span><span class="p">:</span> <span class="n">logging</span><span class="o">.</span><span class="n">Logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;osxphotos&quot;</span><span class="p">)</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">&quot;https://github.com/RhetTbull/osxphotos&quot;</span>
@@ -320,6 +324,8 @@
<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="s2">&quot;13&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;13&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;13&quot;</span><span class="p">,</span> <span class="s2">&quot;3&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>
@@ -377,6 +383,9 @@
<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">&quot;OSXPhotosXYZZY42_Sentinel$&quot;</span>
<span class="c1"># Lock file extension for reserving filenames when exporting</span>
<span class="n">_OSXPHOTOS_LOCK_EXTENSION</span> <span class="o">=</span> <span class="s2">&quot;.osxphotos.lock&quot;</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>
@@ -548,7 +557,7 @@
<span class="c1"># Max filename length on MacOS</span>
<span class="n">MAX_FILENAME_LEN</span> <span class="o">=</span> <span class="mi">255</span>
<span class="n">MAX_FILENAME_LEN</span> <span class="o">=</span> <span class="mi">255</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">_OSXPHOTOS_LOCK_EXTENSION</span><span class="p">)</span>
<span class="c1"># Max directory name length on MacOS</span>
<span class="n">MAX_DIRNAME_LEN</span> <span class="o">=</span> <span class="mi">255</span>
@@ -653,6 +662,19 @@
<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>
<span class="n">UUID_PATTERN</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">r</span><span class="s2">&quot;[0-9a-fA-F]</span><span class="si">{8}</span><span class="s2">-[0-9a-fA-F]</span><span class="si">{4}</span><span class="s2">-[0-9a-fA-F]</span><span class="si">{4}</span><span class="s2">-[0-9a-fA-F]</span><span class="si">{4}</span><span class="s2">-[0-9a-fA-F]</span><span class="si">{12}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="c1"># Reference: https://docs.python.org/3/library/sqlite3.html?highlight=sqlite3%20threadsafety#sqlite3.threadsafety</span>
<span class="c1"># and https://docs.python.org/3/library/sqlite3.html?highlight=sqlite3%20threadsafety#sqlite3.connect</span>
<span class="c1"># 3: serialized mode; Threads may share the module, connections and cursors</span>
<span class="c1"># 3 is the default in the python.org python 3.11 distribution</span>
<span class="c1"># earlier versions of python.org python 3.x default to 1 which means threads may not share</span>
<span class="c1"># sqlite3 connections and thus PhotoInfo.export() cannot be used in a multithreaded environment</span>
<span class="c1"># pass SQLITE_CHECK_SAME_THREAD to sqlite3.connect() to enable multithreaded access on systems that support it</span>
<span class="n">SQLITE_CHECK_SAME_THREAD</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">threadsafety</span> <span class="o">==</span> <span class="mi">3</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">SQLITE_CHECK_SAME_THREAD</span><span class="si">=}</span><span class="s2">, </span><span class="si">{</span><span class="n">sqlite3</span><span class="o">.</span><span class="n">threadsafety</span><span class="si">=}</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
</article>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.albuminfo - osxphotos 0.54.1 documentation</title>
<title>osxphotos.albuminfo - osxphotos 0.59.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.59.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.54.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.59.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -215,6 +215,7 @@
<span class="n">_PHOTOS_4_VERSION</span><span class="p">,</span>
<span class="n">_PHOTOS_5_ALBUM_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_5_FOLDER_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_5_VERSION</span><span class="p">,</span>
<span class="n">TIME_DELTA</span><span class="p">,</span>
<span class="n">AlbumSortOrder</span><span class="p">,</span>
<span class="p">)</span>
@@ -246,9 +247,8 @@
<span class="sd"> ValueError: raised if len(values) != len(sort_keys)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">sort_keys</span><span class="p">):</span>
<span class="k">return</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;values and sort_keys must have same length&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="nb">sorted</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">sort_keys</span><span class="p">,</span> <span class="n">values</span><span class="p">))))[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;values and sort_keys must be same length&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">sort_keys</span><span class="p">,</span> <span class="n">values</span><span class="p">))]</span>
<span class="k">class</span> <span class="nc">AlbumInfoBaseClass</span><span class="p">:</span>
@@ -258,7 +258,7 @@
<span class="sd"> including folders, photos, etc.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db</span><span class="p">,</span> <span class="n">uuid</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span> <span class="o">=</span> <span class="n">uuid</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span> <span class="o">=</span> <span class="n">db</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_title</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">_dbalbum_details</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;title&quot;</span><span class="p">]</span>
@@ -318,7 +318,8 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">end_date</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;For Albums, return end date (most recent image) of album or None for albums with no images</span>
<span class="sd"> For Import Sessions, return end date of import sessions (when import was completed)&quot;&quot;&quot;</span>
<span class="sd"> For Import Sessions, return end date of import sessions (when import was completed)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_end_date</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
@@ -360,6 +361,16 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_owner</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_owner</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return album info as a dict; does not include photos&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;creation_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">creation_date</span><span class="p">,</span>
<span class="s2">&quot;start_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_date</span><span class="p">,</span>
<span class="s2">&quot;end_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_date</span><span class="p">,</span>
<span class="s2">&quot;owner&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="p">,</span>
<span class="p">}</span>
<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;return number of photos contained in album&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">photos</span><span class="p">)</span>
@@ -371,6 +382,10 @@
<span class="sd"> including folders, photos, etc.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db</span><span class="p">,</span> <span class="n">uuid</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="n">db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">uuid</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_title</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">_dbalbum_details</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;title&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return title / name of album&quot;&quot;&quot;</span>
@@ -402,10 +417,11 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">folder_names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return hierarchical list of folders the album is contained in</span>
<span class="sd">&quot;&quot;&quot;Return hierarchical list of folders the album is contained in</span>
<span class="sd"> the folder list is in form:</span>
<span class="sd"> [&quot;Top level folder&quot;, &quot;sub folder 1&quot;, &quot;sub folder 2&quot;, ...]</span>
<span class="sd"> returns empty list if album is not in any folders&quot;&quot;&quot;</span>
<span class="sd"> or empty list if album is not in any folders</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_folder_names</span>
@@ -415,10 +431,9 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">folder_list</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return hierarchical list of folders the album is contained in</span>
<span class="sd"> as list of FolderInfo objects in form</span>
<span class="sd"> [&quot;Top level folder&quot;, &quot;sub folder 1&quot;, &quot;sub folder 2&quot;, ...]</span>
<span class="sd"> returns empty list if album is not in any folders&quot;&quot;&quot;</span>
<span class="sd">&quot;&quot;&quot;Returns list of FolderInfo objects for each folder the album is contained in</span>
<span class="sd"> or empty list if album is not in any folders</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_folders</span>
@@ -443,7 +458,7 @@
<span class="n">parent_pk</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">_dbalbum_details</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">][</span><span class="s2">&quot;parentfolder&quot;</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_parent</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">FolderInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</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">_dbalbums_pk</span><span class="p">[</span><span class="n">parent_pk</span><span class="p">])</span>
<span class="k">if</span> <span class="n">parent_pk</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">_folder_root_pk</span>
<span class="k">if</span> <span class="n">parent_pk</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">parent_pk</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">_folder_root_pk</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_parent</span>
@@ -476,29 +491,81 @@
<span class="k">return</span> <span class="n">index</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Photo with uuid </span><span class="si">{</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2"> does not appear to be in this album&quot;</span>
<span class="p">)</span></div></div>
<span class="p">)</span></div>
<div class="viewcode-block" id="AlbumInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.AlbumInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return album info as a dict; does not include photos&quot;&quot;&quot;</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;title&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;folder_names&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">folder_names</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;folder_list&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">f</span><span class="o">.</span><span class="n">uuid</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">folder_list</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;sort_order&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">sort_order</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;parent&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">uuid</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">parent</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">dict_data</span></div></div>
<div class="viewcode-block" id="ImportInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.ImportInfo">[docs]</a><span class="k">class</span> <span class="nc">ImportInfo</span><span class="p">(</span><span class="n">AlbumInfoBaseClass</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Information about import sessions&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db</span><span class="p">,</span> <span class="n">uuid</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span> <span class="o">=</span> <span class="n">uuid</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span> <span class="o">=</span> <span class="n">db</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&gt;=</span> <span class="n">_PHOTOS_5_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="n">db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">uuid</span><span class="p">)</span>
<span class="n">import_session</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">_db_import_group</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">]</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_creation_date_timestamp</span> <span class="o">=</span> <span class="n">import_session</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">,</span> <span class="ne">KeyError</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_creation_date_timestamp</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="bp">self</span><span class="o">.</span><span class="n">_start_date_timestamp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_creation_date_timestamp</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_end_date_timestamp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_creation_date_timestamp</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_title</span> <span class="o">=</span> <span class="n">import_session</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_local_tz</span> <span class="o">=</span> <span class="n">get_local_tz</span><span class="p">(</span>
<span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_creation_date_timestamp</span> <span class="o">+</span> <span class="n">TIME_DELTA</span><span class="p">)</span>
<span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return title / name of import session&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_title</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return list of photos contained in import session&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photos</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">uuid_list</span><span class="p">,</span> <span class="n">sort_order</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span>
<span class="o">*</span><span class="p">[</span>
<span class="p">(</span><span class="n">uuid</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;fok_import_session&quot;</span><span class="p">])</span>
<span class="k">for</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&gt;=</span> <span class="n">_PHOTOS_5_VERSION</span><span class="p">:</span>
<span class="n">uuid_list</span><span class="p">,</span> <span class="n">sort_order</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span>
<span class="o">*</span><span class="p">[</span>
<span class="p">(</span><span class="n">uuid</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;fok_import_session&quot;</span><span class="p">])</span>
<span class="k">for</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span>
<span class="p">]</span>
<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">else</span><span class="p">:</span>
<span class="n">import_photo_uuids</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">u</span>
<span class="k">for</span> <span class="n">u</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span>
<span class="p">]</span>
<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="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">import_photo_uuids</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 class="viewcode-block" id="ImportInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.ImportInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return import info as a dict; does not include photos&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;creation_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">creation_date</span><span class="p">,</span>
<span class="s2">&quot;start_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_date</span><span class="p">,</span>
<span class="s2">&quot;end_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_date</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="p">}</span></div>
<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>
@@ -506,13 +573,32 @@
<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>
<span class="sd"> ProjectInfo with info about projects</span>
<span class="sd"> Projects are cards, calendars, slideshows, etc.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="o">...</span></div>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">folder_names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return hierarchical list of folders the album is contained in</span>
<span class="sd"> the folder list is in form:</span>
<span class="sd"> [&quot;Top level folder&quot;, &quot;sub folder 1&quot;, &quot;sub folder 2&quot;, ...]</span>
<span class="sd"> or empty list if album is not in any folders</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># projects are not in folders</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">folder_list</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns list of FolderInfo objects for each folder the album is contained in</span>
<span class="sd"> or empty list if album is not in any folders</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># projects are not in folders</span>
<span class="k">return</span> <span class="p">[]</span></div>
<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>
@@ -583,7 +669,7 @@
<span class="n">parent_pk</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">_dbalbum_details</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">][</span><span class="s2">&quot;parentfolder&quot;</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_parent</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">FolderInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</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">_dbalbums_pk</span><span class="p">[</span><span class="n">parent_pk</span><span class="p">])</span>
<span class="k">if</span> <span class="n">parent_pk</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">_folder_root_pk</span>
<span class="k">if</span> <span class="n">parent_pk</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">parent_pk</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">_folder_root_pk</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_parent</span>
@@ -613,6 +699,16 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_folders</span> <span class="o">=</span> <span class="n">folders</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_folders</span>
<div class="viewcode-block" id="FolderInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.FolderInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return folder info as a dict&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;parent&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">uuid</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">parent</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">&quot;subfolders&quot;</span><span class="p">:</span> <span class="p">[</span><span class="n">f</span><span class="o">.</span><span class="n">uuid</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">subfolders</span><span class="p">],</span>
<span class="s2">&quot;albums&quot;</span><span class="p">:</span> <span class="p">[</span><span class="n">a</span><span class="o">.</span><span class="n">uuid</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">],</span>
<span class="p">}</span></div>
<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></div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.debug - osxphotos 0.56.7 documentation</title>
<title>osxphotos.debug - osxphotos 0.58.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.exiftool - osxphotos 0.56.4 documentation</title>
<title>osxphotos.exiftool - osxphotos 0.59.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.59.1 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.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.59.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.fileutil - osxphotos 0.54.2 documentation</title>
<title>osxphotos.fileutil - osxphotos 0.60.0 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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.2 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.60.0 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.54.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.0 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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -206,9 +206,11 @@
<span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="kn">from</span> <span class="nn">tempfile</span> <span class="kn">import</span> <span class="n">TemporaryDirectory</span>
<span class="kn">import</span> <span class="nn">Foundation</span>
<span class="kn">from</span> <span class="nn">.imageconverter</span> <span class="kn">import</span> <span class="n">ImageConverter</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">is_macos</span><span class="p">,</span> <span class="n">normalize_fs_path</span>
<span class="k">if</span> <span class="n">is_macos</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">Foundation</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>
@@ -287,6 +289,9 @@
<span class="k">if</span> <span class="n">src</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">dest</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;src and dest must not be None&quot;</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">src</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</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">isfile</span><span class="p">(</span><span class="n">src</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">FileNotFoundError</span><span class="p">(</span><span class="s2">&quot;src file does not appear to exist&quot;</span><span class="p">,</span> <span class="n">src</span><span class="p">)</span>
@@ -312,6 +317,9 @@
<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="n">src</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dest</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">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>
@@ -332,6 +340,7 @@
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">unlink</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">filepath</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;unlink filepath; if it&#39;s pathlib.Path, use Path.unlink, otherwise use os.unlink&quot;&quot;&quot;</span>
<span class="n">filepath</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">filepath</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">filepath</span><span class="o">.</span><span class="n">unlink</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
@@ -340,6 +349,7 @@
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">rmdir</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">dirpath</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;remove directory filepath; dirpath must be empty&quot;&quot;&quot;</span>
<span class="n">dirpath</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dirpath</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">dirpath</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">dirpath</span><span class="o">.</span><span class="n">rmdir</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
@@ -348,6 +358,7 @@
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">utime</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">times</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Set the access and modified time of path.&quot;&quot;&quot;</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">utime</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">times</span><span class="o">=</span><span class="n">times</span><span class="p">)</span>
<span class="nd">@classmethod</span>
@@ -363,6 +374,9 @@
<span class="sd"> Does not do a byte-by-byte comparison.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">f1</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">f1</span><span class="p">)</span>
<span class="n">f2</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">f2</span><span class="p">)</span>
<span class="n">s1</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_sig</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">f1</span><span class="p">))</span>
<span class="k">if</span> <span class="n">mtime1</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">s1</span> <span class="o">=</span> <span class="p">(</span><span class="n">s1</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">s1</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="nb">int</span><span class="p">(</span><span class="n">mtime1</span><span class="p">))</span>
@@ -385,6 +399,7 @@
<span class="k">if</span> <span class="ow">not</span> <span class="n">s2</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="n">f1</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">f1</span><span class="p">)</span>
<span class="n">s1</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_sig</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">f1</span><span class="p">))</span>
<span class="k">if</span> <span class="n">s1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="n">stat</span><span class="o">.</span><span class="n">S_IFREG</span> <span class="ow">or</span> <span class="n">s2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="n">stat</span><span class="o">.</span><span class="n">S_IFREG</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
@@ -393,6 +408,7 @@
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">file_sig</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">f1</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return os.stat signature for file f1 as tuple of (mode, size, mtime)&quot;&quot;&quot;</span>
<span class="n">f1</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">f1</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_sig</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">f1</span><span class="p">))</span>
<span class="nd">@classmethod</span>
@@ -407,6 +423,8 @@
<span class="sd"> Returns:</span>
<span class="sd"> True if success, otherwise False</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">src_file</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">src_file</span><span class="p">)</span>
<span class="n">dest_file</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dest_file</span><span class="p">)</span>
<span class="n">converter</span> <span class="o">=</span> <span class="n">ImageConverter</span><span class="p">()</span>
<span class="k">return</span> <span class="n">converter</span><span class="o">.</span><span class="n">write_jpeg</span><span class="p">(</span>
<span class="n">src_file</span><span class="p">,</span> <span class="n">dest_file</span><span class="p">,</span> <span class="n">compression_quality</span><span class="o">=</span><span class="n">compression_quality</span>
@@ -424,6 +442,8 @@
<span class="sd"> Name of renamed file (dest)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">src</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">rename</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">return</span> <span class="n">dest</span>
@@ -468,6 +488,9 @@
<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="n">src</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dest</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">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>
@@ -485,7 +508,7 @@
<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>
<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">FileUtilShUtil</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Various file utilities&quot;&quot;&quot;</span>
<span class="k">pass</span></div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.personinfo - osxphotos 0.56.5 documentation</title>
<title>osxphotos.personinfo - osxphotos 0.58.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 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.56.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photoexporter - osxphotos 0.56.4 documentation</title>
<title>osxphotos.photoexporter - osxphotos 0.60.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.60.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.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -198,6 +198,8 @@
<span></span><span class="sd">&quot;&quot;&quot; PhotoExport class to export photos</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">logging</span>
@@ -205,12 +207,11 @@
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">typing</span> <span class="k">as</span> <span class="nn">t</span>
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span> <span class="c1"># pylint: disable=syntax-error</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">asdict</span><span class="p">,</span> <span class="n">dataclass</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="kn">from</span> <span class="nn">types</span> <span class="kn">import</span> <span class="n">SimpleNamespace</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">mako.template</span> <span class="kn">import</span> <span class="n">Template</span>
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="p">(</span>
@@ -228,16 +229,9 @@
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">._version</span> <span class="kn">import</span> <span class="n">__version__</span>
<span class="kn">from</span> <span class="nn">.datetime_utils</span> <span class="kn">import</span> <span class="n">datetime_tz_to_utc</span>
<span class="kn">from</span> <span class="nn">.exiftool</span> <span class="kn">import</span> <span class="n">ExifTool</span><span class="p">,</span> <span class="n">exiftool_can_write</span>
<span class="kn">from</span> <span class="nn">.exiftool</span> <span class="kn">import</span> <span class="n">ExifTool</span><span class="p">,</span> <span class="n">ExifToolCaching</span><span class="p">,</span> <span class="n">exiftool_can_write</span><span class="p">,</span> <span class="n">get_exiftool_path</span>
<span class="kn">from</span> <span class="nn">.export_db</span> <span class="kn">import</span> <span class="n">ExportDB</span><span class="p">,</span> <span class="n">ExportDBTemp</span>
<span class="kn">from</span> <span class="nn">.fileutil</span> <span class="kn">import</span> <span class="n">FileUtil</span>
<span class="kn">from</span> <span class="nn">.photokit</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">PHOTOS_VERSION_CURRENT</span><span class="p">,</span>
<span class="n">PHOTOS_VERSION_ORIGINAL</span><span class="p">,</span>
<span class="n">PHOTOS_VERSION_UNADJUSTED</span><span class="p">,</span>
<span class="n">PhotoKitFetchFailed</span><span class="p">,</span>
<span class="n">PhotoLibrary</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">.phototemplate</span> <span class="kn">import</span> <span class="n">RenderOptions</span>
<span class="kn">from</span> <span class="nn">.rich_utils</span> <span class="kn">import</span> <span class="n">add_rich_markup_tag</span>
<span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span>
@@ -245,10 +239,26 @@
<span class="n">hexdigest</span><span class="p">,</span>
<span class="n">increment_filename</span><span class="p">,</span>
<span class="n">increment_filename_with_count</span><span class="p">,</span>
<span class="n">is_macos</span><span class="p">,</span>
<span class="n">lineno</span><span class="p">,</span>
<span class="n">list_directory</span><span class="p">,</span>
<span class="n">lock_filename</span><span class="p">,</span>
<span class="n">normalize_fs_path</span><span class="p">,</span>
<span class="n">unlock_filename</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">is_macos</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">.photokit</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">PHOTOS_VERSION_CURRENT</span><span class="p">,</span>
<span class="n">PHOTOS_VERSION_ORIGINAL</span><span class="p">,</span>
<span class="n">PHOTOS_VERSION_UNADJUSTED</span><span class="p">,</span>
<span class="n">PhotoKitFetchFailed</span><span class="p">,</span>
<span class="n">PhotoLibrary</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;ExportError&quot;</span><span class="p">,</span>
<span class="s2">&quot;ExportOptions&quot;</span><span class="p">,</span>
@@ -263,6 +273,11 @@
<span class="c1"># retry if download_missing/use_photos_export fails the first time (which sometimes it does)</span>
<span class="n">MAX_PHOTOSCRIPT_RETRIES</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1"># Global to hold the compiled XMP template</span>
<span class="c1"># This is expensive to compile so we only want to do it once</span>
<span class="n">_global_xmp_template</span><span class="p">:</span> <span class="n">Template</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># return values for _should_update_photo</span>
<span class="k">class</span> <span class="nc">ShouldUpdate</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="n">NOT_IN_DATABASE</span> <span class="o">=</span> <span class="mi">1</span>
@@ -430,6 +445,9 @@
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">())</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;StagedFiles(</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span><span class="si">}</span><span class="s2">)&quot;</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original</span><span class="p">,</span>
@@ -504,7 +522,6 @@
<span class="n">xattr_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">):</span>
<span class="n">local_vars</span> <span class="o">=</span> <span class="nb">locals</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">:</span>
@@ -569,7 +586,7 @@
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="n">tmpdir</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="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">_render_options</span> <span class="o">=</span> <span class="n">RenderOptions</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_verbose</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">_verbose</span>
<span class="c1"># define functions for adding markup</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">&quot;filepath&quot;</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
@@ -666,6 +683,11 @@
<span class="n">dest</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_should_convert_to_jpeg</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
<span class="c1"># stage files for export by finding path in local library or downloading from iCloud as appropriate</span>
<span class="c1"># for `--download-missing` and `--update` case, this may cause unnecessary downloads</span>
<span class="c1"># as it will download the file even if it&#39;s not needed (won&#39;t be checked until the _should_update_photo() call from _export_photo()</span>
<span class="c1"># fixing this will require major refactoring of the export code, see #1086</span>
<span class="c1"># leaving it for now as this should not be a common use case</span>
<span class="c1"># (if using `--update` it is much better to be using &quot;Download originals to this Mac&quot; in Photos)</span>
<span class="n">staged_files</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photos_for_export</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
<span class="n">src</span> <span class="o">=</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">edited</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">original</span>
@@ -687,6 +709,7 @@
<span class="sa">f</span><span class="s2">&quot;Skipping missing </span><span class="si">{</span><span class="s1">&#39;edited&#39;</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s1">&#39;original&#39;</span><span class="si">}</span><span class="s2"> photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</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">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</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">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">)&quot;</span>
<span class="p">)</span>
<span class="n">all_results</span><span class="o">.</span><span class="n">missing</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="n">unlock_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="c1"># copy live photo associated .mov if requested</span>
<span class="k">if</span> <span class="n">export_original</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
@@ -762,7 +785,9 @@
<span class="n">preview_name</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">preview_name</span>
<span class="k">if</span> <span class="nb">any</span><span class="p">([</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">])</span>
<span class="k">else</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">preview_name</span><span class="p">))</span>
<span class="k">else</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
<span class="n">increment_filename</span><span class="p">(</span><span class="n">preview_name</span><span class="p">,</span> <span class="n">lock</span><span class="o">=</span><span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span><span class="p">)</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo</span><span class="p">(</span>
<span class="n">preview_path</span><span class="p">,</span>
@@ -822,11 +847,10 @@
<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">path_edited</span><span class="p">:</span>
<span class="n">ext</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="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_edited</span><span class="p">)</span><span class="o">.</span><span class="n">suffix</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">uti</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</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">uti_edited</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti</span>
<span class="n">uti</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti</span>
<span class="n">ext</span> <span class="o">=</span> <span class="n">get_preferred_uti_extension</span><span class="p">(</span><span class="n">uti</span><span class="p">)</span>
<span class="n">ext</span> <span class="o">=</span> <span class="s2">&quot;.&quot;</span> <span class="o">+</span> <span class="n">ext</span>
<span class="n">edited_filename</span> <span class="o">=</span> <span class="n">original_filename</span><span class="o">.</span><span class="n">stem</span> <span class="o">+</span> <span class="s2">&quot;_edited&quot;</span> <span class="o">+</span> <span class="n">ext</span>
<span class="k">return</span> <span class="n">edited_filename</span>
<span class="n">ext</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;.</span><span class="si">{</span><span class="n">ext</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">original_filename</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_edited</span><span class="si">{</span><span class="n">ext</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">def</span> <span class="nf">_get_dest_path</span><span class="p">(</span>
<span class="bp">self</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">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
@@ -841,16 +865,32 @@
<span class="sd"> new dest path (pathlib.Path)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># lock files are used to minimize chance of name collision when in parallel mode</span>
<span class="c1"># don&#39;t create lock files if in dry_run mode</span>
<span class="n">lock</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span>
<span class="k">def</span> <span class="nf">_lock_filename</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Lock filename if not in dry_run mode&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">lock_filename</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">if</span> <span class="n">lock</span> <span class="k">else</span> <span class="n">filename</span>
<span class="c1"># if overwrite==False and #increment==False, export should fail if file exists</span>
<span class="k">if</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span>
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">increment</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span>
<span class="ow">not</span> <span class="nb">any</span><span class="p">(</span>
<span class="p">[</span>
<span class="n">options</span><span class="o">.</span><span class="n">increment</span><span class="p">,</span>
<span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span>
<span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">,</span>
<span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
<span class="ow">and</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span>
<span class="p">):</span>
<span class="k">raise</span> <span class="ne">FileExistsError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;destination exists (</span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">); overwrite=</span><span class="si">{</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="si">}</span><span class="s2">, increment=</span><span class="si">{</span><span class="n">options</span><span class="o">.</span><span class="n">increment</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="c1"># if overwrite, we don&#39;t care if the file exists or not</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">:</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span> <span class="ow">and</span> <span class="n">_lock_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">):</span>
<span class="k">return</span> <span class="n">dest</span>
<span class="c1"># if not update or overwrite, check to see if file exists and if so, add (1), (2), etc</span>
@@ -862,19 +902,21 @@
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">increment</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span>
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">]</span>
<span class="p">):</span>
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">))</span>
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">lock</span><span class="o">=</span><span class="n">lock</span><span class="p">))</span>
<span class="c1"># if update and file exists, need to check to see if it&#39;s the right file by checking export db</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">:</span>
<span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span>
<span class="n">dest_uuid</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="n">_lock_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">):</span>
<span class="c1"># destination doesn&#39;t exist in export db and doesn&#39;t exist on disk</span>
<span class="c1"># so we can just use it</span>
<span class="k">return</span> <span class="n">dest</span>
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">:</span>
<span class="c1"># destination is the right file</span>
<span class="c1"># will use it even if locked so don&#39;t check return value of _lock_filename</span>
<span class="n">_lock_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="k">return</span> <span class="n">dest</span>
<span class="c1"># either dest_uuid is wrong or file exists and there&#39;s no associated UUID, so find a name that matches</span>
@@ -883,24 +925,35 @@
<span class="c1"># first, find all matching files in export db and see if there&#39;s a match</span>
<span class="k">if</span> <span class="n">dest_target</span> <span class="o">:=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_target_for_file</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">uuid</span><span class="p">,</span> <span class="n">dest</span><span class="p">):</span>
<span class="c1"># there&#39;s a match so use that</span>
<span class="n">_lock_filename</span><span class="p">(</span><span class="n">dest_target</span><span class="p">)</span>
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest_target</span><span class="p">)</span>
<span class="c1"># no match so need to create a new name</span>
<span class="c1"># increment the destination file until we find one that doesn&#39;t exist and doesn&#39;t match another uuid in the database</span>
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span>
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">lock</span><span class="o">=</span><span class="n">lock</span><span class="p">)</span>
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">while</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span>
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">lock</span><span class="o">=</span><span class="n">lock</span><span class="p">)</span>
<span class="k">return</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="c1"># fail safe...I can&#39;t think of a case that gets here</span>
<span class="n">_lock_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="k">return</span> <span class="n">dest</span>
<span class="k">def</span> <span class="nf">_should_update_photo</span><span class="p">(</span>
<span class="bp">self</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">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">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">t</span><span class="o">.</span><span class="n">Literal</span><span class="p">[</span><span class="kc">True</span><span class="p">,</span> <span class="kc">False</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Return True if photo should be updated, else False&quot;&quot;&quot;</span>
<span class="bp">self</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="o">|</span> <span class="kc">None</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">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span> <span class="o">|</span> <span class="n">ShouldUpdate</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return True if photo should be updated, else False</span>
<span class="sd"> Args:</span>
<span class="sd"> src (pathlib.Path | None): source path; if None, photo is missing and</span>
<span class="sd"> any checks that require src will return True</span>
<span class="sd"> dest (pathlib.Path): destination path</span>
<span class="sd"> Returns:</span>
<span class="sd"> False if photo should not be updated otherwise a truthy ShouldUpdate value</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># NOTE: The order of certain checks is important</span>
<span class="c1"># read the comments below to understand why before changing</span>
@@ -914,11 +967,11 @@
<span class="c1"># photo doesn&#39;t exist in database, should update</span>
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">NOT_IN_DATABASE</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">samefile</span><span class="p">(</span><span class="n">src</span><span class="p">):</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="p">(</span><span class="ow">not</span> <span class="n">src</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">samefile</span><span class="p">(</span><span class="n">src</span><span class="p">)):</span>
<span class="c1"># different files, should update</span>
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">HARDLINK_DIFFERENT_FILES</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="n">dest</span><span class="o">.</span><span class="n">samefile</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="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="p">(</span><span class="ow">not</span> <span class="n">src</span> <span class="ow">or</span> <span class="n">dest</span><span class="o">.</span><span class="n">samefile</span><span class="p">(</span><span class="n">src</span><span class="p">)):</span>
<span class="c1"># same file but not exporting as hardlink, should update</span>
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">NOT_HARDLINK_SAME_FILES</span>
@@ -950,7 +1003,9 @@
<span class="c1"># as exiftool will be used to update edited file</span>
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EXIFTOOL_DIFFERENT</span> <span class="k">if</span> <span class="n">rv</span> <span class="k">else</span> <span class="kc">False</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">cmp_file_sig</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">file_record</span><span class="o">.</span><span class="n">src_sig</span><span class="p">):</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="p">(</span>
<span class="ow">not</span> <span class="n">src</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">cmp_file_sig</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">file_record</span><span class="o">.</span><span class="n">src_sig</span><span class="p">)</span>
<span class="p">):</span>
<span class="c1"># edited file in Photos doesn&#39;t match what was last exported</span>
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EDITED_SIG_DIFFERENT</span>
@@ -1001,22 +1056,46 @@
<span class="c1"># download any missing files</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">download_missing</span><span class="p">:</span>
<span class="n">live_photo</span> <span class="o">=</span> <span class="n">staged</span><span class="o">.</span><span class="n">edited_live</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="n">staged</span><span class="o">.</span><span class="n">original_live</span>
<span class="n">missing_options</span> <span class="o">=</span> <span class="n">ExportOptions</span><span class="p">(</span>
<span class="n">edited</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">edited</span><span class="p">,</span>
<span class="n">preview</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">preview</span><span class="p">,</span>
<span class="n">raw_photo</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">raw</span><span class="p">,</span>
<span class="n">live_photo</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">live_photo</span><span class="p">,</span>
<span class="n">staged</span> <span class="o">|=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_missing_photos_for_export</span><span class="p">(</span>
<span class="n">staged</span><span class="o">=</span><span class="n">staged</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_photokit</span><span class="p">:</span>
<span class="n">missing_staged</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_photokit</span><span class="p">(</span>
<span class="n">options</span><span class="o">=</span><span class="n">missing_options</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">missing_staged</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_applescript</span><span class="p">(</span>
<span class="n">options</span><span class="o">=</span><span class="n">missing_options</span>
<span class="p">)</span>
<span class="n">staged</span> <span class="o">|=</span> <span class="n">missing_staged</span>
<span class="k">return</span> <span class="n">staged</span>
<span class="k">def</span> <span class="nf">_stage_missing_photos_for_export</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span> <span class="n">staged</span><span class="p">:</span> <span class="n">StagedFiles</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">StagedFiles</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Download and stage any missing files for export&quot;&quot;&quot;</span>
<span class="c1"># if live photo and requesting edited version need the edited live photo</span>
<span class="n">live_photo</span> <span class="o">=</span> <span class="n">staged</span><span class="o">.</span><span class="n">edited_live</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="n">staged</span><span class="o">.</span><span class="n">original_live</span>
<span class="c1"># is there actually a missing file? (#1086)</span>
<span class="n">something_to_download</span> <span class="o">=</span> <span class="p">(</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">hasadjustments</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">edited</span><span class="p">)</span>
<span class="ow">or</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">live_photo</span><span class="p">)</span>
<span class="ow">or</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">raw</span><span class="p">)</span>
<span class="ow">or</span> <span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">preview</span><span class="p">)</span>
<span class="ow">or</span> <span class="p">(</span><span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">original</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">something_to_download</span><span class="p">:</span>
<span class="k">return</span> <span class="n">staged</span>
<span class="n">missing_options</span> <span class="o">=</span> <span class="n">ExportOptions</span><span class="p">(</span>
<span class="n">edited</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">edited</span><span class="p">,</span>
<span class="n">preview</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">preview</span><span class="p">,</span>
<span class="n">raw_photo</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">raw</span><span class="p">,</span>
<span class="n">live_photo</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">live_photo</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_photokit</span><span class="p">:</span>
<span class="n">missing_staged</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_photokit</span><span class="p">(</span>
<span class="n">options</span><span class="o">=</span><span class="n">missing_options</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">missing_staged</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_applescript</span><span class="p">(</span>
<span class="n">options</span><span class="o">=</span><span class="n">missing_options</span>
<span class="p">)</span>
<span class="n">staged</span> <span class="o">|=</span> <span class="n">missing_staged</span>
<span class="k">return</span> <span class="n">staged</span>
<span class="k">def</span> <span class="nf">_stage_photo_for_export_with_photokit</span><span class="p">(</span>
@@ -1131,7 +1210,8 @@
<span class="sd">&quot;&quot;&quot;Stage a photo for export with AppleScript to a temporary directory</span>
<span class="sd"> Note: If exporting an edited live photo, the associated live video will not be exported.</span>
<span class="sd"> This is a limitation of the Photos AppleScript interface and Photos behaves the same way.&quot;&quot;&quot;</span>
<span class="sd"> This is a limitation of the Photos AppleScript interface and Photos behaves the same way.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Edited version requested but photo has no adjustments&quot;</span><span class="p">)</span>
@@ -1360,7 +1440,7 @@
<span class="k">try</span><span class="p">:</span>
<span class="n">fileutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dest_str</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Exported </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</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">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="sa">f</span><span class="s2">&quot;Exported </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</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">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="n">normalize_fs_path</span><span class="p">(</span><span class="n">dest_str</span><span class="p">))</span><span class="si">}</span><span class="s2">&quot;</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="n">ExportError</span><span class="p">(</span>
@@ -1391,7 +1471,7 @@
<span class="c1"># set data in the database</span>
<span class="k">with</span> <span class="n">export_db</span><span class="o">.</span><span class="n">create_or_get_file_record</span><span class="p">(</span><span class="n">dest_str</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">uuid</span><span class="p">)</span> <span class="k">as</span> <span class="n">rec</span><span class="p">:</span>
<span class="n">rec</span><span class="o">.</span><span class="n">photoinfo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="n">rec</span><span class="o">.</span><span class="n">photoinfo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">json</span><span class="p">(</span><span class="n">shallow</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">rec</span><span class="o">.</span><span class="n">export_options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">bit_flags</span>
<span class="c1"># don&#39;t set src_sig as that is set above before any modifications by convert_to_jpeg or exiftool</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_signature</span><span class="p">:</span>
@@ -1413,6 +1493,9 @@
<span class="s2">&quot;exiftool_warning&quot;</span><span class="p">:</span> <span class="n">exif_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># clean up lock file</span>
<span class="n">unlock_filename</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span>
<span class="k">return</span> <span class="n">results</span>
<span class="k">def</span> <span class="nf">_export_photo_uuid_applescript</span><span class="p">(</span>
@@ -1697,10 +1780,10 @@
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span><span class="p">:</span>
<span class="n">warning_</span><span class="p">,</span> <span class="n">error_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_write_exif_data</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
<span class="k">if</span> <span class="n">warning_</span><span class="p">:</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">dest</span><span class="p">,</span> <span class="n">warning_</span><span class="p">))</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="o">.</span><span class="n">append</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="nb">str</span><span class="p">(</span><span class="n">warning_</span><span class="p">)))</span>
<span class="k">if</span> <span class="n">error_</span><span class="p">:</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">dest</span><span class="p">,</span> <span class="n">error_</span><span class="p">))</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">dest</span><span class="p">,</span> <span class="n">error_</span><span class="p">))</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_error</span><span class="o">.</span><span class="n">append</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="nb">str</span><span class="p">(</span><span class="n">error_</span><span class="p">)))</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</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="nb">str</span><span class="p">(</span><span class="n">error_</span><span class="p">)))</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exif_updated</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">to_touch</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
@@ -1742,7 +1825,7 @@
<span class="k">with</span> <span class="n">ExifTool</span><span class="p">(</span>
<span class="n">filepath</span><span class="p">,</span>
<span class="n">flags</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">exiftool_flags</span><span class="p">,</span>
<span class="n">exiftool</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_exiftool_path</span><span class="p">,</span>
<span class="n">exiftool</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_exiftool_path</span><span class="p">,</span>
<span class="p">)</span> <span class="k">as</span> <span class="n">exiftool</span><span class="p">:</span>
<span class="k">for</span> <span class="n">exiftag</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">exif_info</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
@@ -1922,7 +2005,6 @@
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">ismovie</span><span class="p">:</span>
<span class="n">exif</span><span class="p">[</span><span class="s2">&quot;Keys:GPSCoordinates&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">exif</span><span class="p">[</span><span class="s2">&quot;UserData:GPSCoordinates&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="c1"># process date/time and timezone offset</span>
<span class="c1"># Photos exports the following fields and sets modify date to creation date</span>
<span class="c1"># [EXIF] Modify Date : 2020:10:30 00:00:00</span>
@@ -2032,7 +2114,7 @@
<span class="k">def</span> <span class="nf">_get_exif_keywords</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of keywords found in the file&#39;s exif metadata&quot;&quot;&quot;</span>
<span class="n">keywords</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">exif</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exiftool</span>
<span class="n">exif</span> <span class="o">=</span> <span class="n">exiftool_caching</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">)</span>
<span class="k">if</span> <span class="n">exif</span><span class="p">:</span>
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">exif</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;IPTC:Keywords&quot;</span><span class="p">,</span> <span class="s2">&quot;XMP:TagsList&quot;</span><span class="p">,</span> <span class="s2">&quot;XMP:Subject&quot;</span><span class="p">]:</span>
@@ -2049,7 +2131,7 @@
<span class="k">def</span> <span class="nf">_get_exif_persons</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of persons found in the file&#39;s exif metadata&quot;&quot;&quot;</span>
<span class="n">persons</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">exif</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exiftool</span>
<span class="n">exif</span> <span class="o">=</span> <span class="n">exiftool_caching</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">)</span>
<span class="k">if</span> <span class="n">exif</span><span class="p">:</span>
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">exif</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
@@ -2130,10 +2212,7 @@
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span> <span class="ow">or</span> <span class="n">ExportOptions</span><span class="p">()</span>
<span class="n">xmp_template_file</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">_XMP_TEMPLATE_NAME</span> <span class="k">if</span> <span class="ow">not</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">_beta</span> <span class="k">else</span> <span class="n">_XMP_TEMPLATE_NAME_BETA</span>
<span class="p">)</span>
<span class="n">xmp_template</span> <span class="o">=</span> <span class="n">Template</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">_TEMPLATE_DIR</span><span class="p">,</span> <span class="n">xmp_template_file</span><span class="p">))</span>
<span class="n">xmp_template</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_xmp_template</span><span class="p">()</span>
<span class="k">if</span> <span class="n">extension</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">extension</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="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span>
@@ -2240,6 +2319,20 @@
<span class="n">xmp_str</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">xmp_str</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">xmp_str</span>
<span class="k">def</span> <span class="nf">_xmp_template</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return the mako template for XMP sidecar, creating it if necessary&quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_global_xmp_template</span>
<span class="k">if</span> <span class="n">_global_xmp_template</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">_global_xmp_template</span>
<span class="n">xmp_template_file</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">_XMP_TEMPLATE_NAME_BETA</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">_beta</span> <span class="k">else</span> <span class="n">_XMP_TEMPLATE_NAME</span>
<span class="p">)</span>
<span class="n">_global_xmp_template</span> <span class="o">=</span> <span class="n">Template</span><span class="p">(</span>
<span class="n">filename</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">_TEMPLATE_DIR</span><span class="p">,</span> <span class="n">xmp_template_file</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">_global_xmp_template</span>
<span class="k">def</span> <span class="nf">_write_sidecar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="n">sidecar_str</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;write sidecar_str to filename</span>
<span class="sd"> used for exporting sidecar info&quot;&quot;&quot;</span>
@@ -2320,6 +2413,35 @@
<span class="k">else</span><span class="p">:</span>
<span class="n">new_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
<span class="k">return</span> <span class="n">new_files</span>
<span class="k">def</span> <span class="nf">exiftool_caching</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="n">SimpleNamespace</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ExifToolCaching</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return ExifToolCaching object for photo</span>
<span class="sd"> Args:</span>
<span class="sd"> photo: SimpleNamespace object with photo info</span>
<span class="sd"> Returns:</span>
<span class="sd"> ExifToolCaching object</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">photo</span><span class="o">.</span><span class="n">_exiftool_caching</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">exiftool_path</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">_exiftool_path</span> <span class="ow">or</span> <span class="n">get_exiftool_path</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">path</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span><span class="p">):</span>
<span class="n">exiftool</span> <span class="o">=</span> <span class="n">ExifToolCaching</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool_path</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">exiftool</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">except</span> <span class="ne">FileNotFoundError</span><span class="p">:</span>
<span class="c1"># get_exiftool_path raises FileNotFoundError if exiftool not found</span>
<span class="n">exiftool</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
<span class="s2">&quot;exiftool not in path; download and install from https://exiftool.org/&quot;</span>
<span class="p">)</span>
<span class="n">photo</span><span class="o">.</span><span class="n">_exiftool_caching</span> <span class="o">=</span> <span class="n">exiftool</span>
<span class="k">return</span> <span class="n">photo</span><span class="o">.</span><span class="n">_exiftool_caching</span>
</pre></div>
</article>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photoinfo - osxphotos 0.56.7 documentation</title>
<title>osxphotos.photoinfo - osxphotos 0.60.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -205,17 +205,18 @@
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">datetime</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">plistlib</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span><span class="p">,</span> <span class="n">timezone</span>
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">cached_property</span>
<span class="kn">from</span> <span class="nn">types</span> <span class="kn">import</span> <span class="n">SimpleNamespace</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">yaml</span>
<span class="kn">from</span> <span class="nn">osxmetadata</span> <span class="kn">import</span> <span class="n">OSXMetaData</span>
<span class="kn">import</span> <span class="nn">osxphotos</span>
@@ -254,19 +255,24 @@
<span class="kn">from</span> <span class="nn">.momentinfo</span> <span class="kn">import</span> <span class="n">MomentInfo</span>
<span class="kn">from</span> <span class="nn">.personinfo</span> <span class="kn">import</span> <span class="n">FaceInfo</span><span class="p">,</span> <span class="n">PersonInfo</span>
<span class="kn">from</span> <span class="nn">.photoexporter</span> <span class="kn">import</span> <span class="n">ExportOptions</span><span class="p">,</span> <span class="n">PhotoExporter</span>
<span class="kn">from</span> <span class="nn">.phototables</span> <span class="kn">import</span> <span class="n">PhotoTables</span>
<span class="kn">from</span> <span class="nn">.phototemplate</span> <span class="kn">import</span> <span class="n">PhotoTemplate</span><span class="p">,</span> <span class="n">RenderOptions</span>
<span class="kn">from</span> <span class="nn">.placeinfo</span> <span class="kn">import</span> <span class="n">PlaceInfo4</span><span class="p">,</span> <span class="n">PlaceInfo5</span>
<span class="kn">from</span> <span class="nn">.query_builder</span> <span class="kn">import</span> <span class="n">get_query</span>
<span class="kn">from</span> <span class="nn">.scoreinfo</span> <span class="kn">import</span> <span class="n">ScoreInfo</span>
<span class="kn">from</span> <span class="nn">.searchinfo</span> <span class="kn">import</span> <span class="n">SearchInfo</span>
<span class="kn">from</span> <span class="nn">.text_detection</span> <span class="kn">import</span> <span class="n">detect_text</span>
<span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span><span class="p">,</span> <span class="n">get_uti_for_extension</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">_get_resource_loc</span><span class="p">,</span> <span class="n">hexdigest</span><span class="p">,</span> <span class="n">list_directory</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">_get_resource_loc</span><span class="p">,</span> <span class="n">assert_macos</span><span class="p">,</span> <span class="n">is_macos</span><span class="p">,</span> <span class="n">hexdigest</span><span class="p">,</span> <span class="n">list_directory</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotoInfoNone&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">is_macos</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">osxmetadata</span> <span class="kn">import</span> <span class="n">OSXMetaData</span>
<span class="kn">from</span> <span class="nn">.text_detection</span> <span class="kn">import</span> <span class="n">detect_text</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotoInfoNone&quot;</span><span class="p">,</span> <span class="s2">&quot;frozen_photoinfo_factory&quot;</span><span class="p">]</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;osxphotos&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="PhotoInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo">[docs]</a><span class="k">class</span> <span class="nc">PhotoInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Info about a specific photo, contains all the details about the photo</span>
@@ -277,6 +283,7 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">uuid</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">=</span> <span class="n">info</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">:</span> <span class="s2">&quot;osxphotos.PhotosDB&quot;</span> <span class="o">=</span> <span class="n">db</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_path</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">_exiftool_path</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</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">_verbose</span>
<span class="nd">@property</span>
@@ -584,6 +591,8 @@
<span class="sd">&quot;&quot;&quot;return path_edited_live_photo for Photos &lt;= 4&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&gt;</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Wrong database format!&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_predicted_path_edited_live_photo_4</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photopath</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># the heuristic failed, so try to find the file</span>
@@ -597,10 +606,6 @@
<span class="p">),</span>
<span class="kc">None</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">photopath</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;MISSING PATH: edited live photo file for UUID </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2"> does not appear to exist&quot;</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_edited_5_live_photo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@@ -712,39 +717,26 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">person_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of PersonInfo objects for person in picture&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_personinfo</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_personinfo</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">PersonInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;persons&quot;</span><span class="p">]</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_personinfo</span>
<span class="k">return</span> <span class="p">[</span><span class="n">PersonInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;persons&quot;</span><span class="p">]]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">face_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of FaceInfo objects for faces in picture&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">faces</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">_db_faceinfo_uuid</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span> <span class="o">=</span> <span class="p">[</span><span class="n">FaceInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="n">faces</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># no faces</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span>
<span class="n">faces</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">_db_faceinfo_uuid</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span> <span class="o">=</span> <span class="p">[</span><span class="n">FaceInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="n">faces</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># no faces</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">moment_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Moment photo belongs to&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_moment</span> <span class="o">=</span> <span class="n">MomentInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">moment_pk</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;momentID&quot;</span><span class="p">])</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_moment</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span>
<span class="k">return</span> <span class="n">MomentInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">moment_pk</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;momentID&quot;</span><span class="p">])</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">albums</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@@ -761,65 +753,41 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_albums</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;If photo is burst photo, list of albums it is contained in as well as any albums the key photo is contained in, otherwise returns self.albums&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">burst_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
<span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">burst_key</span><span class="p">:</span>
<span class="n">burst_albums</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_albums</span><span class="p">))</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span>
<span class="n">burst_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
<span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">burst_key</span><span class="p">:</span>
<span class="n">burst_albums</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_albums</span><span class="p">))</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">album_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of AlbumInfo objects representing albums the photo is contained in&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">album_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">AlbumInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">album</span><span class="p">)</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">album_uuids</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span>
<span class="n">album_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">()</span>
<span class="k">return</span> <span class="p">[</span><span class="n">AlbumInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">album</span><span class="p">)</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">album_uuids</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_album_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;If photo is a burst photo, returns list of AlbumInfo objects representing albums the photo is contained in as well as albums the burst key photo is contained in, otherwise returns self.album_info.&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">burst_album_info</span> <span class="o">=</span> <span class="nb">list</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">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">burst_key</span><span class="p">:</span>
<span class="n">burst_album_info</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_album_info</span><span class="p">))</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span>
<span class="n">burst_album_info</span> <span class="o">=</span> <span class="nb">list</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">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">burst_key</span><span class="p">:</span>
<span class="n">burst_album_info</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_album_info</span><span class="p">))</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">import_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;ImportInfo object representing import session for the photo or None if no import session&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_import_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_import_info</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">ImportInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_import_info</span>
<span class="k">return</span> <span class="p">(</span>
<span class="n">ImportInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">project_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of AlbumInfo objects representing projects for the photo or None if no projects&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_project_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">project_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">(</span><span class="n">project</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_project_info</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">ProjectInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">album</span><span class="p">)</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">project_uuids</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_project_info</span>
<span class="n">project_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">(</span><span class="n">project</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="n">ProjectInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">album</span><span class="p">)</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">project_uuids</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">keywords</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@@ -833,8 +801,7 @@
<span class="c1"># in this case, return None so result is the same as if title had never been set (which returns NULL)</span>
<span class="c1"># issue #512</span>
<span class="n">title</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span>
<span class="n">title</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">if</span> <span class="n">title</span> <span class="o">==</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="n">title</span>
<span class="k">return</span> <span class="n">title</span>
<span class="k">return</span> <span class="kc">None</span> <span class="k">if</span> <span class="n">title</span> <span class="o">==</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="n">title</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@@ -1107,33 +1074,10 @@
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">:</span>
<span class="n">live_model_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;live_model_id&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">live_model_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;missing live_model_id: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span><span class="p">,</span> <span class="n">nn_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">live_model_id</span><span class="p">)</span>
<span class="n">library_path</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">library_path</span>
<span class="n">photopath</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">library_path</span><span class="p">,</span>
<span class="s2">&quot;resources&quot;</span><span class="p">,</span>
<span class="s2">&quot;media&quot;</span><span class="p">,</span>
<span class="s2">&quot;master&quot;</span><span class="p">,</span>
<span class="n">folder_id</span><span class="p">,</span>
<span class="n">nn_id</span><span class="p">,</span>
<span class="sa">f</span><span class="s2">&quot;jpegvideocomplement_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov&quot;</span><span class="p">,</span>
<span class="p">)</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># In testing, I&#39;ve seen occasional missing movie for live photo</span>
<span class="c1"># These appear to be valid -- e.g. live component hasn&#39;t been downloaded from iCloud</span>
<span class="c1"># photos 4 has &quot;isOnDisk&quot; column we could check</span>
<span class="c1"># or could do the actual check with &quot;isfile&quot;</span>
<span class="c1"># TODO: should this be a warning or debug?</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_live_photo_4</span><span class="p">()</span>
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_live_photo_shared_5</span><span class="p">()</span>
<span class="n">filename</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="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="n">filename</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">filename</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_3.mov&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">photopath</span><span class="p">)</span>
@@ -1147,6 +1091,50 @@
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_live_photo_shared_5</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path for live photo for shared photos&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2"> is not a shared photo&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2"> is not a live photo&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_5_shared</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photopath</span><span class="p">:</span>
<span class="n">photopath</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">photopath</span><span class="p">)</span><span class="o">.</span><span class="n">with_suffix</span><span class="p">(</span><span class="s2">&quot;.MOV&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">photopath</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_live_photo_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path for live edited photo for Photos &lt;= 4&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">:</span>
<span class="n">live_model_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;live_model_id&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">live_model_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;missing live_model_id: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span><span class="p">,</span> <span class="n">nn_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">live_model_id</span><span class="p">)</span>
<span class="n">library_path</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">library_path</span>
<span class="n">photopath</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">library_path</span><span class="p">,</span>
<span class="s2">&quot;resources&quot;</span><span class="p">,</span>
<span class="s2">&quot;media&quot;</span><span class="p">,</span>
<span class="s2">&quot;master&quot;</span><span class="p">,</span>
<span class="n">folder_id</span><span class="p">,</span>
<span class="n">nn_id</span><span class="p">,</span>
<span class="sa">f</span><span class="s2">&quot;jpegvideocomplement_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov&quot;</span><span class="p">,</span>
<span class="p">)</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># In testing, I&#39;ve seen occasional missing movie for live photo</span>
<span class="c1"># These appear to be valid -- e.g. live component hasn&#39;t been downloaded from iCloud</span>
<span class="c1"># photos 4 has &quot;isOnDisk&quot; column we could check</span>
<span class="c1"># or could do the actual check with &quot;isfile&quot;</span>
<span class="c1"># TODO: should this be a warning or debug?</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">path_derivatives</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)&quot;&quot;&quot;</span>
@@ -1394,7 +1382,6 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;score not implemented for this database version&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
@@ -1540,7 +1527,6 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;exif_info not implemented for this database version&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
@@ -1623,11 +1609,14 @@
<span class="k">def</span> <span class="nf">hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns a unique digest of the photo&#39;s properties and metadata;</span>
<span class="sd"> useful for detecting changes in any property/metadata of the photo&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">json</span><span class="p">())</span>
<span class="k">return</span> <span class="n">hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_json_hexdigest</span><span class="p">())</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">cloud_metadata</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns contents of ZCLOUDMASTERMEDIAMETADATA as dict&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">cloud_metadata</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">[</span><span class="n">Any</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Returns contents of ZCLOUDMASTERMEDIAMETADATA as dict; Photos 5+ only&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{}</span>
<span class="c1"># This is a large blob of data so don&#39;t load it unless requested</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="bp">self</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="s2">&quot;ASSET&quot;</span><span class="p">]</span>
<span class="n">sql_cloud_metadata</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
@@ -1638,10 +1627,6 @@
<span class="s2"> WHERE </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID = ?</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;cloud_metadata not implemented for this database version&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{}</span>
<span class="n">_</span><span class="p">,</span> <span class="n">cursor</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">get_db_connection</span><span class="p">()</span>
<span class="n">metadata</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="n">results</span> <span class="o">:=</span> <span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">sql_cloud_metadata</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,))</span><span class="o">.</span><span class="n">fetchone</span><span class="p">():</span>
@@ -1649,6 +1634,16 @@
<span class="n">metadata</span> <span class="o">=</span> <span class="n">plistlib</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">return</span> <span class="n">metadata</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">cloud_guid</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the GUID of the photo in iCloud (Photos 5+ only)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudMasterGUID&quot;</span><span class="p">]</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">cloud_owner_hashed_id</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the hashed iCloud ID of the owner of the shared photo (Photos 5+ only)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudownerhashedpersonid&quot;</span><span class="p">]</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">fingerprint</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns fingerprint of original photo as a string&quot;&quot;&quot;</span>
@@ -1686,6 +1681,8 @@
<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;detect text in photo, either from cached extended attribute or by attempting text detection&quot;&quot;&quot;</span>
<span class="n">assert_macos</span><span class="p">()</span>
<span class="n">path</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span>
<span class="p">)</span>
@@ -1965,94 +1962,189 @@
<span class="p">}</span>
<span class="k">return</span> <span class="n">yaml</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<div class="viewcode-block" id="PhotoInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return dict representation&quot;&quot;&quot;</span>
<div class="viewcode-block" id="PhotoInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">shallow</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Return dict representation of PhotoInfo object.</span>
<span class="sd"> Args:</span>
<span class="sd"> shallow: if True, return shallow representation (does not contain folder_info, person_info, etc.)</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict representation of PhotoInfo object</span>
<span class="sd"> Note:</span>
<span class="sd"> The shallow representation is used internally by export as it contains only the subset of data needed for export.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">comments</span> <span class="o">=</span> <span class="p">[</span><span class="n">comment</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">comment</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">comments</span><span class="p">]</span>
<span class="n">exif_info</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">face_info</span> <span class="o">=</span> <span class="p">[</span><span class="n">face</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">face</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">face_info</span><span class="p">]</span>
<span class="n">folders</span> <span class="o">=</span> <span class="p">{</span><span class="n">album</span><span class="o">.</span><span class="n">title</span><span class="p">:</span> <span class="n">album</span><span class="o">.</span><span class="n">folder_names</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">}</span>
<span class="n">exif</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">likes</span> <span class="o">=</span> <span class="p">[</span><span class="n">like</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">like</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">likes</span><span class="p">]</span>
<span class="n">place</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">score</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">score</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">score</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">comments</span> <span class="o">=</span> <span class="p">[</span><span class="n">comment</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">comment</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">comments</span><span class="p">]</span>
<span class="n">likes</span> <span class="o">=</span> <span class="p">[</span><span class="n">like</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">like</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">likes</span><span class="p">]</span>
<span class="n">faces</span> <span class="o">=</span> <span class="p">[</span><span class="n">face</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">face</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">face_info</span><span class="p">]</span>
<span class="n">search_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;library&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span>
<span class="s2">&quot;original_filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span><span class="p">,</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;albums&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">,</span>
<span class="s2">&quot;burst&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">,</span>
<span class="s2">&quot;cloud_guid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_guid</span><span class="p">,</span>
<span class="s2">&quot;cloud_owner_hashed_id&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_owner_hashed_id</span><span class="p">,</span>
<span class="s2">&quot;comments&quot;</span><span class="p">:</span> <span class="n">comments</span><span class="p">,</span>
<span class="s2">&quot;date_added&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_added</span><span class="p">,</span>
<span class="s2">&quot;date_modified&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_modified</span><span class="p">,</span>
<span class="s2">&quot;date_trashed&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_trashed</span><span class="p">,</span>
<span class="s2">&quot;date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span><span class="p">,</span>
<span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">&quot;exif_info&quot;</span><span class="p">:</span> <span class="n">exif_info</span><span class="p">,</span>
<span class="s2">&quot;external_edit&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">external_edit</span><span class="p">,</span>
<span class="s2">&quot;face_info&quot;</span><span class="p">:</span> <span class="n">face_info</span><span class="p">,</span>
<span class="s2">&quot;favorite&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">favorite</span><span class="p">,</span>
<span class="s2">&quot;filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span>
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">fingerprint</span><span class="p">,</span>
<span class="s2">&quot;folders&quot;</span><span class="p">:</span> <span class="n">folders</span><span class="p">,</span>
<span class="s2">&quot;has_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span><span class="p">,</span>
<span class="s2">&quot;hasadjustments&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">,</span>
<span class="s2">&quot;hdr&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hdr</span><span class="p">,</span>
<span class="s2">&quot;height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">height</span><span class="p">,</span>
<span class="s2">&quot;hidden&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hidden</span><span class="p">,</span>
<span class="s2">&quot;incloud&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">incloud</span><span class="p">,</span>
<span class="s2">&quot;intrash&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">intrash</span><span class="p">,</span>
<span class="s2">&quot;iscloudasset&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">iscloudasset</span><span class="p">,</span>
<span class="s2">&quot;ismissing&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">,</span>
<span class="s2">&quot;ismovie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismovie</span><span class="p">,</span>
<span class="s2">&quot;isphoto&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">,</span>
<span class="s2">&quot;israw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">israw</span><span class="p">,</span>
<span class="s2">&quot;isreference&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isreference</span><span class="p">,</span>
<span class="s2">&quot;keywords&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">,</span>
<span class="s2">&quot;labels&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">labels</span><span class="p">,</span>
<span class="s2">&quot;keywords&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">,</span>
<span class="s2">&quot;albums&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">,</span>
<span class="s2">&quot;folders&quot;</span><span class="p">:</span> <span class="n">folders</span><span class="p">,</span>
<span class="s2">&quot;persons&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">,</span>
<span class="s2">&quot;faces&quot;</span><span class="p">:</span> <span class="n">faces</span><span class="p">,</span>
<span class="s2">&quot;path&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">,</span>
<span class="s2">&quot;ismissing&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">,</span>
<span class="s2">&quot;hasadjustments&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">,</span>
<span class="s2">&quot;external_edit&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">external_edit</span><span class="p">,</span>
<span class="s2">&quot;favorite&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">favorite</span><span class="p">,</span>
<span class="s2">&quot;hidden&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hidden</span><span class="p">,</span>
<span class="s2">&quot;latitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_latitude</span><span class="p">,</span>
<span class="s2">&quot;longitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_longitude</span><span class="p">,</span>
<span class="s2">&quot;path_edited&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span><span class="p">,</span>
<span class="s2">&quot;shared&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">,</span>
<span class="s2">&quot;isphoto&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">,</span>
<span class="s2">&quot;ismovie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismovie</span><span class="p">,</span>
<span class="s2">&quot;uti&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span><span class="p">,</span>
<span class="s2">&quot;uti_original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_original</span><span class="p">,</span>
<span class="s2">&quot;burst&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">,</span>
<span class="s2">&quot;library&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="s2">&quot;likes&quot;</span><span class="p">:</span> <span class="n">likes</span><span class="p">,</span>
<span class="s2">&quot;live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span><span class="p">,</span>
<span class="s2">&quot;location&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span>
<span class="s2">&quot;longitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_longitude</span><span class="p">,</span>
<span class="s2">&quot;orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">orientation</span><span class="p">,</span>
<span class="s2">&quot;original_filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span><span class="p">,</span>
<span class="s2">&quot;original_filesize&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filesize</span><span class="p">,</span>
<span class="s2">&quot;original_height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_height</span><span class="p">,</span>
<span class="s2">&quot;original_orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_orientation</span><span class="p">,</span>
<span class="s2">&quot;original_width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_width</span><span class="p">,</span>
<span class="s2">&quot;owner&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="p">,</span>
<span class="s2">&quot;panorama&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">panorama</span><span class="p">,</span>
<span class="s2">&quot;path_edited_live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited_live_photo</span><span class="p">,</span>
<span class="s2">&quot;path_edited&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span><span class="p">,</span>
<span class="s2">&quot;path_live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_live_photo</span><span class="p">,</span>
<span class="s2">&quot;iscloudasset&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">iscloudasset</span><span class="p">,</span>
<span class="s2">&quot;incloud&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">incloud</span><span class="p">,</span>
<span class="s2">&quot;isreference&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isreference</span><span class="p">,</span>
<span class="s2">&quot;date_modified&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_modified</span><span class="p">,</span>
<span class="s2">&quot;path_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_raw</span><span class="p">,</span>
<span class="s2">&quot;path&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">,</span>
<span class="s2">&quot;persons&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">,</span>
<span class="s2">&quot;place&quot;</span><span class="p">:</span> <span class="n">place</span><span class="p">,</span>
<span class="s2">&quot;portrait&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">portrait</span><span class="p">,</span>
<span class="s2">&quot;raw_original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw_original</span><span class="p">,</span>
<span class="s2">&quot;score&quot;</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span>
<span class="s2">&quot;screenshot&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">screenshot</span><span class="p">,</span>
<span class="s2">&quot;selfie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">selfie</span><span class="p">,</span>
<span class="s2">&quot;shared&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">,</span>
<span class="s2">&quot;slow_mo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">slow_mo</span><span class="p">,</span>
<span class="s2">&quot;time_lapse&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">time_lapse</span><span class="p">,</span>
<span class="s2">&quot;hdr&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hdr</span><span class="p">,</span>
<span class="s2">&quot;selfie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">selfie</span><span class="p">,</span>
<span class="s2">&quot;panorama&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">panorama</span><span class="p">,</span>
<span class="s2">&quot;has_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span><span class="p">,</span>
<span class="s2">&quot;israw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">israw</span><span class="p">,</span>
<span class="s2">&quot;raw_original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw_original</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">&quot;tzoffset&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">tzoffset</span><span class="p">,</span>
<span class="s2">&quot;uti_edited&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_edited</span><span class="p">,</span>
<span class="s2">&quot;uti_original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_original</span><span class="p">,</span>
<span class="s2">&quot;uti_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_raw</span><span class="p">,</span>
<span class="s2">&quot;path_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_raw</span><span class="p">,</span>
<span class="s2">&quot;place&quot;</span><span class="p">:</span> <span class="n">place</span><span class="p">,</span>
<span class="s2">&quot;exif&quot;</span><span class="p">:</span> <span class="n">exif</span><span class="p">,</span>
<span class="s2">&quot;score&quot;</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span>
<span class="s2">&quot;intrash&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">intrash</span><span class="p">,</span>
<span class="s2">&quot;height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">height</span><span class="p">,</span>
<span class="s2">&quot;uti&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span><span class="p">,</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;visible&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">visible</span><span class="p">,</span>
<span class="s2">&quot;width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">width</span><span class="p">,</span>
<span class="s2">&quot;orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">orientation</span><span class="p">,</span>
<span class="s2">&quot;original_height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_height</span><span class="p">,</span>
<span class="s2">&quot;original_width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_width</span><span class="p">,</span>
<span class="s2">&quot;original_orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_orientation</span><span class="p">,</span>
<span class="s2">&quot;original_filesize&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filesize</span><span class="p">,</span>
<span class="s2">&quot;comments&quot;</span><span class="p">:</span> <span class="n">comments</span><span class="p">,</span>
<span class="s2">&quot;likes&quot;</span><span class="p">:</span> <span class="n">likes</span><span class="p">,</span>
<span class="s2">&quot;search_info&quot;</span><span class="p">:</span> <span class="n">search_info</span><span class="p">,</span>
<span class="p">}</span></div>
<span class="p">}</span>
<div class="viewcode-block" id="PhotoInfo.json"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.json">[docs]</a> <span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return JSON representation&quot;&quot;&quot;</span>
<span class="c1"># non-shallow keys</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">shallow</span><span class="p">:</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;album_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">album</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;path_derivatives&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_derivatives</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;adjustments&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span> <span class="k">else</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;burst_album_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_album_info</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;burst_albums&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_albums</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;burst_default_pick&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_default_pick</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;burst_key&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_key</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;burst_photos&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;burst_selected&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_selected</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;cloud_metadata&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_metadata</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;import_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">import_info</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">import_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;labels_normalized&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">labels_normalized</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;person_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">person_info</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;project_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">project_info</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;search_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;search_info_normalized&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">search_info_normalized</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info_normalized</span>
<span class="k">else</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">dict_data</span></div>
<div class="viewcode-block" id="PhotoInfo.json"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.json">[docs]</a> <span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">indent</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">shallow</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return JSON representation</span>
<span class="sd"> Args:</span>
<span class="sd"> indent: indent level for JSON, if None, no indent</span>
<span class="sd"> shallow: if True, return shallow JSON representation (does not contain folder_info, person_info, etc.)</span>
<span class="sd"> Returns:</span>
<span class="sd"> JSON string</span>
<span class="sd"> Note:</span>
<span class="sd"> The shallow representation is used internally by export as it contains only the subset of data needed for export.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">default</span><span class="p">(</span><span class="n">o</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">)):</span>
<span class="k">return</span> <span class="n">o</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="n">shallow</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">if</span> <span class="n">shallow</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="n">shallow</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">dict_data</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="c1"># sort lists such as keywords so JSON is consistent</span>
<span class="c1"># but do not sort certain items like location</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;location&quot;</span><span class="p">]:</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">))</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nb">dict</span><span class="p">):</span>
<span class="n">dict_data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">dict_data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">)</span></div>
<span class="n">dict_data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="n">v</span> <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">dict_data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="n">indent</span><span class="p">)</span></div>
<div class="viewcode-block" id="PhotoInfo.tables"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.tables">[docs]</a> <span class="k">def</span> <span class="nf">tables</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">PhotoTables</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return PhotoTables object to provide access database tables associated with this photo (Photos 5+)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">None</span> <span class="k">if</span> <span class="bp">self</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">5</span> <span class="k">else</span> <span class="n">PhotoTables</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
<span class="k">def</span> <span class="nf">_json_hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;JSON for use by hexdigest()&quot;&quot;&quot;</span>
<span class="c1"># This differs from json() because hexdigest must not change if metadata changed</span>
<span class="c1"># With json(), sort order of lists of dicts is not consistent but these aren&#39;t needed</span>
<span class="c1"># for computing hexdigest so we can ignore them</span>
<span class="c1"># also don&#39;t use visible because it changes based on Photos UI state</span>
<span class="k">def</span> <span class="nf">default</span><span class="p">(</span><span class="n">o</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">)):</span>
<span class="k">return</span> <span class="n">o</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="n">shallow</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;face_info&quot;</span><span class="p">,</span> <span class="s2">&quot;visible&quot;</span><span class="p">]:</span>
<span class="k">del</span> <span class="n">dict_data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">dict_data</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="c1"># sort lists such as keywords so JSON is consistent</span>
<span class="c1"># but do not sort certain items like location</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;location&quot;</span><span class="p">]:</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">))</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nb">dict</span><span class="p">):</span>
<span class="n">dict_data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="n">v</span> <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">dict_data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Compare two PhotoInfo objects for equality&quot;&quot;&quot;</span>
@@ -2076,13 +2168,114 @@
<span class="k">class</span> <span class="nc">PhotoInfoNone</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;mock class that returns None for all attributes&quot;&quot;&quot;</span>
<span class="sd">&quot;&quot;&quot;Mock class that returns None for all attributes&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="fm">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">frozen_photoinfo_factory</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="n">PhotoInfo</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">SimpleNamespace</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return a frozen SimpleNamespace object for a PhotoInfo object&quot;&quot;&quot;</span>
<span class="n">photo_json</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_object_hook</span><span class="p">(</span><span class="n">d</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="n">Any</span><span class="p">,</span> <span class="n">Any</span><span class="p">]):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">d</span><span class="p">:</span>
<span class="k">return</span> <span class="n">d</span>
<span class="c1"># if d key matches a ISO 8601 datetime (&#39;2023-03-24T06:46:57.690786&#39;, &#39;2019-07-04T16:24:01-07:00&#39;, &#39;2019-07-04T16:24:01+07:00&#39;), convert to datetime</span>
<span class="c1"># fromisoformat will also handle dates with timezone offset in form +0700, etc.</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="ow">and</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span>
<span class="sa">r</span><span class="s2">&quot;\d</span><span class="si">{4}</span><span class="s2">-\d</span><span class="si">{2}</span><span class="s2">-\d</span><span class="si">{2}</span><span class="s2">T\d</span><span class="si">{2}</span><span class="s2">:\d</span><span class="si">{2}</span><span class="s2">:\d</span><span class="si">{2}</span><span class="s2">[.]?\d*[+-]?\d</span><span class="si">{2}</span><span class="s2">[:]?\d</span><span class="si">{2}</span><span class="s2">?&quot;</span><span class="p">,</span> <span class="n">v</span>
<span class="p">):</span>
<span class="n">d</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="k">return</span> <span class="n">SimpleNamespace</span><span class="p">(</span><span class="o">**</span><span class="n">d</span><span class="p">)</span>
<span class="n">frozen</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">photo_json</span><span class="p">,</span> <span class="n">object_hook</span><span class="o">=</span><span class="k">lambda</span> <span class="n">d</span><span class="p">:</span> <span class="n">_object_hook</span><span class="p">(</span><span class="n">d</span><span class="p">))</span>
<span class="c1"># add on json() method to frozen object</span>
<span class="k">def</span> <span class="nf">_json</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="k">return</span> <span class="n">photo_json</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">_json</span>
<span class="c1"># add hexdigest property to frozen object</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">hexdigest</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span>
<span class="k">def</span> <span class="nf">detected_text</span><span class="p">(</span><span class="n">confidence_threshold</span><span class="o">=</span><span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Detects text in photo and returns lists of results as (detected text, confidence)</span>
<span class="sd"> confidence_threshold: float between 0.0 and 1.0. If text detection confidence is below this threshold,</span>
<span class="sd"> text will not be returned. Default is TEXT_DETECTION_CONFIDENCE_THRESHOLD</span>
<span class="sd"> If photo is edited, uses the edited photo, otherwise the original; falls back to the preview image if neither edited or original is available</span>
<span class="sd"> Returns: list of (detected text, confidence) tuples</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">AttributeError</span><span class="p">,</span> <span class="ne">KeyError</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="ne">AttributeError</span><span class="p">):</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text</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="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error detecting text in photo </span><span class="si">{</span><span class="n">frozen</span><span class="o">.</span><span class="n">uuid</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="n">detected_text</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">confidence</span><span class="p">)</span>
<span class="k">for</span> <span class="n">text</span><span class="p">,</span> <span class="n">confidence</span> <span class="ow">in</span> <span class="n">detected_text</span>
<span class="k">if</span> <span class="n">confidence</span> <span class="o">&gt;=</span> <span class="n">confidence_threshold</span>
<span class="p">]</span>
<span class="k">return</span> <span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">_detected_text</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;detect text in photo, either from cached extended attribute or by attempting text detection&quot;&quot;&quot;</span>
<span class="n">path</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">path_edited</span>
<span class="k">if</span> <span class="n">frozen</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="ow">and</span> <span class="n">frozen</span><span class="o">.</span><span class="n">path_edited</span>
<span class="k">else</span> <span class="n">frozen</span><span class="o">.</span><span class="n">path</span>
<span class="p">)</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">path</span> <span class="ow">or</span> <span class="n">frozen</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">frozen</span><span class="o">.</span><span class="n">path_derivatives</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="n">md</span> <span class="o">=</span> <span class="n">OSXMetaData</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">decoder</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Decode value from JSON&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">val</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">))</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">md</span><span class="o">.</span><span class="n">get_xattr</span><span class="p">(</span>
<span class="s2">&quot;osxphotos.metadata:detected_text&quot;</span><span class="p">,</span> <span class="n">decode</span><span class="o">=</span><span class="n">decoder</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">detected_text</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">orientation</span> <span class="o">=</span> <span class="n">frozen</span><span class="o">.</span><span class="n">orientation</span> <span class="ow">or</span> <span class="kc">None</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">detect_text</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">orientation</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">encoder</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Encode value as JSON&quot;&quot;&quot;</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">return</span> <span class="n">val</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">md</span><span class="o">.</span><span class="n">set_xattr</span><span class="p">(</span>
<span class="s2">&quot;osxphotos.metadata:detected_text&quot;</span><span class="p">,</span> <span class="n">detected_text</span><span class="p">,</span> <span class="n">encode</span><span class="o">=</span><span class="n">encoder</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">detected_text</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">detected_text</span> <span class="o">=</span> <span class="n">detected_text</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text</span> <span class="o">=</span> <span class="n">_detected_text</span>
<span class="k">return</span> <span class="n">frozen</span>
</pre></div>
</article>
</div>

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.photosalbum - osxphotos 0.51.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photosalbum - osxphotos 0.60.0 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.60.0 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.60.0 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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -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,34 +195,37 @@
</div>
<article role="main">
<h1>Source code for osxphotos.photosalbum</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosAlbum class to create an album in default Photos library and add photos to it """</span>
<span></span><span class="sd">&quot;&quot;&quot; PhotosAlbum class to create an album in default Photos library and add photos to it &quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">more_itertools</span> <span class="kn">import</span> <span class="n">chunked</span>
<span class="kn">from</span> <span class="nn">photoscript</span> <span class="kn">import</span> <span class="n">Album</span><span class="p">,</span> <span class="n">Folder</span><span class="p">,</span> <span class="n">Photo</span><span class="p">,</span> <span class="n">PhotosLibrary</span>
<span class="kn">from</span> <span class="nn">.photoinfo</span> <span class="kn">import</span> <span class="n">PhotoInfo</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">noop</span><span class="p">,</span> <span class="n">pluralize</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">assert_macos</span><span class="p">,</span> <span class="n">noop</span><span class="p">,</span> <span class="n">pluralize</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"PhotosAlbum"</span><span class="p">,</span> <span class="s2">"PhotosAlbumPhotoScript"</span><span class="p">]</span>
<span class="n">assert_macos</span><span class="p">()</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">photoscript</span> <span class="kn">import</span> <span class="n">Album</span><span class="p">,</span> <span class="n">Folder</span><span class="p">,</span> <span class="n">Photo</span><span class="p">,</span> <span class="n">PhotosLibrary</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotosAlbum&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotosAlbumPhotoScript&quot;</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">folder_by_path</span><span class="p">(</span><span class="n">folders</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Folder</span><span class="p">:</span>
<span class="sd">"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""</span>
<span class="sd">&quot;&quot;&quot;Get (and create if necessary) a Photos Folder by path (passed as list of folder names)&quot;&quot;&quot;</span>
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="n">top_folder_name</span> <span class="o">=</span> <span class="n">folders</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">top_folder</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">top_folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating folder &#39;</span><span class="si">{</span><span class="n">top_folder_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">)</span>
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">top_folder</span>
<span class="k">for</span> <span class="n">folder_name</span> <span class="ow">in</span> <span class="n">folders</span><span class="p">:</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">folder</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating folder &#39;</span><span class="si">{</span><span class="n">folder_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">folder</span>
<span class="k">return</span> <span class="n">current_folder</span>
@@ -230,7 +234,7 @@
<span class="k">def</span> <span class="nf">album_by_path</span><span class="p">(</span>
<span class="n">folders_album</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Album</span><span class="p">:</span>
<span class="sd">"""Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)"""</span>
<span class="sd">&quot;&quot;&quot;Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)&quot;&quot;&quot;</span>
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">folders_album</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
@@ -239,21 +243,21 @@
<span class="n">folder</span> <span class="o">=</span> <span class="n">folder_by_path</span><span class="p">(</span><span class="n">folders_album</span><span class="p">,</span> <span class="n">verbose</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating album &#39;</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># only have album name</span>
<span class="n">album_name</span> <span class="o">=</span> <span class="n">folders_album</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating album &#39;</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">return</span> <span class="n">album</span>
<div class="viewcode-block" id="PhotosAlbum"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbum">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbum</span><span class="p">:</span>
<span class="sd">"""Add osxphotos.photoinfo.PhotoInfo objects to album"""</span>
<span class="sd">&quot;&quot;&quot;Add osxphotos.photoinfo.PhotoInfo objects to album&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -262,17 +266,17 @@
<span class="n">split_folder</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">rich</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">"""Return a PhotosAlbum object, creating the album if necessary</span>
<span class="sd">&quot;&quot;&quot;Return a PhotosAlbum object, creating the album if necessary</span>
<span class="sd"> Args:</span>
<span class="sd"> name: Name of album</span>
<span class="sd"> verbose: optional callable to print verbose output</span>
<span class="sd"> split_folder: if set, split album name on value of split_folder to create folders if necessary,</span>
<span class="sd"> e.g. if name = 'folder1/folder2/album' and split_folder='/',</span>
<span class="sd"> then folders 'folder1' and 'folder2' will be created and album 'album' will be created in 'folder2';</span>
<span class="sd"> if not set, album 'folder1/folder2/album' will be created</span>
<span class="sd"> e.g. if name = &#39;folder1/folder2/album&#39; and split_folder=&#39;/&#39;,</span>
<span class="sd"> then folders &#39;folder1&#39; and &#39;folder2&#39; will be created and album &#39;album&#39; will be created in &#39;folder2&#39;;</span>
<span class="sd"> if not set, album &#39;folder1/folder2/album&#39; will be created</span>
<span class="sd"> rich: if True, use rich themes for verbose output</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="bp">self</span><span class="o">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">PhotosLibrary</span><span class="p">()</span>
@@ -285,7 +289,7 @@
<span class="n">photo_</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo_</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PhotoInfo</span><span class="p">]):</span>
@@ -295,42 +299,42 @@
<span class="n">photos</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Error creating Photo object for photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Error creating Photo object for photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">photolist</span> <span class="ow">in</span> <span class="n">chunked</span><span class="p">(</span><span class="n">photos</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">&#39;photo&#39;</span><span class="p">,</span> <span class="s1">&#39;photos&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">photos</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_format_uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format uuid for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[uuid]</span><span class="si">{</span><span class="n">uuid</span><span class="si">}</span><span class="s2">[/uuid]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">uuid</span>
<span class="sd">&quot;&quot;&quot; &quot;Format uuid for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[uuid]</span><span class="si">{</span><span class="n">uuid</span><span class="si">}</span><span class="s2">[/uuid]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">uuid</span>
<span class="k">def</span> <span class="nf">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">album</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format album name for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[filepath]</span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">[/filepath]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">album</span>
<span class="sd">&quot;&quot;&quot; &quot;Format album name for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[filepath]</span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">[/filepath]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">album</span>
<span class="k">def</span> <span class="nf">_format_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format name for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[filename]</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">[/filename]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">name</span>
<span class="sd">&quot;&quot;&quot; &quot;Format name for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[filename]</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">[/filename]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">name</span>
<span class="k">def</span> <span class="nf">_format_num</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">num</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format number for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[num]</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">[/num]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">num</span><span class="p">)</span></div>
<span class="sd">&quot;&quot;&quot; &quot;Format number for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[num]</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">[/num]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">num</span><span class="p">)</span></div>
<div class="viewcode-block" id="PhotosAlbumPhotoScript"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbumPhotoScript">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbumPhotoScript</span><span class="p">(</span><span class="n">PhotosAlbum</span><span class="p">):</span>
<span class="sd">"""Add photoscript.Photo objects to album"""</span>
<span class="sd">&quot;&quot;&quot;Add photoscript.Photo objects to album&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="n">Photo</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Photo</span><span class="p">]):</span>
@@ -338,7 +342,7 @@
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">&#39;photo&#39;</span><span class="p">,</span> <span class="s1">&#39;photos&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span></div>
</pre></div>
</article>
@@ -377,7 +381,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.photosdb._photosdb_process_comments - osxphotos 0.50.13 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.60.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.60.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -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,8 +195,8 @@
</div>
<article role="main">
<h1>Source code for osxphotos.photosdb._photosdb_process_comments</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosDB method for processing comments and likes on shared photos.</span>
<span class="sd"> Do not import this module directly """</span>
<span></span><span class="sd">&quot;&quot;&quot; PhotosDB method for processing comments and likes on shared photos.</span>
<span class="sd"> Do not import this module directly &quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">datetime</span>
@@ -207,10 +208,10 @@
<span class="k">def</span> <span class="nf">_process_comments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""load the comments and likes data from the database</span>
<span class="sd">&quot;&quot;&quot;load the comments and likes data from the database</span>
<span class="sd"> this is a PhotosDB method that should be imported in</span>
<span class="sd"> the PhotosDB class definition in photosdb.py</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_hashed_person_id</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_comments_uuid</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
@@ -221,7 +222,7 @@
<div class="viewcode-block" id="CommentInfo"><a class="viewcode-back" href="../../../reference.html#osxphotos.CommentInfo">[docs]</a><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">CommentInfo</span><span class="p">:</span>
<span class="sd">"""Class for shared photo comments"""</span>
<span class="sd">&quot;&quot;&quot;Class for shared photo comments&quot;&quot;&quot;</span>
<span class="n">datetime</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span>
<span class="n">user</span><span class="p">:</span> <span class="nb">str</span>
@@ -234,7 +235,7 @@
<div class="viewcode-block" id="LikeInfo"><a class="viewcode-back" href="../../../reference.html#osxphotos.LikeInfo">[docs]</a><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">LikeInfo</span><span class="p">:</span>
<span class="sd">"""Class for shared photo likes"""</span>
<span class="sd">&quot;&quot;&quot;Class for shared photo likes&quot;&quot;&quot;</span>
<span class="n">datetime</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span>
<span class="n">user</span><span class="p">:</span> <span class="nb">str</span>
@@ -247,25 +248,25 @@
<span class="c1"># The following methods do not get imported into PhotosDB</span>
<span class="c1"># but will get called by _process_comments</span>
<span class="k">def</span> <span class="nf">_process_comments_4</span><span class="p">(</span><span class="n">photosdb</span><span class="p">):</span>
<span class="sd">"""process comments and likes info for Photos &lt;= 4</span>
<span class="sd"> photosdb: PhotosDB instance"""</span>
<span class="sd">&quot;&quot;&quot;process comments and likes info for Photos &lt;= 4</span>
<span class="sd"> photosdb: PhotosDB instance&quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Not implemented for database version </span><span class="si">{</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_db_version</span><span class="si">}</span><span class="s2">."</span>
<span class="sa">f</span><span class="s2">&quot;Not implemented for database version </span><span class="si">{</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_db_version</span><span class="si">}</span><span class="s2">.&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_comments_5</span><span class="p">(</span><span class="n">photosdb</span><span class="p">):</span>
<span class="sd">"""process comments and likes info for Photos &gt;= 5</span>
<span class="sd"> photosdb: PhotosDB instance"""</span>
<span class="sd">&quot;&quot;&quot;process comments and likes info for Photos &gt;= 5</span>
<span class="sd"> photosdb: PhotosDB instance&quot;&quot;&quot;</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_tmp_db</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">"ASSET"</span><span class="p">]</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">&quot;ASSET&quot;</span><span class="p">]</span>
<span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">=</span> <span class="n">sqlite_open_ro</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sd">"""</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> SELECT DISTINCT</span>
<span class="sd"> ZINVITEEHASHEDPERSONID AS HASHEDPERSONID,</span>
<span class="sd"> ZINVITEEFIRSTNAME AS FIRSTNAME,</span>
@@ -273,7 +274,7 @@
<span class="sd"> ZINVITEEFULLNAME AS FULLNAME</span>
<span class="sd"> FROM ZCLOUDSHAREDALBUMINVITATIONRECORD</span>
<span class="sd"> WHERE HASHEDPERSONID IS NOT NULL</span>
<span class="sd"> AND HASHEDPERSONID != ""</span>
<span class="sd"> AND HASHEDPERSONID != &quot;&quot;</span>
<span class="sd"> AND NOT (FIRSTNAME IS NULL AND LASTNAME IS NULL)</span>
<span class="sd"> UNION</span>
<span class="sd"> SELECT DISTINCT</span>
@@ -283,9 +284,9 @@
<span class="sd"> ZCLOUDOWNERFULLNAME AS FULLNAME</span>
<span class="sd"> FROM ZGENERICALBUM</span>
<span class="sd"> WHERE HASHEDPERSONID IS NOT NULL</span>
<span class="sd"> AND HASHEDPERSONID != ""</span>
<span class="sd"> AND HASHEDPERSONID != &quot;&quot;</span>
<span class="sd"> AND NOT (FIRSTNAME IS NULL AND LASTNAME IS NULL)</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">)</span>
<span class="c1"># order of results</span>
@@ -298,35 +299,35 @@
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">results</span><span class="o">.</span><span class="n">fetchall</span><span class="p">():</span>
<span class="n">person_id</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">person_id</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"first_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span>
<span class="s2">"last_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span>
<span class="s2">"full_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span>
<span class="s2">&quot;first_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span>
<span class="s2">&quot;last_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span>
<span class="s2">&quot;full_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span>
<span class="p">}</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"""</span>
<span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> SELECT </span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID, -- UUID of the photo</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a "like"</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a &quot;like&quot;</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTDATE, -- date of comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTTEXT, -- text of comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTERHASHEDPERSONID, -- hashed ID of person who made comment/like</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user's) comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user&#39;s) comment</span>
<span class="s2"> FROM ZCLOUDSHAREDCOMMENT</span>
<span class="s2"> JOIN </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"> ON</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK = ZCLOUDSHAREDCOMMENT.ZCOMMENTEDASSET</span>
<span class="s2"> OR</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK = ZCLOUDSHAREDCOMMENT.ZLIKEDASSET</span>
<span class="s2"> """</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="p">)</span>
<span class="c1"># order of results</span>
<span class="c1"># 0: ZGENERICASSET.ZUUID, -- UUID of the photo</span>
<span class="c1"># 1: ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a "like"</span>
<span class="c1"># 1: ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a &quot;like&quot;</span>
<span class="c1"># 2: ZCLOUDSHAREDCOMMENT.ZCOMMENTDATE, -- date of comment</span>
<span class="c1"># 3: ZCLOUDSHAREDCOMMENT.ZCOMMENTTEXT, -- text of comment</span>
<span class="c1"># 4: ZCLOUDSHAREDCOMMENT.ZCOMMENTERHASHEDPERSONID, -- hashed ID of person who made comment/like</span>
<span class="c1"># 5: ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user's) comment</span>
<span class="c1"># 5: ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user&#39;s) comment</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
@@ -334,7 +335,7 @@
<span class="n">is_like</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]][</span><span class="s2">"full_name"</span><span class="p">]</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]][</span><span class="s2">&quot;full_name&quot;</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="kc">None</span>
@@ -348,20 +349,20 @@
<span class="k">try</span><span class="p">:</span>
<span class="n">db_comments</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"likes"</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">"comments"</span><span class="p">:</span> <span class="p">[]}</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;likes&quot;</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">&quot;comments&quot;</span><span class="p">:</span> <span class="p">[]}</span>
<span class="n">db_comments</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
<span class="k">if</span> <span class="n">is_like</span><span class="p">:</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">"likes"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">LikeInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">))</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">LikeInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">text</span><span class="p">:</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">"comments"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">CommentInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">CommentInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span>
<span class="c1"># sort results</span>
<span class="k">for</span> <span class="n">uuid</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"likes"</span><span class="p">]:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"likes"</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"comments"</span><span class="p">]:</span>
<span class="n">value</span><span class="p">[</span><span class="s2">"comments"</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;comments&quot;</span><span class="p">]:</span>
<span class="n">value</span><span class="p">[</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
@@ -401,7 +402,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

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photosdb.photosdb - osxphotos 0.56.7 documentation</title>
<title>osxphotos.photosdb.photosdb - osxphotos 0.60.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -218,7 +218,6 @@
<span class="kn">from</span> <span class="nn">unicodedata</span> <span class="kn">import</span> <span class="n">normalize</span>
<span class="kn">import</span> <span class="nn">bitmath</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">rich</span> <span class="kn">import</span> <span class="nb">print</span>
<span class="kn">from</span> <span class="nn">.._constants</span> <span class="kn">import</span> <span class="p">(</span>
@@ -259,6 +258,7 @@
<span class="kn">from</span> <span class="nn">..sqlite_utils</span> <span class="kn">import</span> <span class="n">sqlite_db_is_locked</span><span class="p">,</span> <span class="n">sqlite_open_ro</span>
<span class="kn">from</span> <span class="nn">..utils</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">_check_file_exists</span><span class="p">,</span>
<span class="n">is_macos</span><span class="p">,</span>
<span class="n">get_macos_version</span><span class="p">,</span>
<span class="n">get_last_library_path</span><span class="p">,</span>
<span class="n">noop</span><span class="p">,</span>
@@ -266,6 +266,9 @@
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">.photosdb_utils</span> <span class="kn">import</span> <span class="n">get_db_model_version</span><span class="p">,</span> <span class="n">get_db_version</span>
<span class="k">if</span> <span class="n">is_macos</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;osxphotos&quot;</span><span class="p">)</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotosDB&quot;</span><span class="p">]</span>
@@ -315,8 +318,8 @@
<span class="c1"># Check OS version</span>
<span class="n">system</span> <span class="o">=</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span><span class="p">()</span>
<span class="p">(</span><span class="n">ver</span><span class="p">,</span> <span class="n">major</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">get_macos_version</span><span class="p">()</span>
<span class="k">if</span> <span class="n">system</span> <span class="o">!=</span> <span class="s2">&quot;Darwin&quot;</span> <span class="ow">or</span> <span class="p">((</span><span class="n">ver</span><span class="p">,</span> <span class="n">major</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">_TESTED_OS_VERSIONS</span><span class="p">):</span>
<span class="p">(</span><span class="n">ver</span><span class="p">,</span> <span class="n">major</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">get_macos_version</span><span class="p">()</span> <span class="k">if</span> <span class="n">is_macos</span> <span class="k">else</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="n">system</span> <span class="o">==</span> <span class="s2">&quot;Darwin&quot;</span> <span class="ow">and</span> <span class="p">((</span><span class="n">ver</span><span class="p">,</span> <span class="n">major</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">_TESTED_OS_VERSIONS</span><span class="p">):</span>
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;WARNING: This module has only been tested with macOS versions &quot;</span>
<span class="sa">f</span><span class="s2">&quot;[</span><span class="si">{</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s1">.</span><span class="si">{</span><span class="n">m</span><span class="si">}</span><span class="s1">&#39;</span> <span class="k">for</span> <span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span> <span class="ow">in</span> <span class="n">_TESTED_OS_VERSIONS</span><span class="p">)</span><span class="si">}</span><span class="s2">]: &quot;</span>
@@ -481,6 +484,9 @@
<span class="c1"># key is Z_PK of ZMOMENT table and values are the moment info</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_moment_pk</span> <span class="o">=</span> <span class="p">{}</span>
<span class="c1"># Dict to hold data on imports for Photos &lt;= 4</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_import_group</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;dbfile = </span><span class="si">{</span><span class="n">dbfile</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">dbfile</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
@@ -710,7 +716,8 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">album_info_shared</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return list of AlbumInfo objects for each shared album in the photos database</span>
<span class="sd"> only valid for Photos 5; on Photos &lt;= 4, prints warning and returns empty list&quot;&quot;&quot;</span>
<span class="sd"> only valid for Photos 5; on Photos &lt;= 4, prints warning and returns empty list</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># if _dbalbum_details[key][&quot;cloudownerhashedpersonid&quot;] is not None, then it&#39;s a shared album</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info_shared</span>
@@ -737,7 +744,8 @@
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">albums_shared</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return list of shared albums found in photos database</span>
<span class="sd"> only valid for Photos 5; on Photos &lt;= 4, prints warning and returns empty list&quot;&quot;&quot;</span>
<span class="sd"> only valid for Photos 5; on Photos &lt;= 4, prints warning and returns empty list</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># Could be more than one album with same name</span>
<span class="c1"># Right now, they are treated as same album and photos are combined from albums with same name</span>
@@ -1005,10 +1013,10 @@
<span class="s2">&quot;cloudlibrarystate&quot;</span><span class="p">:</span> <span class="n">album</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span>
<span class="s2">&quot;cloudidentifier&quot;</span><span class="p">:</span> <span class="n">album</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span>
<span class="s2">&quot;intrash&quot;</span><span class="p">:</span> <span class="kc">False</span> <span class="k">if</span> <span class="n">album</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;cloudlocalstate&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5</span>
<span class="s2">&quot;cloudownerfirstname&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5</span>
<span class="s2">&quot;cloudownderlastname&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5</span>
<span class="s2">&quot;cloudownerhashedpersonid&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5</span>
<span class="s2">&quot;cloudlocalstate&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5+</span>
<span class="s2">&quot;cloudownerfirstname&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5+</span>
<span class="s2">&quot;cloudownderlastname&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5+</span>
<span class="s2">&quot;cloudownerhashedpersonid&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="c1"># Photos 5+</span>
<span class="s2">&quot;folderUuid&quot;</span><span class="p">:</span> <span class="n">album</span><span class="p">[</span><span class="mi">5</span><span class="p">],</span>
<span class="s2">&quot;albumType&quot;</span><span class="p">:</span> <span class="n">album</span><span class="p">[</span><span class="mi">6</span><span class="p">],</span>
<span class="s2">&quot;albumSubclass&quot;</span><span class="p">:</span> <span class="n">album</span><span class="p">[</span><span class="mi">7</span><span class="p">],</span>
@@ -1239,9 +1247,8 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span>
<span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">+</span> <span class="n">TIME_DELTA</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes the date is invalid or null</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;imageTimeZoneOffsetSeconds&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span>
@@ -1253,8 +1260,8 @@
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 as image date</span>
<span class="n">imagedate</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="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
@@ -1349,12 +1356,13 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;momentID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">28</span><span class="p">]</span>
<span class="c1"># Init cloud details that will be filled in later if cloud asset</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudAssetGUID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 5</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudLocalState&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 5</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudAssetGUID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 5+</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudLocalState&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 5+</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudLibraryState&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudStatus&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudAvailable&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;incloud&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudMasterGUID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 5+</span>
<span class="c1"># associated RAW image info</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;has_raw&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">row</span><span class="p">[</span><span class="mi">25</span><span class="p">]</span> <span class="o">==</span> <span class="mi">7</span> <span class="k">else</span> <span class="kc">False</span>
@@ -2215,8 +2223,8 @@
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes imageDate is invalid or null so use 1 Jan 1970 in UTC as image date (#1014)</span>
<span class="n">imagedate</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="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
@@ -2317,6 +2325,7 @@
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;cloudLibraryState&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 4</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;cloudStatus&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 4</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;cloudAvailable&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Photos 4</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;cloudMasterGUID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># reverse geolocation info</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;reverse_geolocation&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">25</span><span class="p">]</span>
@@ -2563,7 +2572,8 @@
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;&quot;&quot; SELECT</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID,</span>
<span class="s2"> ZCLOUDMASTER.ZCLOUDLOCALSTATE</span>
<span class="s2"> ZCLOUDMASTER.ZCLOUDLOCALSTATE,</span>
<span class="s2"> ZCLOUDMASTER.ZCLOUDMASTERGUID</span>
<span class="s2"> FROM ZCLOUDMASTER, </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"></span>
<span class="s2"> WHERE </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZMASTER = ZCLOUDMASTER.Z_PK &quot;&quot;&quot;</span>
<span class="p">)</span>
@@ -2572,6 +2582,7 @@
<span class="k">if</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudLocalState&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;incloud&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span> <span class="k">else</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;cloudMasterGUID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># get information about associted RAW images</span>
<span class="c1"># RAW images have ZDATASTORESUBTYPE = 17</span>
@@ -2795,8 +2806,8 @@
<span class="c1"># save raw time stamp valu</span>
<span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span> <span class="o">+</span> <span class="s2">&quot;_timestamp&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span><span class="p">]</span>
<span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">moment_date</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes imageDate is invalid or null so use 1 Jan 1970 in UTC as image date</span>
<span class="n">moment_date</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="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
<span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span> <span class="o">+</span> <span class="s2">&quot;_timestamp&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">date_stamp</span>
@@ -3244,7 +3255,6 @@
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span></div>
<span class="c1"># TODO: add to docs and test</span>
<div class="viewcode-block" id="PhotosDB.photos_by_uuid"><a class="viewcode-back" href="../../../reference.html#osxphotos.PhotosDB.photos_by_uuid">[docs]</a> <span class="k">def</span> <span class="nf">photos_by_uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuids</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns a list of photos with UUID in uuids.</span>
<span class="sd"> Does not generate error if invalid or missing UUID passed.</span>
@@ -3257,14 +3267,11 @@
<span class="sd"> Returns:</span>
<span class="sd"> list of PhotoInfo instance for photo with UUID matching uuid or [] if no match</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="n">uuids</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">photos</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">PhotoInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">uuid</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">]))</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># ignore missing/invlaid UUID</span>
<span class="k">pass</span>
<span class="k">return</span> <span class="n">photos</span></div>
<span class="k">return</span> <span class="p">[</span>
<span class="n">PhotoInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">uuid</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">])</span>
<span class="k">for</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="n">uuids</span>
<span class="k">if</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span>
<span class="p">]</span></div>
<div class="viewcode-block" id="PhotosDB.query"><a class="viewcode-back" href="../../../reference.html#osxphotos.PhotosDB.query">[docs]</a> <span class="k">def</span> <span class="nf">query</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">QueryOptions</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">PhotoInfo</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Run a query against PhotosDB to extract the photos based on user supplied options</span>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.phototemplate - osxphotos 0.56.7 documentation</title>
<title>osxphotos.phototemplate - osxphotos 0.60.0 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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.60.0 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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.0 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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -218,7 +218,6 @@
<span class="kn">from</span> <span class="nn">.datetime_formatter</span> <span class="kn">import</span> <span class="n">DateTimeFormatter</span>
<span class="kn">from</span> <span class="nn">.exiftool</span> <span class="kn">import</span> <span class="n">ExifToolCaching</span>
<span class="kn">from</span> <span class="nn">.path_utils</span> <span class="kn">import</span> <span class="n">sanitize_dirname</span><span class="p">,</span> <span class="n">sanitize_filename</span><span class="p">,</span> <span class="n">sanitize_pathpart</span>
<span class="kn">from</span> <span class="nn">.text_detection</span> <span class="kn">import</span> <span class="n">detect_text</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">expand_and_validate_filepath</span><span class="p">,</span> <span class="n">load_function</span><span class="p">,</span> <span class="n">uuid_to_shortuuid</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
@@ -482,6 +481,8 @@
<span class="o">+</span> <span class="s2">&quot;e.g. join(): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; &#39;abc&#39;.&quot;</span><span class="p">,</span>
<span class="s2">&quot;append(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Append x to list of values, e.g. append(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepend(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Prepend x to list of values, e.g. prepend(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;d&#39;, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;appends(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Append s[tring] Append x to each value of list of values, e.g. appends(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;ad&#39;, &#39;bd&#39;, &#39;cd&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepends(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Prepend s[tring] x to each value of list of values, e.g. prepends(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;da&#39;, &#39;db&#39;, &#39;dc&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;remove(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Remove x from list of values, e.g. remove(b): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;a&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;slice(start:stop:step)&quot;</span><span class="p">:</span> <span class="s2">&quot;Slice list using same semantics as Python&#39;s list slicing, &quot;</span>
<span class="o">+</span> <span class="s2">&quot;e.g. slice(1:3): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;c&#39;]; slice(1:4:2): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;d&#39;]; &quot;</span>
@@ -740,6 +741,8 @@
<span class="c1"># process field arguments</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">field_arg</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span><span class="o">.</span><span class="n">value</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">field_arg</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># process delim</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">delim</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
@@ -1204,15 +1207,17 @@
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unknown filter: </span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">filter_</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">&quot;split&quot;</span><span class="p">,</span>
<span class="s2">&quot;chop&quot;</span><span class="p">,</span>
<span class="s2">&quot;chomp&quot;</span><span class="p">,</span>
<span class="s2">&quot;append&quot;</span><span class="p">,</span>
<span class="s2">&quot;appends&quot;</span><span class="p">,</span>
<span class="s2">&quot;chomp&quot;</span><span class="p">,</span>
<span class="s2">&quot;chop&quot;</span><span class="p">,</span>
<span class="s2">&quot;filter&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepend&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepends&quot;</span><span class="p">,</span>
<span class="s2">&quot;remove&quot;</span><span class="p">,</span>
<span class="s2">&quot;slice&quot;</span><span class="p">,</span>
<span class="s2">&quot;split&quot;</span><span class="p">,</span>
<span class="s2">&quot;sslice&quot;</span><span class="p">,</span>
<span class="s2">&quot;filter&quot;</span><span class="p">,</span>
<span class="p">]</span> <span class="ow">and</span> <span class="p">(</span><span class="n">args</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2"> requires arguments&quot;</span><span class="p">)</span>
@@ -1229,15 +1234,13 @@
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;braces&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;{&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;}&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;parens&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;(&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;(</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;brackets&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;[&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;]&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;[</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">]&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;shell_quote&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;split&quot;</span><span class="p">:</span>
<span class="c1"># split on delimiter</span>
<span class="n">delim</span> <span class="o">=</span> <span class="n">args</span>
<span class="k">if</span> <span class="n">delim</span><span class="p">:</span>
<span class="k">if</span> <span class="n">delim</span> <span class="o">:=</span> <span class="n">args</span><span class="p">:</span>
<span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
<span class="n">new_values</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">delim</span><span class="p">))</span>
@@ -1248,15 +1251,15 @@
<span class="c1"># chop off characters from the end</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">chop</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chop: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chop: </span><span class="si">{</span><span class="n">args</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="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[:</span><span class="o">-</span><span class="n">chop</span><span class="p">]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chop</span> <span class="k">else</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;chomp&quot;</span><span class="p">:</span>
<span class="c1"># chop off characters from the beginning</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">chomp</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chomp: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chomp: </span><span class="si">{</span><span class="n">args</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="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[</span><span class="n">chomp</span><span class="p">:]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chomp</span> <span class="k">else</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;autosplit&quot;</span><span class="p">:</span>
<span class="c1"># try to split keyword strings automatically</span>
@@ -1291,6 +1294,12 @@
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;prepend&quot;</span><span class="p">:</span>
<span class="c1"># prepend value to list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">args</span><span class="p">]</span> <span class="o">+</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;appends&quot;</span><span class="p">:</span>
<span class="c1"># append value to each item in list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">v</span><span class="si">}{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;prepends&quot;</span><span class="p">:</span>
<span class="c1"># prepend value to each item in list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">args</span><span class="si">}{</span><span class="n">v</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;remove&quot;</span><span class="p">:</span>
<span class="c1"># remove value from list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span> <span class="k">if</span> <span class="n">v</span> <span class="o">!=</span> <span class="n">args</span><span class="p">]</span>
@@ -1340,7 +1349,7 @@
<span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">vals</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="sa">f</span><span class="s2">&quot;comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</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="n">predicate_is_true</span> <span class="o">=</span> <span class="kc">False</span>
@@ -1539,7 +1548,7 @@
<span class="n">subfield</span> <span class="o">=</span> <span class="n">subfield</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
<span class="k">if</span> <span class="n">subfield</span> <span class="ow">in</span> <span class="n">exifdict</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">exifdict</span><span class="p">[</span><span class="n">subfield</span><span class="p">]</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</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">values</span><span class="p">,</span> <span class="nb">list</span><span class="p">)</span> <span class="k">else</span> <span class="n">values</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">values</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nb">list</span><span class="p">)</span> <span class="k">else</span> <span class="p">[</span><span class="n">values</span><span class="p">]</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="c1"># sanitize directory names if needed</span>
@@ -1869,7 +1878,7 @@
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unhandled template value: </span><span class="si">{</span><span class="n">field</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">def</span> <span class="nf">get_place_value</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_place_value</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="c1"># noqa: F821</span>
<span class="sd">&quot;&quot;&quot;Get the value of a &#39;place&#39; field by attribute</span>
<span class="sd"> Args:</span>

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.placeinfo - osxphotos 0.51.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.placeinfo - osxphotos 0.60.0 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.60.0 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.60.0 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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -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,13 +195,13 @@
</div>
<article role="main">
<h1>Source code for osxphotos.placeinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">""" </span>
<span></span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> PlaceInfo class</span>
<span class="sd"> Provides reverse geolocation info for photos </span>
<span class="sd"> </span>
<span class="sd"> See https://developer.apple.com/documentation/corelocation/clplacemark</span>
<span class="sd"> for additional documentation on reverse geolocation data</span>
<span class="sd">"""</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span> <span class="c1"># pylint: disable=syntax-error</span>
@@ -211,27 +212,27 @@
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"PLRevGeoLocationInfo"</span><span class="p">,</span>
<span class="s2">"PLRevGeoMapItem"</span><span class="p">,</span>
<span class="s2">"PLRevGeoMapItemAdditionalPlaceInfo"</span><span class="p">,</span>
<span class="s2">"CNPostalAddress"</span><span class="p">,</span>
<span class="s2">"PlaceInfo"</span><span class="p">,</span>
<span class="s2">"PlaceInfo4"</span><span class="p">,</span>
<span class="s2">"PlaceInfo5"</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoLocationInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoMapItem&quot;</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoMapItemAdditionalPlaceInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;CNPostalAddress&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo4&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo5&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="c1"># postal address information, returned by PlaceInfo.address</span>
<span class="n">PostalAddress</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span>
<span class="s2">"PostalAddress"</span><span class="p">,</span>
<span class="s2">&quot;PostalAddress&quot;</span><span class="p">,</span>
<span class="p">[</span>
<span class="s2">"street"</span><span class="p">,</span>
<span class="s2">"sub_locality"</span><span class="p">,</span>
<span class="s2">"city"</span><span class="p">,</span>
<span class="s2">"sub_administrative_area"</span><span class="p">,</span>
<span class="s2">"state_province"</span><span class="p">,</span>
<span class="s2">"postal_code"</span><span class="p">,</span>
<span class="s2">"country"</span><span class="p">,</span>
<span class="s2">"iso_country_code"</span><span class="p">,</span>
<span class="s2">&quot;street&quot;</span><span class="p">,</span>
<span class="s2">&quot;sub_locality&quot;</span><span class="p">,</span>
<span class="s2">&quot;city&quot;</span><span class="p">,</span>
<span class="s2">&quot;sub_administrative_area&quot;</span><span class="p">,</span>
<span class="s2">&quot;state_province&quot;</span><span class="p">,</span>
<span class="s2">&quot;postal_code&quot;</span><span class="p">,</span>
<span class="s2">&quot;country&quot;</span><span class="p">,</span>
<span class="s2">&quot;iso_country_code&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="p">)</span>
@@ -240,38 +241,39 @@
<span class="c1"># PLRevGeoLocationInfo.mapInfo.sortedPlaceInfos</span>
<span class="c1"># field 18 is combined bodies of water (ocean + inland_water)</span>
<span class="c1"># and maps to Photos &lt;= 4, RKPlace.type == 44</span>
<span class="c1"># (Photos &lt;= 4 doesn't have ocean or inland_water types)</span>
<span class="c1"># The fields named "field0", etc. appear to be unused</span>
<span class="c1"># (Photos &lt;= 4 doesn&#39;t have ocean or inland_water types)</span>
<span class="c1"># The fields named &quot;field0&quot;, etc. appear to be unused</span>
<span class="n">PlaceNames</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span>
<span class="s2">"PlaceNames"</span><span class="p">,</span>
<span class="s2">&quot;PlaceNames&quot;</span><span class="p">,</span>
<span class="p">[</span>
<span class="s2">"field0"</span><span class="p">,</span>
<span class="s2">"country"</span><span class="p">,</span> <span class="c1"># The name of the country associated with the placemark.</span>
<span class="s2">"state_province"</span><span class="p">,</span> <span class="c1"># administrativeArea, The state or province associated with the placemark.</span>
<span class="s2">"sub_administrative_area"</span><span class="p">,</span> <span class="c1"># Additional administrative area information for the placemark.</span>
<span class="s2">"city"</span><span class="p">,</span> <span class="c1"># locality, The city associated with the placemark.</span>
<span class="s2">"field5"</span><span class="p">,</span>
<span class="s2">"additional_city_info"</span><span class="p">,</span> <span class="c1"># subLocality, Additional city-level information for the placemark.</span>
<span class="s2">"ocean"</span><span class="p">,</span> <span class="c1"># The name of the ocean associated with the placemark.</span>
<span class="s2">"area_of_interest"</span><span class="p">,</span> <span class="c1"># areasOfInterest, The relevant areas of interest associated with the placemark.</span>
<span class="s2">"inland_water"</span><span class="p">,</span> <span class="c1"># The name of the inland water body associated with the placemark.</span>
<span class="s2">"field10"</span><span class="p">,</span>
<span class="s2">"region"</span><span class="p">,</span> <span class="c1"># The geographic region associated with the placemark.</span>
<span class="s2">"sub_throughfare"</span><span class="p">,</span> <span class="c1"># Additional street-level information for the placemark.</span>
<span class="s2">"field13"</span><span class="p">,</span>
<span class="s2">"postal_code"</span><span class="p">,</span> <span class="c1"># The postal code associated with the placemark.</span>
<span class="s2">"field15"</span><span class="p">,</span>
<span class="s2">"field16"</span><span class="p">,</span>
<span class="s2">"street_address"</span><span class="p">,</span> <span class="c1"># throughfare, The street address associated with the placemark.</span>
<span class="s2">"body_of_water"</span><span class="p">,</span> <span class="c1"># RKPlace.type == 44, appears to be any body of water (ocean or inland)</span>
<span class="s2">&quot;field0&quot;</span><span class="p">,</span>
<span class="s2">&quot;country&quot;</span><span class="p">,</span> <span class="c1"># The name of the country associated with the placemark.</span>
<span class="s2">&quot;state_province&quot;</span><span class="p">,</span> <span class="c1"># administrativeArea, The state or province associated with the placemark.</span>
<span class="s2">&quot;sub_administrative_area&quot;</span><span class="p">,</span> <span class="c1"># Additional administrative area information for the placemark.</span>
<span class="s2">&quot;city&quot;</span><span class="p">,</span> <span class="c1"># locality, The city associated with the placemark.</span>
<span class="s2">&quot;field5&quot;</span><span class="p">,</span>
<span class="s2">&quot;additional_city_info&quot;</span><span class="p">,</span> <span class="c1"># subLocality, Additional city-level information for the placemark.</span>
<span class="s2">&quot;ocean&quot;</span><span class="p">,</span> <span class="c1"># The name of the ocean associated with the placemark.</span>
<span class="s2">&quot;area_of_interest&quot;</span><span class="p">,</span> <span class="c1"># areasOfInterest, The relevant areas of interest associated with the placemark.</span>
<span class="s2">&quot;inland_water&quot;</span><span class="p">,</span> <span class="c1"># The name of the inland water body associated with the placemark.</span>
<span class="s2">&quot;field10&quot;</span><span class="p">,</span>
<span class="s2">&quot;region&quot;</span><span class="p">,</span> <span class="c1"># The geographic region associated with the placemark.</span>
<span class="s2">&quot;sub_throughfare&quot;</span><span class="p">,</span> <span class="c1"># Additional street-level information for the placemark.</span>
<span class="s2">&quot;field13&quot;</span><span class="p">,</span>
<span class="s2">&quot;postal_code&quot;</span><span class="p">,</span> <span class="c1"># The postal code associated with the placemark.</span>
<span class="s2">&quot;field15&quot;</span><span class="p">,</span>
<span class="s2">&quot;field16&quot;</span><span class="p">,</span>
<span class="s2">&quot;street_address&quot;</span><span class="p">,</span> <span class="c1"># throughfare, The street address associated with the placemark.</span>
<span class="s2">&quot;body_of_water&quot;</span><span class="p">,</span> <span class="c1"># RKPlace.type == 44, appears to be any body of water (ocean or inland)</span>
<span class="p">],</span>
<span class="p">)</span>
<span class="c1"># The following classes represent Photo Library Reverse Geolocation Info as stored</span>
<span class="c1"># in ZADDITIONALASSETATTRIBUTES.ZREVERSELOCATIONDATA</span>
<span class="c1"># These classes are used by bpylist.archiver to unarchive the serialized objects</span>
<span class="k">class</span> <span class="nc">PLRevGeoLocationInfo</span><span class="p">:</span>
<span class="sd">"""The top level reverse geolocation object"""</span>
<span class="sd">&quot;&quot;&quot;The top level reverse geolocation object&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -299,14 +301,14 @@
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">"addressString"</span><span class="p">,</span>
<span class="s2">"countryCode"</span><span class="p">,</span>
<span class="s2">"isHome"</span><span class="p">,</span>
<span class="s2">"compoundNames"</span><span class="p">,</span>
<span class="s2">"compoundSecondaryNames"</span><span class="p">,</span>
<span class="s2">"version"</span><span class="p">,</span>
<span class="s2">"geoServiceProvider"</span><span class="p">,</span>
<span class="s2">"postalAddress"</span><span class="p">,</span>
<span class="s2">&quot;addressString&quot;</span><span class="p">,</span>
<span class="s2">&quot;countryCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;isHome&quot;</span><span class="p">,</span>
<span class="s2">&quot;compoundNames&quot;</span><span class="p">,</span>
<span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">,</span>
<span class="s2">&quot;version&quot;</span><span class="p">,</span>
<span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">,</span>
<span class="s2">&quot;postalAddress&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
@@ -314,31 +316,31 @@
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"addressString: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">addressString</span><span class="si">}</span><span class="s2">, countryCode: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">countryCode</span><span class="si">}</span><span class="s2">, isHome: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">isHome</span><span class="si">}</span><span class="s2">, mapItem: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">mapItem</span><span class="si">}</span><span class="s2">, postalAddress: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">postalAddress</span><span class="si">}</span><span class="s2">"</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;addressString: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">addressString</span><span class="si">}</span><span class="s2">, countryCode: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">countryCode</span><span class="si">}</span><span class="s2">, isHome: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">isHome</span><span class="si">}</span><span class="s2">, mapItem: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">mapItem</span><span class="si">}</span><span class="s2">, postalAddress: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">postalAddress</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"addressString"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">addressString</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"countryCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">countryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"mapItem"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">mapItem</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"isHome"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">isHome</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"compoundNames"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"compoundSecondaryNames"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundSecondaryNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"version"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">version</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"geoServiceProvider"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">geoServiceProvider</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"postalAddress"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">postalAddress</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;addressString&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">addressString</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;countryCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">countryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;mapItem&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">mapItem</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;isHome&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">isHome</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;compoundNames&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundSecondaryNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;version&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">version</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">geoServiceProvider</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;postalAddress&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">postalAddress</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">addressString</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"addressString"</span><span class="p">)</span>
<span class="n">countryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"countryCode"</span><span class="p">)</span>
<span class="n">mapItem</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"mapItem"</span><span class="p">)</span>
<span class="n">isHome</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"isHome"</span><span class="p">)</span>
<span class="n">compoundNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"compoundNames"</span><span class="p">)</span>
<span class="n">compoundSecondaryNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"compoundSecondaryNames"</span><span class="p">)</span>
<span class="n">version</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"version"</span><span class="p">)</span>
<span class="n">geoServiceProvider</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"geoServiceProvider"</span><span class="p">)</span>
<span class="n">postalAddress</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"postalAddress"</span><span class="p">)</span>
<span class="n">addressString</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;addressString&quot;</span><span class="p">)</span>
<span class="n">countryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;countryCode&quot;</span><span class="p">)</span>
<span class="n">mapItem</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;mapItem&quot;</span><span class="p">)</span>
<span class="n">isHome</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;isHome&quot;</span><span class="p">)</span>
<span class="n">compoundNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;compoundNames&quot;</span><span class="p">)</span>
<span class="n">compoundSecondaryNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">)</span>
<span class="n">version</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;version&quot;</span><span class="p">)</span>
<span class="n">geoServiceProvider</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">)</span>
<span class="n">postalAddress</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;postalAddress&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">(</span>
<span class="n">addressString</span><span class="p">,</span>
<span class="n">countryCode</span><span class="p">,</span>
@@ -353,7 +355,7 @@
<span class="k">class</span> <span class="nc">PLRevGeoMapItem</span><span class="p">:</span>
<span class="sd">"""Stores the list of place names, organized by area"""</span>
<span class="sd">&quot;&quot;&quot;Stores the list of place names, organized by area&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sortedPlaceInfos</span><span class="p">,</span> <span class="n">finalPlaceInfos</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">sortedPlaceInfos</span>
@@ -362,7 +364,7 @@
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"sortedPlaceInfos"</span><span class="p">,</span> <span class="s2">"finalPlaceInfos"</span><span class="p">]</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">,</span> <span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
@@ -372,23 +374,23 @@
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">place</span><span class="p">)</span> <span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">]</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">place</span><span class="p">)</span> <span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">]</span>
<span class="k">return</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">"finalPlaceInfos: </span><span class="si">{</span><span class="n">finalPlaceInfos</span><span class="si">}</span><span class="s2">, sortedPlaceInfos: </span><span class="si">{</span><span class="n">sortedPlaceInfos</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;finalPlaceInfos: </span><span class="si">{</span><span class="n">finalPlaceInfos</span><span class="si">}</span><span class="s2">, sortedPlaceInfos: </span><span class="si">{</span><span class="n">sortedPlaceInfos</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"sortedPlaceInfos"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"finalPlaceInfos"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"sortedPlaceInfos"</span><span class="p">)</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"finalPlaceInfos"</span><span class="p">)</span>
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">)</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoMapItem</span><span class="p">(</span><span class="n">sortedPlaceInfos</span><span class="p">,</span> <span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">:</span>
<span class="sd">"""Additional info about individual places"""</span>
<span class="sd">&quot;&quot;&quot;Additional info about individual places&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">area</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">placeType</span><span class="p">,</span> <span class="n">dominantOrderType</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">area</span> <span class="o">=</span> <span class="n">area</span>
@@ -399,35 +401,35 @@
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"area"</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">,</span> <span class="s2">"placeType"</span><span class="p">,</span> <span class="s2">"dominantOrderType"</span><span class="p">]</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;area&quot;</span><span class="p">,</span> <span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;placeType&quot;</span><span class="p">,</span> <span class="s2">&quot;dominantOrderType&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"area: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">area</span><span class="si">}</span><span class="s2">, name: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">, placeType: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">placeType</span><span class="si">}</span><span class="s2">"</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;area: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">area</span><span class="si">}</span><span class="s2">, name: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">, placeType: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">placeType</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"area"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">area</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"name"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"placeType"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">placeType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"dominantOrderType"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">dominantOrderType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;area&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">area</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;placeType&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">placeType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;dominantOrderType&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">dominantOrderType</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">area</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"area"</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"name"</span><span class="p">)</span>
<span class="n">placeType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"placeType"</span><span class="p">)</span>
<span class="n">dominantOrderType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"dominantOrderType"</span><span class="p">)</span>
<span class="n">area</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;area&quot;</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">)</span>
<span class="n">placeType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;placeType&quot;</span><span class="p">)</span>
<span class="n">dominantOrderType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;dominantOrderType&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">(</span>
<span class="n">area</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">placeType</span><span class="p">,</span> <span class="n">dominantOrderType</span>
<span class="p">)</span>
<span class="k">class</span> <span class="nc">CNPostalAddress</span><span class="p">:</span>
<span class="sd">"""postal address for the reverse geolocation info"""</span>
<span class="sd">&quot;&quot;&quot;postal address for the reverse geolocation info&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -453,14 +455,14 @@
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">"_ISOCountryCode"</span><span class="p">,</span>
<span class="s2">"_city"</span><span class="p">,</span>
<span class="s2">"_country"</span><span class="p">,</span>
<span class="s2">"_postalCode"</span><span class="p">,</span>
<span class="s2">"_state"</span><span class="p">,</span>
<span class="s2">"_street"</span><span class="p">,</span>
<span class="s2">"_subAdministrativeArea"</span><span class="p">,</span>
<span class="s2">"_subLocality"</span><span class="p">,</span>
<span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;_city&quot;</span><span class="p">,</span>
<span class="s2">&quot;_country&quot;</span><span class="p">,</span>
<span class="s2">&quot;_postalCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;_state&quot;</span><span class="p">,</span>
<span class="s2">&quot;_street&quot;</span><span class="p">,</span>
<span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">,</span>
<span class="s2">&quot;_subLocality&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
@@ -468,7 +470,7 @@
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="k">return</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="nb">map</span><span class="p">(</span>
<span class="nb">str</span><span class="p">,</span>
<span class="p">[</span>
@@ -486,25 +488,25 @@
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_ISOCountryCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_ISOCountryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_country"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_country</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_city"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_city</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_postalCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_postalCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_state"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_state</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_street"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_street</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_subAdministrativeArea"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subAdministrativeArea</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_subLocality"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subLocality</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_ISOCountryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_country&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_country</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_city&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_city</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_postalCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_postalCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_state&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_state</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_street&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_street</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subAdministrativeArea</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_subLocality&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subLocality</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">_ISOCountryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_ISOCountryCode"</span><span class="p">)</span>
<span class="n">_country</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_country"</span><span class="p">)</span>
<span class="n">_city</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_city"</span><span class="p">)</span>
<span class="n">_postalCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_postalCode"</span><span class="p">)</span>
<span class="n">_state</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_state"</span><span class="p">)</span>
<span class="n">_street</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_street"</span><span class="p">)</span>
<span class="n">_subAdministrativeArea</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_subAdministrativeArea"</span><span class="p">)</span>
<span class="n">_subLocality</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_subLocality"</span><span class="p">)</span>
<span class="n">_ISOCountryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">)</span>
<span class="n">_country</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_country&quot;</span><span class="p">)</span>
<span class="n">_city</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_city&quot;</span><span class="p">)</span>
<span class="n">_postalCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_postalCode&quot;</span><span class="p">)</span>
<span class="n">_state</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_state&quot;</span><span class="p">)</span>
<span class="n">_street</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_street&quot;</span><span class="p">)</span>
<span class="n">_subAdministrativeArea</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">)</span>
<span class="n">_subLocality</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_subLocality&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">CNPostalAddress</span><span class="p">(</span>
<span class="n">_ISOCountryCode</span><span class="p">,</span>
@@ -519,12 +521,12 @@
<span class="c1"># register the classes with bpylist.archiver</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"CNPostalAddress"</span><span class="p">:</span> <span class="n">CNPostalAddress</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;CNPostalAddress&quot;</span><span class="p">:</span> <span class="n">CNPostalAddress</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">(</span>
<span class="p">{</span><span class="s2">"PLRevGeoMapItemAdditionalPlaceInfo"</span><span class="p">:</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">}</span>
<span class="p">{</span><span class="s2">&quot;PLRevGeoMapItemAdditionalPlaceInfo&quot;</span><span class="p">:</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">}</span>
<span class="p">)</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"PLRevGeoMapItem"</span><span class="p">:</span> <span class="n">PLRevGeoMapItem</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"PLRevGeoLocationInfo"</span><span class="p">:</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;PLRevGeoMapItem&quot;</span><span class="p">:</span> <span class="n">PLRevGeoMapItem</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;PLRevGeoLocationInfo&quot;</span><span class="p">:</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">})</span>
<div class="viewcode-block" id="PlaceInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.PlaceInfo">[docs]</a><span class="k">class</span> <span class="nc">PlaceInfo</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
@@ -560,18 +562,18 @@
<span class="k">class</span> <span class="nc">PlaceInfo4</span><span class="p">(</span><span class="n">PlaceInfo</span><span class="p">):</span>
<span class="sd">"""Reverse geolocation place info for a photo (Photos &lt;= 4)"""</span>
<span class="sd">&quot;&quot;&quot;Reverse geolocation place info for a photo (Photos &lt;= 4)&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">place_names</span><span class="p">,</span> <span class="n">country_code</span><span class="p">):</span>
<span class="sd">"""place_names: list of place name tuples in ascending order by area</span>
<span class="sd">&quot;&quot;&quot;place_names: list of place name tuples in ascending order by area</span>
<span class="sd"> tuple fields are: modelID, place name, place type, area, e.g.</span>
<span class="sd"> [(5, "St James's Park", 45, 0),</span>
<span class="sd"> (4, 'Westminster', 16, 22097376),</span>
<span class="sd"> (3, 'London', 4, 1596146816),</span>
<span class="sd"> (2, 'England', 2, 180406091776),</span>
<span class="sd"> (1, 'United Kingdom', 1, 414681432064)]</span>
<span class="sd"> [(5, &quot;St James&#39;s Park&quot;, 45, 0),</span>
<span class="sd"> (4, &#39;Westminster&#39;, 16, 22097376),</span>
<span class="sd"> (3, &#39;London&#39;, 4, 1596146816),</span>
<span class="sd"> (2, &#39;England&#39;, 2, 180406091776),</span>
<span class="sd"> (1, &#39;United Kingdom&#39;, 1, 414681432064)]</span>
<span class="sd"> country_code: two letter country code for the country</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place_names</span> <span class="o">=</span> <span class="n">place_names</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_country_code</span> <span class="o">=</span> <span class="n">country_code</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_place_info</span><span class="p">()</span>
@@ -610,14 +612,14 @@
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_place_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Process place_names to set self._name and self._names"""</span>
<span class="sd">&quot;&quot;&quot;Process place_names to set self._name and self._names&quot;&quot;&quot;</span>
<span class="n">places</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place_names</span>
<span class="c1"># build a dictionary where key is placetype</span>
<span class="n">places_dict</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">places</span><span class="p">:</span>
<span class="c1"># places in format:</span>
<span class="c1"># [(5, "St James's Park", 45, 0), ]</span>
<span class="c1"># [(5, &quot;St James&#39;s Park&quot;, 45, 0), ]</span>
<span class="c1"># 0: modelID</span>
<span class="c1"># 1: name</span>
<span class="c1"># 2: type</span>
@@ -659,7 +661,7 @@
<span class="c1"># build the name as it appears in Photos</span>
<span class="c1"># the length of the name is at most 3 fields and appears to be based on available</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by ',')</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by &#39;,&#39;)</span>
<span class="c1"># always has country if available then either area of interest and city OR</span>
<span class="c1"># city and state</span>
<span class="c1"># e.g. 4, 2, 1 OR 8, 4, 1</span>
@@ -683,61 +685,61 @@
<span class="k">if</span> <span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
<span class="n">name_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">name_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">""</span> <span class="k">else</span> <span class="kc">None</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">name_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">"PlaceInfo("</span> <span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">='</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">'"</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">")"</span>
<span class="k">return</span> <span class="s2">&quot;PlaceInfo(&quot;</span> <span class="o">+</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">&#39;&quot;</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">PlaceInfo5</span><span class="p">(</span><span class="n">PlaceInfo</span><span class="p">):</span>
<span class="sd">"""Reverse geolocation place info for a photo (Photos &gt;= 5)"""</span>
<span class="sd">&quot;&quot;&quot;Reverse geolocation place info for a photo (Photos &gt;= 5)&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">revgeoloc_bplist</span><span class="p">):</span>
<span class="sd">"""revgeoloc_bplist: a binary plist blob containing</span>
<span class="sd"> a serialized PLRevGeoLocationInfo object"""</span>
<span class="sd">&quot;&quot;&quot;revgeoloc_bplist: a binary plist blob containing</span>
<span class="sd"> a serialized PLRevGeoLocationInfo object&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_bplist</span> <span class="o">=</span> <span class="n">revgeoloc_bplist</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span> <span class="o">=</span> <span class="n">archiver</span><span class="o">.</span><span class="n">unarchive</span><span class="p">(</span><span class="n">revgeoloc_bplist</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_place_info</span><span class="p">()</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">address_str</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns the postal address as a string"""</span>
<span class="sd">&quot;&quot;&quot;returns the postal address as a string&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">addressString</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">country_code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns the country code"""</span>
<span class="sd">&quot;&quot;&quot;returns the country code&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">countryCode</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">ishome</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns True if place is user's home address"""</span>
<span class="sd">&quot;&quot;&quot;returns True if place is user&#39;s home address&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">isHome</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns local place name"""</span>
<span class="sd">&quot;&quot;&quot;returns local place name&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns PlaceNames tuple with detailed reverse geolocation place names"""</span>
<span class="sd">&quot;&quot;&quot;returns PlaceNames tuple with detailed reverse geolocation place names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_names</span>
<span class="nd">@property</span>
@@ -762,7 +764,7 @@
<span class="k">return</span> <span class="n">postal_address</span>
<span class="k">def</span> <span class="nf">_process_place_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Process sortedPlaceInfos to set self._name and self._names"""</span>
<span class="sd">&quot;&quot;&quot;Process sortedPlaceInfos to set self._name and self._names&quot;&quot;&quot;</span>
<span class="n">places</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">mapItem</span><span class="o">.</span><span class="n">sortedPlaceInfos</span>
<span class="c1"># build a dictionary where key is placetype</span>
@@ -795,20 +797,20 @@
<span class="c1"># build the name as it appears in Photos</span>
<span class="c1"># the length of the name is variable and appears to be based on available</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by ',')</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by &#39;,&#39;)</span>
<span class="c1"># 8: area_of_interest</span>
<span class="c1"># 11: region (I've only seen this applied to islands)</span>
<span class="c1"># 11: region (I&#39;ve only seen this applied to islands)</span>
<span class="c1"># 4: locality / city</span>
<span class="c1"># 2: administrative area (state/province)</span>
<span class="c1"># 1: country</span>
<span class="c1"># 9: inland_water</span>
<span class="c1"># 7: ocean</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="p">[</span>
<span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="p">[</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">8</span><span class="p">],</span> <span class="c1"># area of interest</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span> <span class="c1"># region (I've only seen this applied to islands)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span> <span class="c1"># region (I&#39;ve only seen this applied to islands)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="c1"># locality / city</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="c1"># administrative area (state/province)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="c1"># country</span>
@@ -818,7 +820,7 @@
<span class="k">if</span> <span class="n">p</span> <span class="ow">and</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="p">]</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">""</span> <span class="k">else</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)):</span>
@@ -831,23 +833,23 @@
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"ishome"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">"address_str"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">"address"</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">),</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;ishome&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">&quot;address_str&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">&quot;address&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">"PlaceInfo("</span> <span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">='</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">'"</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">")"</span>
<span class="k">return</span> <span class="s2">&quot;PlaceInfo(&quot;</span> <span class="o">+</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">&#39;&quot;</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"ishome"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">"address_str"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">"address"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;ishome&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">&quot;address_str&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">&quot;address&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</article>
@@ -886,7 +888,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

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.queryoptions - osxphotos 0.56.7 documentation</title>
<title>osxphotos.queryoptions - osxphotos 0.58.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -199,12 +199,17 @@
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">datetime</span>
<span class="kn">import</span> <span class="nn">io</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">asdict</span><span class="p">,</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span>
<span class="kn">import</span> <span class="nn">bitmath</span>
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="n">UUID_PATTERN</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;QueryOptions&quot;</span><span class="p">,</span> <span class="s2">&quot;query_options_from_kwargs&quot;</span><span class="p">,</span> <span class="s2">&quot;IncompatibleQueryOptions&quot;</span><span class="p">]</span>
@@ -224,7 +229,7 @@
<span class="sd"> added_before: search for photos added before a given date</span>
<span class="sd"> added_in_last: search for photos added in last X datetime.timedelta</span>
<span class="sd"> album: list of album names to search for</span>
<span class="sd"> burst_photos: search for burst photos</span>
<span class="sd"> burst_photos: include all associated burst photos for photos in query results</span>
<span class="sd"> burst: search for burst photos</span>
<span class="sd"> cloudasset: search for photos that are managed by iCloud</span>
<span class="sd"> deleted_only: search only for deleted photos</span>
@@ -390,7 +395,12 @@
<span class="k">def</span> <span class="nf">query_options_from_kwargs</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">QueryOptions</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Validate query options and create a QueryOptions instance&quot;&quot;&quot;</span>
<span class="sd">&quot;&quot;&quot;Validate query options and create a QueryOptions instance.</span>
<span class="sd"> Note: this will block on stdin if uuid_from_file is set to &quot;-&quot;</span>
<span class="sd"> so it is best to call function before creating the PhotosDB instance</span>
<span class="sd"> so that the validation of query options can happen before the database</span>
<span class="sd"> is loaded.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># sanity check input args</span>
<span class="n">nonexclusive</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;added_after&quot;</span><span class="p">,</span>
@@ -446,6 +456,8 @@
<span class="p">(</span><span class="s2">&quot;slow_mo&quot;</span><span class="p">,</span> <span class="s2">&quot;not_slow_mo&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;time_lapse&quot;</span><span class="p">,</span> <span class="s2">&quot;not_time_lapse&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;deleted&quot;</span><span class="p">,</span> <span class="s2">&quot;not_deleted&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;deleted&quot;</span><span class="p">,</span> <span class="s2">&quot;deleted_only&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;deleted_only&quot;</span><span class="p">,</span> <span class="s2">&quot;not_deleted&quot;</span><span class="p">),</span>
<span class="p">]</span>
<span class="c1"># TODO: add option to validate requiring at least one query arg</span>
<span class="k">for</span> <span class="n">arg</span><span class="p">,</span> <span class="n">not_arg</span> <span class="ow">in</span> <span class="n">exclusive</span><span class="p">:</span>
@@ -453,7 +465,7 @@
<span class="n">arg</span> <span class="o">=</span> <span class="n">arg</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;_&quot;</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">)</span>
<span class="n">not_arg</span> <span class="o">=</span> <span class="n">not_arg</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;_&quot;</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">IncompatibleQueryOptions</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;--</span><span class="si">{</span><span class="n">arg</span><span class="si">}</span><span class="s2"> and --</span><span class="si">{</span><span class="n">not_arg</span><span class="si">}</span><span class="s2"> are mutually exclusive&quot;</span>
<span class="sa">f</span><span class="s2">&quot;Incompatible query options: --</span><span class="si">{</span><span class="n">arg</span><span class="si">}</span><span class="s2"> and --</span><span class="si">{</span><span class="n">not_arg</span><span class="si">}</span><span class="s2"> are mutually exclusive&quot;</span>
<span class="p">)</span>
<span class="c1"># some options like title can be specified multiple times</span>
@@ -473,24 +485,27 @@
<span class="n">include_movies</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># load UUIDs if necessary and append to any uuids passed with --uuid</span>
<span class="n">uuid</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">uuids</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;uuid&quot;</span><span class="p">,</span> <span class="p">[]))</span> <span class="c1"># Click option is a tuple</span>
<span class="k">if</span> <span class="n">uuid_from_file</span> <span class="o">:=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;uuid_from_file&quot;</span><span class="p">):</span>
<span class="n">uuid_list</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;uuid&quot;</span><span class="p">,</span> <span class="p">[]))</span> <span class="c1"># Click option is a tuple</span>
<span class="n">uuid_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">load_uuid_from_file</span><span class="p">(</span><span class="n">uuid_from_file</span><span class="p">))</span>
<span class="n">uuid</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">uuid_list</span><span class="p">)</span>
<span class="n">uuids</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">load_uuid_from_file</span><span class="p">(</span><span class="n">uuid_from_file</span><span class="p">))</span>
<span class="n">uuids</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">uuids</span><span class="p">)</span>
<span class="n">query_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">field</span><span class="o">.</span><span class="n">name</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">fields</span><span class="p">(</span><span class="n">QueryOptions</span><span class="p">)]</span>
<span class="n">query_dict</span> <span class="o">=</span> <span class="p">{</span><span class="n">field</span><span class="p">:</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">field</span><span class="p">)</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">query_fields</span><span class="p">}</span>
<span class="n">query_dict</span><span class="p">[</span><span class="s2">&quot;photos&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">include_photos</span>
<span class="n">query_dict</span><span class="p">[</span><span class="s2">&quot;movies&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">include_movies</span>
<span class="n">query_dict</span><span class="p">[</span><span class="s2">&quot;uuid&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">uuid</span>
<span class="n">query_dict</span><span class="p">[</span><span class="s2">&quot;uuid&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">uuids</span>
<span class="n">query_dict</span><span class="p">[</span><span class="s2">&quot;function&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;query_function&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">QueryOptions</span><span class="p">(</span><span class="o">**</span><span class="n">query_dict</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">load_uuid_from_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Load UUIDs from file. Does not validate UUIDs.</span>
<span class="sd"> Format is 1 UUID per line, any line beginning with # is ignored.</span>
<span class="sd"> Whitespace is stripped.</span>
<span class="k">def</span> <span class="nf">load_uuid_from_file</span><span class="p">(</span><span class="n">filename</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Load UUIDs from file.</span>
<span class="sd"> Does not validate UUIDs but does validate that the UUIDs are in the correct format.</span>
<span class="sd"> Format is 1 UUID per line, any line beginning with # is ignored.</span>
<span class="sd"> Whitespace is stripped.</span>
<span class="sd"> Arguments:</span>
<span class="sd"> filename: file name of the file containing UUIDs</span>
@@ -500,17 +515,44 @@
<span class="sd"> Raises:</span>
<span class="sd"> FileNotFoundError if file does not exist</span>
<span class="sd"> ValueError if UUID is not in correct format</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">filename</span> <span class="o">==</span> <span class="s2">&quot;-&quot;</span><span class="p">:</span>
<span class="k">return</span> <span class="n">_load_uuid_from_stream</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">stdin</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">is_file</span><span class="p">():</span>
<span class="k">raise</span> <span class="ne">FileNotFoundError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Could not find file </span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s2">&quot;r&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="k">return</span> <span class="n">_load_uuid_from_stream</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_load_uuid_from_stream</span><span class="p">(</span><span class="n">stream</span><span class="p">:</span> <span class="n">io</span><span class="o">.</span><span class="n">IOBase</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Load UUIDs from stream.</span>
<span class="sd"> Does not validate UUIDs but does validate that the UUIDs are in the correct format.</span>
<span class="sd"> Format is 1 UUID per line, any line beginning with # is ignored.</span>
<span class="sd"> Whitespace is stripped.</span>
<span class="sd"> Arguments:</span>
<span class="sd"> filename: file name of the file containing UUIDs</span>
<span class="sd"> Returns:</span>
<span class="sd"> list of UUIDs or empty list of no UUIDs in file</span>
<span class="sd"> Raises:</span>
<span class="sd"> ValueError if UUID is not in correct format</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">uuid</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s2">&quot;r&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">uuid_file</span><span class="p">:</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">uuid_file</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="ow">and</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="s2">&quot;#&quot;</span><span class="p">:</span>
<span class="n">uuid</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">stream</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="ow">and</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="s2">&quot;#&quot;</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;^</span><span class="si">{</span><span class="n">UUID_PATTERN</span><span class="si">}</span><span class="s2">$&quot;</span><span class="p">,</span> <span class="n">line</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid UUID: </span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
<span class="n">uuid</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">return</span> <span class="n">uuid</span>
</pre></div>
</article>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.scoreinfo - osxphotos 0.56.4 documentation</title>
<title>osxphotos.scoreinfo - osxphotos 0.60.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.60.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.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.searchinfo - osxphotos 0.56.4 documentation</title>
<title>osxphotos.searchinfo - osxphotos 0.58.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" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 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.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.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">
@@ -160,7 +160,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
OSXPhotos python API
====================
OSXPhotos Python Reference
==========================
.. automodule:: osxphotos
:members:

View File

@@ -58,6 +58,8 @@ Valid filters are:
* `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'].
* `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
* `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
* `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().
@@ -359,7 +361,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.56.7'
- The osxphotos version, e.g. '0.60.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

View File

@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.56.7',
VERSION: '0.60.2',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.56.7 documentation</title>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -212,9 +212,9 @@
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-library">
<span id="cmdoption-osxphotos-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -329,7 +329,7 @@ See <cite>osxphotos help timewarp</cite> for more information.</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-uuid-from-file">
<span class="sig-name descname"><span class="pre">--uuid-from-file</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;FILE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-uuid-from-file" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored.</p>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored. If FILE is -, read UUIDs from stdin.</p>
</dd></dl>
<dl class="std option">
@@ -778,9 +778,9 @@ See <cite>osxphotos help timewarp</cite> for more information.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-albums-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-albums-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-albums-library">
<span id="cmdoption-osxphotos-albums-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-albums-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -796,6 +796,101 @@ See <cite>osxphotos help timewarp</cite> for more information.</p>
<dd><p>Optional argument(s)</p>
</dd></dl>
</section>
<section id="osxphotos-batch-edit">
<h3>batch-edit<a class="headerlink" href="#osxphotos-batch-edit" title="Permalink to this heading">#</a></h3>
<p>Batch edit photo metadata such as title, description, keywords, etc.
Operates on currently selected photos.</p>
<p>Select one or more photos in Photos then run this command to edit the metadata.</p>
<p>For example:</p>
<div class="line-block">
<div class="line">osxphotos batch-edit </div>
<div class="line">verbose </div>
<div class="line">title “California vacation 2023 {created.year}-{created.dd}-{created.mm} {counter:03d}” </div>
<div class="line">description “{place.name}” </div>
<div class="line">keyword “Family” keyword “Travel”</div>
</div>
<p>This will set the title to “California vacation 2023 2023-02-20 001”, and so on,
the description to the reverse geolocation place name,
and add the keywords “Family” and “Travel”.</p>
<p>title, description, and keyword may be any valid template string.
See <a class="reference external" href="https://rhettbull.github.io/osxphotos/template_help.html">https://rhettbull.github.io/osxphotos/template_help.html</a>
or <cite>osxphotos docs</cite> for more information on the osxphotos template system.</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos batch-edit <span class="o">[</span>OPTIONS<span class="o">]</span>
</pre></div>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-title">
<span class="sig-name descname"><span class="pre">--title</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;TITLE_TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-title" title="Permalink to this definition">#</a></dt>
<dd><p>Set title of photo.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-description">
<span class="sig-name descname"><span class="pre">--description</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;DESCRIPTION_TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-description" title="Permalink to this definition">#</a></dt>
<dd><p>Set description of photo.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-keyword">
<span class="sig-name descname"><span class="pre">--keyword</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;KEYWORD_TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-keyword" title="Permalink to this definition">#</a></dt>
<dd><p>Add keywords to photo. May be specified multiple times.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-replace-keywords">
<span class="sig-name descname"><span class="pre">--replace-keywords</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-replace-keywords" title="Permalink to this definition">#</a></dt>
<dd><p>When specified with keyword, replace existing keywords. Default is to add to existing keywords.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-location">
<span class="sig-name descname"><span class="pre">--location</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;LATITUDE</span> <span class="pre">LONGITUDE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-location" title="Permalink to this definition">#</a></dt>
<dd><p>Set location of photo. Must be specified as a pair of numbers with latitude in the range -90 to 90 and longitude in the range -180 to 180.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-dry-run">
<span class="sig-name descname"><span class="pre">--dry-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-dry-run" title="Permalink to this definition">#</a></dt>
<dd><p>Dont actually change anything.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-undo">
<span class="sig-name descname"><span class="pre">--undo</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-undo" title="Permalink to this definition">#</a></dt>
<dd><p>Restores photo metadata to what it was prior to the last batch edit. May be combined with dry-run.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-V">
<span id="cmdoption-osxphotos-batch-edit-v"></span><span id="cmdoption-osxphotos-batch-edit-verbose"></span><span class="sig-name descname"><span class="pre">-V</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--verbose</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-V" title="Permalink to this definition">#</a></dt>
<dd><p>Print verbose output; may be specified multiple times for more verbose output.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-timestamp">
<span class="sig-name descname"><span class="pre">--timestamp</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-timestamp" title="Permalink to this definition">#</a></dt>
<dd><p>Add time stamp to verbose output</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-theme">
<span class="sig-name descname"><span class="pre">--theme</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;THEME&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-theme" title="Permalink to this definition">#</a></dt>
<dd><p>Specify the color theme to use for output. Valid themes are dark, light, mono, and plain. Defaults to dark or light depending on system dark mode setting.</p>
<dl class="field-list simple">
<dt class="field-odd">Options<span class="colon">:</span></dt>
<dd class="field-odd"><p>dark | light | mono | plain</p>
</dd>
</dl>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-library">
<span id="cmdoption-osxphotos-batch-edit-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
</section>
<section id="osxphotos-diff">
<h3>diff<a class="headerlink" href="#osxphotos-diff" title="Permalink to this heading">#</a></h3>
@@ -824,9 +919,9 @@ library specified by db to the database specified by DB2</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-diff-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-diff-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-diff-library">
<span id="cmdoption-osxphotos-diff-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-diff-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -871,14 +966,16 @@ library specified by db to the database specified by DB2</p>
<section id="osxphotos-dump">
<h3>dump<a class="headerlink" href="#osxphotos-dump" title="Permalink to this heading">#</a></h3>
<p>Print list of all photos &amp; associated info from the Photos library.</p>
<p>NOTE: dump is DEPRECATED and will be removed in a future release.
Use <cite>osxphotos query</cite> instead.</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos dump <span class="o">[</span>OPTIONS<span class="o">]</span> <span class="o">[</span>PHOTOS_LIBRARY<span class="o">]</span>...
</pre></div>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-dump-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-dump-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-dump-library">
<span id="cmdoption-osxphotos-dump-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-dump-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -887,18 +984,18 @@ library specified by db to the database specified by DB2</p>
<dd><p>Print output in JSON format.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-dump-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-dump-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-dump-deleted-only">
<span class="sig-name descname"><span class="pre">--deleted-only</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-dump-deleted-only" title="Permalink to this definition">#</a></dt>
<dd><p>Include only photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-dump-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-dump-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-dump-f">
<span id="cmdoption-osxphotos-dump-field"></span><span class="sig-name descname"><span class="pre">-f</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--field</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;FIELD</span> <span class="pre">TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-dump-f" title="Permalink to this definition">#</a></dt>
@@ -1060,9 +1157,9 @@ with <cite>osxphotos export</cite> in the future will work correctly and not unn
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-library">
<span id="cmdoption-osxphotos-exiftool-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<p class="rubric">Arguments</p>
@@ -1095,9 +1192,9 @@ to modify this behavior.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-library">
<span id="cmdoption-osxphotos-export-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -1163,7 +1260,7 @@ to modify this behavior.</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-uuid-from-file">
<span class="sig-name descname"><span class="pre">--uuid-from-file</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;FILE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-uuid-from-file" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored.</p>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored. If FILE is -, read UUIDs from stdin.</p>
</dd></dl>
<dl class="std option">
@@ -1592,18 +1689,18 @@ to modify this behavior.</p>
<dd><p>Run function to filter photos. Use this in format: query-function filename.py::function where filename.py is a python file youve created and function is the name of the function in the python file you want to call. Your function will be passed a list of PhotoInfo objects and is expected to return a filtered list of PhotoInfo objects. You may use more than one function by repeating the query-function option with a different value. Your query function will be called after all other query options have been evaluated. See https://github.com/RhetTbull/osxphotos/blob/master/examples/query_function.py for example of how to use this option.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-deleted-only">
<span class="sig-name descname"><span class="pre">--deleted-only</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-deleted-only" title="Permalink to this definition">#</a></dt>
<dd><p>Include only photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-update">
<span class="sig-name descname"><span class="pre">--update</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-update" title="Permalink to this definition">#</a></dt>
@@ -2158,6 +2255,12 @@ to modify this behavior.</p>
<dd><p>Execute SQL_STATEMENT against export database and print results.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-migrate-photos-library">
<span class="sig-name descname"><span class="pre">--migrate-photos-library</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-migrate-photos-library" title="Permalink to this definition">#</a></dt>
<dd><p>Migrate the export database to use the specified Photos library. Use this if you have moved your Photos library to a new location or computer and want to keep using the same export database. This will update the UUIDs in the export database to match the new Photos library.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-export-dir">
<span class="sig-name descname"><span class="pre">--export-dir</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;export_dir&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-export-dir" title="Permalink to this definition">#</a></dt>
@@ -2182,10 +2285,21 @@ to modify this behavior.</p>
<dd><p>Add time stamp to verbose output</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-theme">
<span class="sig-name descname"><span class="pre">--theme</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;THEME&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-theme" title="Permalink to this definition">#</a></dt>
<dd><p>Specify the color theme to use for output. Valid themes are dark, light, mono, and plain. Defaults to dark or light depending on system dark mode setting.</p>
<dl class="field-list simple">
<dt class="field-odd">Options<span class="colon">:</span></dt>
<dd class="field-odd"><p>dark | light | mono | plain</p>
</dd>
</dl>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-dry-run">
<span class="sig-name descname"><span class="pre">--dry-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-dry-run" title="Permalink to this definition">#</a></dt>
<dd><p>Run in dry-run mode (dont actually update files), e.g. for use with update-signatures.</p>
<dd><p>Run in dry-run mode (dont actually update files); for example, use with update-signatures or migrate-photos-library.</p>
</dd></dl>
<p class="rubric">Arguments</p>
@@ -2394,9 +2508,9 @@ to modify this behavior.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-info-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-info-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-info-library">
<span id="cmdoption-osxphotos-info-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-info-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2448,9 +2562,9 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-inspect-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-inspect-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-inspect-library">
<span id="cmdoption-osxphotos-inspect-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-inspect-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
</section>
@@ -2483,9 +2597,9 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-keywords-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-keywords-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-keywords-library">
<span id="cmdoption-osxphotos-keywords-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-keywords-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2510,9 +2624,9 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-labels-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-labels-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-labels-library">
<span id="cmdoption-osxphotos-labels-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-labels-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2557,9 +2671,9 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-orphans-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-orphans-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-orphans-library">
<span id="cmdoption-osxphotos-orphans-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-orphans-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2594,9 +2708,9 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-persons-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-persons-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-persons-library">
<span id="cmdoption-osxphotos-persons-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-persons-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2621,9 +2735,9 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-places-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-places-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-places-library">
<span id="cmdoption-osxphotos-places-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-places-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2650,14 +2764,15 @@ If the same query option is provided multiple times, they are treated as
<p>For example:</p>
<p>osxphotos query person “John Doe” person “Jane Doe” keyword “vacation”</p>
<p>will return all photos with either person of (“John Doe” OR “Jane Doe”) AND keyword of “vacation”</p>
<p>If not query options are provided, all photos in the library will be returned.</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos query <span class="o">[</span>OPTIONS<span class="o">]</span> <span class="o">[</span>PHOTOS_LIBRARY<span class="o">]</span>...
</pre></div>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-query-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-library">
<span id="cmdoption-osxphotos-query-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-query-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -2711,7 +2826,7 @@ If the same query option is provided multiple times, they are treated as
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-uuid-from-file">
<span class="sig-name descname"><span class="pre">--uuid-from-file</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;FILE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-query-uuid-from-file" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored.</p>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored. If FILE is -, read UUIDs from stdin.</p>
</dd></dl>
<dl class="std option">
@@ -3140,18 +3255,18 @@ If the same query option is provided multiple times, they are treated as
<dd><p>Run function to filter photos. Use this in format: query-function filename.py::function where filename.py is a python file youve created and function is the name of the function in the python file you want to call. Your function will be passed a list of PhotoInfo objects and is expected to return a filtered list of PhotoInfo objects. You may use more than one function by repeating the query-function option with a different value. Your query function will be called after all other query options have been evaluated. See https://github.com/RhetTbull/osxphotos/blob/master/examples/query_function.py for example of how to use this option.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-deleted-only">
<span class="sig-name descname"><span class="pre">--deleted-only</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-deleted-only" title="Permalink to this definition">#</a></dt>
<dd><p>Include only photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-add-to-album">
<span class="sig-name descname"><span class="pre">--add-to-album</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;ALBUM&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-query-add-to-album" title="Permalink to this definition">#</a></dt>
@@ -3192,9 +3307,9 @@ If the same query option is provided multiple times, they are treated as
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-repl-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-library">
<span id="cmdoption-osxphotos-repl-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-repl-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -3248,7 +3363,7 @@ If the same query option is provided multiple times, they are treated as
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-uuid-from-file">
<span class="sig-name descname"><span class="pre">--uuid-from-file</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;FILE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-repl-uuid-from-file" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored.</p>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored. If FILE is -, read UUIDs from stdin.</p>
</dd></dl>
<dl class="std option">
@@ -3677,18 +3792,18 @@ If the same query option is provided multiple times, they are treated as
<dd><p>Run function to filter photos. Use this in format: query-function filename.py::function where filename.py is a python file youve created and function is the name of the function in the python file you want to call. Your function will be passed a list of PhotoInfo objects and is expected to return a filtered list of PhotoInfo objects. You may use more than one function by repeating the query-function option with a different value. Your query function will be called after all other query options have been evaluated. See https://github.com/RhetTbull/osxphotos/blob/master/examples/query_function.py for example of how to use this option.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-deleted-only">
<span class="sig-name descname"><span class="pre">--deleted-only</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-deleted-only" title="Permalink to this definition">#</a></dt>
<dd><p>Include only photos from the Recently Deleted folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-deleted">
<span class="sig-name descname"><span class="pre">--deleted</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-deleted" title="Permalink to this definition">#</a></dt>
<dd><p>Include photos from the Recently Deleted folder.</p>
</dd></dl>
</section>
<section id="osxphotos-run">
<h3>run<a class="headerlink" href="#osxphotos-run" title="Permalink to this heading">#</a></h3>
@@ -3717,6 +3832,41 @@ Any args are made available to the python file.</p>
<dd><p>Optional argument(s)</p>
</dd></dl>
</section>
<section id="osxphotos-show">
<h3>show<a class="headerlink" href="#osxphotos-show" title="Permalink to this heading">#</a></h3>
<p>Show photo, album, or folder in Photos from UUID_OR_NAME</p>
<p>Examples:</p>
<p>osxphotos show 12345678-1234-1234-1234-123456789012</p>
<p>osxphotos show “My Album”</p>
<p>osxphotos show “My Folder”</p>
<p>osxphotos show IMG_1234.JPG</p>
<p>show can also be used to show a photo exported with <cite>osxphotos export</cite>:</p>
<p>osxphotos show /path/to/exported/photo.jpg</p>
<p>In this case, the UUID_OR_NAME is the path to the exported photo and osxphotos
will attempt to find the export database to match the photo to the original in
Photos. If your export database is not in the default location in the root of the
export directory, this will not work.</p>
<p>Notes:</p>
<p>This command requires Photos library version 5 or higher.
Currently this command cannot be used to show subfolders in Photos.</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos show <span class="o">[</span>OPTIONS<span class="o">]</span> UUID_OR_NAME
</pre></div>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-show-library">
<span id="cmdoption-osxphotos-show-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-show-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<p class="rubric">Arguments</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-show-arg-UUID_OR_NAME">
<span id="cmdoption-osxphotos-show-arg-uuid-or-name"></span><span class="sig-name descname"><span class="pre">UUID_OR_NAME</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-show-arg-UUID_OR_NAME" title="Permalink to this definition">#</a></dt>
<dd><p>Required argument</p>
</dd></dl>
</section>
<section id="osxphotos-snap">
<h3>snap<a class="headerlink" href="#osxphotos-snap" title="Permalink to this heading">#</a></h3>
@@ -3730,9 +3880,9 @@ uses /private/tmp/osxphotos_snapshots</p>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-snap-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-snap-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-snap-library">
<span id="cmdoption-osxphotos-snap-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-snap-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
</section>
@@ -3884,7 +4034,7 @@ two different computers, you can export the metadata to a shared folder.</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-uuid-from-file">
<span class="sig-name descname"><span class="pre">--uuid-from-file</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;FILE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-sync-uuid-from-file" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored.</p>
<dd><p>Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines preceded with # are ignored. If FILE is -, read UUIDs from stdin.</p>
</dd></dl>
<dl class="std option">
@@ -3995,18 +4145,6 @@ two different computers, you can export the metadata to a shared folder.</p>
<dd><p>Search for photos not marked hidden.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-shared">
<span class="sig-name descname"><span class="pre">--shared</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-shared" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos in shared iCloud album (Photos 5+ only).</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-not-shared">
<span class="sig-name descname"><span class="pre">--not-shared</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-not-shared" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos not in shared iCloud album (Photos 5+ only).</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-burst">
<span class="sig-name descname"><span class="pre">--burst</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-burst" title="Permalink to this definition">#</a></dt>
@@ -4314,9 +4452,9 @@ two different computers, you can export the metadata to a shared folder.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-sync-db" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-library">
<span id="cmdoption-osxphotos-sync-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-sync-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify path to Photos library. If not provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
<dl class="std option">
@@ -4422,6 +4560,18 @@ See Timewarp Overview below for additional information.</p>
<dd><p>Set timezone for selected photos as offset from UTC. Format is one of ±HH:MM, ±H:MM, or ±HHMM. The actual time of the photo is not adjusted which means, somewhat counterintuitively, that the time in the new timezone will be different. For example, if photo has time of 12:00 and timezone of GMT+01:00 and new timezone is specified as timezone +02:00 (one hour ahead of current GMT+01:00 timezone), the photos new time will be 13:00 GMT+02:00, which is equivalent to the old time of 12:00+01:00. This is the same behavior exhibited by Photos when manually adjusting timezone in the Get Info window. See also match-time.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-timewarp-date-added">
<span class="sig-name descname"><span class="pre">--date-added</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;DATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-timewarp-date-added" title="Permalink to this definition">#</a></dt>
<dd><p>Set date/time added for selected photos. This changes the date added or imported date in Photos but does not change the date/time/timezone of the photo itself. This is useful for removing photos from the Recents album, for example if you have imported old scanned photos. Format is YYYY-MM-DD or YYYY-MM-DD HH:MM:SS. If time is not included, midnight is assumed.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-timewarp-date-added-from-photo">
<span class="sig-name descname"><span class="pre">--date-added-from-photo</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-timewarp-date-added-from-photo" title="Permalink to this definition">#</a></dt>
<dd><p>Set date/time added for selected photos to the date/time the photo was taken. This changes the date added or imported date in Photos but does not change the date/time/timezone of the photo itself.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-timewarp-i">
<span id="cmdoption-osxphotos-timewarp-inspect"></span><span class="sig-name descname"><span class="pre">-i</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--inspect</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-timewarp-i" title="Permalink to this definition">#</a></dt>
@@ -4708,6 +4858,7 @@ Commands:
<li><a class="reference internal" href="#osxphotos-about">about</a></li>
<li><a class="reference internal" href="#osxphotos-add-locations">add-locations</a></li>
<li><a class="reference internal" href="#osxphotos-albums">albums</a></li>
<li><a class="reference internal" href="#osxphotos-batch-edit">batch-edit</a></li>
<li><a class="reference internal" href="#osxphotos-diff">diff</a></li>
<li><a class="reference internal" href="#osxphotos-docs">docs</a></li>
<li><a class="reference internal" href="#osxphotos-dump">dump</a></li>
@@ -4728,6 +4879,7 @@ Commands:
<li><a class="reference internal" href="#osxphotos-query">query</a></li>
<li><a class="reference internal" href="#osxphotos-repl">repl</a></li>
<li><a class="reference internal" href="#osxphotos-run">run</a></li>
<li><a class="reference internal" href="#osxphotos-show">show</a></li>
<li><a class="reference internal" href="#osxphotos-snap">snap</a></li>
<li><a class="reference internal" href="#osxphotos-sync">sync</a></li>
<li><a class="reference internal" href="#osxphotos-theme">theme</a></li>

View File

@@ -4,7 +4,7 @@
<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-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.56.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.60.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" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -159,7 +159,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -437,6 +437,20 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-d">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
--date-added
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
--date-added-from-photo
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added-from-photo">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
@@ -450,39 +464,43 @@
--db
<ul>
<li><a href="cli.html#cmdoption-osxphotos-db">osxphotos command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-library">osxphotos command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-db">osxphotos-albums command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-albums-library">osxphotos-albums command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-db">osxphotos-diff command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-db">osxphotos-dump command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-diff-library">osxphotos-diff command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db">osxphotos-exiftool command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-dump-library">osxphotos-dump command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-db">osxphotos-export command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-library">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-info-db">osxphotos-info command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-export-library">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-db">osxphotos-inspect command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-info-library">osxphotos-info command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-keywords-db">osxphotos-keywords command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-inspect-library">osxphotos-inspect command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-db">osxphotos-labels command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-keywords-library">osxphotos-keywords command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-db">osxphotos-orphans command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-labels-library">osxphotos-labels command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-db">osxphotos-persons command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-orphans-library">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-places-db">osxphotos-places command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-persons-library">osxphotos-persons command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-db">osxphotos-query command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-places-library">osxphotos-places command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-db">osxphotos-repl command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-query-library">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-snap-db">osxphotos-snap command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-repl-library">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-db">osxphotos-sync command line option</a>
<li><a href="cli.html#cmdoption-osxphotos-show-library">osxphotos-show command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-snap-library">osxphotos-snap command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-library">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
@@ -551,6 +569,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-description">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-description">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-description">osxphotos-export command line option</a>
</li>
@@ -598,6 +618,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-dry-run">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-dry-run">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-dry-run">osxphotos-exiftool command line option</a>
</li>
@@ -1148,6 +1170,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-keyword">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-keyword">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-keyword">osxphotos-export command line option</a>
</li>
@@ -1202,6 +1226,44 @@
--library
<ul>
<li><a href="cli.html#cmdoption-osxphotos-library">osxphotos command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-library">osxphotos-albums command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-library">osxphotos-diff command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-library">osxphotos-dump command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-library">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-library">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-info-library">osxphotos-info command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-library">osxphotos-inspect command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-keywords-library">osxphotos-keywords command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-library">osxphotos-labels command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-library">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-library">osxphotos-persons command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-places-library">osxphotos-places command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-library">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-library">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-show-library">osxphotos-show command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-snap-library">osxphotos-snap command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-library">osxphotos-sync command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-L">osxphotos-timewarp command line option</a>
</li>
</ul></li>
@@ -1248,6 +1310,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-location">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-location">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-location">osxphotos-export command line option</a>
</li>
@@ -1301,6 +1365,13 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-migrate">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
--migrate-photos-library
<ul>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-migrate-photos-library">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
@@ -1492,6 +1563,8 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-not-cloudasset">osxphotos-sync command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-edited
@@ -1522,8 +1595,6 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-not-favorite">osxphotos-sync command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-hdr
@@ -1700,8 +1771,6 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-shared">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
@@ -2011,6 +2080,8 @@
--replace-keywords
<ul>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-replace-keywords">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-replace-keywords">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">osxphotos-export command line option</a>
@@ -2126,8 +2197,6 @@
<li><a href="cli.html#cmdoption-osxphotos-query-shared">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-shared">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
@@ -2248,10 +2317,14 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-theme">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-theme">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-theme">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-theme">osxphotos-exportdb command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-import-theme">osxphotos-import command line option</a>
</li>
@@ -2298,6 +2371,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-timestamp">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-timestamp">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-timestamp">osxphotos-diff command line option</a>
</li>
@@ -2328,6 +2403,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-title">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-title">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-title">osxphotos-export command line option</a>
</li>
@@ -2384,6 +2461,13 @@
<li><a href="cli.html#cmdoption-osxphotos-export-touch-file">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-touch-file">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
--undo
<ul>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-undo">osxphotos-batch-edit command line option</a>
</li>
</ul></li>
<li>
@@ -2513,6 +2597,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-V">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
</li>
@@ -2815,6 +2901,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-V">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
</li>
@@ -2903,12 +2991,12 @@
<li><a href="reference.html#osxphotos.PhotosDB.albums">(osxphotos.PhotosDB property)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotosDB.albums_as_dict">albums_as_dict (osxphotos.PhotosDB property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosDB.albums_shared">albums_shared (osxphotos.PhotosDB property)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotosDB.albums_shared_as_dict">albums_shared_as_dict (osxphotos.PhotosDB property)</a>
</li>
<li><a href="reference.html#osxphotos.AlbumSortOrder">AlbumSortOrder (class in osxphotos)</a>
@@ -2924,9 +3012,15 @@
<li><a href="cli.html#cmdoption-osxphotos-run-arg-ARGS">osxphotos-run command line option</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.ExifTool.asdict">asdict() (osxphotos.ExifTool method)</a>
<li><a href="reference.html#osxphotos.AlbumInfo.asdict">asdict() (osxphotos.AlbumInfo method)</a>
<ul>
<li><a href="reference.html#osxphotos.ExifTool.asdict">(osxphotos.ExifTool method)</a>
</li>
<li><a href="reference.html#osxphotos.FolderInfo.asdict">(osxphotos.FolderInfo method)</a>
</li>
<li><a href="reference.html#osxphotos.ImportInfo.asdict">(osxphotos.ImportInfo method)</a>
</li>
<li><a href="reference.html#osxphotos.MomentInfo.asdict">(osxphotos.MomentInfo method)</a>
</li>
<li><a href="reference.html#osxphotos.PersonInfo.asdict">(osxphotos.PersonInfo method)</a>
@@ -2989,8 +3083,12 @@
<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>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.cloud_guid">cloud_guid (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.cloud_metadata">cloud_metadata (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.cloud_owner_hashed_id">cloud_owner_hashed_id (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.cloudasset">cloudasset (osxphotos.QueryOptions attribute)</a>
</li>
@@ -2999,6 +3097,8 @@
</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.ExportDB.connection">connection (osxphotos.ExportDB property)</a>
</li>
<li><a href="reference.html#osxphotos.ExportOptions.convert_to_jpeg">convert_to_jpeg (osxphotos.ExportOptions attribute)</a>
</li>
@@ -3205,10 +3305,10 @@
</ul></li>
<li><a href="reference.html#osxphotos.FileUtil">FileUtil (class in osxphotos)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.ExportOptions.fileutil">fileutil (osxphotos.ExportOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.FileUtilNoOp">FileUtilNoOp (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate.filter_predicate">filter_predicate() (osxphotos.PhotoTemplate method)</a>
@@ -3220,9 +3320,17 @@
<li><a href="reference.html#osxphotos.PhotosDB.folder_info">folder_info (osxphotos.PhotosDB property)</a>
</li>
<li><a href="reference.html#osxphotos.AlbumInfo.folder_list">folder_list (osxphotos.AlbumInfo property)</a>
<ul>
<li><a href="reference.html#osxphotos.ProjectInfo.folder_list">(osxphotos.ProjectInfo property)</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.AlbumInfo.folder_names">folder_names (osxphotos.AlbumInfo property)</a>
<ul>
<li><a href="reference.html#osxphotos.ProjectInfo.folder_names">(osxphotos.ProjectInfo property)</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.FolderInfo">FolderInfo (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosDB.folders">folders (osxphotos.PhotosDB property)</a>
@@ -3617,9 +3725,11 @@
osxphotos command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-v">--version</a>
</li>
@@ -3809,18 +3919,53 @@
osxphotos-albums command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-albums-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-albums-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
<li>
osxphotos-batch-edit command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-description">--description</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-dry-run">--dry-run</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-keyword">--keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-location">--location</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-replace-keywords">--replace-keywords</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-theme">--theme</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-timestamp">--timestamp</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-title">--title</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-undo">--undo</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">--verbose</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">-V</a>
</li>
</ul></li>
<li>
osxphotos-diff command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-diff-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-diff-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-r">--raw-output</a>
</li>
@@ -3843,7 +3988,7 @@
osxphotos-dump command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-dump-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-dump-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-deleted">--deleted</a>
</li>
@@ -3852,6 +3997,8 @@
<li><a href="cli.html#cmdoption-osxphotos-dump-f">--field</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-dump-print">--print</a>
</li>
@@ -3868,7 +4015,7 @@
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-append">--append</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db-config">--db-config</a>
</li>
@@ -3889,6 +4036,8 @@
<li><a href="cli.html#cmdoption-osxphotos-exiftool-ignore-date-modified">--ignore-date-modified</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-keyword-template">--keyword-template</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-load-config">--load-config</a>
</li>
@@ -3947,7 +4096,7 @@
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-current-name">--current-name</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-export-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-deleted">--deleted</a>
</li>
@@ -4040,6 +4189,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-keyword-template">--keyword-template</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-label">--label</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-limit">--limit</a>
</li>
@@ -4257,12 +4408,16 @@
<li><a href="cli.html#cmdoption-osxphotos-exportdb-last-run">--last-run</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-migrate">--migrate</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-migrate-photos-library">--migrate-photos-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-report">--report</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-save-config">--save-config</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-sql">--sql</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-theme">--theme</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-timestamp">--timestamp</a>
</li>
@@ -4387,26 +4542,30 @@
<li><a href="cli.html#cmdoption-osxphotos-import-arg-FILES">FILES</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-info command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-info-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-info-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-info-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-info-library">--library</a>
</li>
<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
<ul>
<li><a href="cli.html#cmdoption-osxphotos-inspect-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-inspect-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-t">--detect-text</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-T">--template</a>
</li>
@@ -4432,9 +4591,11 @@
osxphotos-keywords command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-keywords-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-keywords-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-keywords-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-keywords-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-keywords-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
@@ -4443,9 +4604,11 @@
osxphotos-labels command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-labels-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-labels-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
@@ -4461,9 +4624,11 @@
osxphotos-orphans command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-orphans-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-orphans-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-export">--export</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-theme">--theme</a>
</li>
@@ -4478,9 +4643,11 @@
osxphotos-persons command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-persons-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-persons-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
@@ -4489,9 +4656,11 @@
osxphotos-places command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-places-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-places-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-places-json">--json</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-places-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-places-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
@@ -4514,7 +4683,7 @@
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-cloudasset">--cloudasset</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-query-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-deleted">--deleted</a>
</li>
@@ -4563,6 +4732,8 @@
<li><a href="cli.html#cmdoption-osxphotos-query-keyword">--keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-label">--label</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-live">--live</a>
</li>
@@ -4697,7 +4868,7 @@
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-cloudasset">--cloudasset</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-repl-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-deleted">--deleted</a>
</li>
@@ -4744,6 +4915,8 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-keyword">--keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-label">--label</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-live">--live</a>
</li>
@@ -4865,13 +5038,26 @@
<li><a href="cli.html#cmdoption-osxphotos-run-arg-ARGS">ARGS</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-run-arg-PYTHON_FILE">PYTHON_FILE</a>
</li>
</ul></li>
<li>
osxphotos-show command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-show-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-show-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-show-arg-UUID_OR_NAME">UUID_OR_NAME</a>
</li>
</ul></li>
<li>
osxphotos-snap command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-snap-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-snap-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-snap-library">--library</a>
</li>
</ul></li>
<li>
@@ -4892,7 +5078,7 @@
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-cloudasset">--cloudasset</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-db">--db</a>
<li><a href="cli.html#cmdoption-osxphotos-sync-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-description">--description</a>
</li>
@@ -4939,6 +5125,8 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-keyword">--keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-label">--label</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-live">--live</a>
</li>
@@ -4997,8 +5185,6 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-not-screenshot">--not-screenshot</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-selfie">--not-selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-shared">--not-shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-slow-mo">--not-slow-mo</a>
</li>
@@ -5031,8 +5217,6 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-selfie">--selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-s">--set</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-shared">--shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-slow-mo">--slow-mo</a>
</li>
@@ -5105,6 +5289,10 @@
<li><a href="cli.html#cmdoption-osxphotos-timewarp-c">--compare-exif</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-d">--date</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added">--date-added</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added-from-photo">--date-added-from-photo</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-D">--date-delta</a>
</li>
@@ -5523,6 +5711,8 @@
<h2>T</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotoInfo.tables">tables() (osxphotos.PhotoInfo method)</a>
</li>
<li><a href="reference.html#osxphotos.SearchInfo.text_found">text_found (osxphotos.SearchInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.time_lapse">time_lapse (osxphotos.PhotoInfo property)</a>
@@ -5537,6 +5727,8 @@
<ul>
<li><a href="reference.html#osxphotos.FolderInfo.title">(osxphotos.FolderInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.ImportInfo.title">(osxphotos.ImportInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.MomentInfo.title">(osxphotos.MomentInfo property)</a>
</li>
@@ -5608,6 +5800,13 @@
<li><a href="reference.html#osxphotos.PhotoInfo.uuid">(osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.uuid">(osxphotos.QueryOptions attribute)</a>
</li>
</ul></li>
<li>
UUID_OR_NAME
<ul>
<li><a href="cli.html#cmdoption-osxphotos-show-arg-UUID_OR_NAME">osxphotos-show command line option</a>
</li>
</ul></li>
</ul></td>

View File

@@ -6,7 +6,7 @@
<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-5.3.0, furo 2022.09.29"/>
<title>osxphotos 0.56.7 documentation</title>
<title>osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="#"><div class="brand">osxphotos 0.60.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="#">
<span class="sidebar-brand-text">osxphotos 0.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -238,6 +238,7 @@
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-about">about</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-add-locations">add-locations</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-albums">albums</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-batch-edit">batch-edit</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-diff">diff</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-docs">docs</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-dump">dump</a></li>
@@ -258,6 +259,7 @@
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-query">query</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-repl">repl</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-run">run</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-show">show</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-snap">snap</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-sync">sync</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-theme">theme</a></li>
@@ -280,8 +282,9 @@
<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><ul>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</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.asdict"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.asdict()</span></code></a></li>
<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>
@@ -306,6 +309,7 @@
</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.connection"><code class="docutils literal notranslate"><span class="pre">ExportDB.connection</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>
@@ -395,6 +399,7 @@
</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.asdict"><code class="docutils literal notranslate"><span class="pre">FolderInfo.asdict()</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>
@@ -402,7 +407,9 @@
</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.asdict"><code class="docutils literal notranslate"><span class="pre">ImportInfo.asdict()</span></code></a></li>
<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>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ImportInfo.title"><code class="docutils literal notranslate"><span class="pre">ImportInfo.title</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>
@@ -447,7 +454,9 @@
<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_guid"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.cloud_guid</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.cloud_owner_hashed_id"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.cloud_owner_hashed_id</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>
@@ -515,6 +524,7 @@
<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.tables"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.tables()</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>
@@ -581,7 +591,11 @@
</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.ProjectInfo"><code class="docutils literal notranslate"><span class="pre">ProjectInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ProjectInfo.folder_list"><code class="docutils literal notranslate"><span class="pre">ProjectInfo.folder_list</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.ProjectInfo.folder_names"><code class="docutils literal notranslate"><span class="pre">ProjectInfo.folder_names</span></code></a></li>
</ul>
</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>

Binary file not shown.

View File

@@ -6,7 +6,7 @@
<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-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos - osxphotos 0.56.7 documentation</title>
<title>OSXPhotos - osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -3,10 +3,10 @@
<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.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" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Reference" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Python Package Overview - osxphotos 0.56.7 documentation</title>
<title>OSXPhotos Python Package Overview - osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<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 current current-page"><a class="current reference internal" href="#">OSXPhotos Python Package Overview</a></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 Reference</a></li>
</ul>
</div>
@@ -392,7 +392,7 @@ as well as <code class="docutils literal notranslate"><span class="pre">{functio
<div class="context">
<span>Next</span>
</div>
<div class="title">OSXPhotos python API</div>
<div class="title">OSXPhotos Python Reference</div>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>

View File

@@ -4,7 +4,7 @@
<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-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.56.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.60.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" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -159,7 +159,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="OSXPhotos Python Package Overview" href="package_overview.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos python API - osxphotos 0.56.7 documentation</title>
<title>OSXPhotos Python Reference - osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<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 current current-page"><a class="current reference internal" href="#">OSXPhotos python API</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -197,29 +197,33 @@
</div>
<article role="main">
<section id="module-osxphotos">
<span id="osxphotos-python-api"></span><h1>OSXPhotos python API<a class="headerlink" href="#module-osxphotos" title="Permalink to this heading">#</a></h1>
<span id="osxphotos-python-reference"></span><h1>OSXPhotos Python Reference<a class="headerlink" href="#module-osxphotos" title="Permalink to this heading">#</a></h1>
<p>__init__.py for osxphotos</p>
<dl class="py class">
<dt class="sig sig-object py" id="osxphotos.AlbumInfo">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">AlbumInfo</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">db</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#AlbumInfo"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.AlbumInfo" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">AlbumInfo</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">db</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#AlbumInfo"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.AlbumInfo" title="Permalink to this definition">#</a></dt>
<dd><p>Info about a specific Album, contains all the details about the album
including folders, photos, etc.</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.AlbumInfo.asdict">
<span class="sig-name descname"><span class="pre">asdict</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#AlbumInfo.asdict"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.AlbumInfo.asdict" title="Permalink to this definition">#</a></dt>
<dd><p>Return album info as a dict; does not include photos</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.AlbumInfo.folder_list">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">folder_list</span></span><a class="headerlink" href="#osxphotos.AlbumInfo.folder_list" title="Permalink to this definition">#</a></dt>
<dd><p>return hierarchical list of folders the album is contained in
as list of FolderInfo objects in form
[“Top level folder”, “sub folder 1”, “sub folder 2”, …]
returns empty list if album is not in any folders</p>
<dd><p>Returns list of FolderInfo objects for each folder the album is contained in
or empty list if album is not in any folders</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.AlbumInfo.folder_names">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">folder_names</span></span><a class="headerlink" href="#osxphotos.AlbumInfo.folder_names" title="Permalink to this definition">#</a></dt>
<dd><p>return hierarchical list of folders the album is contained in
<dd><p>Return hierarchical list of folders the album is contained in
the folder list is in form:
[“Top level folder”, “sub folder 1”, “sub folder 2”, …]
returns empty list if album is not in any folders</p>
or empty list if album is not in any folders</p>
</dd></dl>
<dl class="py property">
@@ -390,7 +394,7 @@ If called in context manager, returns True (execution is delayed until exiting c
<dl class="py class">
<dt class="sig sig-object py" id="osxphotos.ExportDB">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">ExportDB</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">dbfile</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">export_dir</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">ExportDB</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">dbfile</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">export_dir</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB" title="Permalink to this definition">#</a></dt>
<dd><p>Interface to sqlite3 database used to store state information for osxphotos export command</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.close">
@@ -398,41 +402,47 @@ If called in context manager, returns True (execution is delayed until exiting c
<dd><p>close the database connection</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ExportDB.connection">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">connection</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">Connection</span></em><a class="headerlink" href="#osxphotos.ExportDB.connection" title="Permalink to this definition">#</a></dt>
<dd><p>returns sqlite3 connection</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.create_file_record">
<span class="sig-name descname"><span class="pre">create_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Union</span><span class="p"><span class="pre">[</span></span><span class="pre">Path</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.create_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.create_file_record" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">create_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.create_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.create_file_record" title="Permalink to this definition">#</a></dt>
<dd><p>create a new record for filename and uuid</p>
<p>Returns: an ExportRecord object</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.create_or_get_file_record">
<span class="sig-name descname"><span class="pre">create_or_get_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Union</span><span class="p"><span class="pre">[</span></span><span class="pre">Path</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.create_or_get_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.create_or_get_file_record" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">create_or_get_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.create_or_get_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.create_or_get_file_record" title="Permalink to this definition">#</a></dt>
<dd><p>create a new record for filename and uuid or return existing record</p>
<p>Returns: an ExportRecord object</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.delete_data_for_filepath">
<span class="sig-name descname"><span class="pre">delete_data_for_filepath</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filepath</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.delete_data_for_filepath"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.delete_data_for_filepath" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">delete_data_for_filepath</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filepath</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.delete_data_for_filepath"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.delete_data_for_filepath" title="Permalink to this definition">#</a></dt>
<dd><p>Delete all exportdb data for given filepath</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.delete_data_for_uuid">
<span class="sig-name descname"><span class="pre">delete_data_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.delete_data_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.delete_data_for_uuid" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">delete_data_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.delete_data_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.delete_data_for_uuid" title="Permalink to this definition">#</a></dt>
<dd><p>Delete all exportdb data for given UUID</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ExportDB.export_dir">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">export_dir</span></span><a class="headerlink" href="#osxphotos.ExportDB.export_dir" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">export_dir</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">str</span></em><a class="headerlink" href="#osxphotos.ExportDB.export_dir" title="Permalink to this definition">#</a></dt>
<dd><p>returns path to export directory</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_export_results">
<span class="sig-name descname"><span class="pre">get_export_results</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">run</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">0</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_export_results"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_export_results" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_export_results</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">run</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">0</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="s"><span class="pre">'osxphotos.photoexporter.ExportResults'</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_export_results"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_export_results" title="Permalink to this definition">#</a></dt>
<dd><p>Retrieve export results from database</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
@@ -458,21 +468,21 @@ If called in context manager, returns True (execution is delayed until exiting c
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_file_record">
<span class="sig-name descname"><span class="pre">get_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Union</span><span class="p"><span class="pre">[</span></span><span class="pre">Path</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_file_record" title="Permalink to this definition">#</a></dt>
<dd><p>get info for filename and uuid</p>
<span class="sig-name descname"><span class="pre">get_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="s"><span class="pre">'ExportRecord'</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_file_record" title="Permalink to this definition">#</a></dt>
<dd><p>get info for filename</p>
<p>Returns: an ExportRecord object or None if filename not found</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_files_for_uuid">
<span class="sig-name descname"><span class="pre">get_files_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">List</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_files_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_files_for_uuid" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_files_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">list</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_files_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_files_for_uuid" title="Permalink to this definition">#</a></dt>
<dd><p>query database for UUID and return list of files associated with UUID or empty list</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_photoinfo_for_uuid">
<span class="sig-name descname"><span class="pre">get_photoinfo_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_photoinfo_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_photoinfo_for_uuid" title="Permalink to this definition">#</a></dt>
<dd><p>returns the photoinfo JSON struct for a UUID</p>
<span class="sig-name descname"><span class="pre">get_photoinfo_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_photoinfo_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_photoinfo_for_uuid" title="Permalink to this definition">#</a></dt>
<dd><p>returns the photoinfo JSON string for a UUID or None if not found</p>
</dd></dl>
<dl class="py method">
@@ -483,7 +493,7 @@ If called in context manager, returns True (execution is delayed until exiting c
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_target_for_file">
<span class="sig-name descname"><span class="pre">get_target_for_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Union</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Path</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_target_for_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_target_for_file" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_target_for_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">pathlib.Path</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_target_for_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_target_for_file" title="Permalink to this definition">#</a></dt>
<dd><dl class="simple">
<dt>query database for file matching file name and return the matching filename if there is one;</dt><dd><p>otherwise return None; looks for file.ext, file (1).ext, file (2).ext and so on to find the
actual target name that was used to export filename</p>
@@ -494,33 +504,33 @@ actual target name that was used to export filename</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_uuid_for_file">
<span class="sig-name descname"><span class="pre">get_uuid_for_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_uuid_for_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_uuid_for_file" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">get_uuid_for_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_uuid_for_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_uuid_for_file" title="Permalink to this definition">#</a></dt>
<dd><p>query database for filename and return UUID
returns None if filename not found in database</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ExportDB.path">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">path</span></span><a class="headerlink" href="#osxphotos.ExportDB.path" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">path</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">str</span></em><a class="headerlink" href="#osxphotos.ExportDB.path" title="Permalink to this definition">#</a></dt>
<dd><p>returns path to export database</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.set_config">
<span class="sig-name descname"><span class="pre">set_config</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config_data</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.set_config"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.set_config" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">set_config</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config_data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.set_config"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.set_config" title="Permalink to this definition">#</a></dt>
<dd><p>set config in the database</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.set_export_results">
<span class="sig-name descname"><span class="pre">set_export_results</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">results</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.set_export_results"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.set_export_results" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">set_export_results</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">results</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#osxphotos.ExportResults" title="osxphotos.photoexporter.ExportResults"><span class="pre">ExportResults</span></a></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.set_export_results"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.set_export_results" title="Permalink to this definition">#</a></dt>
<dd><p>Store export results in database; data is pickled and gzipped for storage</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.set_photoinfo_for_uuid">
<span class="sig-name descname"><span class="pre">set_photoinfo_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">info</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.set_photoinfo_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.set_photoinfo_for_uuid" title="Permalink to this definition">#</a></dt>
<dd><p>sets the photoinfo JSON struct for a UUID</p>
<span class="sig-name descname"><span class="pre">set_photoinfo_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">info</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.set_photoinfo_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.set_photoinfo_for_uuid" title="Permalink to this definition">#</a></dt>
<dd><p>sets the photoinfo JSON string for a UUID</p>
</dd></dl>
</dd></dl>
@@ -1090,7 +1100,7 @@ file_cmp returns mock data</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.FileUtilNoOp.copy">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">copy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">src</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">dest</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/fileutil.html#FileUtilNoOp.copy"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.FileUtilNoOp.copy" title="Permalink to this definition">#</a></dt>
<dd><p>Copies a file from src path to dest path</p>
<dd><p>Copies a file from src path to dest path using shutil.copy</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
@@ -1184,6 +1194,12 @@ including folders, albums, etc</p>
<dd><p>return list of albums (as AlbumInfo objects) contained in the folder</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.FolderInfo.asdict">
<span class="sig-name descname"><span class="pre">asdict</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#FolderInfo.asdict"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.FolderInfo.asdict" title="Permalink to this definition">#</a></dt>
<dd><p>Return folder info as a dict</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.FolderInfo.parent">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">parent</span></span><a class="headerlink" href="#osxphotos.FolderInfo.parent" title="Permalink to this definition">#</a></dt>
@@ -1212,14 +1228,26 @@ including folders, albums, etc</p>
<dl class="py class">
<dt class="sig sig-object py" id="osxphotos.ImportInfo">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">ImportInfo</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">db</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#ImportInfo"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ImportInfo" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">ImportInfo</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">db</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#ImportInfo"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ImportInfo" title="Permalink to this definition">#</a></dt>
<dd><p>Information about import sessions</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ImportInfo.asdict">
<span class="sig-name descname"><span class="pre">asdict</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#ImportInfo.asdict"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ImportInfo.asdict" title="Permalink to this definition">#</a></dt>
<dd><p>Return import info as a dict; does not include photos</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ImportInfo.photos">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">photos</span></span><a class="headerlink" href="#osxphotos.ImportInfo.photos" title="Permalink to this definition">#</a></dt>
<dd><p>return list of photos contained in import session</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ImportInfo.title">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">title</span></span><a class="headerlink" href="#osxphotos.ImportInfo.title" title="Permalink to this definition">#</a></dt>
<dd><p>return title / name of import session</p>
</dd></dl>
</dd></dl>
<dl class="py class">
@@ -1345,7 +1373,7 @@ Highest quality face is result[0] and lowest quality face is result[n]</p>
<dl class="py class">
<dt class="sig sig-object py" id="osxphotos.PhotoExporter">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">PhotoExporter</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">photo</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#osxphotos.PhotoInfo" title="osxphotos.PhotoInfo"><span class="pre">PhotoInfo</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">tmpdir</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/photoexporter.html#PhotoExporter"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoExporter" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">PhotoExporter</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">photo</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#osxphotos.PhotoInfo" title="osxphotos.PhotoInfo"><span class="pre">PhotoInfo</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">tmpdir</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">t.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/photoexporter.html#PhotoExporter"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoExporter" title="Permalink to this definition">#</a></dt>
<dd><p>Export a photo</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoExporter.exiftool_json_sidecar">
@@ -1462,8 +1490,20 @@ including keywords, persons, albums, uuid, path, etc.</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.asdict">
<span class="sig-name descname"><span class="pre">asdict</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/photoinfo.html#PhotoInfo.asdict"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoInfo.asdict" title="Permalink to this definition">#</a></dt>
<dd><p>return dict representation</p>
<span class="sig-name descname"><span class="pre">asdict</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">shallow</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">dict</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/photoinfo.html#PhotoInfo.asdict"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoInfo.asdict" title="Permalink to this definition">#</a></dt>
<dd><p>Return dict representation of PhotoInfo object.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>shallow</strong> if True, return shallow representation (does not contain folder_info, person_info, etc.)</p>
</dd>
<dt class="field-even">Returns<span class="colon">:</span></dt>
<dd class="field-even"><p>dict representation of PhotoInfo object</p>
</dd>
</dl>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The shallow representation is used internally by export as it contains only the subset of data needed for export.</p>
</div>
</dd></dl>
<dl class="py property">
@@ -1510,10 +1550,22 @@ self is not included in the returned list</p>
<dd><p>Returns True if photo is a burst photo and has been selected from the burst set by the user, otherwise False</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.cloud_guid">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">cloud_guid</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">str</span></em><a class="headerlink" href="#osxphotos.PhotoInfo.cloud_guid" title="Permalink to this definition">#</a></dt>
<dd><p>Returns the GUID of the photo in iCloud (Photos 5+ only)</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.cloud_metadata">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">cloud_metadata</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">Dict</span></em><a class="headerlink" href="#osxphotos.PhotoInfo.cloud_metadata" title="Permalink to this definition">#</a></dt>
<dd><p>Returns contents of ZCLOUDMASTERMEDIAMETADATA as dict</p>
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">cloud_metadata</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">dict</span><span class="p"><span class="pre">[</span></span><span class="pre">Any</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></em><a class="headerlink" href="#osxphotos.PhotoInfo.cloud_metadata" title="Permalink to this definition">#</a></dt>
<dd><p>Returns contents of ZCLOUDMASTERMEDIAMETADATA as dict; Photos 5+ only</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.cloud_owner_hashed_id">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">cloud_owner_hashed_id</span></span><em class="property"><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="pre">str</span></em><a class="headerlink" href="#osxphotos.PhotoInfo.cloud_owner_hashed_id" title="Permalink to this definition">#</a></dt>
<dd><p>Returns the hashed iCloud ID of the owner of the shared photo (Photos 5+ only)</p>
</dd></dl>
<dl class="py property">
@@ -1774,8 +1826,23 @@ isMissing = 1</p>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.json">
<span class="sig-name descname"><span class="pre">json</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/photoinfo.html#PhotoInfo.json"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoInfo.json" title="Permalink to this definition">#</a></dt>
<span class="sig-name descname"><span class="pre">json</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">indent</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">shallow</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">str</span></span></span><a class="reference internal" href="_modules/osxphotos/photoinfo.html#PhotoInfo.json"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoInfo.json" title="Permalink to this definition">#</a></dt>
<dd><p>Return JSON representation</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>indent</strong> indent level for JSON, if None, no indent</p></li>
<li><p><strong>shallow</strong> if True, return shallow JSON representation (does not contain folder_info, person_info, etc.)</p></li>
</ul>
</dd>
<dt class="field-even">Returns<span class="colon">:</span></dt>
<dd class="field-even"><p>JSON string</p>
</dd>
</dl>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The shallow representation is used internally by export as it contains only the subset of data needed for export.</p>
</div>
</dd></dl>
<dl class="py property">
@@ -2017,6 +2084,12 @@ Only valid on Photos 5; returns None on older versions</p>
<dd><p>Returns True if photo is a slow motion video, otherwise False</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.tables">
<span class="sig-name descname"><span class="pre">tables</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">PhotoTables</span></span></span><a class="reference internal" href="_modules/osxphotos/photoinfo.html#PhotoInfo.tables"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoInfo.tables" title="Permalink to this definition">#</a></dt>
<dd><p>Return PhotoTables object to provide access database tables associated with this photo (Photos 5+)</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.PhotoInfo.time_lapse">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">time_lapse</span></span><a class="headerlink" href="#osxphotos.PhotoInfo.time_lapse" title="Permalink to this definition">#</a></dt>
@@ -2533,9 +2606,25 @@ Returns photos regardless of intrash state.</p>
<dl class="py class">
<dt class="sig sig-object py" id="osxphotos.ProjectInfo">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">ProjectInfo</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">db</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#ProjectInfo"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ProjectInfo" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">ProjectInfo</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">db</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/albuminfo.html#ProjectInfo"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ProjectInfo" title="Permalink to this definition">#</a></dt>
<dd><p>ProjectInfo with info about projects
Projects are cards, calendars, slideshows, etc.</p>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ProjectInfo.folder_list">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">folder_list</span></span><a class="headerlink" href="#osxphotos.ProjectInfo.folder_list" title="Permalink to this definition">#</a></dt>
<dd><p>Returns list of FolderInfo objects for each folder the album is contained in
or empty list if album is not in any folders</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="osxphotos.ProjectInfo.folder_names">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">folder_names</span></span><a class="headerlink" href="#osxphotos.ProjectInfo.folder_names" title="Permalink to this definition">#</a></dt>
<dd><p>Return hierarchical list of folders the album is contained in
the folder list is in form:
[“Top level folder”, “sub folder 1”, “sub folder 2”, …]
or empty list if album is not in any folders</p>
</dd></dl>
</dd></dl>
<dl class="py class">
@@ -2589,7 +2678,7 @@ Projects are cards, calendars, slideshows, etc.</p>
<dl class="py attribute">
<dt class="sig sig-object py" id="osxphotos.QueryOptions.burst_photos">
<span class="sig-name descname"><span class="pre">burst_photos</span></span><a class="headerlink" href="#osxphotos.QueryOptions.burst_photos" title="Permalink to this definition">#</a></dt>
<dd><p>search for burst photos</p>
<dd><p>include all associated burst photos for photos in query results</p>
<dl class="field-list simple">
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><p>Optional[bool]</p>
@@ -3649,8 +3738,9 @@ Projects are cards, calendars, slideshows, etc.</p>
<div class="toc-tree-container">
<div class="toc-tree">
<ul>
<li><a class="reference internal" href="#">OSXPhotos python API</a><ul>
<li><a class="reference internal" href="#">OSXPhotos Python Reference</a><ul>
<li><a class="reference internal" href="#osxphotos.AlbumInfo"><code class="docutils literal notranslate"><span class="pre">AlbumInfo</span></code></a><ul>
<li><a class="reference internal" href="#osxphotos.AlbumInfo.asdict"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.asdict()</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.AlbumInfo.folder_list"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.folder_list</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.AlbumInfo.folder_names"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.folder_names</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.AlbumInfo.parent"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.parent</span></code></a></li>
@@ -3675,6 +3765,7 @@ Projects are cards, calendars, slideshows, etc.</p>
</li>
<li><a class="reference internal" href="#osxphotos.ExportDB"><code class="docutils literal notranslate"><span class="pre">ExportDB</span></code></a><ul>
<li><a class="reference internal" href="#osxphotos.ExportDB.close"><code class="docutils literal notranslate"><span class="pre">ExportDB.close()</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ExportDB.connection"><code class="docutils literal notranslate"><span class="pre">ExportDB.connection</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ExportDB.create_file_record"><code class="docutils literal notranslate"><span class="pre">ExportDB.create_file_record()</span></code></a></li>
<li><a class="reference internal" href="#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><a class="reference internal" href="#osxphotos.ExportDB.delete_data_for_filepath"><code class="docutils literal notranslate"><span class="pre">ExportDB.delete_data_for_filepath()</span></code></a></li>
@@ -3764,6 +3855,7 @@ Projects are cards, calendars, slideshows, etc.</p>
</li>
<li><a class="reference internal" href="#osxphotos.FolderInfo"><code class="docutils literal notranslate"><span class="pre">FolderInfo</span></code></a><ul>
<li><a class="reference internal" href="#osxphotos.FolderInfo.album_info"><code class="docutils literal notranslate"><span class="pre">FolderInfo.album_info</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.FolderInfo.asdict"><code class="docutils literal notranslate"><span class="pre">FolderInfo.asdict()</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.FolderInfo.parent"><code class="docutils literal notranslate"><span class="pre">FolderInfo.parent</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.FolderInfo.subfolders"><code class="docutils literal notranslate"><span class="pre">FolderInfo.subfolders</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.FolderInfo.title"><code class="docutils literal notranslate"><span class="pre">FolderInfo.title</span></code></a></li>
@@ -3771,7 +3863,9 @@ Projects are cards, calendars, slideshows, etc.</p>
</ul>
</li>
<li><a class="reference internal" href="#osxphotos.ImportInfo"><code class="docutils literal notranslate"><span class="pre">ImportInfo</span></code></a><ul>
<li><a class="reference internal" href="#osxphotos.ImportInfo.asdict"><code class="docutils literal notranslate"><span class="pre">ImportInfo.asdict()</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ImportInfo.photos"><code class="docutils literal notranslate"><span class="pre">ImportInfo.photos</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ImportInfo.title"><code class="docutils literal notranslate"><span class="pre">ImportInfo.title</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#osxphotos.LikeInfo"><code class="docutils literal notranslate"><span class="pre">LikeInfo</span></code></a></li>
@@ -3816,7 +3910,9 @@ Projects are cards, calendars, slideshows, etc.</p>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.burst_key"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_key</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.burst_photos"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_photos</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.burst_selected"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.burst_selected</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.cloud_guid"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.cloud_guid</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.cloud_metadata"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.cloud_metadata</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.cloud_owner_hashed_id"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.cloud_owner_hashed_id</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.comments"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.comments</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.date"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.date</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.date_added"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.date_added</span></code></a></li>
@@ -3884,6 +3980,7 @@ Projects are cards, calendars, slideshows, etc.</p>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.selfie"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.selfie</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.shared"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.shared</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.slow_mo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.slow_mo</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.tables"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.tables()</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.time_lapse"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.time_lapse</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.title"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.title</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.PhotoInfo.tzoffset"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.tzoffset</span></code></a></li>
@@ -3950,7 +4047,11 @@ Projects are cards, calendars, slideshows, etc.</p>
</ul>
</li>
<li><a class="reference internal" href="#osxphotos.PlaceInfo"><code class="docutils literal notranslate"><span class="pre">PlaceInfo</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ProjectInfo"><code class="docutils literal notranslate"><span class="pre">ProjectInfo</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ProjectInfo"><code class="docutils literal notranslate"><span class="pre">ProjectInfo</span></code></a><ul>
<li><a class="reference internal" href="#osxphotos.ProjectInfo.folder_list"><code class="docutils literal notranslate"><span class="pre">ProjectInfo.folder_list</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.ProjectInfo.folder_names"><code class="docutils literal notranslate"><span class="pre">ProjectInfo.folder_names</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#osxphotos.QueryOptions"><code class="docutils literal notranslate"><span class="pre">QueryOptions</span></code></a><ul>
<li><a class="reference internal" href="#osxphotos.QueryOptions.added_after"><code class="docutils literal notranslate"><span class="pre">QueryOptions.added_after</span></code></a></li>
<li><a class="reference internal" href="#osxphotos.QueryOptions.added_before"><code class="docutils literal notranslate"><span class="pre">QueryOptions.added_before</span></code></a></li>

View File

@@ -4,7 +4,7 @@
<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-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.56.7 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.60.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.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -158,7 +158,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
<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-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Template System - osxphotos 0.56.7 documentation</title>
<title>OSXPhotos Template System - osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<li class="toctree-l1"><a class="reference internal" href="cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -240,6 +240,8 @@
<li><p><cite>join(x)</cite>: Join list of values with delimiter x, e.g. join(,): [a, b, c] =&gt; 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] =&gt; abc.</p></li>
<li><p><cite>append(x)</cite>: Append x to list of values, e.g. append(d): [a, b, c] =&gt; [a, b, c, d].</p></li>
<li><p><cite>prepend(x)</cite>: Prepend x to list of values, e.g. prepend(d): [a, b, c] =&gt; [d, a, b, c].</p></li>
<li><p><cite>appends(x)</cite>: Append s[tring] Append x to each value of list of values, e.g. appends(d): [a, b, c] =&gt; [ad, bd, cd].</p></li>
<li><p><cite>prepends(x)</cite>: Prepend s[tring] x to each value of list of values, e.g. prepends(d): [a, b, c] =&gt; [da, db, dc].</p></li>
<li><p><cite>remove(x)</cite>: Remove x from list of values, e.g. remove(b): [a, b, c] =&gt; [a, c].</p></li>
<li><p><cite>slice(start:stop:step)</cite>: Slice list using same semantics as Pythons list slicing, e.g. slice(1:3): [a, b, c, d] =&gt; [b, c]; slice(1:4:2): [a, b, c, d] =&gt; [b, d]; slice(1:): [a, b, c, d] =&gt; [b, c, d]; slice(:-1): [a, b, c, d] =&gt; [a, b, c]; slice(::-1): [a, b, c, d] =&gt; [d, c, b, a]. See also sslice().</p></li>
<li><p><cite>sslice(start:stop:step)</cite>: [s(tring) slice] Slice values in a list using same semantics as Pythons string slicing, e.g. sslice(1:3):abcd =&gt; bc; sslice(1:4:2): abcd =&gt; bd, etc. See also slice().</p></li>
@@ -611,7 +613,7 @@
</td>
</tr>
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
<td><p>The osxphotos version, e.g. 0.56.7</p></td>
<td><p>The osxphotos version, e.g. 0.60.2</p></td>
</tr>
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
<td><p>The full command line used to run osxphotos</p></td>

View File

@@ -6,7 +6,7 @@
<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-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Tutorial - osxphotos 0.56.7 documentation</title>
<title>OSXPhotos Tutorial - osxphotos 0.60.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" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.60.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.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.60.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">
@@ -161,7 +161,7 @@
<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>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -1,5 +1,5 @@
OSXPhotos python API
====================
OSXPhotos Python Reference
==========================
.. automodule:: osxphotos
:members:

View File

@@ -58,6 +58,8 @@ Valid filters are:
* `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'].
* `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
* `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
* `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().
@@ -359,7 +361,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.56.7'
- The osxphotos version, e.g. '0.60.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

327
examples/batch_edit.py Normal file
View File

@@ -0,0 +1,327 @@
"""
Batch edit currently selected photo metadata using osxphotos.
Run this with `osxphotos run batch_edit.py` or `osxphotos run batch_edit.py --help` for more information.
"""
from __future__ import annotations
import functools
import json
import sys
import click
import photoscript
import osxphotos
from osxphotos.cli import echo, echo_error, kvstore, selection_command, verbose
from osxphotos.cli.param_types import TemplateString
from osxphotos.phototemplate import RenderOptions
from osxphotos.sqlitekvstore import SQLiteKVStore
class Latitude(click.ParamType):
name = "Latitude"
def convert(self, value, param, ctx):
try:
latitude = float(value)
if latitude < -90 or latitude > 90:
raise ValueError
return latitude
except Exception:
self.fail(
f"Invalid latitude {value}. Must be a floating point number between -90 and 90."
)
class Longitude(click.ParamType):
name = "Longitude"
def convert(self, value, param, ctx):
try:
longitude = float(value)
if longitude < -180 or longitude > 180:
raise ValueError
return longitude
except Exception:
self.fail(
f"Invalid longitude {value}. Must be a floating point number between -180 and 180."
)
@selection_command(name="batch-edit")
@click.option(
"--title",
metavar="TITLE_TEMPLATE",
type=TemplateString(),
help="Set title of photo.",
)
@click.option(
"--description",
metavar="DESCRIPTION_TEMPLATE",
type=TemplateString(),
help="Set description of photo.",
)
@click.option(
"--keyword",
metavar="KEYWORD_TEMPLATE",
type=TemplateString(),
multiple=True,
help="Set keywords of photo. May be specified multiple times.",
)
@click.option(
"--location",
metavar="LATITUDE LONGITUDE",
type=click.Tuple([Latitude(), Longitude()]),
help="Set location of photo. "
"Must be specified as a pair of numbers with latitude in the range -90 to 90 and longitude in the range -180 to 180.",
)
@click.option("--dry-run", is_flag=True, help="Don't actually change anything.")
@click.option(
"--undo",
is_flag=True,
help="Restores photo metadata to what it was prior to the last batch edit. "
"May be combined with --dry-run.",
)
def batch_edit(
photos: list[osxphotos.PhotoInfo],
title,
description,
keyword,
location,
dry_run,
undo,
**kwargs,
):
"""
Batch edit photo metadata such as title, description, keywords, etc.
Operates on currently selected photos.
Select one or more photos in Photos then run this command to edit the metadata.
For example:
\b
osxphotos run batch_edit.py \\
--verbose \\
--title "California vacation 2023 {created.year}-{created.dd}-{created.mm} {counter:03d}" \\
--description "{place.name}" \\
--keyword "Family" --keyword "Travel" --keyword "{keyword}"
This will set the title to "California vacation 2023 2023-02-20 001", and so on,
the description to the reverse geolocation place name,
and the keywords to "Family", "Travel", and any existing keywords of the photo.
--title, --description, and --keyword may be any valid template string.
See https://rhettbull.github.io/osxphotos/template_help.html for more information
on the osxphotos template system.
"""
if not title and not description and not keyword and not location and not undo:
echo_error(
"[error] Must specify at least one of: "
" --title, --description, --keyword, --location, --undo. "
"Use --help for more information."
)
sys.exit(1)
if undo and (title or description or keyword or location):
echo_error(
"[error] Cannot specify --undo and any options other than --dry-run. "
"Use --help for more information."
)
sys.exit(1)
if not photos:
echo_error("[error] No photos selected")
sys.exit(1)
# sort photos by date so that {counter} order is correct
photos.sort(key=lambda p: p.date)
undo_store = kvstore("batch_edit")
verbose(f"Undo database stored in [filepath]{undo_store.path}", level=2)
echo(f"Processing [num]{len(photos)}[/] photos...")
for photo in photos:
verbose(
f"Processing [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
if undo:
undo_photo_edits(photo, undo_store, dry_run)
continue
save_photo_undo_info(undo_store, photo)
set_photo_title_from_template(photo, title, dry_run)
set_photo_description_from_template(photo, description, dry_run)
set_photo_keywords_from_template(photo, keyword, dry_run)
set_photo_location(photo, location, dry_run)
# cache photoscript Photo object to avoid re-creating it for each photo
# maxsize=1 as this function is called repeatedly for each photo then
# the next photo is processed
@functools.lru_cache(maxsize=1)
def photoscript_photo(photo: osxphotos.PhotoInfo) -> photoscript.Photo:
"""Return photoscript Photo object for photo"""
return photoscript.Photo(photo.uuid)
def save_photo_undo_info(undo_store: SQLiteKVStore, photo: osxphotos.PhotoInfo):
"""Save undo information to undo store"""
undo_store[photo.uuid] = photo.json()
def undo_photo_edits(
photo: osxphotos.PhotoInfo, undo_store: SQLiteKVStore, dry_run: bool
):
"""Undo edits for photo"""
if not (undo_info := undo_store.get(photo.uuid)):
verbose(
f"[warning] No undo information for photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
return
undo_info = json.loads(undo_info)
ps_photo = photoscript_photo(photo)
exiting_title, exiting_description, exiting_keywords, exiting_location = (
photo.title,
photo.description,
sorted(photo.keywords),
photo.location,
)
previous_title, previous_description, previous_keywords, previous_location = (
undo_info.get("title"),
undo_info.get("description"),
sorted(undo_info.get("keywords")),
(undo_info.get("latitude"), undo_info.get("longitude")),
)
verbose(
f"Undoing edits for [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
for name, existing, previous in (
("title", exiting_title, previous_title),
("description", exiting_description, previous_description),
("keywords", exiting_keywords, previous_keywords),
("location", exiting_location, previous_location),
):
if existing != previous:
verbose(
f" [i]{name}[/]: [change]{existing}[/] -> [no_change]{previous}[/]"
)
if not dry_run:
setattr(ps_photo, name, previous)
else:
verbose(f" [i]{name} (no change)[/]: [no_change]{existing}[/]", level=2)
def set_photo_title_from_template(
photo: osxphotos.PhotoInfo, title_template: str, dry_run: bool
):
"""Set photo title from template"""
if not title_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
title_string, _ = photo.render_template(title_template, render_options)
title_string = [ts for ts in title_string if ts]
if not title_string:
verbose(
f"No title returned from template, nothing to do: [bold]{title_template}"
)
return
if len(title_string) > 1:
echo_error(
f"[error] Title template must return a single string: [bold]{title_string}"
)
sys.exit(1)
verbose(f"Setting [i]title[/i] to [bold]{title_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.title = title_string[0]
def set_photo_description_from_template(
photo: osxphotos.PhotoInfo, description_template: str, dry_run: bool
):
"""Set photo description from template"""
if not description_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
description_string, _ = photo.render_template(description_template, render_options)
description_string = [ds for ds in description_string if ds]
if not description_string:
verbose(
f"No description returned from template, nothing to do: [bold]{description_template}"
)
return
if len(description_string) > 1:
echo_error(
f"[error] Description template must return a single string: [bold]{description_string}"
)
sys.exit(1)
verbose(f"Setting [i]description[/] to [bold]{description_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.description = description_string[0]
def set_photo_keywords_from_template(
photo: osxphotos.PhotoInfo, keyword_template: list[str], dry_run: bool
):
"""Set photo keywords from template"""
if not keyword_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
keywords = set()
for kw in keyword_template:
kw_string, _ = photo.render_template(kw, render_options)
if kw_string:
# filter out empty strings
keywords.update([k for k in kw_string if k])
if not keywords:
verbose(
f"No keywords returned from template, nothing to do: [bold]{keyword_template}"
)
return
verbose(
f"Setting [i]keywords[/] to {', '.join(f'[bold]{kw}[/]' for kw in keywords)}"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.keywords = list(keywords)
def set_photo_location(
photo: osxphotos.PhotoInfo, location: tuple[float, float], dry_run: bool
):
"""Set photo location"""
if not location or location[0] is None or location[1] is None:
return
latitude, longitude = location
verbose(
f"Setting [i]location[/] to [num]{latitude:.6f}[/], [num]{longitude:.6f}[/]"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.location = (latitude, longitude)
if __name__ == "__main__":
batch_edit()

67
examples/cli_example_1.py Normal file
View File

@@ -0,0 +1,67 @@
"""Sample query command for osxphotos
This shows how simple it is to create a command line tool using osxphotos to process your photos.
Using the @query_command decorator turns your function to a full-fledged command line app that
can be run via `osxphotos run cli_example_1.py` or `python cli_example_1.py` if you have pip installed osxphotos.
Using this decorator makes it very easy to create a quick command line tool that can operate on
a subset of your photos. Additionally, writing a command in this way makes it easy to later
incorporate the command into osxphotos as a full-fledged command.
The decorator will add all the query options available in `osxphotos query` as command line options
as well as the following options:
--verbose
--timestamp
--theme
--db
--debug (hidden, won't show in help)
The decorated function will perform the query and pass the list of filtered PhotoInfo objects
to your function. You can then do whatever you want with the photos.
For example, to run the command on only selected photos:
osxphotos run cli_example_1.py --selected
To run the command on all photos with the keyword "foo":
osxphotos run cli_example_1.py --keyword foo
For more advanced example, see `cli_example_2.py`
"""
from __future__ import annotations
import osxphotos
from osxphotos.cli import query_command, verbose
@query_command
def example(photos: list[osxphotos.PhotoInfo], **kwargs):
"""Sample query command for osxphotos. Prints out the filename and date of each photo.
Whatever text you put in the function's docstring here, will be used as the command's
help text when run via `osxphotos run cli_example_1.py --help` or `python cli_example_1.py --help`
"""
# verbose() will print to stdout if --verbose option is set
# you can optionally provide a level (default is 1) to print only if --verbose is set to that level
# for example: -VV or --verbose --verbose == level 2
verbose(f"Found {len(photos)} photo(s)")
verbose("This message will only be printed if verbose level 2 is set", level=2)
# do something with photos here
for photo in photos:
# photos is a list of PhotoInfo objects
# see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo
verbose(f"Processing {photo.original_filename}")
print(f"{photo.original_filename} {photo.date}")
...
if __name__ == "__main__":
# call your function here
# you do not need to pass any arguments to the function
# as the decorator will handle parsing the command line arguments
example()

160
examples/cli_example_2.py Normal file
View File

@@ -0,0 +1,160 @@
"""Sample query command for osxphotos
This shows how simple it is to create a command line tool using osxphotos to process your photos.
Using the @query_command decorator turns your function to a full-fledged command line app that
can be run via `osxphotos run cli_example_2.py` or `python cli_example_2.py` if you have pip installed osxphotos.
Using this decorator makes it very easy to create a quick command line tool that can operate on
a subset of your photos. Additionally, writing a command in this way makes it easy to later
incorporate the command into osxphotos as a full-fledged command.
The decorator will add all the query options available in `osxphotos query` as command line options
as well as the following options:
--verbose
--timestamp
--theme
--db
--debug (hidden, won't show in help)
The decorated function will perform the query and pass the list of filtered PhotoInfo objects
to your function. You can then do whatever you want with the photos.
For example, to run the command on only selected photos:
osxphotos run cli_example_2.py --selected
To run the command on all photos with the keyword "foo":
osxphotos run cli_example_2.py --keyword foo
The following helper functions may be useful and can be imported from osxphotos.cli:
abort(message: str, exit_code: int = 1)
Abort with error message and exit code
echo(message: str)
Print message to stdout using rich formatting
echo_error(message: str)
Print message to stderr using rich formatting
logger: logging.Logger
Python logger for osxphotos; for example, logger.debug("debug message")
verbose(*args, level: int = 1)
Print args to stdout if --verbose option is set
query_command: decorator to create an osxphotos query command
kvstore(name: str) -> SQLiteKVStore useful for storing state between runs
The verbose, echo, and echo_error functions use rich formatting to print messages to stdout and stderr.
See https://github.com/Textualize/rich for more information on rich formatting.
In addition to standard rich formatting styles, the following styles will be defined
(and can be changed using --theme):
[change]: something change
[no_change]: indicate no change
[count]: a count
[error]: an error
[filename]: a filename
[filepath]: a filepath
[num]: a number
[time]: a time or date
[tz]: a timezone
[warning]: a warning
[uuid]: a uuid
The tags should be closed with [/] to end the style. For example:
echo("[filename]foo[/] [time]bar[/]")
For simpler examples, see `cli_example_1.py`
"""
from __future__ import annotations
import datetime
import click
import osxphotos
from osxphotos.cli import (
abort,
echo,
echo_error,
kvstore,
logger,
query_command,
verbose,
)
@query_command()
@click.option(
"--resume",
is_flag=True,
help="Resume processing from last run, do not reprocess photos",
)
@click.option(
"--dry-run", is_flag=True, help="Do a dry run, don't actually do anything"
)
def example(resume, dry_run, photos: list[osxphotos.PhotoInfo], **kwargs):
"""Sample query command for osxphotos. Prints out the filename and date of each photo.
Whatever text you put in the function's docstring here, will be used as the command's
help text when run via `osxphotos run cli_example_2.py --help` or `python cli_example_2.py --help`
The @query_command decorator returns a click.command so you can add additional options
using standard click decorators. For example, the --resume and --dry-run options.
For more information on click, see https://palletsprojects.com/p/click/.
"""
# abort will print the message to stderr and exit with the given exit code
if not photos:
abort("Nothing to do!", 1)
# verbose() will print to stdout if --verbose option is set
# you can optionally provide a level (default is 1) to print only if --verbose is set to that level
# for example: -VV or --verbose --verbose == level 2
verbose(f"Found [count]{len(photos)}[/] photos")
verbose("This message will only be printed if verbose level 2 is set", level=2)
# the logger is a python logging.Logger object
# debug messages will only be printed if --debug option is set
logger.debug(f"{kwargs=}")
# kvstore() returns a SQLiteKVStore object for storing state between runs
# this is basically a persistent dictionary that can be used to store state
# see https://github.com/RhetTbull/sqlitekvstore for more information
kv = kvstore("cli_example_2")
verbose(f"Using key-value cache: {kv.path}")
# do something with photos here
for photo in photos:
# photos is a list of PhotoInfo objects
# see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo
if resume and photo.uuid in kv:
echo(
f"Skipping processed photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
continue
# store the uuid and current time in the kvstore
# the key and value must be a type supported by SQLite: int, float, str, bytes, bool, None
# if you need to store other values, you should serialize them to a string or bytes first
# for example, using json.dumps() or pickle.dumps()
kv[photo.uuid] = datetime.datetime.now().isoformat()
echo(f"Processing [filename]{photo.original_filename}[/] [time]{photo.date}[/]")
if not dry_run:
# do something with the photo here
echo(f"Doing something with [filename]{photo.original_filename}[/]")
# echo_error will print to stderr
# if you add [warning] or [error], it will be formatted accordingly
# and include an emoji to make the message stand out
echo_error("[warning]This is a warning message!")
echo_error("[error]This is an error message!")
if __name__ == "__main__":
# call your function here
# you do not need to pass any arguments to the function
# as the decorator will handle parsing the command line arguments
example()

60
examples/cli_example_3.py Normal file
View File

@@ -0,0 +1,60 @@
"""Sample query command for osxphotos
This shows how simple it is to create a command line tool using osxphotos to process your photos.
Using the @selection_command decorator turns your function to a full-fledged command line app that
can be run via `osxphotos run cli_example_1.py` or `python cli_example_1.py` if you have pip installed osxphotos.
Using this decorator makes it very easy to create a quick command line tool that can operate on
a subset of your photos. Additionally, writing a command in this way makes it easy to later
incorporate the command into osxphotos as a full-fledged command.
The decorator will add the following options to your command:
--verbose
--timestamp
--theme
--db
--debug (hidden, won't show in help)
The decorated function will get the selected photos and pass the list of PhotoInfo objects
to your function. You can then do whatever you want with the photos.
"""
from __future__ import annotations
import osxphotos
from osxphotos.cli import (
selection_command,
verbose,
)
@selection_command
def example(photos: list[osxphotos.PhotoInfo], **kwargs):
"""Sample command for osxphotos. Prints out the filename and date of each photo
currently selected in Photos.app.
Whatever text you put in the function's docstring here, will be used as the command's
help text when run via `osxphotos run cli_example_1.py --help` or `python cli_example_1.py --help`
"""
# verbose() will print to stdout if --verbose option is set
# you can optionally provide a level (default is 1) to print only if --verbose is set to that level
# for example: -VV or --verbose --verbose == level 2
verbose(f"Found {len(photos)} photo(s)")
verbose("This message will only be printed if verbose level 2 is set", level=2)
# do something with photos here
for photo in photos:
# photos is a list of PhotoInfo objects
# see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo
verbose(f"Processing {photo.original_filename}")
print(f"{photo.original_filename} {photo.date}")
...
if __name__ == "__main__":
# call your function here
# you do not need to pass any arguments to the function
# as the decorator will handle parsing the command line arguments
example()

View File

@@ -0,0 +1,52 @@
"""Example for concurrent export of photos using osxphotos.PhotoExporter.export()
Note: concurrent export can only be used on Python 3.11 and later due to the way
python's sqlite3 module is implemented. See https://docs.python.org/3/library/sqlite3.html#sqlite3.threadsafety
for more information.
"""
import concurrent.futures
import os
import time
import click
import osxphotos
from osxphotos.cli import echo, query_command, verbose
@query_command()
@click.option(
"--workers",
metavar="WORKERS",
help="Maximum number of worker threads to use for export. "
"If not specified, it will default to the number of processors on the machine, multiplied by 5.",
type=int,
)
@click.argument(
"export_dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True),
)
def export(workers, export_dir, photos: list[osxphotos.PhotoInfo], **kwargs):
"""Export photos to EXPORT_DIR using concurrent export.
Use --workers to specify the number of worker threads to use.
This simple example exports only the original photo and does not export
edited versions, live photos, etc.
"""
workers = workers or os.cpu_count() * 5
echo(f"Exporting {len(photos)} photos to {export_dir} using {workers} workers")
start_t = time.perf_counter()
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
futures = [executor.submit(p.export, export_dir) for p in photos]
exported = []
for future in concurrent.futures.as_completed(futures):
exported.extend(future.result())
end_t = time.perf_counter()
echo(
f"Exported {len(exported)} photos to {export_dir} in {end_t-start_t:.4f} seconds"
)
if __name__ == "__main__":
export()

126
examples/export_previews.py Normal file
View File

@@ -0,0 +1,126 @@
""" Export previews for photos in the Photos library
To run this with osxphotos on currently selected photos:
osxphotos run export_previews.py --selected export_path
"""
from __future__ import annotations
import pathlib
import shutil
import click
import osxphotos
from osxphotos.cli import echo, echo_error, query_command, verbose
from osxphotos.cli.export import get_dirnames_from_template, get_filenames_from_template
from osxphotos.cli.param_types import TemplateString
from osxphotos.utils import pluralize
@query_command
@click.option("--dry-run", is_flag=True, help="Dry run, don't actually export")
@click.option(
"--directory",
type=TemplateString(),
help="Directory to export to under export_path. This is an osxphotos template string. "
"For example, to export previews to directories based on creation date: "
"--directory '{created.year}/{created.mm}/{created.dd}' ",
)
@click.option(
"--filename",
"filename_template",
type=TemplateString(),
help="Filename for exported preview. "
"This is an osxphotos template string; "
"for example, to export previews to filename based on creation date: "
"--filename '{original_name}-{created.year}-{created.mm}-{created.dd}' ",
)
@click.argument(
"export_dir", type=click.Path(exists=True, file_okay=False, dir_okay=True)
)
def export_previews(
photos: list[osxphotos.PhotoInfo],
dry_run: bool,
directory: str,
filename_template: str,
export_dir: str,
**kwargs,
):
"""Export previews for photos in the Photos library.
Pass one or more query options to select photos to export or use without query options
to export all images.
If --directory is passed, the previews will be exported to subdirectories under export_path
based on the template string passed to --directory. For example, to export previews to
directories based on creation date: --directory '{created.year}/{created.mm}/{created.dd}'
If --filename is passed, the previews will be exported with the filename specified by the
template string passed to --filename. For example, to export previews to filename based on
creation date: --filename '{original_name}-{created.year}-{created.mm}-{created.dd}'
If --dry-run is passed, the previews will not actually be exported but the export will be
simulated and the export paths printed to stdout.
"""
verbose(f"Found {len(photos)} photo(s)")
count = 0
for photo in photos:
try:
# first derivative is the largest preview, so use that one
preview = pathlib.Path(photo.path_derivatives[0])
except IndexError:
echo(f"No preview for {photo.original_filename} ({photo.uuid})")
continue
verbose(f"Found preview for {photo.original_filename}: {preview}")
count += export_preview_to_directory_with_filename(
photo, preview, export_dir, directory, filename_template, dry_run
)
echo(f"Exported {count} preview {pluralize(count, 'image', 'images')}")
def export_preview_to_directory_with_filename(
photo: osxphotos.PhotoInfo,
preview_path: pathlib.Path,
export_dir: str,
directory: str,
filename_template: str,
dry_run: bool,
) -> int:
"""Export preview for photo to directory with filename; returns count of images exported"""
count = 0
for dirname in get_dirnames_from_template(
photo=photo,
directory=directory,
export_by_date=False,
dest=export_dir,
dry_run=dry_run,
):
for filename in get_filenames_from_template(
photo=photo,
filename_template=filename_template,
export_dir=export_dir,
dest_path=dirname,
original_name=True,
):
# need to change filename extension to match preview
filename = pathlib.Path(filename).with_suffix(preview_path.suffix)
dest_path = pathlib.Path(dirname) / filename
echo(
f"Exporting preview for {photo.original_filename} ({photo.uuid}) to {dest_path}"
)
if not dry_run:
try:
shutil.copy(preview_path, dest_path)
except Exception as e:
echo_error(f"Error exporting preview: {e}")
continue
count += 1
return count
if __name__ == "__main__":
export_previews()

View File

@@ -16,22 +16,25 @@ from .momentinfo import MomentInfo
from .personinfo import PersonInfo
from .photoexporter import ExportOptions, ExportResults, PhotoExporter
from .photoinfo import PhotoInfo
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
from .photosdb import PhotosDB
from .photosdb._photosdb_process_comments import CommentInfo, LikeInfo
from .phototables import PhotoTables
from .phototemplate import PhotoTemplate
from .placeinfo import PlaceInfo
from .queryoptions import QueryOptions
from .scoreinfo import ScoreInfo
from .searchinfo import SearchInfo
from .utils import is_macos
if is_macos:
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
# configure logging; every module in osxphotos should use this logger
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(filename)s - %(lineno)d - %(message)s",
)
logger: logging.Logger = logging.getLogger("osxphotos")
if not is_debug():
logging.disable(logging.DEBUG)
@@ -54,6 +57,7 @@ __all__ = [
"PersonInfo",
"PhotoExporter",
"PhotoInfo",
"PhotoTables",
"PhotoTemplate",
"PhotosAlbum",
"PhotosAlbumPhotoScript",

View File

@@ -2,10 +2,14 @@
from __future__ import annotations
import logging
import os.path
import sqlite3
from datetime import datetime
from enum import Enum
logger: logging.Logger = logging.getLogger("osxphotos")
APP_NAME = "osxphotos"
OSXPHOTOS_URL = "https://github.com/RhetTbull/osxphotos"
@@ -123,6 +127,8 @@ _TESTED_OS_VERSIONS = [
("12", "6"),
("13", "0"),
("13", "1"),
("13", "2"),
("13", "3"),
]
# Photos 5 has persons who are empty string if unidentified face
@@ -180,6 +186,9 @@ _MAX_IPTC_KEYWORD_LEN = 64
# If anyone has a keyword matching this, then too bad...
_OSXPHOTOS_NONE_SENTINEL = "OSXPhotosXYZZY42_Sentinel$"
# Lock file extension for reserving filenames when exporting
_OSXPHOTOS_LOCK_EXTENSION = ".osxphotos.lock"
class SearchCategory:
"""SearchInfo categories for Photos 5+; corresponds to categories in database/search/psi.sqlite:groups.category
@@ -351,7 +360,7 @@ def search_category_factory(version: int) -> SearchCategory:
# Max filename length on MacOS
MAX_FILENAME_LEN = 255
MAX_FILENAME_LEN = 255 - len(_OSXPHOTOS_LOCK_EXTENSION)
# Max directory name length on MacOS
MAX_DIRNAME_LEN = 255
@@ -456,3 +465,16 @@ PROFILE_SORT_KEYS = [
"time",
"tottime",
]
UUID_PATTERN = (
r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
)
# Reference: https://docs.python.org/3/library/sqlite3.html?highlight=sqlite3%20threadsafety#sqlite3.threadsafety
# and https://docs.python.org/3/library/sqlite3.html?highlight=sqlite3%20threadsafety#sqlite3.connect
# 3: serialized mode; Threads may share the module, connections and cursors
# 3 is the default in the python.org python 3.11 distribution
# earlier versions of python.org python 3.x default to 1 which means threads may not share
# sqlite3 connections and thus PhotoInfo.export() cannot be used in a multithreaded environment
# pass SQLITE_CHECK_SAME_THREAD to sqlite3.connect() to enable multithreaded access on systems that support it
SQLITE_CHECK_SAME_THREAD = not sqlite3.threadsafety == 3
logger.debug(f"{SQLITE_CHECK_SAME_THREAD=}, {sqlite3.threadsafety=}")

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.56.7"
__version__ = "0.60.2"

View File

@@ -18,6 +18,7 @@ from ._constants import (
_PHOTOS_4_VERSION,
_PHOTOS_5_ALBUM_KIND,
_PHOTOS_5_FOLDER_KIND,
_PHOTOS_5_VERSION,
TIME_DELTA,
AlbumSortOrder,
)
@@ -49,9 +50,8 @@ def sort_list_by_keys(values, sort_keys):
ValueError: raised if len(values) != len(sort_keys)
"""
if len(values) != len(sort_keys):
return ValueError("values and sort_keys must have same length")
return list(zip(*sorted(zip(sort_keys, values))))[1]
raise ValueError("values and sort_keys must be same length")
return [x for _, x in sorted(zip(sort_keys, values))]
class AlbumInfoBaseClass:
@@ -61,7 +61,7 @@ class AlbumInfoBaseClass:
including folders, photos, etc.
"""
def __init__(self, db=None, uuid=None):
def __init__(self, db, uuid):
self._uuid = uuid
self._db = db
self._title = self._db._dbalbum_details[uuid]["title"]
@@ -121,7 +121,8 @@ class AlbumInfoBaseClass:
@property
def end_date(self):
"""For Albums, return end date (most recent image) of album or None for albums with no images
For Import Sessions, return end date of import sessions (when import was completed)"""
For Import Sessions, return end date of import sessions (when import was completed)
"""
try:
return self._end_date
except AttributeError:
@@ -163,6 +164,16 @@ class AlbumInfoBaseClass:
self._owner = None
return self._owner
def asdict(self):
"""Return album info as a dict; does not include photos"""
return {
"uuid": self.uuid,
"creation_date": self.creation_date,
"start_date": self.start_date,
"end_date": self.end_date,
"owner": self.owner,
}
def __len__(self):
"""return number of photos contained in album"""
return len(self.photos)
@@ -174,6 +185,10 @@ class AlbumInfo(AlbumInfoBaseClass):
including folders, photos, etc.
"""
def __init__(self, db, uuid):
super().__init__(db=db, uuid=uuid)
self._title = self._db._dbalbum_details[uuid]["title"]
@property
def title(self):
"""return title / name of album"""
@@ -205,10 +220,11 @@ class AlbumInfo(AlbumInfoBaseClass):
@property
def folder_names(self):
"""return hierarchical list of folders the album is contained in
"""Return hierarchical list of folders the album is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2", ...]
returns empty list if album is not in any folders"""
or empty list if album is not in any folders
"""
try:
return self._folder_names
@@ -218,10 +234,9 @@ class AlbumInfo(AlbumInfoBaseClass):
@property
def folder_list(self):
"""return hierarchical list of folders the album is contained in
as list of FolderInfo objects in form
["Top level folder", "sub folder 1", "sub folder 2", ...]
returns empty list if album is not in any folders"""
"""Returns list of FolderInfo objects for each folder the album is contained in
or empty list if album is not in any folders
"""
try:
return self._folders
@@ -246,7 +261,7 @@ class AlbumInfo(AlbumInfoBaseClass):
parent_pk = self._db._dbalbum_details[self._uuid]["parentfolder"]
self._parent = (
FolderInfo(db=self._db, uuid=self._db._dbalbums_pk[parent_pk])
if parent_pk != self._db._folder_root_pk
if parent_pk is not None and parent_pk != self._db._folder_root_pk
else None
)
return self._parent
@@ -281,27 +296,79 @@ class AlbumInfo(AlbumInfoBaseClass):
f"Photo with uuid {photo.uuid} does not appear to be in this album"
)
def asdict(self):
"""Return album info as a dict; does not include photos"""
dict_data = super().asdict()
dict_data["title"] = self.title
dict_data["folder_names"] = self.folder_names
dict_data["folder_list"] = [f.uuid for f in self.folder_list]
dict_data["sort_order"] = self.sort_order
dict_data["parent"] = self.parent.uuid if self.parent else None
return dict_data
class ImportInfo(AlbumInfoBaseClass):
"""Information about import sessions"""
def __init__(self, db, uuid):
self._uuid = uuid
self._db = db
if self._db._db_version >= _PHOTOS_5_VERSION:
return super().__init__(db=db, uuid=uuid)
import_session = self._db._db_import_group[self._uuid]
try:
self._creation_date_timestamp = import_session[3]
except (ValueError, TypeError, KeyError):
self._creation_date_timestamp = datetime(1970, 1, 1)
self._start_date_timestamp = self._creation_date_timestamp
self._end_date_timestamp = self._creation_date_timestamp
self._title = import_session[2]
self._local_tz = get_local_tz(
datetime.fromtimestamp(self._creation_date_timestamp + TIME_DELTA)
)
@property
def title(self):
"""return title / name of import session"""
return self._title
@property
def photos(self):
"""return list of photos contained in import session"""
try:
return self._photos
except AttributeError:
uuid_list, sort_order = zip(
*[
(uuid, self._db._dbphotos[uuid]["fok_import_session"])
for uuid in self._db._dbphotos
if self._db._dbphotos[uuid]["import_uuid"] == self.uuid
if self._db._db_version >= _PHOTOS_5_VERSION:
uuid_list, sort_order = zip(
*[
(uuid, self._db._dbphotos[uuid]["fok_import_session"])
for uuid in self._db._dbphotos
if self._db._dbphotos[uuid]["import_uuid"] == self.uuid
]
)
sorted_uuid = sort_list_by_keys(uuid_list, sort_order)
self._photos = self._db.photos_by_uuid(sorted_uuid)
else:
import_photo_uuids = [
u
for u in self._db._dbphotos
if self._db._dbphotos[u]["import_uuid"] == self.uuid
]
)
sorted_uuid = sort_list_by_keys(uuid_list, sort_order)
self._photos = self._db.photos_by_uuid(sorted_uuid)
self._photos = self._db.photos_by_uuid(import_photo_uuids)
return self._photos
def asdict(self):
"""Return import info as a dict; does not include photos"""
return {
"uuid": self.uuid,
"creation_date": self.creation_date,
"start_date": self.start_date,
"end_date": self.end_date,
"title": self.title,
}
def __bool__(self):
"""Always returns True
A photo without an import session will return None for import_info,
@@ -309,13 +376,32 @@ class ImportInfo(AlbumInfoBaseClass):
"""
return True
class ProjectInfo(AlbumInfo):
"""
ProjectInfo with info about projects
Projects are cards, calendars, slideshows, etc.
"""
...
@property
def folder_names(self):
"""Return hierarchical list of folders the album is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2", ...]
or empty list if album is not in any folders
"""
# projects are not in folders
return []
@property
def folder_list(self):
"""Returns list of FolderInfo objects for each folder the album is contained in
or empty list if album is not in any folders
"""
# projects are not in folders
return []
class FolderInfo:
@@ -386,7 +472,7 @@ class FolderInfo:
parent_pk = self._db._dbalbum_details[self._uuid]["parentfolder"]
self._parent = (
FolderInfo(db=self._db, uuid=self._db._dbalbums_pk[parent_pk])
if parent_pk != self._db._folder_root_pk
if parent_pk is not None and parent_pk != self._db._folder_root_pk
else None
)
return self._parent
@@ -416,6 +502,16 @@ class FolderInfo:
self._folders = folders
return self._folders
def asdict(self):
"""Return folder info as a dict"""
return {
"title": self.title,
"uuid": self.uuid,
"parent": self.parent.uuid if self.parent is not None else None,
"subfolders": [f.uuid for f in self.subfolders],
"albums": [a.uuid for a in self.album_info],
}
def __len__(self):
"""returns count of folders + albums contained in the folder"""
return len(self.subfolders) + len(self.album_info)

View File

@@ -5,6 +5,7 @@ import sys
from rich import print
from rich.traceback import install as install_traceback
from osxphotos.utils import is_macos
from osxphotos.debug import (
debug_breakpoint,
debug_watch,
@@ -44,11 +45,21 @@ if args.get("--debug", False):
print("Debugging enabled", file=sys.stderr)
from .about import about
from .add_locations import add_locations
from .albums import albums
from .cli import cli_main
from .common import get_photos_db
from .cli_commands import (
abort,
echo,
echo_error,
logger,
query_command,
selection_command,
verbose,
)
from .cli_params import DB_OPTION, DEBUG_OPTIONS, JSON_OPTION
from .common import OSXPHOTOS_HIDDEN, get_photos_db
from .debug_dump import debug_dump
from .docs import docs_command
from .dump import dump
from .exiftool_cli import exiftool
from .export import export
@@ -58,48 +69,71 @@ from .help import help
from .info import info
from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
from .kvstore import kvstore
from .labels import labels
from .list import _list_libraries, list_libraries
from .orphans import orphans
from .persons import persons
from .photo_inspect import photo_inspect
from .places import places
from .query import query
from .repl import repl
from .snap_diff import diff, snap
from .theme import theme
from .tutorial import tutorial
from .uuid import uuid
from .version import version
if is_macos:
from .add_locations import add_locations
from .batch_edit import batch_edit
from .import_cli import import_cli
from .photo_inspect import photo_inspect
from .show_command import show
from .sync import sync
from .timewarp import timewarp
from .uuid import uuid
install_traceback()
__all__ = [
"abort",
"about",
"add_locations",
"albums",
"batch_edit",
"cli_main",
"debug_dump",
"diff",
"docs_command",
"dump",
"echo",
"echo_error",
"exiftool_cli",
"export",
"exportdb",
"grep",
"help",
"import_cli",
"info",
"install",
"keywords",
"kvstore",
"labels",
"list_libraries",
"list_libraries",
"logger",
"orphans",
"persons",
"photo_inspect",
"places",
"query",
"query_command",
"repl",
"run",
"selection_command",
"set_debug",
"show",
"snap",
"tutorial",
"uuid",
"verbose",
]

View File

@@ -5,19 +5,22 @@ from __future__ import annotations
import datetime
import click
import photoscript
import osxphotos
from osxphotos.queryoptions import IncompatibleQueryOptions, query_options_from_kwargs
from osxphotos.utils import pluralize
from osxphotos.utils import assert_macos, pluralize
from .cli_params import QUERY_OPTIONS, THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import rich_echo_error as echo_error
from .common import QUERY_OPTIONS, THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .param_types import TimeOffset
from .rich_progress import rich_progress
from .verbose import get_verbose_console, verbose_print
assert_macos()
import photoscript
def get_location(
photos: list[osxphotos.PhotoInfo], idx: int, window: datetime.timedelta
@@ -135,8 +138,6 @@ def add_locations(
verbose("Searching for photos with missing location data...")
# load photos database
photosdb = osxphotos.PhotosDB(verbose=verbose)
try:
query_options = query_options_from_kwargs(**kwargs)
except IncompatibleQueryOptions as e:
@@ -144,6 +145,7 @@ def add_locations(
echo_error(ctx.obj.group.commands["repl"].get_help(ctx))
ctx.exit(1)
photosdb = osxphotos.PhotosDB(verbose=verbose)
photos = photosdb.query(query_options)
# sort photos by date

View File

@@ -6,12 +6,12 @@ import click
import yaml
import osxphotos
from .common import DB_ARGUMENT, DB_OPTION, JSON_OPTION, get_photos_db
from .list import _list_libraries
from osxphotos._constants import _PHOTOS_4_VERSION
from .cli_params import DB_ARGUMENT, DB_OPTION, JSON_OPTION
from .common import get_photos_db
from .list import _list_libraries
@click.command()
@DB_OPTION
@@ -36,7 +36,8 @@ def albums(ctx, cli_obj, db, json_, photos_library):
if photosdb.db_version > _PHOTOS_4_VERSION:
albums["shared albums"] = photosdb.albums_shared_as_dict
if json_ or cli_obj.json:
# cli_obj will be None if called from pytest
if json_ or (cli_obj and cli_obj.json):
click.echo(json.dumps(albums, ensure_ascii=False))
else:
click.echo(yaml.dump(albums, sort_keys=False, allow_unicode=True))

315
osxphotos/cli/batch_edit.py Normal file
View File

@@ -0,0 +1,315 @@
"""
batch-edit command for osxphotos CLI
"""
from __future__ import annotations
import functools
import json
import sys
import click
import osxphotos
from osxphotos.phototemplate import RenderOptions
from osxphotos.sqlitekvstore import SQLiteKVStore
from osxphotos.utils import assert_macos
assert_macos()
import photoscript
from .cli_commands import echo, echo_error, selection_command, verbose
from .kvstore import kvstore
from .param_types import Latitude, Longitude, TemplateString
@selection_command(name="batch-edit")
@click.option(
"--title",
metavar="TITLE_TEMPLATE",
type=TemplateString(),
help="Set title of photo.",
)
@click.option(
"--description",
metavar="DESCRIPTION_TEMPLATE",
type=TemplateString(),
help="Set description of photo.",
)
@click.option(
"--keyword",
metavar="KEYWORD_TEMPLATE",
type=TemplateString(),
multiple=True,
help="Add keywords to photo. May be specified multiple times.",
)
@click.option(
"--replace-keywords",
is_flag=True,
help="When specified with --keyword, replace existing keywords. "
"Default is to add to existing keywords.",
)
@click.option(
"--location",
metavar="LATITUDE LONGITUDE",
type=click.Tuple([Latitude(), Longitude()]),
help="Set location of photo. "
"Must be specified as a pair of numbers with latitude in the range -90 to 90 and longitude in the range -180 to 180.",
)
@click.option("--dry-run", is_flag=True, help="Don't actually change anything.")
@click.option(
"--undo",
is_flag=True,
help="Restores photo metadata to what it was prior to the last batch edit. "
"May be combined with --dry-run.",
)
def batch_edit(
photos: list[osxphotos.PhotoInfo],
title,
description,
keyword,
replace_keywords,
location,
dry_run,
undo,
**kwargs,
):
"""
Batch edit photo metadata such as title, description, keywords, etc.
Operates on currently selected photos.
Select one or more photos in Photos then run this command to edit the metadata.
For example:
\b
osxphotos batch-edit \\
--verbose \\
--title "California vacation 2023 {created.year}-{created.dd}-{created.mm} {counter:03d}" \\
--description "{place.name}" \\
--keyword "Family" --keyword "Travel"
This will set the title to "California vacation 2023 2023-02-20 001", and so on,
the description to the reverse geolocation place name,
and add the keywords "Family" and "Travel".
--title, --description, and --keyword may be any valid template string.
See https://rhettbull.github.io/osxphotos/template_help.html
or `osxphotos docs` for more information on the osxphotos template system.
"""
if not title and not description and not keyword and not location and not undo:
echo_error(
"[error] Must specify at least one of: "
" --title, --description, --keyword, --location, --undo. "
"Use --help for more information."
)
sys.exit(1)
if undo and (title or description or keyword or location):
echo_error(
"[error] Cannot specify --undo and any options other than --dry-run. "
"Use --help for more information."
)
sys.exit(1)
if replace_keywords and not keyword:
echo_error(
"[error] Cannot specify --replace-keywords without --keyword. "
"Use --help for more information."
)
sys.exit(1)
if not photos:
echo_error("[error] No photos selected")
sys.exit(1)
# sort photos by date so that {counter} order is correct
photos.sort(key=lambda p: p.date)
undo_store = kvstore("batch_edit")
verbose(f"Undo database stored in [filepath]{undo_store.path}", level=2)
echo(f"Processing [num]{len(photos)}[/] photos...")
for photo in photos:
verbose(
f"Processing [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
if undo:
undo_photo_edits(photo, undo_store, dry_run)
continue
save_photo_undo_info(undo_store, photo)
set_photo_title_from_template(photo, title, dry_run)
set_photo_description_from_template(photo, description, dry_run)
set_photo_keywords_from_template(photo, keyword, replace_keywords, dry_run)
set_photo_location(photo, location, dry_run)
# cache photoscript Photo object to avoid re-creating it for each photo
# maxsize=1 as this function is called repeatedly for each photo then
# the next photo is processed
@functools.lru_cache(maxsize=1)
def photoscript_photo(photo: osxphotos.PhotoInfo) -> photoscript.Photo:
"""Return photoscript Photo object for photo"""
return photoscript.Photo(photo.uuid)
def save_photo_undo_info(undo_store: SQLiteKVStore, photo: osxphotos.PhotoInfo):
"""Save undo information to undo store"""
undo_store[photo.uuid] = photo.json()
def undo_photo_edits(
photo: osxphotos.PhotoInfo, undo_store: SQLiteKVStore, dry_run: bool
):
"""Undo edits for photo"""
if not (undo_info := undo_store.get(photo.uuid)):
verbose(
f"[warning] No undo information for photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
return
undo_info = json.loads(undo_info)
ps_photo = photoscript_photo(photo)
exiting_title, exiting_description, exiting_keywords, exiting_location = (
photo.title,
photo.description,
sorted(photo.keywords),
photo.location,
)
previous_title, previous_description, previous_keywords, previous_location = (
undo_info.get("title"),
undo_info.get("description"),
sorted(undo_info.get("keywords")),
(undo_info.get("latitude"), undo_info.get("longitude")),
)
verbose(
f"Undoing edits for [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
for name, existing, previous in (
("title", exiting_title, previous_title),
("description", exiting_description, previous_description),
("keywords", exiting_keywords, previous_keywords),
("location", exiting_location, previous_location),
):
if existing != previous:
verbose(
f" [i]{name}[/]: [change]{existing}[/] -> [no_change]{previous}[/]"
)
if not dry_run:
setattr(ps_photo, name, previous)
else:
verbose(f" [i]{name} (no change)[/]: [no_change]{existing}[/]", level=2)
def set_photo_title_from_template(
photo: osxphotos.PhotoInfo, title_template: str, dry_run: bool
):
"""Set photo title from template"""
if not title_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
title_string, _ = photo.render_template(title_template, render_options)
title_string = [ts for ts in title_string if ts]
if not title_string:
verbose(
f"No title returned from template, nothing to do: [bold]{title_template}"
)
return
if len(title_string) > 1:
echo_error(
f"[error] Title template must return a single string: [bold]{title_string}"
)
sys.exit(1)
verbose(f"Setting [i]title[/i] to [bold]{title_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.title = title_string[0]
def set_photo_description_from_template(
photo: osxphotos.PhotoInfo, description_template: str, dry_run: bool
):
"""Set photo description from template"""
if not description_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
description_string, _ = photo.render_template(description_template, render_options)
description_string = [ds for ds in description_string if ds]
if not description_string:
verbose(
f"No description returned from template, nothing to do: [bold]{description_template}"
)
return
if len(description_string) > 1:
echo_error(
f"[error] Description template must return a single string: [bold]{description_string}"
)
sys.exit(1)
verbose(f"Setting [i]description[/] to [bold]{description_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.description = description_string[0]
def set_photo_keywords_from_template(
photo: osxphotos.PhotoInfo,
keyword_template: list[str],
replace_keywords: bool,
dry_run: bool,
):
"""Set photo keywords from template"""
if not keyword_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
keywords = set()
for kw in keyword_template:
kw_string, _ = photo.render_template(kw, render_options)
if kw_string:
# filter out empty strings
keywords.update([k for k in kw_string if k])
if not keywords:
verbose(
f"No keywords returned from template, nothing to do: [bold]{keyword_template}"
)
return
if not replace_keywords:
keywords.update(photo.keywords)
verbose(
f"Setting [i]keywords[/] to {', '.join(f'[bold]{kw}[/]' for kw in keywords)}"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.keywords = list(keywords)
def set_photo_location(
photo: osxphotos.PhotoInfo, location: tuple[float, float], dry_run: bool
):
"""Set photo location"""
if not location or location[0] is None or location[1] is None:
return
latitude, longitude = location
verbose(
f"Setting [i]location[/] to [num]{latitude:.6f}[/], [num]{longitude:.6f}[/]"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.location = (latitude, longitude)

View File

@@ -9,20 +9,20 @@ import click
from osxphotos._constants import PROFILE_SORT_KEYS
from osxphotos._version import __version__
from osxphotos.utils import is_macos
from .about import about
from .add_locations import add_locations
from .albums import albums
from .common import DB_OPTION, JSON_OPTION, OSXPHOTOS_HIDDEN
from .cli_params import DB_OPTION, DEBUG_OPTIONS, JSON_OPTION, VERSION_OPTION
from .common import OSXPHOTOS_HIDDEN
from .debug_dump import debug_dump
from .docs import docs
from .docs import docs_command
from .dump import dump
from .exiftool_cli import exiftool
from .export import export
from .exportdb import exportdb
from .grep import grep
from .help import help
from .import_cli import import_cli
from .info import info
from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
@@ -30,18 +30,23 @@ from .labels import labels
from .list import list_libraries
from .orphans import orphans
from .persons import persons
from .photo_inspect import photo_inspect
from .places import places
from .query import query
from .repl import repl
from .snap_diff import diff, snap
from .sync import sync
from .theme import theme
from .timewarp import timewarp
from .tutorial import tutorial
from .uuid import uuid
from .version import version
from .common import DEBUG_OPTIONS
if is_macos:
from .add_locations import add_locations
from .batch_edit import batch_edit
from .import_cli import import_cli
from .photo_inspect import photo_inspect
from .show_command import show
from .sync import sync
from .timewarp import timewarp
from .uuid import uuid
# Click CLI object & context settings
@@ -56,7 +61,7 @@ CTX_SETTINGS = dict(help_option_names=["-h", "--help"])
@click.group(context_settings=CTX_SETTINGS)
@click.version_option(__version__, "--version", "-v")
@VERSION_OPTION
@DB_OPTION
@JSON_OPTION
@DEBUG_OPTIONS
@@ -104,20 +109,18 @@ def cli_main(ctx, db, json_, profile, profile_sort, **kwargs):
# install CLI commands
for command in [
commands = [
about,
add_locations,
albums,
debug_dump,
diff,
docs,
docs_command,
dump,
exiftool,
export,
exportdb,
grep,
help,
import_cli,
info,
install,
keywords,
@@ -125,18 +128,28 @@ for command in [
list_libraries,
orphans,
persons,
photo_inspect,
places,
query,
repl,
run,
snap,
sync,
theme,
timewarp,
tutorial,
uninstall,
uuid,
version,
]:
]
if is_macos:
commands += [
add_locations,
batch_edit,
import_cli,
photo_inspect,
show,
sync,
timewarp,
uuid,
]
for command in commands:
cli_main.add_command(command)

View File

@@ -0,0 +1,310 @@
"""Helper functions to make writing an osxphotos CLI tool easy.
Includes decorator to create an osxphotos query command to be run via `osxphotos run example.py`.
May also be run via `python example.py` if you have pip installed osxphotos
"""
from __future__ import annotations
import logging
import sys
import typing as t # match style used in Click source code
import click
from osxphotos.photosdb import PhotosDB
from osxphotos.queryoptions import QueryOptions, query_options_from_kwargs
from osxphotos.sqlitekvstore import SQLiteKVStore
from .cli_params import (
_DB_PARAMETER,
_QUERY_PARAMETERS_DICT,
DB_OPTION,
THEME_OPTION,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
)
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import rich_echo_error as echo_error
from .click_rich_echo import set_rich_theme
from .color_themes import get_theme
from .verbose import verbose, verbose_print
logger = logging.getLogger("osxphotos")
# ensure echo, echo_error are configured with correct theme
set_rich_theme(get_theme())
__all__ = [
"abort",
"echo",
"echo_error",
"logger",
"query_command",
"selection_command",
"verbose",
]
def abort(message: str, exit_code: int = 1):
"""Abort with error message and exit code"""
echo_error(f"[error]{message}[/]")
sys.exit(exit_code)
def config_verbose_callback(ctx: click.Context, param: click.Parameter, value: t.Any):
"""Callback for --verbose option"""
# calling verbose_print() will set the verbose level for the verbose() function
theme = ctx.params.get("theme")
timestamp = ctx.params.get("timestamp")
verbose_print(verbose=value, timestamp=timestamp, theme=theme)
return value
def get_photos_for_query(ctx: click.Context):
"""Return list of PhotoInfo objects for the photos matching the query options in ctx.params"""
options = query_options_from_kwargs(**ctx.params)
db = ctx.params.get("db")
photosdb = PhotosDB(dbfile=db, verbose=verbose)
return photosdb.query(options=options)
def get_selected_photos(ctx: click.Context):
"""Return list of PhotoInfo objects for the photos currently selected in Photos.app"""
photosdb = PhotosDB(verbose=verbose)
return photosdb.query(options=QueryOptions(selected=True))
class QueryCommand(click.Command):
"""
Click command to create an osxphotos query command.
This class is used by the query_command decorator to create a click command
that runs an osxphotos query. It will automatically add the query options as
well as the --verbose, --timestamp, --theme, and --db options.
"""
standalone_mode = False
def __init__(
self,
name: t.Optional[str],
context_settings: t.Optional[t.Dict[str, t.Any]] = None,
callback: t.Optional[t.Callable[..., t.Any]] = None,
params: t.Optional[t.List[click.Parameter]] = None,
help: t.Optional[str] = None,
epilog: t.Optional[str] = None,
short_help: t.Optional[str] = None,
options_metavar: t.Optional[str] = "[OPTIONS]",
add_help_option: bool = True,
no_args_is_help: bool = False,
hidden: bool = False,
deprecated: bool = False,
) -> None:
self.params = params or []
self.params.append(
click.Option(
param_decls=["--verbose", "-V", "verbose_flag"],
count=True,
help="Print verbose output; may be specified multiple times for more verbose output.",
callback=config_verbose_callback,
)
)
self.params.append(
click.Option(
param_decls=["--timestamp"],
is_flag=True,
help="Add time stamp to verbose output",
)
)
self.params.append(
click.Option(
param_decls=["--theme"],
metavar="THEME",
type=click.Choice(
["dark", "light", "mono", "plain"], case_sensitive=False
),
help="Specify the color theme to use for output. "
"Valid themes are 'dark', 'light', 'mono', and 'plain'. "
"Defaults to 'dark' or 'light' depending on system dark mode setting.",
)
)
self.params.append(_DB_PARAMETER)
self.params.extend(_QUERY_PARAMETERS_DICT.values())
super().__init__(
name,
context_settings,
callback,
self.params,
help,
epilog,
short_help,
options_metavar,
add_help_option,
no_args_is_help,
hidden,
deprecated,
)
def make_context(
self,
info_name: t.Optional[str],
args: t.List[str],
parent: t.Optional[click.Context] = None,
**extra: t.Any,
) -> click.Context:
ctx = super().make_context(info_name, args, parent, **extra)
ctx.obj = self
photos = get_photos_for_query(ctx)
ctx.params["photos"] = photos
# remove params handled by this class
ctx.params.pop("verbose_flag")
ctx.params.pop("timestamp")
ctx.params.pop("theme")
return ctx
class SelectionCommand(click.Command):
"""
Click command to create an osxphotos selection command that runs on selected photos.
This class is used by the query_command decorator to create a click command
that runs on currently selected photos.
The --verbose, --timestamp, --theme, and --db options will also be added to the command.
"""
standalone_mode = False
def __init__(
self,
name: t.Optional[str],
context_settings: t.Optional[t.Dict[str, t.Any]] = None,
callback: t.Optional[t.Callable[..., t.Any]] = None,
params: t.Optional[t.List[click.Parameter]] = None,
help: t.Optional[str] = None,
epilog: t.Optional[str] = None,
short_help: t.Optional[str] = None,
options_metavar: t.Optional[str] = "[OPTIONS]",
add_help_option: bool = True,
no_args_is_help: bool = False,
hidden: bool = False,
deprecated: bool = False,
) -> None:
self.params = params or []
self.params.append(
click.Option(
param_decls=["--verbose", "-V", "verbose_flag"],
count=True,
help="Print verbose output; may be specified multiple times for more verbose output.",
callback=config_verbose_callback,
)
)
self.params.append(
click.Option(
param_decls=["--timestamp"],
is_flag=True,
help="Add time stamp to verbose output",
)
)
self.params.append(
click.Option(
param_decls=["--theme"],
metavar="THEME",
type=click.Choice(
["dark", "light", "mono", "plain"], case_sensitive=False
),
help="Specify the color theme to use for output. "
"Valid themes are 'dark', 'light', 'mono', and 'plain'. "
"Defaults to 'dark' or 'light' depending on system dark mode setting.",
)
)
self.params.append(
click.Option(
param_decls=["--library", "--db"],
required=False,
metavar="PHOTOS_LIBRARY_PATH",
default=None,
help=(
"Specify Photos database path. "
"Path to Photos library/database can be specified using either --db "
"or directly as PHOTOS_LIBRARY positional argument. "
"If neither --db or PHOTOS_LIBRARY provided, will attempt to find the library "
"to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary"
),
type=click.Path(exists=True),
)
)
super().__init__(
name,
context_settings,
callback,
self.params,
help,
epilog,
short_help,
options_metavar,
add_help_option,
no_args_is_help,
hidden,
deprecated,
)
def make_context(
self,
info_name: t.Optional[str],
args: t.List[str],
parent: t.Optional[click.Context] = None,
**extra: t.Any,
) -> click.Context:
ctx = super().make_context(info_name, args, parent, **extra)
ctx.obj = self
photos = get_selected_photos(ctx)
ctx.params["photos"] = photos
# remove params handled by this class
ctx.params.pop("verbose_flag")
ctx.params.pop("timestamp")
ctx.params.pop("theme")
return ctx
def query_command(name=None, cls=QueryCommand, **attrs):
"""Decorator to create an osxphotos command to be run via `osxphotos run example.py`
The command will be passed a list of PhotoInfo objects for all photos in Photos
matching the query options or all photos if no query options are specified.
The standard osxphotos query options will be added to the command.
The CLI will also be passed the following options:
--verbose
--timestamp
--theme
--db
"""
if callable(name) and cls:
return click.command(cls=cls, **attrs)(name)
return click.command(name, cls=cls, **attrs)
def selection_command(name=None, cls=SelectionCommand, **attrs):
"""Decorator to create an osxphotos command to be run via `osxphotos run example.py`
The command will be passed a list of PhotoInfo objects for all photos selected in Photos.
The CLI will also be passed the following options:
--verbose
--timestamp
--theme
--db
"""
if callable(name) and cls:
return click.command(cls=cls, **attrs)(name)
return click.command(name, cls=cls, **attrs)

738
osxphotos/cli/cli_params.py Normal file
View File

@@ -0,0 +1,738 @@
"""Common options & parameters for osxphotos CLI commands"""
from __future__ import annotations
import functools
from typing import Any, Callable
import click
import contextlib
from textwrap import dedent
from ..utils import is_macos
from .common import OSXPHOTOS_HIDDEN, print_version
from .param_types import *
__all__ = [
"DB_ARGUMENT",
"DB_OPTION",
"DEBUG_OPTIONS",
"DELETED_OPTIONS",
"FIELD_OPTION",
"JSON_OPTION",
"QUERY_OPTIONS",
"THEME_OPTION",
"TIMESTAMP_OPTION",
"VERBOSE_OPTION",
"VERSION_OPTION",
]
def validate_selected(ctx, param, value):
""" "Validate photos are actually selected when --selected is used"""
if not value:
# --selected not used, just return
return value
# imports here to avoid conflict with linux port
# TODO: fix this once linux port is complete
import photoscript
from applescript import ScriptError
selection = None
with contextlib.suppress(ScriptError):
# ScriptError raised if selection made in edit mode or Smart Albums (on older versions of Photos)
selection = photoscript.PhotosLibrary().selection
if not selection:
click.echo(
dedent(
"""
--selected option used but no photos selected in Photos.
To select photos in Photos use one of the following methods:
- Select a single photo: Click the photo, or press the arrow keys to quickly navigate to and select the photo.
- Select a group of adjacent photos in a day: Click the first photo, then hold down the Shift key while you click the last photo.
You can also hold down Shift and press the arrow keys, or simply drag to enclose the photos within the selection rectangle.
- Select photos in a day that are not adjacent to each other: Hold down the Command key as you click each photo.
- Deselect specific photos: Hold down the Command key and click the photos you want to deselect.
- Deselect all photos: Click an empty space in the window (not a photo).
"""
),
err=True,
)
ctx.exit(1)
return value
def _param_memo(f: Callable[..., Any], param: click.Parameter) -> None:
"""Add param to the list of params for a click.Command
This is directly from the click source code and
the implementation is thus tightly coupled to click internals
"""
if isinstance(f, click.Command):
f.params.append(param)
else:
if not hasattr(f, "__click_params__"):
f.__click_params__ = [] # type: ignore
f.__click_params__.append(param) # type: ignore
def make_click_option_decorator(*params: click.Parameter) -> Callable[..., Any]:
"""Make a decorator for a click option from one or more click Parameter objects"""
def decorator(wrapped=None) -> Callable[..., Any]:
"""Function decorator to add option to a click command.
Args:
wrapped: function to decorate (this is normally passed automatically
"""
if wrapped is None:
return decorator
def _add_options(wrapped):
"""Add query options to wrapped function"""
for param in params:
_param_memo(wrapped, param)
return wrapped
return _add_options(wrapped)
return decorator
VERSION_CHECK_OPTION = click.option("--no-version-check", required=False, is_flag=True)
_DB_PARAMETER = click.Option(
["--library", "--db", "db"],
required=False,
metavar="PHOTOS_LIBRARY_PATH",
default=None,
help=(
"Specify path to Photos library. "
"If not provided, will attempt to find the library to use in the following order: "
"1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary"
),
type=click.Path(exists=True),
)
DB_OPTION = make_click_option_decorator(_DB_PARAMETER)
DB_ARGUMENT = click.argument(
"photos_library",
nargs=-1,
type=DeprecatedPath(
exists=True,
deprecation_warning="The PHOTOS_LIBRARY argument is deprecated and "
"will be removed in a future version of osxphotos. "
"Use --library instead to specify the Photos Library path.",
),
)
_JSON_PARAMETER = click.Option(
["--json", "json_"],
required=False,
is_flag=True,
default=False,
help="Print output in JSON format.",
)
JSON_OPTION = make_click_option_decorator(_JSON_PARAMETER)
_FIELD_PARAMETER = click.Option(
["--field", "-f"],
metavar="FIELD TEMPLATE",
multiple=True,
nargs=2,
help="Output only specified custom fields. "
"FIELD is the name of the field and TEMPLATE is the template to use as the field value. "
"May be repeated to output multiple fields. "
"For example, to output photo uuid, name, and title: "
'`--field uuid "{uuid}" --field name "{original_name}" --field title "{title}"`.',
)
FIELD_OPTION = make_click_option_decorator(_FIELD_PARAMETER)
_DELETED_PARAMETERS = [
click.Option(
["--deleted"],
is_flag=True,
help="Include photos from the 'Recently Deleted' folder.",
),
click.Option(
["--deleted-only"],
is_flag=True,
help="Include only photos from the 'Recently Deleted' folder.",
),
]
DELETED_OPTIONS = make_click_option_decorator(*_DELETED_PARAMETERS)
# The following are used by the query command and by
# QUERY_OPTIONS to add the query options to other commands
# To add new query options, add them to _QUERY_OPTIONS as
# a click.Option, add them to osxphotos/photosdb/photosdb.py::PhotosDB.query(),
# and to osxphotos/query_options.py::QueryOptions
_QUERY_PARAMETERS_DICT = {
"--keyword": click.Option(
["--keyword"],
metavar="KEYWORD",
default=None,
multiple=True,
help="Search for photos with keyword KEYWORD. "
'If more than one keyword, treated as "OR", e.g. find photos matching any keyword',
),
"--no-keyword": click.Option(
["--no-keyword"],
is_flag=True,
help="Search for photos with no keyword.",
),
"--person": click.Option(
["--person"],
metavar="PERSON",
default=None,
multiple=True,
help="Search for photos with person PERSON. "
'If more than one person, treated as "OR", e.g. find photos matching any person',
),
"--album": click.Option(
["--album"],
metavar="ALBUM",
default=None,
multiple=True,
help="Search for photos in album ALBUM. "
'If more than one album, treated as "OR", e.g. find photos matching any album',
),
"--folder": click.Option(
["--folder"],
metavar="FOLDER",
default=None,
multiple=True,
help="Search for photos in an album in folder FOLDER. "
'If more than one folder, treated as "OR", e.g. find photos in any FOLDER. '
"Only searches top level folders (e.g. does not look at subfolders)",
),
"--name": click.Option(
["--name"],
metavar="FILENAME",
default=None,
multiple=True,
help="Search for photos with filename matching FILENAME. "
'If more than one --name options is specified, they are treated as "OR", '
"e.g. find photos matching any FILENAME. ",
),
"--uuid": click.Option(
["--uuid"],
metavar="UUID",
default=None,
multiple=True,
help="Search for photos with UUID(s). "
"May be repeated to include multiple UUIDs.",
),
"--uuid-from-file": click.Option(
["--uuid-from-file"],
metavar="FILE",
default=None,
multiple=False,
help="Search for photos with UUID(s) loaded from FILE. "
"Format is a single UUID per line. Lines preceded with # are ignored. "
"If FILE is '-', read UUIDs from stdin.",
type=PathOrStdin(exists=True),
),
"--title": click.Option(
["--title"],
metavar="TITLE",
default=None,
multiple=True,
help="Search for TITLE in title of photo.",
),
"--no-title": click.Option(
["--no-title"], is_flag=True, help="Search for photos with no title."
),
"--description": click.Option(
["--description"],
metavar="DESC",
default=None,
multiple=True,
help="Search for DESC in description of photo.",
),
"--no-description": click.Option(
["--no-description"],
is_flag=True,
help="Search for photos with no description.",
),
"--place": click.Option(
["--place"],
metavar="PLACE",
default=None,
multiple=True,
help="Search for PLACE in photo's reverse geolocation info",
),
"--no-place": click.Option(
["--no-place"],
is_flag=True,
help="Search for photos with no associated place name info (no reverse geolocation info)",
),
"--location": click.Option(
["--location"],
is_flag=True,
help="Search for photos with associated location info (e.g. GPS coordinates)",
),
"--no-location": click.Option(
["--no-location"],
is_flag=True,
help="Search for photos with no associated location info (e.g. no GPS coordinates)",
),
"--label": click.Option(
["--label"],
metavar="LABEL",
multiple=True,
help="Search for photos with image classification label LABEL (Photos 5+ only). "
'If more than one label, treated as "OR", e.g. find photos matching any label',
),
"--uti": click.Option(
["--uti"],
metavar="UTI",
default=None,
multiple=False,
help="Search for photos whose uniform type identifier (UTI) matches UTI",
),
"--ignore_case": click.Option(
["-i", "--ignore-case"],
is_flag=True,
help="Case insensitive search for title, description, place, keyword, person, or album.",
),
"--edited": click.Option(
["--edited"],
is_flag=True,
help="Search for photos that have been edited.",
),
"--not-edited": click.Option(
["--not-edited"],
is_flag=True,
help="Search for photos that have not been edited.",
),
"--external-edit": click.Option(
["--external-edit"],
is_flag=True,
help="Search for photos edited in external editor.",
),
"--favorite": click.Option(
["--favorite"], is_flag=True, help="Search for photos marked favorite."
),
"--not-favorite": click.Option(
["--not-favorite"],
is_flag=True,
help="Search for photos not marked favorite.",
),
"--hidden": click.Option(
["--hidden"], is_flag=True, help="Search for photos marked hidden."
),
"--not-hidden": click.Option(
["--not-hidden"],
is_flag=True,
help="Search for photos not marked hidden.",
),
"--shared": click.Option(
["--shared"],
is_flag=True,
help="Search for photos in shared iCloud album (Photos 5+ only).",
),
"--not-shared": click.Option(
["--not-shared"],
is_flag=True,
help="Search for photos not in shared iCloud album (Photos 5+ only).",
),
"--burst": click.Option(
["--burst"],
is_flag=True,
help="Search for photos that were taken in a burst.",
),
"--not-burst": click.Option(
["--not-burst"],
is_flag=True,
help="Search for photos that are not part of a burst.",
),
"--live": click.Option(
["--live"], is_flag=True, help="Search for Apple live photos"
),
"--not-live": click.Option(
["--not-live"],
is_flag=True,
help="Search for photos that are not Apple live photos.",
),
"--portrait": click.Option(
["--portrait"],
is_flag=True,
help="Search for Apple portrait mode photos.",
),
"--not-portrait": click.Option(
["--not-portrait"],
is_flag=True,
help="Search for photos that are not Apple portrait mode photos.",
),
"--screenshot": click.Option(
["--screenshot"], is_flag=True, help="Search for screenshot photos."
),
"--not-screenshot": click.Option(
["--not-screenshot"],
is_flag=True,
help="Search for photos that are not screenshot photos.",
),
"--slow-mo": click.Option(
["--slow-mo"], is_flag=True, help="Search for slow motion videos."
),
"--not-slow-mo": click.Option(
["--not-slow-mo"],
is_flag=True,
help="Search for photos that are not slow motion videos.",
),
"--time-lapse": click.Option(
["--time-lapse"], is_flag=True, help="Search for time lapse videos."
),
"--not-time-lapse": click.Option(
["--not-time-lapse"],
is_flag=True,
help="Search for photos that are not time lapse videos.",
),
"--hdr": click.Option(
["--hdr"],
is_flag=True,
help="Search for high dynamic range (HDR) photos.",
),
"--not-hdr": click.Option(
["--not-hdr"],
is_flag=True,
help="Search for photos that are not HDR photos.",
),
"--selfie": click.Option(
["--selfie"],
is_flag=True,
help="Search for selfies (photos taken with front-facing cameras).",
),
"--not-selfie": click.Option(
["--not-selfie"],
is_flag=True,
help="Search for photos that are not selfies.",
),
"--panorama": click.Option(
["--panorama"], is_flag=True, help="Search for panorama photos."
),
"--not-panorama": click.Option(
["--not-panorama"],
is_flag=True,
help="Search for photos that are not panoramas.",
),
"--has-raw": click.Option(
["--has-raw"],
is_flag=True,
help="Search for photos with both a jpeg and raw version",
),
"--only-movies": click.Option(
["--only-movies"],
is_flag=True,
help="Search only for movies (default searches both images and movies).",
),
"--only-photos": click.Option(
["--only-photos"],
is_flag=True,
help="Search only for photos/images (default searches both images and movies).",
),
"--from-date": click.Option(
["--from-date"],
help="Search by item start date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
"--to-date": click.Option(
["--to-date"],
help="Search by item end date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
"--from-time": click.Option(
["--from-time"],
help="Search by item start time of day, e.g. 12:00, or 12:00:00.",
type=TimeISO8601(),
),
"--to-time": click.Option(
["--to-time"],
help="Search by item end time of day, e.g. 12:00 or 12:00:00.",
type=TimeISO8601(),
),
"--year": click.Option(
["--year"],
metavar="YEAR",
help="Search for items from a specific year, e.g. --year 2022 to find all photos from the year 2022. "
"May be repeated to search multiple years.",
multiple=True,
type=int,
),
"--added-before": click.Option(
["--added-before"],
metavar="DATE",
help="Search for items added to the library before a specific date/time, "
"e.g. --added-before e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
"--added-after": click.Option(
["--added-after"],
metavar="DATE",
help="Search for items added to the libray after a specific date/time, "
"e.g. --added-after e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
"--added-in-last": click.Option(
["--added-in-last"],
metavar="TIME_DELTA",
help="Search for items added to the library in the last TIME_DELTA, "
"where TIME_DELTA is a string like "
"'12 hrs', '1 day', '1d', '1 week', '2weeks', '1 month', '1 year'. "
"for example, `--added-in-last 7d` and `--added-in-last '1 week'` are equivalent. "
"months are assumed to be 30 days and years are assumed to be 365 days. "
"Common English abbreviations are accepted, e.g. d, day, days or m, min, minutes.",
type=TimeOffset(),
),
"--has-comment": click.Option(
["--has-comment"],
is_flag=True,
help="Search for photos that have comments.",
),
"--no-comment": click.Option(
["--no-comment"],
is_flag=True,
help="Search for photos with no comments.",
),
"--has-likes": click.Option(
["--has-likes"], is_flag=True, help="Search for photos that have likes."
),
"--no-likes": click.Option(
["--no-likes"], is_flag=True, help="Search for photos with no likes."
),
"--is-reference": click.Option(
["--is-reference"],
is_flag=True,
help="Search for photos that were imported as referenced files (not copied into Photos library).",
),
"--not-reference": click.Option(
["--not-reference"],
is_flag=True,
help="Search for photos that are not references, that is, they were copied into the Photos library "
"and are managed by Photos.",
),
"--in-album": click.Option(
["--in-album"],
is_flag=True,
help="Search for photos that are in one or more albums.",
),
"--not-in-album": click.Option(
["--not-in-album"],
is_flag=True,
help="Search for photos that are not in any albums.",
),
"--duplicate": click.Option(
["--duplicate"],
is_flag=True,
help="Search for photos with possible duplicates. osxphotos will compare signatures of photos, "
"evaluating date created, size, height, width, and edited status to find *possible* duplicates. "
"This does not compare images byte-for-byte nor compare hashes but should find photos imported multiple "
"times or duplicated within Photos.",
),
"--min-size": click.Option(
["--min-size"],
metavar="SIZE",
type=BitMathSize(),
help="Search for photos with size >= SIZE bytes. "
"The size evaluated is the photo's original size (when imported to Photos). "
"Size may be specified as integer bytes or using SI or NIST units. "
"For example, the following are all valid and equivalent sizes: '1048576' '1.048576MB', '1 MiB'.",
),
"--max-size": click.Option(
["--max-size"],
metavar="SIZE",
type=BitMathSize(),
help="Search for photos with size <= SIZE bytes. "
"The size evaluated is the photo's original size (when imported to Photos). "
"Size may be specified as integer bytes or using SI or NIST units. "
"For example, the following are all valid and equivalent sizes: '1048576' '1.048576MB', '1 MiB'.",
),
"--missing": click.Option(
["--missing"], is_flag=True, help="Search for photos missing from disk."
),
"--not-missing": click.Option(
["--not-missing"],
is_flag=True,
help="Search for photos present on disk (e.g. not missing).",
),
"--cloudasset": click.Option(
["--cloudasset"],
is_flag=True,
help="Search for photos that are part of an iCloud library",
),
"--not-cloudasset": click.Option(
["--not-cloudasset"],
is_flag=True,
help="Search for photos that are not part of an iCloud library",
),
"--incloud": click.Option(
["--incloud"],
is_flag=True,
help="Search for photos that are in iCloud (have been synched)",
),
"--not-incloud": click.Option(
["--not-incloud"],
is_flag=True,
help="Search for photos that are not in iCloud (have not been synched)",
),
"--regex": click.Option(
["--regex"],
metavar="REGEX TEMPLATE",
nargs=2,
multiple=True,
help="Search for photos where TEMPLATE matches regular expression REGEX. "
"For example, to find photos in an album that begins with 'Beach': '--regex \"^Beach\" \"{album}\"'. "
"You may specify more than one regular expression match by repeating '--regex' with different arguments.",
),
"--selected": click.Option(
["--selected"],
is_flag=True,
help="Filter for photos that are currently selected in Photos.",
callback=validate_selected,
),
"--exif": click.Option(
["--exif"],
metavar="EXIF_TAG VALUE",
nargs=2,
multiple=True,
help="Search for photos where EXIF_TAG exists in photo's EXIF data and contains VALUE. "
"For example, to find photos created by Adobe Photoshop: `--exif Software 'Adobe Photoshop' `"
"or to find all photos shot on a Canon camera: `--exif Make Canon`. "
"EXIF_TAG can be any valid exiftool tag, with or without group name, e.g. `EXIF:Make` or `Make`. "
"To use --exif, exiftool must be installed and in the path.",
),
"--query-eval": click.Option(
["--query-eval"],
metavar="CRITERIA",
multiple=True,
help="Evaluate CRITERIA to filter photos. "
"CRITERIA will be evaluated in context of the following python list comprehension: "
"`photos = [photo for photo in photos if CRITERIA]` "
"where photo represents a PhotoInfo object. "
"For example: `--query-eval photo.favorite` returns all photos that have been "
"favorited and is equivalent to --favorite. "
"You may specify more than one CRITERIA by using --query-eval multiple times. "
"CRITERIA must be a valid python expression. "
"See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.",
),
"--query-function": click.Option(
["--query-function"],
metavar="filename.py::function",
multiple=True,
type=FunctionCall(),
help="Run function to filter photos. Use this in format: --query-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. "
+ "Your function will be passed a list of PhotoInfo objects and is expected to return a filtered list of PhotoInfo objects. "
+ "You may use more than one function by repeating the --query-function option with a different value. "
+ "Your query function will be called after all other query options have been evaluated. "
+ "See https://github.com/RhetTbull/osxphotos/blob/master/examples/query_function.py for example of how to use this option.",
),
}
if not is_macos:
del _QUERY_PARAMETERS_DICT["--selected"]
def QUERY_OPTIONS(
wrapped=None, *, exclude: list[str] | None = None
) -> Callable[..., Any]:
"""Function decorator to add query options to a click command.
Args:
wrapped: function to decorate (this is normally passed automatically
exclude: list of query options to exclude from the command, for example `exclude=["--shared"]
"""
if wrapped is None:
return functools.partial(QUERY_OPTIONS, exclude=exclude)
exclude = exclude or []
def _add_options(wrapped):
"""Add query options to wrapped function"""
for option in reversed(_QUERY_PARAMETERS_DICT.keys()):
if option in exclude:
continue
click_opt = _QUERY_PARAMETERS_DICT[option]
_param_memo(wrapped, click_opt)
return wrapped
return _add_options(wrapped)
_DEBUG_PARAMETERS = [
click.Option(
["--debug"],
is_flag=True,
help="Enable debug output.",
hidden=OSXPHOTOS_HIDDEN,
),
click.Option(
["--watch"],
metavar="MODULE::NAME",
multiple=True,
help="Watch function or method calls. The function to watch must be in the form "
"MODULE::NAME where MODULE is the module path and NAME is the function or method name "
"contained in the module. For example, to watch all calls to FileUtil.copy() which is in "
"osxphotos.fileutil, use: "
"'--watch osxphotos.fileutil::FileUtil.copy'. More than one --watch option can be specified.",
hidden=OSXPHOTOS_HIDDEN,
),
click.Option(
["--breakpoint"],
metavar="MODULE::NAME",
multiple=True,
help="Add breakpoint to function calls. The function to watch must be in the form "
"MODULE::NAME where MODULE is the module path and NAME is the function or method name "
"contained in the module. For example, to set a breakpoint for calls to "
"FileUtil.copy() which is in osxphotos.fileutil, use: "
"'--breakpoint osxphotos.fileutil::FileUtil.copy'. More than one --breakpoint option can be specified.",
hidden=OSXPHOTOS_HIDDEN,
),
]
DEBUG_OPTIONS = make_click_option_decorator(*_DEBUG_PARAMETERS)
_THEME_PARAMETER = click.Option(
["--theme"],
metavar="THEME",
type=click.Choice(["dark", "light", "mono", "plain"], case_sensitive=False),
help="Specify the color theme to use for output. "
"Valid themes are 'dark', 'light', 'mono', and 'plain'. "
"Defaults to 'dark' or 'light' depending on system dark mode setting.",
)
THEME_OPTION = make_click_option_decorator(_THEME_PARAMETER)
_VERBOSE_PARAMETER = click.Option(
["--verbose", "-V", "verbose_flag"],
count=True,
help="Print verbose output; may be specified multiple times for more verbose output.",
)
VERBOSE_OPTION = make_click_option_decorator(_VERBOSE_PARAMETER)
_TIMESTAMP_PARAMETER = click.Option(
["--timestamp"], is_flag=True, help="Add time stamp to verbose output"
)
TIMESTAMP_OPTION = make_click_option_decorator(_TIMESTAMP_PARAMETER)
_VERSION_PARAMETER = click.Option(
["--version", "-v", "_version_flag"],
is_flag=True,
help="Show the version and exit.",
callback=print_version,
)
VERSION_OPTION = make_click_option_decorator(_VERSION_PARAMETER)

View File

@@ -119,7 +119,9 @@ def rich_echo(
# if not outputting to terminal, use a huge width to avoid wrapping
# otherwise tests fail
width = 10_000
console = get_rich_console() or Console(theme=theme, width=width)
console = get_rich_console() or Console(
theme=theme or get_rich_theme(), width=width
)
if markdown:
message = Markdown(message)
# Markdown always adds a new line so disable unless explicitly specified

View File

@@ -1,8 +1,11 @@
"""Globals and constants use by the CLI commands"""
from __future__ import annotations
import os
import pathlib
import platform
import sys
from datetime import datetime
import click
@@ -12,9 +15,7 @@ from xdg import xdg_config_home, xdg_data_home
import osxphotos
from osxphotos._constants import APP_NAME
from osxphotos._version import __version__
from osxphotos.utils import get_latest_version
from .param_types import *
from osxphotos.utils import get_latest_version, get_macos_version
# used to show/hide hidden commands
OSXPHOTOS_HIDDEN = not bool(os.getenv("OSXPHOTOS_SHOW_HIDDEN", default=False))
@@ -31,16 +32,6 @@ CLI_COLOR_WARNING = "yellow"
__all__ = [
"CLI_COLOR_ERROR",
"CLI_COLOR_WARNING",
"DB_ARGUMENT",
"DB_OPTION",
"DEBUG_OPTIONS",
"DELETED_OPTIONS",
"FIELD_OPTION",
"JSON_OPTION",
"QUERY_OPTIONS",
"THEME_OPTION",
"VERBOSE_OPTION",
"TIMESTAMP_OPTION",
"get_photos_db",
"noop",
"time_stamp",
@@ -90,540 +81,6 @@ def get_photos_db(*db_options):
return None
VERSION_CHECK_OPTION = click.option("--no-version-check", required=False, is_flag=True)
DB_OPTION = click.option(
"--db",
required=False,
metavar="PHOTOS_LIBRARY_PATH",
default=None,
help=(
"Specify Photos database path. "
"Path to Photos library/database can be specified using either --db "
"or directly as PHOTOS_LIBRARY positional argument. "
"If neither --db or PHOTOS_LIBRARY provided, will attempt to find the library "
"to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary"
),
type=click.Path(exists=True),
)
DB_ARGUMENT = click.argument("photos_library", nargs=-1, type=click.Path(exists=True))
JSON_OPTION = click.option(
"--json",
"json_",
required=False,
is_flag=True,
default=False,
help="Print output in JSON format.",
)
FIELD_OPTION = click.option(
"--field",
"-f",
metavar="FIELD TEMPLATE",
multiple=True,
nargs=2,
help="Output only specified custom fields. "
"FIELD is the name of the field and TEMPLATE is the template to use as the field value. "
"May be repeated to output multiple fields. "
"For example, to output photo uuid, name, and title: "
'`--field uuid "{uuid}" --field name "{original_name}" --field title "{title}"`.',
)
def DELETED_OPTIONS(f):
o = click.option
options = [
o(
"--deleted",
is_flag=True,
help="Include photos from the 'Recently Deleted' folder.",
),
o(
"--deleted-only",
is_flag=True,
help="Include only photos from the 'Recently Deleted' folder.",
),
]
for o in options[::-1]:
f = o(f)
return f
def QUERY_OPTIONS(f):
o = click.option
options = [
o(
"--keyword",
metavar="KEYWORD",
default=None,
multiple=True,
help="Search for photos with keyword KEYWORD. "
'If more than one keyword, treated as "OR", e.g. find photos matching any keyword',
),
o(
"--no-keyword",
is_flag=True,
help="Search for photos with no keyword.",
),
o(
"--person",
metavar="PERSON",
default=None,
multiple=True,
help="Search for photos with person PERSON. "
'If more than one person, treated as "OR", e.g. find photos matching any person',
),
o(
"--album",
metavar="ALBUM",
default=None,
multiple=True,
help="Search for photos in album ALBUM. "
'If more than one album, treated as "OR", e.g. find photos matching any album',
),
o(
"--folder",
metavar="FOLDER",
default=None,
multiple=True,
help="Search for photos in an album in folder FOLDER. "
'If more than one folder, treated as "OR", e.g. find photos in any FOLDER. '
"Only searches top level folders (e.g. does not look at subfolders)",
),
o(
"--name",
metavar="FILENAME",
default=None,
multiple=True,
help="Search for photos with filename matching FILENAME. "
'If more than one --name options is specified, they are treated as "OR", '
"e.g. find photos matching any FILENAME. ",
),
o(
"--uuid",
metavar="UUID",
default=None,
multiple=True,
help="Search for photos with UUID(s). "
"May be repeated to include multiple UUIDs.",
),
o(
"--uuid-from-file",
metavar="FILE",
default=None,
multiple=False,
help="Search for photos with UUID(s) loaded from FILE. "
"Format is a single UUID per line. Lines preceded with # are ignored.",
type=click.Path(exists=True),
),
o(
"--title",
metavar="TITLE",
default=None,
multiple=True,
help="Search for TITLE in title of photo.",
),
o("--no-title", is_flag=True, help="Search for photos with no title."),
o(
"--description",
metavar="DESC",
default=None,
multiple=True,
help="Search for DESC in description of photo.",
),
o(
"--no-description",
is_flag=True,
help="Search for photos with no description.",
),
o(
"--place",
metavar="PLACE",
default=None,
multiple=True,
help="Search for PLACE in photo's reverse geolocation info",
),
o(
"--no-place",
is_flag=True,
help="Search for photos with no associated place name info (no reverse geolocation info)",
),
o(
"--location",
is_flag=True,
help="Search for photos with associated location info (e.g. GPS coordinates)",
),
o(
"--no-location",
is_flag=True,
help="Search for photos with no associated location info (e.g. no GPS coordinates)",
),
o(
"--label",
metavar="LABEL",
multiple=True,
help="Search for photos with image classification label LABEL (Photos 5+ only). "
'If more than one label, treated as "OR", e.g. find photos matching any label',
),
o(
"--uti",
metavar="UTI",
default=None,
multiple=False,
help="Search for photos whose uniform type identifier (UTI) matches UTI",
),
o(
"-i",
"--ignore-case",
is_flag=True,
help="Case insensitive search for title, description, place, keyword, person, or album.",
),
o("--edited", is_flag=True, help="Search for photos that have been edited."),
o(
"--not-edited",
is_flag=True,
help="Search for photos that have not been edited.",
),
o(
"--external-edit",
is_flag=True,
help="Search for photos edited in external editor.",
),
o("--favorite", is_flag=True, help="Search for photos marked favorite."),
o(
"--not-favorite",
is_flag=True,
help="Search for photos not marked favorite.",
),
o("--hidden", is_flag=True, help="Search for photos marked hidden."),
o("--not-hidden", is_flag=True, help="Search for photos not marked hidden."),
o(
"--shared",
is_flag=True,
help="Search for photos in shared iCloud album (Photos 5+ only).",
),
o(
"--not-shared",
is_flag=True,
help="Search for photos not in shared iCloud album (Photos 5+ only).",
),
o(
"--burst",
is_flag=True,
help="Search for photos that were taken in a burst.",
),
o(
"--not-burst",
is_flag=True,
help="Search for photos that are not part of a burst.",
),
o("--live", is_flag=True, help="Search for Apple live photos"),
o(
"--not-live",
is_flag=True,
help="Search for photos that are not Apple live photos.",
),
o("--portrait", is_flag=True, help="Search for Apple portrait mode photos."),
o(
"--not-portrait",
is_flag=True,
help="Search for photos that are not Apple portrait mode photos.",
),
o("--screenshot", is_flag=True, help="Search for screenshot photos."),
o(
"--not-screenshot",
is_flag=True,
help="Search for photos that are not screenshot photos.",
),
o("--slow-mo", is_flag=True, help="Search for slow motion videos."),
o(
"--not-slow-mo",
is_flag=True,
help="Search for photos that are not slow motion videos.",
),
o("--time-lapse", is_flag=True, help="Search for time lapse videos."),
o(
"--not-time-lapse",
is_flag=True,
help="Search for photos that are not time lapse videos.",
),
o("--hdr", is_flag=True, help="Search for high dynamic range (HDR) photos."),
o("--not-hdr", is_flag=True, help="Search for photos that are not HDR photos."),
o(
"--selfie",
is_flag=True,
help="Search for selfies (photos taken with front-facing cameras).",
),
o("--not-selfie", is_flag=True, help="Search for photos that are not selfies."),
o("--panorama", is_flag=True, help="Search for panorama photos."),
o(
"--not-panorama",
is_flag=True,
help="Search for photos that are not panoramas.",
),
o(
"--has-raw",
is_flag=True,
help="Search for photos with both a jpeg and raw version",
),
o(
"--only-movies",
is_flag=True,
help="Search only for movies (default searches both images and movies).",
),
o(
"--only-photos",
is_flag=True,
help="Search only for photos/images (default searches both images and movies).",
),
o(
"--from-date",
help="Search by item start date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
o(
"--to-date",
help="Search by item end date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
o(
"--from-time",
help="Search by item start time of day, e.g. 12:00, or 12:00:00.",
type=TimeISO8601(),
),
o(
"--to-time",
help="Search by item end time of day, e.g. 12:00 or 12:00:00.",
type=TimeISO8601(),
),
o(
"--year",
metavar="YEAR",
help="Search for items from a specific year, e.g. --year 2022 to find all photos from the year 2022. "
"May be repeated to search multiple years.",
multiple=True,
type=int,
),
o(
"--added-before",
metavar="DATE",
help="Search for items added to the library before a specific date/time, "
"e.g. --added-before e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
o(
"--added-after",
metavar="DATE",
help="Search for items added to the libray after a specific date/time, "
"e.g. --added-after e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601 with/without timezone).",
type=DateTimeISO8601(),
),
o(
"--added-in-last",
metavar="TIME_DELTA",
help="Search for items added to the library in the last TIME_DELTA, "
"where TIME_DELTA is a string like "
"'12 hrs', '1 day', '1d', '1 week', '2weeks', '1 month', '1 year'. "
"for example, `--added-in-last 7d` and `--added-in-last '1 week'` are equivalent. "
"months are assumed to be 30 days and years are assumed to be 365 days. "
"Common English abbreviations are accepted, e.g. d, day, days or m, min, minutes.",
type=TimeOffset(),
),
o("--has-comment", is_flag=True, help="Search for photos that have comments."),
o("--no-comment", is_flag=True, help="Search for photos with no comments."),
o("--has-likes", is_flag=True, help="Search for photos that have likes."),
o("--no-likes", is_flag=True, help="Search for photos with no likes."),
o(
"--is-reference",
is_flag=True,
help="Search for photos that were imported as referenced files (not copied into Photos library).",
),
o(
"--not-reference",
is_flag=True,
help="Search for photos that are not references, that is, they were copied into the Photos library "
"and are managed by Photos.",
),
o(
"--in-album",
is_flag=True,
help="Search for photos that are in one or more albums.",
),
o(
"--not-in-album",
is_flag=True,
help="Search for photos that are not in any albums.",
),
o(
"--duplicate",
is_flag=True,
help="Search for photos with possible duplicates. osxphotos will compare signatures of photos, "
"evaluating date created, size, height, width, and edited status to find *possible* duplicates. "
"This does not compare images byte-for-byte nor compare hashes but should find photos imported multiple "
"times or duplicated within Photos.",
),
o(
"--min-size",
metavar="SIZE",
type=BitMathSize(),
help="Search for photos with size >= SIZE bytes. "
"The size evaluated is the photo's original size (when imported to Photos). "
"Size may be specified as integer bytes or using SI or NIST units. "
"For example, the following are all valid and equivalent sizes: '1048576' '1.048576MB', '1 MiB'.",
),
o(
"--max-size",
metavar="SIZE",
type=BitMathSize(),
help="Search for photos with size <= SIZE bytes. "
"The size evaluated is the photo's original size (when imported to Photos). "
"Size may be specified as integer bytes or using SI or NIST units. "
"For example, the following are all valid and equivalent sizes: '1048576' '1.048576MB', '1 MiB'.",
),
o("--missing", is_flag=True, help="Search for photos missing from disk."),
o(
"--not-missing",
is_flag=True,
help="Search for photos present on disk (e.g. not missing).",
),
o(
"--cloudasset",
is_flag=True,
help="Search for photos that are part of an iCloud library",
),
o(
"--not-cloudasset",
is_flag=True,
help="Search for photos that are not part of an iCloud library",
),
o(
"--incloud",
is_flag=True,
help="Search for photos that are in iCloud (have been synched)",
),
o(
"--not-incloud",
is_flag=True,
help="Search for photos that are not in iCloud (have not been synched)",
),
o(
"--regex",
metavar="REGEX TEMPLATE",
nargs=2,
multiple=True,
help="Search for photos where TEMPLATE matches regular expression REGEX. "
"For example, to find photos in an album that begins with 'Beach': '--regex \"^Beach\" \"{album}\"'. "
"You may specify more than one regular expression match by repeating '--regex' with different arguments.",
),
o(
"--selected",
is_flag=True,
help="Filter for photos that are currently selected in Photos.",
),
o(
"--exif",
metavar="EXIF_TAG VALUE",
nargs=2,
multiple=True,
help="Search for photos where EXIF_TAG exists in photo's EXIF data and contains VALUE. "
"For example, to find photos created by Adobe Photoshop: `--exif Software 'Adobe Photoshop' `"
"or to find all photos shot on a Canon camera: `--exif Make Canon`. "
"EXIF_TAG can be any valid exiftool tag, with or without group name, e.g. `EXIF:Make` or `Make`. "
"To use --exif, exiftool must be installed and in the path.",
),
o(
"--query-eval",
metavar="CRITERIA",
multiple=True,
help="Evaluate CRITERIA to filter photos. "
"CRITERIA will be evaluated in context of the following python list comprehension: "
"`photos = [photo for photo in photos if CRITERIA]` "
"where photo represents a PhotoInfo object. "
"For example: `--query-eval photo.favorite` returns all photos that have been "
"favorited and is equivalent to --favorite. "
"You may specify more than one CRITERIA by using --query-eval multiple times. "
"CRITERIA must be a valid python expression. "
"See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.",
),
o(
"--query-function",
metavar="filename.py::function",
multiple=True,
type=FunctionCall(),
help="Run function to filter photos. Use this in format: --query-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. "
+ "Your function will be passed a list of PhotoInfo objects and is expected to return a filtered list of PhotoInfo objects. "
+ "You may use more than one function by repeating the --query-function option with a different value. "
+ "Your query function will be called after all other query options have been evaluated. "
+ "See https://github.com/RhetTbull/osxphotos/blob/master/examples/query_function.py for example of how to use this option.",
),
]
for o in options[::-1]:
f = o(f)
return f
def DEBUG_OPTIONS(f):
o = click.option
options = [
o(
"--debug",
is_flag=True,
help="Enable debug output.",
hidden=OSXPHOTOS_HIDDEN,
),
o(
"--watch",
metavar="MODULE::NAME",
multiple=True,
help="Watch function or method calls. The function to watch must be in the form "
"MODULE::NAME where MODULE is the module path and NAME is the function or method name "
"contained in the module. For example, to watch all calls to FileUtil.copy() which is in "
"osxphotos.fileutil, use: "
"'--watch osxphotos.fileutil::FileUtil.copy'. More than one --watch option can be specified.",
hidden=OSXPHOTOS_HIDDEN,
),
o(
"--breakpoint",
metavar="MODULE::NAME",
multiple=True,
help="Add breakpoint to function calls. The function to watch must be in the form "
"MODULE::NAME where MODULE is the module path and NAME is the function or method name "
"contained in the module. For example, to set a breakpoint for calls to "
"FileUtil.copy() which is in osxphotos.fileutil, use: "
"'--breakpoint osxphotos.fileutil::FileUtil.copy'. More than one --breakpoint option can be specified.",
hidden=OSXPHOTOS_HIDDEN,
),
]
for o in options[::-1]:
f = o(f)
return f
THEME_OPTION = click.option(
"--theme",
metavar="THEME",
type=click.Choice(["dark", "light", "mono", "plain"], case_sensitive=False),
help="Specify the color theme to use for output. "
"Valid themes are 'dark', 'light', 'mono', and 'plain'. "
"Defaults to 'dark' or 'light' depending on system dark mode setting.",
)
VERBOSE_OPTION = click.option(
"--verbose",
"-V",
"verbose_flag",
count=True,
help="Print verbose output; may be specified multiple times for more verbose output.",
)
TIMESTAMP_OPTION = click.option(
"--timestamp", is_flag=True, help="Add time stamp to verbose output"
)
def get_config_dir() -> pathlib.Path:
"""Get the directory where config files are stored; create it if necessary."""
config_dir = xdg_config_home() / APP_NAME
@@ -651,3 +108,13 @@ def check_version():
"to suppress this message and prevent osxphotos from checking for latest version.",
err=True,
)
def print_version(ctx, param, value):
"""Print version, this is a callback for the --version option"""
if not value:
return
click.echo(f"osxphotos, version {__version__}")
click.echo(f"Python {sys.version}")
click.echo(f"macOS {'.'.join(get_macos_version())}, {platform.machine()}")
ctx.exit()

View File

@@ -1,19 +1,31 @@
"""Detect dark mode on MacOS >= 10.14"""
"""Detect dark mode on MacOS >= 10.14 or fake it elsewhere"""
import objc
import Foundation
from osxphotos.utils import is_macos
def theme():
with objc.autorelease_pool():
user_defaults = Foundation.NSUserDefaults.standardUserDefaults()
system_theme = user_defaults.stringForKey_("AppleInterfaceStyle")
return "dark" if system_theme == "Dark" else "light"
if is_macos:
import objc
import Foundation
def theme():
with objc.autorelease_pool():
user_defaults = Foundation.NSUserDefaults.standardUserDefaults()
system_theme = user_defaults.stringForKey_("AppleInterfaceStyle")
return "dark" if system_theme == "Dark" else "light"
def is_dark_mode():
return theme() == "dark"
def is_dark_mode():
return theme() == "dark"
def is_light_mode():
return theme() == "light"
def is_light_mode():
return theme() == "light"
else:
def theme():
return "light"
def is_dark_mode():
return theme() == "dark"
def is_light_mode():
return theme() == "light"

View File

@@ -8,16 +8,17 @@ from rich import print
import osxphotos
from osxphotos._constants import _PHOTOS_4_VERSION, _UNKNOWN_PLACE
from osxphotos.queryoptions import query_options_from_kwargs
from .common import (
from .cli_params import (
DB_ARGUMENT,
DB_OPTION,
JSON_OPTION,
OSXPHOTOS_HIDDEN,
QUERY_OPTIONS,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
get_photos_db,
)
from .common import OSXPHOTOS_HIDDEN, get_photos_db
from .list import _list_libraries
from .verbose import verbose_print
@@ -32,28 +33,32 @@ from .verbose import verbose_print
+ "can also use albums, persons, keywords, photos to dump related attributes.",
multiple=True,
)
@click.option(
"--uuid",
metavar="UUID",
help="Use with '--dump photos' to dump only certain UUIDs. "
"May be repeated to include multiple UUIDs.",
multiple=True,
)
@VERBOSE_OPTION
@TIMESTAMP_OPTION
@QUERY_OPTIONS
@click.pass_obj
@click.pass_context
def debug_dump(ctx, cli_obj, db, photos_library, dump, uuid, verbose_flag, timestamp):
"""Print out debug info"""
def debug_dump(
ctx, cli_obj, db, photos_library, dump, verbose_flag, timestamp, **kwargs
):
"""Print out debug info.
When run with --dump photos, any of the query options can be used to limit the
photos printed. For example, to print info on currently selected photos:
osxphotos debug-dump --dump photos --selected
"""
verbose = verbose_print(verbose_flag, timestamp)
db = get_photos_db(*photos_library, db, cli_obj.db)
db = get_photos_db(*photos_library, db, cli_obj.db if cli_obj else None)
if db is None:
click.echo(ctx.obj.group.commands["debug-dump"].get_help(ctx), err=True)
click.echo("\n\nLocated the following Photos library databases: ", err=True)
_list_libraries()
return
query_options = query_options_from_kwargs(**kwargs)
start_t = time.perf_counter()
print(f"Opening database: {db}")
photosdb = osxphotos.PhotosDB(dbfile=db, verbose=verbose)
@@ -87,16 +92,14 @@ def debug_dump(ctx, cli_obj, db, photos_library, dump, uuid, verbose_flag, times
print("_dbpersons_fullname:")
pprint.pprint(photosdb._dbpersons_fullname)
elif attr == "photos":
if uuid:
for uuid_ in uuid:
print(f"_dbphotos['{uuid_}']:")
try:
pprint.pprint(photosdb._dbphotos[uuid_])
except KeyError:
print(f"Did not find uuid {uuid_} in _dbphotos")
else:
print("_dbphotos:")
pprint.pprint(photosdb._dbphotos)
photos = photosdb.query(options=query_options)
uuid = [photo.uuid for photo in photos]
for uuid_ in uuid:
print(f"_dbphotos['{uuid_}']:")
try:
pprint.pprint(photosdb._dbphotos[uuid_])
except KeyError:
print(f"Did not find uuid {uuid_} in _dbphotos")
else:
try:
val = getattr(photosdb, attr)

View File

@@ -13,10 +13,10 @@ from osxphotos._version import __version__
from .common import get_config_dir, get_data_dir
@click.command()
@click.command(name="docs")
@click.pass_obj
@click.pass_context
def docs(ctx, cli_obj):
def docs_command(ctx, cli_obj):
"""Open osxphotos documentation in your browser."""
# first check if docs installed in old location in confir dir and if so, delete them

View File

@@ -11,15 +11,15 @@ from osxphotos.cli.click_rich_echo import (
from osxphotos.phototemplate import RenderOptions
from osxphotos.queryoptions import QueryOptions
from .color_themes import get_default_theme
from .common import (
from .cli_params import (
DB_ARGUMENT,
DB_OPTION,
DELETED_OPTIONS,
FIELD_OPTION,
JSON_OPTION,
get_photos_db,
)
from .color_themes import get_default_theme
from .common import get_photos_db
from .list import _list_libraries
from .print_photo_info import print_photo_fields, print_photo_info
from .verbose import get_verbose_console
@@ -56,7 +56,11 @@ def dump(
photos_library,
print_template,
):
"""Print list of all photos & associated info from the Photos library."""
"""Print list of all photos & associated info from the Photos library.
NOTE: dump is DEPRECATED and will be removed in a future release.
Use `osxphotos query` instead.
"""
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None

View File

@@ -17,14 +17,9 @@ from osxphotos.fileutil import FileUtil, FileUtilNoOp
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
from osxphotos.utils import pluralize
from .cli_params import DB_OPTION, THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .click_rich_echo import rich_click_echo, rich_echo_error
from .common import (
DB_OPTION,
THEME_OPTION,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
get_photos_db,
)
from .common import get_photos_db
from .export import export, render_and_validate_report
from .param_types import ExportDBType, TemplateString
from .report_writer import ReportWriterNoOp, export_report_writer_factory

View File

@@ -1,5 +1,7 @@
"""export command for osxphotos CLI"""
import atexit
import inspect
import os
import pathlib
import platform
@@ -10,13 +12,6 @@ import time
from typing import Iterable, List, Optional, Tuple
import click
from osxmetadata import (
MDITEM_ATTRIBUTE_DATA,
MDITEM_ATTRIBUTE_SHORT_NAMES,
OSXMetaData,
Tag,
)
from osxmetadata.constants import _TAGS_NAMES
import osxphotos
from osxphotos._constants import (
@@ -45,39 +40,54 @@ from osxphotos.datetime_formatter import DateTimeFormatter
from osxphotos.debug import is_debug
from osxphotos.exiftool import get_exiftool_path
from osxphotos.export_db import ExportDB, ExportDBInMemory
from osxphotos.fileutil import FileUtil, FileUtilNoOp, FileUtilShUtil
from osxphotos.fileutil import FileUtilMacOS, 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
from osxphotos.photokit import (
check_photokit_authorization,
request_photokit_authorization,
)
from osxphotos.photosalbum import PhotosAlbum
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
from osxphotos.queryoptions import QueryOptions, load_uuid_from_file
from osxphotos.queryoptions import load_uuid_from_file, query_options_from_kwargs
from osxphotos.uti import get_preferred_uti_extension
from osxphotos.utils import (
get_macos_version,
format_sec_to_hhmmss,
get_macos_version,
is_macos,
normalize_fs_path,
pluralize,
under_test,
)
from .click_rich_echo import rich_click_echo, rich_echo, rich_echo_error
from .common import (
CLI_COLOR_ERROR,
CLI_COLOR_WARNING,
if is_macos:
from osxmetadata import (
MDITEM_ATTRIBUTE_DATA,
MDITEM_ATTRIBUTE_SHORT_NAMES,
OSXMetaData,
Tag,
)
from osxmetadata.constants import _TAGS_NAMES
from osxphotos.photokit import (
check_photokit_authorization,
request_photokit_authorization,
)
from osxphotos.photosalbum import PhotosAlbum
from .cli_commands import logger
from .cli_params import (
DB_ARGUMENT,
DB_OPTION,
DELETED_OPTIONS,
JSON_OPTION,
OSXPHOTOS_CRASH_LOG,
OSXPHOTOS_HIDDEN,
QUERY_OPTIONS,
THEME_OPTION,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
)
from .click_rich_echo import rich_click_echo, rich_echo, rich_echo_error
from .common import (
CLI_COLOR_ERROR,
CLI_COLOR_WARNING,
OSXPHOTOS_CRASH_LOG,
OSXPHOTOS_HIDDEN,
get_photos_db,
noop,
)
@@ -845,7 +855,6 @@ def export(
retry,
save_config,
screenshot,
selected,
selfie,
shared,
sidecar,
@@ -877,6 +886,7 @@ def export(
verbose_flag,
xattr_template,
year,
selected=False, # Isn't provided on unsupported platforms
# debug, # debug, watch, breakpoint handled in cli/__init__.py
# watch,
# breakpoint,
@@ -905,7 +915,8 @@ def export(
# capture locals for use with ConfigOptions before changing any of them
locals_ = locals()
set_crash_data("locals", locals_)
crash_data = locals_.copy()
set_crash_data("locals", crash_data)
# config expects --verbose to be named "verbose" not "verbose_flag"
locals_["verbose"] = verbose_flag
@@ -940,7 +951,8 @@ def export(
# re-set the local vars to the corresponding config value
# this isn't elegant but avoids having to rewrite this function to use cfg.varname for every parameter
# the query options appear to be unaccessed but they are used below by query_options_from_kwargs
# which accesses them via locals() to avoid a long list of parameters
add_exported_to_album = cfg.add_exported_to_album
add_missing_to_album = cfg.add_missing_to_album
add_skipped_to_album = cfg.add_skipped_to_album
@@ -1103,7 +1115,10 @@ def export(
verbose(f"osxphotos version: {__version__}")
verbose(f"Python version: {sys.version}")
verbose(f"Platform: {platform.platform()}, {'.'.join(get_macos_version())}")
if is_macos:
verbose(f"Platform: {platform.platform()}, {'.'.join(get_macos_version())}")
else:
verbose(f"Platform: {platform.platform()}")
verbose(f"Verbose level: {verbose_flag}")
# validate options
@@ -1165,14 +1180,14 @@ def export(
if config_only and not save_config:
rich_click_echo(
"[error]--config-only must be used with --save-config",
"[error]Incompatible export options: --config-only must be used with --save-config",
err=True,
)
sys.exit(1)
if all(x in [s.lower() for s in sidecar] for x in ["json", "exiftool"]):
rich_click_echo(
"[error]Cannot use --sidecar json with --sidecar exiftool due to name collisions",
"[error]Incompatible export options:: cannot use --sidecar json with --sidecar exiftool due to name collisions",
err=True,
)
sys.exit(1)
@@ -1263,12 +1278,6 @@ def export(
if only_photos:
movies = False
# load UUIDs if necessary and append to any uuids passed with --uuid
if uuid_from_file:
uuid_list = list(uuid) # Click option is a tuple
uuid_list.extend(load_uuid_from_file(uuid_from_file))
uuid = tuple(uuid_list)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
db = get_photos_db(*photos_library, db, cli_db)
@@ -1311,7 +1320,8 @@ def export(
)
for other_db in other_db_files:
rich_click_echo(f"{other_db}")
click.confirm("Do you want to continue?", abort=True)
if not click.confirm("Do you want to continue?"):
sys.exit(1)
if dry_run:
export_db = ExportDBInMemory(dbfile=export_db_path, export_dir=dest)
@@ -1322,7 +1332,7 @@ def export(
if ramdb
else ExportDB(dbfile=export_db_path, export_dir=dest)
)
fileutil = FileUtilShUtil if alt_copy else FileUtil
fileutil = FileUtilShUtil if alt_copy or not is_macos else FileUtilMacOS
if verbose:
if export_db.was_created:
@@ -1338,6 +1348,14 @@ def export(
# save config to export_db
export_db.set_config(cfg.write_to_str())
query_kwargs = locals()
# skip missing bursts if using --download-missing by itself as AppleScript otherwise causes errors
query_kwargs["missing_bursts"] = (
(download_missing and use_photokit) or not download_missing,
)
query_kwargs["burst_photos"] = export_bursts
query_options = query_options_from_kwargs(**query_kwargs)
photosdb = osxphotos.PhotosDB(
dbfile=db, verbose=verbose, exiftool=exiftool_path, rich=True
)
@@ -1345,92 +1363,6 @@ def export(
# enable beta features if requested
photosdb._beta = beta
query_options = QueryOptions(
added_after=added_after,
added_before=added_before,
added_in_last=added_in_last,
album=album,
burst_photos=export_bursts,
burst=burst,
cloudasset=cloudasset,
deleted_only=deleted_only,
deleted=deleted,
description=description,
duplicate=duplicate,
edited=edited,
exif=exif,
external_edit=external_edit,
favorite=favorite,
folder=folder,
from_date=from_date,
from_time=from_time,
function=query_function,
has_comment=has_comment,
has_likes=has_likes,
has_raw=has_raw,
hdr=hdr,
hidden=hidden,
ignore_case=ignore_case,
in_album=in_album,
incloud=incloud,
is_reference=is_reference,
keyword=keyword,
label=label,
live=live,
location=location,
max_size=max_size,
min_size=min_size,
# skip missing bursts if using --download-missing by itself as AppleScript otherwise causes errors
missing_bursts=(download_missing and use_photokit) or not download_missing,
missing=missing,
movies=movies,
name=name,
no_comment=no_comment,
no_description=no_description,
no_likes=no_likes,
no_location=no_location,
no_keyword=no_keyword,
no_place=no_place,
no_title=no_title,
not_burst=not_burst,
not_cloudasset=not_cloudasset,
not_edited=not_edited,
not_favorite=not_favorite,
not_hdr=not_hdr,
not_hidden=not_hidden,
not_in_album=not_in_album,
not_incloud=not_incloud,
not_live=not_live,
not_missing=not_missing,
not_panorama=not_panorama,
not_portrait=not_portrait,
not_reference=not_reference,
not_screenshot=not_screenshot,
not_selfie=not_selfie,
not_shared=not_shared,
not_slow_mo=not_slow_mo,
not_time_lapse=not_time_lapse,
panorama=panorama,
person=person,
photos=photos,
place=place,
portrait=portrait,
query_eval=query_eval,
regex=regex,
screenshot=screenshot,
selected=selected,
selfie=selfie,
shared=shared,
slow_mo=slow_mo,
time_lapse=time_lapse,
title=title,
to_date=to_date,
to_time=to_time,
uti=uti,
uuid=uuid,
year=year,
)
try:
photos = photosdb.query(query_options)
except ValueError as e:
@@ -1486,69 +1418,40 @@ def export(
else None
)
def cleanup_lock_files():
"""Cleanup lock files"""
if not under_test():
verbose("Cleaning up lock files")
if dry_run:
return
for lock_file in pathlib.Path(dest).rglob("*.osxphotos.lock"):
try:
lock_file.unlink()
except Exception as e:
logger.debug(f"Error removing lock file {lock_file}: {e}")
atexit.register(cleanup_lock_files)
photo_num = 0
num_exported = 0
limit_str = f" (limit = [num]{limit}[/num])" if limit else ""
# hack to avoid passing all the options to export_photo
kwargs = {
k: v
for k, v in locals().items()
if k in inspect.getfullargspec(export_photo).args
}
kwargs["export_dir"] = dest
kwargs["export_preview"] = preview
with rich_progress(console=get_verbose_console(), mock=no_progress) as progress:
task = progress.add_task(
f"Exporting [num]{num_photos}[/] photos{limit_str}", total=num_photos
)
for p in photos:
photo_num += 1
export_results = export_photo(
photo=p,
dest=dest,
album_keyword=album_keyword,
convert_to_jpeg=convert_to_jpeg,
description_template=description_template,
directory=directory,
download_missing=download_missing,
dry_run=dry_run,
edited_suffix=edited_suffix,
exiftool_merge_keywords=exiftool_merge_keywords,
exiftool_merge_persons=exiftool_merge_persons,
exiftool_option=exiftool_option,
exiftool=exiftool,
export_as_hardlink=export_as_hardlink,
export_by_date=export_by_date,
export_db=export_db,
export_dir=dest,
export_edited=export_edited,
export_live=export_live,
export_preview=preview,
export_raw=export_raw,
favorite_rating=favorite_rating,
filename_template=filename_template,
fileutil=fileutil,
force_update=force_update,
ignore_date_modified=ignore_date_modified,
ignore_signature=ignore_signature,
jpeg_ext=jpeg_ext,
jpeg_quality=jpeg_quality,
keyword_template=keyword_template,
num_photos=num_photos,
original_name=original_name,
original_suffix=original_suffix,
overwrite=overwrite,
person_keyword=person_keyword,
photo_num=photo_num,
preview_if_missing=preview_if_missing,
preview_suffix=preview_suffix,
replace_keywords=replace_keywords,
retry=retry,
sidecar_drop_ext=sidecar_drop_ext,
sidecar=sidecar,
skip_original_if_edited=skip_original_if_edited,
strip=strip,
touch_file=touch_file,
update=update,
update_errors=update_errors,
use_photokit=use_photokit,
use_photos_export=use_photos_export,
verbose=verbose,
tmpdir=tmpdir,
)
kwargs["photo"] = p
kwargs["photo_num"] = photo_num
export_results = export_photo(**kwargs)
if post_function:
for function in post_function:
# post function is tuple of (function, filename.py::function_name)
@@ -1817,7 +1720,7 @@ def export_photo(
keyword_template=None,
description_template=None,
export_db=None,
fileutil=FileUtil,
fileutil=FileUtilShUtil,
dry_run=None,
touch_file=None,
edited_suffix="_edited",
@@ -1896,7 +1799,6 @@ def export_photo(
Raises:
ValueError on invalid filename_template
"""
export_original = not (skip_original_if_edited and photo.hasadjustments)
# can't export edited if photo doesn't have edited versions
@@ -2360,7 +2262,7 @@ def export_photo_to_directory(
err=True,
)
if tries > retry:
results.error.append((str(pathlib.Path(dest) / filename), e))
results.error.append((str(pathlib.Path(dest) / filename), str(e)))
break
else:
rich_echo(
@@ -2597,8 +2499,12 @@ def cleanup_files(dest_path, files_to_keep, dirs_to_keep, fileutil, verbose):
for p in pathlib.Path(dest_path).rglob("*"):
if p.is_file() and normalize_fs_path(str(p).lower()) not in keepers:
verbose(f"Deleting [filepath]{p}")
fileutil.unlink(p)
deleted_files.append(str(p))
try:
fileutil.unlink(p)
deleted_files.append(str(p))
except OSError as e:
# ignore errors deleting files, #987
verbose(f"Error deleting file {p}: {e}")
# delete empty directories
deleted_dirs = []
@@ -2609,8 +2515,12 @@ def cleanup_files(dest_path, files_to_keep, dirs_to_keep, fileutil, verbose):
if not list(pathlib.Path(dirpath).glob("*")):
# directory and directory is empty
verbose(f"Deleting empty directory {dirpath}")
fileutil.rmdir(dirpath)
deleted_dirs.append(str(dirpath))
try:
fileutil.rmdir(dirpath)
deleted_dirs.append(str(dirpath))
except OSError as e:
# ignore errors deleting directories, #987
verbose(f"Error deleting directory {dirpath}: {e}")
return (deleted_files, deleted_dirs)

View File

@@ -3,6 +3,7 @@
import json
import pathlib
import sys
from textwrap import dedent
import click
from rich import print
@@ -15,6 +16,7 @@ from osxphotos.export_db import (
ExportDB,
)
from osxphotos.export_db_utils import (
export_db_backup,
export_db_check_signatures,
export_db_get_errors,
export_db_get_last_run,
@@ -23,9 +25,12 @@ from osxphotos.export_db_utils import (
export_db_touch_files,
export_db_update_signatures,
export_db_vacuum,
export_db_migrate_photos_library,
export_db_get_last_library,
)
from osxphotos.utils import pluralize
from .cli_params import THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .click_rich_echo import (
rich_click_echo,
rich_echo,
@@ -34,7 +39,6 @@ from .click_rich_echo import (
set_rich_theme,
)
from .color_themes import get_theme
from .common import TIMESTAMP_OPTION, VERBOSE_OPTION
from .export import render_and_validate_report
from .param_types import TemplateString
from .report_writer import export_report_writer_factory
@@ -140,6 +144,15 @@ from .verbose import get_verbose_console, verbose_print
metavar="SQL_STATEMENT",
help="Execute SQL_STATEMENT against export database and print results.",
)
@click.option(
"--migrate-photos-library",
metavar="PHOTOS_LIBRARY",
help="Migrate the export database to use the specified Photos library. "
"Use this if you have moved your Photos library to a new location or computer and "
"want to keep using the same export database. "
"This will update the UUIDs in the export database to match the new Photos library.",
type=click.Path(exists=True, file_okay=True, dir_okay=True),
)
@click.option(
"--export-dir",
help="Optional path to export directory (if not parent of export database).",
@@ -153,10 +166,11 @@ from .verbose import get_verbose_console, verbose_print
)
@VERBOSE_OPTION
@TIMESTAMP_OPTION
@THEME_OPTION
@click.option(
"--dry-run",
is_flag=True,
help="Run in dry-run mode (don't actually update files), e.g. for use with --update-signatures.",
help="Run in dry-run mode (don't actually update files); for example, use with --update-signatures or --migrate-photos-library.",
)
@click.argument("export_db", metavar="EXPORT_DATABASE", type=click.Path(exists=True))
def exportdb(
@@ -170,9 +184,11 @@ def exportdb(
last_errors,
last_run,
migrate,
migrate_photos_library,
report,
save_config,
sql,
theme,
timestamp,
touch_file,
update_signatures,
@@ -185,12 +201,12 @@ def exportdb(
version,
):
"""Utilities for working with the osxphotos export database"""
verbose = verbose_print(verbose_flag, timestamp=timestamp)
verbose = verbose_print(verbose=verbose_flag, timestamp=timestamp, theme=theme)
# validate options and args
if append and not report:
rich_echo(
"[error]Error: --append requires --report; ee --help for more information.[/]",
rich_echo_error(
"[error]Error: --append requires --report; see --help for more information.[/]",
file=sys.stderr,
)
sys.exit(1)
@@ -200,7 +216,7 @@ def exportdb(
# assume it's the export folder
export_db = export_db / OSXPHOTOS_EXPORT_DB
if not export_db.is_file():
rich_echo(
rich_echo_error(
f"[error]Error: {OSXPHOTOS_EXPORT_DB} missing from {export_db.parent}[/error]"
)
sys.exit(1)
@@ -226,7 +242,9 @@ def exportdb(
]
]
if sum(sub_commands) > 1:
rich_echo("[error]Only a single sub-command may be specified at a time[/error]")
rich_echo_error(
"[error]Only a single sub-command may be specified at a time[/error]"
)
sys.exit(1)
# process sub-commands
@@ -235,7 +253,7 @@ def exportdb(
try:
osxphotos_ver, export_db_ver = export_db_get_version(export_db)
except Exception as e:
rich_echo(
rich_echo_error(
f"[error]Error: could not read version from {export_db}: {e}[/error]"
)
sys.exit(1)
@@ -250,7 +268,7 @@ def exportdb(
start_size = pathlib.Path(export_db).stat().st_size
export_db_vacuum(export_db)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
rich_echo(
@@ -264,7 +282,7 @@ def exportdb(
export_db, export_dir, verbose, dry_run
)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
rich_echo(
@@ -276,7 +294,7 @@ def exportdb(
try:
last_run_info = export_db_get_last_run(export_db)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
rich_echo(f"last run at [time]{last_run_info[0]}:")
@@ -287,7 +305,7 @@ def exportdb(
try:
export_db_save_config_to_file(export_db, save_config)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
rich_echo(f"Saved configuration to [filepath]{save_config}")
@@ -299,7 +317,7 @@ def exportdb(
export_db, export_dir, verbose_=verbose
)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
rich_echo(
@@ -314,7 +332,7 @@ def exportdb(
export_db, export_dir, verbose_=verbose, dry_run=dry_run
)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
rich_echo(
@@ -328,7 +346,7 @@ def exportdb(
try:
info_rec = exportdb.get_file_record(info)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
if info_rec:
@@ -343,7 +361,7 @@ def exportdb(
try:
error_list = export_db_get_errors(export_db)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
if error_list:
@@ -375,7 +393,7 @@ def exportdb(
try:
info_rec = exportdb.get_photoinfo_for_uuid(uuid_info)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
if info_rec:
@@ -393,7 +411,7 @@ def exportdb(
try:
file_list = exportdb.get_files_for_uuid(uuid_files)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
if file_list:
@@ -433,12 +451,14 @@ def exportdb(
report_filename = render_and_validate_report(report_template, "", export_dir)
export_results = exportdb.get_export_results(run_id)
if not export_results:
rich_echo(f"[error]No report results found for run ID {run_id}[/error]")
rich_echo_error(
f"[error]No report results found for run ID {run_id}[/error]"
)
sys.exit(1)
try:
report_writer = export_report_writer_factory(report_filename, append=append)
except ValueError as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
report_writer.write(export_results)
report_writer.close()
@@ -463,9 +483,39 @@ def exportdb(
c = exportdb._conn.cursor()
results = c.execute(sql)
except Exception as e:
rich_echo(f"[error]Error: {e}[/error]")
rich_echo_error(f"[error]Error: {e}[/error]")
sys.exit(1)
else:
for row in results:
print(row)
sys.exit(0)
if migrate_photos_library:
# migrate Photos library to new library and update UUIDs in export database
last_library = export_db_get_last_library(export_db)
rich_echo(
dedent(
f"""
[warning]:warning-emoji: This command will update your export database ([filepath]{export_db}[/])
to use [filepath]{migrate_photos_library}[/] as the new source library.
The last library used was [filepath]{last_library}[/].
This will allow you to use the export database with the new library but it will
no longer work correctly with the old library unless you run the `--migrate-photos-library`
command again to update the export database to use the previous library.
A backup of the export database will be created in the same directory as the export database.
"""
)
)
if not click.confirm("Do you want to continue?"):
sys.exit(0)
if not dry_run:
backup_file = export_db_backup(export_db)
verbose(f"Backed up export database to [filepath]{backup_file}[/]")
migrated, notmigrated = export_db_migrate_photos_library(
export_db, migrate_photos_library, verbose, dry_run
)
rich_echo(
f"Migrated [num]{migrated}[/] {pluralize(migrated, 'photo', 'photos')}, "
f"[num]{notmigrated}[/] not migrated."
)

View File

@@ -8,7 +8,8 @@ from rich import print
from osxphotos.photosdb.photosdb_utils import get_photos_library_version
from osxphotos.sqlgrep import sqlgrep
from .common import DB_OPTION, OSXPHOTOS_HIDDEN, get_photos_db
from .cli_params import DB_OPTION, OSXPHOTOS_HIDDEN
from .common import get_photos_db
@click.command(name="grep", hidden=OSXPHOTOS_HIDDEN)

View File

@@ -5,7 +5,6 @@ import re
import typing as t
import click
from osxmetadata import MDITEM_ATTRIBUTE_DATA, MDITEM_ATTRIBUTE_SHORT_NAMES
from rich.console import Console
from rich.markdown import Markdown
@@ -21,6 +20,10 @@ from osxphotos.phototemplate import (
TEMPLATE_SUBSTITUTIONS_PATHLIB,
get_template_help,
)
from osxphotos.utils import is_macos
if is_macos:
from osxmetadata import MDITEM_ATTRIBUTE_DATA, MDITEM_ATTRIBUTE_SHORT_NAMES
from .click_rich_echo import rich_echo_via_pager
from .color_themes import get_theme
@@ -249,68 +252,73 @@ class ExportCommand(click.Command):
+ f"rebuilding the '{OSXPHOTOS_EXPORT_DB}' database."
)
formatter.write("\n")
formatter.write(
rich_text("## Extended Attributes", width=formatter.width, markdown=True)
)
formatter.write("\n")
formatter.write_text(
"""
Some options (currently '--finder-tag-template', '--finder-tag-keywords', '-xattr-template') write
additional metadata accessible by Spotlight to facilitate searching.
For example, --finder-tag-keyword writes all keywords (including any specified by '--keyword-template'
or other options) to Finder tags that are searchable in Spotlight using the syntax: 'tag:tagname'.
For example, if you have images with keyword "Travel" then using '--finder-tag-keywords' you could quickly
find those images in the Finder by typing 'tag:Travel' in the Spotlight search bar.
Finder tags are written to the 'com.apple.metadata:_kMDItemUserTags' extended attribute.
Unlike EXIF metadata, extended attributes do not modify the actual file;
the metadata is written to extended attributes associated with the file and the Spotlight metadata database.
Most cloud storage services do not synch extended attributes.
Dropbox does sync them and any changes to a file's extended attributes
will cause Dropbox to re-sync the files.
The following attributes may be used with '--xattr-template':
"""
)
# build help text from all the attribute names
# passed to click.HelpFormatter.write_dl for formatting
attr_tuples = [
(
rich_text("[bold]Attribute[/bold]", width=formatter.width),
rich_text("[bold]Description[/bold]", width=formatter.width),
if is_macos:
formatter.write(
rich_text(
"## Extended Attributes", width=formatter.width, markdown=True
)
)
]
for attr_key in sorted(EXTENDED_ATTRIBUTE_NAMES):
# get short and long name
attr = MDITEM_ATTRIBUTE_SHORT_NAMES[attr_key]
short_name = MDITEM_ATTRIBUTE_DATA[attr]["short_name"]
long_name = MDITEM_ATTRIBUTE_DATA[attr]["name"]
constant = MDITEM_ATTRIBUTE_DATA[attr]["xattr_constant"]
formatter.write("\n")
formatter.write_text(
"""
Some options (currently '--finder-tag-template', '--finder-tag-keywords', '-xattr-template') write
additional metadata accessible by Spotlight to facilitate searching.
For example, --finder-tag-keyword writes all keywords (including any specified by '--keyword-template'
or other options) to Finder tags that are searchable in Spotlight using the syntax: 'tag:tagname'.
For example, if you have images with keyword "Travel" then using '--finder-tag-keywords' you could quickly
find those images in the Finder by typing 'tag:Travel' in the Spotlight search bar.
Finder tags are written to the 'com.apple.metadata:_kMDItemUserTags' extended attribute.
Unlike EXIF metadata, extended attributes do not modify the actual file;
the metadata is written to extended attributes associated with the file and the Spotlight metadata database.
Most cloud storage services do not synch extended attributes.
Dropbox does sync them and any changes to a file's extended attributes
will cause Dropbox to re-sync the files.
# get help text
description = MDITEM_ATTRIBUTE_DATA[attr]["description"]
type_ = MDITEM_ATTRIBUTE_DATA[attr]["help_type"]
attr_help = f"{long_name}; {constant}; {description}; {type_}"
The following attributes may be used with '--xattr-template':
# add to list
attr_tuples.append((short_name, attr_help))
"""
)
formatter.write_dl(attr_tuples)
formatter.write("\n")
formatter.write_text(
"For additional information on extended attributes see: https://developer.apple.com/documentation/coreservices/file_metadata/mditem/common_metadata_attribute_keys"
)
formatter.write("\n")
formatter.write(
rich_text("## Templating System", width=formatter.width, markdown=True)
)
formatter.write("\n")
help_text += formatter.getvalue()
help_text += template_help(width=formatter.width)
formatter = click.HelpFormatter(width=HELP_WIDTH)
# build help text from all the attribute names
# passed to click.HelpFormatter.write_dl for formatting
attr_tuples = [
(
rich_text("[bold]Attribute[/bold]", width=formatter.width),
rich_text("[bold]Description[/bold]", width=formatter.width),
)
]
for attr_key in sorted(EXTENDED_ATTRIBUTE_NAMES):
# get short and long name
attr = MDITEM_ATTRIBUTE_SHORT_NAMES[attr_key]
short_name = MDITEM_ATTRIBUTE_DATA[attr]["short_name"]
long_name = MDITEM_ATTRIBUTE_DATA[attr]["name"]
constant = MDITEM_ATTRIBUTE_DATA[attr]["xattr_constant"]
# get help text
description = MDITEM_ATTRIBUTE_DATA[attr]["description"]
type_ = MDITEM_ATTRIBUTE_DATA[attr]["help_type"]
attr_help = f"{long_name}; {constant}; {description}; {type_}"
# add to list
attr_tuples.append((short_name, attr_help))
formatter.write_dl(attr_tuples)
formatter.write("\n")
formatter.write_text(
"For additional information on extended attributes see: https://developer.apple.com/documentation/coreservices/file_metadata/mditem/common_metadata_attribute_keys"
)
formatter.write("\n")
formatter.write(
rich_text("## Templating System", width=formatter.width, markdown=True)
)
formatter.write("\n")
help_text += formatter.getvalue()
help_text += template_help(width=formatter.width)
formatter = click.HelpFormatter(width=HELP_WIDTH)
formatter.write("\n")
formatter.write("\n")
formatter.write_text(
"With the --directory and --filename options you may specify a template for the "
+ "export directory or filename, respectively. "

View File

@@ -20,14 +20,14 @@ from textwrap import dedent
from typing import Callable, Dict, List, Optional, Tuple, Union
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._constants import _OSXPHOTOS_NONE_SENTINEL, SQLITE_CHECK_SAME_THREAD
from osxphotos._version import __version__
from osxphotos.cli.common import TIMESTAMP_OPTION, VERBOSE_OPTION, get_data_dir
from osxphotos.cli.cli_params import TIMESTAMP_OPTION, VERBOSE_OPTION
from osxphotos.cli.common import get_data_dir
from osxphotos.cli.help import HELP_WIDTH
from osxphotos.cli.param_types import FunctionCall, StrpDateTimePattern, TemplateString
from osxphotos.datetime_utils import (
@@ -42,10 +42,14 @@ from osxphotos.photoinfo import PhotoInfoNone
from osxphotos.photosalbum import PhotosAlbumPhotoScript
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
from osxphotos.sqlitekvstore import SQLiteKVStore
from osxphotos.utils import pluralize
from osxphotos.utils import assert_macos, pluralize
assert_macos()
from photoscript import Photo, PhotosLibrary
from .cli_params import THEME_OPTION
from .click_rich_echo import rich_click_echo, rich_echo_error
from .common import THEME_OPTION
from .rich_progress import rich_progress
from .verbose import get_verbose_console, verbose_print
@@ -76,7 +80,8 @@ def echo(message, emoji=True, **kwargs):
class PhotoInfoFromFile:
"""Mock PhotoInfo class for a file to be imported
Returns None for most attributes but allows some templates like exiftool and created to work correctly"""
Returns None for most attributes but allows some templates like exiftool and created to work correctly
"""
def __init__(self, filepath: Union[str, Path], exiftool: Optional[str] = None):
self._path = str(filepath)
@@ -744,7 +749,7 @@ def write_sqlite_report(
file_exists = os.path.isfile(report_file)
conn = sqlite3.connect(report_file)
conn = sqlite3.connect(report_file, check_same_thread=SQLITE_CHECK_SAME_THREAD)
c = conn.cursor()
if not append or not file_exists:

View File

@@ -8,7 +8,8 @@ import yaml
import osxphotos
from osxphotos._constants import _PHOTOS_4_VERSION
from .common import DB_ARGUMENT, DB_OPTION, JSON_OPTION, get_photos_db
from .cli_params import DB_ARGUMENT, DB_OPTION, JSON_OPTION
from .common import get_photos_db
from .list import _list_libraries

View File

@@ -1,5 +1,7 @@
"""install/uninstall/run commands for osxphotos CLI"""
import contextlib
import sys
from runpy import run_module, run_path
@@ -50,14 +52,19 @@ def uninstall(packages, yes):
@click.command(name="run", cls=RunCommand)
# help command passed just to keep click from intercepting help
# and allowing --help to be passed to the script being run
@click.option("--help", "-h", is_flag=True, help="Show this message and exit")
@click.argument("python_file", nargs=1, type=click.Path(exists=True))
@click.argument("args", metavar="ARGS", nargs=-1)
def run(python_file, help, args):
"""Run a python file using same environment as osxphotos.
Any args are made available to the python file."""
# drop first two arguments, which are the osxphotos script and run command
sys.argv = sys.argv[2:]
# Need to drop all the args from sys.argv up to and including the run command
# For example, command could be one of the following:
# osxphotos run example.py --help
# osxphotos --debug run example.py --verbose --db /path/to/photos.db
# etc.
with contextlib.suppress(ValueError):
index = sys.argv.index("run")
sys.argv = sys.argv[index + 1 :]
run_path(python_file, run_name="__main__")

View File

@@ -7,7 +7,8 @@ import yaml
import osxphotos
from .common import DB_ARGUMENT, DB_OPTION, JSON_OPTION, get_photos_db
from .cli_params import DB_ARGUMENT, DB_OPTION, JSON_OPTION
from .common import get_photos_db
from .list import _list_libraries

50
osxphotos/cli/kvstore.py Normal file
View File

@@ -0,0 +1,50 @@
"""Simple interface to SQLiteKVStore for storing state between runs of the CLI tool."""
from __future__ import annotations
import atexit
import contextlib
import datetime
from osxphotos.sqlitekvstore import SQLiteKVStore
from .common import get_data_dir
__all__ = ["kvstore"]
# Store open connections
__kvstores = []
@atexit.register
def close_kvstore():
"""Close any open SQLiteKVStore databases"""
global __kvstores
for kv in __kvstores:
with contextlib.suppress(Exception):
kv.close()
def kvstore(name: str) -> SQLiteKVStore:
"""Return a key/value store for storing state between commands.
The key/value store is a SQLite database stored in the user's XDG data directory,
usually `~/.local/share/`. The key/value store can be used like a dict to store
arbitrary key/value pairs which persist between runs of the CLI tool.
Args:
name: a unique name for the key/value store
Returns:
SQLiteKVStore object
"""
global __kvstores
data_dir = get_data_dir()
if not name.endswith(".db"):
name += ".db"
kv = SQLiteKVStore(str(data_dir / name), wal=True)
if not kv.about:
kv.about = f"Key/value store for {name}, created by osxphotos CLI on {datetime.datetime.now()}"
__kvstores.append(kv)
return kv

View File

@@ -7,7 +7,8 @@ import yaml
import osxphotos
from .common import DB_ARGUMENT, DB_OPTION, JSON_OPTION, get_photos_db
from .cli_params import DB_ARGUMENT, DB_OPTION, JSON_OPTION
from .common import get_photos_db
from .list import _list_libraries

View File

@@ -6,7 +6,7 @@ import click
import osxphotos
from .common import JSON_OPTION
from .cli_params import JSON_OPTION
@click.command(name="list")

View File

@@ -14,18 +14,13 @@ from typing import Dict
import click
from osxphotos import PhotosDB
from osxphotos._constants import _PHOTOS_4_VERSION
from osxphotos._constants import _PHOTOS_4_VERSION, UUID_PATTERN
from osxphotos.fileutil import FileUtil
from osxphotos.utils import increment_filename, pluralize
from .cli_params import DB_OPTION, THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .click_rich_echo import rich_click_echo as echo
from .common import (
DB_OPTION,
THEME_OPTION,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
get_photos_db,
)
from .common import get_photos_db
from .help import get_help_msg
from .list import _list_libraries
from .verbose import verbose_print
@@ -136,8 +131,7 @@ def scan_for_files(directory: str, uuid_dict: Dict):
Note: modifies uuid_dict
"""
uuid_pattern = r"([0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12})"
uuid_regex = re.compile(uuid_pattern)
uuid_regex = re.compile(UUID_PATTERN)
for dirpath, dirname, filenames in os.walk(directory):
for filename in filenames:
if match := uuid_regex.match(filename):

View File

@@ -20,8 +20,12 @@ __all__ = [
"BitMathSize",
"DateOffset",
"DateTimeISO8601",
"DeprecatedPath",
"ExportDBType",
"FunctionCall",
"Latitude",
"Longitude",
"PathOrStdin",
"StrpDateTimePattern",
"TemplateString",
"TimeISO8601",
@@ -31,8 +35,39 @@ __all__ = [
]
class DateTimeISO8601(click.ParamType):
class DeprecatedPath(click.Path):
"""A click.Path that prints a deprecation warning when used."""
name = "DEPRECATED_PATH"
def __init__(self, *args, **kwargs):
if "deprecation_warning" in kwargs:
self.deprecation_warning = kwargs.pop("deprecation_warning")
else:
self.deprecation_warning = "This option is deprecated and will be removed in a future version of osxphotos."
super().__init__(*args, **kwargs)
def convert(self, value, param, ctx):
click.echo(
f"WARNING: {param.name} is deprecated. {self.deprecation_warning}",
err=True,
)
return super().convert(value, param, ctx)
class PathOrStdin(click.Path):
"""A click.Path or "-" to represent STDIN."""
name = "PATH_OR_STDIN"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def convert(self, value, param, ctx):
return value if value == "-" else super().convert(value, param, ctx)
class DateTimeISO8601(click.ParamType):
name = "DATETIME"
def convert(self, value, param, ctx):
@@ -46,7 +81,6 @@ class DateTimeISO8601(click.ParamType):
class BitMathSize(click.ParamType):
name = "BITMATH"
def convert(self, value, param, ctx):
@@ -66,7 +100,6 @@ class BitMathSize(click.ParamType):
class TimeISO8601(click.ParamType):
name = "TIME"
def convert(self, value, param, ctx):
@@ -105,7 +138,6 @@ class FunctionCall(click.ParamType):
class ExportDBType(click.ParamType):
name = "EXPORTDB"
def convert(self, value, param, ctx):
@@ -240,3 +272,33 @@ class StrpDateTimePattern(click.ParamType):
self.fail(f"Invalid strpdatetime format string: {value}. {e}")
else:
return value
class Latitude(click.ParamType):
name = "Latitude"
def convert(self, value, param, ctx):
try:
latitude = float(value)
if latitude < -90 or latitude > 90:
raise ValueError
return latitude
except Exception:
self.fail(
f"Invalid latitude {value}. Must be a floating point number between -90 and 90."
)
class Longitude(click.ParamType):
name = "Longitude"
def convert(self, value, param, ctx):
try:
longitude = float(value)
if longitude < -180 or longitude > 180:
raise ValueError
return longitude
except Exception:
self.fail(
f"Invalid longitude {value}. Must be a floating point number between -180 and 180."
)

View File

@@ -6,7 +6,8 @@ import yaml
import osxphotos
from .common import DB_ARGUMENT, DB_OPTION, JSON_OPTION, get_photos_db
from .cli_params import DB_ARGUMENT, DB_OPTION, JSON_OPTION
from .common import get_photos_db
from .list import _list_libraries

View File

@@ -13,8 +13,6 @@ from typing import Generator, List, Optional, Tuple
import bitmath
import click
from applescript import ScriptError
from photoscript import PhotosLibrary
from rich.console import Console
from rich.layout import Layout
from rich.live import Live
@@ -23,11 +21,18 @@ from rich.panel import Panel
from osxphotos import PhotoInfo, PhotosDB
from osxphotos._constants import _UNKNOWN_PERSON, search_category_factory
from osxphotos.rich_utils import add_rich_markup_tag
from osxphotos.text_detection import detect_text as detect_text_in_photo
from osxphotos.utils import dd_to_dms_str
from osxphotos.utils import assert_macos, dd_to_dms_str
assert_macos()
from applescript import ScriptError
from photoscript import PhotosLibrary
from osxphotos.text_detection import detect_text as detect_text_in_photo
from .cli_params import DB_OPTION, THEME_OPTION
from .color_themes import get_theme
from .common import DB_OPTION, THEME_OPTION, get_photos_db
from .common import get_photos_db
# global that tracks UUID being inspected
CURRENT_UUID = None

View File

@@ -8,7 +8,8 @@ import yaml
import osxphotos
from osxphotos._constants import _PHOTOS_4_VERSION, _UNKNOWN_PLACE
from .common import DB_ARGUMENT, DB_OPTION, JSON_OPTION, get_photos_db
from .cli_params import DB_ARGUMENT, DB_OPTION, JSON_OPTION
from .common import get_photos_db
from .list import _list_libraries

View File

@@ -1,5 +1,6 @@
"""query command for osxphotos CLI"""
import sys
import click
import osxphotos
@@ -9,42 +10,50 @@ from osxphotos.cli.click_rich_echo import (
set_rich_theme,
)
from osxphotos.debug import set_debug
from osxphotos.photosalbum import PhotosAlbum
from osxphotos.phototemplate import RenderOptions
from osxphotos.queryoptions import QueryOptions, load_uuid_from_file
from osxphotos.queryoptions import query_options_from_kwargs
from osxphotos.utils import assert_macos, is_macos
from .color_themes import get_default_theme
from .common import (
CLI_COLOR_ERROR,
CLI_COLOR_WARNING,
if is_macos:
from osxphotos.photosalbum import PhotosAlbum
from .cli_params import (
DB_ARGUMENT,
DB_OPTION,
DELETED_OPTIONS,
FIELD_OPTION,
JSON_OPTION,
OSXPHOTOS_HIDDEN,
QUERY_OPTIONS,
get_photos_db,
make_click_option_decorator,
)
from .color_themes import get_default_theme
from .common import CLI_COLOR_ERROR, CLI_COLOR_WARNING, OSXPHOTOS_HIDDEN, get_photos_db
from .list import _list_libraries
from .print_photo_info import print_photo_fields, print_photo_info
from .verbose import get_verbose_console
MACOS_OPTIONS = make_click_option_decorator(
*[
click.Option(
["--add-to-album"],
metavar="ALBUM",
help="Add all photos from query to album ALBUM in Photos. Album ALBUM will be created "
"if it doesn't exist. All photos in the query results will be added to this album. "
"This only works if the Photos library being queried is the last-opened (default) library in Photos. "
"This feature is currently experimental. I don't know how well it will work on large query sets.",
),
]
if is_macos
else []
)
@click.command()
@DB_OPTION
@JSON_OPTION
@QUERY_OPTIONS
@DELETED_OPTIONS
@click.option(
"--add-to-album",
metavar="ALBUM",
help="Add all photos from query to album ALBUM in Photos. Album ALBUM will be created "
"if it doesn't exist. All photos in the query results will be added to this album. "
"This only works if the Photos library being queried is the last-opened (default) library in Photos. "
"This feature is currently experimental. I don't know how well it will work on large query sets.",
)
@MACOS_OPTIONS
@click.option(
"--quiet",
is_flag=True,
@@ -63,9 +72,6 @@ from .verbose import get_verbose_console
"Most useful with --quiet. "
"May be repeated to print multiple template strings. ",
)
@click.option(
"--debug", required=False, is_flag=True, default=False, hidden=OSXPHOTOS_HIDDEN
)
@DB_ARGUMENT
@click.pass_obj
@click.pass_context
@@ -73,94 +79,13 @@ def query(
ctx,
cli_obj,
db,
photos_library,
add_to_album,
added_after,
added_before,
added_in_last,
album,
burst,
cloudasset,
deleted_only,
deleted,
description,
duplicate,
edited,
exif,
external_edit,
favorite,
field,
folder,
from_date,
from_time,
has_comment,
has_likes,
has_raw,
hdr,
hidden,
ignore_case,
in_album,
incloud,
is_reference,
json_,
keyword,
label,
live,
location,
max_size,
min_size,
missing,
name,
no_comment,
no_description,
no_likes,
no_location,
no_keyword,
no_place,
no_title,
not_burst,
not_cloudasset,
not_edited,
not_favorite,
not_hdr,
not_hidden,
not_in_album,
not_incloud,
not_live,
not_missing,
not_panorama,
not_portrait,
not_reference,
not_screenshot,
not_selfie,
not_shared,
not_slow_mo,
not_time_lapse,
only_movies,
only_photos,
panorama,
person,
place,
portrait,
print_template,
query_eval,
query_function,
quiet,
regex,
screenshot,
selected,
selfie,
shared,
slow_mo,
time_lapse,
title,
to_date,
to_time,
uti,
uuid_from_file,
uuid,
year,
debug, # handled in cli/__init__.py
photos_library,
add_to_album=False,
**kwargs,
):
"""Query the Photos database using 1 or more search options;
if more than one different option is provided, they are treated as "AND"
@@ -173,95 +98,14 @@ def query(
osxphotos query --person "John Doe" --person "Jane Doe" --keyword "vacation"
will return all photos with either person of ("John Doe" OR "Jane Doe") AND keyword of "vacation"
"""
# if no query terms, show help and return
# sanity check input args
nonexclusive = [
added_after,
added_before,
added_in_last,
album,
duplicate,
exif,
external_edit,
folder,
from_date,
from_time,
has_raw,
keyword,
label,
max_size,
min_size,
name,
person,
query_eval,
query_function,
regex,
selected,
to_date,
to_time,
uti,
uuid_from_file,
uuid,
year,
]
exclusive = [
(any(description), no_description),
(any(place), no_place),
(any(title), no_title),
(any(keyword), no_keyword),
(burst, not_burst),
(cloudasset, not_cloudasset),
(deleted, deleted_only),
(edited, not_edited),
(favorite, not_favorite),
(has_comment, no_comment),
(has_likes, no_likes),
(hdr, not_hdr),
(hidden, not_hidden),
(in_album, not_in_album),
(incloud, not_incloud),
(live, not_live),
(location, no_location),
(missing, not_missing),
(only_photos, only_movies),
(panorama, not_panorama),
(portrait, not_portrait),
(screenshot, not_screenshot),
(selfie, not_selfie),
(shared, not_shared),
(slow_mo, not_slow_mo),
(time_lapse, not_time_lapse),
(is_reference, not_reference),
]
# print help if no non-exclusive term or a double exclusive term is given
if any(all(bb) for bb in exclusive) or not any(
nonexclusive + [b ^ n for b, n in exclusive]
):
click.echo("Incompatible query options", err=True)
click.echo(ctx.obj.group.commands["query"].get_help(ctx), err=True)
return
If not query options are provided, all photos in the library will be returned.
"""
# set console for rich_echo to be same as for verbose_
set_rich_console(get_verbose_console())
set_rich_theme(get_default_theme())
# actually have something to query
# default searches for everything
photos = True
movies = True
if only_movies:
photos = False
if only_photos:
movies = False
# load UUIDs if necessary and append to any uuids passed with --uuid
if uuid_from_file:
uuid_list = list(uuid) # Click option is a tuple
uuid_list.extend(load_uuid_from_file(uuid_from_file))
uuid = tuple(uuid_list)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
db = get_photos_db(*photos_library, db, cli_db)
@@ -271,105 +115,30 @@ def query(
_list_libraries()
return
try:
query_options = query_options_from_kwargs(**kwargs)
except Exception as e:
raise click.BadOptionUsage("query", str(e)) from e
photosdb = osxphotos.PhotosDB(dbfile=db)
query_options = QueryOptions(
added_after=added_after,
added_before=added_before,
added_in_last=added_in_last,
album=album,
burst=burst,
cloudasset=cloudasset,
deleted_only=deleted_only,
deleted=deleted,
description=description,
duplicate=duplicate,
edited=edited,
exif=exif,
external_edit=external_edit,
favorite=favorite,
folder=folder,
from_date=from_date,
from_time=from_time,
function=query_function,
has_comment=has_comment,
has_likes=has_likes,
has_raw=has_raw,
hdr=hdr,
hidden=hidden,
ignore_case=ignore_case,
in_album=in_album,
incloud=incloud,
is_reference=is_reference,
keyword=keyword,
label=label,
live=live,
location=location,
max_size=max_size,
min_size=min_size,
missing=missing,
movies=movies,
name=name,
no_comment=no_comment,
no_description=no_description,
no_likes=no_likes,
no_location=no_location,
no_keyword=no_keyword,
no_place=no_place,
no_title=no_title,
not_burst=not_burst,
not_cloudasset=not_cloudasset,
not_edited=not_edited,
not_favorite=not_favorite,
not_hdr=not_hdr,
not_hidden=not_hidden,
not_in_album=not_in_album,
not_incloud=not_incloud,
not_live=not_live,
not_missing=not_missing,
not_panorama=not_panorama,
not_portrait=not_portrait,
not_reference=not_reference,
not_screenshot=not_screenshot,
not_selfie=not_selfie,
not_shared=not_shared,
not_slow_mo=not_slow_mo,
not_time_lapse=not_time_lapse,
panorama=panorama,
person=person,
photos=photos,
place=place,
portrait=portrait,
query_eval=query_eval,
regex=regex,
screenshot=screenshot,
selected=selected,
selfie=selfie,
shared=shared,
slow_mo=slow_mo,
time_lapse=time_lapse,
title=title,
to_date=to_date,
to_time=to_time,
uti=uti,
uuid=uuid,
year=year,
)
try:
photos = photosdb.query(query_options)
except ValueError as e:
if "Invalid query_eval CRITERIA:" in str(e):
msg = str(e).split(":")[1]
raise click.BadOptionUsage(
"query_eval", f"Invalid query-eval CRITERIA: {msg}"
)
else:
raise ValueError(e)
if "Invalid query_eval CRITERIA:" not in str(e):
raise ValueError(e) from e
msg = str(e).split(":")[1]
raise click.BadOptionUsage(
"query_eval", f"Invalid query-eval CRITERIA: {msg}"
) from e
# below needed for to make CliRunner work for testing
cli_json = cli_obj.json if cli_obj is not None else None
if add_to_album and photos:
assert_macos()
album_query = PhotosAlbum(add_to_album, verbose=None)
photo_len = len(photos)
photo_word = "photos" if photo_len > 1 else "photo"

View File

@@ -1,14 +1,15 @@
"""repl command for osxphotos CLI"""
from __future__ import annotations
import os
import os.path
import pathlib
import re
import sys
import time
from typing import List
import click
import photoscript
from rich import pretty, print
import osxphotos
@@ -22,14 +23,14 @@ from osxphotos.queryoptions import (
QueryOptions,
query_options_from_kwargs,
)
from osxphotos.utils import assert_macos, is_macos
from .common import (
DB_ARGUMENT,
DB_OPTION,
DELETED_OPTIONS,
QUERY_OPTIONS,
get_photos_db,
)
if is_macos:
import photoscript
from applescript import ScriptError
from .cli_params import DB_ARGUMENT, DB_OPTION, DELETED_OPTIONS, QUERY_OPTIONS
from .common import get_photos_db
@click.command(name="repl")
@@ -57,7 +58,9 @@ def repl(ctx, cli_obj, db, emacs, beta, **kwargs):
import logging
from objexplore import explore
from photoscript import Album, Photo, PhotosLibrary
if is_macos:
from photoscript import Album, Photo, PhotosLibrary
from rich import inspect as _inspect
from osxphotos import ExifTool, PhotoInfo, PhotosDB
@@ -196,15 +199,23 @@ def _get_selected(photosdb):
"""get list of PhotoInfo objects for photos selected in Photos"""
def get_selected():
selected = photoscript.PhotosLibrary().selection
if not selected:
return []
return photosdb.photos(uuid=[p.uuid for p in selected])
assert_macos()
try:
selected = photoscript.PhotosLibrary().selection
except ScriptError as e:
# some photos (e.g. shared items) can't be selected and raise ScriptError:
# applescript.ScriptError: Photos got an error: Cant get media item id "34C26DFA-0CEA-4DB7-8FDA-B87789B3209D/L0/001". (-1728) app='Photos' range=16820-16873
# In this case, we can parse the UUID from the error (though this only works for a single selected item)
if match := re.match(r".*Cant get media item id \"(.*)\".*", str(e)):
uuid = match[1].split("/")[0]
return photosdb.photos(uuid=[uuid])
return photosdb.photos(uuid=[p.uuid for p in selected]) if selected else []
return get_selected
def _spotlight_photo(photo: PhotoInfo):
assert_macos()
photo_ = photoscript.Photo(photo.uuid)
photo_.spotlight()

View File

@@ -12,6 +12,7 @@ from abc import ABC, abstractmethod
from contextlib import suppress
from typing import Dict, Union
from osxphotos._constants import SQLITE_CHECK_SAME_THREAD
from osxphotos.export_db import OSXPHOTOS_ABOUT_STRING
from osxphotos.photoexporter import ExportResults
from osxphotos.sqlite_utils import sqlite_columns
@@ -181,7 +182,9 @@ class ExportReportWriterSQLite(ReportWriterABC):
with suppress(FileNotFoundError):
os.unlink(self.output_file)
self._conn = sqlite3.connect(self.output_file)
self._conn = sqlite3.connect(
self.output_file, check_same_thread=SQLITE_CHECK_SAME_THREAD
)
self._create_tables()
self.report_id = self._generate_report_id()
@@ -533,7 +536,9 @@ class SyncReportWriterSQLite(ReportWriterABC):
with suppress(FileNotFoundError):
os.unlink(self.output_file)
self._conn = sqlite3.connect(self.output_file)
self._conn = sqlite3.connect(
self.output_file, check_same_thread=SQLITE_CHECK_SAME_THREAD
)
self._create_tables()
self.report_id = self._generate_report_id()

View File

@@ -0,0 +1,98 @@
"""osxphotos show command"""
import pathlib
import re
import click
from osxphotos._constants import UUID_PATTERN
from osxphotos.export_db_utils import get_uuid_for_filepath
from osxphotos.photosdb.photosdb_utils import get_photos_library_version
from osxphotos.utils import get_last_library_path, assert_macos
assert_macos()
from osxphotos.photoscript_utils import (
photoscript_object_from_name,
photoscript_object_from_uuid,
)
from .cli_commands import echo, echo_error
from .cli_params import DB_OPTION
from .click_rich_echo import set_rich_theme
@click.command(name="show")
@DB_OPTION
@click.argument("uuid_or_name", metavar="UUID_OR_NAME", nargs=1, required=True)
@click.pass_context
def show(ctx, db, uuid_or_name):
"""Show photo, album, or folder in Photos from UUID_OR_NAME
Examples:
osxphotos show 12345678-1234-1234-1234-123456789012
osxphotos show "My Album"
osxphotos show "My Folder"
osxphotos show IMG_1234.JPG
show can also be used to show a photo exported with `osxphotos export`:
osxphotos show /path/to/exported/photo.jpg
In this case, the UUID_OR_NAME is the path to the exported photo and osxphotos
will attempt to find the export database to match the photo to the original in
Photos. If your export database is not in the default location in the root of the
export directory, this will not work.
Notes:
This command requires Photos library version 5 or higher.
Currently this command cannot be used to show subfolders in Photos.
"""
db = db or get_last_library_path()
if not db:
echo(
"Could not find Photos library. Use --library/--db to specify path to Photos library."
)
ctx.exit(1)
if get_photos_library_version(db) < 5:
echo_error("[error]show command requires Photos library version 5 or higher")
ctx.exit(1)
try:
if re.match(UUID_PATTERN, uuid_or_name):
if not (obj := photoscript_object_from_uuid(uuid_or_name, db)):
raise ValueError(
f"could not find asset with UUID [uuid]{uuid_or_name}[/]"
)
obj_type = obj.__class__.__name__
echo(f"Found [filename]{obj_type}[/] with UUID: [uuid]{uuid_or_name}[/]")
obj.spotlight()
elif obj := photoscript_object_from_name(uuid_or_name, db):
obj_type = obj.__class__.__name__
echo(
f"Found [filename]{obj_type}[/] with name: [filepath]{uuid_or_name}[/]"
)
obj.spotlight()
elif uuid := get_uuid_for_filepath(pathlib.Path(uuid_or_name).resolve()):
if not (obj := photoscript_object_from_uuid(uuid, db)):
raise ValueError(
f"could not find asset with UUID [uuid]{uuid}[/] for file [filepath]{uuid_or_name}[/]"
)
obj_type = obj.__class__.__name__
echo(
f"Found [filename]{obj_type}[/] from export database: [filepath]{uuid_or_name}[/]"
)
obj.spotlight()
else:
raise ValueError(
f"could not find asset with name [filepath]{uuid_or_name}[/]"
)
except Exception as e:
echo_error(f"[error]Error finding asset [uuid]{uuid_or_name}[/]: {e}")
ctx.exit(1)

View File

@@ -12,13 +12,8 @@ from rich.syntax import Syntax
import osxphotos
from .common import (
DB_OPTION,
OSXPHOTOS_SNAPSHOT_DIR,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
get_photos_db,
)
from .cli_params import DB_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .common import OSXPHOTOS_SNAPSHOT_DIR, get_photos_db
from .verbose import verbose_print
@@ -36,7 +31,7 @@ def snap(ctx, cli_obj, db):
Works only on Photos library versions since Catalina (10.15) or newer.
"""
db = get_photos_db(db, cli_obj.db)
db = get_photos_db(db, cli_obj.db if cli_obj else None)
db_path = pathlib.Path(db)
if db_path.is_file():
# assume it's the sqlite file
@@ -127,7 +122,7 @@ def diff(ctx, cli_obj, db, raw_output, style, db2, verbose_flag, timestamp):
ctx.exit(2)
verbose(f"sqldiff found at '{sqldiff}'")
db = get_photos_db(db, cli_obj.db)
db = get_photos_db(db, cli_obj.db if cli_obj else None)
db_path = pathlib.Path(db)
if db_path.is_file():
# assume it's the sqlite file

View File

@@ -9,7 +9,6 @@ import pathlib
from typing import Any, Callable, Literal
import click
import photoscript
from osxphotos import PhotoInfo, PhotosDB, __version__
from osxphotos.photoinfo import PhotoInfoNone
@@ -22,17 +21,21 @@ from osxphotos.queryoptions import (
query_options_from_kwargs,
)
from osxphotos.sqlitekvstore import SQLiteKVStore
from osxphotos.utils import pluralize
from osxphotos.utils import assert_macos, pluralize
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import rich_echo_error as echo_error
from .common import (
assert_macos()
import photoscript
from .cli_params import (
DB_OPTION,
QUERY_OPTIONS,
THEME_OPTION,
TIMESTAMP_OPTION,
VERBOSE_OPTION,
)
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import rich_echo_error as echo_error
from .param_types import TemplateString
from .report_writer import sync_report_writer_factory
from .rich_progress import rich_progress
@@ -167,6 +170,7 @@ def get_photo_metadata(photos: list[PhotoInfo]) -> str:
photos_dict[k] = v
elif photos_dict[k] and v != photos_dict[k]:
photos_dict[k] = f"{photos_dict[k]} {v}"
# convert photos_dict to JSON string
# wouldn't it be nice if json encoder handled datetimes...
def default(o):
@@ -640,7 +644,7 @@ def print_import_summary(results: SyncResults):
)
@VERBOSE_OPTION
@TIMESTAMP_OPTION
@QUERY_OPTIONS
@QUERY_OPTIONS(exclude=["--shared", "--not-shared"])
@DB_OPTION
@THEME_OPTION
@click.pass_obj
@@ -721,14 +725,6 @@ def sync(
ctx.exit(1)
# filter out photos in shared albums as these cannot be updated
# Not elegant but works for now without completely refactoring QUERY_OPTIONS
if kwargs.get("shared"):
echo_error(
"[warning]--shared cannot be used with --import/--export "
"as photos in shared iCloud albums cannot be updated; "
"--shared will be ignored[/]"
)
kwargs["shared"] = False
kwargs["not_shared"] = True
set_ = parse_set_merge(set_)
@@ -771,7 +767,7 @@ def sync(
print_import_summary(results)
if export_path:
photosdb = PhotosDB(dbfile=db, verbose=verbose)
query_options = query_options_from_kwargs(**kwargs)
photosdb = PhotosDB(dbfile=db, verbose=verbose)
photos = photosdb.query(query_options)
export_metadata(photos, export_path, verbose)

View File

@@ -7,28 +7,36 @@ from functools import partial
from textwrap import dedent
import click
from photoscript import PhotosLibrary
from rich.console import Console
from osxphotos._constants import APP_NAME
from osxphotos._version import __version__
from osxphotos.compare_exif import PhotoCompare
from osxphotos.crash_reporter import crash_reporter, set_crash_data
from osxphotos.datetime_utils import datetime_naive_to_local, datetime_to_new_tz
from osxphotos.exif_datetime_updater import ExifDateTimeUpdater
from osxphotos.exiftool import get_exiftool_path
from osxphotos.photodates import (
get_photo_date_added,
set_photo_date_added,
set_photo_date_from_filename,
update_photo_date_time,
update_photo_from_function,
update_photo_time_for_new_timezone,
)
from osxphotos.photosalbum import PhotosAlbumPhotoScript
from osxphotos.phototz import PhotoTimeZone, PhotoTimeZoneUpdater
from osxphotos.utils import noop, pluralize
from osxphotos.utils import assert_macos, noop, pluralize
assert_macos()
from photoscript import PhotosLibrary
from osxphotos.photosalbum import PhotosAlbumPhotoScript
from .cli_params import THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import rich_echo_error as echo_error
from .color_themes import get_theme
from .common import THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
from .common import OSXPHOTOS_CRASH_LOG
from .darkmode import is_dark_mode
from .help import HELP_WIDTH, rich_text
from .param_types import (
@@ -108,6 +116,14 @@ For this to work, you'll need to install the third-party exiftool (https://exift
*Note on timezones and times*: In Photos, when you change the timezone, Photos assumes the time itself was correct for the previous timezone and adjusts the time accordingly to the new timezone. E.g. if the photo's time is `13:00` and the timezone is `GMT -07:00` and you adjust the timezone one hour east to `GMT -06:00`, Photos will change the time of the photo to `14:00`. osxphotos timewarp follows this behavior. Using `--match-time` allows you to adjust the timezone but keep the same time without adjustment. For example, if your camera clock was correct but lacked timezone information and you took photos in one timezone but imported them to photos in another, Photos will add the timezone of the computer at time of import. You can use osxphotos timewarp to adjust the timezone but keep the time using `--match-time`.
**Update the date the photos were added to Photos**
`osxphotos timewarp --date-added 2021-09-10`
**Update the date the photos were added to Photos to match the date of the photo**
`osxphotos timewarp --date-added-from-photo`
**Compare the date/time/timezone of selected photos with the date/time/timezone in the photos' original EXIF metadata**
`osxphotos timewarp --compare-exif`
@@ -151,7 +167,7 @@ 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.
"""
""" # noqa: E501
),
width=formatter.width,
markdown=True,
@@ -207,6 +223,25 @@ command which can be used to change the time zone of photos after import.
"This is the same behavior exhibited by Photos when manually adjusting timezone in the Get Info window. "
"See also --match-time. ",
)
@click.option(
"--date-added",
metavar="DATE",
type=DateTimeISO8601(),
help="Set date/time added for selected photos. "
"This changes the date added or imported date in Photos but "
"does not change the date/time/timezone of the photo itself. "
"This is useful for removing photos from the Recents album, "
"for example if you have imported old scanned photos. "
"Format is 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. "
"If time is not included, midnight is assumed.",
)
@click.option(
"--date-added-from-photo",
is_flag=True,
help="Set date/time added for selected photos to the date/time the photo was taken. "
"This changes the date added or imported date in Photos but "
"does not change the date/time/timezone of the photo itself. ",
)
@click.option(
"--inspect",
"-i",
@@ -333,12 +368,21 @@ command which can be used to change the time zone of photos after import.
is_flag=True,
help="Bypass confirmation prompt. Use with caution.",
)
@crash_reporter(
OSXPHOTOS_CRASH_LOG,
"[red]Something went wrong and osxphotos encountered an error:[/red]",
"osxphotos crash log",
"Please file a bug report at https://github.com/RhetTbull/osxphotos/issues with the crash log attached.",
f"osxphotos version: {__version__}",
)
def timewarp(
date,
date_delta,
time,
time_delta,
timezone,
date_added,
date_added_from_photo,
inspect,
compare_exif,
push_exif,
@@ -364,26 +408,30 @@ def timewarp(
See Timewarp Overview below for additional information.
"""
set_crash_data("locals", locals())
# check constraints
if not any(
[
date,
date_delta,
time,
time_delta,
timezone,
inspect,
compare_exif,
parse_date,
push_exif,
pull_exif,
date_added_from_photo,
date_added,
date_delta,
date,
function,
inspect,
parse_date,
pull_exif,
push_exif,
time_delta,
time,
timezone,
]
):
raise click.UsageError(
"At least one of --date, --date-delta, --time, --time-delta, "
"--timezone, --inspect, --compare-exif, --push-exif, --pull-exif, "
"--parse-date, --function "
"--parse-date, --function, --date-added, or --date-added-from-photo "
"must be specified."
)
@@ -429,15 +477,17 @@ def timewarp(
if (
any(
[
date,
date_added_from_photo,
date_added,
date_delta,
time,
time_delta,
timezone,
push_exif,
pull_exif,
date,
function,
parse_date,
pull_exif,
push_exif,
time_delta,
time,
timezone,
]
)
and not force
@@ -476,6 +526,24 @@ def timewarp(
verbose=verbose,
)
set_photo_date_added_ = partial(
set_photo_date_added,
library_path=library,
verbose=verbose,
date_added=date_added,
)
set_photo_date_added_from_photo_ = partial(
set_photo_date_added,
library_path=library,
verbose=verbose,
)
get_photo_date_added_ = partial(
get_photo_date_added,
library_path=library,
)
if function:
update_photo_from_function_ = partial(
update_photo_from_function,
@@ -493,17 +561,21 @@ def timewarp(
"[filename]filename[/filename], [uuid]uuid[/uuid], "
"[time]photo time (local)[/time], "
"[time]photo time[/time], "
"[tz]timezone offset[/tz], [tz]timezone name[/tz]"
"[tz]timezone offset[/tz], [tz]timezone name[/tz], "
"[time]date added (local)[/time]"
)
for photo in photos:
set_crash_data("photo", f"{photo.uuid} {photo.filename}")
tz_seconds, tz_str, tz_name = tzinfo.get_timezone(photo)
photo_date_local = datetime_naive_to_local(photo.date)
photo_date_tz = datetime_to_new_tz(photo_date_local, tz_seconds)
date_added = datetime_naive_to_local(get_photo_date_added_(photo))
echo(
f"[filename]{photo.filename}[/filename], [uuid]{photo.uuid}[/uuid], "
f"[time]{photo_date_local.strftime(DATETIME_FORMAT)}[/time], "
f"[time]{photo_date_tz.strftime(DATETIME_FORMAT)}[/time], "
f"[tz]{tz_str}[/tz], [tz]{tz_name}[/tz]"
f"[tz]{tz_str}[/tz], [tz]{tz_name}[/tz], "
f"[time]{date_added.strftime(DATETIME_FORMAT)}[/time]"
)
sys.exit(0)
@@ -521,6 +593,7 @@ def timewarp(
"filename, uuid, photo time (Photos), photo time (EXIF), timezone offset (Photos), timezone offset (EXIF)"
)
for photo in photos:
set_crash_data("photo", f"{photo.uuid} {photo.filename}")
diff_results = (
photocomp.compare_exif_no_markup(photo)
if plain
@@ -579,29 +652,34 @@ def timewarp(
f"Processing [num]{num_photos}[/] {pluralize(len(photos), 'photo', 'photos')}",
total=num_photos,
)
for p in photos:
for photo in photos:
set_crash_data("photo", f"{photo.uuid} {photo.filename}")
if parse_date:
set_photo_date_from_filename_(p, p.filename, parse_date)
set_photo_date_from_filename_(photo, photo.filename, parse_date)
if pull_exif:
exif_updater.update_photos_from_exif(
p, use_file_modify_date=use_file_time
photo, use_file_modify_date=use_file_time
)
if any([date, time, date_delta, time_delta]):
update_photo_date_time_(p)
update_photo_date_time_(photo)
if match_time:
# need to adjust time before the timezone is updated
# or the old timezone will be overwritten in the database
update_photo_time_for_new_timezone_(photo=p, new_timezone=timezone)
update_photo_time_for_new_timezone_(photo=photo, new_timezone=timezone)
if timezone:
tz_updater.update_photo(p)
tz_updater.update_photo(photo)
if date_added:
set_photo_date_added_(photo)
if date_added_from_photo:
set_photo_date_added_from_photo_(photo, date_added=photo.date)
if function:
verbose(f"Calling function [bold]{function[1]}")
photo_path = exif_updater.get_photo_path(p)
update_photo_from_function_(photo=p, path=photo_path)
photo_path = exif_updater.get_photo_path(photo)
update_photo_from_function_(photo=photo, path=photo_path)
if push_exif:
# this should be the last step in the if chain to ensure all Photos data is updated
# before exiftool is run
exif_warn, exif_error = exif_updater.update_exif_from_photos(p)
exif_warn, exif_error = exif_updater.update_exif_from_photos(photo)
if exif_warn:
echo_error(f"[warning]Warning running exiftool: {exif_warn}[/]")
if exif_error:

View File

@@ -1,6 +1,11 @@
"""uuid command for osxphotos CLI"""
import click
from osxphotos.utils import assert_macos
assert_macos()
import photoscript

View File

@@ -56,21 +56,22 @@ def noop(*args, **kwargs):
pass
def verbose(*args, level: int = 1, **kwargs):
def verbose(*args, level: int = 1):
"""Print verbose output
Args:
*args: arguments to pass to verbose function for printing
level: verbose level; if level > get_verbose_level(), output is suppressed
Notes:
Normally you should use verbose_print() to get the verbose function instead of calling this directly
"""
# Notes:
# Normally you should use verbose_print() to get the verbose function instead of calling this directly
# This is here so that verbose can be directly imported and used in other modules without calling verbose_print()
# Use of verbose_print() will set the verbose function so that calling verbose() will work as expected
global __verbose_function
if __verbose_function is None:
return
__verbose_function(*args, level=level, **kwargs)
__verbose_function(*args, level=level)
def set_verbose_level(level: int):
@@ -199,15 +200,17 @@ def _verbose_print_function(
Returns:
function to print output
"""
if not verbose:
return noop
# configure console even if verbose is False so that rich_echo will work correctly
global _console
if file:
_console.console = Console(theme=theme, file=file)
else:
_console.console = Console(theme=theme, width=10_000)
if not verbose:
return noop
# closure to capture timestamp
def verbose_(*args, level: int = 1):
"""print output if verbose flag set"""

View File

@@ -5,12 +5,15 @@ from typing import Callable, List, Optional, Tuple
from osxphotos import PhotosDB
from osxphotos.exiftool import ExifTool
from photoscript import Photo
from .datetime_utils import datetime_naive_to_local, datetime_to_new_tz
from .utils import assert_macos, noop
assert_macos()
from photoscript import Photo
from .exif_datetime_updater import get_exif_date_time_offset
from .phototz import PhotoTimeZone
from .utils import noop
ExifDiff = namedtuple(
"ExifDiff",

View File

@@ -50,7 +50,7 @@ class ConfigOptions:
try:
arg = args[attr]
# don't test 'not arg'; need to handle empty strings as valid values
if arg is None or arg == False:
if arg is None or arg is False:
if type(self._attrs[attr]) == tuple:
setattr(self, attr, ())
else:

View File

@@ -44,7 +44,7 @@ def crash_reporter(filename, message, title, postamble, *extra_args):
f.write(f"Platform: {platform.platform()}\n")
f.write(f"Python version: {sys.version}\n")
f.write(f"sys.argv: {sys.argv}\n")
f.write("CRASH_DATA: \\n")
f.write("CRASH_DATA:\n")
for k, v in CRASH_DATA.items():
f.write(f"{k}: {v}\n")
for arg in extra_args:

View File

@@ -21,6 +21,7 @@ __all__ = [
"utc_offset_seconds",
]
# TODO: look at https://github.com/regebro/tzlocal for more robust implementation
def get_local_tz(dt: datetime.datetime) -> datetime.tzinfo:
"""Return local timezone as datetime.timezone tzinfo for dt
@@ -207,4 +208,3 @@ def utc_offset_seconds(dt: datetime.datetime) -> int:
return dt.tzinfo.utcoffset(dt).total_seconds()
else:
raise ValueError("dt does not have timezone info")

Binary file not shown.

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