RhetTbull / osxmetadata

Python package to read and write various MacOS extended attribute metadata such as tags/keywords and Finder comments from files. Includes CLI tool for reading/writing metadata.
MIT License
117 stars 2 forks source link

Updating/ Setting keywords does not work #92

Closed onc closed 1 year ago

onc commented 1 year ago

I updated/ set keywords for images, but it does not work. Script to reproduce:

from osxmetadata import *

md = OSXMetaData('path/to/image')
print(md.keywords)
md.keywords = ["Test"]
print(md.keywords)

I tried multiple variations of the line, setting the keywords, but without success. e.g.

md.keywords = "Test"
md.set(kMDItemKeywords, "Test")
md.set(kMDItemKeywords, ["Test"])

Output always is:

['MeinFilmLab']
['MeinFilmLab']

or

None
None

Depending if the file already has a keyword or not.

Screenshot 2023-04-16 at 20 18 31 Screenshot 2023-04-16 at 20 20 28

I tried two versions of osxmetadata (1.2.2 and 1.2.0) and multiple versions of python (3.11.3, 3.8.10, 3.9.7), installed using pip into a virtualenv. I'm on MacOS 12.6.4.

When trying something other than keywords (e.g. tags), it works, so I assume the bug is related to the keywords.

RhetTbull commented 1 year ago

I don't believe this is a bug but rather an artifact of how macOS handles certain types of metadata. If you get/set kMDItemKeywords on a regular file, it works as expected (I tested on both Ventura 13.1 and Monterey 12.6):

❯ python
Python 3.11.2 (v3.11.2:878ead1ac1, Feb  7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import osxmetadata
>>> from osxmetadata import *
>>> import pathlib
>>> import platform
>>> osxmetadata.__version__
'1.2.2'
>>> platform.platform()
'macOS-13.1-arm64-arm-64bit'
>>> pathlib.Path("test_file").touch()
>>> md = OSXMetaData("test_file")
>>> md.keywords
>>> md.set(kMDItemKeywords, ["Test"])
>>> md.keywords
['Test']
Screenshot 2023-04-16 at 2 06 09 PM

However, with image files (which contain embedded data), the metadata embedded in the image always takes precedent. There's a Spotlight Importer process that reads the EXIF/IPTC/XMP metadata and sets the metadata attributes in the Spotlight database (e.g. kMDItemKeywords) accordingly. Any changes the the attribute don't change the underlying file and the metadata contained in it. The same is likely true for things like audio files which also contain embedded metadata.

However, if we change the underlying EXIF data then it works as expected (with one caveat that while testing this I've uncovered a possible bug in osxmetadata -- namely that the metadata did not refresh after changing the EXIF data until I created a new OSXMetaData instance.

In the example below, I use the ExifTool class of my osxphotos project because I'm familiar with it but there are several other python wrappers for exiftool.

❯ python
Python 3.11.2 (v3.11.2:878ead1ac1, Feb  7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from osxmetadata import *
>>> from osxphotos import ExifTool
>>> md = OSXMetaData("test.jpg")
>>> md.keywords
['Test']
>>> md.set(kMDItemKeywords, ["Foo"])
>>> md.keywords
['Test']
>>> exif = ExifTool("test.jpg")
>>> exif.asdict()["IPTC:Keywords"]
'Test'
>>> exif.addvalues("IPTC:Keywords", "Foo")
True
>>> exif.asdict()["IPTC:Keywords"]
['Test', 'Foo']
>>> md.keywords
>>> md.get(kMDItemKeywords)
>>> # ^^^^ these two lines should have returned the metadata but they didn't until I created
>>> # a new OSXMetaData object
>>> # even though the Finder was displaying the new metadata so the Spotlight importer had run
>>> md = OSXMetaData("test.jpg")
>>> md.keywords
['Test', 'Foo']
>>> exif.addvalues("IPTC:Keywords", "Bar")
True
>>> md.keywords
>>> md = OSXMetaData("test.jpg")
>>> md.keywords
['Test', 'Foo', 'Bar']
>>>

So in summary, osxmetadata cannot change metadata associated with a file that has embedded metadata if Spotlight is using the embedded metadata as the Spotlight metadata attributes. I'll add something about this in the documentation. osxmetadata can alter metadata that is not associated with the embedded metadata, for example, Finder tags:

❯ python
Python 3.11.2 (v3.11.2:878ead1ac1, Feb  7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from osxmetadata import *
>>> md = OSXMetaData("test.jpg")
>>> md.tags
[]
>>> md.tags = [Tag("Test", 0)]
>>> md.tags
[Tag(name='Test', color=0)]
>>>
onc commented 1 year ago

First, thank you really much for your help. The metadata stuff is really confusing to me...

After more debugging with the exif lib, I figured out the issue. The files I'm dealing with have a metadata field called XMP:Subject that contains the string in question (MeinFilmLab). This string was shown under keywords in Finder. After deleting this metadata field, I was able to set the keywords the way you described above.

Like so:

exif = ExifTool(image)
exif.setvalue("XMP:Subject", "")
exif.setvalue("IPTC:Keywords", "Foo")
exif.addvalues("IPTC:Keywords", "Bar")

So I guess this issue can be closed.

Thank you!