Oops
Now that you want to use choose folder I forgot to add instructions accepting only the mp3 and/or aiff files.
I’m forced to post the complete script.
(*
rename & move mp3 ASObjC - Æ’
Written for http://macscripter.net/viewtopic.php?id=45629
by Nigel GARVEY, Shane STANLEY & Yvan KOENIG
2017/04/15
Tested under 10.12.4 public version
# Edited on 2017/04/18 : report the list of files which weren't moved
# Edited on 2017/04/19 : at Nigel GARVEY's request, removed some instructions
# Edited on 2017/04/20 : now in the run handler choose file is replaced by choose folder + code getting the content of the selected folder
# Edited on 2017/04/21 : insert theArtist in the new fileNames. Write the count of moved files and the count of not moved files in the report : "report Mp3_3pMtroper.txt"
# Edited on 2017/04/29 : now write info about a not saved file one by one. Inserted numerous log instructions.
# Edited on 2017/04/30 : added a complementary transcoder handler named decodeText2 to treat an "unknown" encoding applied to theArtist
# Edited on 2017/05/01 : enhanced the handler decodeText2
# Edited on 2017/05/03 : disabled decodeText2 and try to filter the reported odd error
# Edited on 2017/05/11 : removed the log instruction and the decodeText2 handler
CAUTION. THE SCRIPT IS SUPPOSED TO RENAME AND MOVE SOME FILES.
IF YOU ARE NOT SURE OF WHAT IT DOES, EDIT THE PROPERTY NAMED testIt SETTING IT TO THE VALUE : true
*)
use AppleScript version "2.4" # requires at least Yosemite
use scripting additions
use framework "Foundation"
property copyString : " #" -- added to file names for copies; edit to suit your language
property destinationRootPath : "/Volumes/Production/test/" # I assume that it's the one you need # Is silently changed when the script run on my machine
property testIt : false
#################### true = log some metadatas and DON'T MOVE the files
#################### false = don't log these metadatas and MOVE the files
script o
# These properties are available for the handlers but aren't saved on disk
property filesMoved : 0
property filesNotMoved : 0
end script
on run -- For when testing.
set theFolder to choose folder with prompt "Please select folder"
set theFiles to its listFolder:(POSIX path of theFolder) # ADDED to get the list of mp3 and/or aiff files
mainBusiness(theFiles)
end run
on adding folder items to thisFolder after receiving addedItems -- For when run as a folder action.
# CAUTION : this entry doesn't check that the files are mp3 and/or aiff ones !
mainBusiness(addedItems)
end adding folder items to
on mainBusiness(theFiles)
-- Get the root path in a convenient form for use with ASObjC.
local filesMoved, filesNotMoved, newPath, blank, forYvanKoenig, |⌘|, newArray, destinationRootPath, thisFile, thePath, theExtension
local theName, theContainer, thePred, theQuery, metaItem, theArtist, moveItArtist, theGenre, moveItGenre, theKey, moveItKey
local theTempo, moveItTempo, moveForbidden, needComma, wrongMessage, destinationFolderPath, theTitle, destinationFileName
# Prepare the counters of treated files
set {filesMoved, filesNotMoved} to {0, 0}
set newPath to POSIX path of ((path to desktop as text) & "report Mp3_3pMtroper.txt")
# create a blank text file
set blank to current application's NSString's stringWithString:("")
set blank to blank's dataUsingEncoding:(current application's NSUTF8StringEncoding)
(blank's writeToFile:newPath atomically:true) -- as boolean # clears the report
# Instructions useful to get a script running flawlessly on MY machine!
set forYvanKoenig to (path to home folder as text) starts with "SSD 500"
if forYvanKoenig then
set my destinationRootPath to "/Volumes/Macintosh HD/Users/Important/Test/"
set my testIt to false --true
end if
set |⌘| to current application
set newArray to |⌘|'s NSMutableArray's arrayWithArray:{}
set destinationRootPath to |⌘|'s class "NSString"'s stringWithString:(my destinationRootPath)
repeat with thisFile in theFiles
set thePath to (|⌘|'s NSString's stringWithString:(POSIX path of thisFile))
set theExtension to thePath's pathExtension()
set theName to thePath's lastPathComponent()
set theContainer to thePath's stringByDeletingLastPathComponent()
set thePred to |⌘|'s NSPredicate's predicateWithFormat_("kMDItemFSName == %@", theName)
-- build and start query
set theQuery to |⌘|'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()
try
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:(|⌘|'s NSMetadataItemAuthorsKey))
set moveItArtist to (theArtist is not missing value)
if (theArtist is missing value) then
set theArtist to "(no artist)"
else
set theArtist to (theArtist's componentsJoinedByString:",")
set theArtist to my decodeText(theArtist)
end if
set theGenre to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemMusicalGenreKey))
set moveItGenre to (theGenre is not missing value)
if (theGenre is missing value) then set theGenre to "(no genre)"
-- Get the key, tempo, and title values.
set theKey to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemKeySignatureKey))
set moveItKey to (theKey is not missing value)
if (theKey is missing value) then set theKey to "(no key signature)"
set theTempo to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemTempoKey))
set moveItTempo to (theTempo is not missing value)
if (theTempo is missing value) then set theKey to "(no tempo)"
# Instructions useful to get a script running flawlessly on MY machine!
if forYvanKoenig then
# Here theKey metadata is NEVER defined so this instruction forbid every move.
--set moveForbidden to (moveItArtist is false) or (moveItGenre is false) or (moveItKey is false)
# With this one move is often allowed
set moveForbidden to (moveItArtist is false) or (moveItGenre is false)
else
# The test below matches what you asked in the thread. If one of the four flags is false we don't move the file.
set moveForbidden to (moveItArtist is false) or (moveItGenre is false) or (moveItKey is false) or (moveItTempo is false)
end if
if moveForbidden then
set needComma to false
set wrongMessage to "Not moved : " & thePath & linefeed & " because these flags are false --> "
if moveItArtist is false then
set wrongMessage to wrongMessage & "moveItArtist"
set needComma to true
end if # moveItArtist
if moveItGenre is false then
if needComma then set wrongMessage to wrongMessage & ", "
set wrongMessage to wrongMessage & "moveItGenre"
set needComma to true
end if # moveItGenre
if moveItKey is false then
if needComma then set wrongMessage to wrongMessage & ", "
set wrongMessage to wrongMessage & "moveItKey"
set needComma to true
end if # moveItKey
if moveItTempo is false then
if needComma then set wrongMessage to wrongMessage & ", "
set wrongMessage to wrongMessage & "moveItTempo"
set needComma to true
end if # moveItTempo
set wrongMessage to wrongMessage & linefeed
my writeTo(newPath, wrongMessage)
set filesNotMoved to filesNotMoved + 1
else
-- 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.
my createFolder(destinationFolderPath) -- as text)
set theTitle to (metaItem's valueForAttribute:(|⌘|'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.
# but replace theTitle by the original fileName
set destinationFileName to (|⌘|'s NSString's stringWithFormat_("{%@-%@} %@ - %@", theTempo, theKey, theArtist, theName))
else
# CAUTION, if the original title contained underscores they are replaced by slashes in theTitle.
# We must revert that
set theTitle to (theTitle's stringByReplacingOccurrencesOfString:"/" withString:"_")
-- Derive a destination path for the file with a new name made up from these details.
set destinationFileName to (|⌘|'s NSString's stringWithFormat_("{%@-%@} %@ - %@.%@", theTempo, theKey, theArtist, theTitle, theExtension))
end if
if not my testIt then
# Move the file to the new destination. It'll be renamed in the process.
# DISABLED DURING TESTS
(my moveThisFile:thePath uniquelyToPath:destinationFolderPath newName:destinationFileName)
set filesMoved to filesMoved + 1
else
if theKey is not missing value then log "theKey : " & theKey
set filesMoved to filesMoved + 1 # ADDED on 2017/04/25
end if # not testIt
end if # (moveItTitle .
on error errMsg number errNbr
# report the file as not moved
set wrongMessage to "Not moved : " & thePath & linefeed & " Error " & "number #" & errNbr & linefeed & " " & errMsg & linefeed
my writeTo(thePath, wrongMessage)
set filesNotMoved to filesNotMoved + 1
end try
end repeat
my writeTo(newPath, (linefeed & filesMoved & " were moved." & linefeed & filesNotMoved & " were not moved !" & linefeed))
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
# Called with sourceString defined as NSString
on replaceSeveralStrings:sourceString existingStrings:listOfExistingStrings newStrings:listOfNewStrings
if (count listOfExistingStrings) > (count listOfNewStrings) then error "The first list of strings is longer than the second one !" number 1789
--set sourceString to current application's NSString's stringWithString:sourceString
repeat with i from 1 to count listOfExistingStrings
set sourceString to (sourceString's stringByReplacingOccurrencesOfString:(listOfExistingStrings's item i) withString:(listOfNewStrings's item i))
end repeat
return sourceString as text
end replaceSeveralStrings:existingStrings:newStrings:
# Called with sourceString defined as NSString
on decodeText(theText) # Handler written by Nigel GARVEY
set |⌘| to current application
--set theText to |⌘|'s class "NSString"'s stringWithString:(theText)
-- If the string contains at least two consecutive 8+bit characters, assume it's a mangled result.
if ((theText's rangeOfString:("[\\u0080-\\U0010ffff]{2}") options:(|⌘|'s NSRegularExpressionSearch))'s |length|() > 0) then
set dataObj to (theText's dataUsingEncoding:(|⌘|'s NSISOLatin1StringEncoding))
set theText to (|⌘|'s class "NSString"'s alloc()'s initWithData:(dataObj) encoding:(|⌘|'s NSUTF8StringEncoding))
end if
return theText as text
end decodeText
# Modified version of a Shane STANLEY's handler borrowed from File Manager.scptd
on moveThisFile:aFileOrPath uniquelyToPath:folderOrPath newName:destName
# makes a NSURL from aFileOrPath
set theSourceURL to my makeURLFromFileOrPath:aFileOrPath
# makes a NSURL from folderOrPath
set theFolderURL to my makeURLFromFileOrPath:folderOrPath
# Drops the name extension from the proposed name
set destNameLessExt to destName's |stringByDeletingPathExtension|()
set theExtension to theSourceURL's |pathExtension|()
# Builds the URL pointing to the final file
set theDestURL to theFolderURL's URLByAppendingPathComponent:destName
set theFileManager to current application's NSFileManager's |defaultManager|()
set i to 2
repeat
set {theResult, theError} to (theFileManager's moveItemAtURL:theSourceURL toURL:theDestURL |error|:(reference))
if theResult as boolean then exit repeat
if (theError's code() as integer = current application's NSFileWriteFileExistsError as integer) then -- it already exists, so change name
set proposedName to (destNameLessExt's stringByAppendingString:(copyString & i))'s stringByAppendingPathExtension:theExtension
set theDestURL to theFolderURL's URLByAppendingPathComponent:proposedName
set i to i + 1
else -- an error other than file already exists, so return error
error (theError's |localizedDescription|() as text)
end if
end repeat
-- return theDestURL's |path|() as text
end moveThisFile:uniquelyToPath:newName:
# Shane STANLEY's handler borrowed from File Manager.scptd
-- This handler is called by moveItem
on makeURLFromFileOrPath:theFileOrPathInput
set |⌘| to current application
-- make it into a Cocoa object for easier comparison
set theFileOrPath to item 1 of (|⌘|'s NSArray's arrayWithObject:theFileOrPathInput)
if (theFileOrPath's isKindOfClass:(|⌘|'s NSString)) as boolean then
if (theFileOrPath's hasPrefix:"/") as boolean then -- full POSIX path
return |⌘|'s class "NSURL"'s fileURLWithPath:theFileOrPath
else if (theFileOrPath's hasPrefix:"~") as boolean then -- POSIX path needing ~ expansion
return |⌘|'s class "NSURL"'s fileURLWithPath:(theFileOrPath's |stringByExpandingTildeInPath|())
else -- must be HFS path
return |⌘|'s class "NSURL"'s fileURLWithPath:(POSIX path of theFileOrPathInput)
end if
else if (theFileOrPath's isKindOfClass:(|⌘|'s class "NSURL")) as boolean then -- happens with files and aliases in 10.11
return theFileOrPath
else -- must be a file or alias
return |⌘|'s class "NSURL"'s fileURLWithPath:(POSIX path of theFileOrPathInput)
end if
end makeURLFromFileOrPath:
on listFolder:POSIXPath # handler using ASObjC ADDED
set fileManager to current application's NSFileManager's defaultManager()
set aURL to current application's |NSURL|'s fileURLWithPath:POSIXPath
set theOptions to (current application's NSDirectoryEnumerationSkipsPackageDescendants as integer) + (current application's NSDirectoryEnumerationSkipsHiddenFiles as integer)
set theEnumerator to fileManager's enumeratorAtURL:aURL includingPropertiesForKeys:{} options:theOptions errorHandler:(missing value)
set theFiles to theEnumerator's allObjects()
set theFiles to theFiles's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"(lastPathComponent ENDSWITH '.mp3') OR (lastPathComponent ENDSWITH '.aiff')")
return theFiles as list # if we work upon files
--return (theFiles's valueForKey:"path") as list # if we work upon POSIX paths
end listFolder:
#=====
on writeTo(newPath, theString)
set fileAccess to current application's NSFileHandle's fileHandleForWritingAtPath:newPath
try
tell fileAccess to seekToEndOfFile()
set theString to current application's NSString's stringWithString:theString
set stringData to theString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
tell fileAccess to writeData:stringData
set theResult to true
end try
tell fileAccess to closeFile()
end writeTo
#=====
Edited on 2017/04/21 : insert theArtist in the new fileNames. Write the count of moved files and the count of not moved files in the report : “report Mp3_3pMtroper.txt”
Edited on 2017/04/30 : added a complementary transcoder handler named decodeText2 to treat an “unknown” encoding applied to theArtist
Edited on 2017/05/01 : enhanced the handler decodeText2
Edited on 2017/05/03 : disabled decodeText2 and try to filter the reported odd error
Edited on 2017/05/11 : removed the log instruction and the decodeText2 handler
I was far from home so I was unable to edit this message before my return.
Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) jeudi 20 avril 2017 19:20:56