From 2ebd4c33ffe677dcbfb7456e26708c825f2ae207 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Sun, 20 Dec 2020 22:11:50 -0800 Subject: [PATCH] remove duplicate keywords with --exiftool and --sidecar, closes #294 --- osxphotos/_version.py | 2 +- osxphotos/photoinfo/_photoinfo_export.py | 29 ++++++--- .../database/Photos.sqlite-shm | Bin 32768 -> 32768 bytes .../database/Photos.sqlite-wal | Bin 3815152 -> 3971712 bytes .../database/Photos.sqlite.lock | 2 +- .../database/search/psi.sqlite | Bin 200704 -> 200704 bytes .../CLSBusinessCategoryCache.POI.sqlite-shm | Bin 32768 -> 32768 bytes .../CLSBusinessCategoryCache.POI.sqlite-wal | Bin 3069432 -> 3073552 bytes .../graph/PGCurationCache.sqlite.sqlite-shm | Bin 32768 -> 32768 bytes .../caches/graph/changetoken.plist | Bin 606 -> 606 bytes .../resources/journals/Asset-change.plj | Bin 27317 -> 27551 bytes .../resources/journals/HistoryToken.plist | Bin 553 -> 553 bytes .../resources/journals/Keyword-change.plj | Bin 284 -> 377 bytes tests/test_catalina_10_15_7.py | 4 +- tests/test_cli.py | 58 ++++++++++++++++-- tests/test_export_catalina_10_15_7.py | 18 +++--- tests/test_export_mojave_10_14_6.py | 4 +- 17 files changed, 91 insertions(+), 26 deletions(-) diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 1d40f460..e742d2b2 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,5 +1,5 @@ """ version info """ -__version__ = "0.38.7" +__version__ = "0.38.8" diff --git a/osxphotos/photoinfo/_photoinfo_export.py b/osxphotos/photoinfo/_photoinfo_export.py index ae4eba45..dec00ecf 100644 --- a/osxphotos/photoinfo/_photoinfo_export.py +++ b/osxphotos/photoinfo/_photoinfo_export.py @@ -102,7 +102,7 @@ class ExportResults: ) files += [x[0] for x in self.exiftool_warning] files += [x[0] for x in self.exiftool_error] - + files = list(set(files)) return files @@ -1341,13 +1341,13 @@ def _exiftool_dict( person_list = [] if self.persons: # filter out _UNKNOWN_PERSON - person_list = sorted([p for p in self.persons if p != _UNKNOWN_PERSON]) + person_list = [p for p in self.persons if p != _UNKNOWN_PERSON] if use_persons_as_keywords and person_list: - keyword_list.extend(sorted(person_list)) + keyword_list.extend(person_list) if use_albums_as_keywords and self.albums: - keyword_list.extend(sorted(self.albums)) + keyword_list.extend(self.albums) if keyword_template: rendered_keywords = [] @@ -1382,16 +1382,19 @@ def _exiftool_dict( keyword_list.extend(rendered_keywords) if keyword_list: + # remove duplicates + keyword_list = sorted(list(set(keyword_list))) exif["XMP:TagsList"] = keyword_list.copy() exif["IPTC:Keywords"] = keyword_list.copy() if person_list: + person_list = sorted(list(set(person_list))) exif["XMP:PersonInImage"] = person_list.copy() if self.keywords or person_list: # Photos puts both keywords and persons in Subject when using "Export IPTC as XMP" # only use Photos' keywords for subject (e.g. don't include template values) - exif["XMP:Subject"] = self.keywords.copy() + person_list.copy() + exif["XMP:Subject"] = sorted(list(set(self.keywords + person_list))) # if self.favorite(): # exif["Rating"] = 5 @@ -1460,13 +1463,12 @@ def _exiftool_dict( date_utc = datetime_tz_to_utc(date) creationdate = date_utc.strftime("%Y:%m:%d %H:%M:%S") exif["QuickTime:CreateDate"] = creationdate - if self.date_modified is not None and not ignore_date_modified: + if self.date_modified is None or ignore_date_modified: + exif["QuickTime:ModifyDate"] = creationdate + else: exif["QuickTime:ModifyDate"] = datetime_tz_to_utc( self.date_modified ).strftime("%Y:%m:%d %H:%M:%S") - else: - exif["QuickTime:ModifyDate"] = creationdate - return exif @@ -1604,6 +1606,15 @@ def _xmp_sidecar( # Photos puts both keywords and persons in Subject when using "Export IPTC as XMP" subject_list = list(self.keywords) + person_list + # remove duplicates + # sorted mainly to make testing the XMP file easier + if keyword_list: + keyword_list = sorted(list(set(keyword_list))) + if subject_list: + subject_list = sorted(list(set(subject_list))) + if person_list: + person_list = sorted(list(set(person_list))) + xmp_str = xmp_template.render( photo=self, description=description, diff --git a/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite-shm b/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite-shm index c640b0d98889a1aa01f3d904e608e17359a3b7b9..cd19c001af89da1e775b3962f4a992175fdec0c6 100644 GIT binary patch delta 750 zcmZo@U}|V!s+V}A%K!q*K+MR%AaDf8Sk1)1urV#|>f+o(j=QXDuG* zI=LvYfMp}|Vdl-x0{NJkH!&Xp>Dw$7+Q7)T8pzb#d?f4|6Z0bG?Ldy|QM9z=EclAz}gSQ++t#0!n_m6QQW*Q{st5CQs!M?nLSCX z7#Y_9nVOp?q_8kDFJs;f7T=Y+hK+F>kg2-4Cx?TNbpi8o<~_hbu$_FZN{4X;&U&?F|Gvb)vCM32mom}|5yM3 delta 661 zcmZo@U}|V!s+V}A%K!pgK+MR%ATW=afnhZh1H;p>r~_+NGmq@DuDNa>eCGg@;ig3a zb46`PRSz~CNPx`!j|8CN%nb5O8=LC*CYzKmo7|AMWfY@?LDwXQiph(@4JNN}NC47a zn}Zy;F-~T3klU>0{ErzZqP5x2eFo#?Phmove|WGk1Eo|ZEBM@*ER-Iw*~fPsNUh3b zIsa9_aBZ6G74U-_67Go%FBw}VGdieFE($DQ0f}vX7RU$EuC`e!w1IK*0SB$kN5ZZ# z0ZrDJd?+%6m4U&XAq%KOeX~QP wAVy3`VF5W#ZS$_wHEdwhdvZAVfbMphe631na)DRK=CJBjOknG@>aH;Y0M$e#0ssI2 diff --git a/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite-wal b/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite-wal index fcddc203b5a4816186761bc6781fea86b73faff2..514d5d7330d4e82b557552ec11cdfcb66016c19e 100644 GIT binary patch delta 7900 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 delta 127 zcmV~$S8_oC006*wAzGB^En4&*?GdN*u}9)ge#{sruzR_^g6HENTm(KuM16{hOGrxj zl9rK`^DVESsHCi-s-~`?=|@XjM_13UzJZ~Uv5BdfxrL>bwLcqMJ9`JAqm%#Z`5C?e D(^xB9 diff --git a/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite.lock b/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite.lock index 2c98250f..4c746646 100644 --- a/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite.lock +++ b/tests/Test-10.15.7.photoslibrary/database/Photos.sqlite.lock @@ -7,7 +7,7 @@ hostuuid 9575E48B-8D5F-5654-ABAC-4431B1167324 pid - 19275 + 55247 processname photolibraryd uid diff --git a/tests/Test-10.15.7.photoslibrary/database/search/psi.sqlite b/tests/Test-10.15.7.photoslibrary/database/search/psi.sqlite index a8d06033b4e291a9d1ecfef14c1be057c4cd9096..4a97457008796cffe91bbc27c7ed9b316ffb58da 100644 GIT binary patch delta 2337 zcmZ8ieQ;FO6~E`a@6Wp-knAh&-dz%$fd<$lfT<(dB&`(b*an56CD;TpqY23xgN%Gk zYc>mhRBW?d?Te0OXzfg`7R`1o&wioBu|lU(^pE0DqzScZKCo40oGHVU-uLo|ggdiy z&ffDo=bqoYzkBb%L37}sIU=@3WO|Cwi}V7WqW_|2>8VmO@j6vx5loP=bhiEah>(}E zC8duEqVx#&l8KFIi@CT!Y!GuX-;{o^*JPj4+KCo_e8CK->rA7a9Hc zv|1|--XH)pGih)ow@D@st`HE&B%fH$Cz`me=Qho49k+2-%AQ^jE+Qw|TM zxQ+7MWq$4;cb59jQSL19oe2)9;gCjF`saH|6g(|oP)0YV>~CA0w8j3NU9dM;o9T^q z$$rd?n3MWFdX>J1*3rf0H_X-AtL7?mxw$}ly72wcFDd)A!`dNU;KXPHTA6}8wivCQ zh16K1Xbo!5a#r)z8&nYhn;A?yNd_x55dk?ZSP{gU7%UH*G=uuUi8H7x9UEr?S{n-E zQ$0=gT%r&{f}q2oHgKX0Qq!dd87!Ojjxty}?M*OP62KZ6)Rdk)e{ahi73v86%sx(! zQJ3CH7ui#8Bn1^-O$wS#fokYN1%B(=`2 zG0^ot>HpI|(?8ZfaEI1GwRdD4sG1^Wy9E*f_pMF73bPsi50SFHq3sFnHs5+u8?g1j z+=VZ}KYRtc$_QlKeIvk+NT;<~dsTbd3bkcrtew}0i8Y5}Vo6_2fXK*XC??hviixE| zF|jz*dB=VOftuX$-@|M`#;XI;elvf`{JfBiOA*-BEzsw#?Z?JjiOg%od|!GF~3 zYm@{c>m*Oem3PF>G&eOT>1--oZOUx{$)GsDSl z^SSxtuGvkAIN9!**zBE?+~O`=TURktX2ywSdwfzptE)%_8_qwu>@L}j2X33yK{A`+ z8UEe|88b{j-(HolY_Ct*=j>O#LOFzR>RxOQemGayuQqtf5Y7Wp{j%D?-;t;ilVUBueDDy8wcg7kxJNL3(DZSDXS`J}d2Ccg7{K56z4!XwzPYxAmR4}ta!G2`%@zc)$CeNS9_hBU6rm+8vEhbls7D)= za%hA=S)XLU1XB~^fm(N2bRZ%riIFOYU@$~eprDeJBQ|PCApGHU7o17vm&{~lzWl!V zW@8WC*h7<6oZI4}A?}EO#0~MMxR@>upA_<9HB30rYz573yF2Z)jW_l53QTvmzd38v z72Ypi7kfm6_lg4E!QbW0dUYoEn~!b=lK$gpEC|G({L?^=U`E>O@|`nl#Q^(-oo2^b zA3Mwru+RMUXRhf@qXOx!-%8Axc7q8}MeGF{#l)`Hh!ML^qmtP3HA2L$)hHr%jYc7{ z;~Ett{riPtG}D28-x+L(u&sQFn9aZ8KHI?S#VnrY+i8$q_m+DTy-pDoCA6Bg)2C=T zoyMBz1b^q?6V48{hPASK|McKY-@RPoPrF>916$;#`-jGx$VdA_H&(|S3r6B=$gr3! z$S_9gGpr4 z&whjdYdmk9HN<6cNbD8Qh==%jewgp$TX+k9nos3~{l(6)BWy2wl`UaaYz%Yg5IsiU zqw8rCt)vqvC%4H}@&oB5-K2xGk~n#igo*79d*{7_-do05?`7{fFY1j6+zp%v>+ z$mK!&P!0z%N6}o2SkUVihxf}ziZGyt^KrBXEz%C-V!1SobLHn@oM4&D%w_6y7;jn- zPo$nz?i9Qmu$DrZE8Ph0lJ7^bQ4U7*iFYE{0*L2iQxrR3Tq0E{hoe{v%M+;=)W@aR zR0gx8-G;BoU2XV)>}f+!>EVOJ_CVFxWbV*9UoMGe}Z|AdCe-GG+2(&v7g(8IF>N;F5mMRwC`V2h0P+kx AQvd(} delta 165 zcmZo@U}|V!s+V}A%K!q55G?SLiGe`?$aa`=H&uMDPx?CRsOu-!?py6HUNynJTP>DU x^&qoB0A%ieBmfa*U}AW&v2iu`W&@scW|JLwST_H2y1=^GkViyi^P?O&VE}SVHTwVn diff --git a/tests/Test-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal b/tests/Test-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal index fc1a45c00182668f0dd4fdf598bbaccbed39c834..d07a0a3656bedf61eb53d86dc0ad909784cfed45 100644 GIT binary patch delta 135 zcmV~$xe~!p7=Y3FZLvq}#B%M#GFr74$s2fu6kktw8#DA8GpO z`r;u(h!P`C0)r$e(qzbzBacY|iy|e;RH#y;PJ<>kE!uSGqUh0Qz>pDRCQO;(FlWK? gz6xLZdU7n|yV-sAzlZi*WlNxsuG&g)d#}&tA71}4rvLx| delta 103 zcmV~$wH3lp006-EaSN^q?mi>M4Uj!(>9PVXyQJhU{bnJ5XW=C%C@LwdsH&-JXljYH nb#(Rg4GfKpO-#+i5_1bnD{C8DJ9`I5CubK|H+PTEGynYo2Vx@T diff --git a/tests/Test-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm b/tests/Test-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm index 60b593d1bce7a757ad972439f6e7d5f4313e7bd7..0c057888a612f4ebdf5379c90eeaad0768fcee47 100644 GIT binary patch delta 86 zcmZo@U}|V!;*@x#%K!!wIpqZf+B3Xgudxk87Sx0ZLXAP934)aY2@xRvj|5=i3>zEk>j4@z B8u(TiIW>GRFsZ%;nWdj(A3$znrBk5n4*I6(eRV2IJ{G~b{sv#Y8H4;x5&yk WUdS<^JtwR6?3*gF$rp{37|j9DE;beb delta 9 QcmbP#opI|`#tmDu02rkFqx;pj0s|J dY-w|JFfh>p0#$TrbZli+O<{6rVUau{0T*n=7=8c% delta 7 Ocmey#G>2(}3?l#xKLTd} diff --git a/tests/test_catalina_10_15_7.py b/tests/test_catalina_10_15_7.py index 7f1b7364..fb4bd5a0 100644 --- a/tests/test_catalina_10_15_7.py +++ b/tests/test_catalina_10_15_7.py @@ -35,6 +35,7 @@ KEYWORDS = [ "United Kingdom", "foo/bar", "Travel", + "Maria", ] # Photos 5 includes blank person for detected face PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON] @@ -60,6 +61,7 @@ KEYWORDS_DICT = { "United Kingdom": 1, "foo/bar": 1, "Travel": 2, + "Maria": 1, } PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 2, _UNKNOWN_PERSON: 1} ALBUM_DICT = { @@ -339,7 +341,7 @@ def test_attributes_2(photosdb): photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]]) assert len(photos) == 1 p = photos[0] - assert p.keywords == ["wedding"] + assert sorted(p.keywords) == ["Maria", "wedding"] assert p.original_filename == "wedding.jpg" assert p.filename == "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51.jpeg" assert p.date == datetime.datetime( diff --git a/tests/test_cli.py b/tests/test_cli.py index 7c07ed75..02489398 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -407,6 +407,10 @@ CLI_EXIFTOOL_IGNORE_DATE_MODIFIED = { CLI_EXIFTOOL_ERROR = ["E2078879-A29C-4D6F-BACB-E3BBE6C3EB91"] +CLI_EXIFTOOL_DUPLICATE_KEYWORDS = { + "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51": "wedding.jpg" +} + LABELS_JSON = { "labels": { "Plant": 7, @@ -1017,7 +1021,10 @@ def test_export_exiftool(): exif = ExifTool(CLI_EXIFTOOL[uuid]["File:FileName"]).asdict() for key in CLI_EXIFTOOL[uuid]: - assert exif[key] == CLI_EXIFTOOL[uuid][key] + if type(exif[key]) == list: + assert sorted(exif[key]) == sorted(CLI_EXIFTOOL[uuid][key]) + else: + assert exif[key] == CLI_EXIFTOOL[uuid][key] @pytest.mark.skipif(exiftool is None, reason="exiftool not installed") @@ -1051,7 +1058,10 @@ def test_export_exiftool_ignore_date_modified(): CLI_EXIFTOOL_IGNORE_DATE_MODIFIED[uuid]["File:FileName"] ).asdict() for key in CLI_EXIFTOOL_IGNORE_DATE_MODIFIED[uuid]: - assert exif[key] == CLI_EXIFTOOL_IGNORE_DATE_MODIFIED[uuid][key] + if type(exif[key]) == list: + assert sorted(exif[key]) == sorted(CLI_EXIFTOOL_IGNORE_DATE_MODIFIED[uuid][key]) + else: + assert exif[key] == CLI_EXIFTOOL_IGNORE_DATE_MODIFIED[uuid][key] @pytest.mark.skipif(exiftool is None, reason="exiftool not installed") @@ -1093,6 +1103,38 @@ def test_export_exiftool_quicktime(): for filename in files: os.unlink(filename) + +@pytest.mark.skipif(exiftool is None, reason="exiftool not installed") +def test_export_exiftool_duplicate_keywords(): + """ ensure duplicate keywords are removed """ + import glob + import os + import os.path + from osxphotos.__main__ import export + from osxphotos.exiftool import ExifTool + + runner = CliRunner() + cwd = os.getcwd() + # pylint: disable=not-context-manager + with runner.isolated_filesystem(): + for uuid in CLI_EXIFTOOL_DUPLICATE_KEYWORDS: + result = runner.invoke( + export, + [ + os.path.join(cwd, PHOTOS_DB_15_7), + ".", + "-V", + "--exiftool", + "--uuid", + f"{uuid}", + ], + ) + exif = ExifTool(CLI_EXIFTOOL_DUPLICATE_KEYWORDS[uuid]) + exifdict = exif.asdict() + assert sorted(exifdict["IPTC:Keywords"]) == ["Maria", "wedding"] + assert sorted(exifdict["XMP:Subject"]) == ["Maria", "wedding"] + + @pytest.mark.skipif(exiftool is None, reason="exiftool not installed") def test_export_exiftool_error(): """" test --exiftool catching error """ @@ -1106,7 +1148,7 @@ def test_export_exiftool_error(): cwd = os.getcwd() # pylint: disable=not-context-manager with runner.isolated_filesystem(): - for uuid in CLI_EXIFTOOL_ERROR: + for uuid in CLI_EXIFTOOL: result = runner.invoke( export, [ @@ -1119,7 +1161,15 @@ def test_export_exiftool_error(): ], ) assert result.exit_code == 0 - assert "exiftool error" in result.output + files = glob.glob("*") + assert sorted(files) == sorted([CLI_EXIFTOOL[uuid]["File:FileName"]]) + + exif = ExifTool(CLI_EXIFTOOL[uuid]["File:FileName"]).asdict() + for key in CLI_EXIFTOOL[uuid]: + if type(exif[key]) == list: + assert sorted(exif[key]) == sorted(CLI_EXIFTOOL[uuid][key]) + else: + assert exif[key] == CLI_EXIFTOOL[uuid][key] def test_export_edited_suffix(): diff --git a/tests/test_export_catalina_10_15_7.py b/tests/test_export_catalina_10_15_7.py index 3bdd7426..708678e4 100644 --- a/tests/test_export_catalina_10_15_7.py +++ b/tests/test_export_catalina_10_15_7.py @@ -22,6 +22,7 @@ KEYWORDS = [ "St. James's Park", "UK", "United Kingdom", + "Maria" ] # Photos 5 includes blank person for detected face PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON] @@ -39,6 +40,7 @@ KEYWORDS_DICT = { "St. James's Park": 1, "UK": 1, "United Kingdom": 1, + "Maria": 1, } PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1, _UNKNOWN_PERSON: 1} ALBUM_DICT = { @@ -70,8 +72,8 @@ EXIF_JSON_UUID = UUID_DICT["has_adjustments"] EXIF_JSON_EXPECTED = """ [{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", - "XMP:TagsList": ["wedding"], - "IPTC:Keywords": ["wedding"], + "XMP:TagsList": ["Maria", "wedding"], + "IPTC:Keywords": ["Maria", "wedding"], "XMP:PersonInImage": ["Maria"], "XMP:Subject": ["wedding", "Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", @@ -85,8 +87,8 @@ EXIF_JSON_EXPECTED = """ EXIF_JSON_EXPECTED_IGNORE_DATE_MODIFIED = """ [{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", - "XMP:TagsList": ["wedding"], - "IPTC:Keywords": ["wedding"], + "XMP:TagsList": ["Maria", "wedding"], + "IPTC:Keywords": ["Maria", "wedding"], "XMP:PersonInImage": ["Maria"], "XMP:Subject": ["wedding", "Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", @@ -522,8 +524,8 @@ def test_exiftool_json_sidecar_keyword_template_long(caplog): """ [{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", - "XMP:TagsList": ["wedding", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"], - "IPTC:Keywords": ["wedding", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"], + "XMP:TagsList": ["Maria", "wedding", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"], + "IPTC:Keywords": ["Maria", "wedding", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"], "XMP:PersonInImage": ["Maria"], "XMP:Subject": ["wedding", "Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", @@ -571,8 +573,8 @@ def test_exiftool_json_sidecar_keyword_template(): """ [{"EXIF:ImageDescription": "Bride Wedding day", "XMP:Description": "Bride Wedding day", - "XMP:TagsList": ["wedding", "Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin"], - "IPTC:Keywords": ["wedding", "Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin"], + "XMP:TagsList": ["Maria", "wedding", "Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin"], + "IPTC:Keywords": ["Maria", "wedding", "Folder1/SubFolder2/AlbumInFolder", "I have a deleted twin"], "XMP:PersonInImage": ["Maria"], "XMP:Subject": ["wedding", "Maria"], "EXIF:DateTimeOriginal": "2019:04:15 14:40:24", diff --git a/tests/test_export_mojave_10_14_6.py b/tests/test_export_mojave_10_14_6.py index 37c74f78..24e93011 100644 --- a/tests/test_export_mojave_10_14_6.py +++ b/tests/test_export_mojave_10_14_6.py @@ -427,9 +427,9 @@ def test_xmp_sidecar(): + Katie Kids Suzy - Katie 2018-09-28T15:35:49.063000-04:00 @@ -438,8 +438,8 @@ def test_xmp_sidecar(): xmlns:Iptc4xmpExt='http://iptc.org/std/Iptc4xmpExt/2008-02-29/'> - Suzy Katie + Suzy