Wednesday, June 28, 2017

#1 2017-03-31 07:11:28 am

squigee90
Member
Registered: 2017-03-26
Posts: 24

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


Filed under: Applescript mp3

Offline

 

#2 2017-03-31 10:07:42 am

t.spoon
Member
From: BFE, Massachusetts
Registered: 2013-01-12
Posts: 185

Re: Applescript to read mp3 metatags and sort file into folder

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

Applescript:

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


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#3 2017-03-31 07:48:15 pm

squigee90
Member
Registered: 2017-03-26
Posts: 24

Re: Applescript to read mp3 metatags and sort file into folder

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.


Applescript:

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

Applescript:

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

Offline

 

#4 2017-04-01 03:06:08 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

(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 :

Applescript:

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

Last edited by Yvan Koenig (2017-04-01 03:06:58 am)

Offline

 

#5 2017-04-01 07:32:39 am

squigee90
Member
Registered: 2017-03-26
Posts: 24

Re: Applescript to read mp3 metatags and sort file into folder

Hi Yvan ,

That didn't work sad , 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.


Applescript:



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

Offline

 

#6 2017-04-01 08:38:27 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

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 :

Applescript:

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

Offline

 

#7 2017-04-01 01:34:49 pm

t.spoon
Member
From: BFE, Massachusetts
Registered: 2013-01-12
Posts: 185

Re: Applescript to read mp3 metatags and sort file into folder

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/13 … p;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.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#8 2017-04-07 10:21:26 pm

squigee90
Member
Registered: 2017-03-26
Posts: 24

Re: Applescript to read mp3 metatags and sort file into folder

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:

Applescript:


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.

Applescript:




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

Offline

 

#9 2017-04-08 02:41:17 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

The problem is in the part :

Applescript:


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 :

Applescript:

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

Last edited by Yvan Koenig (2017-04-08 04:13:59 am)

Offline

 

#10 2017-04-08 05:14:11 am

squigee90
Member
Registered: 2017-03-26
Posts: 24

Re: Applescript to read mp3 metatags and sort file into folder

"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"

Applescript:


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

Offline

 

#11 2017-04-08 05:22:57 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

You miss that a POSIX file uses the Hfs syntax.

Applescript:

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

Last edited by Yvan Koenig (2017-04-08 07:50:47 am)

Offline

 

#12 2017-04-14 02:22:24 am

squigee90
Member
Registered: 2017-03-26
Posts: 24

Re: Applescript to read mp3 metatags and sort file into folder

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.


Applescript:




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

Offline

 

#13 2017-04-14 04:10:58 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

Go to  messages #53 & 54

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

Last edited by Yvan Koenig (2017-04-27 08:50:51 am)

Offline

 

#14 2017-04-14 05:35:51 am

squigee90
Member
Registered: 2017-03-26
Posts: 24

Re: Applescript to read mp3 metatags and sort file into folder

Applescript:


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.


Applescript:


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

Offline

 

#15 2017-04-14 07:27:52 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

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

Offline

 

#16 2017-04-14 09:35:18 am

Nigel Garvey
Moderator
From: Warwickshire, England
Registered: 2002-11-19
Posts: 4312

Re: Applescript to read mp3 metatags and sort file into folder

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.

Applescript:

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

Last edited by Nigel Garvey (2017-04-16 08:23:53 am)


NG

Offline

 

#17 2017-04-14 11:33:35 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

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

Last edited by Yvan Koenig (2017-04-14 11:40:33 am)

Offline

 

#18 2017-04-14 05:45:28 pm

Nigel Garvey
Moderator
From: Warwickshire, England
Registered: 2002-11-19
Posts: 4312

Re: Applescript to read mp3 metatags and sort file into folder

I wrote:

… here's something I've been working on today. It's in ASObjC for much of the process …

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.

Applescript:

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.

Last edited by Nigel Garvey (2017-04-16 08:34:34 am)


NG

Offline

 

#19 2017-04-14 07:17:11 pm

Shane Stanley
Member
From: Australia
Registered: 2002-12-07
Posts: 5023

Re: Applescript to read mp3 metatags and sort file into folder

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:

Applescript:

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.

Last edited by Shane Stanley (2017-04-15 07:49:00 am)


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#20 2017-04-14 07:37:26 pm

Nigel Garvey
Moderator
From: Warwickshire, England
Registered: 2002-11-19
Posts: 4312

Re: Applescript to read mp3 metatags and sort file into folder

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.


NG

Offline

 

#21 2017-04-15 07:07:45 am

Nigel Garvey
Moderator
From: Warwickshire, England
Registered: 2002-11-19
Posts: 4312

Re: Applescript to read mp3 metatags and sort file into folder

Thanks again, Shane.

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.

Applescript:

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

Last edited by Nigel Garvey (2017-04-15 11:54:54 am)


NG

Offline

 

#22 2017-04-15 07:47:35 am

Shane Stanley
Member
From: Australia
Registered: 2002-12-07
Posts: 5023

Re: Applescript to read mp3 metatags and sort file into folder

Thanks, Nigel.

Yvan has also found a couple of issues with accents. It looks like mdls is escaping non-ASCII characters -- so "e\\U0301" for "é", for example. My guess is this is because the record is normally output in the old NeXT property list format, which was ACSII-only. This makes the mdls method a bit less attractive.

There also seems to be some other mangling of some accented characters going on, which looks like an underlying Unicode normalization issue.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#23 2017-04-15 09:35:41 am

StefanK
Member
From: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11451
Website

Re: Applescript to read mp3 metatags and sort file into folder

Shane Stanley wrote:

This is an alternative for Nigel's mainBusiness() ASObjC handler

coolcoolcool That's a pretty nice way to work around the asynchronous behavior of NSMetadataQuery.


regards

Stefan

Offline

 

#24 2017-04-15 10:01:58 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3059

Re: Applescript to read mp3 metatags and sort file into folder

Go to messages #53 & 54


Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) samedi 15 avril 2017 17:00:43

Last edited by Yvan Koenig (2017-04-27 08:51:55 am)

Offline

 

#25 2017-04-15 11:59:40 am

Nigel Garvey
Moderator
From: Warwickshire, England
Registered: 2002-11-19
Posts: 4312

Re: Applescript to read mp3 metatags and sort file into folder

Yvan Koenig wrote:

I corrected also three copy paste errors in the posted handler.

Thanks, Yvan. I've corrected the script in post #21.


NG

Offline

 

Board footer

Powered by FluxBB

[ Generated in 0.181 seconds, 11 queries executed ]

RSS (new topics) RSS (active topics)