Compare commits

...

14 Commits

Author SHA1 Message Date
Rhet Turnbull
11459d1da4 Updated --exiftool to set dates/times as Photos does, issue #247 2020-10-31 22:11:00 -07:00
Rhet Turnbull
fd14242022 Version bump 2020-10-31 21:05:42 -07:00
Rhet Turnbull
6ac311199e Partial fix for issue #247 on Mojave 2020-10-31 21:04:44 -07:00
Rhet Turnbull
13df6a2395 Add @hhoeck as a contributor 2020-10-31 10:10:21 -07:00
Rhet Turnbull
28dce72a67 Add @agprimatic as a contributor 2020-10-31 10:10:07 -07:00
Rhet Turnbull
e5548ed160 Add @grundsch as a contributor 2020-10-31 10:09:55 -07:00
Rhet Turnbull
5714509765 Add @dethi as a contributor 2020-10-31 10:09:24 -07:00
Rhet Turnbull
46b62af4e2 Add @jystervinou as a contributor 2020-10-31 10:09:06 -07:00
Rhet Turnbull
01ea88fe57 Add @dmd as a contributor 2020-10-31 10:08:43 -07:00
Rhet Turnbull
e6d043ab65 Add @hshore29 as a contributor 2020-10-31 10:08:18 -07:00
Rhet Turnbull
5b1174db5d Add @PabloKohan as a contributor 2020-10-31 10:07:12 -07:00
Rhet Turnbull
9cff8e89c6 Add @mwort as a contributor 2020-10-31 10:03:13 -07:00
Rhet Turnbull
1553563629 Add @britiscurious as a contributor 2020-10-31 10:00:42 -07:00
Rhet Turnbull
db262f58b0 Updated CHANGELOG.md 2020-10-31 09:01:53 -07:00
16 changed files with 303 additions and 73 deletions

106
.all-contributorsrc Normal file
View File

