I have run into the same problem. Files end up with multiple labels, and user-defined labels cannot be cleared by setting f.FinderLabelMBS = 0. Did you ever find a solution?
Ok so I ended up going the command-line way using "xattr". It allows to display or change file extended attributes. Labels are one of the extended attributes.
I can't describe all parts in details, but I'll try to explain what I can.as once everything worked, I have not documented what each component does. But in short,
Reading the labels:
xattr -p com.apple.metadata:_kMDItemUserTags /path/to/file | xxd -r -p | plutil -convert json - -o -
In more details:
xattr -p com.apple.metadata:_kMDItemUserTags /path/to/file
xattr allows to read and manipulate extended attributes. Labels are one of them. It outputs the result as a hex dump.
xxd -r -p
xxd is a convert utility from hex dump to binary data
plutil -convert json - -o -
plutil is a utility to work with property lists and will perform the final conversion in json so it's easier to work with.
The final output is a string in json format which looks like:
Each item contains the label name followed by the color code (if there's a color). The name and color are separated by the "\n" string. The color codes are: 0=none | 1=gray | 2=green | 3=purple | 4=blue | 5=yellow | 6=red
To set the tags, rebuild the string in the same format. The same utilities are used in reverse order with some different options, but you can basically figure out the process. Any new label will still be assigned and shown, but not available in the label popup list in the finder.
xattr -xw com.apple.metadata:_kMDItemUserTags $(echo '[labelname1,labelname2,labelname3]' | plutil -convert binary1 - -o - | xxd -p -c 256 -u) /path/to/file
My needs were to replicate the labels from one file to another. I have an audio converter application and I needed the converted files to have the same labels as the originals. So I have created a small class that reads the labels, and then sets them on another file. It does not allow to manually define the labels, but it would be trivial to add.
Here's some sample code (adapted from my class but not tested)
Private Sub ReadLabels(targetFile as FolderItem) // READ FILE LABELS dim theLabels() as string dim theColors() as integer if targetFile <> nil and targetFile.Exists = true then dim s as new shell s.Execute "xattr -p com.apple.metadata:_kMDItemUserTags " + targetFile.ShellPath + " | xxd -r -p | plutil -convert json - -o -" if s.ErrorCode = 0 then dim rawResult as string = s.Result dim jsonLabels as new JSONItem(rawResult) dim lastItem as integer lastItem = jsonLabels.Count - 1 for i as integer = 0 to lastItem dim item as string item = jsonLabels.Value(i).StringValue dim parts() as string parts = item.Split(&u0A) theLabels.Append parts(0) if parts.Ubound > 0 then theColors.Append(parts(1).val) else theColors.Append(0) end if next end if end if // do your stuff with the labels and the color codes // theLabels contains an array of label names // theColors contains the matching color codes End Sub
To write the labels for a given file, create a properly formatted string as: ["hot\n1","projectX","mytag1"]
Private Sub SetLabels(targetFile as FolderItem,jsonlist as string) // WRITE FILE LABELS dim cmd as string = "xattr -xw com.apple.metadata:_kMDItemUserTags $(echo '" + _ jsonlist + "' | plutil -convert binary1 - -o - | xxd -p -c 256 -u) " + _ targetFile.ShellPath dim s as new Shell s.Execute cmd End Sub
In terminal, use "man" if you want to learn more about the different tools used:
man xattr man xxd man plutil
Thanks Bruno, this is extremely useful. Much appreciated!
I'm almost there, but curiously, if a file has 2 or more labels calling SetLabels with jsonlist = "" or "" removes all but one label. I cannot remove all labels. Even a 2nd call won't do it.
And even xattr -d com.apple.metadata:_kMDItemUserTags myFile won't do it (even tho xattr -l then reports that the com.apple.metadata:_kMDItemUserTags attribute is in fact gone, but the label in Finder remains, and must be removed manually from the Finder).
Do you know of a way to remove ALL labels from a file in one shot?
An addendum: I just noticed that if a user manually sets a label from the Finder, this label:
So it almost seems like if you set the labels in code, you can read them and remove them. If you set them manually in the Finder, they are inaccessible to Read/SetLabels methods.
... I couldn't help but check immediately :-) There is a legacy finder info attribute that stores single labels (and other info). Meaning that sometimes, the label info can be stored there instead of the new com.apple.metadata:_kMDItemUserTags attribute.
To remove all labels with a simpler command:
xattr -d com.apple.metadata:_kMDItemUserTags /path/to/file
This will leave the last single label if stored in com.apple.FinderInfo. To remove it:
xattr -d com.apple.FinderInfo /path/to/file
BUT com.apple.FinderInfo also stores other info. Although it's legacy info from the Classic environment, you should check if there's anything useful for you. One example is the 4 character file type code. Here's a link to a page that documents this attribute
From my (non scientific) experiments, it seems that setting the first label from the Finder will set it using com.apple.FinderInfo IF this attribute is present. After removing it, if you set it again in the Finder, it's then correctly stored in the new attribute.
I haven't poked around to decode the com.apple.FinderInfo attribute to extract the label set there, but it should be feasible.
thanks Bruno, this makes sense and explains the odd behavior. Just for the record, creating a manual label from the Finder will resist clearance by xattr -d com.apple.metadata:_kMDItemUserTags. You must also execute xattr -d com.apple.FinderInfo in addition. This is on a freshly created file from TextEdit so no chance of some old legacy attributes hanging around. Odd that Apple would store labels under different com.apple.XXX files.
Yeah, my tests are similar with new files created on Mojave. Sorry if I misled you in thinking that old files were the culprit. It seems that sometimes the Finder still creates the old attribute, so probably some legacy code buried in MacOS :-) I would guess that the first label uses the old attribute, and that any additional label get set with the new one.
Glad it works for you. I'm going to make a more elegant class to read set and clear the labels. I can send it to you if you're interested.
Thanks Bruno, I'd appreciate looking at your class. One other point I just noticed during testing: seems that xattr is a python script and unfortunately, compared to FinderLabelMBS, it is extremely slow, at least on a large folder tree (3000 files: 15 min and still running :-( ) on a remote server. So there is no easy way to efficiently manage ALL labels it seems. Just an FYI if your application also includes complex hierarchies.
My guess is that the slow process comes from instantiating a shell with every call. Try to make a call with a wildcard to process a batch of files. I did a quick test locally directly in terminal and processing 539 files with xattr -d com.apple.metadata:_kMDItemUserTags took 148ms and xattr -d com.apple.FinderInfo took 151ms. I know a file server adds quite a bit of lag but maybe you'll want to see if a wildcard helps. Try it directly in the terminal, transpose in a shell in Xojo after.
If I extract a list of items in a folder and set each one at a time using FinderLabelMBS it's faster than xattr with the -r flag, BUT FinderLabelMBS only seems to manipulate the classic labels, not the com.apple.metadata:_kMDItemUserTags, so it's not that useful because you end up with dual labels, a mix of new and old, and it becomes a mess. I wish we could just pass an array of numbers (= color indexes) to a function that would stamp none, one or several color labels to a folderItem, and quickly, taking care of new and old sets at once.
I have searched a bit but the only other way I found (at a high level anyways) is using Applescript. I doubt it'll be faster, but it may be worth a try.
At this point, you may want to contact Christian about this. He may be willing to update its classes to support both old and new attributes.
As for me, I'll work on a clean class during the Christmas Holiday (for fun). If it can be useful, even if it's slow, I'll happily pass it along. It can work as you described, but it'll still be slow on a remote volume.
@Bruno F As for me, I'll work on a clean class during the Christmas Holiday (for fun). If it can be useful, even if it's slow, I'll happily pass it along. It can work as you described, but it'll still be slow on a remote volume.
I would also be interested in seeing your class. I only need read ability, but I can see how we ability would be useful too.