I organise my Kontakt sample instruments using a directory of aliases on my internal drive. I recently moved the libraries (that contain the original files these aliases point to), from an HDD to a new SSD. This obviously broke the alias links.
I thought I’d prepared for this by creating an AppleScript that returned the path to an alias’ original file, but I hadn’t accounted for the fact that this would no longer be possible when the original file no longer existed. Silly me.
There are thousands of aliases, so I’m trying to regenerate them using Keyboard Maestro. I’m fairly experienced with KM, and all the pieces are in place except for the retrieval of the original path, which can be seen in the file’s Info window in the Finder (screenshot above).
I have managed to get my aliases up and running again by simply renaming the new drive name to match the old one. This isn’t ideal, but it’s better than having to recreate the whole instrument catalogue from scratch.
If anyone happens to know of a way to direct aliases to a new volume, please let me know. I won’t be holding my breath though, as I’ve done a decent amount of searching and came up short.
Thankyou if you took the time to read any of this.
If the aliases are functional, which it sounds like they now are, you can get the original item property from them.
tell application "Finder"
set salt to selection as alias
original item of salt as text
end tell
This property is also writable so you can replace it with your desired path.
set original item of salt to alias "Newdrive:Users:username:Desktop:filename"
I used the applescript alias here but you should also be able to use file urls and HFS paths as well. You could set up a script to loop through all of the aliases. Just to be paranoid, I’d back up the aliases and do some testing before grinding through changing them all.
As to your original question, I found this solution from Chris Stone. I got an error (an encoding error) when I used it as is so I tweaked it a bit (ie stripped out the error handling) and this worked for me.
use framework "Foundation"
tell application "Finder"
set selectedAliasFile to selection as alias
end tell
set posixFilePath to POSIX path of selectedAliasFile
set originalItem to my nameOfOriginalFileForAlias:posixFilePath
-------------------------------------------------------------------------------------------
--» HANDLERS
-------------------------------------------------------------------------------------------
on nameOfOriginalFileForAlias:posixPath
tell current application's class "NSURL"
# Make URL
set anNSURL to its fileURLWithPath:posixPath
# Load data from alias file
set theData to its bookmarkDataWithContentsOfURL:anNSURL |error|:(missing value)
# Get value as dictionary from data
set theResult to its resourceValuesForKeys:{current application's NSURLPathKey} fromBookmarkData:theData
end tell
# Extract original item and coerce to text
return (theResult's objectForKey:(current application's NSURLPathKey)) as text
end nameOfOriginalFileForAlias:
-------------------------------------------------------------------------------------------
His script was set up to work with the first alias file in a selection of them. I’ve modified it to use just a single selected item. Of course, it can be modified to work with a list of whatever alias files are fed to it.
Thankyou so much for the reply. I’m going to experiment when I get back in tomorrow.
The question is whether I’ll be able to alter the paths the aliases point to before I’ve renamed the volume. In my experiments thus far I’ve run into roadblocks in that regard, but I’m hopeful!
Ok so this works when the originals exist but not after the drive has been renamed. I’ve also noticed that it still does work for some files even then, but it’s not consistent. No idea why.
YES! This returns the path perfectly!!! Thankyou!
Now I just have to figure out how to replace the alias with a new original path.
The bit I haven’t figured out yet is how to get the alias replacement script to process the file whose path is stored as Local__Alias rather than the single alias that is currently selected, in order that I can use KM’s For Each action.
I’ve tried using the usual variable-setting syntax, but I get all sorts of path errors.
Here’s that script as it is:
tell application "Keyboard Maestro Engine"
set inst to system attribute "KMINSTANCE"
set filePath to getvariable "Local__NewOriginal" instance inst
end tell
tell application "Finder"
-- Convert Unix path to HFS format for target file
set targetPath to (POSIX file filePath) as text
set selectedAlias to selection as alias
try
-- Delete the existing alias
set aliasPath to selectedAlias as text
set aliasName to name of selectedAlias
set aliasContainer to container of selectedAlias as alias
delete selectedAlias
-- Create new alias with same name but new target
make new alias file at aliasContainer to file targetPath with properties {name:aliasName}
on error errMsg
display dialog "Error modifying alias: " & errMsg
end try
end tell
As you can see, you can grab a KM variable like so:
set inst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
set ASVar to getvariable "Local__KMVar" instance inst
end tell
But I’m as yet unable to get that to work. I’m going to ask for help on the KM forum and will report back if I get anywhere. So close!
Try posix path of instead of posix file. The former coerces the path to a posix path, the latter works with a posix path. That said, I’m not sure why you’re using posix paths at all.
As a general thought… you could probably dispense with some of the coercions.
Finally, you could try to modify the existing alias file rather than making a new one. The command is in my post above.
To be honest, my AppleScript skills aren’t really up to this and I’m relying on Claude.ai for help. I’ve asked it about a million questions and can’t seem to get it to work. I thought my existing version was working, but it turns out it can’t find the target file much of the time. I’m a bit lost now.
I’m sure you’re right, but I don’t really know what I’m doing and Claude isn’t being much help.
I’d love that to work, as it would probably be much faster than deleting the old aliases and generating new ones. Unfortunately, it doesn’t seem to do anything.
First of all, make a copy of your alias folder.
Rename your hard drive.
Change the newVolumeName value in the script to this new name.
Select one alias file and run the script.
This alias file should now open to the right file on the renamed volume.
If you need help to loop thru all aliases, let me know.
use framework "Foundation"
use framework "AppKit"
use scripting additions
property oldVolumeName : "AUDIO"
property newVolumeName : "New Volume Name"
-- get the selected alias file in Finder
tell application "Finder" to set theSel to item 1 of (selection as alias list)
set aliasFileURL to current application's NSURL's fileURLWithPath:(POSIX path of theSel)
-- get data from the actual alias file
set theData to current application's NSURL's bookmarkDataWithContentsOfURL:aliasFileURL |error|:(missing value)
if theData = missing value then return "no data"
set theResult to current application's |NSURL|'s resourceValuesForKeys:{"NSURLBookmarkAllPropertiesKey"} fromBookmarkData:theData
-- get the old original item
set oldTarget to (theResult's valueForKeyPath:"NSURLBookmarkAllPropertiesKey._NSURLPathKey")
if oldTarget = missing value then return "no target"
-- rebuild the path to the original item by changinng the volume name
set newTarget to (oldTarget's stringByReplacingOccurrencesOfString:oldVolumeName withString:newVolumeName options:1024 range:{0, oldTarget's |length|()})
set newTarget to current application's NSURL's fileURLWithPath:newTarget
-- delete the old alias file
set theFileManager to current application's NSFileManager's defaultManager()
theFileManager's removeItemAtURL:aliasFileURL |error|:(missing value)
-- make a new alias file
set bookmarkData to newTarget's bookmarkDataWithOptions:1024 includingResourceValuesForKeys:{} relativeToURL:(missing value) |error|:(missing value)
set {theResult, theError} to current application's NSURL's writeBookmarkData:bookmarkData toURL:aliasFileURL options:0 |error|:(reference)
if (not theResult) then return theError's localizedDescription()
My goodness… The kindness of strangers always gets me. Thankyou so much!
That works perfectly!
Yes, looping through all aliases is something I wouldn’t know how to do without using Keyboard Maestro. If you could help me with that it would be great. Presumably, the way to do it would be to search the alias folder at root level for files of kind (other) “alias”, select them and then run the script.
Two questions if you don’t mind:
1: I’ve noticed that one or two of my older libraries have been moved around a bit between folders over the years, so their aliases are broken regardless of the volume. When I run your script on them, it deletes the alias and doesn’t create a new one.
With that in mind, what would you recommend as a course of action? Should I go through and fix any broken aliases before renaming the drive? Is there a way to find broken aliases? Perhaps, to be on the safe side, your script shouldn’t delete aliases unless a new one can be successfully created?
2: The current path to my Kontakt folder is
/Volumes/AUDIO/Instrument Libraries/KONTAKT.
If I wanted to change the path to
/Volumes/Libraries/KONTAKT
would it be as simple as setting
oldVolumeName to AUDIO/Instrument Libraries
and oldVolumeName to Libraries?
if I understood you correctly, Libraries is the name of the volume, right?
If the answer is yes then the modification is easy.
Try this updated script. One time on a valid alias and a second time on a broken one.
It will not delete the alias file if the original item has been moved.
Instead, the broken alias file will be marked with a Finder tag.
After you run the script, search for the Finder tag Broken Alias and give it a color.
This way, it will be easy to identify those files.
use framework "Foundation"
use framework "AppKit"
use scripting additions
property oldVolumeName : "/AUDIO/Instrument Libraries"
property newVolumeName : "/Libraries"
-- get the selected alias file in Finder
tell application "Finder" to set theSel to item 1 of (selection as alias list)
set aliasFileURL to current application's NSURL's fileURLWithPath:(POSIX path of theSel)
-- get data from the actual alias file
set theData to current application's NSURL's bookmarkDataWithContentsOfURL:aliasFileURL |error|:(missing value)
if theData = missing value then return "no data"
set theResult to current application's |NSURL|'s resourceValuesForKeys:{"NSURLBookmarkAllPropertiesKey"} fromBookmarkData:theData
-- get the old original item
set oldTarget to (theResult's valueForKeyPath:"NSURLBookmarkAllPropertiesKey._NSURLPathKey")
if oldTarget = missing value then return "no target"
-- rebuild the path to the original item by changinng the volume name
set newTarget to (oldTarget's stringByReplacingOccurrencesOfString:oldVolumeName withString:newVolumeName options:1024 range:{0, oldTarget's |length|()})
set newTarget to current application's NSURL's fileURLWithPath:newTarget
-- check out if original file exists
set fileExists to newTarget's checkResourceIsReachableAndReturnError:(missing value)
-- if it exists, update the alias
if fileExists then
-- delete the old alias file
set theFileManager to current application's NSFileManager's defaultManager()
theFileManager's removeItemAtURL:aliasFileURL |error|:(missing value)
-- make a new alias file
set bookmarkData to newTarget's bookmarkDataWithOptions:1024 includingResourceValuesForKeys:{} relativeToURL:(missing value) |error|:(missing value)
set {theResult, theError} to current application's NSURL's writeBookmarkData:bookmarkData toURL:aliasFileURL options:0 |error|:(reference)
if (not theResult) then return theError's localizedDescription()
end if
-- if it does not exist, mark it with a tag
if not fileExists then
aliasFileURL's setResourceValue:"Broken Alias" forKey:(current application's NSURLTagNamesKey) |error|:(missing value)
end if
It works great, one at a time, on valid aliases but if the alias is broken, it doesn’t add a tag, as far as I can see from looking at the Info window.
It’s all good though. I’ll set aside a couple of days to go through them all manully. You’ve helped me massively already and I don’t want to take up any more of your time.