This version populates the attachment file list with aliases to where Notes keeps the attachments. It works for me, BUT it carries an enormous health warning. It parses Notes’s database file using sed, which isn’t the appropriate tool for the job. So while it works with my test notes, there may be cases where it doesn’t correctly identify the ends of the file URLs. In such cases, it should error when it tries to coerce the NSURLs derived from them to aliases. Hopefully someone who knows how to script database queries will be able to improve it.
Edit: Script reposted with redundant code from the previous version removed, the getAttachmentLookup() handler rewritten entirely in ASObjC, and the Evernote code re-enabled.
(*
====================================================
[EN] Import Apple Notes into Evernote
====================================================
DATE: 2013-10-24
AUTHOR: d.b.walker
REVISED BY: JMichaelTX on 2016-03-28 to make BUG fix. <https://discussion.evernote.com/topic/64814-apple-notes-app/#comment-395941>
REF:
¢ Importing from Apple Mail.app's Notes - Mac Help - Evernote User Forum
¢ https://discussion.evernote.com/topic/4046-importing-from-apple-mailapps-notes/?do=findComment&comment=236445
Posted 24 Oct 2013
Modified this script to work with Mavericks Notes, which is no longer in the mail app.
Added the original creation and modification dates
Added multiple tags - replace with your own
Did not add the long note name fix (I needed to preserve my note names)
====================================================
FURTHER DEVELOPED BY: Nigel Garvey 2017-03-21/22/23, based on information in the Evernote fora, to allow a choice of Notes source folder(s) and to handle attachments.
REVISION BY NG, 2017-03-29: "Note" folder names in the temporary desktop hierarchy for attachments now based on the notes' names. Any path delimiters in potential folder names now replaced with dashes.
FURTHER REVSION 2017-04-03/4: The attachment files list is now populated with aliases to Notes's copies of the files.
CAVEATS:
1. I don't have Evernote and can't test that part of the code.
2. Only intended for use with Notes's "On my Mac" account.
3. Any attachments are simply "appended" to the Evernote notes in the order they happen to be returned by Notes.
4. The effect in Evernote of Notes's references to the attachments in the note HTML is unknown.
5. Although the script works for me, there's a possibility it may not correctly identify some file URLs.
*)
use AppleScript version "2.5" -- Mac OS 10.11 (El Capitan) or later. (For NSURL coercions to alias.)
use framework "Foundation"
use scripting additions
main()
on main()
-- User choice of one or more Notes folders (by name).
tell application "Notes"
activate
set folderNames to name of folders
set chosenFolderNames to (choose from list folderNames with multiple selections allowed)
if (chosenFolderNames is false) then error number -128 -- Cancel button.
end tell
-- Get an NSDictionary which has the attachment CIDs as keys and the corresponding file URLs as values.
set attachmentLookup to getAttachmentLookup()
-- Repeat with each chosen folder name:
repeat with i from 1 to (count chosenFolderNames)
-- Get all the notes in the folder with this name.
set thisFolderName to item i of chosenFolderNames
tell application "Notes" to set theNotes to notes of folder thisFolderName
-- Repeat with each note in the folder:
repeat with j from 1 to (count theNotes)
set thisNote to item j of theNotes
tell application "Notes"
-- Get the relevant note data.
set myTitle to the name of thisNote
set myText to the body of thisNote
set myCreateDate to the creation date of thisNote
set myModDate to the modification date of thisNote
set myAttachments to the attachments of thisNote
end tell
-- Get alias specifiers for any attachments.
set attachmentFiles to {}
repeat with thisAttachment in myAttachments
-- Get this attachment's content identifier (cid) from Notes.
tell application "Notes" to set thisCID to content identifier of thisAttachment
-- Use it to look up the corresponding NSURL in the lookup dictionary and store the result as a file specifier.
set end of attachmentFiles to (attachmentLookup's valueForKey:(thisCID)) as alias
end repeat
tell application "Evernote"
set myNote to create note with text myTitle title myTitle notebook "Imported From Notes" tags ["imported_from_notes"]
set the HTML content of myNote to myText
repeat with thisFile in attachmentFiles
tell myNote to append attachment thisFile
end
set the creation date of myNote to myCreateDate
set the modification date of myNote to myModDate
end tell
end repeat
end repeat
end main
-- Create a lookup dictionary (NSDictionary) which has all the available attachment CIDs as keys and URLs to the corresponding files as values.
on getAttachmentLookup()
set |⌘| to current application
set fileManager to |⌘|'s class "NSFileManager"'s defaultManager()
-- Get an NSURL to the user Library folder and thence to the folder containing Note's database file(s).
set userLibraryFolderURL to (fileManager's URLForDirectory:(|⌘|'s NSLibraryDirectory) inDomain:(|⌘|'s NSUserDomainMask) appropriateForURL:(missing value) create:(false) |error|:(missing value))
set NotesDBFolderURL to userLibraryFolderURL's URLByAppendingPathComponent:("Containers/com.apple.Notes/Data/Library/Notes")
-- Or of course:
(* set NotesDBFolderPath to |⌘|'s class "NSString"'s stringWithString:("~/Library/Containers/com.apple.Notes/Data/Library/Notes")
set NotesDBFolderPath to NotesDBFolderPath's stringByExpandingTildeInPath()
set NotesDBFolderURL to |⌘|'s class "NSURL"'s fileURLWithPath:(NotesDBFolderPath) *)
-- On my system, the database file of interest has a name ending with "-wal".
set databaseCandidates to fileManager's contentsOfDirectoryAtURL:(NotesDBFolderURL) includingPropertiesForKeys:({}) options:(|⌘|'s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set walFilter to |⌘|'s class "NSPredicate"'s predicateWithFormat:("path ENDSWITH '-wal'")
set databaseURL to (databaseCandidates's filteredArrayUsingPredicate:(walFilter))'s firstObject()
-- The file's contents are binary data, but this hack involves treating them as ISO Latin1 encoded text.
set databaseText to |⌘|'s class "NSString"'s stringWithContentsOfURL:(databaseURL) encoding:(|⌘|'s NSISOLatin1StringEncoding) |error|:(missing value)
-- Set a regex to scan for instances of attachment content identifiers (CIDs) in angle brackets ("<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@home>" on my machine, simply "<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>" on Yvan's) and of URLs to files in Notes's attachments folder hierarchy ("file:///Users/username/Library/Containers/com.apple.Notes/Data/Library/CoreData/Attachments/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/filename").
set searchRegex to |⌘|'s class "NSRegularExpression"'s regularExpressionWithPattern:("(?<=<)[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}(?:@home)?(?=>)|file:///Users/[^/]++/Library/Containers/com\\.apple\\.Notes/Data/Library/CoreData/Attachments/[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}/[\\u0021-\\u007f]++") options:(0) |error|:(missing value)
-- For additional security, find out where the first CID occurs in the "text".
set searchStart to (databaseText's rangeOfString:("<[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}(?:@home)?>") options:(|⌘|'s NSRegularExpressionSearch))'s location()
-- Start the search proper from there and get the ranges of all the matches. (No harm if none.)
set matchRanges to (searchRegex's matchesInString:(databaseText) options:(0) range:({searchStart, (databaseText's |length|()) - searchStart}))'s valueForKey:("range")
-- The matches should be alternating instances of CIDs and file URLs, but the code below can easily be modified if this turns out not always to be the case.
-- Extract the CIDs to one array and NSURL versions of the file URLs to another.
set cidPrefix to |⌘|'s class "NSString"'s stringWithString:("cid:")
set theCIDs to |⌘|'s class "NSMutableArray"'s new()
set theURLs to |⌘|'s class "NSMutableArray"'s new()
set i to 1
set j to 2
set matchCount to matchRanges's |count|()
repeat until (j > matchCount)
set thisCID to databaseText's substringWithRange:(item i of matchRanges)
set thisURL to databaseText's substringWithRange:(item j of matchRanges)
tell theCIDs to addObject:(cidPrefix's stringByAppendingString:(thisCID))
tell theURLs to addObject:(|⌘|'s class "NSURL"'s URLWithString:(thisURL))
set i to j + 1
set j to i + 1
end repeat
-- Make and return an NSDictionary with the CIDs as the keys and the NSURLs as the values.
-- The CID/NSURL pairs will no doubt be duplicated in the lists, but not in the dictionary.
return |⌘|'s class "NSDictionary"'s dictionaryWithObjects:(theURLs) forKeys:(theCIDs)
end getAttachmentLookup