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
127 stars 3 forks source link

Question: Can OSXMetaData access Finder Getinfo information? #104

Open macdeport opened 6 months ago

macdeport commented 6 months ago

Thanks for OSXMetaData. Can OSXMetaData access Finder Getinfo information?

test-enedis jpg-alias-finder-get-info
RhetTbull commented 6 months ago

OSXMetaData can access any metadata that is available to Spotlight and much of this is what shows in the GetInfo panel. This includes tags/keywords, Finder comments, and specific metadata about an item. For example, I did "GetInfo" on a PDF file and Finder displayed Title, authors, version, pages, resolution, content creator, and encoding software in the "More Info". This is all available via OSXMetaData as kMDItemTitle, kMDItemAuthors, kMDItemVersion, kMDItemNumberOfPages, kMDItemPageHeight, kMDItemPageWidth, kMDItemCreator, and kMDItemEncodingApplications.

What specific information are you looking to access?

Edit: when I typed this the photo wasn't loading. It did load after I submitted the comment. The screenshot shows type, size, path, creation date, modified date. This is all file system data and in Python you should be able to get all of this through stat(). Determining if a file is an alias will take a bit more work on macOS via Python. I don't think there's a straightforward way to figure that out.

macdeport commented 6 months ago

Thanks for your quick detailed answer.

What I need, and don't know how to do in Python, is to determine the source file of an alias (‘Original’ in the screenshot that ended up loading).

I cannot figure out how to get this path through stat()

On the other hand, this code retrieved from the web works, but I don't know how to code the ‘bridge’ version in Python:

use framework "Foundation"
use AppleScript version "2.4" -- Yosemite 10.10 or later
use scripting additions

property NSURL : a reference to current application's NSURL
property NSURLPathKey : a reference to current application's NSURLPathKey

set af to POSIX path of (choose file default location (path to desktop))

set aliasURL to NSURL's fileURLWithPath:af
set bookmarkData to NSURL's bookmarkDataWithContentsOfURL:aliasURL |error|:0
set bmvalue to NSURL's resourceValuesForKeys:[NSURLPathKey] fromBookmarkData:bookmarkData

display dialog ((bmvalue's objectForKey:"_NSURLPathKey")'s stringByAbbreviatingWithTildeInPath()) as text

"Determining if a file is an alias": it seems that you gave the solution yourself, unless I'm mistaken:

from osxmetadata import *

fp='/Users/alain/Documents/Logiciels/Developpement/pdf/test-enedis.jpg alias'
md = OSXMetaData(fp)
print(md.kind)

Alias

RhetTbull commented 6 months ago

"Determining if a file is an alias": it seems that you gave the solution yourself, unless I'm mistaken:

You're right! That appears to work.

What I need, and don't know how to do in Python, is to determine the source file of an alias (‘Original’ in the screenshot that ended up loading).

Ah, this should work:

"""Resolve path to macOS alias file"""

from Foundation import (
    NSURL,
    NSData,
    NSError,
    NSURLBookmarkResolutionWithoutMounting,
    NSURLBookmarkResolutionWithoutUI,
)

def resolve_alias_path(alias_path: str) -> str:
    """Given a path to a macOS alias file, return the resolved path to the original file"""
    options = NSURLBookmarkResolutionWithoutUI | NSURLBookmarkResolutionWithoutMounting
    alias_url = NSURL.fileURLWithPath_(alias_path)
    bookmark, error = NSURL.bookmarkDataWithContentsOfURL_error_(alias_url, None)
    if error:
        raise ValueError(f"Error creating bookmark data: {error}")

    resolved_url, is_stale, error = (
        NSURL.URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(
            bookmark, options, None, None, None
        )
    )
    if error:
        raise ValueError(f"Error resolving bookmark data: {error}")

    return str(resolved_url.path())

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print("Usage: python alias.py <alias_path>")
        sys.exit(1)

    alias_path = sys.argv[1]
    resolved_path = resolve_alias_path(alias_path)
    print(f"alias: {alias_path}\noriginal: {resolved_path}")

You'll need pyobjc installed but if you've installed OSXMetaData to determine if it's an alias, that will have already installed the pyobjc pre-requisites.

macdeport commented 6 months ago

Thank you very much for the time and expertise you've devoted to a functional solution.

Regarding alias detection, for your information, here is the functional code I was using before I met OSXMetaData:

# Is fp alias?
# https://github.com/xattr/xattr
# https://pypi.org/project/xattr/
from xattr import getxattr,listxattr,XATTR_NOFOLLOW

l_alias = False; mdx_tag = 'com.apple.FinderInfo'; s = ''
if mdx_tag in listxattr(fp, XATTR_NOFOLLOW):
    s = getxattr(fp, mdx_tag, XATTR_NOFOLLOW)
    # macOS Ventura 13.6.6
    stype = (b'alisMACS\x80', b'fapaMACS\x80', b'fdrpMACS\x80')
    if s and s.startswith(stype):
        l_alias = True