@@ -0,0 +1,106 @@
{
"projectName": "osxphotos",
"projectOwner": "RhetTbull",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "none",
"contributors": [
{
"login": "britiscurious",
"name": "britiscurious",
"avatar_url": "https://avatars1.githubusercontent.com/u/25646439?v=4",
"profile": "https://github.com/britiscurious",
"contributions": [
"doc",
"code"
]
},
{
"login": "mwort",
"name": "Michel Wortmann",
"avatar_url": "https://avatars3.githubusercontent.com/u/8170417?v=4",
"profile": "https://github.com/mwort",
"contributions": [
"code"
]
},
{
"login": "PabloKohan",
"name": "Pablo 'merKur' Kohan",
"avatar_url": "https://avatars3.githubusercontent.com/u/8790976?v=4",
"profile": "https://github.com/PabloKohan",
"contributions": [
"code"
]
},
{
"login": "hshore29",
"name": "hshore29",
"avatar_url": "https://avatars2.githubusercontent.com/u/7023497?v=4",
"profile": "https://github.com/hshore29",
"contributions": [
"code"
]
},
{
"login": "dmd",
"name": "Daniel M. Drucker",
"avatar_url": "https://avatars0.githubusercontent.com/u/41439?v=4",
"profile": "http://3e.org/",
"contributions": [
"code"
]
},
{
"login": "jystervinou",
"name": "Jean-Yves Stervinou",
"avatar_url": "https://avatars3.githubusercontent.com/u/132356?v=4",
"profile": "https://github.com/jystervinou",
"contributions": [
"code"
]
},
{
"login": "dethi",
"name": "Thibault Deutsch",
"avatar_url": "https://avatars2.githubusercontent.com/u/1011520?v=4",
"profile": "https://dethi.me/",
"contributions": [
"code"
]
},
{
"login": "grundsch",
"name": "grundsch",
"avatar_url": "https://avatars0.githubusercontent.com/u/3874928?v=4",
"profile": "https://github.com/grundsch",
"contributions": [
"code"
]
},
{
"login": "agprimatic",
"name": "Ag Primatic",
"avatar_url": "https://avatars1.githubusercontent.com/u/4685054?v=4",
"profile": "https://github.com/agprimatic",
"contributions": [
"code"
]
},
{
"login": "hhoeck",
"name": "Horst Höck",
"avatar_url": "https://avatars1.githubusercontent.com/u/6313998?v=4",
"profile": "https://github.com/hhoeck",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7
}

View File

@@ -4,6 +4,20 @@ 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.2](https://github.com/RhetTbull/osxphotos/compare/v0.36.1...v0.36.2)
> 31 October 2020
- Fixed handling of date_modified for Catalina, issue #247 [`0cce234`](https://github.com/RhetTbull/osxphotos/commit/0cce234a8cbba63dc1cba439c06fe9de078ff480)
#### [v0.36.1](https://github.com/RhetTbull/osxphotos/compare/v0.36.0...v0.36.1)
> 30 October 2020
- Added --has-comment/--has-likes to CLI, issue #240 [`c5dba8c`](https://github.com/RhetTbull/osxphotos/commit/c5dba8c89bba35d7a77e087b180b2a3d7b94280a)
- Cleaned up as_dict/asdict, issue #144, #188 [`603dabb`](https://github.com/RhetTbull/osxphotos/commit/603dabb8f420a89e993d5aadcd3a5614bbb262dd)
- Updated README.md [`d16932d`](https://github.com/RhetTbull/osxphotos/commit/d16932d0fd8d160ccf44e9842329d5933dc25b36)
#### [v0.36.0](https://github.com/RhetTbull/osxphotos/compare/v0.35.7...v0.36.0) #### [v0.36.0](https://github.com/RhetTbull/osxphotos/compare/v0.35.7...v0.36.0)
> 26 October 2020 > 26 October 2020

View File

@@ -1,8 +1,10 @@
# OSXPhotos # OSXPhotos
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Python package](https://github.com/RhetTbull/osxphotos/workflows/Python%20package/badge.svg) [![Python package](https://github.com/RhetTbull/osxphotos/workflows/Python%20package/badge.svg)](https://github.com/RhetTbull/osxphotos/workflows/Python%20package/badge.svg)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
- [OSXPhotos](#osxphotos) - [OSXPhotos](#osxphotos)
* [What is osxphotos?](#what-is-osxphotos) * [What is osxphotos?](#what-is-osxphotos)
@@ -1988,21 +1990,37 @@ If you have an interesting example that shows usage of this package, submit an i
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 since data point and I'm certain there are issues lurking in various edge cases I haven't discovered yet.
### Contributors
Thank-you to the following people who have contributed to improving osxphotos! If I've inadvertently left you off, please open an issue or send me a note. ### Contributors ✨
- [britiscurious](https://github.com/britiscurious) Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
- [Michel Wortmann](https://github.com/mwort)
- [hshore29](https://github.com/hshore29)
- [Pablo 'merKur' Kohan](https://github.com/PabloKohan)
- [Jean-Yves Stervinou](https://github.com/jystervinou)
- [Thibault Deutsch](https://github.com/dethi)
- [grundsch](https://github.com/grundsch)
- [Ag Primatic](https://github.com/agprimatic)
- [Daniel M. Drucker](https://github.com/dmd)
- [Horst Höck](https://github.com/hhoeck)
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/britiscurious"><img src="https://avatars1.githubusercontent.com/u/25646439?v=4?s=100" width="100px;" alt=""/><br /><sub><b>britiscurious</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=britiscurious" title="Documentation">📖</a> <a href="https://github.com/RhetTbull/osxphotos/commits?author=britiscurious" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mwort"><img src="https://avatars3.githubusercontent.com/u/8170417?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michel Wortmann</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=mwort" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/PabloKohan"><img src="https://avatars3.githubusercontent.com/u/8790976?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pablo 'merKur' Kohan</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=PabloKohan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hshore29"><img src="https://avatars2.githubusercontent.com/u/7023497?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hshore29</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hshore29" title="Code">💻</a></td>
<td align="center"><a href="http://3e.org/"><img src="https://avatars0.githubusercontent.com/u/41439?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel M. Drucker</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dmd" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jystervinou"><img src="https://avatars3.githubusercontent.com/u/132356?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jean-Yves Stervinou</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=jystervinou" title="Code">💻</a></td>
<td align="center"><a href="https://dethi.me/"><img src="https://avatars2.githubusercontent.com/u/1011520?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thibault Deutsch</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=dethi" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/grundsch"><img src="https://avatars0.githubusercontent.com/u/3874928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>grundsch</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=grundsch" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/agprimatic"><img src="https://avatars1.githubusercontent.com/u/4685054?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ag Primatic</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=agprimatic" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hhoeck"><img src="https://avatars1.githubusercontent.com/u/6313998?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Horst Höck</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=hhoeck" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## Known Bugs ## Known Bugs
@@ -2036,4 +2054,3 @@ For additional details about how osxphotos is implemented or if you would like t
This project was originally inspired by [photo-export](https://github.com/patrikhson/photo-export) by Patrick Fältström, Copyright (c) 2015 Patrik Fältström paf@frobbit.se This project was originally inspired by [photo-export](https://github.com/patrikhson/photo-export) by Patrick Fältström, Copyright (c) 2015 Patrik Fältström paf@frobbit.se
I use [py-applescript](https://github.com/rdhyee/py-applescript) by "Raymond Yee / rdhyee" to interact with Photos. Rather than import this package, I included the entire package (which is published as public domain code) in a private package to prevent ambiguity with other applescript packages on PyPi. py-applescript uses a native bridge via PyObjC and is very fast compared to the other osascript based packages. I use [py-applescript](https://github.com/rdhyee/py-applescript) by "Raymond Yee / rdhyee" to interact with Photos. Rather than import this package, I included the entire package (which is published as public domain code) in a private package to prevent ambiguity with other applescript packages on PyPi. py-applescript uses a native bridge via PyObjC and is very fast compared to the other osascript based packages.

View File

@@ -1,4 +1,4 @@
""" version info """ """ version info """
__version__ = "0.36.2" __version__ = "0.36.4"

View File

@@ -1137,19 +1137,38 @@ def _exiftool_json_sidecar(
exif["EXIF:GPSLongitudeRef"] = lon_ref exif["EXIF:GPSLongitudeRef"] = lon_ref
# process date/time and timezone offset # process date/time and timezone offset
# Photos exports the following fields and sets modify date to creation date
# [EXIF] Modify Date : 2020:10:30 00:00:00
# [EXIF] Date/Time Original : 2020:10:30 00:00:00
# [EXIF] Create Date : 2020:10:30 00:00:00
# [IPTC] Digital Creation Date : 2020:10:30
# [IPTC] Date Created : 2020:10:30
#
# This code deviates from Photos in one regard:
# if photo has modification date, use it otherwise use creation date
date = self.date date = self.date
# exiftool expects format to "2015:01:18 12:00:00" # exiftool expects format to "2015:01:18 12:00:00"
datetimeoriginal = date.strftime("%Y:%m:%d %H:%M:%S") datetimeoriginal = date.strftime("%Y:%m:%d %H:%M:%S")
exif["EXIF:DateTimeOriginal"] = datetimeoriginal
exif["EXIF:CreateDate"] = datetimeoriginal
offsettime = date.strftime("%z") offsettime = date.strftime("%z")
# find timezone offset in format "-04:00" # find timezone offset in format "-04:00"
offset = re.findall(r"([+-]?)([\d]{2})([\d]{2})", offsettime) offset = re.findall(r"([+-]?)([\d]{2})([\d]{2})", offsettime)
offset = offset[0] # findall returns list of tuples offset = offset[0] # findall returns list of tuples
offsettime = f"{offset[0]}{offset[1]}:{offset[2]}" offsettime = f"{offset[0]}{offset[1]}:{offset[2]}"
exif["EXIF:DateTimeOriginal"] = datetimeoriginal
exif["EXIF:OffsetTimeOriginal"] = offsettime exif["EXIF:OffsetTimeOriginal"] = offsettime
dateoriginal = date.strftime("%Y:%m:%d")
exif["IPTC:DigitalCreationDate"] = dateoriginal
exif["IPTC:DateCreated"] = dateoriginal
if self.date_modified is not None: if self.date_modified is not None:
exif["EXIF:ModifyDate"] = self.date_modified.strftime("%Y:%m:%d %H:%M:%S") exif["EXIF:ModifyDate"] = self.date_modified.strftime("%Y:%m:%d %H:%M:%S")
else:
exif["EXIF:ModifyDate"] = self.date.strftime("%Y:%m:%d %H:%M:%S")
json_str = json.dumps([exif]) json_str = json.dumps([exif])
return json_str return json_str

View File

@@ -70,7 +70,11 @@ class PhotoInfo:
@property @property
def filename(self): def filename(self):
""" filename of the picture """ """ filename of the picture """
if self._db._db_version <= _PHOTOS_4_VERSION and self.has_raw and self.raw_original: if (
self._db._db_version <= _PHOTOS_4_VERSION
and self.has_raw
and self.raw_original
):
# return the JPEG version as that's what Photos 5+ does # return the JPEG version as that's what Photos 5+ does
return self._info["raw_pair_info"]["filename"] return self._info["raw_pair_info"]["filename"]
else: else:
@@ -80,7 +84,11 @@ class PhotoInfo:
def original_filename(self): def original_filename(self):
""" original filename of the picture """ original filename of the picture
Photos 5 mangles filenames upon import """ Photos 5 mangles filenames upon import """
if self._db._db_version <= _PHOTOS_4_VERSION and self.has_raw and self.raw_original: if (
self._db._db_version <= _PHOTOS_4_VERSION
and self.has_raw
and self.raw_original
):
# return the JPEG version as that's what Photos 5+ does # return the JPEG version as that's what Photos 5+ does
return self._info["raw_pair_info"]["originalFilename"] return self._info["raw_pair_info"]["originalFilename"]
else: else:
@@ -95,12 +103,20 @@ class PhotoInfo:
def date_modified(self): def date_modified(self):
""" image modification date as timezone aware datetime object """ image modification date as timezone aware datetime object
or None if no modification date set """ or None if no modification date set """
imagedate = self._info["lastmodifieddate"]
if imagedate: # Photos <= 4 provides no way to get date of adjustment and will update
seconds = self._info["imageTimeZoneOffsetSeconds"] or 0 # lastmodifieddate anytime photo database record is updated (e.g. adding tags)
delta = timedelta(seconds=seconds) # only report lastmodified date for Photos <=4 if photo is edited;
tz = timezone(delta) # even in this case, the date could be incorrect
return imagedate.astimezone(tz=tz) if self.hasadjustments or self._db._db_version > _PHOTOS_4_VERSION:
imagedate = self._info["lastmodifieddate"]
if imagedate:
seconds = self._info["imageTimeZoneOffsetSeconds"] or 0
delta = timedelta(seconds=seconds)
tz = timezone(delta)
return imagedate.astimezone(tz=tz)
else:
return None
else: else:
return None return None
@@ -282,7 +298,7 @@ class PhotoInfo:
# could be elsewhere--I haven't figured out this logic yet # could be elsewhere--I haven't figured out this logic yet
# first see if it's in 00 # first see if it's in 00
photopath = os.path.join( photopath = os.path.join(
library, "resources", "media", "version", folder_id, "00", filename, library, "resources", "media", "version", folder_id, "00", filename
) )
if not os.path.isfile(photopath): if not os.path.isfile(photopath):
@@ -841,7 +857,7 @@ class PhotoInfo:
inplace_sep=inplace_sep, inplace_sep=inplace_sep,
filename=filename, filename=filename,
dirname=dirname, dirname=dirname,
replacement=replacement replacement=replacement,
) )
@property @property
@@ -1026,6 +1042,7 @@ class PhotoInfo:
def json(self): def json(self):
""" Return JSON representation """ """ Return JSON representation """
def default(o): def default(o):
if isinstance(o, (datetime.date, datetime.datetime)): if isinstance(o, (datetime.date, datetime.datetime)):
return o.isoformat() return o.isoformat()

View File

@@ -3,8 +3,8 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate</key> <key>PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate</key>
<date>2020-10-09T16:14:42Z</date> <date>2020-11-01T02:34:49Z</date>
<key>PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate</key> <key>PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate</key>
<date>2020-10-10T05:21:03Z</date> <date>2020-11-01T02:34:49Z</date>
</dict> </dict>
</plist> </plist>

View File

@@ -11,6 +11,6 @@
<key>PLLastRevGeoForcedProviderOutOfDateCheckVersionKey</key> <key>PLLastRevGeoForcedProviderOutOfDateCheckVersionKey</key>
<integer>1</integer> <integer>1</integer>
<key>PLLastRevGeoVerFileFetchDateKey</key> <key>PLLastRevGeoVerFileFetchDateKey</key>
<date>2020-10-04T23:43:17Z</date> <date>2020-11-01T02:34:46Z</date>
</dict> </dict>
</plist> </plist>

View File

@@ -24,7 +24,7 @@
<key>SnapshotCompletedDate</key> <key>SnapshotCompletedDate</key>
<date>2019-07-27T13:16:43Z</date> <date>2019-07-27T13:16:43Z</date>
<key>SnapshotLastValidated</key> <key>SnapshotLastValidated</key>
<date>2020-10-10T05:22:36Z</date> <date>2020-11-01T02:34:46Z</date>
<key>SnapshotTables</key> <key>SnapshotTables</key>
<dict/> <dict/>
</dict> </dict>

View File

@@ -2632,16 +2632,21 @@ def test_export_sidecar_keyword_template():
json_expected = json.loads( json_expected = json.loads(
""" """
[{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos", [{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos",
"EXIF:ImageDescription": "Girl holding pumpkin", "EXIF:ImageDescription": "Girl holding pumpkin",
"XMP:Description": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin",
"XMP:Title": "I found one!", "XMP:Title": "I found one!",
"XMP:TagsList": ["Kids", "Multi Keyword", "Test Album", "Pumpkin Farm"], "XMP:TagsList": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"],
"IPTC:Keywords": ["Kids", "Multi Keyword", "Test Album", "Pumpkin Farm"], "IPTC:Keywords": ["Kids", "Multi Keyword", "Pumpkin Farm", "Test Album"],
"XMP:PersonInImage": ["Katie"], "XMP:PersonInImage": ["Katie"],
"XMP:Subject": ["Kids", "Katie"], "XMP:Subject": ["Kids", "Katie"],
"EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:DateTimeOriginal": "2018:09:28 16:07:07",
"EXIF:OffsetTimeOriginal": "-04:00"}]""" "EXIF:CreateDate": "2018:09:28 16:07:07",
"EXIF:OffsetTimeOriginal": "-04:00",
"IPTC:DigitalCreationDate": "2018:09:28",
"IPTC:DateCreated": "2018:09:28",
"EXIF:ModifyDate": "2018:09:28 16:07:07"}]
"""
)[0] )[0]
with open("Pumkins2.jpg.json", "r") as json_file: with open("Pumkins2.jpg.json", "r") as json_file:

View File

@@ -67,18 +67,21 @@ XMP_FILENAME = "Pumkins1.jpg.xmp"
XMP_JPG_FILENAME = "Pumkins1.jpg" XMP_JPG_FILENAME = "Pumkins1.jpg"
EXIF_JSON_UUID = UUID_DICT["has_adjustments"] EXIF_JSON_UUID = UUID_DICT["has_adjustments"]
EXIF_JSON_EXPECTED = ( EXIF_JSON_EXPECTED = """
'[{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos", ' [{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos",
'"EXIF:ImageDescription": "Bride Wedding day", ' "EXIF:ImageDescription": "Bride Wedding day",
'"XMP:Description": "Bride Wedding day", ' "XMP:Description": "Bride Wedding day",
'"XMP:TagsList": ["wedding"], ' "XMP:TagsList": ["wedding"],
'"IPTC:Keywords": ["wedding"], ' "IPTC:Keywords": ["wedding"],
'"XMP:PersonInImage": ["Maria"], ' "XMP:PersonInImage": ["Maria"],
'"XMP:Subject": ["wedding", "Maria"], ' "XMP:Subject": ["wedding", "Maria"],
'"EXIF:DateTimeOriginal": "2019:04:15 14:40:24", ' "EXIF:DateTimeOriginal": "2019:04:15 14:40:24",
'"EXIF:OffsetTimeOriginal": "-04:00", ' "EXIF:CreateDate": "2019:04:15 14:40:24",
'"EXIF:ModifyDate": "2019:07:27 17:33:28"}]' "EXIF:OffsetTimeOriginal": "-04:00",
) "IPTC:DigitalCreationDate": "2019:04:15",
"IPTC:DateCreated": "2019:04:15",
"EXIF:ModifyDate": "2019:07:27 17:33:28"}]
"""
def test_export_1(): def test_export_1():
@@ -507,7 +510,10 @@ def test_exiftool_json_sidecar_keyword_template_long(caplog):
"XMP:PersonInImage": ["Maria"], "XMP:PersonInImage": ["Maria"],
"XMP:Subject": ["wedding", "Maria"], "XMP:Subject": ["wedding", "Maria"],
"EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:DateTimeOriginal": "2019:04:15 14:40:24",
"EXIF:CreateDate": "2019:04:15 14:40:24",
"EXIF:OffsetTimeOriginal": "-04:00", "EXIF:OffsetTimeOriginal": "-04:00",
"IPTC:DigitalCreationDate": "2019:04:15",
"IPTC:DateCreated": "2019:04:15",
"EXIF:ModifyDate": "2019:07:27 17:33:28"}] "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
""" """
)[0] )[0]
@@ -554,7 +560,10 @@ def test_exiftool_json_sidecar_keyword_template():
"XMP:PersonInImage": ["Maria"], "XMP:PersonInImage": ["Maria"],
"XMP:Subject": ["wedding", "Maria"], "XMP:Subject": ["wedding", "Maria"],
"EXIF:DateTimeOriginal": "2019:04:15 14:40:24", "EXIF:DateTimeOriginal": "2019:04:15 14:40:24",
"EXIF:CreateDate": "2019:04:15 14:40:24",
"EXIF:OffsetTimeOriginal": "-04:00", "EXIF:OffsetTimeOriginal": "-04:00",
"IPTC:DigitalCreationDate": "2019:04:15",
"IPTC:DateCreated": "2019:04:15",
"EXIF:ModifyDate": "2019:07:27 17:33:28"}] "EXIF:ModifyDate": "2019:07:27 17:33:28"}]
""" """
)[0] )[0]
@@ -613,7 +622,11 @@ def test_exiftool_json_sidecar_use_persons_keyword():
"XMP:PersonInImage": ["Suzy", "Katie"], "XMP:PersonInImage": ["Suzy", "Katie"],
"XMP:Subject": ["Kids", "Suzy", "Katie"], "XMP:Subject": ["Kids", "Suzy", "Katie"],
"EXIF:DateTimeOriginal": "2018:09:28 15:35:49", "EXIF:DateTimeOriginal": "2018:09:28 15:35:49",
"EXIF:OffsetTimeOriginal": "-04:00"}] "EXIF:CreateDate": "2018:09:28 15:35:49",
"EXIF:OffsetTimeOriginal": "-04:00",
"IPTC:DigitalCreationDate": "2018:09:28",
"IPTC:DateCreated": "2018:09:28",
"EXIF:ModifyDate": "2018:09:28 15:35:49"}]
""" """
)[0] )[0]
@@ -652,7 +665,11 @@ def test_exiftool_json_sidecar_use_albums_keyword():
"XMP:PersonInImage": ["Suzy", "Katie"], "XMP:PersonInImage": ["Suzy", "Katie"],
"XMP:Subject": ["Kids", "Suzy", "Katie"], "XMP:Subject": ["Kids", "Suzy", "Katie"],
"EXIF:DateTimeOriginal": "2018:09:28 15:35:49", "EXIF:DateTimeOriginal": "2018:09:28 15:35:49",
"EXIF:OffsetTimeOriginal": "-04:00"}] "EXIF:CreateDate": "2018:09:28 15:35:49",
"EXIF:OffsetTimeOriginal": "-04:00",
"IPTC:DigitalCreationDate": "2018:09:28",
"IPTC:DateCreated": "2018:09:28",
"EXIF:ModifyDate": "2018:09:28 15:35:49"}]
""" """
)[0] )[0]

View File

@@ -45,17 +45,23 @@ UUID_DICT = {
"xmp": "8SOE9s0XQVGsuq4ONohTng", "xmp": "8SOE9s0XQVGsuq4ONohTng",
} }
EXIF_JSON_EXPECTED = ( EXIF_JSON_EXPECTED = """
'[{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos", ' [{"_CreatedBy": "osxphotos, https://github.com/RhetTbull/osxphotos",
'"XMP:Title": "St. James\'s Park", "XMP:TagsList": ["UK", "England", ' "XMP:Title": "St. James\'s Park",
'"London", "United Kingdom", "London 2018", "St. James\'s Park"], ' "XMP:TagsList": ["UK", "England", "London", "United Kingdom", "London 2018", "St. James\'s Park"],
'"IPTC:Keywords": ["UK", "England", "London", "United Kingdom", "London 2018", ' "IPTC:Keywords": ["UK", "England", "London", "United Kingdom", "London 2018", "St. James\'s Park"],
'"St. James\'s Park"], "XMP:Subject": ["UK", "England", "London", "United Kingdom", ' "XMP:Subject": ["UK", "England", "London", "United Kingdom", "London 2018", "St. James\'s Park"],
'"London 2018", "St. James\'s Park"], "EXIF:GPSLatitude": 51.50357167, ' "EXIF:GPSLatitude": 51.50357167,
'"EXIF:GPSLongitude": -0.1318055, "EXIF:GPSLatitudeRef": "N", ' "EXIF:GPSLongitude": -0.1318055,
'"EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:10:13 09:18:12", ' "EXIF:GPSLatitudeRef": "N",
'"EXIF:OffsetTimeOriginal": "-04:00", "EXIF:ModifyDate": "2019:12:01 11:43:45"}]' "EXIF:GPSLongitudeRef": "W",
) "EXIF:DateTimeOriginal": "2018:10:13 09:18:12",
"EXIF:CreateDate": "2018:10:13 09:18:12",
"EXIF:OffsetTimeOriginal": "-04:00",
"IPTC:DigitalCreationDate": "2018:10:13",
"IPTC:DateCreated": "2018:10:13",
"EXIF:ModifyDate": "2019:12:01 11:43:45"}]
"""
def test_export_1(): def test_export_1():

View File

@@ -1,6 +1,10 @@
""" Test basic methods for Mojave 10.14.6 """
import datetime
from collections import namedtuple
import pytest import pytest
from collections import namedtuple
from osxphotos._constants import _UNKNOWN_PERSON from osxphotos._constants import _UNKNOWN_PERSON
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db" PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
@@ -535,6 +539,31 @@ def test_date_modified_invalid(photosdb):
assert p.date_modified is None assert p.date_modified is None
def test_date_modified(photosdb):
""" Test date modified for photo that has been edited """
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
p = photos[0]
assert p.date_modified == datetime.datetime(
2019,
11,
27,
1,
30,
16,
681150,
tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)),
)
def test_date_modified_none(photosdb):
""" Test date modified for a photo that hasn't been edited """
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
p = photos[0]
assert p.date_modified is None
def test_uti(photosdb): def test_uti(photosdb):
for uuid, utis in UUID_UTI_DICT.items(): for uuid, utis in UUID_UTI_DICT.items():
photo = photosdb.get_photo(uuid) photo = photosdb.get_photo(uuid)