Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cce234a8c | ||
|
|
c5dba8c89b | ||
|
|
603dabb8f4 | ||
|
|
091f1d9bb4 | ||
|
|
d16932d0fd |
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. Dates are d
|
|||||||
|
|
||||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||||
|
|
||||||
|
#### [v0.36.0](https://github.com/RhetTbull/osxphotos/compare/v0.35.7...v0.36.0)
|
||||||
|
|
||||||
|
> 26 October 2020
|
||||||
|
|
||||||
|
- Added verbose to PhotosDB(), partial fix for #110 [`d87b8f3`](https://github.com/RhetTbull/osxphotos/commit/d87b8f30a45cbb6fdb315a12f8585e2bdc21be6b)
|
||||||
|
- Added comments/likes, implements #214 [`23de6b5`](https://github.com/RhetTbull/osxphotos/commit/23de6b58908371d9ca55d1d1999c6d56de454180)
|
||||||
|
- Cleaned up constructor for PhotosDB [`667c89e`](https://github.com/RhetTbull/osxphotos/commit/667c89e32c3f96baeafebc03e83517ea05693b00)
|
||||||
|
|
||||||
#### [v0.35.7](https://github.com/RhetTbull/osxphotos/compare/v0.35.6...v0.35.7)
|
#### [v0.35.7](https://github.com/RhetTbull/osxphotos/compare/v0.35.6...v0.35.7)
|
||||||
|
|
||||||
> 24 October 2020
|
> 24 October 2020
|
||||||
|
|||||||
74
README.md
@@ -221,6 +221,10 @@ Options:
|
|||||||
2000-01-12T12:00:00,
|
2000-01-12T12:00:00,
|
||||||
2001-01-12T12:00:00-07:00, or 2000-12-31
|
2001-01-12T12:00:00-07:00, or 2000-12-31
|
||||||
(ISO 8601).
|
(ISO 8601).
|
||||||
|
--has-comment Search for photos that have comments.
|
||||||
|
--no-comment Search for photos with no comments.
|
||||||
|
--has-likes Search for photos that have likes.
|
||||||
|
--no-likes Search for photos with no likes.
|
||||||
--deleted Include photos from the 'Recently Deleted'
|
--deleted Include photos from the 'Recently Deleted'
|
||||||
folder.
|
folder.
|
||||||
--deleted-only Include only photos from the 'Recently
|
--deleted-only Include only photos from the 'Recently
|
||||||
@@ -423,23 +427,24 @@ Substitution Description
|
|||||||
{descr} Description of the photo
|
{descr} Description of the photo
|
||||||
{created.date} Photo's creation date in ISO format, e.g.
|
{created.date} Photo's creation date in ISO format, e.g.
|
||||||
'2020-03-22'
|
'2020-03-22'
|
||||||
{created.year} 4-digit year of file creation time
|
{created.year} 4-digit year of photo creation time
|
||||||
{created.yy} 2-digit year of file creation time
|
{created.yy} 2-digit year of photo creation time
|
||||||
{created.mm} 2-digit month of the file creation time
|
{created.mm} 2-digit month of the photo creation time
|
||||||
(zero padded)
|
(zero padded)
|
||||||
{created.month} Month name in user's locale of the file
|
{created.month} Month name in user's locale of the photo
|
||||||
creation time
|
creation time
|
||||||
{created.mon} Month abbreviation in the user's locale of
|
{created.mon} Month abbreviation in the user's locale of
|
||||||
the file creation time
|
the photo creation time
|
||||||
{created.dd} 2-digit day of the month (zero padded) of
|
{created.dd} 2-digit day of the month (zero padded) of
|
||||||
file creation time
|
photo creation time
|
||||||
{created.dow} Day of week in user's locale of the file
|
{created.dow} Day of week in user's locale of the photo
|
||||||
creation time
|
creation time
|
||||||
{created.doy} 3-digit day of year (e.g Julian day) of file
|
{created.doy} 3-digit day of year (e.g Julian day) of
|
||||||
creation time, starting from 1 (zero padded)
|
photo creation time, starting from 1 (zero
|
||||||
{created.hour} 2-digit hour of the file creation time
|
padded)
|
||||||
{created.min} 2-digit minute of the file creation time
|
{created.hour} 2-digit hour of the photo creation time
|
||||||
{created.sec} 2-digit second of the file creation time
|
{created.min} 2-digit minute of the photo creation time
|
||||||
|
{created.sec} 2-digit second of the photo creation time
|
||||||
{created.strftime} Apply strftime template to file creation
|
{created.strftime} Apply strftime template to file creation
|
||||||
date/time. Should be used in form
|
date/time. Should be used in form
|
||||||
{created.strftime,TEMPLATE} where TEMPLATE
|
{created.strftime,TEMPLATE} where TEMPLATE
|
||||||
@@ -451,22 +456,26 @@ Substitution Description
|
|||||||
templates.
|
templates.
|
||||||
{modified.date} Photo's modification date in ISO format,
|
{modified.date} Photo's modification date in ISO format,
|
||||||
e.g. '2020-03-22'
|
e.g. '2020-03-22'
|
||||||
{modified.year} 4-digit year of file modification time
|
{modified.year} 4-digit year of photo modification time
|
||||||
{modified.yy} 2-digit year of file modification time
|
{modified.yy} 2-digit year of photo modification time
|
||||||
{modified.mm} 2-digit month of the file modification time
|
{modified.mm} 2-digit month of the photo modification time
|
||||||
(zero padded)
|
(zero padded)
|
||||||
{modified.month} Month name in user's locale of the file
|
{modified.month} Month name in user's locale of the photo
|
||||||
modification time
|
modification time
|
||||||
{modified.mon} Month abbreviation in the user's locale of
|
{modified.mon} Month abbreviation in the user's locale of
|
||||||
the file modification time
|
the photo modification time
|
||||||
{modified.dd} 2-digit day of the month (zero padded) of
|
{modified.dd} 2-digit day of the month (zero padded) of
|
||||||
the file modification time
|
the photo modification time
|
||||||
{modified.doy} 3-digit day of year (e.g Julian day) of file
|
{modified.dow} Day of week in user's locale of the photo
|
||||||
modification time, starting from 1 (zero
|
modification time
|
||||||
padded)
|
{modified.doy} 3-digit day of year (e.g Julian day) of
|
||||||
{modified.hour} 2-digit hour of the file modification time
|
photo modification time, starting from 1
|
||||||
{modified.min} 2-digit minute of the file modification time
|
(zero padded)
|
||||||
{modified.sec} 2-digit second of the file modification time
|
{modified.hour} 2-digit hour of the photo modification time
|
||||||
|
{modified.min} 2-digit minute of the photo modification
|
||||||
|
time
|
||||||
|
{modified.sec} 2-digit second of the photo modification
|
||||||
|
time
|
||||||
{today.date} Current date in iso format, e.g.
|
{today.date} Current date in iso format, e.g.
|
||||||
'2020-03-22'
|
'2020-03-22'
|
||||||
{today.year} 4-digit year of current date
|
{today.year} 4-digit year of current date
|
||||||
@@ -542,6 +551,7 @@ Substitution Description
|
|||||||
(Photos 5 only)
|
(Photos 5 only)
|
||||||
{label_normalized} All lower case version of 'label' (Photos 5 only)
|
{label_normalized} All lower case version of 'label' (Photos 5 only)
|
||||||
{comment} Comment(s) on shared Photos; format is 'Person name:
|
{comment} Comment(s) on shared Photos; format is 'Person name:
|
||||||
|
comment text' (Photos 5 only)
|
||||||
```
|
```
|
||||||
|
|
||||||
Example: export all photos to ~/Desktop/export group in folders by date created
|
Example: export all photos to ~/Desktop/export group in folders by date created
|
||||||
@@ -1294,7 +1304,8 @@ exiftool must be installed in the path for this to work. If exiftool cannot be
|
|||||||
|
|
||||||
`ExifTool` provides the following methods:
|
`ExifTool` provides the following methods:
|
||||||
|
|
||||||
- `as_dict()`: returns all EXIF metadata found in the file as a dictionary in following form (Note: this shows just a subset of available metadata). See [exiftool](https://exiftool.org/) documentation to understand which metadata keys are available.
|
- `asdict()`: returns all EXIF metadata found in the file as a dictionary in following form (Note: this shows just a subset of available metadata). See [exiftool](https://exiftool.org/) documentation to understand which metadata keys are available.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{'Composite:Aperture': 2.2,
|
{'Composite:Aperture': 2.2,
|
||||||
'Composite:GPSPosition': '-34.9188916666667 138.596861111111',
|
'Composite:GPSPosition': '-34.9188916666667 138.596861111111',
|
||||||
@@ -1307,7 +1318,7 @@ exiftool must be installed in the path for this to work. If exiftool cannot be
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `json()`: returns same information as `as_dict()` but as a serialized JSON string.
|
- `json()`: returns same information as `asdict()` but as a serialized JSON string.
|
||||||
|
|
||||||
- `setvalue(tag, value)`: write to the EXIF data in the photo file. To delete a tag, use setvalue with value = `None`. For example:
|
- `setvalue(tag, value)`: write to the EXIF data in the photo file. To delete a tag, use setvalue with value = `None`. For example:
|
||||||
```python
|
```python
|
||||||
@@ -1318,7 +1329,7 @@ photo.exiftool.setvalue("XMP:Title", "Title of photo")
|
|||||||
photo.exiftool.addvalues("IPTC:Keywords", "vacation", "beach")
|
photo.exiftool.addvalues("IPTC:Keywords", "vacation", "beach")
|
||||||
```
|
```
|
||||||
|
|
||||||
**Caution**: I caution against writing new EXIF data to photos in the Photos library because this will overwrite the original copy of the photo and could adversely affect how Photos behaves. `exiftool.as_dict()` is useful for getting access to all the photos information but if you want to write new EXIF data, I recommend you export the photo first then write the data. [PhotoInfo.export()](#export) does this if called with `exiftool=True`.
|
**Caution**: I caution against writing new EXIF data to photos in the Photos library because this will overwrite the original copy of the photo and could adversely affect how Photos behaves. `exiftool.asdict()` is useful for getting access to all the photos information but if you want to write new EXIF data, I recommend you export the photo first then write the data. [PhotoInfo.export()](#export) does this if called with `exiftool=True`.
|
||||||
|
|
||||||
#### `score`
|
#### `score`
|
||||||
Returns a [ScoreInfo](#scoreinfo) data class object which provides access to the computed aesthetic scores for each photo.
|
Returns a [ScoreInfo](#scoreinfo) data class object which provides access to the computed aesthetic scores for each photo.
|
||||||
@@ -1326,7 +1337,10 @@ Returns a [ScoreInfo](#scoreinfo) data class object which provides access to the
|
|||||||
**Note**: Valid only for Photos 5; returns None for earlier Photos versions.
|
**Note**: Valid only for Photos 5; returns None for earlier Photos versions.
|
||||||
|
|
||||||
#### `json()`
|
#### `json()`
|
||||||
Returns a JSON representation of all photo info
|
Returns a JSON representation of all photo info.
|
||||||
|
|
||||||
|
#### `asdict()`
|
||||||
|
Returns a dictionary representation of all photo info.
|
||||||
|
|
||||||
#### `export()`
|
#### `export()`
|
||||||
`export(dest, *filename, edited=False, live_photo=False, export_as_hardlink=False, overwrite=False, increment=True, sidecar_json=False, sidecar_xmp=False, use_photos_export=False, timeout=120, exiftool=False, no_xattr=False, use_albums_as_keywords=False, use_persons_as_keywords=False)`
|
`export(dest, *filename, edited=False, live_photo=False, export_as_hardlink=False, overwrite=False, increment=True, sidecar_json=False, sidecar_xmp=False, use_photos_export=False, timeout=120, exiftool=False, no_xattr=False, use_albums_as_keywords=False, use_persons_as_keywords=False)`
|
||||||
@@ -1674,6 +1688,9 @@ Returns a list of [FaceInfo](#faceinfo) objects associated with this person sort
|
|||||||
#### `json()`
|
#### `json()`
|
||||||
Returns a json string representation of the PersonInfo instance.
|
Returns a json string representation of the PersonInfo instance.
|
||||||
|
|
||||||
|
#### `asdict()`
|
||||||
|
Returns a dictionary representation of the PersonInfo instance.
|
||||||
|
|
||||||
### FaceInfo
|
### FaceInfo
|
||||||
[PhotoInfo.face_info](#photofaceinfo) return a list of FaceInfo objects representing detected faces in a photo. The FaceInfo class has the following properties and methods.
|
[PhotoInfo.face_info](#photofaceinfo) return a list of FaceInfo objects representing detected faces in a photo. The FaceInfo class has the following properties and methods.
|
||||||
|
|
||||||
@@ -1871,6 +1888,7 @@ The following substitutions are availabe for use with `PhotoInfo.render_template
|
|||||||
|{person}|Person(s) / face(s) in a photo|
|
|{person}|Person(s) / face(s) in a photo|
|
||||||
|{label}|Image categorization label associated with a photo (Photos 5 only)|
|
|{label}|Image categorization label associated with a photo (Photos 5 only)|
|
||||||
|{label_normalized}|All lower case version of 'label' (Photos 5 only)|
|
|{label_normalized}|All lower case version of 'label' (Photos 5 only)|
|
||||||
|
|{comment}|Comment(s) on shared Photos; format is 'Person name: comment text' (Photos 5 only)|
|
||||||
|
|
||||||
### Utility Functions
|
### Utility Functions
|
||||||
|
|
||||||
|
|||||||
@@ -489,6 +489,10 @@ def query_options(f):
|
|||||||
help="Search by end item date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601).",
|
help="Search by end item date, e.g. 2000-01-12T12:00:00, 2001-01-12T12:00:00-07:00, or 2000-12-31 (ISO 8601).",
|
||||||
type=DateTimeISO8601(),
|
type=DateTimeISO8601(),
|
||||||
),
|
),
|
||||||
|
o("--has-comment", is_flag=True, help="Search for photos that have comments."),
|
||||||
|
o("--no-comment", is_flag=True, help="Search for photos with no comments."),
|
||||||
|
o("--has-likes", is_flag=True, help="Search for photos that have likes."),
|
||||||
|
o("--no-likes", is_flag=True, help="Search for photos with no likes."),
|
||||||
]
|
]
|
||||||
for o in options[::-1]:
|
for o in options[::-1]:
|
||||||
f = o(f)
|
f = o(f)
|
||||||
@@ -529,7 +533,7 @@ def debug_dump(ctx, cli_obj, db, photos_library, dump, uuid, verbose_):
|
|||||||
|
|
||||||
global VERBOSE
|
global VERBOSE
|
||||||
VERBOSE = bool(verbose_)
|
VERBOSE = bool(verbose_)
|
||||||
|
|
||||||
db = get_photos_db(*photos_library, db, cli_obj.db)
|
db = get_photos_db(*photos_library, db, cli_obj.db)
|
||||||
if db is None:
|
if db is None:
|
||||||
click.echo(cli.commands["debug-dump"].get_help(ctx), err=True)
|
click.echo(cli.commands["debug-dump"].get_help(ctx), err=True)
|
||||||
@@ -982,6 +986,10 @@ def query(
|
|||||||
label,
|
label,
|
||||||
deleted,
|
deleted,
|
||||||
deleted_only,
|
deleted_only,
|
||||||
|
has_comment,
|
||||||
|
no_comment,
|
||||||
|
has_likes,
|
||||||
|
no_likes,
|
||||||
):
|
):
|
||||||
""" Query the Photos database using 1 or more search options;
|
""" Query the Photos database using 1 or more search options;
|
||||||
if more than one option is provided, they are treated as "AND"
|
if more than one option is provided, they are treated as "AND"
|
||||||
@@ -1026,6 +1034,8 @@ def query(
|
|||||||
(any(place), no_place),
|
(any(place), no_place),
|
||||||
(deleted, deleted_only),
|
(deleted, deleted_only),
|
||||||
(shared, not_shared),
|
(shared, not_shared),
|
||||||
|
(has_comment, no_comment),
|
||||||
|
(has_likes, no_likes),
|
||||||
]
|
]
|
||||||
# print help if no non-exclusive term or a double exclusive term is given
|
# print help if no non-exclusive term or a double exclusive term is given
|
||||||
if any(all(bb) for bb in exclusive) or not any(
|
if any(all(bb) for bb in exclusive) or not any(
|
||||||
@@ -1112,6 +1122,10 @@ def query(
|
|||||||
label=label,
|
label=label,
|
||||||
deleted=deleted,
|
deleted=deleted,
|
||||||
deleted_only=deleted_only,
|
deleted_only=deleted_only,
|
||||||
|
has_comment=has_comment,
|
||||||
|
no_comment=no_comment,
|
||||||
|
has_likes=has_likes,
|
||||||
|
no_likes=no_likes,
|
||||||
)
|
)
|
||||||
|
|
||||||
# below needed for to make CliRunner work for testing
|
# below needed for to make CliRunner work for testing
|
||||||
@@ -1395,6 +1409,10 @@ def export(
|
|||||||
edited_suffix,
|
edited_suffix,
|
||||||
place,
|
place,
|
||||||
no_place,
|
no_place,
|
||||||
|
has_comment,
|
||||||
|
no_comment,
|
||||||
|
has_likes,
|
||||||
|
no_likes,
|
||||||
no_extended_attributes,
|
no_extended_attributes,
|
||||||
label,
|
label,
|
||||||
deleted,
|
deleted,
|
||||||
@@ -1442,6 +1460,8 @@ def export(
|
|||||||
(skip_edited, skip_original_if_edited),
|
(skip_edited, skip_original_if_edited),
|
||||||
(export_as_hardlink, convert_to_jpeg),
|
(export_as_hardlink, convert_to_jpeg),
|
||||||
(shared, not_shared),
|
(shared, not_shared),
|
||||||
|
(has_comment, no_comment),
|
||||||
|
(has_likes, no_likes),
|
||||||
]
|
]
|
||||||
if any(all(bb) for bb in exclusive):
|
if any(all(bb) for bb in exclusive):
|
||||||
click.echo("Incompatible export options", err=True)
|
click.echo("Incompatible export options", err=True)
|
||||||
@@ -1580,6 +1600,10 @@ def export(
|
|||||||
label=label,
|
label=label,
|
||||||
deleted=deleted,
|
deleted=deleted,
|
||||||
deleted_only=deleted_only,
|
deleted_only=deleted_only,
|
||||||
|
has_comment=has_comment,
|
||||||
|
no_comment=no_comment,
|
||||||
|
has_likes=has_likes,
|
||||||
|
no_likes=no_likes,
|
||||||
)
|
)
|
||||||
|
|
||||||
if photos:
|
if photos:
|
||||||
@@ -1899,6 +1923,10 @@ def _query(
|
|||||||
label=None,
|
label=None,
|
||||||
deleted=False,
|
deleted=False,
|
||||||
deleted_only=False,
|
deleted_only=False,
|
||||||
|
has_comment=False,
|
||||||
|
no_comment=False,
|
||||||
|
has_likes=False,
|
||||||
|
no_likes=False,
|
||||||
):
|
):
|
||||||
""" run a query against PhotosDB to extract the photos based on user supply criteria
|
""" run a query against PhotosDB to extract the photos based on user supply criteria
|
||||||
used by query and export commands
|
used by query and export commands
|
||||||
@@ -2118,6 +2146,16 @@ def _query(
|
|||||||
if has_raw:
|
if has_raw:
|
||||||
photos = [p for p in photos if p.has_raw]
|
photos = [p for p in photos if p.has_raw]
|
||||||
|
|
||||||
|
if has_comment:
|
||||||
|
photos = [p for p in photos if p.comments]
|
||||||
|
elif no_comment:
|
||||||
|
photos = [p for p in photos if not p.comments]
|
||||||
|
|
||||||
|
if has_likes:
|
||||||
|
photos = [p for p in photos if p.likes]
|
||||||
|
elif no_likes:
|
||||||
|
photos = [p for p in photos if not p.likes]
|
||||||
|
|
||||||
return photos
|
return photos
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.36.0"
|
__version__ = "0.36.2"
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class ExifTool:
|
|||||||
ver = self.run_commands("-ver", no_file=True)
|
ver = self.run_commands("-ver", no_file=True)
|
||||||
return ver.decode("utf-8")
|
return ver.decode("utf-8")
|
||||||
|
|
||||||
def as_dict(self):
|
def asdict(self):
|
||||||
""" return dictionary of all EXIF tags and values from exiftool
|
""" return dictionary of all EXIF tags and values from exiftool
|
||||||
returns empty dict if no tags
|
returns empty dict if no tags
|
||||||
"""
|
"""
|
||||||
@@ -245,7 +245,7 @@ class ExifTool:
|
|||||||
|
|
||||||
def _read_exif(self):
|
def _read_exif(self):
|
||||||
""" read exif data from file """
|
""" read exif data from file """
|
||||||
data = self.as_dict()
|
data = self.asdict()
|
||||||
self.data = {k: v for k, v in data.items()}
|
self.data = {k: v for k, v in data.items()}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -66,10 +66,10 @@ class PersonInfo:
|
|||||||
# no faces
|
# no faces
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def json(self):
|
def asdict(self):
|
||||||
""" Returns JSON representation of class instance """
|
""" Returns dictionary representation of class instance """
|
||||||
keyphoto = self.keyphoto.uuid if self.keyphoto is not None else None
|
keyphoto = self.keyphoto.uuid if self.keyphoto is not None else None
|
||||||
person = {
|
return {
|
||||||
"uuid": self.uuid,
|
"uuid": self.uuid,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"displayname": self.display_name,
|
"displayname": self.display_name,
|
||||||
@@ -77,7 +77,10 @@ class PersonInfo:
|
|||||||
"facecount": self.facecount,
|
"facecount": self.facecount,
|
||||||
"keyphoto": keyphoto,
|
"keyphoto": keyphoto,
|
||||||
}
|
}
|
||||||
return json.dumps(person)
|
|
||||||
|
def json(self):
|
||||||
|
""" Returns JSON representation of class instance """
|
||||||
|
return json.dumps(self.asdict())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"PersonInfo(name={self.name}, display_name={self.display_name}, uuid={self.uuid}, facecount={self.facecount})"
|
return f"PersonInfo(name={self.name}, display_name={self.display_name}, uuid={self.uuid}, facecount={self.facecount})"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ PhotosDB.photos() returns a list of PhotoInfo objects
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@@ -950,22 +951,23 @@ class PhotoInfo:
|
|||||||
}
|
}
|
||||||
return yaml.dump(info, sort_keys=False)
|
return yaml.dump(info, sort_keys=False)
|
||||||
|
|
||||||
def json(self):
|
def asdict(self):
|
||||||
""" return JSON representation """
|
""" return dict representation """
|
||||||
|
|
||||||
date_modified_iso = (
|
|
||||||
self.date_modified.isoformat() if self.date_modified else None
|
|
||||||
)
|
|
||||||
folders = {album.title: album.folder_names for album in self.album_info}
|
folders = {album.title: album.folder_names for album in self.album_info}
|
||||||
exif = dataclasses.asdict(self.exif_info) if self.exif_info else {}
|
exif = dataclasses.asdict(self.exif_info) if self.exif_info else {}
|
||||||
place = self.place.as_dict() if self.place else {}
|
place = self.place.asdict() if self.place else {}
|
||||||
score = dataclasses.asdict(self.score) if self.score else {}
|
score = dataclasses.asdict(self.score) if self.score else {}
|
||||||
|
comments = [comment.asdict() for comment in self.comments]
|
||||||
|
likes = [like.asdict() for like in self.likes]
|
||||||
|
faces = [face.asdict() for face in self.face_info]
|
||||||
|
|
||||||
pic = {
|
return {
|
||||||
|
"library": self._db._library_path,
|
||||||
"uuid": self.uuid,
|
"uuid": self.uuid,
|
||||||
"filename": self.filename,
|
"filename": self.filename,
|
||||||
"original_filename": self.original_filename,
|
"original_filename": self.original_filename,
|
||||||
"date": self.date.isoformat(),
|
"date": self.date,
|
||||||
"description": self.description,
|
"description": self.description,
|
||||||
"title": self.title,
|
"title": self.title,
|
||||||
"keywords": self.keywords,
|
"keywords": self.keywords,
|
||||||
@@ -974,6 +976,7 @@ class PhotoInfo:
|
|||||||
"albums": self.albums,
|
"albums": self.albums,
|
||||||
"folders": folders,
|
"folders": folders,
|
||||||
"persons": self.persons,
|
"persons": self.persons,
|
||||||
|
"faces": faces,
|
||||||
"path": self.path,
|
"path": self.path,
|
||||||
"ismissing": self.ismissing,
|
"ismissing": self.ismissing,
|
||||||
"hasadjustments": self.hasadjustments,
|
"hasadjustments": self.hasadjustments,
|
||||||
@@ -987,12 +990,13 @@ class PhotoInfo:
|
|||||||
"isphoto": self.isphoto,
|
"isphoto": self.isphoto,
|
||||||
"ismovie": self.ismovie,
|
"ismovie": self.ismovie,
|
||||||
"uti": self.uti,
|
"uti": self.uti,
|
||||||
|
"uti_original": self.uti_original,
|
||||||
"burst": self.burst,
|
"burst": self.burst,
|
||||||
"live_photo": self.live_photo,
|
"live_photo": self.live_photo,
|
||||||
"path_live_photo": self.path_live_photo,
|
"path_live_photo": self.path_live_photo,
|
||||||
"iscloudasset": self.iscloudasset,
|
"iscloudasset": self.iscloudasset,
|
||||||
"incloud": self.incloud,
|
"incloud": self.incloud,
|
||||||
"date_modified": date_modified_iso,
|
"date_modified": self.date_modified,
|
||||||
"portrait": self.portrait,
|
"portrait": self.portrait,
|
||||||
"screenshot": self.screenshot,
|
"screenshot": self.screenshot,
|
||||||
"slow_mo": self.slow_mo,
|
"slow_mo": self.slow_mo,
|
||||||
@@ -1001,6 +1005,8 @@ class PhotoInfo:
|
|||||||
"selfie": self.selfie,
|
"selfie": self.selfie,
|
||||||
"panorama": self.panorama,
|
"panorama": self.panorama,
|
||||||
"has_raw": self.has_raw,
|
"has_raw": self.has_raw,
|
||||||
|
"israw": self.israw,
|
||||||
|
"raw_original": self.raw_original,
|
||||||
"uti_raw": self.uti_raw,
|
"uti_raw": self.uti_raw,
|
||||||
"path_raw": self.path_raw,
|
"path_raw": self.path_raw,
|
||||||
"place": place,
|
"place": place,
|
||||||
@@ -1014,8 +1020,17 @@ class PhotoInfo:
|
|||||||
"original_width": self.original_width,
|
"original_width": self.original_width,
|
||||||
"original_orientation": self.original_orientation,
|
"original_orientation": self.original_orientation,
|
||||||
"original_filesize": self.original_filesize,
|
"original_filesize": self.original_filesize,
|
||||||
|
"comments": comments,
|
||||||
|
"likes": likes,
|
||||||
}
|
}
|
||||||
return json.dumps(pic)
|
|
||||||
|
def json(self):
|
||||||
|
""" Return JSON representation """
|
||||||
|
def default(o):
|
||||||
|
if isinstance(o, (datetime.date, datetime.datetime)):
|
||||||
|
return o.isoformat()
|
||||||
|
|
||||||
|
return json.dumps(self.asdict(), sort_keys=True, default=default)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
""" Compare two PhotoInfo objects for equality """
|
""" Compare two PhotoInfo objects for equality """
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
""" PhotosDB method for processing comments and likes on shared photos.
|
""" PhotosDB method for processing comments and likes on shared photos.
|
||||||
Do not import this module directly """
|
Do not import this module directly """
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
import datetime
|
import datetime
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
@@ -30,6 +31,9 @@ class CommentInfo:
|
|||||||
ismine: bool
|
ismine: bool
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return dataclasses.asdict(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LikeInfo:
|
class LikeInfo:
|
||||||
@@ -39,6 +43,9 @@ class LikeInfo:
|
|||||||
user: str
|
user: str
|
||||||
ismine: bool
|
ismine: bool
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return dataclasses.asdict(self)
|
||||||
|
|
||||||
|
|
||||||
# The following methods do not get imported into PhotosDB
|
# The following methods do not get imported into PhotosDB
|
||||||
# but will get called by _process_comments
|
# but will get called by _process_comments
|
||||||
|
|||||||
@@ -1809,7 +1809,8 @@ class PhotosDB:
|
|||||||
ZADDITIONALASSETATTRIBUTES.ZORIGINALWIDTH,
|
ZADDITIONALASSETATTRIBUTES.ZORIGINALWIDTH,
|
||||||
ZADDITIONALASSETATTRIBUTES.ZORIGINALORIENTATION,
|
ZADDITIONALASSETATTRIBUTES.ZORIGINALORIENTATION,
|
||||||
ZADDITIONALASSETATTRIBUTES.ZORIGINALFILESIZE,
|
ZADDITIONALASSETATTRIBUTES.ZORIGINALFILESIZE,
|
||||||
{depth_state}
|
{depth_state},
|
||||||
|
{asset_table}.ZADJUSTMENTTIMESTAMP
|
||||||
FROM {asset_table}
|
FROM {asset_table}
|
||||||
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = {asset_table}.Z_PK
|
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = {asset_table}.Z_PK
|
||||||
ORDER BY {asset_table}.ZUUID """
|
ORDER BY {asset_table}.ZUUID """
|
||||||
@@ -1853,6 +1854,7 @@ class PhotosDB:
|
|||||||
# 34 ZADDITIONALASSETATTRIBUTES.ZORIGINALORIENTATION,
|
# 34 ZADDITIONALASSETATTRIBUTES.ZORIGINALORIENTATION,
|
||||||
# 35 ZADDITIONALASSETATTRIBUTES.ZORIGINALFILESIZE
|
# 35 ZADDITIONALASSETATTRIBUTES.ZORIGINALFILESIZE
|
||||||
# 36 ZGENERICASSET.ZDEPTHSTATES / ZASSET.ZDEPTHTYPE
|
# 36 ZGENERICASSET.ZDEPTHSTATES / ZASSET.ZDEPTHTYPE
|
||||||
|
# 37 ZGENERICASSET.ZADJUSTMENTTIMESTAMP -- when was photo edited?
|
||||||
|
|
||||||
for row in c:
|
for row in c:
|
||||||
uuid = row[0]
|
uuid = row[0]
|
||||||
@@ -1866,9 +1868,9 @@ class PhotosDB:
|
|||||||
# There are sometimes negative values for lastmodifieddate in the database
|
# There are sometimes negative values for lastmodifieddate in the database
|
||||||
# I don't know what these mean but they will raise exception in datetime if
|
# I don't know what these mean but they will raise exception in datetime if
|
||||||
# not accounted for
|
# not accounted for
|
||||||
info["lastmodifieddate_timestamp"] = row[4]
|
info["lastmodifieddate_timestamp"] = row[37]
|
||||||
try:
|
try:
|
||||||
info["lastmodifieddate"] = datetime.fromtimestamp(row[4] + TIME_DELTA)
|
info["lastmodifieddate"] = datetime.fromtimestamp(row[37] + TIME_DELTA)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
info["lastmodifieddate"] = None
|
info["lastmodifieddate"] = None
|
||||||
except TypeError:
|
except TypeError:
|
||||||
@@ -2282,7 +2284,7 @@ class PhotosDB:
|
|||||||
# process shared comments/likes
|
# process shared comments/likes
|
||||||
verbose("Processing comments and likes for shared photos.")
|
verbose("Processing comments and likes for shared photos.")
|
||||||
self._process_comments()
|
self._process_comments()
|
||||||
|
|
||||||
# done processing, dump debug data if requested
|
# done processing, dump debug data if requested
|
||||||
verbose("Done processing details from Photos library.")
|
verbose("Done processing details from Photos library.")
|
||||||
if _debug():
|
if _debug():
|
||||||
|
|||||||
@@ -30,33 +30,34 @@ TEMPLATE_SUBSTITUTIONS = {
|
|||||||
"{title}": "Title of the photo",
|
"{title}": "Title of the photo",
|
||||||
"{descr}": "Description of the photo",
|
"{descr}": "Description of the photo",
|
||||||
"{created.date}": "Photo's creation date in ISO format, e.g. '2020-03-22'",
|
"{created.date}": "Photo's creation date in ISO format, e.g. '2020-03-22'",
|
||||||
"{created.year}": "4-digit year of file creation time",
|
"{created.year}": "4-digit year of photo creation time",
|
||||||
"{created.yy}": "2-digit year of file creation time",
|
"{created.yy}": "2-digit year of photo creation time",
|
||||||
"{created.mm}": "2-digit month of the file creation time (zero padded)",
|
"{created.mm}": "2-digit month of the photo creation time (zero padded)",
|
||||||
"{created.month}": "Month name in user's locale of the file creation time",
|
"{created.month}": "Month name in user's locale of the photo creation time",
|
||||||
"{created.mon}": "Month abbreviation in the user's locale of the file creation time",
|
"{created.mon}": "Month abbreviation in the user's locale of the photo creation time",
|
||||||
"{created.dd}": "2-digit day of the month (zero padded) of file creation time",
|
"{created.dd}": "2-digit day of the month (zero padded) of photo creation time",
|
||||||
"{created.dow}": "Day of week in user's locale of the file creation time",
|
"{created.dow}": "Day of week in user's locale of the photo creation time",
|
||||||
"{created.doy}": "3-digit day of year (e.g Julian day) of file creation time, starting from 1 (zero padded)",
|
"{created.doy}": "3-digit day of year (e.g Julian day) of photo creation time, starting from 1 (zero padded)",
|
||||||
"{created.hour}": "2-digit hour of the file creation time",
|
"{created.hour}": "2-digit hour of the photo creation time",
|
||||||
"{created.min}": "2-digit minute of the file creation time",
|
"{created.min}": "2-digit minute of the photo creation time",
|
||||||
"{created.sec}": "2-digit second of the file creation time",
|
"{created.sec}": "2-digit second of the photo creation time",
|
||||||
"{created.strftime}": "Apply strftime template to file creation date/time. Should be used in form "
|
"{created.strftime}": "Apply strftime template to file creation date/time. Should be used in form "
|
||||||
+ "{created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. "
|
+ "{created.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. "
|
||||||
+ "{created.strftime,%Y-%U} would result in year-week number of year: '2020-23'. "
|
+ "{created.strftime,%Y-%U} would result in year-week number of year: '2020-23'. "
|
||||||
+ "If used with no template will return null value. "
|
+ "If used with no template will return null value. "
|
||||||
+ "See https://strftime.org/ for help on strftime templates.",
|
+ "See https://strftime.org/ for help on strftime templates.",
|
||||||
"{modified.date}": "Photo's modification date in ISO format, e.g. '2020-03-22'",
|
"{modified.date}": "Photo's modification date in ISO format, e.g. '2020-03-22'",
|
||||||
"{modified.year}": "4-digit year of file modification time",
|
"{modified.year}": "4-digit year of photo modification time",
|
||||||
"{modified.yy}": "2-digit year of file modification time",
|
"{modified.yy}": "2-digit year of photo modification time",
|
||||||
"{modified.mm}": "2-digit month of the file modification time (zero padded)",
|
"{modified.mm}": "2-digit month of the photo modification time (zero padded)",
|
||||||
"{modified.month}": "Month name in user's locale of the file modification time",
|
"{modified.month}": "Month name in user's locale of the photo modification time",
|
||||||
"{modified.mon}": "Month abbreviation in the user's locale of the file modification time",
|
"{modified.mon}": "Month abbreviation in the user's locale of the photo modification time",
|
||||||
"{modified.dd}": "2-digit day of the month (zero padded) of the file modification time",
|
"{modified.dd}": "2-digit day of the month (zero padded) of the photo modification time",
|
||||||
"{modified.doy}": "3-digit day of year (e.g Julian day) of file modification time, starting from 1 (zero padded)",
|
"{modified.dow}": "Day of week in user's locale of the photo modification time",
|
||||||
"{modified.hour}": "2-digit hour of the file modification time",
|
"{modified.doy}": "3-digit day of year (e.g Julian day) of photo modification time, starting from 1 (zero padded)",
|
||||||
"{modified.min}": "2-digit minute of the file modification time",
|
"{modified.hour}": "2-digit hour of the photo modification time",
|
||||||
"{modified.sec}": "2-digit second of the file modification time",
|
"{modified.min}": "2-digit minute of the photo modification time",
|
||||||
|
"{modified.sec}": "2-digit second of the photo modification time",
|
||||||
# "{modified.strftime}": "Apply strftime template to file modification date/time. Should be used in form "
|
# "{modified.strftime}": "Apply strftime template to file modification date/time. Should be used in form "
|
||||||
# + "{modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. "
|
# + "{modified.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. "
|
||||||
# + "{modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. "
|
# + "{modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. "
|
||||||
@@ -102,7 +103,7 @@ TEMPLATE_SUBSTITUTIONS_MULTI_VALUED = {
|
|||||||
"{person}": "Person(s) / face(s) in a photo",
|
"{person}": "Person(s) / face(s) in a photo",
|
||||||
"{label}": "Image categorization label associated with a photo (Photos 5 only)",
|
"{label}": "Image categorization label associated with a photo (Photos 5 only)",
|
||||||
"{label_normalized}": "All lower case version of 'label' (Photos 5 only)",
|
"{label_normalized}": "All lower case version of 'label' (Photos 5 only)",
|
||||||
"{comment}": "Comment(s) on shared Photos; format is 'Person name: comment text' (Photos 5 only)"
|
"{comment}": "Comment(s) on shared Photos; format is 'Person name: comment text' (Photos 5 only)",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Just the multi-valued substitution names without the braces
|
# Just the multi-valued substitution names without the braces
|
||||||
@@ -445,6 +446,12 @@ class PhotoTemplate:
|
|||||||
if self.photo.date_modified
|
if self.photo.date_modified
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
elif field == "modified.dow":
|
||||||
|
value = (
|
||||||
|
DateTimeFormatter(self.photo.date_modified).dow
|
||||||
|
if self.photo.date_modified
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif field == "modified.doy":
|
elif field == "modified.doy":
|
||||||
value = (
|
value = (
|
||||||
DateTimeFormatter(self.photo.date_modified).doy
|
DateTimeFormatter(self.photo.date_modified).doy
|
||||||
@@ -639,7 +646,9 @@ class PhotoTemplate:
|
|||||||
else:
|
else:
|
||||||
values.append(album.title)
|
values.append(album.title)
|
||||||
elif field == "comment":
|
elif field == "comment":
|
||||||
values = [f"{comment.user}: {comment.text}" for comment in self.photo.comments]
|
values = [
|
||||||
|
f"{comment.user}: {comment.text}" for comment in self.photo.comments
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unhandled template value: {field}")
|
raise ValueError(f"Unhandled template value: {field}")
|
||||||
|
|
||||||
|
|||||||
@@ -491,7 +491,7 @@ class PlaceInfo4(PlaceInfo):
|
|||||||
}
|
}
|
||||||
return "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")"
|
return "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")"
|
||||||
|
|
||||||
def as_dict(self):
|
def asdict(self):
|
||||||
return {
|
return {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"names": self.names._asdict(),
|
"names": self.names._asdict(),
|
||||||
@@ -634,7 +634,7 @@ class PlaceInfo5(PlaceInfo):
|
|||||||
}
|
}
|
||||||
return "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")"
|
return "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")"
|
||||||
|
|
||||||
def as_dict(self):
|
def asdict(self):
|
||||||
return {
|
return {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"names": self.names._asdict(),
|
"names": self.names._asdict(),
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>LibrarySchemaVersion</key>
|
||||||
|
<integer>5001</integer>
|
||||||
|
<key>MetaSchemaVersion</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
tests/Test-10.15.7.photoslibrary/database/Photos.sqlite
Normal file
BIN
tests/Test-10.15.7.photoslibrary/database/Photos.sqlite-shm
Normal file
BIN
tests/Test-10.15.7.photoslibrary/database/Photos.sqlite-wal
Normal file
16
tests/Test-10.15.7.photoslibrary/database/Photos.sqlite.lock
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>hostname</key>
|
||||||
|
<string>Rhets-MacBook-Pro.local</string>
|
||||||
|
<key>hostuuid</key>
|
||||||
|
<string>9575E48B-8D5F-5654-ABAC-4431B1167324</string>
|
||||||
|
<key>pid</key>
|
||||||
|
<integer>1797</integer>
|
||||||
|
<key>processname</key>
|
||||||
|
<string>photolibraryd</string>
|
||||||
|
<key>uid</key>
|
||||||
|
<integer>501</integer>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
tests/Test-10.15.7.photoslibrary/database/metaSchema.db
Normal file
BIN
tests/Test-10.15.7.photoslibrary/database/photos.db
Normal file
BIN
tests/Test-10.15.7.photoslibrary/database/search/psi.sqlite
Normal file
BIN
tests/Test-10.15.7.photoslibrary/database/search/psi.sqlite-shm
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>BlacklistedMeaningsByMeaning</key>
|
||||||
|
<dict/>
|
||||||
|
<key>MePersonUUID</key>
|
||||||
|
<string>39488755-78C0-40B2-B378-EDA280E1823C</string>
|
||||||
|
<key>SceneWhitelist</key>
|
||||||
|
<array>
|
||||||
|
<string>Graduation</string>
|
||||||
|
<string>Aquarium</string>
|
||||||
|
<string>Food</string>
|
||||||
|
<string>Ice Skating</string>
|
||||||
|
<string>Mountain</string>
|
||||||
|
<string>Cliff</string>
|
||||||
|
<string>Basketball</string>
|
||||||
|
<string>Tennis</string>
|
||||||
|
<string>Jewelry</string>
|
||||||
|
<string>Cheese</string>
|
||||||
|
<string>Softball</string>
|
||||||
|
<string>Football</string>
|
||||||
|
<string>Circus</string>
|
||||||
|
<string>Jet Ski</string>
|
||||||
|
<string>Playground</string>
|
||||||
|
<string>Carousel</string>
|
||||||
|
<string>Paint Ball</string>
|
||||||
|
<string>Windsurfing</string>
|
||||||
|
<string>Sailboat</string>
|
||||||
|
<string>Sunbathing</string>
|
||||||
|
<string>Dam</string>
|
||||||
|
<string>Fireplace</string>
|
||||||
|
<string>Flower</string>
|
||||||
|
<string>Scuba</string>
|
||||||
|
<string>Hiking</string>
|
||||||
|
<string>Cetacean</string>
|
||||||
|
<string>Pier</string>
|
||||||
|
<string>Bowling</string>
|
||||||
|
<string>Snowboarding</string>
|
||||||
|
<string>Zoo</string>
|
||||||
|
<string>Snowmobile</string>
|
||||||
|
<string>Theater</string>
|
||||||
|
<string>Boat</string>
|
||||||
|
<string>Casino</string>
|
||||||
|
<string>Car</string>
|
||||||
|
<string>Diving</string>
|
||||||
|
<string>Cycling</string>
|
||||||
|
<string>Musical Instrument</string>
|
||||||
|
<string>Board Game</string>
|
||||||
|
<string>Castle</string>
|
||||||
|
<string>Sunset Sunrise</string>
|
||||||
|
<string>Martial Arts</string>
|
||||||
|
<string>Motocross</string>
|
||||||
|
<string>Submarine</string>
|
||||||
|
<string>Cat</string>
|
||||||
|
<string>Snow</string>
|
||||||
|
<string>Kiteboarding</string>
|
||||||
|
<string>Squash</string>
|
||||||
|
<string>Geyser</string>
|
||||||
|
<string>Music</string>
|
||||||
|
<string>Archery</string>
|
||||||
|
<string>Desert</string>
|
||||||
|
<string>Blackjack</string>
|
||||||
|
<string>Fireworks</string>
|
||||||
|
<string>Sportscar</string>
|
||||||
|
<string>Feline</string>
|
||||||
|
<string>Soccer</string>
|
||||||
|
<string>Museum</string>
|
||||||
|
<string>Baby</string>
|
||||||
|
<string>Fencing</string>
|
||||||
|
<string>Railroad</string>
|
||||||
|
<string>Nascar</string>
|
||||||
|
<string>Sky Surfing</string>
|
||||||
|
<string>Bird</string>
|
||||||
|
<string>Games</string>
|
||||||
|
<string>Baseball</string>
|
||||||
|
<string>Dressage</string>
|
||||||
|
<string>Snorkeling</string>
|
||||||
|
<string>Pyramid</string>
|
||||||
|
<string>Kite</string>
|
||||||
|
<string>Rowboat</string>
|
||||||
|
<string>Golf</string>
|
||||||
|
<string>Watersports</string>
|
||||||
|
<string>Lightning</string>
|
||||||
|
<string>Canyon</string>
|
||||||
|
<string>Auditorium</string>
|
||||||
|
<string>Night Sky</string>
|
||||||
|
<string>Karaoke</string>
|
||||||
|
<string>Skiing</string>
|
||||||
|
<string>Parade</string>
|
||||||
|
<string>Forest</string>
|
||||||
|
<string>Hot Air Balloon</string>
|
||||||
|
<string>Dragon Parade</string>
|
||||||
|
<string>Easter Egg</string>
|
||||||
|
<string>Monument</string>
|
||||||
|
<string>Jungle</string>
|
||||||
|
<string>Thanksgiving</string>
|
||||||
|
<string>Jockey Horse</string>
|
||||||
|
<string>Stadium</string>
|
||||||
|
<string>Airplane</string>
|
||||||
|
<string>Ballet</string>
|
||||||
|
<string>Yoga</string>
|
||||||
|
<string>Coral Reef</string>
|
||||||
|
<string>Skating</string>
|
||||||
|
<string>Wrestling</string>
|
||||||
|
<string>Bicycle</string>
|
||||||
|
<string>Tattoo</string>
|
||||||
|
<string>Amusement Park</string>
|
||||||
|
<string>Canoe</string>
|
||||||
|
<string>Cheerleading</string>
|
||||||
|
<string>Ping Pong</string>
|
||||||
|
<string>Fishing</string>
|
||||||
|
<string>Magic</string>
|
||||||
|
<string>Reptile</string>
|
||||||
|
<string>Winter Sport</string>
|
||||||
|
<string>Waterfall</string>
|
||||||
|
<string>Train</string>
|
||||||
|
<string>Bonsai</string>
|
||||||
|
<string>Surfing</string>
|
||||||
|
<string>Dog</string>
|
||||||
|
<string>Cake</string>
|
||||||
|
<string>Sledding</string>
|
||||||
|
<string>Sandcastle</string>
|
||||||
|
<string>Glacier</string>
|
||||||
|
<string>Lighthouse</string>
|
||||||
|
<string>Equestrian</string>
|
||||||
|
<string>Rafting</string>
|
||||||
|
<string>Shore</string>
|
||||||
|
<string>Hockey</string>
|
||||||
|
<string>Santa Claus</string>
|
||||||
|
<string>Formula One Car</string>
|
||||||
|
<string>Sport</string>
|
||||||
|
<string>Vehicle</string>
|
||||||
|
<string>Boxing</string>
|
||||||
|
<string>Rollerskating</string>
|
||||||
|
<string>Underwater</string>
|
||||||
|
<string>Orchestra</string>
|
||||||
|
<string>Carnival</string>
|
||||||
|
<string>Rocket</string>
|
||||||
|
<string>Skateboarding</string>
|
||||||
|
<string>Helicopter</string>
|
||||||
|
<string>Performance</string>
|
||||||
|
<string>Oktoberfest</string>
|
||||||
|
<string>Water Polo</string>
|
||||||
|
<string>Skate Park</string>
|
||||||
|
<string>Animal</string>
|
||||||
|
<string>Nightclub</string>
|
||||||
|
<string>String Instrument</string>
|
||||||
|
<string>Dinosaur</string>
|
||||||
|
<string>Gymnastics</string>
|
||||||
|
<string>Cricket</string>
|
||||||
|
<string>Volcano</string>
|
||||||
|
<string>Lake</string>
|
||||||
|
<string>Aurora</string>
|
||||||
|
<string>Dancing</string>
|
||||||
|
<string>Concert</string>
|
||||||
|
<string>Rock Climbing</string>
|
||||||
|
<string>Hang Glider</string>
|
||||||
|
<string>Rodeo</string>
|
||||||
|
<string>Fish</string>
|
||||||
|
<string>Art</string>
|
||||||
|
<string>Motorcycle</string>
|
||||||
|
<string>Volleyball</string>
|
||||||
|
<string>Wake Boarding</string>
|
||||||
|
<string>Badminton</string>
|
||||||
|
<string>Motor Sport</string>
|
||||||
|
<string>Sumo</string>
|
||||||
|
<string>Parasailing</string>
|
||||||
|
<string>Skydiving</string>
|
||||||
|
<string>Kickboxing</string>
|
||||||
|
<string>Pinata</string>
|
||||||
|
<string>Foosball</string>
|
||||||
|
<string>Go Kart</string>
|
||||||
|
<string>Poker</string>
|
||||||
|
<string>Kayak</string>
|
||||||
|
<string>Swimming</string>
|
||||||
|
<string>Atv</string>
|
||||||
|
<string>Beach</string>
|
||||||
|
<string>Dartboard</string>
|
||||||
|
<string>Athletics</string>
|
||||||
|
<string>Camping</string>
|
||||||
|
<string>Tornado</string>
|
||||||
|
<string>Billiards</string>
|
||||||
|
<string>Rugby</string>
|
||||||
|
<string>Airshow</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>insertAlbum</key>
|
||||||
|
<array/>
|
||||||
|
<key>insertAsset</key>
|
||||||
|
<array/>
|
||||||
|
<key>insertHighlight</key>
|
||||||
|
<array/>
|
||||||
|
<key>insertMemory</key>
|
||||||
|
<array/>
|
||||||
|
<key>insertMoment</key>
|
||||||
|
<array/>
|
||||||
|
<key>removeAlbum</key>
|
||||||
|
<array/>
|
||||||
|
<key>removeAsset</key>
|
||||||
|
<array/>
|
||||||
|
<key>removeHighlight</key>
|
||||||
|
<array/>
|
||||||
|
<key>removeMemory</key>
|
||||||
|
<array/>
|
||||||
|
<key>removeMoment</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>embeddingVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>localeIdentifier</key>
|
||||||
|
<string>en_US</string>
|
||||||
|
<key>sceneTaxonomySHA</key>
|
||||||
|
<string>87914a047c69fbe8013fad2c70fa70c6c03b08b56190fe4054c880e6b9f57cc3</string>
|
||||||
|
<key>searchIndexVersion</key>
|
||||||
|
<string>10</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
After Width: | Height: | Size: 574 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 500 KiB |
|
After Width: | Height: | Size: 524 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 528 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 450 KiB |
|
After Width: | Height: | Size: 541 KiB |
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>MigrationService</key>
|
||||||
|
<dict>
|
||||||
|
<key>State</key>
|
||||||
|
<integer>4</integer>
|
||||||
|
</dict>
|
||||||
|
<key>MigrationService.LastCompletedTask</key>
|
||||||
|
<integer>12</integer>
|
||||||
|
<key>MigrationService.ValidationCounts</key>
|
||||||
|
<dict>
|
||||||
|
<key>MigrationDetectedFaceprint</key>
|
||||||
|
<integer>6</integer>
|
||||||
|
<key>MigrationManagedAsset</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>MigrationSceneClassification</key>
|
||||||
|
<integer>44</integer>
|
||||||
|
<key>MigrationUnmanagedAdjustment</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>RDVersion.cloudLocalState.CPLIsNotPushed</key>
|
||||||
|
<integer>7</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CollapsedSidebarSectionIdentifiers</key>
|
||||||
|
<array/>
|
||||||
|
<key>ExpandedSidebarItemIdentifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>92D68107-B6C7-453B-96D2-97B0F26D5B8B/L0/020</string>
|
||||||
|
<string>88A5F8B8-5B9A-43C7-BB85-3952B81580EB/L0/020</string>
|
||||||
|
<string>29EF7A97-7E76-4D5F-A5E0-CC0A93E8524C/L0/020</string>
|
||||||
|
<string>2C2AF115-BD1D-4434-A747-D1C8BD8E2045/L0/020</string>
|
||||||
|
<string>CB051A4C-2CB7-4B90-B59B-08CC4D0C2823/L0/020</string>
|
||||||
|
</array>
|
||||||
|
<key>Photos</key>
|
||||||
|
<dict>
|
||||||
|
<key>CollapsedSidebarSectionIdentifiers</key>
|
||||||
|
<array/>
|
||||||
|
<key>ExpandedSidebarItemIdentifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>TopLevelAlbums</string>
|
||||||
|
<string>TopLevelSlideshows</string>
|
||||||
|
</array>
|
||||||
|
<key>IPXWorkspaceControllerZoomLevelsKey</key>
|
||||||
|
<dict>
|
||||||
|
<key>kZoomLevelIdentifierAlbums</key>
|
||||||
|
<integer>7</integer>
|
||||||
|
<key>kZoomLevelIdentifierVersions</key>
|
||||||
|
<integer>7</integer>
|
||||||
|
</dict>
|
||||||
|
<key>lastAddToDestination</key>
|
||||||
|
<dict>
|
||||||
|
<key>key</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>lastKnownDisplayName</key>
|
||||||
|
<string>September 28, 2018</string>
|
||||||
|
<key>type</key>
|
||||||
|
<string>album</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>DFFKmHt3Tk+AGzZLe2Xq+g</string>
|
||||||
|
</dict>
|
||||||
|
<key>lastKnownItemCounts</key>
|
||||||
|
<dict>
|
||||||
|
<key>other</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>photos</key>
|
||||||
|
<integer>7</integer>
|
||||||
|
<key>videos</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>BackgroundHighlightCollection</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
<key>BackgroundHighlightEnrichment</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
<key>BackgroundJobAssetRevGeocode</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
<key>BackgroundJobSearch</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
<key>BackgroundPeopleSuggestion</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
<key>BackgroundUserBehaviorProcessor</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
<key>PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey</key>
|
||||||
|
<date>2020-10-17T23:45:33Z</date>
|
||||||
|
<key>PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate</key>
|
||||||
|
<date>2020-10-17T23:45:24Z</date>
|
||||||
|
<key>PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate</key>
|
||||||
|
<date>2020-10-17T23:45:26Z</date>
|
||||||
|
<key>SiriPortraitDonation</key>
|
||||||
|
<date>2020-10-17T23:45:25Z</date>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>revgeoprovider</key>
|
||||||
|
<string>7618</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NumberOfFacesProcessedOnLastRun</key>
|
||||||
|
<integer>11</integer>
|
||||||
|
<key>ProcessedInQuiescentState</key>
|
||||||
|
<true/>
|
||||||
|
<key>SuggestedMeIdentifier</key>
|
||||||
|
<string></string>
|
||||||
|
<key>Version</key>
|
||||||
|
<integer>4</integer>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>FaceIDModelLastGenerationKey</key>
|
||||||
|
<date>2020-10-17T23:45:32Z</date>
|
||||||
|
<key>LastContactClassificationKey</key>
|
||||||
|
<date>2020-10-17T23:45:54Z</date>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PVClustererBringUpState</key>
|
||||||
|
<integer>50</integer>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IncrementalPersonProcessingStage</key>
|
||||||
|
<integer>6</integer>
|
||||||
|
<key>PersonBuilderLastMinimumFaceGroupSizeForCreatingMergeCandidates</key>
|
||||||
|
<integer>15</integer>
|
||||||
|
<key>PersonBuilderMergeCandidatesEnabled</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||