Applescript to read mp3 metatags and sort file into folder

Hi there,

I’m trying to create a folder action(so when a mp3 file is added) which then triggers a applescript to sort the mp3 file by genre.

In my case I want to know is it possible to create a applescript that reads its “genre” ID3 tag and then moves the file to a specified genre folder. I have done some searching, but I haven’t came across any applescript that can do this.

If anyone could give me a start or point me in the right direction that would be greatly appreciated.

Thanks Matt

Here’s how you get the genre tag. It should be easy to find lots of good references for scripting the rest.

set theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}
set posixFilePath to the quoted form of (POSIX path of theFile)

set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")

set theGenre to text ((offset of "= " in genreTag) + 2) through end of genreTag

thanks t.spoon!

I have modified the script to this, but I can’t get it to move into the “House” folder within Test. The mp3 file is a house track.

 set theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}
set posixFilePath to the quoted form of (POSIX path of theFile)

set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")

set theGenre to text ((offset of "= " in genreTag) + 2) through end of genreTag

set destFolder to "Volumes/Production/test" & theGenre

tell application "Finder"
	move file theFile to folder destFolder
end tell     

Error is as below

      error "Finder got an error: Can't get folder \"Volumes/Production/test\\\"House\\\"\"." number -1728 from folder "Volumes/Production/test\"House\""
           

(1) Finder is unable to work with Posix paths like the one you use.
(2) your posix path is wrongly defined. It must start with a slash
(3) if I understand well your original question you are supposed to insert a slash between “/Volumes/Production/test” and theGenre
(4) theFile is an alias object so you don’t have to use file in the instruction.
Correct syntax would be :

set theGenre to "House"
set destFolder to POSIX file ("/Volumes/Production/test/" & theGenre)
tell application "Finder"
	move theFile to destFolder
end tell

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) samedi 1 avril 2017 10:05:51

Hi Yvan ,

That didn’t work :frowning: , your script sets the variable theGenre to “House”

My objective is to select a mp3 and have the script define its theGenre variable by the mp3s genre tag. So this could be anything from Jazz, Progressive, Rock etc.

Then I’m trying to move the mp3 “theFile” (which was selected) to /Volumes/Production/test/%genre%

So I can organise my mp3s into genre folders automatically.

    

set theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}
set posixFilePath to the quoted form of (POSIX path of theFile)

set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")

set theGenre to text ((offset of "= " in genreTag) + 2) through end of genreTag

set destFolder to POSIX file ("/Volumes/Production/test/" & theGenre)
tell application "Finder"
	move theFile to destFolder
end tell


error “Finder got an error: AppleEvent handler failed.” number -10000

I just used House as an example assuming that you would be able to insert the instructions in your script.
Doing that you were supposed to get :

set theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}
set posixFilePath to the quoted form of (POSIX path of theFile)

set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")

set theGenre to text ((offset of "= " in genreTag) + 2) through end of genreTag

set destFolder to POSIX file ("/Volumes/Production/test/" & theGenre) # EDITED

tell application "Finder"
	move file theFile to destFolder # EDITED
end tell

I clearly assumed wrongly.

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) samedi 1 avril 2017 15:38:23

squigee,

As your first post asked about getting the track genre and said “If anyone could give me a start or point me in the right direction,” those of us reading this were figuring you could write or adapt one of the many scripts already around for creating folder actions and sorting files. That’s why my script just got the tag - it isn’t a folder action, it doesn’t run a repeat through multiple files, it doesn’t move anything.

Similarly, when you had trouble with the file move, Yvan fixed that part of the script for you. He was still assuming you were going to glue the functional parts together.

I think we’re getting the impression that you’re not actually hung up on one technical part of the script, but needing someone to write the whole thing from start to finish. Surprisingly, that actually happens pretty often on these forums, but you can’t really expect it. Help forums are aimed at helping with the parts someone gets stuck on, not writing whole scripts from scratch for free.

If you continue to take a crack at this, you have all the pieces. It’s just a matter of gluing them together.

This post https://discussions.apple.com/thread/133447?start=0&tstart=0

gives a good outline for this script. If you try gluing this together with the contributions from myself & Yvan and post what you’ve got, people here will probably continue to help you out.

hi guys,

My script is nearly finished however I can’t get it to create a folder with genre/artist if it doesn’t exist. I get the below error, any ideas.

Error:


error "Can't make \"/Volumes/Production/test/House/DJ Leoni\" into type boolean." number -1700 from "/Volumes/Production/test/House/DJ Leoni" to boolean


My script.




set theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}
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 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 DestFolder to POSIX file ("/Volumes/Production/test/" & theGenre & "/" & theArtist)


