Compare commits
57 Commits
feature_co
...
v0.60.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a1a4ad991 | ||
|
|
2190628e82 | ||
|
|
bb8e164f21 | ||
|
|
7ccfe26e37 | ||
|
|
2c80226ec8 | ||
|
|
492e1edb7f | ||
|
|
72c6a165c7 | ||
|
|
e9aa289e8a | ||
|
|
1fd73e5351 | ||
|
|
8de871ccf1 | ||
|
|
0473b45631 | ||
|
|
6b541b83ad | ||
|
|
633f3a92fa | ||
|
|
de0b4fa11f | ||
|
|
269a2dfa9e | ||
|
|
986010ec5f | ||
|
|
e062582d14 | ||
|
|
d30314bedc | ||
|
|
7dfd24cb96 | ||
|
|
fcb015dd4a | ||
|
|
d2338992a2 | ||
|
|
234db3fb54 | ||
|
|
55d4be6892 | ||
|
|
922634ab3e | ||
|
|
f0aa69d8bb | ||
|
|
ee8053d867 | ||
|
|
253901281d | ||
|
|
051da093ea | ||
|
|
fd5b16578c | ||
|
|
ca3da647f2 | ||
|
|
0c85298c03 | ||
|
|
b4b58d3b00 | ||
|
|
ed543aa2d0 | ||
|
|
1b0c91db97 | ||
|
|
dd3914328b | ||
|
|
4b4252a73c | ||
|
|
956cecfa30 | ||
|
|
6f88f19950 | ||
|
|
ce4f3c4c0b | ||
|
|
3993cf220d | ||
|
|
2ae41a5a7a | ||
|
|
519a6b0035 | ||
|
|
a4b4f1c288 | ||
|
|
f668ecf0c4 | ||
|
|
058b56092f | ||
|
|
d2b7783125 | ||
|
|
f4a743468d | ||
|
|
adac985309 | ||
|
|
4939324d2f | ||
|
|
1a3b4c2afe | ||
|
|
1b16a39cef | ||
|
|
0d31a152be | ||
|
|
34fbd87fc6 | ||
|
|
3ddfea6a3d | ||
|
|
8b59615ff7 | ||
|
|
c590a16d70 | ||
|
|
36ce86c4a6 |
@@ -262,7 +262,8 @@
|
||||
"bug",
|
||||
"ideas",
|
||||
"test",
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -529,10 +530,67 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2597142?v=4",
|
||||
"profile": "https://github.com/pekingduck",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MaxLyt",
|
||||
"name": "MaxLyt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/136200430?v=4",
|
||||
"profile": "https://github.com/MaxLyt",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ces3001",
|
||||
"name": "ces3001",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23762610?v=4",
|
||||
"profile": "https://github.com/ces3001",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
"skipCi": true,
|
||||
"commitType": "docs"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.59.0
|
||||
current_version = 0.60.5
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||
serialize = {major}.{minor}.{patch}
|
||||
|
||||
|
||||
14
.github/workflows/tests.yml
vendored
14
.github/workflows/tests.yml
vendored
@@ -3,15 +3,23 @@ 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 }}
|
||||
|
||||
@@ -23,6 +23,7 @@ In addition to a command line interface, OSXPhotos provides a access to a Python
|
||||
* [CommentInfo](#commentinfo)
|
||||
* [LikeInfo](#likeinfo)
|
||||
* [AdjustmentsInfo](#adjustmentsinfo)
|
||||
* [PhotoTables](#phototables)
|
||||
* [Raw Photos](#raw-photos)
|
||||
* [Template System](#template-system)
|
||||
* [ExifTool](#exiftoolExifTool)
|
||||
@@ -338,10 +339,7 @@ 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,
|
||||
)
|
||||
from osxphotos.cli import selection_command, verbose
|
||||
|
||||
|
||||
@selection_command
|
||||
@@ -389,7 +387,6 @@ Python < 3.11.
|
||||
For example, the following code will work on Python >= 3.11. This code is available in the `examples` directory as
|
||||
[concurrent_export.py](https://github.com/RhetTbull/osxphotos/blob/main/examples/concurrent_export.py).
|
||||
|
||||
|
||||
```python
|
||||
"""Example for concurrent export of photos using osxphotos.PhotoExporter.export()
|
||||
|
||||
@@ -1182,6 +1179,15 @@ Returns True if photo is a [cloud asset](#iscloudasset) and is synched to iCloud
|
||||
|
||||
**Note**: Applies to master (original) photo only. It's possible for the master to be in iCloud but a local edited version is not yet synched to iCloud. `incloud` provides status of only the master photo. osxphotos does not yet provide a means to determine if the edited version is in iCloud. If you need this feature, please open an [issue](https://github.com/RhetTbull/osxphotos/issues).
|
||||
|
||||
#### `syndicated`
|
||||
|
||||
Return true if photo was shared via syndication (e.g. via Messages, etc.); these are photos that appear in "Shared with you" album. Photos 8+ only; returns None if not Photos 8+.
|
||||
|
||||
#### `saved_to_library`
|
||||
|
||||
Return True if syndicated photo has been saved to library; returns False if photo is not syndicated or has not been saved to the library.
|
||||
Syndicated photos are photos that appear in "Shared with you" album. Photos 8+ only; returns None if not Photos 8+.
|
||||
|
||||
#### `uti`
|
||||
|
||||
Returns Uniform Type Identifier (UTI) for the current version of the image, for example: 'public.jpeg' or 'com.apple. quicktime-movie'. If the image has been edited, `uti` will return the UTI for the edited image, otherwise it will return the UTI for the original image.
|
||||
@@ -1395,6 +1401,12 @@ Returns a unique fingerprint for the original photo file. This is a hash of the
|
||||
|
||||
Returns a unique digest of the photo's properties and metadata; useful for detecting changes in any property/metadata of the photo.
|
||||
|
||||
#### `tables()`
|
||||
|
||||
Returns a PhotoTables object which provides access to the underlying SQLite database tables for the photo.
|
||||
See [PhotoTables](#phototables) for more details. This is useful for debugging or developing new features but
|
||||
is not intended for general use.
|
||||
|
||||
#### `json()`
|
||||
|
||||
Returns a JSON representation of all photo info.
|
||||
@@ -2157,6 +2169,37 @@ Returns a JSON representation of the FaceInfo instance.
|
||||
* `adj_version_info`: version info for the application which made the adjustments to the photo decoded from the adjustments data.
|
||||
* `asdict()`: dict representation of the AdjustmentsInfo object; contains all properties with exception of `plist`.
|
||||
|
||||
### PhotoTables
|
||||
|
||||
[PhotoInfo.tables](#tables) returns a PhotoTables object that contains information about the tables in the Photos database that contain information about the photo.
|
||||
The following properties are available:
|
||||
|
||||
* `ZASSET`
|
||||
* `ZADDITIONALASSETATTRIBUTES`
|
||||
* `ZDETECTEDFACE`
|
||||
* `ZPERSON`
|
||||
|
||||
Each of these properties returns a `Table` object that provides access to the row(s) in the table that correspond to the photo.
|
||||
|
||||
The Table object has dynamically created properties that correspond to the associated column in the table and return a tuple of values for that column.
|
||||
|
||||
```pycon
|
||||
>>> photo.tables().ZADDITIONALASSETATTRIBUTES.ZTITLE
|
||||
("St. James's Park",)
|
||||
```
|
||||
|
||||
The Table object also provides a `rows()` method which returns a list a of tuples for the matching rows in the table
|
||||
and a `rows_dict()` method which returns a list of dicts for the matching rows in the table.
|
||||
|
||||
```pycon
|
||||
|
||||
>>> photo.tables().ZASSET.rows()
|
||||
[(6, 3, 35, 0, 0, 0, 0, 0, 0, None, None, None, None, None, 0, 0, 1, 0, 0, 0, 0, -100, 0, 1, 0, 1356, 0, 0, 0, 0, 0, 0, 0, 1, 6192599813128215, 1, 2814835671629878, 1, 0, 3, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 2047, 7, None, 8, None, None, None, None, None, None, None, None, 3, 6, 6, 6, None, 6, 4, None, None, 8, 4, None, 2, None, 3, None, 3, None, None, 585926209.859624, 596906868.198932, 689981763.374756, None, None, None, 0.5, 561129492.501, 0.0, 596906868.198932, None, 0.03816793893129771, None, 51.50357167, -0.1318055, 689982854.802854, 0.6494140625, 0.0, 561129492.501, None, None, None, None, None, None, None, 'D', 'DC99FBDD-7A52-4100-A5BB-344131646C30.jpeg', None, 'sRGB IEC61966-2.1', 'public.jpeg', 'DC99FBDD-7A52-4100-A5BB-344131646C30', b'Ki\t@\x01\x00\x00\x00\td\tH\x01\x00\x00\x00\x93\\\tL\x01\x00\x00\x00\x1aK\x0c\x03\x0c\xa8q\x92\x00\x12C\x0c\x03\x0c"\r\x90\x00\x00<\x0c\x03\x08"\x19\x80\x00', b'\xca\xebV\tu\xc0I@/j\xf7\xab\x00\xdf\xc0\xbf\xcd\xcc\xcc\xcc\xcc\xcc\x04@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')]
|
||||
|
||||
>>> photo.tables().ZASSET.rows_dict()
|
||||
[{'Z_PK': 6, 'Z_ENT': 3, 'Z_OPT': 35, 'ZACTIVELIBRARYSCOPEPARTICIPATIONSTATE': 0, 'ZAVALANCHEPICKTYPE': 0, 'ZBUNDLESCOPE': 0, 'ZCAMERAPROCESSINGADJUSTMENTSTATE': 0, 'ZCLOUDDELETESTATE': 0, 'ZCLOUDDOWNLOADREQUESTS': 0, 'ZCLOUDHASCOMMENTSBYME': None, 'ZCLOUDHASCOMMENTSCONVERSATION': None, 'ZCLOUDHASUNSEENCOMMENTS': None, 'ZCLOUDISDELETABLE': None, 'ZCLOUDISMYASSET': None, 'ZCLOUDLOCALSTATE': 0, 'ZCLOUDPLACEHOLDERKIND': 0, 'ZCOMPLETE': 1, 'ZDEFERREDPROCESSINGNEEDED': 0, 'ZDEPTHTYPE': 0, 'ZDERIVEDCAMERACAPTUREDEVICE': 0, 'ZDUPLICATEASSETVISIBILITYSTATE': 0, 'ZFACEAREAPOINTS': -100, 'ZFAVORITE': 0, 'ZHASADJUSTMENTS': 1, 'ZHDRTYPE': 0, 'ZHEIGHT': 1356, 'ZHIDDEN': 0, 'ZHIGHFRAMERATESTATE': 0, 'ZISMAGICCARPET': 0, 'ZKIND': 0, 'ZKINDSUBTYPE': 0, 'ZLIBRARYSCOPESHARESTATE': 0, 'ZMONOSKITYPE': 0, 'ZORIENTATION': 1, 'ZPACKEDACCEPTABLECROPRECT': 6192599813128215, 'ZPACKEDBADGEATTRIBUTES': 1, 'ZPACKEDPREFERREDCROPRECT': 2814835671629878, 'ZPLAYBACKSTYLE': 1, 'ZPLAYBACKVARIATION': 0, 'ZSAVEDASSETTYPE': 3, 'ZSEARCHINDEXREBUILDSTATE': 0, 'ZSYNDICATIONSTATE': 0, 'ZTHUMBNAILINDEX': 5, 'ZTRASHEDSTATE': 0, 'ZVIDEOCPDURATIONVALUE': 0, 'ZVIDEOCPVISIBILITYSTATE': 0, 'ZVIDEODEFERREDPROCESSINGNEEDED': 0, 'ZVIDEOKEYFRAMETIMESCALE': 0, 'ZVIDEOKEYFRAMEVALUE': 0, 'ZVISIBILITYSTATE': 0, 'ZWIDTH': 2047, 'ZADDITIONALATTRIBUTES': 7, 'ZCLOUDFEEDASSETSENTRY': None, 'ZCOMPUTEDATTRIBUTES': 8, 'ZCONVERSATION': None, 'ZDAYGROUPHIGHLIGHTBEINGASSETS': None, 'ZDAYGROUPHIGHLIGHTBEINGEXTENDEDASSETS': None, 'ZDAYGROUPHIGHLIGHTBEINGKEYASSETPRIVATE': None, 'ZDAYGROUPHIGHLIGHTBEINGKEYASSETSHARED': None, 'ZDAYGROUPHIGHLIGHTBEINGSUMMARYASSETS': None, 'ZDUPLICATEMETADATAMATCHINGALBUM': None, 'ZDUPLICATEPERCEPTUALMATCHINGALBUM': None, 'ZEXTENDEDATTRIBUTES': 3, 'ZHIGHLIGHTBEINGASSETS': 6, 'ZHIGHLIGHTBEINGEXTENDEDASSETS': 6, 'ZHIGHLIGHTBEINGKEYASSETPRIVATE': 6, 'ZHIGHLIGHTBEINGKEYASSETSHARED': None, 'ZHIGHLIGHTBEINGSUMMARYASSETS': 6, 'ZIMPORTSESSION': 4, 'ZLIBRARYSCOPE': None, 'ZMASTER': None, 'ZMEDIAANALYSISATTRIBUTES': 8, 'ZMOMENT': 4, 'ZMOMENTSHARE': None, 'ZMONTHHIGHLIGHTBEINGKEYASSETPRIVATE': 2, 'ZMONTHHIGHLIGHTBEINGKEYASSETSHARED': None, 'ZPHOTOANALYSISATTRIBUTES': 3, 'ZTRASHEDBYPARTICIPANT': None, 'ZYEARHIGHLIGHTBEINGKEYASSETPRIVATE': 3, 'ZYEARHIGHLIGHTBEINGKEYASSETSHARED': None, 'Z_FOK_CLOUDFEEDASSETSENTRY': None, 'ZADDEDDATE': 585926209.859624, 'ZADJUSTMENTTIMESTAMP': 596906868.198932, 'ZANALYSISSTATEMODIFICATIONDATE': 689981763.374756, 'ZCLOUDBATCHPUBLISHDATE': None, 'ZCLOUDLASTVIEWEDCOMMENTDATE': None, 'ZCLOUDSERVERPUBLISHDATE': None, 'ZCURATIONSCORE': 0.5, 'ZDATECREATED': 561129492.501, 'ZDURATION': 0.0, 'ZFACEADJUSTMENTVERSION': 596906868.198932, 'ZHDRGAIN': None, 'ZHIGHLIGHTVISIBILITYSCORE': 0.03816793893129771, 'ZLASTSHAREDDATE': None, 'ZLATITUDE': 51.50357167, 'ZLONGITUDE': -0.1318055, 'ZMODIFICATIONDATE': 689982854.802854, 'ZOVERALLAESTHETICSCORE': 0.6494140625, 'ZPROMOTIONSCORE': 0.0, 'ZSORTTOKEN': 561129492.501, 'ZTRASHEDDATE': None, 'ZAVALANCHEUUID': None, 'ZCLOUDASSETGUID': None, 'ZCLOUDBATCHID': None, 'ZCLOUDCOLLECTIONGUID': None, 'ZCLOUDOWNERHASHEDPERSONID': None, 'ZDELETEREASON': None, 'ZDIRECTORY': 'D', 'ZFILENAME': 'DC99FBDD-7A52-4100-A5BB-344131646C30.jpeg', 'ZMEDIAGROUPUUID': None, 'ZORIGINALCOLORSPACE': 'sRGB IEC61966-2.1', 'ZUNIFORMTYPEIDENTIFIER': 'public.jpeg', 'ZUUID': 'DC99FBDD-7A52-4100-A5BB-344131646C30', 'ZIMAGEREQUESTHINTS': b'Ki\t@\x01\x00\x00\x00\td\tH\x01\x00\x00\x00\x93\\\tL\x01\x00\x00\x00\x1aK\x0c\x03\x0c\xa8q\x92\x00\x12C\x0c\x03\x0c"\r\x90\x00\x00<\x0c\x03\x08"\x19\x80\x00', 'ZLOCATIONDATA': b'\xca\xebV\tu\xc0I@/j\xf7\xab\x00\xdf\xc0\xbf\xcd\xcc\xcc\xcc\xcc\xcc\x04@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'}]
|
||||
```
|
||||
|
||||
### Raw Photos
|
||||
|
||||
Handling raw photos in `osxphotos` requires a bit of extra work. Raw photos in Photos can be imported in two different ways: 1) a single raw photo with no associated JPEG image is imported 2) a raw+JPEG pair is imported -- two separate images with same file stem (e.g. `IMG_0001.CR2` and `IMG_001.JPG`) are imported.
|
||||
@@ -2456,7 +2499,7 @@ cog.out(get_template_field_table())
|
||||
|{cr}|A carriage return: '\r'|
|
||||
|{crlf}|A carriage return + line feed: '\r\n'|
|
||||
|{tab}|:A tab: '\t'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.59.0'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.60.5'|
|
||||
|{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|
|
||||
|
||||
183
CHANGELOG.md
183
CHANGELOG.md
@@ -2,6 +2,168 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [v0.60.4](https://github.com/RhetTbull/osxphotos/compare/v0.60.3...v0.60.4)
|
||||
|
||||
Updated testing / compatibility matrix to include macOS 13.4.
|
||||
|
||||
## [v0.60.4] - 2023-06-18
|
||||
|
||||
### Fixed
|
||||
|
||||
### Added
|
||||
|
||||
### Removed
|
||||
|
||||
### Changed
|
||||
|
||||
### Contributors
|
||||
|
||||
- [@RhetTbull](https://github.com/RhetTbull/osxphotos) - code & testing
|
||||
|
||||
## [v0.60.3](https://github.com/RhetTbull/osxphotos/compare/v0.60.2...v0.60.3)
|
||||
|
||||
Ventura introduced a "shared with you" album which shows photos shared via Messages (and possible other apps). These show up in the Photos library in the
|
||||
"Shared with you" album but the images are stored in a different location that regular images so osxphotos could not previously access the images.
|
||||
It can now do so.
|
||||
|
||||
### [v0.60.3] 2023-06-18
|
||||
|
||||
#### Fixed
|
||||
|
||||
#### Added
|
||||
|
||||
- `PhotoInfo.syndicated` property to identify syndicated photos.
|
||||
- `PhotoInfo.saved_to_library` property to identify syndicated photos that have been saved to the library.
|
||||
- `--syndicated`/`--not-syndicated`, `--saved-to-library`/`--not-saved-to-library` query options.
|
||||
- `find()` function in `osxphotos repl` to search for files in the active Photos library directory.
|
||||
|
||||
#### Removed
|
||||
|
||||
#### Changed
|
||||
|
||||
#### Contributors
|
||||
|
||||
- [@RhetTbull](https://github.com/RhetTbull) for code.
|
||||
|
||||
## [v0.60.2](https://github.com/RhetTbull/osxphotos/compare/v0.60.1...v0.60.2)
|
||||
|
||||
Performance Improvements for --download-missing
|
||||
|
||||
### [v0.60.2] - 2023-06-17
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Performance improvements for `osxphotos export` when used with `--download-missing` or `--sidecar XMP` options. (#1086)
|
||||
|
||||
#### Added
|
||||
|
||||
#### Changed
|
||||
|
||||
#### Contributors
|
||||
|
||||
- [@RhetTbull](https://github.com/RhetTbull) for code changes.
|
||||
- [@MaxLyt](https://github.com/MaxLyt) for finding the issue.
|
||||
|
||||
## [v0.60.1](https://github.com/RhetTbull/osxphotos/compare/v0.60.0...v0.60.1)
|
||||
|
||||
Hot fix for a bug with in-memory database and --dry-run.
|
||||
|
||||
### 14 May 2023
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fixed crash with --dry-run with large export database (#1071)
|
||||
|
||||
#### Contributors
|
||||
|
||||
- @RhetTbull for code changes.
|
||||
- @rajscode for identifying the bug and filing a detailed bug report.
|
||||
|
||||
## [v0.60.0](https://github.com/RhetTbull/osxphotos/compare/v0.59.3...v0.60.0)
|
||||
|
||||
Linux Support: adds support for using a subset of osxphotos capabilities on Linux.
|
||||
|
||||
### 07 May 2023
|
||||
|
||||
#### Added
|
||||
|
||||
- osxphotos now supports Linux (tested on Ubuntu 22.04); some commands are macOS only and will not be available (nor shown) on Linux. Huge thank you to [@dvdkon](https://github.com/dvdkon) for doing the Linux port!
|
||||
- Added `PhotoTables` API and `tables` property to `PhotoInfo` to access underlying SQL tables for a photo.
|
||||
|
||||
#### Changed
|
||||
|
||||
- Added macOS 13.3 to supported versions table.
|
||||
|
||||
#### Contributors
|
||||
|
||||
- @dvdkon for Linux port.
|
||||
- @RhetTbull for code changes.
|
||||
- @pekingduck for bug report.
|
||||
- @cclause for updating ruff test runner.
|
||||
|
||||
## [v0.59.3](https://github.com/RhetTbull/osxphotos/compare/v0.59.2...v0.59.3)
|
||||
|
||||
Bug fixes for memory leak, crash during export
|
||||
|
||||
### 10 April 2023
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fixed memory leak in export (#1047)
|
||||
- Fixed crash during export (#1046)
|
||||
- Fixed large crash log size (#1048)
|
||||
|
||||
#### Changed
|
||||
|
||||
- Added better help for no selection with --selected (#1036)
|
||||
- Changed PhotoInfo.asdict() and PhotoInfo.json() to allow deep or shallow option (#1038)
|
||||
- Updated development docs (#1043)
|
||||
|
||||
#### Contributors
|
||||
|
||||
- @RhetTbull for code changes
|
||||
- @wernerzj for finding bug with memory leak
|
||||
- @rajscode for finding export crash
|
||||
- @oPromessa for development docs fix
|
||||
|
||||
## [v0.59.2](https://github.com/RhetTbull/osxphotos/compare/v0.59.1...v0.59.2)
|
||||
|
||||
Bug Fix for Export
|
||||
|
||||
### 08 April 2023
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fixed error on export when photo belonged to a project (#999)
|
||||
- Fixed large increase in export database size (#999)
|
||||
|
||||
#### Changed
|
||||
|
||||
- Added indent, shallow args to PhotoInfo.json() (#1038)
|
||||
|
||||
#### Contributors
|
||||
|
||||
- @RhetTbull for code
|
||||
- @oPromessa for finding bugs, running tests
|
||||
|
||||
## [v0.59.1](https://github.com/RhetTbull/osxphotos/compare/v0.59.0...v0.59.1)
|
||||
|
||||
Performance Boost
|
||||
|
||||
### 2 April 2023
|
||||
|
||||
#### Changed
|
||||
|
||||
- Removed lock files from export code (speed boost for NAS export, see #999); will need to eventually add this back for multithreaded export
|
||||
- Optimized some code in export CLI to speed export
|
||||
- Some linting fixed for move to ruff
|
||||
-
|
||||
|
||||
#### Contributors
|
||||
|
||||
- [@RhetTbull](https://github.com/RhetTbull) for code changes.
|
||||
- [@cclauss](https://github.com/cclauss) for linting fixes
|
||||
|
||||
## [v0.59.0](https://github.com/RhetTbull/osxphotos/compare/v0.58.2...v0.59.0)
|
||||
|
||||
### 1 April 2023
|
||||
@@ -96,15 +258,15 @@ See example code in [concurrent_export.py](https://github.com/RhetTbull/osxphoto
|
||||
|
||||
#### Added
|
||||
|
||||
- `--migrate-photos-library` option added to `osxphotos exportdb` to migrate the export database from one Photos library to another. This is useful when moving to a new computer but maintaining the existing osxphotos export. Thanks to @swduncan for the idea. (#990)
|
||||
- `--migrate-photos-library` option added to `osxphotos exportdb` to migrate the export database from one Photos library to another. This is useful when moving to a new computer but maintaining the existing osxphotos export. Thanks to @swduncan for the idea. (#990)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fixed a bug in `osxphotos export --cleanup` to handle files which could not be deleted. Thanks to @oPromessa for finding this and suggesting the fix. (#987)
|
||||
- Fixed a bug in `osxphotos export --cleanup` to handle files which could not be deleted. Thanks to @oPromessa for finding this and suggesting the fix. (#987)
|
||||
|
||||
#### Internal
|
||||
|
||||
- Fixed a bug that caused `rich_echo()` to not display rich text if `--verbose` wasn't specified.
|
||||
- Fixed a bug that caused `rich_echo()` to not display rich text if `--verbose` wasn't specified.
|
||||
|
||||
#### Contributors To This Release
|
||||
|
||||
@@ -112,10 +274,10 @@ See example code in [concurrent_export.py](https://github.com/RhetTbull/osxphoto
|
||||
- [@Promessa](https://github.com/promessa) who found the cleanup bug and suggested a code fix.
|
||||
- [@swduncan](https://github.com/swduncan) who suggested the library migrate use case.
|
||||
|
||||
|
||||
## [v0.57.1](https://github.com/RhetTbull/osxphotos/compare/v0.57.0...v0.57.1)
|
||||
|
||||
### 12 February 2023
|
||||
|
||||
### Added show command, bug fix, refactoring
|
||||
|
||||
A bug fix and some refactoring to prepare for adding a parallel export mode. Also added `osxphotos show` command.
|
||||
@@ -138,7 +300,6 @@ A bug fix and some refactoring to prepare for adding a parallel export mode. Als
|
||||
- [@RhetTbull](https://github.com/RhetTbull)
|
||||
- [@aa599](https://github.com/aa599) for reporting the timezone bug and suggesting change to `--uuid-from-file`
|
||||
|
||||
|
||||
## [v0.57.0](https://github.com/RhetTbull/osxphotos/compare/v0.56.7...v0.57.0)
|
||||
|
||||
### 5 February 2023
|
||||
@@ -170,7 +331,7 @@ for building simple command line tools.
|
||||
|
||||
#### Contributors
|
||||
|
||||
- @RhetTbull
|
||||
- @RhetTbull
|
||||
|
||||
## [v0.56.7](https://github.com/RhetTbull/osxphotos/compare/v0.56.6...v0.56.7)
|
||||
|
||||
@@ -197,7 +358,6 @@ for building simple command line tools.
|
||||
- @pweaver - thanks for finding the bug with `--dry-run` and `--finder-tag-keywords`
|
||||
- @eecue for providing testing data
|
||||
|
||||
|
||||
## [v0.56.6](https://github.com/RhetTbull/osxphotos/compare/v0.56.5...v0.56.6)
|
||||
|
||||
### 22 January 2023
|
||||
@@ -246,7 +406,6 @@ for building simple command line tools.
|
||||
- Thanks to @djbeadle for idea of adding SearchInfo.source
|
||||
- Thanks for @oPromessa for testing `import` speed-ups and providing test data
|
||||
|
||||
|
||||
### [v0.56.3](https://github.com/RhetTbull/osxphotos/compare/v0.56.2...v0.56.3)
|
||||
|
||||
### 16 January 2023
|
||||
@@ -283,7 +442,7 @@ for building simple command line tools.
|
||||
|
||||
#### Contributors to this release
|
||||
|
||||
- @RhetTbull - Added AI scores to `osxphotos inspect`
|
||||
- @RhetTbull - Added AI scores to `osxphotos inspect`
|
||||
- @mave2k - Documentation fixes
|
||||
- @oPromessa - Bug fix for metadata in `osxphotos import`
|
||||
|
||||
@@ -297,7 +456,6 @@ for building simple command line tools.
|
||||
|
||||
- Added new `osxphotos sync` command to sync metadata between libraries (#887)
|
||||
|
||||
|
||||
## [v0.56.0](https://github.com/RhetTbull/osxphotos/compare/v0.55.7...v0.56.0)
|
||||
|
||||
### 13 January 2023
|
||||
@@ -310,6 +468,7 @@ for building simple command line tools.
|
||||
- Added PhotoInfo.fingerprint (#900)
|
||||
|
||||
#### Changed
|
||||
|
||||
- Added --profile, --watch, --breakpoint, --debug as global options; previously these worked only with export
|
||||
|
||||
#### New Contributors
|
||||
@@ -317,6 +476,7 @@ for building simple command line tools.
|
||||
- Added @oPromessa as a contributor for code
|
||||
- Added @johnsturgeon as a contributor for bug, and doc
|
||||
- Added @qkeddy as a contributor for ideas, and data
|
||||
|
||||
## [v0.55.7](https://github.com/RhetTbull/osxphotos/compare/v0.55.6...v0.55.7)
|
||||
|
||||
### 1 January 2023
|
||||
@@ -335,6 +495,7 @@ for building simple command line tools.
|
||||
### Updates for timewarp and export when reading/writing QuickTime dates
|
||||
|
||||
#### Changed
|
||||
|
||||
- Added QuickTime:ContentCreateDate as a source for `osxphotos timewarp`
|
||||
- Write QuickTime:ContentCreateDate when exporting with --exiftool for `osxphotos export`
|
||||
|
||||
@@ -2124,7 +2285,7 @@ Thanks to @PetrochukM for identifying this and providing code!
|
||||
- doc: start with examples before the export reference [`7c7bf1b`](https://github.com/RhetTbull/osxphotos/commit/7c7bf1be6b6382a995a4e17906adfd8720d0a1c3)
|
||||
- Updated dependencies in README.md [`b1cab32`](https://github.com/RhetTbull/osxphotos/commit/b1cab32ff4c7b65ae4c9a5a9a11c175dbd487c0a)
|
||||
- remove extra spaces [`a59bb5b`](https://github.com/RhetTbull/osxphotos/commit/a59bb5b02f10fa554dae346a7271be37f50d8bcc)
|
||||
- Adding back dependency https://github.com/RhetTbull/PhotoScript) [`7c8bfc8`](https://github.com/RhetTbull/osxphotos/commit/7c8bfc811ab3a93dabadf1655f7d0e217d6c7b01)
|
||||
- Adding back dependency <https://github.com/RhetTbull/PhotoScript>) [`7c8bfc8`](https://github.com/RhetTbull/osxphotos/commit/7c8bfc811ab3a93dabadf1655f7d0e217d6c7b01)
|
||||
|
||||
## [v0.39.6](https://github.com/RhetTbull/osxphotos/compare/v0.39.5...v0.39.6)
|
||||
|
||||
|
||||
85
README.md
85
README.md
@@ -7,10 +7,10 @@
|
||||
[](https://pepy.tech/project/osxphotos)
|
||||
[](https://www.reddit.com/r/osxphotos/)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors)
|
||||
[](#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.4 | 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`.
|
||||
@@ -70,6 +74,14 @@ Once you've installed osxphotos with pipx, to upgrade to the latest version:
|
||||
|
||||
pipx upgrade osxphotos
|
||||
|
||||
**Note**: When installing other packages with homebrew, homebrew may update the version of Python installed which would then cause any app (including osxphotos) installed with `pipx` to fail. If this happens, the easiest fix is to reinstall osxphotos with:
|
||||
|
||||
pipx reinstall osxphotos
|
||||
|
||||
Alternatively, you can reinstall all apps installed with `pipx` with:
|
||||
|
||||
pipx reinstall-all
|
||||
|
||||
### Installation using pip
|
||||
|
||||
You can also install directly from [pypi](https://pypi.org/project/osxphotos/):
|
||||
@@ -865,6 +877,16 @@ Options:
|
||||
been synched)
|
||||
--not-incloud Search for photos that are not in iCloud (have
|
||||
not been synched)
|
||||
--syndicated Search for photos that have been shared via
|
||||
syndication ('Shared with You' album via
|
||||
Messages, etc.)
|
||||
--not-syndicated Search for photos that have not been shared
|
||||
via syndication ('Shared with You' album via
|
||||
Messages, etc.)
|
||||
--saved-to-library Search for syndicated photos that have saved
|
||||
to the library
|
||||
--not-saved-to-library Search for syndicated photos that have not
|
||||
saved to the library
|
||||
--regex REGEX TEMPLATE Search for photos where TEMPLATE matches
|
||||
regular expression REGEX. For example, to find
|
||||
photos in an album that begins with 'Beach': '
|
||||
@@ -1440,22 +1462,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
|
||||
@@ -2094,7 +2117,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.59.0'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.60.5'
|
||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||
|
||||
The following substitutions may result in multiple values. Thus if specified
|
||||
@@ -2581,7 +2604,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.59.0'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.60.5'|
|
||||
|{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|
|
||||
@@ -2621,7 +2644,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 ✨
|
||||
|
||||
@@ -2665,7 +2688,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>
|
||||
@@ -2701,7 +2724,15 @@ 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/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></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>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MaxLyt"><img src="https://avatars.githubusercontent.com/u/136200430?v=4?s=75" width="75px;" alt="MaxLyt"/><br /><sub><b>MaxLyt</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3AMaxLyt" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ces3001"><img src="https://avatars.githubusercontent.com/u/23762610?v=4?s=75" width="75px;" alt="ces3001"/><br /><sub><b>ces3001</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aces3001" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: 97939ee226e568d1b630529e47e51a47
|
||||
config: 7fe2be825ec9ab49bbb41359aee23684
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>Overview: module code - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>osxphotos._constants - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -215,9 +215,6 @@
|
||||
<span class="c1"># Apple Epoch is Jan 1, 2001</span>
|
||||
<span class="n">TIME_DELTA</span> <span class="o">=</span> <span class="p">(</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2001</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">-</span> <span class="n">datetime</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()</span>
|
||||
|
||||
<span class="c1"># Unicode format to use for comparing strings</span>
|
||||
<span class="n">UNICODE_FORMAT</span> <span class="o">=</span> <span class="s2">"NFC"</span>
|
||||
|
||||
<span class="c1"># which Photos library database versions have been tested</span>
|
||||
<span class="c1"># Photos 2.0 (10.12.6) == 2622</span>
|
||||
<span class="c1"># Photos 3.0 (10.13.6) == 3301</span>
|
||||
@@ -325,6 +322,8 @@
|
||||
<span class="p">(</span><span class="s2">"13"</span><span class="p">,</span> <span class="s2">"0"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"13"</span><span class="p">,</span> <span class="s2">"1"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"13"</span><span class="p">,</span> <span class="s2">"2"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"13"</span><span class="p">,</span> <span class="s2">"3"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"13"</span><span class="p">,</span> <span class="s2">"4"</span><span class="p">),</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="c1"># Photos 5 has persons who are empty string if unidentified face</span>
|
||||
|
||||
@@ -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.59.0 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.59.0 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.59.0 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">
|
||||
@@ -247,9 +247,8 @@
|
||||
<span class="sd"> ValueError: raised if len(values) != len(sort_keys)</span>
|
||||
<span class="sd"> """</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">"values and sort_keys must have same length"</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">"values and sort_keys must be same length"</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>
|
||||
@@ -363,14 +362,13 @@
|
||||
<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">"""Return album info as a dict"""</span>
|
||||
<span class="sd">"""Return album info as a dict; does not include photos"""</span>
|
||||
<span class="k">return</span> <span class="p">{</span>
|
||||
<span class="s2">"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="s2">"creation_date"</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">"start_date"</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">"end_date"</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">"owner"</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">"photos"</span><span class="p">:</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">photos</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>
|
||||
@@ -496,7 +494,7 @@
|
||||
<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">"""Return album info as a dict"""</span>
|
||||
<span class="sd">"""Return album info as a dict; does not include photos"""</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">"title"</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">"folder_names"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">folder_names</span>
|
||||
@@ -559,14 +557,13 @@
|
||||
<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">"""Return import info as a dict"""</span>
|
||||
<span class="sd">"""Return import info as a dict; does not include photos"""</span>
|
||||
<span class="k">return</span> <span class="p">{</span>
|
||||
<span class="s2">"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="s2">"creation_date"</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">"start_date"</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">"end_date"</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">"title"</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">"photos"</span><span class="p">:</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">photos</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>
|
||||
@@ -583,7 +580,25 @@
|
||||
<span class="sd"> Projects are cards, calendars, slideshows, etc.</span>
|
||||
<span class="sd"> """</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">"""Return hierarchical list of folders the album is contained in</span>
|
||||
<span class="sd"> the folder list is in form:</span>
|
||||
<span class="sd"> ["Top level folder", "sub folder 1", "sub folder 2", ...]</span>
|
||||
<span class="sd"> or empty list if album is not in any folders</span>
|
||||
<span class="sd"> """</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">"""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"> """</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>
|
||||
|
||||
@@ -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.58.1 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.58.1 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.58.1 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">
|
||||
|
||||
@@ -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.export_db - osxphotos 0.59.0 documentation</title>
|
||||
<title>osxphotos.export_db - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -224,7 +224,7 @@
|
||||
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="n">OSXPHOTOS_EXPORT_DB</span><span class="p">,</span> <span class="n">SQLITE_CHECK_SAME_THREAD</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">.fileutil</span> <span class="kn">import</span> <span class="n">FileUtil</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">normalize_fs_path</span>
|
||||
<span class="kn">from</span> <span class="nn">.unicode</span> <span class="kn">import</span> <span class="n">normalize_fs_path</span>
|
||||
|
||||
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="s2">"ExportDB"</span><span class="p">,</span>
|
||||
@@ -1118,32 +1118,35 @@
|
||||
<span class="sd"> returns: connection to the database</span>
|
||||
<span class="sd"> """</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">dbfile</span><span class="p">):</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_db_connection</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">conn</span><span class="p">:</span>
|
||||
<span class="c1"># database doesn't exist so create it in-memory</span>
|
||||
<span class="n">src</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_db_connection</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">src</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">"Error getting connection to in-memory database"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_create_or_migrate_db_tables</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_create_or_migrate_db_tables</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_created</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">()</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">dbfile</span><span class="p">,</span> <span class="n">check_same_thread</span><span class="o">=</span><span class="n">SQLITE_CHECK_SAME_THREAD</span><span class="p">)</span>
|
||||
<span class="n">dbdump</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dump_db</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">version</span> <span class="o">=</span> <span class="n">OSXPHOTOS_EXPORTDB_VERSION</span>
|
||||
<span class="k">return</span> <span class="n">src</span>
|
||||
|
||||
<span class="c1"># Create a database in memory and import from the dump</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span>
|
||||
<span class="s2">":memory:"</span><span class="p">,</span> <span class="n">check_same_thread</span><span class="o">=</span><span class="n">SQLITE_CHECK_SAME_THREAD</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span><span class="o">.</span><span class="n">executescript</span><span class="p">(</span><span class="n">dbdump</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_created</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
<span class="n">version_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_database_version</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">version_info</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="n">OSXPHOTOS_EXPORTDB_VERSION</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_create_or_migrate_db_tables</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">(</span><span class="n">version_info</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">OSXPHOTOS_EXPORTDB_VERSION</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">()</span>
|
||||
<span class="c1"># database exists so copy it to memory</span>
|
||||
<span class="n">src</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">dbfile</span><span class="p">,</span> <span class="n">check_same_thread</span><span class="o">=</span><span class="n">SQLITE_CHECK_SAME_THREAD</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># Create a database in memory by backing up the on-disk database</span>
|
||||
<span class="n">dst</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s2">":memory:"</span><span class="p">,</span> <span class="n">check_same_thread</span><span class="o">=</span><span class="n">SQLITE_CHECK_SAME_THREAD</span><span class="p">)</span>
|
||||
<span class="k">with</span> <span class="n">dst</span><span class="p">:</span>
|
||||
<span class="n">src</span><span class="o">.</span><span class="n">backup</span><span class="p">(</span><span class="n">dst</span><span class="p">,</span> <span class="n">pages</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">src</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_created</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
<span class="n">version_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_database_version</span><span class="p">(</span><span class="n">dst</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">version_info</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="n">OSXPHOTOS_EXPORTDB_VERSION</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_create_or_migrate_db_tables</span><span class="p">(</span><span class="n">dst</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">(</span><span class="n">version_info</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">OSXPHOTOS_EXPORTDB_VERSION</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">version</span> <span class="o">=</span> <span class="n">OSXPHOTOS_EXPORTDB_VERSION</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">conn</span>
|
||||
<span class="k">return</span> <span class="n">dst</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_get_db_connection</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""return db connection to in memory database"""</span>
|
||||
|
||||
@@ -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.58.1 documentation</title>
|
||||
<title>osxphotos.fileutil - osxphotos 0.60.5 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.58.1 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.58.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -206,9 +206,12 @@
|
||||
<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">.platform</span> <span class="kn">import</span> <span class="n">is_macos</span>
|
||||
<span class="kn">from</span> <span class="nn">.unicode</span> <span class="kn">import</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">"FileUtilABC"</span><span class="p">,</span> <span class="s2">"FileUtilMacOS"</span><span class="p">,</span> <span class="s2">"FileUtilShUtil"</span><span class="p">,</span> <span class="s2">"FileUtil"</span><span class="p">,</span> <span class="s2">"FileUtilNoOp"</span><span class="p">]</span>
|
||||
|
||||
@@ -287,6 +290,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">"src and dest must not be None"</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">"src file does not appear to exist"</span><span class="p">,</span> <span class="n">src</span><span class="p">)</span>
|
||||
|
||||
@@ -312,6 +318,9 @@
|
||||
<span class="sd"> OSError if copy fails</span>
|
||||
<span class="sd"> TypeError if either path is None</span>
|
||||
<span class="sd"> """</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 +341,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">"""unlink filepath; if it's pathlib.Path, use Path.unlink, otherwise use os.unlink"""</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 +350,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">"""remove directory filepath; dirpath must be empty"""</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 +359,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">"""Set the access and modified time of path."""</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 +375,9 @@
|
||||
<span class="sd"> Does not do a byte-by-byte comparison.</span>
|
||||
<span class="sd"> """</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 +400,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 +409,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">"""return os.stat signature for file f1 as tuple of (mode, size, mtime)"""</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 +424,8 @@
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> True if success, otherwise False</span>
|
||||
<span class="sd"> """</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 +443,8 @@
|
||||
<span class="sd"> Name of renamed file (dest)</span>
|
||||
|
||||
<span class="sd"> """</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 +489,9 @@
|
||||
<span class="sd"> OSError if copy fails</span>
|
||||
<span class="sd"> TypeError if either path is None</span>
|
||||
<span class="sd"> """</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 +509,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">"""Various file utilities"""</span>
|
||||
|
||||
<span class="k">pass</span></div>
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>osxphotos.photoexporter - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -198,6 +198,8 @@
|
||||
<span></span><span class="sd">""" PhotoExport class to export photos</span>
|
||||
<span class="sd">"""</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,13 +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>
|
||||
@@ -232,15 +232,10 @@
|
||||
<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">.platform</span> <span class="kn">import</span> <span class="n">is_macos</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">.unicode</span> <span class="kn">import</span> <span class="n">normalize_fs_path</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="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="p">(</span>
|
||||
<span class="n">hexdigest</span><span class="p">,</span>
|
||||
@@ -252,6 +247,18 @@
|
||||
<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">"ExportError"</span><span class="p">,</span>
|
||||
<span class="s2">"ExportOptions"</span><span class="p">,</span>
|
||||
@@ -266,6 +273,10 @@
|
||||
<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>
|
||||
@@ -434,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">"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">)"</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">"original"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original</span><span class="p">,</span>
|
||||
@@ -669,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's not needed (won'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 "Download originals to this Mac" 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>
|
||||
|
||||
@@ -855,8 +874,16 @@
|
||||
<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">"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">"</span>
|
||||
@@ -915,9 +942,18 @@
|
||||
<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">-></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">"""Return True if photo should be updated, else False"""</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">-></span> <span class="nb">bool</span> <span class="o">|</span> <span class="n">ShouldUpdate</span><span class="p">:</span>
|
||||
<span class="sd">"""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"> """</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>
|
||||
@@ -931,11 +967,11 @@
|
||||
<span class="c1"># photo doesn'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>
|
||||
|
||||
@@ -967,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'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>
|
||||
|
||||
@@ -1018,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">-></span> <span class="n">StagedFiles</span><span class="p">:</span>
|
||||
<span class="sd">"""Download and stage any missing files for export"""</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>
|
||||
@@ -1378,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">"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">"</span>
|
||||
<span class="sa">f</span><span class="s2">"Exported </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_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">"</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>
|
||||
@@ -1409,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'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>
|
||||
@@ -1718,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>
|
||||
@@ -2150,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>
|
||||
@@ -2260,6 +2319,20 @@
|
||||
<span class="n">xmp_str</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</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">"</span><span class="se">\n</span><span class="s2">"</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">""</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">"""Return the mako template for XMP sidecar, creating it if necessary"""</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">"""write sidecar_str to filename</span>
|
||||
<span class="sd"> used for exporting sidecar info"""</span>
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>osxphotos.photoinfo - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -217,7 +217,6 @@
|
||||
<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">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>
|
||||
|
||||
@@ -256,15 +255,21 @@
|
||||
<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">.platform</span> <span class="kn">import</span> <span class="n">assert_macos</span><span class="p">,</span> <span class="n">is_macos</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="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">"PhotoInfo"</span><span class="p">,</span> <span class="s2">"PhotoInfoNone"</span><span class="p">,</span> <span class="s2">"frozen_photoinfo_factory"</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">"osxphotos"</span><span class="p">)</span>
|
||||
@@ -328,8 +333,7 @@
|
||||
<span class="k">if</span> <span class="ow">not</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">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o"><=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">None</span>
|
||||
|
||||
<span class="n">imagedate</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">"lastmodifieddate"</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">imagedate</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">imagedate</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">"lastmodifieddate"</span><span class="p">]:</span>
|
||||
<span class="n">seconds</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">"imageTimeZoneOffsetSeconds"</span><span class="p">]</span> <span class="ow">or</span> <span class="mi">0</span>
|
||||
<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>
|
||||
@@ -366,6 +370,9 @@
|
||||
<span class="sd">"""Returns candidate path for original photo on Photos >= version 5"""</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">"shared"</span><span class="p">]:</span>
|
||||
<span class="k">return</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="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
|
||||
<span class="c1"># path for "shared with you" syndicated photos that have not yet been saved to the library</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_syndication</span><span class="p">()</span>
|
||||
<span class="k">return</span> <span class="p">(</span>
|
||||
<span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"directory"</span><span class="p">],</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"filename"</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">"directory"</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"/"</span><span class="p">)</span>
|
||||
@@ -405,6 +412,21 @@
|
||||
<span class="n">filename</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_path_syndication</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Return path for syndicated photo on Photos >= version 8"""</span>
|
||||
<span class="c1"># Photos 8+ stores syndicated photos in a separate directory</span>
|
||||
<span class="c1"># in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID.ext</span>
|
||||
<span class="c1"># where X is first digit of UUID</span>
|
||||
<span class="n">syndication_path</span> <span class="o">=</span> <span class="s2">"scopes/syndication/originals"</span>
|
||||
<span class="n">uuid_dir</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="mi">0</span><span class="p">]</span>
|
||||
<span class="n">path</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="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="n">syndication_path</span><span class="p">,</span>
|
||||
<span class="n">uuid_dir</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="p">)</span>
|
||||
<span class="k">return</span> <span class="n">path</span> <span class="k">if</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">path</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_path_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Returns candidate path for original photo on Photos <= version 4"""</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">"has_raw"</span><span class="p">]:</span>
|
||||
@@ -714,39 +736,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">"""list of PersonInfo objects for person in picture"""</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">"persons"</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">"persons"</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">"""list of FaceInfo objects for faces in picture"""</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">"""Moment photo belongs to"""</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">"momentID"</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">"momentID"</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>
|
||||
@@ -763,65 +772,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">"""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"""</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">"""list of AlbumInfo objects representing albums the photo is contained in"""</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">"""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."""</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">"""ImportInfo object representing import session for the photo or None if no import session"""</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">"import_uuid"</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">"import_uuid"</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">"import_uuid"</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">"import_uuid"</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">"""list of AlbumInfo objects representing projects for the photo or None if no projects"""</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>
|
||||
@@ -835,8 +820,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">"name"</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">""</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">""</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>
|
||||
@@ -1109,33 +1093,14 @@
|
||||
|
||||
<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"><=</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">"live_model_id"</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">"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">"</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">"resources"</span><span class="p">,</span>
|
||||
<span class="s2">"media"</span><span class="p">,</span>
|
||||
<span class="s2">"master"</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">"jpegvideocomplement_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov"</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've seen occasional missing movie for live photo</span>
|
||||
<span class="c1"># These appear to be valid -- e.g. live component hasn't been downloaded from iCloud</span>
|
||||
<span class="c1"># photos 4 has "isOnDisk" column we could check</span>
|
||||
<span class="c1"># or could do the actual check with "isfile"</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="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
|
||||
<span class="c1"># syndicated ("Shared with you") photos not yet saved to library</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_live_syndicated</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">"</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"</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>
|
||||
@@ -1149,8 +1114,68 @@
|
||||
|
||||
<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">"""Return path for live photo for shared photos"""</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">"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"</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">"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"</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">".MOV"</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">"""Return path for live edited photo for Photos <= 4"""</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">"live_model_id"</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">"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">"</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">"resources"</span><span class="p">,</span>
|
||||
<span class="s2">"media"</span><span class="p">,</span>
|
||||
<span class="s2">"master"</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">"jpegvideocomplement_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov"</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've seen occasional missing movie for live photo</span>
|
||||
<span class="c1"># These appear to be valid -- e.g. live component hasn't been downloaded from iCloud</span>
|
||||
<span class="c1"># photos 4 has "isOnDisk" column we could check</span>
|
||||
<span class="c1"># or could do the actual check with "isfile"</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="k">def</span> <span class="nf">_path_live_syndicated</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Return path for live syndicated photo on Photos >= version 8"""</span>
|
||||
<span class="c1"># Photos 8+ stores live syndicated photos in a separate directory</span>
|
||||
<span class="c1"># in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID_3.mov</span>
|
||||
<span class="c1"># where X is first digit of UUID</span>
|
||||
<span class="n">syndication_path</span> <span class="o">=</span> <span class="s2">"scopes/syndication/originals"</span>
|
||||
<span class="n">uuid_dir</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="mi">0</span><span class="p">]</span>
|
||||
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</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">filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_3.mov"</span>
|
||||
<span class="n">live_photo</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="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="n">syndication_path</span><span class="p">,</span>
|
||||
<span class="n">uuid_dir</span><span class="p">,</span>
|
||||
<span class="n">filename</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">live_photo</span> <span class="k">if</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">live_photo</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</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="k">def</span> <span class="nf">path_derivatives</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
||||
<span class="sd">"""Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)"""</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"><=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_4</span><span class="p">()</span>
|
||||
@@ -1159,24 +1184,36 @@
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_5_shared</span><span class="p">()</span>
|
||||
|
||||
<span class="n">directory</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="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
|
||||
<span class="n">derivative_path</span> <span class="o">=</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="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="o">/</span> <span class="sa">f</span><span class="s2">"resources/derivatives/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
|
||||
<span class="c1"># syndicated ("Shared with you") photos not yet saved to library</span>
|
||||
<span class="n">derivative_path</span> <span class="o">=</span> <span class="s2">"scopes/syndication/resources/derivatives"</span>
|
||||
<span class="n">thumb_path</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">derivative_path</span><span class="si">}</span><span class="s2">/masters/</span><span class="si">{</span><span class="n">directory</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="si">}</span><span class="s2">_4_5005_c.jpeg"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">derivative_path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"resources/derivatives/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="n">thumb_path</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"resources/derivatives/masters/</span><span class="si">{</span><span class="n">directory</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="si">}</span><span class="s2">_4_5005_c.jpeg"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">derivative_path</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">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="n">derivative_path</span><span class="p">)</span>
|
||||
<span class="n">thumb_path</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">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="n">thumb_path</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># find all files that start with uuid in derivative path</span>
|
||||
<span class="n">files</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">derivative_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="sa">f</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="si">}</span><span class="s2">*.*"</span><span class="p">))</span>
|
||||
|
||||
<span class="c1"># previews may be missing from derivatives path</span>
|
||||
<span class="c1"># there are what appear to be low res thumbnails in the "masters" subfolder</span>
|
||||
<span class="n">thumb_path</span> <span class="o">=</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="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="o">/</span> <span class="sa">f</span><span class="s2">"resources/derivatives/masters/</span><span class="si">{</span><span class="n">directory</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="si">}</span><span class="s2">_4_5005_c.jpeg"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">thumb_path</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
|
||||
<span class="n">files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">thumb_path</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># sort by file size, largest first</span>
|
||||
<span class="n">files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="n">f</span><span class="o">.</span><span class="n">stat</span><span class="p">()</span><span class="o">.</span><span class="n">st_size</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># return list of filename but skip .THM files (these are actually low-res thumbnails in JPEG format but with .THM extension)</span>
|
||||
<span class="n">derivatives</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span> <span class="k">if</span> <span class="n">filename</span><span class="o">.</span><span class="n">suffix</span> <span class="o">!=</span> <span class="s2">".THM"</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">derivatives</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".mov"</span><span class="p">):</span>
|
||||
<span class="c1"># ensure .mov is first in list as poster image could be larger than the movie preview</span>
|
||||
<span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">derivatives</span>
|
||||
@@ -1495,6 +1532,38 @@
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_search_info_normalized</span> <span class="o">=</span> <span class="n">SearchInfo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">normalized</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search_info_normalized</span>
|
||||
|
||||
<span class="nd">@cached_property</span>
|
||||
<span class="k">def</span> <span class="nf">syndicated</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="sd">"""Return true if photo was shared via syndication (e.g. via Messages, etc.);</span>
|
||||
<span class="sd"> these are photos that appear in "Shared with you" album.</span>
|
||||
<span class="sd"> Photos 8+ only; returns None if not Photos 8+.</span>
|
||||
<span class="sd"> """</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_version</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">None</span>
|
||||
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</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">_db_syndication_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="s2">"syndication_identifier"</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="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
|
||||
<span class="nd">@cached_property</span>
|
||||
<span class="k">def</span> <span class="nf">saved_to_library</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="sd">"""Return True if syndicated photo has been saved to library;</span>
|
||||
<span class="sd"> returns False if photo is not syndicated or has not been saved to the library.</span>
|
||||
<span class="sd"> Returns None if not Photos 8+.</span>
|
||||
<span class="sd"> Syndicated photos are photos that appear in "Shared with you" album; Photos 8+ only.</span>
|
||||
<span class="sd"> """</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_version</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">None</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">_db</span><span class="o">.</span><span class="n">_db_syndication_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="s2">"syndication_history"</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span>
|
||||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">labels</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""returns list of labels applied to photo by Photos image categorization</span>
|
||||
@@ -1695,6 +1764,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">"""detect text in photo, either from cached extended attribute or by attempting text detection"""</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>
|
||||
@@ -1974,41 +2045,31 @@
|
||||
<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">"""return dict representation"""</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">-></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">"""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"> """</span>
|
||||
|
||||
<span class="n">adjustments</span> <span class="o">=</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="n">album_info</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">burst_album_info</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">burst_photos</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">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">import_info</span> <span class="o">=</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="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">person_info</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">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">project_info</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">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">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="n">search_info_normalized</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="p">{</span>
|
||||
<span class="s2">"adjustments"</span><span class="p">:</span> <span class="n">adjustments</span><span class="p">,</span>
|
||||
<span class="s2">"album_info"</span><span class="p">:</span> <span class="n">album_info</span><span class="p">,</span>
|
||||
<span class="n">dict_data</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"albums"</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">"burst_album_info"</span><span class="p">:</span> <span class="n">burst_album_info</span><span class="p">,</span>
|
||||
<span class="s2">"burst_albums"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_albums</span><span class="p">,</span>
|
||||
<span class="s2">"burst_default_pick"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_default_pick</span><span class="p">,</span>
|
||||
<span class="s2">"burst_key"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_key</span><span class="p">,</span>
|
||||
<span class="s2">"burst_photos"</span><span class="p">:</span> <span class="n">burst_photos</span><span class="p">,</span>
|
||||
<span class="s2">"burst_selected"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_selected</span><span class="p">,</span>
|
||||
<span class="s2">"burst"</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">"cloud_guid"</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">"cloud_metadata"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_metadata</span><span class="p">,</span>
|
||||
<span class="s2">"cloud_owner_hashed_id"</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">"comments"</span><span class="p">:</span> <span class="n">comments</span><span class="p">,</span>
|
||||
<span class="s2">"date_added"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_added</span><span class="p">,</span>
|
||||
@@ -2028,7 +2089,6 @@
|
||||
<span class="s2">"hdr"</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">"height"</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">"hidden"</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">"import_info"</span><span class="p">:</span> <span class="n">import_info</span><span class="p">,</span>
|
||||
<span class="s2">"incloud"</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">"intrash"</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">"iscloudasset"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">iscloudasset</span><span class="p">,</span>
|
||||
@@ -2038,7 +2098,6 @@
|
||||
<span class="s2">"israw"</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">"isreference"</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">"keywords"</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">"labels_normalized"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">labels_normalized</span><span class="p">,</span>
|
||||
<span class="s2">"labels"</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">"latitude"</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">"library"</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>
|
||||
@@ -2054,22 +2113,17 @@
|
||||
<span class="s2">"original_width"</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">"owner"</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">"panorama"</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">"path_derivatives"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">,</span>
|
||||
<span class="s2">"path_edited_live_photo"</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">"path_edited"</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">"path_live_photo"</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">"path_raw"</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">"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="s2">"person_info"</span><span class="p">:</span> <span class="n">person_info</span><span class="p">,</span>
|
||||
<span class="s2">"persons"</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">"place"</span><span class="p">:</span> <span class="n">place</span><span class="p">,</span>
|
||||
<span class="s2">"portrait"</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">"project_info"</span><span class="p">:</span> <span class="n">project_info</span><span class="p">,</span>
|
||||
<span class="s2">"raw_original"</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">"score"</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span>
|
||||
<span class="s2">"screenshot"</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">"search_info_normalized"</span><span class="p">:</span> <span class="n">search_info_normalized</span><span class="p">,</span>
|
||||
<span class="s2">"search_info"</span><span class="p">:</span> <span class="n">search_info</span><span class="p">,</span>
|
||||
<span class="s2">"selfie"</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">"shared"</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">"slow_mo"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">slow_mo</span><span class="p">,</span>
|
||||
@@ -2083,16 +2137,59 @@
|
||||
<span class="s2">"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="s2">"visible"</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">"width"</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="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">"""Return JSON representation"""</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">"album_info"</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">"path_derivatives"</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">"adjustments"</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">"burst_album_info"</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">"burst_albums"</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">"burst_default_pick"</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">"burst_key"</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">"burst_photos"</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">"burst_selected"</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">"cloud_metadata"</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">"import_info"</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">"labels_normalized"</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">"person_info"</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">"project_info"</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">"search_info"</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">"search_info_normalized"</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">-></span> <span class="nb">str</span><span class="p">:</span>
|
||||
<span class="sd">"""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"> """</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>
|
||||
@@ -2100,7 +2197,11 @@
|
||||
<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">""</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="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">-></span> <span class="n">PhotoTables</span><span class="p">:</span>
|
||||
<span class="sd">"""Return PhotoTables object to provide access database tables associated with this photo (Photos 5+)"""</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"><</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">"""JSON for use by hexdigest()"""</span>
|
||||
@@ -2114,15 +2215,9 @@
|
||||
<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">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span>
|
||||
<span class="s2">"album_info"</span><span class="p">,</span>
|
||||
<span class="s2">"burst_album_info"</span><span class="p">,</span>
|
||||
<span class="s2">"face_info"</span><span class="p">,</span>
|
||||
<span class="s2">"person_info"</span><span class="p">,</span>
|
||||
<span class="s2">"visible"</span><span class="p">,</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">"face_info"</span><span class="p">,</span> <span class="s2">"visible"</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>
|
||||
|
||||
@@ -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.photosalbum - osxphotos 0.58.1 documentation</title>
|
||||
<title>osxphotos.photosalbum - osxphotos 0.60.5 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.58.1 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.58.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -197,31 +197,55 @@
|
||||
<h1>Source code for osxphotos.photosalbum</h1><div class="highlight"><pre>
|
||||
<span></span><span class="sd">""" PhotosAlbum class to create an album in default Photos library and add photos to it """</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">unicodedata</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">.platform</span> <span class="kn">import</span> <span class="n">assert_macos</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">noop</span><span class="p">,</span> <span class="n">pluralize</span>
|
||||
|
||||
<span class="n">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">"PhotosAlbum"</span><span class="p">,</span> <span class="s2">"PhotosAlbumPhotoScript"</span><span class="p">]</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_unicode_variants</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
||||
<span class="sd">"""Get all unicode variants of string"""</span>
|
||||
<span class="n">variants</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">form</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"NFC"</span><span class="p">,</span> <span class="s2">"NFD"</span><span class="p">,</span> <span class="s2">"NFKC"</span><span class="p">,</span> <span class="s2">"NFKD"</span><span class="p">]:</span>
|
||||
<span class="n">normalized</span> <span class="o">=</span> <span class="n">unicodedata</span><span class="o">.</span><span class="n">normalize</span><span class="p">(</span><span class="n">form</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
|
||||
<span class="n">variants</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">normalized</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">variants</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">-></span> <span class="n">Folder</span><span class="p">:</span>
|
||||
<span class="sd">"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""</span>
|
||||
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
|
||||
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
|
||||
<span class="n">top_folder_name</span> <span class="o">=</span> <span class="n">folders</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
|
||||
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">top_folder</span><span class="p">:</span>
|
||||
|
||||
<span class="k">for</span> <span class="n">folder_variant</span> <span class="ow">in</span> <span class="n">get_unicode_variants</span><span class="p">(</span><span class="n">top_folder_name</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">folder_variant</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">top_folder</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">top_folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">)</span>
|
||||
|
||||
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">top_folder</span>
|
||||
<span class="k">for</span> <span class="n">folder_name</span> <span class="ow">in</span> <span class="n">folders</span><span class="p">:</span>
|
||||
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">folder</span><span class="p">:</span>
|
||||
<span class="k">for</span> <span class="n">folder_variant</span> <span class="ow">in</span> <span class="n">get_unicode_variants</span><span class="p">(</span><span class="n">folder_name</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_variant</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">folder</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
|
||||
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">folder</span>
|
||||
@@ -238,15 +262,24 @@
|
||||
<span class="c1"># have folders</span>
|
||||
<span class="n">album_name</span> <span class="o">=</span> <span class="n">folders_album</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
|
||||
<span class="n">folder</span> <span class="o">=</span> <span class="n">folder_by_path</span><span class="p">(</span><span class="n">folders_album</span><span class="p">,</span> <span class="n">verbose</span><span class="p">)</span>
|
||||
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">for</span> <span class="n">album_variant</span> <span class="ow">in</span> <span class="n">get_unicode_variants</span><span class="p">(</span><span class="n">album_name</span><span class="p">):</span>
|
||||
<span class="c1"># Get album if it exists</span>
|
||||
<span class="c1"># need to check every unicode variant to avoid creating duplicate albums with same visual representation (#1085)</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_variant</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># only have album name</span>
|
||||
<span class="n">album_name</span> <span class="o">=</span> <span class="n">folders_album</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">for</span> <span class="n">album_variant</span> <span class="ow">in</span> <span class="n">get_unicode_variants</span><span class="p">(</span><span class="n">album_name</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_variant</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="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># album doesn't exist, create it</span>
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
|
||||
|
||||
|
||||
@@ -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_process_comments - osxphotos 0.58.1 documentation</title>
|
||||
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.60.5 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.58.1 documentation</div></a>
|
||||
<a href="../../../index.html"><div class="brand">osxphotos 0.60.5 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.58.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -203,8 +203,8 @@
|
||||
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.._constants</span> <span class="kn">import</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">,</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">,</span> <span class="n">TIME_DELTA</span>
|
||||
<span class="kn">from</span> <span class="nn">..utils</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
|
||||
<span class="kn">from</span> <span class="nn">..sqlite_utils</span> <span class="kn">import</span> <span class="n">sqlite_open_ro</span>
|
||||
<span class="kn">from</span> <span class="nn">..unicode</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">_process_comments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>osxphotos.photosdb.photosdb - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../../../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -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>
|
||||
@@ -254,18 +253,17 @@
|
||||
<span class="kn">from</span> <span class="nn">..personinfo</span> <span class="kn">import</span> <span class="n">PersonInfo</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">..phototemplate</span> <span class="kn">import</span> <span class="n">RenderOptions</span>
|
||||
<span class="kn">from</span> <span class="nn">..platform</span> <span class="kn">import</span> <span class="n">get_macos_version</span><span class="p">,</span> <span class="n">is_macos</span>
|
||||
<span class="kn">from</span> <span class="nn">..queryoptions</span> <span class="kn">import</span> <span class="n">QueryOptions</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">..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">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>
|
||||
<span class="n">normalize_unicode</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="kn">from</span> <span class="nn">..unicode</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
|
||||
<span class="kn">from</span> <span class="nn">..utils</span> <span class="kn">import</span> <span class="n">_check_file_exists</span><span class="p">,</span> <span class="n">get_last_library_path</span><span class="p">,</span> <span class="n">noop</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">"osxphotos"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"PhotosDB"</span><span class="p">]</span>
|
||||
@@ -290,6 +288,7 @@
|
||||
<span class="n">labels_normalized</span><span class="p">,</span>
|
||||
<span class="n">labels_normalized_as_dict</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="kn">from</span> <span class="nn">._photosdb_process_syndicationinfo</span> <span class="kn">import</span> <span class="n">_process_syndicationinfo</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
@@ -315,8 +314,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">"Darwin"</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">"Darwin"</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">"WARNING: This module has only been tested with macOS versions "</span>
|
||||
<span class="sa">f</span><span class="s2">"[</span><span class="si">{</span><span class="s1">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="sa">f</span><span class="s1">'</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">'</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">]: "</span>
|
||||
@@ -484,6 +483,10 @@
|
||||
<span class="c1"># Dict to hold data on imports for Photos <= 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="c1"># Dict to hold syndication info for Photos >= 8</span>
|
||||
<span class="c1"># key is UUID and value is dict of syndication info</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_db_syndication_uuid</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">"dbfile = </span><span class="si">{</span><span class="n">dbfile</span><span class="si">}</span><span class="s2">"</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>
|
||||
@@ -713,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">"""return list of AlbumInfo objects for each shared album in the photos database</span>
|
||||
<span class="sd"> only valid for Photos 5; on Photos <= 4, prints warning and returns empty list"""</span>
|
||||
<span class="sd"> only valid for Photos 5; on Photos <= 4, prints warning and returns empty list</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="c1"># if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it'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>
|
||||
@@ -740,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">"""return list of shared albums found in photos database</span>
|
||||
<span class="sd"> only valid for Photos 5; on Photos <= 4, prints warning and returns empty list"""</span>
|
||||
<span class="sd"> only valid for Photos 5; on Photos <= 4, prints warning and returns empty list</span>
|
||||
<span class="sd"> """</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>
|
||||
@@ -2709,6 +2714,10 @@
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="s2">"Processing moments."</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process_moments</span><span class="p">()</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">>=</span> <span class="mi">8</span><span class="p">:</span>
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="s2">"Processing syndication info."</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process_syndicationinfo</span><span class="p">()</span>
|
||||
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="s2">"Done processing details from Photos library."</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_process_moments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
@@ -3250,7 +3259,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">"""Returns a list of photos with UUID in uuids.</span>
|
||||
<span class="sd"> Does not generate error if invalid or missing UUID passed.</span>
|
||||
@@ -3263,14 +3271,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"> """</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">-></span> <span class="n">List</span><span class="p">[</span><span class="n">PhotoInfo</span><span class="p">]:</span>
|
||||
<span class="sd">"""Run a query against PhotosDB to extract the photos based on user supplied options</span>
|
||||
@@ -3712,6 +3717,16 @@
|
||||
<span class="n">added_after</span> <span class="o">=</span> <span class="n">datetime_naive_to_local</span><span class="p">(</span><span class="n">added_after</span><span class="p">)</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">date_added</span> <span class="ow">and</span> <span class="n">p</span><span class="o">.</span><span class="n">date_added</span> <span class="o">></span> <span class="n">added_after</span><span class="p">]</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">syndicated</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">syndicated</span><span class="p">]</span>
|
||||
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">not_syndicated</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">p</span><span class="o">.</span><span class="n">syndicated</span><span class="p">]</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="n">p</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">]</span>
|
||||
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">not_saved_to_library</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">p</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">]</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">function</span><span class="p">:</span>
|
||||
<span class="k">for</span> <span class="n">function</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">function</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="n">function</span><span class="p">[</span><span class="mi">0</span><span class="p">](</span><span class="n">photos</span><span class="p">)</span>
|
||||
|
||||
@@ -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.59.0 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.59.0 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.59.0 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">
|
||||
@@ -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>
|
||||
@@ -1350,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">"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">"</span>
|
||||
<span class="sa">f</span><span class="s2">"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">"</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>
|
||||
@@ -1879,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">"Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">"</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">"PhotoInfo"</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">"PhotoInfo"</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">"""Get the value of a 'place' field by attribute</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
|
||||
@@ -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.placeinfo - osxphotos 0.59.0 documentation</title>
|
||||
<title>osxphotos.placeinfo - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -208,8 +208,7 @@
|
||||
<span class="kn">import</span> <span class="nn">yaml</span>
|
||||
<span class="kn">from</span> <span class="nn">bpylist2</span> <span class="kn">import</span> <span class="n">archiver</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="n">UNICODE_FORMAT</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
|
||||
<span class="kn">from</span> <span class="nn">.unicode</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>
|
||||
@@ -268,6 +267,7 @@
|
||||
<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>
|
||||
|
||||
@@ -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.58.1 documentation</title>
|
||||
<title>osxphotos.queryoptions - osxphotos 0.60.5 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.58.1 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.58.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -305,6 +305,10 @@
|
||||
<span class="sd"> uti: list of UTIs to search for</span>
|
||||
<span class="sd"> uuid: list of uuids to search for</span>
|
||||
<span class="sd"> year: search for photos taken in a given year</span>
|
||||
<span class="sd"> syndicated: search for photos that have been shared via syndication ("Shared with You" album via Messages, etc.)</span>
|
||||
<span class="sd"> not_syndicated: search for photos that have not been shared via syndication ("Shared with You" album via Messages, etc.)</span>
|
||||
<span class="sd"> saved_to_library: search for syndicated photos that have been saved to the Photos library</span>
|
||||
<span class="sd"> not_saved_to_library: search for syndicated photos that have not been saved to the Photos library</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">added_after</span><span class="p">:</span> <span class="n">Optional</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="o">=</span> <span class="kc">None</span>
|
||||
@@ -389,6 +393,10 @@
|
||||
<span class="n">uti</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Iterable</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">uuid</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Iterable</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">year</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Iterable</span><span class="p">[</span><span class="nb">int</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">syndicated</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">not_syndicated</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">saved_to_library</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">not_saved_to_library</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
|
||||
@@ -458,6 +466,8 @@
|
||||
<span class="p">(</span><span class="s2">"deleted"</span><span class="p">,</span> <span class="s2">"not_deleted"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"deleted"</span><span class="p">,</span> <span class="s2">"deleted_only"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"deleted_only"</span><span class="p">,</span> <span class="s2">"not_deleted"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"syndicated"</span><span class="p">,</span> <span class="s2">"not_syndicated"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"saved_to_library"</span><span class="p">,</span> <span class="s2">"not_saved_to_library"</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>
|
||||
|
||||
@@ -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.58.1 documentation</title>
|
||||
<title>osxphotos.scoreinfo - osxphotos 0.60.5 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.58.1 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.60.5 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.58.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -197,7 +197,7 @@
|
||||
<h1>Source code for osxphotos.scoreinfo</h1><div class="highlight"><pre>
|
||||
<span></span><span class="sd">""" ScoreInfo class to expose computed score info from the library """</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">asdict</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">._constants</span> <span class="kn">import</span> <span class="n">_PHOTOS_4_VERSION</span>
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ Template Substitutions
|
||||
* - {tab}
|
||||
- :A tab: '\t'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.59.0'
|
||||
- The osxphotos version, e.g. '0.60.5'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
2
docs/_static/documentation_options.js
vendored
2
docs/_static/documentation_options.js
vendored
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.59.0',
|
||||
VERSION: '0.60.5',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
132
docs/cli.html
132
docs/cli.html
@@ -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.59.0 documentation</title>
|
||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -728,6 +728,30 @@ See <cite>osxphotos help timewarp</cite> for more information.</p>
|
||||
<dd><p>Search for photos that are not in iCloud (have not been synched)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-not-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--not-syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-not-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have not been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-not-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--not-saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-not-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have not saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-regex">
|
||||
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre"><REGEX</span> <span class="pre">TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-regex" title="Permalink to this definition">#</a></dt>
|
||||
@@ -1659,6 +1683,30 @@ to modify this behavior.</p>
|
||||
<dd><p>Search for photos that are not in iCloud (have not been synched)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-not-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--not-syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-not-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have not been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-not-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--not-saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-not-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have not saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-regex">
|
||||
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre"><REGEX</span> <span class="pre">TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-export-regex" title="Permalink to this definition">#</a></dt>
|
||||
@@ -2781,6 +2829,12 @@ If the same query option is provided multiple times, they are treated as
|
||||
<dd><p>Print output in JSON format.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-count">
|
||||
<span class="sig-name descname"><span class="pre">--count</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-count" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print count of photos matching query and exit.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--keyword</span></span><span class="sig-prename descclassname"> <span class="pre"><KEYWORD></span></span><a class="headerlink" href="#cmdoption-osxphotos-query-keyword" title="Permalink to this definition">#</a></dt>
|
||||
@@ -3225,6 +3279,30 @@ If the same query option is provided multiple times, they are treated as
|
||||
<dd><p>Search for photos that are not in iCloud (have not been synched)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-not-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--not-syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-not-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have not been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-not-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--not-saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-not-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have not saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-regex">
|
||||
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre"><REGEX</span> <span class="pre">TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-query-regex" title="Permalink to this definition">#</a></dt>
|
||||
@@ -3762,6 +3840,30 @@ If the same query option is provided multiple times, they are treated as
|
||||
<dd><p>Search for photos that are not in iCloud (have not been synched)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-not-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--not-syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-not-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have not been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-not-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--not-saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-not-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have not saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-regex">
|
||||
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre"><REGEX</span> <span class="pre">TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-repl-regex" title="Permalink to this definition">#</a></dt>
|
||||
@@ -4421,6 +4523,30 @@ two different computers, you can export the metadata to a shared folder.</p>
|
||||
<dd><p>Search for photos that are not in iCloud (have not been synched)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-not-syndicated">
|
||||
<span class="sig-name descname"><span class="pre">--not-syndicated</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-not-syndicated" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos that have not been shared via syndication (‘Shared with You’ album via Messages, etc.)</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-not-saved-to-library">
|
||||
<span class="sig-name descname"><span class="pre">--not-saved-to-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-not-saved-to-library" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for syndicated photos that have not saved to the library</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-regex">
|
||||
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre"><REGEX</span> <span class="pre">TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-sync-regex" title="Permalink to this definition">#</a></dt>
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -423,6 +423,13 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-convert-to-jpeg">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--count
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-count">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1563,8 +1570,6 @@
|
||||
<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
|
||||
|
||||
@@ -1595,6 +1600,8 @@
|
||||
<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
|
||||
|
||||
@@ -1728,6 +1735,21 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-reference">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-reference">osxphotos-sync command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--not-saved-to-library
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-saved-to-library">osxphotos-add-locations command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-saved-to-library">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-saved-to-library">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-saved-to-library">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-saved-to-library">osxphotos-sync command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1786,6 +1808,21 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-slow-mo">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-slow-mo">osxphotos-sync command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--not-syndicated
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-syndicated">osxphotos-add-locations command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-syndicated">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-syndicated">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-syndicated">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-syndicated">osxphotos-sync command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -2132,6 +2169,21 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-save-config">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-save-config">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--saved-to-library
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-saved-to-library">osxphotos-add-locations command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-saved-to-library">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-saved-to-library">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-saved-to-library">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-saved-to-library">osxphotos-sync command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -2303,6 +2355,21 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-s">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--syndicated
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-syndicated">osxphotos-add-locations command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-syndicated">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-syndicated">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-syndicated">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-syndicated">osxphotos-sync command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -3305,10 +3372,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>
|
||||
@@ -3320,9 +3387,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>
|
||||
@@ -3656,10 +3731,10 @@
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_favorite">not_favorite (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_hdr">not_hdr (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_hidden">not_hidden (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_in_album">not_in_album (osxphotos.QueryOptions attribute)</a>
|
||||
@@ -3675,6 +3750,8 @@
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_portrait">not_portrait (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_reference">not_reference (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_saved_to_library">not_saved_to_library (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_screenshot">not_screenshot (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
@@ -3683,6 +3760,8 @@
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_shared">not_shared (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_slow_mo">not_slow_mo (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_syndicated">not_syndicated (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_time_lapse">not_time_lapse (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
@@ -3837,6 +3916,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-portrait">--not-portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-reference">--not-reference</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-saved-to-library">--not-saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-screenshot">--not-screenshot</a>
|
||||
</li>
|
||||
@@ -3845,6 +3926,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-shared">--not-shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-slow-mo">--not-slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-syndicated">--not-syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-time-lapse">--not-time-lapse</a>
|
||||
</li>
|
||||
@@ -3865,6 +3948,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-query-function">--query-function</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-regex">--regex</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-saved-to-library">--saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-screenshot">--screenshot</a>
|
||||
</li>
|
||||
@@ -3875,6 +3960,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-shared">--shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-slow-mo">--slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-syndicated">--syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-add-locations-theme">--theme</a>
|
||||
</li>
|
||||
@@ -4241,6 +4328,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-portrait">--not-portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-reference">--not-reference</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-saved-to-library">--not-saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-screenshot">--not-screenshot</a>
|
||||
</li>
|
||||
@@ -4249,6 +4338,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-shared">--not-shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-slow-mo">--not-slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-syndicated">--not-syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-not-time-lapse">--not-time-lapse</a>
|
||||
</li>
|
||||
@@ -4299,6 +4390,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-retry">--retry</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-save-config">--save-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-saved-to-library">--saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-screenshot">--screenshot</a>
|
||||
</li>
|
||||
@@ -4329,6 +4422,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-slow-mo">--slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-strip">--strip</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-syndicated">--syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-theme">--theme</a>
|
||||
</li>
|
||||
@@ -4674,6 +4769,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-burst">--burst</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-cloudasset">--cloudasset</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-count">--count</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-library">--db</a>
|
||||
</li>
|
||||
@@ -4778,6 +4875,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-portrait">--not-portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-reference">--not-reference</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-saved-to-library">--not-saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-screenshot">--not-screenshot</a>
|
||||
</li>
|
||||
@@ -4786,6 +4885,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared">--not-shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-slow-mo">--not-slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-syndicated">--not-syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-not-time-lapse">--not-time-lapse</a>
|
||||
</li>
|
||||
@@ -4810,6 +4911,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-quiet">--quiet</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-regex">--regex</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-saved-to-library">--saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-screenshot">--screenshot</a>
|
||||
</li>
|
||||
@@ -4820,6 +4923,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-shared">--shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-slow-mo">--slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-syndicated">--syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-time-lapse">--time-lapse</a>
|
||||
</li>
|
||||
@@ -4961,6 +5066,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-portrait">--not-portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-reference">--not-reference</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-saved-to-library">--not-saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-screenshot">--not-screenshot</a>
|
||||
</li>
|
||||
@@ -4969,6 +5076,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared">--not-shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-slow-mo">--not-slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-syndicated">--not-syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-time-lapse">--not-time-lapse</a>
|
||||
</li>
|
||||
@@ -4989,6 +5098,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-query-function">--query-function</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-regex">--regex</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-saved-to-library">--saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-screenshot">--screenshot</a>
|
||||
</li>
|
||||
@@ -4999,6 +5110,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-shared">--shared</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-slow-mo">--slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-syndicated">--syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-time-lapse">--time-lapse</a>
|
||||
</li>
|
||||
@@ -5173,12 +5286,16 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-portrait">--not-portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-reference">--not-reference</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-saved-to-library">--not-saved-to-library</a>
|
||||
</li>
|
||||
<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-slow-mo">--not-slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-syndicated">--not-syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-not-time-lapse">--not-time-lapse</a>
|
||||
</li>
|
||||
@@ -5201,6 +5318,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-regex">--regex</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-R">--report</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-saved-to-library">--saved-to-library</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-screenshot">--screenshot</a>
|
||||
</li>
|
||||
@@ -5211,6 +5330,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-s">--set</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-slow-mo">--slow-mo</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-syndicated">--syndicated</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-sync-theme">--theme</a>
|
||||
</li>
|
||||
@@ -5612,6 +5733,12 @@
|
||||
<h2>S</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.saved_to_library">saved_to_library (osxphotos.PhotoInfo property)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.saved_to_library">(osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.score">score (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ScoreInfo">ScoreInfo (class in osxphotos)</a>
|
||||
@@ -5693,6 +5820,12 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-help-arg-SUBTOPIC">osxphotos-help command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.syndicated">syndicated (osxphotos.PhotoInfo property)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.syndicated">(osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
@@ -5703,6 +5836,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>
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="#"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -517,6 +517,7 @@
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.project_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.project_info</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.raw_original"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.raw_original</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.render_template"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.render_template()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.saved_to_library"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.saved_to_library</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.score"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.score</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.screenshot"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.screenshot</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.search_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.search_info</span></code></a></li>
|
||||
@@ -524,6 +525,8 @@
|
||||
<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.syndicated"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.syndicated</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>
|
||||
@@ -590,7 +593,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>
|
||||
@@ -672,6 +679,10 @@
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.uti"><code class="docutils literal notranslate"><span class="pre">QueryOptions.uti</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.uuid"><code class="docutils literal notranslate"><span class="pre">QueryOptions.uuid</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.year"><code class="docutils literal notranslate"><span class="pre">QueryOptions.year</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.syndicated"><code class="docutils literal notranslate"><span class="pre">QueryOptions.syndicated</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_syndicated"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_syndicated</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.saved_to_library"><code class="docutils literal notranslate"><span class="pre">QueryOptions.saved_to_library</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_saved_to_library"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_saved_to_library</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ScoreInfo"><code class="docutils literal notranslate"><span class="pre">ScoreInfo</span></code></a><ul>
|
||||
|
||||
BIN
docs/objects.inv
BIN
docs/objects.inv
Binary file not shown.
@@ -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 OSXPhotos’s documentation!" href="index.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||
<title>OSXPhotos - osxphotos 0.59.0 documentation</title>
|
||||
<title>OSXPhotos - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
|
||||
@@ -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 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.59.0 documentation</title>
|
||||
<title>OSXPhotos Python Package Overview - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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.59.0 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.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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.59.0 documentation</title>
|
||||
<title>OSXPhotos Template System - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
@@ -613,7 +613,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.59.0’</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.60.5’</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>
|
||||
|
||||
@@ -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.59.0 documentation</title>
|
||||
<title>OSXPhotos Tutorial - osxphotos 0.60.5 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.59.0 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.60.5 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.59.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.60.5 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">
|
||||
|
||||
@@ -361,7 +361,7 @@ Template Substitutions
|
||||
* - {tab}
|
||||
- :A tab: '\t'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.59.0'
|
||||
- The osxphotos version, e.g. '0.60.5'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
@@ -21,7 +21,6 @@ from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||
|
||||
|
||||
class Latitude(click.ParamType):
|
||||
|
||||
name = "Latitude"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
@@ -37,7 +36,6 @@ class Latitude(click.ParamType):
|
||||
|
||||
|
||||
class Longitude(click.ParamType):
|
||||
|
||||
name = "Longitude"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
|
||||
@@ -23,10 +23,7 @@ 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,
|
||||
)
|
||||
from osxphotos.cli import selection_command, verbose
|
||||
|
||||
|
||||
@selection_command
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
import sys
|
||||
|
||||
import osxphotos
|
||||
import click
|
||||
|
||||
import osxphotos
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("album1")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import osxphotos
|
||||
import os.path
|
||||
|
||||
import osxphotos
|
||||
|
||||
|
||||
def main():
|
||||
db = osxphotos.utils.get_system_library_path()
|
||||
|
||||
@@ -32,7 +32,7 @@ import osxphotos
|
||||
default=False,
|
||||
)
|
||||
def export(export_path, default_album, library_path, edited):
|
||||
""" Export all photos, organized by album """
|
||||
"""Export all photos, organized by album"""
|
||||
export_path = os.path.expanduser(export_path)
|
||||
library_path = os.path.expanduser(library_path) if library_path else None
|
||||
|
||||
@@ -66,11 +66,11 @@ def export(export_path, default_album, library_path, edited):
|
||||
os.makedirs(dest_dir)
|
||||
|
||||
filename = p.original_filename
|
||||
# export the photo but only if --edited, photo has adjustments, and
|
||||
# export the photo but only if --edited, photo has adjustments, and
|
||||
# path_edited is not None (can be None if edited photo is missing)
|
||||
if edited and p.hasadjustments and p.path_edited:
|
||||
# export edited version
|
||||
# use original filename with _edited appended but make sure suffix is
|
||||
# use original filename with _edited appended but make sure suffix is
|
||||
# same as edited file
|
||||
edited_filename = f"{pathlib.Path(filename).stem}_edited{pathlib.Path(p.path_edited).suffix}"
|
||||
exported = p.export(dest_dir, edited_filename, edited=True)
|
||||
|
||||
@@ -32,7 +32,7 @@ import osxphotos
|
||||
default=None,
|
||||
)
|
||||
def export(export_path, library_path, uuid):
|
||||
""" export photos to export_path and draw faces """
|
||||
"""export photos to export_path and draw faces"""
|
||||
library_path = os.path.expanduser(library_path) if library_path else None
|
||||
if library_path is not None:
|
||||
photosdb = osxphotos.PhotosDB(library_path)
|
||||
@@ -61,7 +61,7 @@ def export(export_path, library_path, uuid):
|
||||
|
||||
|
||||
def get_circle_points(xy, radius):
|
||||
""" Returns tuples of (x0, y0), (x1, y1) for a circle centered at x, y with radius
|
||||
"""Returns tuples of (x0, y0), (x1, y1) for a circle centered at x, y with radius
|
||||
|
||||
Arguments:
|
||||
xy: tuple of x, y coordinates
|
||||
|
||||
126
examples/export_previews.py
Normal file
126
examples/export_previews.py
Normal 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()
|
||||
@@ -77,7 +77,9 @@ def place_folder(photo: osxphotos.PhotoInfo) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def photos_folders(photo: osxphotos.PhotoInfo, options: osxphotos.phototemplate.RenderOptions, **kwargs) -> Union[List, str]:
|
||||
def photos_folders(
|
||||
photo: osxphotos.PhotoInfo, options: osxphotos.phototemplate.RenderOptions, **kwargs
|
||||
) -> Union[List, str]:
|
||||
"""template function for use with --directory to export photos in a folder structure similar to Photos
|
||||
|
||||
Args:
|
||||
|
||||
@@ -29,7 +29,9 @@ def main():
|
||||
exported = photo.export(tempdir.name, use_photos_export=True, timeout=300)
|
||||
if photo.hasadjustments:
|
||||
exported.extend(
|
||||
photo.export(tempdir.name, use_photos_export=True, edited=True, timeout=300)
|
||||
photo.export(
|
||||
tempdir.name, use_photos_export=True, edited=True, timeout=300
|
||||
)
|
||||
)
|
||||
for filename in exported:
|
||||
print(f"Removing temporary file {filename}")
|
||||
|
||||
@@ -10,7 +10,7 @@ from osxphotos._constants import TIME_DELTA
|
||||
|
||||
@dataclass
|
||||
class Comment:
|
||||
""" Class for shared photo comments """
|
||||
"""Class for shared photo comments"""
|
||||
|
||||
uuid: str
|
||||
sort_fok: int
|
||||
@@ -22,7 +22,7 @@ class Comment:
|
||||
|
||||
@dataclass
|
||||
class Like:
|
||||
""" Class for shared photo likes """
|
||||
"""Class for shared photo likes"""
|
||||
|
||||
uuid: str
|
||||
sort_fok: int
|
||||
@@ -32,10 +32,10 @@ class Like:
|
||||
|
||||
|
||||
def get_shared_person_info(photosdb, hashed_person_id):
|
||||
""" returns tuple of (first name, last name, full name)
|
||||
for person invited to shared album with
|
||||
"""returns tuple of (first name, last name, full name)
|
||||
for person invited to shared album with
|
||||
ZINVITEEHASHEDPERSONID = hashed_person_id
|
||||
|
||||
|
||||
Args:
|
||||
photosdb: a osxphotos.PhotosDB object
|
||||
hashed_person_id: str, value of ZINVITEEHASHEDPERSONID to lookup
|
||||
@@ -66,12 +66,12 @@ def get_shared_person_info(photosdb, hashed_person_id):
|
||||
|
||||
|
||||
def get_comments(photosdb, uuid):
|
||||
""" return comments and likes, if any, for photo with uuid
|
||||
"""return comments and likes, if any, for photo with uuid
|
||||
|
||||
Args:
|
||||
photosdb: a osxphotos.PhotosDB object
|
||||
uuid: uuid of the photo
|
||||
|
||||
|
||||
Returns:
|
||||
tuple of (list of comments as Comment objects or [] if no comments, list of likes as Like objects or [] if no likes)
|
||||
"""
|
||||
|
||||
@@ -15,14 +15,14 @@ import time
|
||||
import click
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.cli import get_photos_db, _list_libraries
|
||||
from osxphotos.cli import _list_libraries, get_photos_db
|
||||
|
||||
|
||||
def show(photo):
|
||||
""" open image with default image viewer
|
||||
|
||||
Note: This is for debugging only -- it will actually open any filetype which could
|
||||
be very, very bad.
|
||||
"""open image with default image viewer
|
||||
|
||||
Note: This is for debugging only -- it will actually open any filetype which could
|
||||
be very, very bad.
|
||||
|
||||
Args:
|
||||
photo: PhotoInfo object or a path to a photo on disk
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
""" Example function for use with osxphotos import --post-function option """
|
||||
|
||||
import typing as t
|
||||
import photoscript
|
||||
import pathlib
|
||||
import typing as t
|
||||
|
||||
import photoscript
|
||||
|
||||
from osxphotos.cli.import_cli import ReportRecord
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import os.path
|
||||
|
||||
import osxphotos
|
||||
|
||||
|
||||
def main():
|
||||
db = os.path.expanduser("~/Pictures/Photos Library.photoslibrary")
|
||||
photosdb = osxphotos.PhotosDB(db)
|
||||
@@ -15,11 +16,14 @@ def main():
|
||||
print(photosdb.albums_as_dict)
|
||||
|
||||
# find all photos with Keyword = Foo and containing John Smith
|
||||
photos = photosdb.photos(keywords=["Foo"],persons=["John Smith"])
|
||||
photos = photosdb.photos(keywords=["Foo"], persons=["John Smith"])
|
||||
|
||||
# find all photos that include Alice Smith but do not contain the keyword Bar
|
||||
photos = [p for p in photosdb.photos(persons=["Alice Smith"])
|
||||
if p not in photosdb.photos(keywords=["Bar"]) ]
|
||||
photos = [
|
||||
p
|
||||
for p in photosdb.photos(persons=["Alice Smith"])
|
||||
if p not in photosdb.photos(keywords=["Bar"])
|
||||
]
|
||||
for p in photos:
|
||||
print(
|
||||
p.uuid,
|
||||
@@ -34,5 +38,6 @@ def main():
|
||||
p.path,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
def myfilter(values: List[str]) -> List[str]:
|
||||
""" Custom filter to append "foo-" to template value """
|
||||
"""Custom filter to append "foo-" to template value"""
|
||||
values = ["foo-" + val for val in values]
|
||||
return values
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from typing import Callable
|
||||
|
||||
from osxphotos import ExportResults, PhotoInfo
|
||||
from osxphotos.exiftool import ExifTool
|
||||
from osxphotos.utils import normalize_unicode
|
||||
from osxphotos.unicode import normalize_unicode
|
||||
|
||||
# Update this for your custom keyword to rating mapping
|
||||
RATINGS = {
|
||||
|
||||
@@ -16,15 +16,19 @@ 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 .platform import is_macos
|
||||
from .queryoptions import QueryOptions
|
||||
from .scoreinfo import ScoreInfo
|
||||
from .searchinfo import SearchInfo
|
||||
|
||||
if is_macos:
|
||||
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
|
||||
|
||||
# configure logging; every module in osxphotos should use this logger
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
@@ -53,6 +57,7 @@ __all__ = [
|
||||
"PersonInfo",
|
||||
"PhotoExporter",
|
||||
"PhotoInfo",
|
||||
"PhotoTables",
|
||||
"PhotoTemplate",
|
||||
"PhotosAlbum",
|
||||
"PhotosAlbumPhotoScript",
|
||||
|
||||
@@ -18,9 +18,6 @@ OSXPHOTOS_URL = "https://github.com/RhetTbull/osxphotos"
|
||||
# Apple Epoch is Jan 1, 2001
|
||||
TIME_DELTA = (datetime(2001, 1, 1, 0, 0) - datetime(1970, 1, 1, 0, 0)).total_seconds()
|
||||
|
||||
# Unicode format to use for comparing strings
|
||||
UNICODE_FORMAT = "NFC"
|
||||
|
||||
# which Photos library database versions have been tested
|
||||
# Photos 2.0 (10.12.6) == 2622
|
||||
# Photos 3.0 (10.13.6) == 3301
|
||||
@@ -128,6 +125,8 @@ _TESTED_OS_VERSIONS = [
|
||||
("13", "0"),
|
||||
("13", "1"),
|
||||
("13", "2"),
|
||||
("13", "3"),
|
||||
("13", "4"),
|
||||
]
|
||||
|
||||
# Photos 5 has persons who are empty string if unidentified face
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.59.0"
|
||||
__version__ = "0.60.5"
|
||||
|
||||
@@ -50,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:
|
||||
@@ -166,14 +165,13 @@ class AlbumInfoBaseClass:
|
||||
return self._owner
|
||||
|
||||
def asdict(self):
|
||||
"""Return album info as a dict"""
|
||||
"""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,
|
||||
"photos": [p.uuid for p in self.photos],
|
||||
}
|
||||
|
||||
def __len__(self):
|
||||
@@ -299,7 +297,7 @@ class AlbumInfo(AlbumInfoBaseClass):
|
||||
)
|
||||
|
||||
def asdict(self):
|
||||
"""Return album info as a dict"""
|
||||
"""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
|
||||
@@ -362,14 +360,13 @@ class ImportInfo(AlbumInfoBaseClass):
|
||||
return self._photos
|
||||
|
||||
def asdict(self):
|
||||
"""Return import info as a dict"""
|
||||
"""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,
|
||||
"photos": [p.uuid for p in self.photos],
|
||||
}
|
||||
|
||||
def __bool__(self):
|
||||
@@ -386,7 +383,25 @@ class ProjectInfo(AlbumInfo):
|
||||
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:
|
||||
|
||||
@@ -13,6 +13,7 @@ from osxphotos.debug import (
|
||||
set_debug,
|
||||
wrap_function,
|
||||
)
|
||||
from osxphotos.platform import is_macos
|
||||
|
||||
# apply any debug functions
|
||||
# need to do this before importing anything else so that the debug functions
|
||||
@@ -44,9 +45,7 @@ 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 .batch_edit import batch_edit
|
||||
from .cli import cli_main
|
||||
from .cli_commands import (
|
||||
abort,
|
||||
@@ -67,7 +66,6 @@ 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
|
||||
@@ -76,19 +74,24 @@ 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 .show_command import show
|
||||
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
|
||||
|
||||
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__ = [
|
||||
|
||||
@@ -5,9 +5,9 @@ from __future__ import annotations
|
||||
import datetime
|
||||
|
||||
import click
|
||||
import photoscript
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.platform import assert_macos
|
||||
from osxphotos.queryoptions import IncompatibleQueryOptions, query_options_from_kwargs
|
||||
from osxphotos.utils import pluralize
|
||||
|
||||
@@ -18,6 +18,10 @@ 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
|
||||
|
||||
@@ -9,12 +9,16 @@ import json
|
||||
import sys
|
||||
|
||||
import click
|
||||
import photoscript
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.phototemplate import RenderOptions
|
||||
from osxphotos.platform import assert_macos
|
||||
from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||
|
||||
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
|
||||
|
||||
@@ -9,11 +9,10 @@ import click
|
||||
|
||||
from osxphotos._constants import PROFILE_SORT_KEYS
|
||||
from osxphotos._version import __version__
|
||||
from osxphotos.platform import is_macos
|
||||
|
||||
from .about import about
|
||||
from .add_locations import add_locations
|
||||
from .albums import albums
|
||||
from .batch_edit import batch_edit
|
||||
from .cli_params import DB_OPTION, DEBUG_OPTIONS, JSON_OPTION, VERSION_OPTION
|
||||
from .common import OSXPHOTOS_HIDDEN
|
||||
from .debug_dump import debug_dump
|
||||
@@ -24,7 +23,6 @@ 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
|
||||
@@ -32,19 +30,24 @@ 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 .show_command import show
|
||||
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
|
||||
|
||||
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
|
||||
class CLI_Obj:
|
||||
@@ -106,11 +109,9 @@ def cli_main(ctx, db, json_, profile, profile_sort, **kwargs):
|
||||
|
||||
|
||||
# install CLI commands
|
||||
for command in [
|
||||
commands = [
|
||||
about,
|
||||
add_locations,
|
||||
albums,
|
||||
batch_edit,
|
||||
debug_dump,
|
||||
diff,
|
||||
docs_command,
|
||||
@@ -120,7 +121,6 @@ for command in [
|
||||
exportdb,
|
||||
grep,
|
||||
help,
|
||||
import_cli,
|
||||
info,
|
||||
install,
|
||||
keywords,
|
||||
@@ -128,19 +128,28 @@ for command in [
|
||||
list_libraries,
|
||||
orphans,
|
||||
persons,
|
||||
photo_inspect,
|
||||
places,
|
||||
query,
|
||||
repl,
|
||||
run,
|
||||
show,
|
||||
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)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
"""Common options & parameters for osxphotos CLI commands"""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
from textwrap import dedent
|
||||
from typing import Any, Callable
|
||||
|
||||
import click
|
||||
|
||||
from ..platform import is_macos
|
||||
from .common import OSXPHOTOS_HIDDEN, print_version
|
||||
from .param_types import *
|
||||
|
||||
@@ -25,6 +29,49 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
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
|
||||
@@ -544,6 +591,26 @@ _QUERY_PARAMETERS_DICT = {
|
||||
is_flag=True,
|
||||
help="Search for photos that are not in iCloud (have not been synched)",
|
||||
),
|
||||
"--syndicated": click.Option(
|
||||
["--syndicated"],
|
||||
is_flag=True,
|
||||
help="Search for photos that have been shared via syndication ('Shared with You' album via Messages, etc.)",
|
||||
),
|
||||
"--not-syndicated": click.Option(
|
||||
["--not-syndicated"],
|
||||
is_flag=True,
|
||||
help="Search for photos that have not been shared via syndication ('Shared with You' album via Messages, etc.)",
|
||||
),
|
||||
"--saved-to-library": click.Option(
|
||||
["--saved-to-library"],
|
||||
is_flag=True,
|
||||
help="Search for syndicated photos that have saved to the library",
|
||||
),
|
||||
"--not-saved-to-library": click.Option(
|
||||
["--not-saved-to-library"],
|
||||
is_flag=True,
|
||||
help="Search for syndicated photos that have not saved to the library",
|
||||
),
|
||||
"--regex": click.Option(
|
||||
["--regex"],
|
||||
metavar="REGEX TEMPLATE",
|
||||
@@ -557,6 +624,7 @@ _QUERY_PARAMETERS_DICT = {
|
||||
["--selected"],
|
||||
is_flag=True,
|
||||
help="Filter for photos that are currently selected in Photos.",
|
||||
callback=validate_selected,
|
||||
),
|
||||
"--exif": click.Option(
|
||||
["--exif"],
|
||||
@@ -597,6 +665,9 @@ _QUERY_PARAMETERS_DICT = {
|
||||
),
|
||||
}
|
||||
|
||||
if not is_macos:
|
||||
del _QUERY_PARAMETERS_DICT["--selected"]
|
||||
|
||||
|
||||
def QUERY_OPTIONS(
|
||||
wrapped=None, *, exclude: list[str] | None = None
|
||||
|
||||
@@ -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 or get_rich_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
|
||||
|
||||
@@ -15,7 +15,8 @@ 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, get_macos_version
|
||||
from osxphotos.platform import get_macos_version
|
||||
from osxphotos.utils import get_latest_version
|
||||
|
||||
# used to show/hide hidden commands
|
||||
OSXPHOTOS_HIDDEN = not bool(os.getenv("OSXPHOTOS_SHOW_HIDDEN", default=False))
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
"""Detect dark mode on MacOS >= 10.14"""
|
||||
"""Detect dark mode on MacOS >= 10.14 or fake it elsewhere"""
|
||||
|
||||
import objc
|
||||
import Foundation
|
||||
from osxphotos.platform import is_macos
|
||||
|
||||
if is_macos:
|
||||
import Foundation
|
||||
import objc
|
||||
|
||||
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 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"
|
||||
|
||||
else:
|
||||
|
||||
def is_light_mode():
|
||||
return theme() == "light"
|
||||
def theme():
|
||||
return "light"
|
||||
|
||||
def is_dark_mode():
|
||||
return theme() == "dark"
|
||||
|
||||
def is_light_mode():
|
||||
return theme() == "light"
|
||||
|
||||
@@ -57,8 +57,8 @@ def dump(
|
||||
print_template,
|
||||
):
|
||||
"""Print list of all photos & associated info from the Photos library.
|
||||
|
||||
NOTE: dump is DEPRECATED and will be removed in a future release.
|
||||
|
||||
NOTE: dump is DEPRECATED and will be removed in a future release.
|
||||
Use `osxphotos query` instead.
|
||||
"""
|
||||
|
||||
|
||||
@@ -12,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 (
|
||||
@@ -47,25 +40,31 @@ 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.platform import get_macos_version, is_macos
|
||||
from osxphotos.queryoptions import load_uuid_from_file, query_options_from_kwargs
|
||||
from osxphotos.unicode import normalize_fs_path
|
||||
from osxphotos.uti import get_preferred_uti_extension
|
||||
from osxphotos.utils import (
|
||||
format_sec_to_hhmmss,
|
||||
get_macos_version,
|
||||
normalize_fs_path,
|
||||
pluralize,
|
||||
under_test,
|
||||
)
|
||||
from osxphotos.utils import format_sec_to_hhmmss, pluralize, under_test
|
||||
|
||||
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 (
|
||||
@@ -851,7 +850,6 @@ def export(
|
||||
retry,
|
||||
save_config,
|
||||
screenshot,
|
||||
selected,
|
||||
selfie,
|
||||
shared,
|
||||
sidecar,
|
||||
@@ -883,6 +881,11 @@ def export(
|
||||
verbose_flag,
|
||||
xattr_template,
|
||||
year,
|
||||
syndicated,
|
||||
not_syndicated,
|
||||
saved_to_library,
|
||||
not_saved_to_library,
|
||||
selected=False, # Isn't provided on unsupported platforms
|
||||
# debug, # debug, watch, breakpoint handled in cli/__init__.py
|
||||
# watch,
|
||||
# breakpoint,
|
||||
@@ -911,7 +914,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
|
||||
@@ -1102,6 +1106,11 @@ def export(
|
||||
verbose_flag = cfg.verbose
|
||||
xattr_template = cfg.xattr_template
|
||||
year = cfg.year
|
||||
syndicated = cfg.syndicated
|
||||
not_syndicated = cfg.not_syndicated
|
||||
saved_to_library = cfg.saved_to_library
|
||||
not_saved_to_library = cfg.not_saved_to_library
|
||||
|
||||
# config file might have changed verbose
|
||||
verbose = verbose_print(verbose=verbose_flag, timestamp=timestamp, theme=theme)
|
||||
verbose(f"Loaded options from file [filepath]{load_config}")
|
||||
@@ -1110,7 +1119,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
|
||||
@@ -1147,6 +1159,8 @@ def export(
|
||||
("slow_mo", "not_slow_mo"),
|
||||
("time_lapse", "not_time_lapse"),
|
||||
("title", "no_title"),
|
||||
("syndicated", "not_syndicated"),
|
||||
("saved_to_library", "not_saved_to_library"),
|
||||
]
|
||||
dependent_options = [
|
||||
("append", ("report")),
|
||||
@@ -1324,7 +1338,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:
|
||||
@@ -1427,21 +1441,22 @@ def export(
|
||||
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
|
||||
# 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["photo"] = p
|
||||
kwargs["export_dir"] = dest
|
||||
kwargs["export_preview"] = preview
|
||||
kwargs["photo_num"] = photo_num
|
||||
export_results = export_photo(**kwargs)
|
||||
if post_function:
|
||||
for function in post_function:
|
||||
@@ -1711,7 +1726,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",
|
||||
@@ -2253,7 +2268,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(
|
||||
|
||||
@@ -19,14 +19,14 @@ from osxphotos.export_db_utils import (
|
||||
export_db_backup,
|
||||
export_db_check_signatures,
|
||||
export_db_get_errors,
|
||||
export_db_get_last_library,
|
||||
export_db_get_last_run,
|
||||
export_db_get_version,
|
||||
export_db_migrate_photos_library,
|
||||
export_db_save_config_to_file,
|
||||
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
|
||||
|
||||
|
||||
@@ -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.platform 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. "
|
||||
|
||||
@@ -20,7 +20,6 @@ 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
|
||||
@@ -42,9 +41,15 @@ from osxphotos.exiftool import ExifToolCaching, get_exiftool_path
|
||||
from osxphotos.photoinfo import PhotoInfoNone
|
||||
from osxphotos.photosalbum import PhotosAlbumPhotoScript
|
||||
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
||||
from osxphotos.platform import assert_macos
|
||||
from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||
from osxphotos.unicode import normalize_unicode
|
||||
from osxphotos.utils import 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 .rich_progress import rich_progress
|
||||
@@ -354,11 +359,12 @@ def set_photo_metadata(
|
||||
merge_keywords: bool,
|
||||
) -> MetaData:
|
||||
"""Set metadata (title, description, keywords) for a Photo object"""
|
||||
photo.title = metadata.title
|
||||
photo.description = metadata.description
|
||||
photo.title = normalize_unicode(metadata.title)
|
||||
photo.description = normalize_unicode(metadata.description)
|
||||
keywords = metadata.keywords.copy()
|
||||
keywords =normalize_unicode(keywords)
|
||||
if merge_keywords:
|
||||
if old_keywords := photo.keywords:
|
||||
if old_keywords := normalize_unicode(photo.keywords):
|
||||
keywords.extend(old_keywords)
|
||||
keywords = list(set(keywords))
|
||||
photo.keywords = keywords
|
||||
@@ -417,7 +423,7 @@ def set_photo_title(
|
||||
verbose(
|
||||
f"Setting title of photo [filename]{filepath.name}[/] to '{title_text[0]}'"
|
||||
)
|
||||
photo.title = title_text[0]
|
||||
photo.title = normalize_unicode(title_text[0])
|
||||
return title_text[0]
|
||||
else:
|
||||
return ""
|
||||
@@ -445,7 +451,7 @@ def set_photo_description(
|
||||
verbose(
|
||||
f"Setting description of photo [filename]{filepath.name}[/] to '{description_text[0]}'"
|
||||
)
|
||||
photo.description = description_text[0]
|
||||
photo.description = normalize_unicode(description_text[0])
|
||||
return description_text[0]
|
||||
else:
|
||||
return ""
|
||||
@@ -466,8 +472,9 @@ def set_photo_keywords(
|
||||
kw = render_photo_template(filepath, relative_filepath, keyword, exiftool_path)
|
||||
keywords.extend(kw)
|
||||
if keywords:
|
||||
keywords = normalize_unicode(keywords)
|
||||
if merge:
|
||||
if old_keywords := photo.keywords:
|
||||
if old_keywords := normalize_unicode(photo.keywords):
|
||||
keywords.extend(old_keywords)
|
||||
keywords = list(set(keywords))
|
||||
verbose(f"Setting keywords of photo [filename]{filepath.name}[/] to {keywords}")
|
||||
|
||||
@@ -68,7 +68,6 @@ class PathOrStdin(click.Path):
|
||||
|
||||
|
||||
class DateTimeISO8601(click.ParamType):
|
||||
|
||||
name = "DATETIME"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
@@ -82,7 +81,6 @@ class DateTimeISO8601(click.ParamType):
|
||||
|
||||
|
||||
class BitMathSize(click.ParamType):
|
||||
|
||||
name = "BITMATH"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
@@ -102,7 +100,6 @@ class BitMathSize(click.ParamType):
|
||||
|
||||
|
||||
class TimeISO8601(click.ParamType):
|
||||
|
||||
name = "TIME"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
@@ -141,7 +138,6 @@ class FunctionCall(click.ParamType):
|
||||
|
||||
|
||||
class ExportDBType(click.ParamType):
|
||||
|
||||
name = "EXPORTDB"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
@@ -279,7 +275,6 @@ class StrpDateTimePattern(click.ParamType):
|
||||
|
||||
|
||||
class Latitude(click.ParamType):
|
||||
|
||||
name = "Latitude"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
@@ -295,7 +290,6 @@ class Latitude(click.ParamType):
|
||||
|
||||
|
||||
class Longitude(click.ParamType):
|
||||
|
||||
name = "Longitude"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
|
||||
@@ -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
|
||||
@@ -22,10 +20,17 @@ from rich.panel import Panel
|
||||
|
||||
from osxphotos import PhotoInfo, PhotosDB
|
||||
from osxphotos._constants import _UNKNOWN_PERSON, search_category_factory
|
||||
from osxphotos.platform import assert_macos
|
||||
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
|
||||
|
||||
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 get_photos_db
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""query command for osxphotos CLI"""
|
||||
|
||||
import sys
|
||||
|
||||
import click
|
||||
|
||||
import osxphotos
|
||||
@@ -9,10 +11,13 @@ 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.platform import assert_macos, is_macos
|
||||
from osxphotos.queryoptions import query_options_from_kwargs
|
||||
|
||||
if is_macos:
|
||||
from osxphotos.photosalbum import PhotosAlbum
|
||||
|
||||
from .cli_params import (
|
||||
DB_ARGUMENT,
|
||||
DB_OPTION,
|
||||
@@ -20,6 +25,7 @@ from .cli_params import (
|
||||
FIELD_OPTION,
|
||||
JSON_OPTION,
|
||||
QUERY_OPTIONS,
|
||||
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
|
||||
@@ -27,20 +33,29 @@ 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
|
||||
@click.option("--count", is_flag=True, help="Print count of photos matching query and exit.")
|
||||
@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,
|
||||
@@ -68,10 +83,11 @@ def query(
|
||||
db,
|
||||
field,
|
||||
json_,
|
||||
count,
|
||||
print_template,
|
||||
quiet,
|
||||
add_to_album,
|
||||
photos_library,
|
||||
add_to_album=False,
|
||||
**kwargs,
|
||||
):
|
||||
"""Query the Photos database using 1 or more search options;
|
||||
@@ -123,7 +139,13 @@ def query(
|
||||
# below needed for to make CliRunner work for testing
|
||||
cli_json = cli_obj.json if cli_obj is not None else None
|
||||
|
||||
if count:
|
||||
click.echo(len(photos))
|
||||
return
|
||||
|
||||
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"
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
"""repl command for osxphotos CLI"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from functools import partial
|
||||
from typing import List
|
||||
|
||||
import click
|
||||
import photoscript
|
||||
from rich import pretty, print
|
||||
|
||||
import osxphotos
|
||||
@@ -16,6 +20,7 @@ from osxphotos._constants import _PHOTOS_4_VERSION
|
||||
from osxphotos.cli.click_rich_echo import rich_echo_error as echo_error
|
||||
from osxphotos.photoinfo import PhotoInfo
|
||||
from osxphotos.photosdb import PhotosDB
|
||||
from osxphotos.platform import assert_macos, is_macos
|
||||
from osxphotos.pyrepl import embed_repl
|
||||
from osxphotos.queryoptions import (
|
||||
IncompatibleQueryOptions,
|
||||
@@ -23,6 +28,10 @@ from osxphotos.queryoptions import (
|
||||
query_options_from_kwargs,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
@@ -52,7 +61,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
|
||||
@@ -92,6 +103,7 @@ def repl(ctx, cli_obj, db, emacs, beta, **kwargs):
|
||||
get_photo = photosdb.get_photo
|
||||
show = _show_photo
|
||||
spotlight = _spotlight_photo
|
||||
find = partial(_find_in_library, photosdb)
|
||||
get_selected = _get_selected(photosdb)
|
||||
try:
|
||||
selected = get_selected()
|
||||
@@ -121,28 +133,31 @@ def repl(ctx, cli_obj, db, emacs, beta, **kwargs):
|
||||
)
|
||||
print(f"\nThe following functions may be helpful:")
|
||||
print(
|
||||
f"- get_photo(uuid): return a PhotoInfo object for photo with uuid; e.g. get_photo('B13F4485-94E0-41CD-AF71-913095D62E31')"
|
||||
"- get_photo(uuid): return a PhotoInfo object for photo with uuid; e.g. get_photo('B13F4485-94E0-41CD-AF71-913095D62E31')"
|
||||
)
|
||||
print(
|
||||
f"- get_selected(); return list of PhotoInfo objects for photos selected in Photos"
|
||||
"- get_selected(); return list of PhotoInfo objects for photos selected in Photos"
|
||||
)
|
||||
print(
|
||||
f"- show(photo): open a photo object in the default viewer; e.g. show(selected[0])"
|
||||
"- show(photo): open a photo object in the default viewer; e.g. show(selected[0])"
|
||||
)
|
||||
print(
|
||||
f"- show(path): open a file at path in the default viewer; e.g. show('/path/to/photo.jpg')"
|
||||
"- show(path): open a file at path in the default viewer; e.g. show('/path/to/photo.jpg')"
|
||||
)
|
||||
print(f"- spotlight(photo): open a photo and spotlight it in Photos")
|
||||
print("- spotlight(photo): open a photo and spotlight it in Photos")
|
||||
# print(
|
||||
# f"- help(object): print help text including list of methods for object; for example, help(PhotosDB)"
|
||||
# )
|
||||
print(
|
||||
f"- inspect(object): print information about an object; e.g. inspect(PhotoInfo)"
|
||||
"- inspect(object): print information about an object; e.g. inspect(PhotoInfo)"
|
||||
)
|
||||
print(
|
||||
f"- explore(object): interactively explore an object with objexplore; e.g. explore(PhotoInfo)"
|
||||
"- explore(object): interactively explore an object with objexplore; e.g. explore(PhotoInfo)"
|
||||
)
|
||||
print(f"- q, quit, quit(), exit, exit(): exit this interactive shell\n")
|
||||
print(
|
||||
"- find(text): search for files matching text in Photos library; e.g. find('B13F4485')"
|
||||
)
|
||||
print("- q, quit, quit(), exit, exit(): exit this interactive shell\n")
|
||||
|
||||
embed_repl(
|
||||
globals=globals(),
|
||||
@@ -191,15 +206,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: Can’t 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".*Can’t 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()
|
||||
|
||||
@@ -217,3 +240,20 @@ def _query_photos(photosdb: PhotosDB, query_options: QueryOptions) -> List:
|
||||
) from e
|
||||
|
||||
return photos
|
||||
|
||||
|
||||
def _find_in_library(photosdb: PhotosDB, search_str: str) -> list[str]:
|
||||
"""Find files in Photos library matching search_str using find command"""
|
||||
# this is a quick and dirty way to find files in the Photos library
|
||||
# e.g. those matching a UUID or a filename
|
||||
library_path = photosdb.library_path
|
||||
if not library_path:
|
||||
raise ValueError("Could not find Photos library")
|
||||
|
||||
search_str = shlex.quote(search_str)
|
||||
library_path = shlex.quote(library_path)
|
||||
cmd = f"find {library_path} | grep {search_str}"
|
||||
output = subprocess.check_output(cmd, shell=True, universal_newlines=True)
|
||||
|
||||
# Split the output into lines and return as a list
|
||||
return output.strip().split("\n")
|
||||
|
||||
@@ -182,7 +182,9 @@ class ExportReportWriterSQLite(ReportWriterABC):
|
||||
with suppress(FileNotFoundError):
|
||||
os.unlink(self.output_file)
|
||||
|
||||
self._conn = sqlite3.connect(self.output_file, check_same_thread=SQLITE_CHECK_SAME_THREAD)
|
||||
self._conn = sqlite3.connect(
|
||||
self.output_file, check_same_thread=SQLITE_CHECK_SAME_THREAD
|
||||
)
|
||||
self._create_tables()
|
||||
self.report_id = self._generate_report_id()
|
||||
|
||||
@@ -534,7 +536,9 @@ class SyncReportWriterSQLite(ReportWriterABC):
|
||||
with suppress(FileNotFoundError):
|
||||
os.unlink(self.output_file)
|
||||
|
||||
self._conn = sqlite3.connect(self.output_file, check_same_thread=SQLITE_CHECK_SAME_THREAD)
|
||||
self._conn = sqlite3.connect(
|
||||
self.output_file, check_same_thread=SQLITE_CHECK_SAME_THREAD
|
||||
)
|
||||
self._create_tables()
|
||||
self.report_id = self._generate_report_id()
|
||||
|
||||
|
||||
@@ -7,12 +7,16 @@ 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.platform import assert_macos
|
||||
from osxphotos.utils import get_last_library_path
|
||||
|
||||
assert_macos()
|
||||
|
||||
from osxphotos.photoscript_utils import (
|
||||
photoscript_object_from_name,
|
||||
photoscript_object_from_uuid,
|
||||
)
|
||||
from osxphotos.photosdb.photosdb_utils import get_photos_library_version
|
||||
from osxphotos.utils import get_last_library_path
|
||||
|
||||
from .cli_commands import echo, echo_error
|
||||
from .cli_params import DB_OPTION
|
||||
|
||||
@@ -9,13 +9,13 @@ import pathlib
|
||||
from typing import Any, Callable, Literal
|
||||
|
||||
import click
|
||||
import photoscript
|
||||
|
||||
from osxphotos import PhotoInfo, PhotosDB, __version__
|
||||
from osxphotos.photoinfo import PhotoInfoNone
|
||||
from osxphotos.photosalbum import PhotosAlbum
|
||||
from osxphotos.photosdb.photosdb_utils import get_db_version
|
||||
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
||||
from osxphotos.platform import assert_macos
|
||||
from osxphotos.queryoptions import (
|
||||
IncompatibleQueryOptions,
|
||||
QueryOptions,
|
||||
@@ -24,6 +24,10 @@ from osxphotos.queryoptions import (
|
||||
from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||
from osxphotos.utils import pluralize
|
||||
|
||||
assert_macos()
|
||||
|
||||
import photoscript
|
||||
|
||||
from .cli_params import (
|
||||
DB_OPTION,
|
||||
QUERY_OPTIONS,
|
||||
@@ -167,6 +171,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):
|
||||
|
||||
@@ -7,7 +7,6 @@ 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
|
||||
@@ -25,10 +24,16 @@ from osxphotos.photodates import (
|
||||
update_photo_from_function,
|
||||
update_photo_time_for_new_timezone,
|
||||
)
|
||||
from osxphotos.photosalbum import PhotosAlbumPhotoScript
|
||||
from osxphotos.phototz import PhotoTimeZone, PhotoTimeZoneUpdater
|
||||
from osxphotos.platform import assert_macos
|
||||
from osxphotos.utils import 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
|
||||
@@ -164,7 +169,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,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
"""uuid command for osxphotos CLI"""
|
||||
|
||||
import click
|
||||
|
||||
from osxphotos.platform import assert_macos
|
||||
|
||||
assert_macos()
|
||||
|
||||
import photoscript
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
"""about command for osxphotos CLI"""
|
||||
|
||||
import click
|
||||
import os
|
||||
|
||||
import click
|
||||
import packaging
|
||||
|
||||
from osxphotos._constants import OSXPHOTOS_URL
|
||||
from osxphotos._version import __version__
|
||||
|
||||
from .common import get_latest_version
|
||||
import packaging
|
||||
|
||||
|
||||
@click.command(name="version")
|
||||
|
||||
@@ -5,12 +5,17 @@ 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 .platform import assert_macos
|
||||
from .utils import 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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
@@ -27,7 +27,7 @@ import osxphotos
|
||||
from ._constants import OSXPHOTOS_EXPORT_DB, SQLITE_CHECK_SAME_THREAD
|
||||
from ._version import __version__
|
||||
from .fileutil import FileUtil
|
||||
from .utils import normalize_fs_path
|
||||
from .unicode import normalize_fs_path
|
||||
|
||||
__all__ = [
|
||||
"ExportDB",
|
||||
@@ -921,32 +921,35 @@ class ExportDBInMemory(ExportDB):
|
||||
returns: connection to the database
|
||||
"""
|
||||
if not os.path.isfile(dbfile):
|
||||
conn = self._get_db_connection()
|
||||
if not conn:
|
||||
# database doesn't exist so create it in-memory
|
||||
src = self._get_db_connection()
|
||||
if not src:
|
||||
raise Exception("Error getting connection to in-memory database")
|
||||
self._create_or_migrate_db_tables(conn)
|
||||
self._create_or_migrate_db_tables(src)
|
||||
self.was_created = True
|
||||
self.was_upgraded = ()
|
||||
else:
|
||||
conn = sqlite3.connect(dbfile, check_same_thread=SQLITE_CHECK_SAME_THREAD)
|
||||
dbdump = self._dump_db(conn)
|
||||
conn.close()
|
||||
self.version = OSXPHOTOS_EXPORTDB_VERSION
|
||||
return src
|
||||
|
||||
# Create a database in memory and import from the dump
|
||||
conn = sqlite3.connect(
|
||||
":memory:", check_same_thread=SQLITE_CHECK_SAME_THREAD
|
||||
)
|
||||
conn.cursor().executescript(dbdump.read())
|
||||
self.was_created = False
|
||||
version_info = self._get_database_version(conn)
|
||||
if version_info[1] < OSXPHOTOS_EXPORTDB_VERSION:
|
||||
self._create_or_migrate_db_tables(conn)
|
||||
self.was_upgraded = (version_info[1], OSXPHOTOS_EXPORTDB_VERSION)
|
||||
else:
|
||||
self.was_upgraded = ()
|
||||
# database exists so copy it to memory
|
||||
src = sqlite3.connect(dbfile, check_same_thread=SQLITE_CHECK_SAME_THREAD)
|
||||
|
||||
# Create a database in memory by backing up the on-disk database
|
||||
dst = sqlite3.connect(":memory:", check_same_thread=SQLITE_CHECK_SAME_THREAD)
|
||||
with dst:
|
||||
src.backup(dst, pages=1)
|
||||
src.close()
|
||||
|
||||
self.was_created = False
|
||||
version_info = self._get_database_version(dst)
|
||||
if version_info[1] < OSXPHOTOS_EXPORTDB_VERSION:
|
||||
self._create_or_migrate_db_tables(dst)
|
||||
self.was_upgraded = (version_info[1], OSXPHOTOS_EXPORTDB_VERSION)
|
||||
else:
|
||||
self.was_upgraded = ()
|
||||
self.version = OSXPHOTOS_EXPORTDB_VERSION
|
||||
|
||||
return conn
|
||||
return dst
|
||||
|
||||
def _get_db_connection(self):
|
||||
"""return db connection to in memory database"""
|
||||
|
||||
@@ -9,9 +9,12 @@ import typing as t
|
||||
from abc import ABC, abstractmethod
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
import Foundation
|
||||
|
||||
from .imageconverter import ImageConverter
|
||||
from .platform import is_macos
|
||||
from .unicode import normalize_fs_path
|
||||
|
||||
if is_macos:
|
||||
import Foundation
|
||||
|
||||
__all__ = ["FileUtilABC", "FileUtilMacOS", "FileUtilShUtil", "FileUtil", "FileUtilNoOp"]
|
||||
|
||||
@@ -90,6 +93,9 @@ class FileUtilMacOS(FileUtilABC):
|
||||
if src is None or dest is None:
|
||||
raise ValueError("src and dest must not be None", src, dest)
|
||||
|
||||
src = normalize_fs_path(src)
|
||||
dest = normalize_fs_path(dest)
|
||||
|
||||
if not os.path.isfile(src):
|
||||
raise FileNotFoundError("src file does not appear to exist", src)
|
||||
|
||||
@@ -115,6 +121,9 @@ class FileUtilMacOS(FileUtilABC):
|
||||
OSError if copy fails
|
||||
TypeError if either path is None
|
||||
"""
|
||||
src = normalize_fs_path(src)
|
||||
dest = normalize_fs_path(dest)
|
||||
|
||||
if not isinstance(src, pathlib.Path):
|
||||
src = pathlib.Path(src)
|
||||
|
||||
@@ -135,6 +144,7 @@ class FileUtilMacOS(FileUtilABC):
|
||||
@classmethod
|
||||
def unlink(cls, filepath):
|
||||
"""unlink filepath; if it's pathlib.Path, use Path.unlink, otherwise use os.unlink"""
|
||||
filepath = normalize_fs_path(filepath)
|
||||
if isinstance(filepath, pathlib.Path):
|
||||
filepath.unlink()
|
||||
else:
|
||||
@@ -143,6 +153,7 @@ class FileUtilMacOS(FileUtilABC):
|
||||
@classmethod
|
||||
def rmdir(cls, dirpath):
|
||||
"""remove directory filepath; dirpath must be empty"""
|
||||
dirpath = normalize_fs_path(dirpath)
|
||||
if isinstance(dirpath, pathlib.Path):
|
||||
dirpath.rmdir()
|
||||
else:
|
||||
@@ -151,6 +162,7 @@ class FileUtilMacOS(FileUtilABC):
|
||||
@classmethod
|
||||
def utime(cls, path, times):
|
||||
"""Set the access and modified time of path."""
|
||||
path = normalize_fs_path(path)
|
||||
os.utime(path, times=times)
|
||||
|
||||
@classmethod
|
||||
@@ -166,6 +178,9 @@ class FileUtilMacOS(FileUtilABC):
|
||||
Does not do a byte-by-byte comparison.
|
||||
"""
|
||||
|
||||
f1 = normalize_fs_path(f1)
|
||||
f2 = normalize_fs_path(f2)
|
||||
|
||||
s1 = cls._sig(os.stat(f1))
|
||||
if mtime1 is not None:
|
||||
s1 = (s1[0], s1[1], int(mtime1))
|
||||
@@ -188,6 +203,7 @@ class FileUtilMacOS(FileUtilABC):
|
||||
if not s2:
|
||||
return False
|
||||
|
||||
f1 = normalize_fs_path(f1)
|
||||
s1 = cls._sig(os.stat(f1))
|
||||
if s1[0] != stat.S_IFREG or s2[0] != stat.S_IFREG:
|
||||
return False
|
||||
@@ -196,6 +212,7 @@ class FileUtilMacOS(FileUtilABC):
|
||||
@classmethod
|
||||
def file_sig(cls, f1):
|
||||
"""return os.stat signature for file f1 as tuple of (mode, size, mtime)"""
|
||||
f1 = normalize_fs_path(f1)
|
||||
return cls._sig(os.stat(f1))
|
||||
|
||||
@classmethod
|
||||
@@ -210,6 +227,8 @@ class FileUtilMacOS(FileUtilABC):
|
||||
Returns:
|
||||
True if success, otherwise False
|
||||
"""
|
||||
src_file = normalize_fs_path(src_file)
|
||||
dest_file = normalize_fs_path(dest_file)
|
||||
converter = ImageConverter()
|
||||
return converter.write_jpeg(
|
||||
src_file, dest_file, compression_quality=compression_quality
|
||||
@@ -227,6 +246,8 @@ class FileUtilMacOS(FileUtilABC):
|
||||
Name of renamed file (dest)
|
||||
|
||||
"""
|
||||
src = normalize_fs_path(src)
|
||||
dest = normalize_fs_path(dest)
|
||||
os.rename(str(src), str(dest))
|
||||
return dest
|
||||
|
||||
@@ -271,6 +292,9 @@ class FileUtilShUtil(FileUtilMacOS):
|
||||
OSError if copy fails
|
||||
TypeError if either path is None
|
||||
"""
|
||||
src = normalize_fs_path(src)
|
||||
dest = normalize_fs_path(dest)
|
||||
|
||||
if not isinstance(src, pathlib.Path):
|
||||
src = pathlib.Path(src)
|
||||
|
||||
@@ -288,7 +312,7 @@ class FileUtilShUtil(FileUtilMacOS):
|
||||
return True
|
||||
|
||||
|
||||
class FileUtil(FileUtilMacOS):
|
||||
class FileUtil(FileUtilShUtil):
|
||||
"""Various file utilities"""
|
||||
|
||||
pass
|
||||
|
||||
@@ -5,16 +5,20 @@
|
||||
# reference: https://stackoverflow.com/questions/59330149/coreimage-ciimage-write-jpg-is-shifting-colors-macos/59334308#59334308
|
||||
|
||||
import pathlib
|
||||
|
||||
import objc
|
||||
import Metal
|
||||
import Quartz
|
||||
from Cocoa import NSURL
|
||||
from Foundation import NSDictionary
|
||||
import sys
|
||||
|
||||
# needed to capture system-level stderr
|
||||
from wurlitzer import pipes
|
||||
|
||||
from .platform import is_macos
|
||||
|
||||
if is_macos:
|
||||
import Metal
|
||||
import objc
|
||||
import Quartz
|
||||
from Cocoa import NSURL
|
||||
from Foundation import NSDictionary
|
||||
|
||||
__all__ = ["ImageConversionError", "ImageConverter"]
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
""" utility functions for validating/sanitizing path components """
|
||||
""" utility functions for validating/sanitizing path components
|
||||
|
||||
This module also performs Unicode normalization. For a quick summary, there are
|
||||
multiple ways to write more complex characters in Unicode. This causes problems
|
||||
when e.g. checking if a file already exists and you have multiple sources for
|
||||
the same string with different encodings. This sadly happens in Photos, but
|
||||
isn't a problem on macOS, since macOS does normalization behind-the-scenes (see
|
||||
https://eclecticlight.co/2021/05/08/explainer-unicode-normalization-and-apfs/).
|
||||
This causes problems on other platforms, so we normalize as part of filename
|
||||
sanitization functions and rely on them being called every time a unique
|
||||
filename is needed.
|
||||
"""
|
||||
|
||||
import pathvalidate
|
||||
|
||||
from osxphotos.unicode import normalize_unicode
|
||||
|
||||
from ._constants import MAX_DIRNAME_LEN, MAX_FILENAME_LEN
|
||||
|
||||
__all__ = [
|
||||
@@ -24,7 +37,7 @@ def is_valid_filepath(filepath):
|
||||
|
||||
|
||||
def sanitize_filename(filename, replacement=":"):
|
||||
"""replace any illegal characters in a filename and truncate filename if needed
|
||||
"""replace any illegal characters in a filename, truncate filename if needed and normalize Unicode to NFC form
|
||||
|
||||
Args:
|
||||
filename: str, filename to sanitze
|
||||
@@ -35,6 +48,7 @@ def sanitize_filename(filename, replacement=":"):
|
||||
"""
|
||||
|
||||
if filename:
|
||||
filename = normalize_unicode(filename)
|
||||
filename = filename.replace("/", replacement)
|
||||
if len(filename) > MAX_FILENAME_LEN:
|
||||
parts = filename.split(".")
|
||||
@@ -54,7 +68,7 @@ def sanitize_filename(filename, replacement=":"):
|
||||
|
||||
|
||||
def sanitize_dirname(dirname, replacement=":"):
|
||||
"""replace any illegal characters in a directory name and truncate directory name if needed
|
||||
"""replace any illegal characters in a directory name, truncate directory name if needed, and normalize Unicode to NFC form
|
||||
|
||||
Args:
|
||||
dirname: str, directory name to sanitize
|
||||
@@ -69,7 +83,7 @@ def sanitize_dirname(dirname, replacement=":"):
|
||||
|
||||
|
||||
def sanitize_pathpart(pathpart, replacement=":"):
|
||||
"""replace any illegal characters in a path part (either directory or filename without extension) and truncate name if needed
|
||||
"""replace any illegal characters in a path part (either directory or filename without extension), truncate name if needed, and normalize Unicode to NFC form
|
||||
|
||||
Args:
|
||||
pathpart: str, path part to sanitize
|
||||
@@ -82,6 +96,7 @@ def sanitize_pathpart(pathpart, replacement=":"):
|
||||
pathpart = (
|
||||
pathpart.replace("/", replacement) if replacement is not None else pathpart
|
||||
)
|
||||
pathpart = normalize_unicode(pathpart)
|
||||
if len(pathpart) > MAX_DIRNAME_LEN:
|
||||
drop = len(pathpart) - MAX_DIRNAME_LEN
|
||||
pathpart = pathpart[:-drop]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
""" PhotoExport class to export photos
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import json
|
||||
import logging
|
||||
@@ -8,13 +10,11 @@ import os
|
||||
import pathlib
|
||||
import re
|
||||
import typing as t
|
||||
from collections import namedtuple # pylint: disable=syntax-error
|
||||
from dataclasses import asdict, dataclass
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from types import SimpleNamespace
|
||||
|
||||
import photoscript
|
||||
from mako.template import Template
|
||||
|
||||
from ._constants import (
|
||||
@@ -35,15 +35,10 @@ from .datetime_utils import datetime_tz_to_utc
|
||||
from .exiftool import ExifTool, ExifToolCaching, exiftool_can_write, get_exiftool_path
|
||||
from .export_db import ExportDB, ExportDBTemp
|
||||
from .fileutil import FileUtil
|
||||
from .photokit import (
|
||||
PHOTOS_VERSION_CURRENT,
|
||||
PHOTOS_VERSION_ORIGINAL,
|
||||
PHOTOS_VERSION_UNADJUSTED,
|
||||
PhotoKitFetchFailed,
|
||||
PhotoLibrary,
|
||||
)
|
||||
from .phototemplate import RenderOptions
|
||||
from .platform import is_macos
|
||||
from .rich_utils import add_rich_markup_tag
|
||||
from .unicode import normalize_fs_path
|
||||
from .uti import get_preferred_uti_extension
|
||||
from .utils import (
|
||||
hexdigest,
|
||||
@@ -55,6 +50,18 @@ from .utils import (
|
||||
unlock_filename,
|
||||
)
|
||||
|
||||
if is_macos:
|
||||
import photoscript
|
||||
|
||||
from .photokit import (
|
||||
PHOTOS_VERSION_CURRENT,
|
||||
PHOTOS_VERSION_ORIGINAL,
|
||||
PHOTOS_VERSION_UNADJUSTED,
|
||||
PhotoKitFetchFailed,
|
||||
PhotoLibrary,
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"ExportError",
|
||||
"ExportOptions",
|
||||
@@ -69,6 +76,10 @@ if t.TYPE_CHECKING:
|
||||
# retry if download_missing/use_photos_export fails the first time (which sometimes it does)
|
||||
MAX_PHOTOSCRIPT_RETRIES = 3
|
||||
|
||||
# Global to hold the compiled XMP template
|
||||
# This is expensive to compile so we only want to do it once
|
||||
_global_xmp_template: Template | None = None
|
||||
|
||||
|
||||
# return values for _should_update_photo
|
||||
class ShouldUpdate(Enum):
|
||||
@@ -237,6 +248,9 @@ class StagedFiles:
|
||||
def __str__(self):
|
||||
return str(self.asdict())
|
||||
|
||||
def __repr__(self):
|
||||
return f"StagedFiles({self.asdict()})"
|
||||
|
||||
def asdict(self):
|
||||
return {
|
||||
"original": self.original,
|
||||
@@ -472,6 +486,11 @@ class PhotoExporter:
|
||||
dest, options = self._should_convert_to_jpeg(dest, options)
|
||||
|
||||
# stage files for export by finding path in local library or downloading from iCloud as appropriate
|
||||
# for `--download-missing` and `--update` case, this may cause unnecessary downloads
|
||||
# as it will download the file even if it's not needed (won't be checked until the _should_update_photo() call from _export_photo()
|
||||
# fixing this will require major refactoring of the export code, see #1086
|
||||
# leaving it for now as this should not be a common use case
|
||||
# (if using `--update` it is much better to be using "Download originals to this Mac" in Photos)
|
||||
staged_files = self._stage_photos_for_export(options)
|
||||
src = staged_files.edited if options.edited else staged_files.original
|
||||
|
||||
@@ -658,8 +677,16 @@ class PhotoExporter:
|
||||
return lock_filename(filename) if lock else filename
|
||||
|
||||
# if overwrite==False and #increment==False, export should fail if file exists
|
||||
if dest.exists() and not any(
|
||||
[options.increment, options.update, options.force_update, options.overwrite]
|
||||
if (
|
||||
not any(
|
||||
[
|
||||
options.increment,
|
||||
options.update,
|
||||
options.force_update,
|
||||
options.overwrite,
|
||||
]
|
||||
)
|
||||
and dest.exists()
|
||||
):
|
||||
raise FileExistsError(
|
||||
f"destination exists ({dest}); overwrite={options.overwrite}, increment={options.increment}"
|
||||
@@ -718,9 +745,18 @@ class PhotoExporter:
|
||||
return dest
|
||||
|
||||
def _should_update_photo(
|
||||
self, src: pathlib.Path, dest: pathlib.Path, options: ExportOptions
|
||||
) -> t.Literal[True, False]:
|
||||
"""Return True if photo should be updated, else False"""
|
||||
self, src: pathlib.Path | None, dest: pathlib.Path, options: ExportOptions
|
||||
) -> bool | ShouldUpdate:
|
||||
"""Return True if photo should be updated, else False
|
||||
|
||||
Args:
|
||||
src (pathlib.Path | None): source path; if None, photo is missing and
|
||||
any checks that require src will return True
|
||||
dest (pathlib.Path): destination path
|
||||
|
||||
Returns:
|
||||
False if photo should not be updated otherwise a truthy ShouldUpdate value
|
||||
"""
|
||||
|
||||
# NOTE: The order of certain checks is important
|
||||
# read the comments below to understand why before changing
|
||||
@@ -734,11 +770,11 @@ class PhotoExporter:
|
||||
# photo doesn't exist in database, should update
|
||||
return ShouldUpdate.NOT_IN_DATABASE
|
||||
|
||||
if options.export_as_hardlink and not dest.samefile(src):
|
||||
if options.export_as_hardlink and (not src or not dest.samefile(src)):
|
||||
# different files, should update
|
||||
return ShouldUpdate.HARDLINK_DIFFERENT_FILES
|
||||
|
||||
if not options.export_as_hardlink and dest.samefile(src):
|
||||
if not options.export_as_hardlink and (not src or dest.samefile(src)):
|
||||
# same file but not exporting as hardlink, should update
|
||||
return ShouldUpdate.NOT_HARDLINK_SAME_FILES
|
||||
|
||||
@@ -770,7 +806,9 @@ class PhotoExporter:
|
||||
# as exiftool will be used to update edited file
|
||||
return ShouldUpdate.EXIFTOOL_DIFFERENT if rv else False
|
||||
|
||||
if options.edited and not fileutil.cmp_file_sig(src, file_record.src_sig):
|
||||
if options.edited and (
|
||||
not src or not fileutil.cmp_file_sig(src, file_record.src_sig)
|
||||
):
|
||||
# edited file in Photos doesn't match what was last exported
|
||||
return ShouldUpdate.EDITED_SIG_DIFFERENT
|
||||
|
||||
@@ -821,22 +859,46 @@ class PhotoExporter:
|
||||
|
||||
# download any missing files
|
||||
if options.download_missing:
|
||||
live_photo = staged.edited_live if options.edited else staged.original_live
|
||||
missing_options = ExportOptions(
|
||||
edited=options.edited,
|
||||
preview=options.preview and not staged.preview,
|
||||
raw_photo=options.raw_photo and not staged.raw,
|
||||
live_photo=options.live_photo and not live_photo,
|
||||
staged |= self._stage_missing_photos_for_export(
|
||||
staged=staged, options=options
|
||||
)
|
||||
if options.use_photokit:
|
||||
missing_staged = self._stage_photo_for_export_with_photokit(
|
||||
options=missing_options
|
||||
)
|
||||
else:
|
||||
missing_staged = self._stage_photo_for_export_with_applescript(
|
||||
options=missing_options
|
||||
)
|
||||
staged |= missing_staged
|
||||
|
||||
return staged
|
||||
|
||||
def _stage_missing_photos_for_export(
|
||||
self, staged: StagedFiles, options: ExportOptions
|
||||
) -> StagedFiles:
|
||||
"""Download and stage any missing files for export"""
|
||||
|
||||
# if live photo and requesting edited version need the edited live photo
|
||||
live_photo = staged.edited_live if options.edited else staged.original_live
|
||||
|
||||
# is there actually a missing file? (#1086)
|
||||
something_to_download = (
|
||||
(self.photo.hasadjustments and options.edited and not staged.edited)
|
||||
or (self.photo.live_photo and options.live_photo and not live_photo)
|
||||
or (self.photo.has_raw and options.raw_photo and not staged.raw)
|
||||
or (options.preview and not staged.preview)
|
||||
or (not options.edited and not staged.original)
|
||||
)
|
||||
if not something_to_download:
|
||||
return staged
|
||||
|
||||
missing_options = ExportOptions(
|
||||
edited=options.edited,
|
||||
preview=options.preview and not staged.preview,
|
||||
raw_photo=self.photo.has_raw and options.raw_photo and not staged.raw,
|
||||
live_photo=self.photo.live_photo and options.live_photo and not live_photo,
|
||||
)
|
||||
if options.use_photokit:
|
||||
missing_staged = self._stage_photo_for_export_with_photokit(
|
||||
options=missing_options
|
||||
)
|
||||
else:
|
||||
missing_staged = self._stage_photo_for_export_with_applescript(
|
||||
options=missing_options
|
||||
)
|
||||
staged |= missing_staged
|
||||
return staged
|
||||
|
||||
def _stage_photo_for_export_with_photokit(
|
||||
@@ -1181,7 +1243,7 @@ class PhotoExporter:
|
||||
try:
|
||||
fileutil.copy(src, dest_str)
|
||||
verbose(
|
||||
f"Exported {self._filename(self.photo.original_filename)} to {self._filepath(dest_str)}"
|
||||
f"Exported {self._filename(self.photo.original_filename)} to {self._filepath(normalize_fs_path(dest_str))}"
|
||||
)
|
||||
except Exception as e:
|
||||
raise ExportError(
|
||||
@@ -1212,7 +1274,7 @@ class PhotoExporter:
|
||||
|
||||
# set data in the database
|
||||
with export_db.create_or_get_file_record(dest_str, self.photo.uuid) as rec:
|
||||
rec.photoinfo = self.photo.json()
|
||||
rec.photoinfo = self.photo.json(shallow=True)
|
||||
rec.export_options = options.bit_flags
|
||||
# don't set src_sig as that is set above before any modifications by convert_to_jpeg or exiftool
|
||||
if not options.ignore_signature:
|
||||
@@ -1521,10 +1583,10 @@ class PhotoExporter:
|
||||
if not options.dry_run:
|
||||
warning_, error_ = self._write_exif_data(src, options=options)
|
||||
if warning_:
|
||||
exiftool_results.exiftool_warning.append((dest, warning_))
|
||||
exiftool_results.exiftool_warning.append((str(dest), str(warning_)))
|
||||
if error_:
|
||||
exiftool_results.exiftool_error.append((dest, error_))
|
||||
exiftool_results.error.append((dest, error_))
|
||||
exiftool_results.exiftool_error.append((str(dest), str(error_)))
|
||||
exiftool_results.error.append((str(dest), str(error_)))
|
||||
|
||||
exiftool_results.exif_updated.append(dest)
|
||||
exiftool_results.to_touch.append(dest)
|
||||
@@ -1953,10 +2015,7 @@ class PhotoExporter:
|
||||
|
||||
options = options or ExportOptions()
|
||||
|
||||
xmp_template_file = (
|
||||
_XMP_TEMPLATE_NAME if not self.photo._db._beta else _XMP_TEMPLATE_NAME_BETA
|
||||
)
|
||||
xmp_template = Template(filename=os.path.join(_TEMPLATE_DIR, xmp_template_file))
|
||||
xmp_template = self._xmp_template()
|
||||
|
||||
if extension is None:
|
||||
extension = pathlib.Path(self.photo.original_filename)
|
||||
@@ -2063,6 +2122,20 @@ class PhotoExporter:
|
||||
xmp_str = "\n".join(line for line in xmp_str.split("\n") if line.strip() != "")
|
||||
return xmp_str
|
||||
|
||||
def _xmp_template(self):
|
||||
"""Return the mako template for XMP sidecar, creating it if necessary"""
|
||||
global _global_xmp_template
|
||||
if _global_xmp_template is not None:
|
||||
return _global_xmp_template
|
||||
|
||||
xmp_template_file = (
|
||||
_XMP_TEMPLATE_NAME_BETA if self.photo._db._beta else _XMP_TEMPLATE_NAME
|
||||
)
|
||||
_global_xmp_template = Template(
|
||||
filename=os.path.join(_TEMPLATE_DIR, xmp_template_file)
|
||||
)
|
||||
return _global_xmp_template
|
||||
|
||||
def _write_sidecar(self, filename, sidecar_str):
|
||||
"""write sidecar_str to filename
|
||||
used for exporting sidecar info"""
|
||||
|
||||
@@ -20,7 +20,6 @@ from types import SimpleNamespace
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import yaml
|
||||
from osxmetadata import OSXMetaData
|
||||
|
||||
import osxphotos
|
||||
|
||||
@@ -59,15 +58,21 @@ from .exiftool import ExifToolCaching, get_exiftool_path
|
||||
from .momentinfo import MomentInfo
|
||||
from .personinfo import FaceInfo, PersonInfo
|
||||
from .photoexporter import ExportOptions, PhotoExporter
|
||||
from .phototables import PhotoTables
|
||||
from .phototemplate import PhotoTemplate, RenderOptions
|
||||
from .placeinfo import PlaceInfo4, PlaceInfo5
|
||||
from .platform import assert_macos, is_macos
|
||||
from .query_builder import get_query
|
||||
from .scoreinfo import ScoreInfo
|
||||
from .searchinfo import SearchInfo
|
||||
from .text_detection import detect_text
|
||||
from .uti import get_preferred_uti_extension, get_uti_for_extension
|
||||
from .utils import _get_resource_loc, hexdigest, list_directory
|
||||
|
||||
if is_macos:
|
||||
from osxmetadata import OSXMetaData
|
||||
|
||||
from .text_detection import detect_text
|
||||
|
||||
__all__ = ["PhotoInfo", "PhotoInfoNone", "frozen_photoinfo_factory"]
|
||||
|
||||
logger = logging.getLogger("osxphotos")
|
||||
@@ -131,8 +136,7 @@ class PhotoInfo:
|
||||
if not self.hasadjustments and self._db._db_version <= _PHOTOS_4_VERSION:
|
||||
return None
|
||||
|
||||
imagedate = self._info["lastmodifieddate"]
|
||||
if imagedate:
|
||||
if imagedate := self._info["lastmodifieddate"]:
|
||||
seconds = self._info["imageTimeZoneOffsetSeconds"] or 0
|
||||
delta = timedelta(seconds=seconds)
|
||||
tz = timezone(delta)
|
||||
@@ -169,6 +173,9 @@ class PhotoInfo:
|
||||
"""Returns candidate path for original photo on Photos >= version 5"""
|
||||
if self._info["shared"]:
|
||||
return self._path_5_shared()
|
||||
if self.syndicated and not self.saved_to_library:
|
||||
# path for "shared with you" syndicated photos that have not yet been saved to the library
|
||||
return self._path_syndication()
|
||||
return (
|
||||
os.path.join(self._info["directory"], self._info["filename"])
|
||||
if self._info["directory"].startswith("/")
|
||||
@@ -208,6 +215,21 @@ class PhotoInfo:
|
||||
filename,
|
||||
)
|
||||
|
||||
def _path_syndication(self):
|
||||
"""Return path for syndicated photo on Photos >= version 8"""
|
||||
# Photos 8+ stores syndicated photos in a separate directory
|
||||
# in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID.ext
|
||||
# where X is first digit of UUID
|
||||
syndication_path = "scopes/syndication/originals"
|
||||
uuid_dir = self.uuid[0]
|
||||
path = os.path.join(
|
||||
self._db._library_path,
|
||||
syndication_path,
|
||||
uuid_dir,
|
||||
self.filename,
|
||||
)
|
||||
return path if os.path.isfile(path) else None
|
||||
|
||||
def _path_4(self):
|
||||
"""Returns candidate path for original photo on Photos <= version 4"""
|
||||
if self._info["has_raw"]:
|
||||
@@ -517,39 +539,26 @@ class PhotoInfo:
|
||||
@property
|
||||
def person_info(self):
|
||||
"""list of PersonInfo objects for person in picture"""
|
||||
try:
|
||||
return self._personinfo
|
||||
except AttributeError:
|
||||
self._personinfo = [
|
||||
PersonInfo(db=self._db, pk=pk) for pk in self._info["persons"]
|
||||
]
|
||||
return self._personinfo
|
||||
return [PersonInfo(db=self._db, pk=pk) for pk in self._info["persons"]]
|
||||
|
||||
@property
|
||||
def face_info(self):
|
||||
"""list of FaceInfo objects for faces in picture"""
|
||||
try:
|
||||
return self._faceinfo
|
||||
except AttributeError:
|
||||
try:
|
||||
faces = self._db._db_faceinfo_uuid[self._uuid]
|
||||
self._faceinfo = [FaceInfo(db=self._db, pk=pk) for pk in faces]
|
||||
except KeyError:
|
||||
# no faces
|
||||
self._faceinfo = []
|
||||
return self._faceinfo
|
||||
faces = self._db._db_faceinfo_uuid[self._uuid]
|
||||
self._faceinfo = [FaceInfo(db=self._db, pk=pk) for pk in faces]
|
||||
except KeyError:
|
||||
# no faces
|
||||
self._faceinfo = []
|
||||
return self._faceinfo
|
||||
|
||||
@property
|
||||
def moment_info(self):
|
||||
"""Moment photo belongs to"""
|
||||
try:
|
||||
return self._moment
|
||||
except AttributeError:
|
||||
try:
|
||||
self._moment = MomentInfo(db=self._db, moment_pk=self._info["momentID"])
|
||||
except ValueError:
|
||||
self._moment = None
|
||||
return self._moment
|
||||
return MomentInfo(db=self._db, moment_pk=self._info["momentID"])
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def albums(self):
|
||||
@@ -566,65 +575,41 @@ class PhotoInfo:
|
||||
@property
|
||||
def burst_albums(self):
|
||||
"""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"""
|
||||
try:
|
||||
return self._burst_albums
|
||||
except AttributeError:
|
||||
burst_albums = list(self.albums)
|
||||
for photo in self.burst_photos:
|
||||
if photo.burst_key:
|
||||
burst_albums.extend(photo.albums)
|
||||
self._burst_albums = list(set(burst_albums))
|
||||
return self._burst_albums
|
||||
burst_albums = list(self.albums)
|
||||
for photo in self.burst_photos:
|
||||
if photo.burst_key:
|
||||
burst_albums.extend(photo.albums)
|
||||
return list(set(burst_albums))
|
||||
|
||||
@property
|
||||
def album_info(self):
|
||||
"""list of AlbumInfo objects representing albums the photo is contained in"""
|
||||
try:
|
||||
return self._album_info
|
||||
except AttributeError:
|
||||
album_uuids = self._get_album_uuids()
|
||||
self._album_info = [
|
||||
AlbumInfo(db=self._db, uuid=album) for album in album_uuids
|
||||
]
|
||||
return self._album_info
|
||||
album_uuids = self._get_album_uuids()
|
||||
return [AlbumInfo(db=self._db, uuid=album) for album in album_uuids]
|
||||
|
||||
@property
|
||||
def burst_album_info(self):
|
||||
"""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."""
|
||||
try:
|
||||
return self._burst_album_info
|
||||
except AttributeError:
|
||||
burst_album_info = list(self.album_info)
|
||||
for photo in self.burst_photos:
|
||||
if photo.burst_key:
|
||||
burst_album_info.extend(photo.album_info)
|
||||
self._burst_album_info = list(set(burst_album_info))
|
||||
return self._burst_album_info
|
||||
burst_album_info = list(self.album_info)
|
||||
for photo in self.burst_photos:
|
||||
if photo.burst_key:
|
||||
burst_album_info.extend(photo.album_info)
|
||||
return list(set(burst_album_info))
|
||||
|
||||
@property
|
||||
def import_info(self):
|
||||
"""ImportInfo object representing import session for the photo or None if no import session"""
|
||||
try:
|
||||
return self._import_info
|
||||
except AttributeError:
|
||||
self._import_info = (
|
||||
ImportInfo(db=self._db, uuid=self._info["import_uuid"])
|
||||
if self._info["import_uuid"] is not None
|
||||
else None
|
||||
)
|
||||
return self._import_info
|
||||
return (
|
||||
ImportInfo(db=self._db, uuid=self._info["import_uuid"])
|
||||
if self._info["import_uuid"] is not None
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def project_info(self):
|
||||
"""list of AlbumInfo objects representing projects for the photo or None if no projects"""
|
||||
try:
|
||||
return self._project_info
|
||||
except AttributeError:
|
||||
project_uuids = self._get_album_uuids(project=True)
|
||||
self._project_info = [
|
||||
ProjectInfo(db=self._db, uuid=album) for album in project_uuids
|
||||
]
|
||||
return self._project_info
|
||||
project_uuids = self._get_album_uuids(project=True)
|
||||
return [ProjectInfo(db=self._db, uuid=album) for album in project_uuids]
|
||||
|
||||
@property
|
||||
def keywords(self):
|
||||
@@ -638,8 +623,7 @@ class PhotoInfo:
|
||||
# in this case, return None so result is the same as if title had never been set (which returns NULL)
|
||||
# issue #512
|
||||
title = self._info["name"]
|
||||
title = None if title == "" else title
|
||||
return title
|
||||
return None if title == "" else title
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
@@ -912,33 +896,14 @@ class PhotoInfo:
|
||||
|
||||
photopath = None
|
||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||
if self.live_photo and not self.ismissing:
|
||||
live_model_id = self._info["live_model_id"]
|
||||
if live_model_id is None:
|
||||
logger.debug(f"missing live_model_id: {self._uuid}")
|
||||
photopath = None
|
||||
else:
|
||||
folder_id, file_id, nn_id = _get_resource_loc(live_model_id)
|
||||
library_path = self._db.library_path
|
||||
photopath = os.path.join(
|
||||
library_path,
|
||||
"resources",
|
||||
"media",
|
||||
"master",
|
||||
folder_id,
|
||||
nn_id,
|
||||
f"jpegvideocomplement_{file_id}.mov",
|
||||
)
|
||||
if not os.path.isfile(photopath):
|
||||
# In testing, I've seen occasional missing movie for live photo
|
||||
# These appear to be valid -- e.g. live component hasn't been downloaded from iCloud
|
||||
# photos 4 has "isOnDisk" column we could check
|
||||
# or could do the actual check with "isfile"
|
||||
# TODO: should this be a warning or debug?
|
||||
photopath = None
|
||||
else:
|
||||
photopath = None
|
||||
return self._path_live_photo_4()
|
||||
elif self.live_photo and self.path and not self.ismissing:
|
||||
if self.shared:
|
||||
return self._path_live_photo_shared_5()
|
||||
if self.syndicated and not self.saved_to_library:
|
||||
# syndicated ("Shared with you") photos not yet saved to library
|
||||
return self._path_live_syndicated()
|
||||
|
||||
filename = pathlib.Path(self.path)
|
||||
photopath = filename.parent.joinpath(f"{filename.stem}_3.mov")
|
||||
photopath = str(photopath)
|
||||
@@ -952,8 +917,68 @@ class PhotoInfo:
|
||||
|
||||
return photopath
|
||||
|
||||
def _path_live_photo_shared_5(self):
|
||||
"""Return path for live photo for shared photos"""
|
||||
if not self.shared:
|
||||
raise ValueError(f"photo {self.uuid} is not a shared photo")
|
||||
if not self.live_photo:
|
||||
raise ValueError(f"photo {self.uuid} is not a live photo")
|
||||
|
||||
photopath = self._path_5_shared()
|
||||
if photopath:
|
||||
photopath = pathlib.Path(photopath).with_suffix(".MOV")
|
||||
if not photopath.exists():
|
||||
photopath = None
|
||||
return photopath
|
||||
|
||||
def _path_live_photo_4(self):
|
||||
"""Return path for live edited photo for Photos <= 4"""
|
||||
if self.live_photo and not self.ismissing:
|
||||
live_model_id = self._info["live_model_id"]
|
||||
if live_model_id is None:
|
||||
logger.debug(f"missing live_model_id: {self._uuid}")
|
||||
photopath = None
|
||||
else:
|
||||
folder_id, file_id, nn_id = _get_resource_loc(live_model_id)
|
||||
library_path = self._db.library_path
|
||||
photopath = os.path.join(
|
||||
library_path,
|
||||
"resources",
|
||||
"media",
|
||||
"master",
|
||||
folder_id,
|
||||
nn_id,
|
||||
f"jpegvideocomplement_{file_id}.mov",
|
||||
)
|
||||
if not os.path.isfile(photopath):
|
||||
# In testing, I've seen occasional missing movie for live photo
|
||||
# These appear to be valid -- e.g. live component hasn't been downloaded from iCloud
|
||||
# photos 4 has "isOnDisk" column we could check
|
||||
# or could do the actual check with "isfile"
|
||||
# TODO: should this be a warning or debug?
|
||||
photopath = None
|
||||
else:
|
||||
photopath = None
|
||||
return photopath
|
||||
|
||||
def _path_live_syndicated(self):
|
||||
"""Return path for live syndicated photo on Photos >= version 8"""
|
||||
# Photos 8+ stores live syndicated photos in a separate directory
|
||||
# in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID_3.mov
|
||||
# where X is first digit of UUID
|
||||
syndication_path = "scopes/syndication/originals"
|
||||
uuid_dir = self.uuid[0]
|
||||
filename = f"{pathlib.Path(self.filename).stem}_3.mov"
|
||||
live_photo = os.path.join(
|
||||
self._db._library_path,
|
||||
syndication_path,
|
||||
uuid_dir,
|
||||
filename,
|
||||
)
|
||||
return live_photo if os.path.isfile(live_photo) else None
|
||||
|
||||
@cached_property
|
||||
def path_derivatives(self):
|
||||
def path_derivatives(self) -> list[str]:
|
||||
"""Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)"""
|
||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||
return self._path_derivatives_4()
|
||||
@@ -962,24 +987,36 @@ class PhotoInfo:
|
||||
return self._path_derivatives_5_shared()
|
||||
|
||||
directory = self._uuid[0] # first char of uuid
|
||||
derivative_path = (
|
||||
pathlib.Path(self._db._library_path) / f"resources/derivatives/{directory}"
|
||||
)
|
||||
if self.syndicated and not self.saved_to_library:
|
||||
# syndicated ("Shared with you") photos not yet saved to library
|
||||
derivative_path = "scopes/syndication/resources/derivatives"
|
||||
thumb_path = (
|
||||
f"{derivative_path}/masters/{directory}/{self.uuid}_4_5005_c.jpeg"
|
||||
)
|
||||
else:
|
||||
derivative_path = f"resources/derivatives/{directory}"
|
||||
thumb_path = (
|
||||
f"resources/derivatives/masters/{directory}/{self.uuid}_4_5005_c.jpeg"
|
||||
)
|
||||
|
||||
derivative_path = pathlib.Path(self._db._library_path).joinpath(derivative_path)
|
||||
thumb_path = pathlib.Path(self._db._library_path).joinpath(thumb_path)
|
||||
|
||||
# find all files that start with uuid in derivative path
|
||||
files = list(derivative_path.glob(f"{self.uuid}*.*"))
|
||||
|
||||
# previews may be missing from derivatives path
|
||||
# there are what appear to be low res thumbnails in the "masters" subfolder
|
||||
thumb_path = (
|
||||
pathlib.Path(self._db._library_path)
|
||||
/ f"resources/derivatives/masters/{directory}/{self.uuid}_4_5005_c.jpeg"
|
||||
)
|
||||
if thumb_path.exists():
|
||||
files.append(thumb_path)
|
||||
|
||||
# sort by file size, largest first
|
||||
files = sorted(files, reverse=True, key=lambda f: f.stat().st_size)
|
||||
|
||||
# return list of filename but skip .THM files (these are actually low-res thumbnails in JPEG format but with .THM extension)
|
||||
derivatives = [str(filename) for filename in files if filename.suffix != ".THM"]
|
||||
if self.isphoto and len(derivatives) > 1 and derivatives[0].endswith(".mov"):
|
||||
# ensure .mov is first in list as poster image could be larger than the movie preview
|
||||
derivatives[1], derivatives[0] = derivatives[0], derivatives[1]
|
||||
|
||||
return derivatives
|
||||
@@ -1298,6 +1335,38 @@ class PhotoInfo:
|
||||
self._search_info_normalized = SearchInfo(self, normalized=True)
|
||||
return self._search_info_normalized
|
||||
|
||||
@cached_property
|
||||
def syndicated(self) -> bool | None:
|
||||
"""Return true if photo was shared via syndication (e.g. via Messages, etc.);
|
||||
these are photos that appear in "Shared with you" album.
|
||||
Photos 8+ only; returns None if not Photos 8+.
|
||||
"""
|
||||
if self._db.photos_version < 8:
|
||||
return None
|
||||
|
||||
try:
|
||||
return (
|
||||
self._db._db_syndication_uuid[self.uuid]["syndication_identifier"]
|
||||
is not None
|
||||
)
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def saved_to_library(self) -> bool | None:
|
||||
"""Return True if syndicated photo has been saved to library;
|
||||
returns False if photo is not syndicated or has not been saved to the library.
|
||||
Returns None if not Photos 8+.
|
||||
Syndicated photos are photos that appear in "Shared with you" album; Photos 8+ only.
|
||||
"""
|
||||
if self._db.photos_version < 8:
|
||||
return None
|
||||
|
||||
try:
|
||||
return self._db._db_syndication_uuid[self.uuid]["syndication_history"] != 0
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
@property
|
||||
def labels(self):
|
||||
"""returns list of labels applied to photo by Photos image categorization
|
||||
@@ -1498,6 +1567,8 @@ class PhotoInfo:
|
||||
|
||||
def _detected_text(self):
|
||||
"""detect text in photo, either from cached extended attribute or by attempting text detection"""
|
||||
assert_macos()
|
||||
|
||||
path = (
|
||||
self.path_edited if self.hasadjustments and self.path_edited else self.path
|
||||
)
|
||||
@@ -1777,41 +1848,31 @@ class PhotoInfo:
|
||||
}
|
||||
return yaml.dump(info, sort_keys=False)
|
||||
|
||||
def asdict(self):
|
||||
"""return dict representation"""
|
||||
def asdict(self, shallow: bool = True) -> dict[str, Any]:
|
||||
"""Return dict representation of PhotoInfo object.
|
||||
|
||||
Args:
|
||||
shallow: if True, return shallow representation (does not contain folder_info, person_info, etc.)
|
||||
|
||||
Returns:
|
||||
dict representation of PhotoInfo object
|
||||
|
||||
Note:
|
||||
The shallow representation is used internally by export as it contains only the subset of data needed for export.
|
||||
"""
|
||||
|
||||
adjustments = self.adjustments.asdict() if self.adjustments else {}
|
||||
album_info = [album.asdict() for album in self.album_info]
|
||||
burst_album_info = [a.asdict() for a in self.burst_album_info]
|
||||
burst_photos = [p.uuid for p in self.burst_photos]
|
||||
comments = [comment.asdict() for comment in self.comments]
|
||||
exif_info = dataclasses.asdict(self.exif_info) if self.exif_info else {}
|
||||
face_info = [face.asdict() for face in self.face_info]
|
||||
folders = {album.title: album.folder_names for album in self.album_info}
|
||||
import_info = self.import_info.asdict() if self.import_info else {}
|
||||
likes = [like.asdict() for like in self.likes]
|
||||
person_info = [p.asdict() for p in self.person_info]
|
||||
place = self.place.asdict() if self.place else {}
|
||||
project_info = [p.asdict() for p in self.project_info]
|
||||
score = dataclasses.asdict(self.score) if self.score else {}
|
||||
search_info = self.search_info.asdict() if self.search_info else {}
|
||||
search_info_normalized = (
|
||||
self.search_info_normalized.asdict() if self.search_info_normalized else {}
|
||||
)
|
||||
|
||||
return {
|
||||
"adjustments": adjustments,
|
||||
"album_info": album_info,
|
||||
dict_data = {
|
||||
"albums": self.albums,
|
||||
"burst_album_info": burst_album_info,
|
||||
"burst_albums": self.burst_albums,
|
||||
"burst_default_pick": self.burst_default_pick,
|
||||
"burst_key": self.burst_key,
|
||||
"burst_photos": burst_photos,
|
||||
"burst_selected": self.burst_selected,
|
||||
"burst": self.burst,
|
||||
"cloud_guid": self.cloud_guid,
|
||||
"cloud_metadata": self.cloud_metadata,
|
||||
"cloud_owner_hashed_id": self.cloud_owner_hashed_id,
|
||||
"comments": comments,
|
||||
"date_added": self.date_added,
|
||||
@@ -1831,7 +1892,6 @@ class PhotoInfo:
|
||||
"hdr": self.hdr,
|
||||
"height": self.height,
|
||||
"hidden": self.hidden,
|
||||
"import_info": import_info,
|
||||
"incloud": self.incloud,
|
||||
"intrash": self.intrash,
|
||||
"iscloudasset": self.iscloudasset,
|
||||
@@ -1841,7 +1901,6 @@ class PhotoInfo:
|
||||
"israw": self.israw,
|
||||
"isreference": self.isreference,
|
||||
"keywords": self.keywords,
|
||||
"labels_normalized": self.labels_normalized,
|
||||
"labels": self.labels,
|
||||
"latitude": self._latitude,
|
||||
"library": self._db._library_path,
|
||||
@@ -1857,22 +1916,17 @@ class PhotoInfo:
|
||||
"original_width": self.original_width,
|
||||
"owner": self.owner,
|
||||
"panorama": self.panorama,
|
||||
"path_derivatives": self.path_derivatives,
|
||||
"path_edited_live_photo": self.path_edited_live_photo,
|
||||
"path_edited": self.path_edited,
|
||||
"path_live_photo": self.path_live_photo,
|
||||
"path_raw": self.path_raw,
|
||||
"path": self.path,
|
||||
"person_info": person_info,
|
||||
"persons": self.persons,
|
||||
"place": place,
|
||||
"portrait": self.portrait,
|
||||
"project_info": project_info,
|
||||
"raw_original": self.raw_original,
|
||||
"score": score,
|
||||
"screenshot": self.screenshot,
|
||||
"search_info_normalized": search_info_normalized,
|
||||
"search_info": search_info,
|
||||
"selfie": self.selfie,
|
||||
"shared": self.shared,
|
||||
"slow_mo": self.slow_mo,
|
||||
@@ -1888,14 +1942,57 @@ class PhotoInfo:
|
||||
"width": self.width,
|
||||
}
|
||||
|
||||
def json(self):
|
||||
"""Return JSON representation"""
|
||||
# non-shallow keys
|
||||
if not shallow:
|
||||
dict_data["album_info"] = [album.asdict() for album in self.album_info]
|
||||
dict_data["path_derivatives"] = self.path_derivatives
|
||||
dict_data["adjustments"] = (
|
||||
self.adjustments.asdict() if self.adjustments else {}
|
||||
)
|
||||
dict_data["burst_album_info"] = [a.asdict() for a in self.burst_album_info]
|
||||
dict_data["burst_albums"] = self.burst_albums
|
||||
dict_data["burst_default_pick"] = self.burst_default_pick
|
||||
dict_data["burst_key"] = self.burst_key
|
||||
dict_data["burst_photos"] = [p.uuid for p in self.burst_photos]
|
||||
dict_data["burst_selected"] = self.burst_selected
|
||||
dict_data["cloud_metadata"] = self.cloud_metadata
|
||||
dict_data["import_info"] = (
|
||||
self.import_info.asdict() if self.import_info else {}
|
||||
)
|
||||
dict_data["labels_normalized"] = self.labels_normalized
|
||||
dict_data["person_info"] = [p.asdict() for p in self.person_info]
|
||||
dict_data["project_info"] = [p.asdict() for p in self.project_info]
|
||||
dict_data["search_info"] = (
|
||||
self.search_info.asdict() if self.search_info else {}
|
||||
)
|
||||
dict_data["search_info_normalized"] = (
|
||||
self.search_info_normalized.asdict()
|
||||
if self.search_info_normalized
|
||||
else {}
|
||||
)
|
||||
|
||||
return dict_data
|
||||
|
||||
def json(self, indent: int | None = None, shallow: bool = True) -> str:
|
||||
"""Return JSON representation
|
||||
|
||||
Args:
|
||||
indent: indent level for JSON, if None, no indent
|
||||
shallow: if True, return shallow JSON representation (does not contain folder_info, person_info, etc.)
|
||||
|
||||
Returns:
|
||||
JSON string
|
||||
|
||||
Note:
|
||||
The shallow representation is used internally by export as it contains only the subset of data needed for export.
|
||||
"""
|
||||
|
||||
def default(o):
|
||||
if isinstance(o, (datetime.date, datetime.datetime)):
|
||||
return o.isoformat()
|
||||
|
||||
dict_data = self.asdict()
|
||||
dict_data = self.asdict(shallow=True) if shallow else self.asdict(shallow=False)
|
||||
|
||||
for k, v in dict_data.items():
|
||||
# sort lists such as keywords so JSON is consistent
|
||||
# but do not sort certain items like location
|
||||
@@ -1903,7 +2000,11 @@ class PhotoInfo:
|
||||
continue
|
||||
if v and isinstance(v, (list, tuple)) and not isinstance(v[0], dict):
|
||||
dict_data[k] = sorted(v, key=lambda v: v if v is not None else "")
|
||||
return json.dumps(dict_data, sort_keys=True, default=default)
|
||||
return json.dumps(dict_data, sort_keys=True, default=default, indent=indent)
|
||||
|
||||
def tables(self) -> PhotoTables:
|
||||
"""Return PhotoTables object to provide access database tables associated with this photo (Photos 5+)"""
|
||||
return None if self._db._photos_ver < 5 else PhotoTables(self)
|
||||
|
||||
def _json_hexdigest(self):
|
||||
"""JSON for use by hexdigest()"""
|
||||
@@ -1917,15 +2018,9 @@ class PhotoInfo:
|
||||
if isinstance(o, (datetime.date, datetime.datetime)):
|
||||
return o.isoformat()
|
||||
|
||||
dict_data = self.asdict()
|
||||
dict_data = self.asdict(shallow=True)
|
||||
|
||||
for k in [
|
||||
"album_info",
|
||||
"burst_album_info",
|
||||
"face_info",
|
||||
"person_info",
|
||||
"visible",
|
||||
]:
|
||||
for k in ["face_info", "visible"]:
|
||||
del dict_data[k]
|
||||
|
||||
for k, v in dict_data.items():
|
||||
|
||||
@@ -19,9 +19,14 @@
|
||||
|
||||
import copy
|
||||
import pathlib
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from .platform import get_macos_version
|
||||
|
||||
assert sys.platform == "darwin"
|
||||
|
||||
import AVFoundation
|
||||
import CoreServices
|
||||
import Foundation
|
||||
@@ -34,7 +39,7 @@ from wurlitzer import pipes
|
||||
|
||||
from .fileutil import FileUtil
|
||||
from .uti import get_preferred_uti_extension
|
||||
from .utils import get_macos_version, increment_filename
|
||||
from .utils import increment_filename
|
||||
|
||||
__all__ = [
|
||||
"NSURL_to_path",
|
||||
@@ -84,6 +89,7 @@ PHOTOKIT_NOTIFICATION_FINISHED_REQUEST = "PyPhotoKitNotificationFinishedRequest"
|
||||
# minimum amount to sleep while waiting for export
|
||||
MIN_SLEEP = 0.015
|
||||
|
||||
|
||||
### utility functions
|
||||
def NSURL_to_path(url):
|
||||
"""Convert URL string as represented by NSURL to a path string"""
|
||||
@@ -626,7 +632,8 @@ class PhotoAsset:
|
||||
|
||||
def handler(imageData, dataUTI, orientation, info):
|
||||
"""result handler for requestImageDataAndOrientationForAsset_options_resultHandler_
|
||||
all returned by the request is set as properties of nonlocal data (Fetchdata object)"""
|
||||
all returned by the request is set as properties of nonlocal data (Fetchdata object)
|
||||
"""
|
||||
|
||||
nonlocal requestdata
|
||||
|
||||
@@ -673,7 +680,8 @@ class PhotoAsset:
|
||||
|
||||
def handler(data):
|
||||
"""result handler for requestImageDataAndOrientationForAsset_options_resultHandler_
|
||||
all returned by the request is set as properties of nonlocal data (Fetchdata object)"""
|
||||
all returned by the request is set as properties of nonlocal data (Fetchdata object)
|
||||
"""
|
||||
|
||||
nonlocal requestdata
|
||||
|
||||
@@ -710,7 +718,8 @@ class PhotoAsset:
|
||||
|
||||
def handler(imageData, dataUTI, orientation, info):
|
||||
"""result handler for requestImageDataAndOrientationForAsset_options_resultHandler_
|
||||
all returned by the request is set as properties of nonlocal data (Fetchdata object)"""
|
||||
all returned by the request is set as properties of nonlocal data (Fetchdata object)
|
||||
"""
|
||||
|
||||
nonlocal data
|
||||
|
||||
|
||||
@@ -1,30 +1,54 @@
|
||||
""" PhotosAlbum class to create an album in default Photos library and add photos to it """
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unicodedata
|
||||
from typing import List, Optional
|
||||
|
||||
import photoscript
|
||||
from more_itertools import chunked
|
||||
from photoscript import Album, Folder, Photo, PhotosLibrary
|
||||
|
||||
from .photoinfo import PhotoInfo
|
||||
from .platform import assert_macos
|
||||
from .utils import noop, pluralize
|
||||
|
||||
assert_macos()
|
||||
|
||||
import photoscript
|
||||
from photoscript import Album, Folder, Photo, PhotosLibrary
|
||||
|
||||
__all__ = ["PhotosAlbum", "PhotosAlbumPhotoScript"]
|
||||
|
||||
|
||||
def get_unicode_variants(s: str) -> list[str]:
|
||||
"""Get all unicode variants of string"""
|
||||
variants = []
|
||||
for form in ["NFC", "NFD", "NFKC", "NFKD"]:
|
||||
normalized = unicodedata.normalize(form, s)
|
||||
variants.append(normalized)
|
||||
return variants
|
||||
|
||||
|
||||
def folder_by_path(folders: List[str], verbose: Optional[callable] = None) -> Folder:
|
||||
"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""
|
||||
library = PhotosLibrary()
|
||||
verbose = verbose or noop
|
||||
top_folder_name = folders.pop(0)
|
||||
top_folder = library.folder(top_folder_name, top_level=True)
|
||||
if not top_folder:
|
||||
|
||||
for folder_variant in get_unicode_variants(top_folder_name):
|
||||
top_folder = library.folder(folder_variant, top_level=True)
|
||||
if top_folder is not None:
|
||||
break
|
||||
else:
|
||||
verbose(f"Creating folder '{top_folder_name}'")
|
||||
top_folder = library.create_folder(top_folder_name)
|
||||
|
||||
current_folder = top_folder
|
||||
for folder_name in folders:
|
||||
folder = current_folder.folder(folder_name)
|
||||
if not folder:
|
||||
for folder_variant in get_unicode_variants(folder_name):
|
||||
folder = current_folder.folder(folder_variant)
|
||||
if folder is not None:
|
||||
break
|
||||
else:
|
||||
verbose(f"Creating folder '{folder_name}'")
|
||||
folder = current_folder.create_folder(folder_name)
|
||||
current_folder = folder
|
||||
@@ -41,15 +65,24 @@ def album_by_path(
|
||||
# have folders
|
||||
album_name = folders_album.pop()
|
||||
folder = folder_by_path(folders_album, verbose)
|
||||
album = folder.album(album_name)
|
||||
if album is None:
|
||||
for album_variant in get_unicode_variants(album_name):
|
||||
# Get album if it exists
|
||||
# need to check every unicode variant to avoid creating duplicate albums with same visual representation (#1085)
|
||||
album = folder.album(album_variant)
|
||||
if album is not None:
|
||||
break
|
||||
else:
|
||||
verbose(f"Creating album '{album_name}'")
|
||||
album = folder.create_album(album_name)
|
||||
else:
|
||||
# only have album name
|
||||
album_name = folders_album[0]
|
||||
album = library.album(album_name, top_level=True)
|
||||
if album is None:
|
||||
for album_variant in get_unicode_variants(album_name):
|
||||
album = library.album(album_variant, top_level=True)
|
||||
if album is not None:
|
||||
break
|
||||
else:
|
||||
# album doesn't exist, create it
|
||||
verbose(f"Creating album '{album_name}'")
|
||||
album = library.create_album(album_name)
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@ from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
|
||||
from .platform import assert_macos
|
||||
|
||||
assert_macos()
|
||||
|
||||
import photoscript
|
||||
|
||||
from ._constants import _DB_TABLE_NAMES, _PHOTOS_5_ALBUM_KIND, _PHOTOS_5_FOLDER_KIND
|
||||
|
||||
@@ -4,4 +4,4 @@ Processes a Photos.app library database to extract information about photos
|
||||
"""
|
||||
|
||||
from .photosdb import PhotosDB
|
||||
from .photosdb_utils import get_db_version, get_db_model_version, get_model_version
|
||||
from .photosdb_utils import get_db_model_version, get_db_version, get_model_version
|
||||
|
||||
@@ -6,8 +6,8 @@ import datetime
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION, TIME_DELTA
|
||||
from ..utils import normalize_unicode
|
||||
from ..sqlite_utils import sqlite_open_ro
|
||||
from ..unicode import normalize_unicode
|
||||
|
||||
|
||||
def _process_comments(self):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import logging
|
||||
|
||||
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
|
||||
from ..sqlite_utils import sqlite_open_ro, sqlite_db_is_locked
|
||||
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
|
||||
from .photosdb_utils import get_db_version
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
|
||||
from ..sqlite_utils import sqlite_open_ro
|
||||
from ..utils import normalize_unicode
|
||||
from ..unicode import normalize_unicode
|
||||
from .photosdb_utils import get_db_version
|
||||
|
||||
"""
|
||||
@@ -309,7 +309,9 @@ def _process_faceinfo_5(photosdb):
|
||||
face["righteyey"] = row[34]
|
||||
face["roll"] = row[35]
|
||||
face["size"] = row[36]
|
||||
face["yaw"] = 0 # Photos 4 only (this is in Photos 5-7, but dropped in Ventura so just don't support it)
|
||||
face[
|
||||
"yaw"
|
||||
] = 0 # Photos 4 only (this is in Photos 5-7, but dropped in Ventura so just don't support it)
|
||||
face["pitch"] = 0 # not defined in Photos 5
|
||||
|
||||
photosdb._db_faceinfo_pk[pk] = face
|
||||
|
||||
@@ -7,11 +7,10 @@ import logging
|
||||
import pathlib
|
||||
import uuid as uuidlib
|
||||
from functools import lru_cache
|
||||
from pprint import pformat
|
||||
|
||||
from .._constants import _PHOTOS_4_VERSION, search_category_factory
|
||||
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
|
||||
from ..utils import normalize_unicode
|
||||
from ..unicode import normalize_unicode
|
||||
|
||||
"""
|
||||
This module should be imported in the class defintion of PhotosDB in photosdb.py
|
||||
@@ -154,7 +153,7 @@ def _process_searchinfo(self):
|
||||
def labels(self):
|
||||
"""return list of all search info labels found in the library"""
|
||||
if self._db_version <= _PHOTOS_4_VERSION:
|
||||
logging.warning(f"SearchInfo not implemented for this library version")
|
||||
logging.warning("SearchInfo not implemented for this library version")
|
||||
return []
|
||||
|
||||
return list(self._db_searchinfo_labels.keys())
|
||||
@@ -164,7 +163,7 @@ def labels(self):
|
||||
def labels_normalized(self):
|
||||
"""return list of all normalized search info labels found in the library"""
|
||||
if self._db_version <= _PHOTOS_4_VERSION:
|
||||
logging.warning(f"SearchInfo not implemented for this library version")
|
||||
logging.warning("SearchInfo not implemented for this library version")
|
||||
return []
|
||||
|
||||
return list(self._db_searchinfo_labels_normalized.keys())
|
||||
@@ -174,8 +173,8 @@ def labels_normalized(self):
|
||||
def labels_as_dict(self):
|
||||
"""return labels as dict of label: count in reverse sorted order (descending)"""
|
||||
if self._db_version <= _PHOTOS_4_VERSION:
|
||||
logging.warning(f"SearchInfo not implemented for this library version")
|
||||
return dict()
|
||||
logging.warning("SearchInfo not implemented for this library version")
|
||||
return {}
|
||||
|
||||
labels = {k: len(v) for k, v in self._db_searchinfo_labels.items()}
|
||||
labels = dict(sorted(labels.items(), key=lambda kv: kv[1], reverse=True))
|
||||
@@ -186,8 +185,8 @@ def labels_as_dict(self):
|
||||
def labels_normalized_as_dict(self):
|
||||
"""return normalized labels as dict of label: count in reverse sorted order (descending)"""
|
||||
if self._db_version <= _PHOTOS_4_VERSION:
|
||||
logging.warning(f"SearchInfo not implemented for this library version")
|
||||
return dict()
|
||||
logging.warning("SearchInfo not implemented for this library version")
|
||||
return {}
|
||||
labels = {k: len(v) for k, v in self._db_searchinfo_labels_normalized.items()}
|
||||
labels = dict(sorted(labels.items(), key=lambda kv: kv[1], reverse=True))
|
||||
return labels
|
||||
|
||||
60
osxphotos/photosdb/_photosdb_process_syndicationinfo.py
Normal file
60
osxphotos/photosdb/_photosdb_process_syndicationinfo.py
Normal file
@@ -0,0 +1,60 @@
|
||||
""" Methods for PhotosDB to process Syndication info (#1054) """
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .._constants import _DB_TABLE_NAMES
|
||||
from ..sqlite_utils import sqlite_open_ro
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from osxphotos.photosdb import PhotosDB
|
||||
|
||||
|
||||
def _process_syndicationinfo(self: PhotosDB):
|
||||
"""Process syndication information"""
|
||||
|
||||
self._db_syndication_uuid = {}
|
||||
|
||||
if self.photos_version < 8:
|
||||
raise NotImplementedError(
|
||||
f"syndication info not implemented for this database version: {self.photos_version}"
|
||||
)
|
||||
else:
|
||||
_process_syndicationinfo_8(self)
|
||||
|
||||
|
||||
def _process_syndicationinfo_8(photosdb: PhotosDB):
|
||||
"""Process Syndication info for Photos 8.0 and later
|
||||
|
||||
Args:
|
||||
photosdb: an OSXPhotosDB instance
|
||||
"""
|
||||
|
||||
db = photosdb._tmp_db
|
||||
zasset = _DB_TABLE_NAMES[photosdb._photos_ver]["ASSET"]
|
||||
|
||||
(conn, cursor) = sqlite_open_ro(db)
|
||||
|
||||
result = cursor.execute(
|
||||
f"""
|
||||
SELECT
|
||||
{zasset}.ZUUID,
|
||||
{zasset}.ZSYNDICATIONSTATE,
|
||||
ZADDITIONALASSETATTRIBUTES.ZSYNDICATIONHISTORY,
|
||||
ZADDITIONALASSETATTRIBUTES.ZSYNDICATIONIDENTIFIER
|
||||
FROM {zasset}
|
||||
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = {zasset}.Z_PK
|
||||
"""
|
||||
)
|
||||
|
||||
for row in result:
|
||||
uuid = row[0]
|
||||
syndication_state = row[1]
|
||||
syndication_history = row[2]
|
||||
syndication_identifier = row[3]
|
||||
photosdb._db_syndication_uuid[uuid] = {
|
||||
"syndication_state": syndication_state,
|
||||
"syndication_identifier": syndication_identifier,
|
||||
"syndication_history": syndication_history,
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user