error "The variable posixFilePath is not defined." number -2753 from "posixFilePath"
Getting the above error after I select multiple files? have tried change some names of variables-no luck.
se AppleScript version "2.4" # requires at least Yosemite
use scripting additions
use framework "Foundation"
-- Creates a new folder. There is no error if the folder already exists, and it will also create intermediate folders if required
on createFolder:POSIXPath
set |⌘| to current application
set theFolderURL to |⌘|'s |NSURL|'s fileURLWithPath:POSIXPath
set theFileManager to |⌘|'s NSFileManager's defaultManager()
set {theResult, theError} to theFileManager's createDirectoryAtURL:theFolderURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
end createFolder:
set theFiles to choose file with prompt "Please select an MP3 file" of type {"mp3"} with multiple selections allowed
repeat with aFile in theFiles
my treatThisFile(aFile)
end repeat
on treatThisFile(theFile)
set posixFilePath to the quoted form of (POSIX path of theFile)
end treatThisFile
set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")
set artistTag to (do shell script "mdls " & posixFilePath & " -name kMDItemAuthors")
set tempoTag to (do shell script "mdls " & posixFilePath & " -name kMDItemTempo")
and the rest........
I tested and never got the reported error but I’m not sure that you used the posted script. NEVER use copy/paste to grab a posted script.
Use the dedicated button : [Open this Scriplet in your Editor :] which will pass the correct script.
I edited slightly the code posted in message #13 because I discovered a possible glitch.
Here, for some files, the variable theTitle was returned as “null” so it generated duplicates.
In such case, I choose to replace “null” by the original name deprieved of the extension.
The new lines are commented as # ADDED
With them the script behaved flawlessly - at least if the folder in which it is supposed to create folders have the required permissions.
In the script you will see which path I was forced to use on the HD used for tests. –set DestFolder to (“/Volumes/Macintosh HD/Users/Important/Test/” & theGenre & “/” & theArtist)
Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) vendredi 14 avril 2017 14:22:57
To give Yvan a well-earned break, here’s something I’ve been working on today. It’s in ASObjC for much of the process and uses just one shell script call to get all the required attribute values for all the files. It assumes the attributes are actually set (if not, you’ll see “(null)” in the file and folder names) and doesn’t contain any provision for name clashes in the destination folders. It can be run directly or used as a folder action.
use AppleScript version "2.4" # requires at least Yosemite
use scripting additions
use framework "Foundation"
property destinationRootPath : "/Volumes/Production/test/" -- Set as required.
on run -- For when testing.
set theFiles to (choose file with prompt "Please select one or more MP3 files" of type {"mp3"} with multiple selections allowed)
mainBusiness(theFiles)
end run
on adding folder items to thisFolder after receiving addedItems -- For when run as a folder action.
mainBusiness(addedItems)
end adding folder items to
-- Original handler by NG. This version by Shane Stanley, debugged by NG. NG's debugging debugged by Yvan Koenig.
on mainBusiness(theFiles)
-- Get the root path in a convenient form for use with ASObjC.
local destinationRootPath
set destinationRootPath to current application's class "NSString"'s stringWithString:(my destinationRootPath)
repeat with thisFile in theFiles
set thePath to POSIX path of thisFile
set thePath to (current application's NSString's stringWithString:thePath)
set theName to thePath's lastPathComponent()
set theContainer to thePath's stringByDeletingLastPathComponent()
set thePred to current application's NSPredicate's predicateWithFormat_("kMDItemFSName == %@", theName)
-- build and start query
set theQuery to current application's NSMetadataQuery's new()
(theQuery's setPredicate:thePred)
(theQuery's setSearchScopes:{theContainer})
theQuery's startQuery()
-- loop until it's stopped gathering
repeat while theQuery's isGathering() as boolean
delay 0.1
end repeat
-- stop and get the first NSMetadataItem (there can only be one in this case)
theQuery's stopQuery()
set metaItem to (theQuery's resultAtIndex:0)
-- get the required attribute values, or default alternatives where the attributes weren't found.
-- Firstly the artist ("authors") and genre values.
set theArtist to (metaItem's valueForAttribute:(current application's NSMetadataItemAuthorsKey))
if (theArtist is missing value) then
set theArtist to "(no artist)"
else
set theArtist to (theArtist's componentsJoinedByString:(","))
end if
set theGenre to (metaItem's valueForAttribute:(current application's NSMetadataItemMusicalGenreKey))
if (theGenre is missing value) then set theGenre to "(no genre)"
-- Derive a path to the destination folder from the root path, genre, and artist.
set destinationFolderPath to destinationRootPath's stringByAppendingFormat_("%@/%@", theGenre, theArtist)
-- Create the folder, if necessary.
createFolder(destinationFolderPath)
-- Get the key, tempo, and title values.
set theKey to (metaItem's valueForAttribute:(current application's NSMetadataItemKeySignatureKey))
if (theKey is missing value) then set theKey to "(no key signature)"
set theTempo to (metaItem's valueForAttribute:(current application's NSMetadataItemTempoKey))
if (theTempo is missing value) then set theTempo to "(no tempo)"
set theTitle to (metaItem's valueForAttribute:(current application's NSMetadataItemTitleKey))
if (theTitle is missing value) then set theTitle to "(no title)"
-- Derive a destination path for the file with a new name made up from these details.
set destinationFilePath to (current application's NSString's stringWithFormat_("%@/%@-%@ %@.mp3", destinationFolderPath, theTempo, theKey, theTitle))
-- Move the file to the new destination. It'll be renamed in the process.
moveFile from thePath to destinationFilePath
end repeat
end mainBusiness
-- Creates a new folder. There is no error if the folder already exists, and it will also create intermediate folders if required
on createFolder(POSIXPath)
set |⌘| to current application
set theFolderURL to |⌘|'s |NSURL|'s fileURLWithPath:POSIXPath
set theFileManager to |⌘|'s NSFileManager's defaultManager()
set {theResult, theError} to theFileManager's createDirectoryAtURL:theFolderURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
end createFolder
-- Moves a file from one path to another, giving the file the name specified in the destination path.
on moveFile from currentPath to destinationPath
set {theResult, theError} to current application's class "NSFileManager"'s defaultManager()'s moveItemAtPath:(currentPath) toPath:(destinationPath) |error|:(reference)
if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
end moveFile
Edit:mainBusiness() handler replaced with the working version of Shane’s. (See posts #19 & #21 below.)
I was hoping that somebody would give a code using ASObjC to extract the metadatas from the very beginning.
It’s why I choose to answer only to precise questions about a single point.
Alas at long we reach the step giving a script doing the entire job using old fashioned code.
Now it’s time to study your modern script.
CAUTION : at first read it seems that you don’t treat the case when theTitle is “null”.
I faced this case in my old fashioned code. In fact two files were in this case so the code built two identical destinationFilePath which generated an error. It seems that your code will behave the same in such case.
Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) vendredi 14 avril 2017 18:33:27
Since it’s not really helpful to offer ASObjC code to someone who’s still coming to grips with basic AppleScript, here’s an ASObjC-free version. It makes the same assumption about the files’ relevant attributes actually being set and makes no provision for name clashes in the destination folder.
The test for the destination folder’s existence is to try coercing its HFS path to alias. If the attempt fails, the folder doesn’t exist. This has always worked for me, but I seem to remember reading a year or four ago that was possible at the time to create aliases to non-existent items. If the coercion test doesn’t work on your system, the test will have to be done with the Finder or System Events instead, which is easy to arrange but is slightly slower.
property destinationRootPath : "Production:test:" -- An HFS path this time. Set as required.
on run -- For when testing.
set theFiles to (choose file with prompt "Please select one or more MP3 files" of type {"mp3"} with multiple selections allowed)
mainBusiness(theFiles)
end run
on adding folder items to thisFolder after receiving addedItems -- For when run as a folder action.
mainBusiness(addedItems)
end adding folder items to
on mainBusiness(theFiles)
-- Construct a text containing the quoted forms of the files' POSIX paths separated by spaces.
set quotedPOSIXPaths to {}
repeat with thisFile in theFiles
set end of quotedPOSIXPaths to quoted form of POSIX path of thisFile
end repeat
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to space
set quotedPathSequence to quotedPOSIXPaths as text
set AppleScript's text item delimiters to astid
-- Get a (character id 0)-delimited text containing all the required attribute values for all the files.
-- The groups of attribute values are returned in the same order as the files in the mdls command. But within each group, the values are in alphabetical order of attribute name, no matter what their order in the shell script. The attribute order has been alphabeticalised here to reflect this and to allow for the situation possibly changing in later Mac OS versions.
-- IF AN ATTRIBUTE HASN'T BEEN SET, ITS VALUE IS RETURNED AS "(null)".
set allAttributeValues to (do shell script "mdls -raw -name kMDItemAuthors -name kMDItemKeySignature -name kMDItemMusicalGenre -name kMDItemTempo -name kMDItemTitle " & quotedPathSequence)
-- Get a list of the attribute values from the shell script result.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to character id 0
set allAttributeValues to allAttributeValues's text items
set AppleScript's text item delimiters to astid
-- Go through the attribute values in groups of five (five attributes per file).
repeat with i from 1 to (count allAttributeValues) by 5
-- Get the artist (ie. "authors") and genre values.
set theArtist to (item i of allAttributeValues)
-- The artist value is a parenthesised collection of authors' names in quotes and bracketed by returns. Reduce it to just the names, separated by commas if there are more than one. If the value doesn't contain quotes, it's "(null)".
if (theArtist contains quote) then
-- (Cut the quotes and anything between them and the ends of the string.)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to quote
set theArtist to theArtist's text items
set AppleScript's text item delimiters to ""
set theArtist to (items 2 thru -2 of theArtist) as text
-- (Replace any remaining returns with commas.)
set AppleScript's text item delimiters to return
set theArtist to theArtist's text items
set AppleScript's text item delimiters to ","
set theArtist to theArtist as text
set AppleScript's text item delimiters to astid
else
set theArtist to "(no artist)"
end if
set theGenre to item (i + 2) of allAttributeValues
if (theGenre is "(null)") then set theGenre to "(no genre)"
-- Derive a path to the destination folder from the root path, genre, and artist.
set destinationFolderPath to (destinationRootPath & ":" & theGenre & ":" & theArtist)
-- Create the folder, if necessary.
set destinationFolder to createFolder(destinationFolderPath)
-- Get the key, tempo, and title values.
set theKey to item (i + 1) of allAttributeValues
if (theKey is "(null)") then set theKey to "(no key signature)"
set theTempo to item (i + 3) of allAttributeValues
if (theTempo is "(null)") then set theTempo to "(no tempo)"
set theTitle to item (i + 4) of allAttributeValues
if (theTitle is "(null)") then set theTitle to "(no title)"
-- Construct a new name the file from these details.
set newFileName to (theTempo & "-" & theKey & space & theTitle & ".mp3")
-- Get the current alias to the file in question.
set currentFile to item (i div 5 + 1) of theFiles
-- Move the file to the new destination and rename it.
moveFile at currentFile into destinationFolder under newFileName
end repeat
end mainBusiness
-- Recursively tests for the existence of a folder in the given path and creates it if necessary.
on createFolder(HFSPath)
try
-- Try returning the folder path as an alias.
return HFSPath as alias
on error
-- If the coercion fails, the folder doesn't exist.
-- If the path doesn't contain any colons, it's for a volume which isn't mounted or doesn't exist.
if (HFSPath does not contain ":") then error "The volume \"" & HFSPath & "\" isn't mounted." number -1728
-- Otherwise, separate the folder name and the path to the containing folder.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ":"
set containerPath to text 1 thru text item -2 of HFSPath
set folderName to text item -1 of HFSPath
set AppleScript's text item delimiters to astid
-- Test if the container folder exists and create that if necessary.
createFolder(containerPath)
-- On returning from the recursion, create the folder in the container folder and return an alias to it.
tell application "Finder" to return (make new folder at folder containerPath with properties {name:folderName}) as alias
end try
end createFolder
-- Moves the file whose alias is given to the folder whose alias is given and renames it with the name given. Renaming is done after the move in order not to retrigger the folder action when the script's run that way.
on moveFile at currentFile into destinationFolder under newFileName
tell application "Finder"
set name of (move currentFile to destinationFolder) to newFileName
end tell
end moveFile
EDIT:mainBusiness() handler updated to treat “(null)” results in the same way as in the script two posts up.
This is an alternative for Nigel’s mainBusiness() ASObjC handler, using ASObjC instead of do shell script and mdls to retrieve the data.
Someone recently asked elsewhere why one would use the seemingly longer ASObjC method instead of a simple shell script, and I think this is a good example of how the comparison can pan out in the (almost) real world. Getting the values from Spotlight takes more code, but the result needs less manipulation.
It may still need some tweaks – I haven’t tested it – but it might be of interest:
on mainBusiness(theFiles)
repeat with thisFile in theFiles
set thePath to POSIX path of thisFile
set thePath to (current application's NSString's stringWithString:thePath)
set theName to thePath's lastPathComponent()
set theContainer to thePath's stringByDeletingLastPathComponent()
set thePred to current application's NSPredicate's predicateWithFormat_("kMDItemFSName == %@", theName)
-- build and start query
set theQuery to current application's NSMetadataQuery's new()
(theQuery's setPredicate:thePred)
(theQuery's setSearchScopes:{theContainer})
theQuery's startQuery()
-- loop until it's stopped gathering
repeat while theQuery's isGathering() as boolean
delay 0.1
end repeat
-- stop and get the first NSMetadataItem (there can only be one in this case)
theQuery's stopQuery()
set metaItem to (theQuery's resultAtIndex:0)
-- get the values from the metadata item
set theArtist to (metaItem's valueForAttribute:(current application's NSMetadataItemAuthorsKey))
if theArtist = missing value then
set theArtist to "(no artist)"
else
set theArtist to (theArtist's componentsJoinedByString:", ") as text
end if
set theGenre to (metaItem's valueForAttribute:(current application's NSMetadataItemGenreKey))
-- Derive a path to the destination folder from the root path, genre, and artist.
set destinationFolderPath to destinationRootPath's stringByAppendingFormat_("%@/%@", theGenre, theArtist)
-- Create the folder, if necessary.
createFolder(destinationFolderPath)
-- Get the key, tempo, and title values.
set theKey to (metaItem's valueForAttribute:(current application's NSMetadataItemKeySignatureKey))
set theTempo to (metaItem's valueForAttribute:(current application's NSMetadataItemTempoKey))
set theTitle to (metaItem's valueForAttribute:(current application's NSMetadataItemTitleKey))
-- Derive a destination path for the file with a new name made up from these details.
set destinationFilePath to (current application's NSString's stringwithFormat_("%@/%@-%@ %@.mp3", destinationRootPath, theTempo, theKey, theTitle))
moveFile from thePath to destinationFilePath
end repeat
end mainBusiness
Here’s a debugged (I hope!) version. Besides a few minor typo corrections, I’ve ditched metaItem’s attributes(), which is just an array of the keys found. (I see you’ve now done the same yourself.) The attributes of interest have to be read from metaItem itself, officially with a method called valueForAttribute:.
I haven’t changed it here, but Yvan’s pointed out to me off-forum that the original file names might be used instead of the title attributes, with a suitable adjustment to the destination path format string.
on mainBusiness(theFiles)
-- Get the root path in a convenient form for use with ASObjC.
local destinationRootPath
set destinationRootPath to current application's class "NSString"'s stringWithString:(my destinationRootPath)
repeat with thisFile in theFiles
set thePath to POSIX path of thisFile
set thePath to (current application's NSString's stringWithString:thePath)
set theName to thePath's lastPathComponent()
set theContainer to thePath's stringByDeletingLastPathComponent()
set thePred to current application's NSPredicate's predicateWithFormat_("kMDItemFSName == %@", theName)
-- build and start query
set theQuery to current application's NSMetadataQuery's new()
(theQuery's setPredicate:thePred)
(theQuery's setSearchScopes:{theContainer})
theQuery's startQuery()
-- loop until it's stopped gathering
repeat while theQuery's isGathering() as boolean
delay 0.1
end repeat
-- stop and get the first NSMetadataItem (there can only be one in this case)
theQuery's stopQuery()
set metaItem to (theQuery's resultAtIndex:0)
-- get the required attribute values, or default alternatives where the attributes weren't found.
-- Firstly the artist ("authors") and genre values.
set theArtist to (metaItem's valueForAttribute:(current application's NSMetadataItemAuthorsKey))
if (theArtist is missing value) then
set theArtist to "(no artist)"
else
set theArtist to (theArtist's componentsJoinedByString:(","))
end if
set theGenre to (metaItem's valueForAttribute:(current application's NSMetadataItemMusicalGenreKey))
if (theGenre is missing value) then set theGenre to "(no genre)"
-- Derive a path to the destination folder from the root path, genre, and artist.
set destinationFolderPath to destinationRootPath's stringByAppendingFormat_("%@/%@", theGenre, theArtist)
-- Create the folder, if necessary.
createFolder(destinationFolderPath)
-- Get the key, tempo, and title values.
set theKey to (metaItem's valueForAttribute:(current application's NSMetadataItemKeySignatureKey))
if (theKey is missing value) then set theKey to "(no key signature)"
set theTempo to (metaItem's valueForAttribute:(current application's NSMetadataItemTempoKey))
if (theTempo is missing value) then set theTempo to "(no tempo)"
set theTitle to (metaItem's valueForAttribute:(current application's NSMetadataItemTitleKey))
if (theTitle is missing value) then set theTitle to "(no title)"
-- Derive a destination path for the file with a new name made up from these details.
set destinationFilePath to (current application's NSString's stringWithFormat_("%@/%@-%@ %@.mp3", destinationFolderPath, theTempo, theKey, theTitle))
-- Move the file to the new destination. It'll be renamed in the process.
moveFile from thePath to destinationFilePath
end repeat
end mainBusiness
Your correct your script from Post 17 works, I will try not to use copy/paste method.
I have still been refining. I want it that if a genre, key, title, artist tag are not filled out (null). That it will prompt you (showing the file) therefore you have to manually change it if you want the script to move it.
I also made a change so it prompts when the file already exists in that target folder location. So if your doing bulk mp3/aiffs it will prompt you showing you the offending mp3/aiff file that is already there. I have also allowed the script to accept .aiff files.
The next step part I’m trying to work out is how to select a folder, and then do a recursive selection of files in that and subfolders for any mp3 or aiff. As I have a few hard drives of mp3s sorted in a deep folder structure.
the script so far…
use AppleScript version "2.4" # requires at least Yosemite
use scripting additions
use framework "Foundation"
-- Creates a new folder. There is no error if the folder already exists, and it will also create intermediate folders if required
on createFolder:POSIXPath
log "point 1"
set |⌘| to current application
log "point 2"
set theFolderURL to |⌘|'s |NSURL|'s fileURLWithPath:POSIXPath
log "point 3"
set theFileManager to |⌘|'s NSFileManager's defaultManager()
log "point 4"
set {theResult, theError} to theFileManager's createDirectoryAtURL:theFolderURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
log "point 5"
if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
log "point 6"
end createFolder:
set theFiles to choose file with prompt "Please select an MP3/AIFF file" of type {"mp3", "aiff"} with multiple selections allowed #TRYING TO FIND HOW TO SELECT BULK FILES FROM FOLDER/SUBFOLDERS??????
repeat with aFile in theFiles
my treatThisFile(aFile)
end repeat
on treatThisFile(theFile)
set posixFilePath to the quoted form of (POSIX path of theFile)
set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")
set artistTag to (do shell script "mdls " & posixFilePath & " -name kMDItemAuthors")
set tempoTag to (do shell script "mdls " & posixFilePath & " -name kMDItemTempo")
set keyTag to (do shell script "mdls " & posixFilePath & " -name kMDItemKeySignature")
set titleTag to (do shell script "mdls " & posixFilePath & " -name kMDItemTitle")
set theGenre to text ((offset of "= " in genreTag) + 3) through -2 of genreTag
set theArtist to text ((offset of "= " in artistTag) + 9) through -4 of artistTag
set theTempo to text ((offset of "= " in tempoTag) + 2) through -1 of tempoTag
set theKey to text ((offset of "= " in keyTag) + 3) through -2 of keyTag
set theTitle to text ((offset of "= " in titleTag) + 3) through -2 of titleTag
#SOME MP3 TAGS ARE NOT PROPERLY FILLED OUT- THIS BELOW PART DOESNT WORK AS SCRIPT PROCEEDS??????.
if {theKey, theArtist, theTempo} is "null" then
display dialog "Error: " & theTitle & " has a blank field - Please Fix"
end if
# For some files, theTitle is returned as "null" so it may generate duplicates # ADDED
# I choose to use the original name deprieved of the extension # ADDED
if theTitle is "null" then # ADDED
tell application "System Events" # ADDED
set theTitle to text 1 thru -5 of (get name of theFile) # ADDED
end tell # ADDED
end if # ADDED
set renamedTitle to ("|" & theTempo & "-" & theKey & " | " & theTitle)
-- set name of theFile to renamedTitle # MISPLACED must be in a finder or a System Events block
set DestFolder to ("/Volumes/Production/test/" & theGenre & "/" & theArtist)
--set DestFolder to ("/Volumes/Macintosh HD/Users/Important/Test/" & theGenre & "/" & theArtist)
my createFolder:DestFolder
set DestFolder to POSIX file DestFolder
tell application "Finder"
set theExtension to name extension of theFile
set name of theFile to renamedTitle & "." & theExtension #AS MAYBE AIFF OR MP3
try
move theFile to DestFolder # EDITED
on error errMsg
display dialog renamedTitle & errMsg #IF ALREADY EXISTS WILL PROMPT
end try
end tell
end treatThisFile
I’ve been looking at this myself, although I don’t have any mp3s with accented characters in their metadata and have had to rely on scripts that Yvan’s sent me which contain substitution handlers to correct the results he gets.
The only point I’d add to this is that in the example I saw that had a “A\U0303\U…” sequence (for a ç, but otherwise similar), the ASObjC code returned it as Ã…, and iTunes also displayed it that way. Given that iTunes is reading from the .mp3 file’s metadata, and not the Spotlight metadata, that makes me think that that part of the problem is unrelated to the code at hand, and may well have been encoded into the file incorrectly in the first instance. In other words, what I think is happening in the mdls process is that à is being decomposed to A\U0303, and the following character is then escaped to \U00a9 or whatever.
For the below I was wanting that if either theGenre,theArtist,theTempo or theKey contain a null, then to prompt saying which file it is and then NOT move the file and continue with the next file.
#SOME MP3 TAGS ARE NOT PROPERLY FILLED OUT- THIS BELOW PART DOESNT WORK AS SCRIPT PROCEEDS??????. # problem solved below
if (count theGenre) ≤ (count "(null)") and theGenre contains "null" then set theGenre to "(no Genre)" # ADDED
if (count theArtist) ≤ (count "(null)") and theArtist contains "null" then set theArtist to "(no Artist)" # ADDED
if (count theTempo) ≤ (count "(null)") and theTempo contains "null" then set theTempo to "(no Tempo)" # ADDED
if (count theKey) ≤ (count "(null)") and theKey contains "null" then set theKey to "(no Key)" # ADDED
So it’s effectively a non-problem. I suspect the OP doesn’t have many French MP3s anyway, correctly coded or otherwise.
Since there’s been no feedback about that, and since the OP appears to have ignored the two scripts I posted, I don’t think there’s much else I can contribute to this thread. I’ve replaced the mainBusiness() handler in the ASObjC script with the working version of Shane’s and I’ve updated the “vanilla” script to handle null results in the same way.