-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Setting kMDItemWhereFroms
doesn't show in Finder
#61
Comments
I wonder if this is related to #10? Any idea how to force mdls/Finder to rebuild the cache? I tried relaunching Finder to no avail. |
I'll take a look and see if I can replicate this. I suspect you're right that this is related to #10. I think the underlying issue is that Spotlight runs a metadata importer for each file type and this importer needs to be triggered to import any changes. For custom filetypes, the developer can write their own importer but absent this, there's no way to programmatically trigger the import that I know of. MDItem for example, only provides methods for reading metadata keys, not writing them. The only exceptions appear to be labels (colors) and tags which can be set via NSURL setresourcevalue:forKey:error:. For these, it would probably be best to rewrite osxphotos to use setresourcevalue rather than direclty modifying the xattr. (I wasn't aware of this limitation on xattr and Spotlight importers when I first wrote osxmetadata). Just brainstorming....one idea might be to change the modification date of the file then change it back to the original date. Will need to do some testing to see if this triggers the importer (and if so, how long does it take the importer to trigger). |
I noticed that if I change a URL on a file which is already correctly showing in the Finder Get Info panel, then the new URL will show up in Finder immediately. So it may not be a caching issue after all? Is it possible that something needs to change in |
Interestingly, WebKit sets both One other thing that both Safari and FF do have, though, is |
Interesting, WebKit uses something call
So...will need to play around with this. Maybe using the undocumented API + setting the extended attribute will work. It would sure be nice if Apple provided a better way to do this! |
@nk9 I've figured out how to call the undocumented MDItemSetAttribute from python. The following snippet (also as a gist) will set kMDItemWhereFroms if called like this:
"""Set metadata on macOS files using undocumented function MDItemSetAttribute
Background: Apple provides MDItemCopyAttribute to get metadata from files:
https://developer.apple.com/documentation/coreservices/1427080-mditemcopyattribute?language=objc
but does not provide a documented way to set file metadata.
This script shows how to use the undocumented function MDItemSetAttribute to do so.
`pip install pyobjc` to install the required Python<-->Objective C bridge package.
"""
import sys
from typing import List, Union
import CoreFoundation
import CoreServices
import objc
# load undocumented function MDItemSetAttribute
# signature: Boolean MDItemSetAttribute(MDItemRef, CFStringRef name, CFTypeRef attr);
# references:
# https://github.com/WebKit/WebKit/blob/5b8ad34f804c64c944ebe43c02aba88482c2afa8/Source/WTF/wtf/mac/FileSystemMac.MDItemSetAttribute
# https://pyobjc.readthedocs.io/en/latest/metadata/manual.html#objc.loadBundleFunctions
# signature of B@@@ translates to returns BOOL, takes 3 arguments, all objects
# In reality, the function takes references (pointers) to the objects, but pyobjc barfs if
# the function signature is specified using pointers.
# Specifying generic objects allows the bridge to convert the Python objects to the
# appropriate Objective C object pointers.
def MDItemSetAttribute(mditem, name, attr):
"""dummy function definition"""
...
# This will load MDItemSetAttribute from the CoreServices framework into module globals
objc.loadBundleFunctions(
CoreServices.__bundle__,
globals(),
[("MDItemSetAttribute", b"B@@@")],
)
def set_file_metadata(file: str, attribute: str, value: Union[str, List]) -> bool:
"""Set file metadata using undocumented function MDItemSetAttribute
file: path to file
attribute: metadata attribute to set
value: value to set attribute to; must match the type expected by the attribute (e.g. str or list)
Note: date attributes (e.g. kMDItemContentCreationDate) not yet handled.
Returns True if successful, False otherwise.
"""
mditem = CoreServices.MDItemCreate(None, file)
if isinstance(value, list):
value = CoreFoundation.CFArrayCreate(
None, value, len(value), CoreFoundation.kCFTypeArrayCallBacks
)
return MDItemSetAttribute(
mditem,
attribute,
value,
)
def main():
"""Set metadata on macOS files using undocumented function MDItemSetAttribute
Usage: setmd.py <file> <attribute> <type> <value> <value> ...
<file>: path to file
<attribute>: metadata attribute to set, e.g. kMDItemWhereFroms
<type>: type of value to set, e.g. string or array; must match the type expected by the attribute (e.g. str or list)
<value>: value(s) to set attribute to
For example: setmd.py /tmp/test.txt kMDItemWhereFroms array http://example.com
For metadata attributes and types, see https://developer.apple.com/documentation/coreservices/file_metadata/mditem/common_metadata_attribute_keys?language=objc
"""
# super simple argument parsing just for demo purposes
if len(sys.argv) < 5:
print(main.__doc__)
sys.exit(1)
file = sys.argv[1]
attribute = sys.argv[2]
type_ = sys.argv[3]
values = sys.argv[4:]
if type_ == "string":
values = values[0]
try:
attribute = getattr(CoreServices, attribute)
except AttributeError:
print(f"Invalid attribute: {attribute}")
sys.exit(1)
if not set_file_metadata(file, attribute, values):
print(f"Failed to set metadata attribute {attribute} on {file}")
sys.exit(1)
else:
print(f"Successfully set metadata attribute {attribute} on {file} to {values}")
if __name__ == "__main__":
main() It doesn't yet handle types other than string or array (need to reference here for full list of attributes/types) -- kMDItemWhereFroms is an array. Finder comments and Finder tags cannot be set this way. Finder comments must be set by AppleScript and Finder tags by xattr using I verified that both mdls and Finder show the updated kMDItemWhereFroms when set this way. More to come -- will look at adapting this for osxmetadata. |
This is incredible, thank you! I was thinking that I should try using |
Glad it's useful! Check out the gist where I've updated the code to handle all the different types that MDItems can have. I plan to rewrite osxmetadata to use |
@all-contributors add @nk9 for bug |
I've put up a pull request to add @nk9! 🎉 |
@nk9 I've release version 1.0.0 of osxmetadata that fixes this bug and several others. It's a complete rewrite to use the native macOS calls to get/set metadata. It does change the API in breaking ways though so check out the README.md. |
So Rhet, I am kind of in awe how much work you've done over the past two weeks on this. I'm just glad I could be the inspiration for the flurry of activity on this project! And thanks for updating the docs too. However, I have some bad news… I'm still seeing the same behavior. 😬
from osxmetadata import *
url = "https://apple.com"
out_path = "/tmp/test_md.txt"
with open(out_path, "w") as f:
f.write("hi")
meta = OSXMetaData(out_path)
meta.kMDItemWhereFroms = [url]
Am I doing something wrong? |
Strange -- looks like you're doing everything right. This does work in my testing. What version of macOS are you using? I'm on Catalina still so perhaps it's an issue with newer versions of macOS?
|
@nk9 would you mind cloning the repo then running the test suite? See instructions in README_DEV.md for how to install/build the package. |
I'm on Monterey 12.6. Hopefully it's not an OS version issue… but I'll give the tests a run and report back. |
The But I got the tests running, and fortunately, they nearly all work! But there are three failures:
Let me know if I can do anything else to help you narrow this down! |
Good point! I'll do so. I use the zsh-poetry plugin which activates/deactivates poetry shells automatically so I always forget that Glad to see most of the tests are running. Interesting that all three failures appear to be with I could add a test that also checks the output of |
I just noticed something....you were writing your test file to Try with a file that's not in a temporary directory and let me know if you get different results. |
Whoa, what a bizarre quirk! Indeed, when I write something to my home directory instead, the Where froms are set as expected and are shown immediately in Finder. Still doesn't explain the test errors… But maybe this can be closed after all? Probably a good idea to document this |
I'll add a note to the docs about temporary files. I got the test suite running last night in GitHub actions (via a BigSur VM, the latest available in GitHub). Interestingly the same three tests fail with the same result. Something about
|
I've opened a new issue (#68) for the |
This doesn't cause the URL to show in Finder:
The attribute also is not listed with
mdls
:But it IS there, and looks to be correctly formatted as a binary plist:
I'm on Monterey 12.6 (21G115)
The text was updated successfully, but these errors were encountered: