Open RhetTbull opened 1 year ago
You could use an AppleScript bridge such as appscript or my own PyXA for this, or interface with ScriptingBridge via pyobjc:
import ScriptingBridge
notes = ScriptingBridge.SBApplication.alloc().initWithBundleIdentifier_("com.apple.notes")
attachments = notes.attachments()
for attachment in attachments:
print(attachment.URL())
I think the way you're handling AppleScripts now works well for one-off actions but is inefficient for bulk actions (e.g. getting the name of 100s of notes at a time). For reference, in PyXA you can do Application("Notes").notes().name()
to list the name of every note almost instantaneously as it requests all note names in one Apple Event request instead of for each note successively.
Granted, PyXA is a larger library than you need here, so I'd recommend either using pyobjc-ScriptingBridge or implementing some logic to merge bulk requests into singular AppleScript calls.
@SKaplanOfficial I wasn't aware of PyXa. Had played around with ScriptingBridge a bit on another project but got frustrated by lack of documentation. I just toyed around with PyXA -- amazing work! I may just rewrite macnotesapp to use PyXA as it is so much faster. I have a similar project photoscript that uses the same AppleScript techniques that might benefit from PyXA (though photoscript currently supports creating folders/albums, moving photos from one album to another, etc. that don't appear to be supported in PyXA yet).
@RhetTbull Yeah, ScriptingBridge is a bit of a pain to work with, but I've found it to be robust enough for my needs. I wanted PyXA to serve as sort of an extended reference for ScriptingBridge/pyobjc as a whole since the existing documentation was... lacking. PyXA currently supports creating folders and albums, but moving photos is something I'm working on (moving AS objects in general, really, since the implementation varies slightly across different apps).
If you do use PyXA, let me know if you run into any problems -- I'd be happy to help!
PyXA currently supports creating folders and albums
Great! I'll dive into the docs and source over the holiday break. I quickly skimmed the docs and thought I saw those as still "todo". Quick question: is it possible to access an existing album by path (e.g. Folder1/SubFolder1/AlbumName
and get back an album object?) That's something I do a lot for another project and the current AppleScript implementation is awfully slow so I'm in the middle of a big refactor.
Quick question: is it possible to access an existing album by path (e.g.
Folder1/SubFolder1/AlbumName
and get back an album object?)
Yes and no; you can't access it by path in the form you specified, but you can use by_name()
to get an album/folder object quickly. For example:
import PyXA
app = PyXA.Application("Photos")
print(app.folders().by_name("Folder1").folders().by_name("Folder2").albums().by_name("Album1"))
>>> <<class 'PyXA.apps.PhotosApp.XAPhotosAlbum'>Album1, id=F0DA93AB-CC92-23B2-9E16-973F6A062468/L0/040>
This should work near-instantly in most cases, unless your by_
call is to something that has to be queried per-object, e.g. for the body text (not plaintext) of notes.
Perfect -- thanks!
I'm able to add attachments (on Ventura at least, does not work on Catalina, not sure about Big Sur/Monterey) using the following AppleScript:
tell application "Notes"
set theNote to note id "x-coredata://B36F4BF3-D2F0-4997-BD9B-96A608B6D5E0/IMAPNote/p40"
set theFile to POSIX file "/Users/rhet/Downloads/debug.txt"
make new attachment at theNote with data theFile
end tell
Can't figure out a way to do this via ScriptingBridge though.
This works for me to add an attachment on Ventura:
import AppKit
import ScriptingBridge
app = ScriptingBridge.SBApplication.alloc().initWithBundleIdentifier_("com.apple.notes")
note = app.notes()[0]
url = AppKit.NSURL.alloc().initFileURLWithPath_("/Users/exampleUser/Documents/Example.jpg")
new_attachment = app.classForScriptingClass_("attachment").alloc().initWithData_andProperties_(url, {})
note.attachments().addObject_(new_attachment)
But the AppleScript approach is probably good enough (and maybe better on other macOS versions).
Thanks! Will give that a try! I've been digging through your PyXA source to figure out ScriptingBridge and its been immensely helpful but it's still a lot of trial and error. I did get most of macnotesapp rewritten to use ScriptingBridge though and it's much faster now. I'd like to contribute back to PyXA once I get through my current round of projects. (for example, I've got a partial interface to Photos via PhotoKit that's much faster than scripting)
Out of curiosity I benchmarked both the ScriptingBridge implementation and the AppleScript version:
M1 Mac Mini running Ventura:
AppleScript: ~300ms to add an attachment ScriptingBridge: ~80ms to add attachment
ScriptingBridge is the clear winner however, I stuck with AppleScript as the ScriptingBridge sometimes ended up in duplicate attachments being added. The formatting of the note (how the breaks were inserted between attachments) was also wonky with ScriptingBridge when inserting multiple attachments.
Also, calling .attachments()
on the Note object resulted in duplicates regardless of which method was used to add the attachment. That is, the resulting SBElementArray always contained the same attachment twice (verified by looking at total number of attachments which was always 2x what it should be and also that each id was in the SBElementArray twice. No idea why but I'm filtering for those now to eliminate the dupes. This appears to happen only with attachments added via the ScriptingBridge or AppleScript, not with those added natively in the GUI.
Attachments are not currently handled.