tell application "Finder"

	
	if not POSIX path of DestFolder exists then
		
		make new folder at POSIX path of DestFolder
	
	end if
	
	move file theFile to DestFolder # EDITED
	
end tell

The problem is in the part :


tell application "Finder"

   
   if not POSIX path of DestFolder exists then # you are testing that a posix string exists
       
       make new folder at POSIX path of DestFolder # As far as I remember, Finder is unable to create several levels of hierarchy in a single call and of course it can't apply upon a posix path
   
   end if
   
   move file theFile to DestFolder # theFile is an alias so don't specify "file"
   
end tell

Finder is unable to work upon POSIX paths. You must use HFS paths.

I was able to do the job with :

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
	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 theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}

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 theGenre to text ((offset of "= " in genreTag) + 3) through -2 of genreTag
set theArtist to text ((offset of "= " in artistTag) + 3) through -2 of artistTag

set DestFolder to ("/Volumes/Production/test/" & theGenre & "/" & theArtist)
my createFolder:DestFolder
set DestFolder to POSIX file DestFolder

tell application "Finder"
	move theFile to DestFolder # EDITED
end tell

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) samedi 8 avril 2017 09:40:38

“Finder is unable to work upon POSIX paths”

The below script works fine and I don’t use HFS path. Why can’t I create folders with Finder using POSIX, yet I can use “move”


set theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}
set posixFilePath to the quoted form of (POSIX path of theFile)

set genreTag to (do shell script "mdls " & posixFilePath & " -name kMDItemMusicalGenre")

set theGenre to text ((offset of "= " in genreTag) + 3) through -2 of genreTag

set destFolder to POSIX file ("/Volumes/Production/test/" & theGenre) # PATH2GENREFOLDERS


tell application "Finder"
	move file theFile to destFolder
end tell


You miss that a POSIX file uses the Hfs syntax.

set aPath to "MyHD:My.2.0:Documents Æ’:"
set POSIXPath to POSIX path of aPath --> "/Volumes/MyHD/My.2.0/Documents Æ’/"

set posixFile to POSIX file POSIXPath --> file "MyHD:My.2.0:Documents Æ’:"

class of posixFile --> «class furl»

tell application "Finder"
	set folderGenre to (make new folder at (aPath as alias) with properties {name:"Jazz"}) as alias
	log folderGenre (*alias MyHD:My.2.0:Documents Æ’:Jazz:*)
	set folderAuthor to (make folder at folderGenre with properties {name:"John Coltrane"}) as alias
	log folderAuthor (*alias MyHD:My.2.0:Documents Æ’:Jazz:John Coltrane:*)
end tell

I never wrote that Finder can’t work with POSIX files (file “MyHD:Macintosh .2.0:Documents Æ’:”) but that it can’t work with POSIX paths (“/Volumes/MyHD/Macintosh .2.0/Documents Æ’/”)

CAUTION.

I got a file whose itemAuthors was returned as : "David Guetta, Giorgio Tuinfort, FrA\U0303\U00a9dA\U0303\U00a9ric Riesterer, Taio Cruz, Nick Van De Wall, Rico Love, Raymond Usher & Aviici"

a bit of transcoding would be fine before using such string as a folder name.
A\U0303\U00a9” is the character é
A\U0303\U00a7” is the character ç

I got some whose displayName was returned as "Le\U0301o Ferre\U0301"
In this case,
e\U0301” is the character é

I don’t know if such encodings may be unEncoded using ASObjC.

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) samedi 8 avril 2017 12:22:47

Hi Yvan, I’m I have now extended the script to try and rename the file with BPM+Key+Title as well.

Though I keep getting the error “stack overflow” at the file renaming line - set name of theFile line.

Also is there any way to make this script to do multiple files?
I added the “with multiple selections allowed” after to choose file with prompt- but it didnt work.




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
	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 theFile to choose file with prompt "Please select an MP3 file" of type {"mp3"}

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

set renamedTitle to (theTempo & "-" & theKey & "  " & theTitle & ".mp3")



set name of theFile to renamedTitle

set DestFolder to ("/Volumes/Production/test/" & theGenre & "/" & theArtist)
my createFolder:DestFolder
set DestFolder to POSIX file DestFolder



tell application "Finder"
	move theFile to DestFolder # EDITED
	
	
end tell


Go to messages #53 & 54

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) vendredi 14 avril 2017 11:16:11


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.)

Thanks Nigel

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

Edited to fix a glaring error – thanks Yvan.

Thanks for this, Shane. I’ll study it in the morning.

Just to point out to anyone else reading this that Shane’s handler’s meant for substitution into the script in post #16, not the one in post #18.