Another 'Move Files & Name Folders' request.

Apologies if this question gets asked often but I promise I have spent many hours researching, writing & editing before coming here.

It seems that there are many examples & options to do the classic manoeuvre of files into folders that derive their title from the filename, but as complete beginner I seem to break things when I try to build, tweak or amend scripts.

I have managed to achieve roughly what I’m aiming for in two parts with Automator, but I have never managed to append or merge them. It would be great to have one script as a service to make it a one-click option.

Does anyone know of a template out there that would follow my workflow plan below, or anybody who could point me in the right direction for help?

I’d like the script to go as follows:

———————————————————————————
START OF SCRIPT

— Get contents of currently selected folder

— Search through the filenames in that folder for one of three specific phrases

  [i]In my case those key phrases to search will be:[/i]

MASTER
STREAMING MASTER
VINYL MASTER

(Example filename: ’Song Name - Revision 1 - VINYL MASTER 96k 32bit.wav’. Sometimes the last characters in the filename will be ‘44.1k 16bit.wav’ which means I can’t use the function to count back through the characters to get to the phrase - although it’s still the same number of spaces.)

— Create folders based on the above phrases (if not found then don’t create a folder)

— Move all the files in to the appropriate folder

— Add the character ’s’ at the end of the newly created folder names to make it plural - so MASTER becomes MASTERS for example.

— Create a new folder at the same root level as the original, selected folder and call that one ‘FINAL MASTERS’

— Copy the three folders (and their contents) created in step 3 to the new ‘MASTERS’ folder.

END OF SCRIPT

———————————————————————————

Also, and this maybe a stupid question, is Automator just a GUI for scrip editing? If so, is there a way to open Automator files in Script Editor to see how the script is compiled or edit them? I have tried but it just appears as gobbledygook that won’t compile.

Model: Mac Pro 2010
AppleScript: 2.7
Browser: Safari 605.1.15
Operating System: macOS 10.14

Here is a script supposed to do the job.
I skipped a step, moving directly files in a folder with the final s.

If it’s OK, it would be interesting to skip otherv step and move directly the files in subfolders of the folder “FINAL MASTERS”.

-- https://macscripter.net/viewtopic.php?id=47412

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

property |⌘| : a reference to current application

Germaine()

on Germaine()
	set f to choose folder
	--set f to ((path to desktop as text) & "dossier source:") as alias
	
	set theFolderURL to (|⌘|'s NSArray's arrayWithObject:(f as «class furl»))'s firstObject()
	
	set finalName to (|⌘|'s NSString's stringWithString:"FINAL MASTERS")
	set rootFolder to (theFolderURL's URLByDeletingLastPathComponent)
	set finalMASTERS to (rootFolder's URLByAppendingPathComponent:finalName)
	
	set fileManager to |⌘|'s NSFileManager's defaultManager()
	-- creates the "FINAL MASTERS" folder
	set {theResult, theError} to (fileManager's createDirectoryAtURL:finalMASTERS withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
	if not (theResult as boolean) then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
	
	-- build an array of every visible items at first level of the source folder
	
	-- set directoryKey to |⌘|'s NSURLIsDirectoryKey
	set requestedKeys to {}
	-- set skipsSubdirectoryDescendants to |⌘|'s NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer --> 1
	-- set skipsPackageDescendants to |⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
	set skipsHiddenFiles to |⌘|'s NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
	set theOptions to skipsHiddenFiles -- only option valid for contentsOfDirectoryAtURL
	set theNSURLs to (fileManager's contentsOfDirectoryAtURL:theFolderURL includingPropertiesForKeys:requestedKeys options:theOptions |error|:(missing value))
	
	set theKeys to {"MASTER", "STREAMING MASTER", "VINYL MASTER"}
	
	repeat with aKey in theKeys
		set theFormat to "lastPathComponent CONTAINS '" & "- " & aKey & "'" -- so "MASTER" will be correctly treated
		-- extract the URLs matching a key
		set theURLs to (theNSURLs's filteredArrayUsingPredicate:(|⌘|'s NSPredicate's predicateWithFormat:theFormat))
		if (count theURLs) > 0 then
			set proposedName to (|⌘|'s NSString's stringWithString:(aKey & "s"))
			set destFolder to (theFolderURL's URLByAppendingPathComponent:proposedName)
			-- create the key subfolder with the final "s"
			set {theResult, theError} to (fileManager's createDirectoryAtURL:destFolder withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
			if not (theResult as boolean) then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
			repeat with aURL in theURLs
				set itsName to (aURL's lastPathComponent())
				set theDestURL to (destFolder's URLByAppendingPathComponent:itsName)
				set {theResult, theError} to (fileManager's moveItemAtURL:aURL toURL:theDestURL |error|:(reference))
			end repeat
			set finalURL to (finalMASTERS's URLByAppendingPathComponent:proposedName)
			-- move the subfolder into the "FINAL MASTERS" folder
			set {theResult, theError} to (fileManager's moveItemAtURL:destFolder toURL:finalURL |error|:(reference))
		end if
	end repeat
end Germaine

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 13 février 2020 19:18:57

Here is the version moving files only once.

-- https://macscripter.net/viewtopic.php?id=47412

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

property |⌘| : a reference to current application

Germaine()

on Germaine()
	set f to choose folder
	--set f to ((path to desktop as text) & "dossier source:") as alias
	
	set theFolderURL to (|⌘|'s NSArray's arrayWithObject:(f as «class furl»))'s firstObject()
	
	set finalName to (|⌘|'s NSString's stringWithString:"FINAL MASTERS")
	set rootFolder to (theFolderURL's URLByDeletingLastPathComponent)
	set finalMASTERS to (rootFolder's URLByAppendingPathComponent:finalName)
	
	set fileManager to |⌘|'s NSFileManager's defaultManager()
	
	-- build an array of every visible items at first level of the source folder
	
	set requestedKeys to {}
	
	-- set skipsSubdirectoryDescendants to |⌘|'s NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer --> 1
	-- set skipsPackageDescendants to |⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
	set skipsHiddenFiles to |⌘|'s NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
	set theOptions to skipsHiddenFiles -- only option valid for contentsOfDirectoryAtURL
	set theNSURLs to (fileManager's contentsOfDirectoryAtURL:theFolderURL includingPropertiesForKeys:requestedKeys options:theOptions |error|:(missing value))
	
	set theKeys to {"MASTER", "STREAMING MASTER", "VINYL MASTER"}
	
	repeat with aKey in theKeys
		set theFormat to "lastPathComponent CONTAINS '" & "- " & aKey & "'" -- so "MASTER" will be correctly treated
		-- extract the URLs matching a key
		set theURLs to (theNSURLs's filteredArrayUsingPredicate:(|⌘|'s NSPredicate's predicateWithFormat:theFormat))
		if (count theURLs) > 0 then
			set proposedName to (|⌘|'s NSString's stringWithString:(aKey & "s"))
			set destFolder to (finalMASTERS's URLByAppendingPathComponent:proposedName)
			-- create the key subfolder with the final "s"
			set {theResult, theError} to (fileManager's createDirectoryAtURL:destFolder withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
			if not (theResult as boolean) then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
			repeat with aURL in theURLs
				set itsName to (aURL's lastPathComponent())
				set theDestURL to (destFolder's URLByAppendingPathComponent:itsName)
				set {theResult, theError} to (fileManager's moveItemAtURL:aURL toURL:theDestURL |error|:(reference))
			end repeat
		end if
	end repeat
end Germaine

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 13 février 2020 21:11:17

Here’s a version using basic AppleScript. I wasn’t sure as to the location of the Final Masters folder, but this script creates that folder if it doesn’t exist. Yvan’s suggestion will be much faster, so this script is just FWIW.

BTW, this script overwrites existing files in the newly created folders. Also, this script can be made simpler and faster by skipping the intermediate folders.

set sourceFolder to (choose folder) as text

set text item delimiters to ":"
set parentFolder to text 1 thru text item -3 of (sourceFolder as text) & ":"
set text item delimiters to ""

set theStrings to {"VINYL MASTER", "STREAMING MASTER", "MASTER"}

tell application "Finder"
	
	set theFiles to (every file in folder sourceFolder whose name contains "master") as alias list
	
	repeat with i from 1 to (count theFiles)
		set aFile to item i of theFiles
		set aFileName to name of aFile
		repeat with j from 1 to (count theStrings)
			set theString to item j of theStrings
			if aFileName contains theString then
				try
					move aFile to folder (sourceFolder & theString & "S") with replacing
					exit repeat
				on error
					make new folder at folder sourceFolder with properties {name:theString & "S"}
					move aFile to folder (sourceFolder & theString & "S")
					exit repeat
				end try
			end if
		end repeat
	end repeat
	
	if not (exists folder (parentFolder & "FINAL MASTERS")) then make new folder at folder parentFolder with properties {name:"FINAL MASTERS"}
	
	repeat with i from 1 to (count theStrings)
		set theString to item i of theStrings
		try
			duplicate folder (sourceFolder & theString & "S") to folder (parentFolder & "FINAL MASTERS")
		end try
	end repeat
	
end tell

@Peavine

With a bit of luck, the OP will tell us which location for the “FINAL MASTERS” folder is the correct one :wink:

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 13 février 2020 22:08:28

We both placed the FINAL MASTERS folder at the same location so hopefully that’s correct. :slight_smile:

Hi, Rozzauk.

Try my script. This is a bug-corrected and optimized script, the structure of which I took from the Peavine code. I think this is what you need:


set sourceFolder to (choose folder)
tell application "Finder"
	set parentFolder to parent of sourceFolder
	set theFiles to (every file in sourceFolder whose name contains "MASTER") as alias list
	try
		set finalMastersFolder to ((parentFolder as text) & "FINAL MASTERS") as alias
	on error
		set finalMastersFolder to make new folder at parentFolder with properties {name:"FINAL MASTERS"}
	end try
	repeat with aFile in theFiles
		set aFileName to name of aFile
		repeat with aString in {"VINYL MASTER", "STREAMING MASTER", "MASTER"}
			if aFileName contains aString then
				try
					set destinationFolder to ((finalMastersFolder as text) & aString & "S") as alias
				on error
					set destinationFolder to make new folder at finalMastersFolder with properties {name:aString & "S"}
				end try
				move aFile to destinationFolder with replacing
			end if
		end repeat
	end repeat
end tell

How to make this script chosen folder’s Automator service:

  1. Open the Automator.
  2. Choose create Quick Action.
  3. In actions’s Library section choose Files and Folders. Opens list of available actions (on right side)
  4. Drag the action Get Selected Finder items to the window were is written this: Drag actions or files here…
  5. In the textfield in (you find it on the right top corner) replace any application with Finder.
  6. In the field Workflow receives current choose folders.
  7. Now, in actions’s Library section choose Utilites. Opens list of available actions (on right side)
  8. Drag the action Run AppleScript to the window were is written this: Drag actions or files here…. NOTE: under the first action which was dragged at the 4) step.
    Opens script template. Replace it with my script in this form:

on run {input, parameters}
	set sourceFolder to item 1 of input
	
	tell application "Finder"
		set parentFolder to parent of sourceFolder
		set theFiles to (every file in sourceFolder whose name contains "MASTER") as alias list
		try
			set finalMastersFolder to ((parentFolder as text) & "FINAL MASTERS") as alias
		on error
			set finalMastersFolder to make new folder at parentFolder with properties {name:"FINAL MASTERS"}
		end try
		repeat with aFile in theFiles
			set aFileName to name of aFile
			repeat with aString in {"VINYL MASTER", "STREAMING MASTER", "MASTER"}
				if aFileName contains aString then
					try
						set destinationFolder to ((finalMastersFolder as text) & aString & "S") as alias
					on error
						set destinationFolder to make new folder at finalMastersFolder with properties {name:aString & "S"}
					end try
					move aFile to destinationFolder with replacing
				end if
			end repeat
		end repeat
	end tell
	
	return input
end run
  1. In the Automator menu choose File menu item, then Save…, give the name to your new action (service), then save it.

So, the service will be created. How to use:

Select in the Finder the folder, right-click, go to Services, click your new service was created. The files will be moved were is need.

I was not entirely satisfied by the way I treated the items containing only “MASTER”.
Here is a version which seems more satisfying.

-- https://macscripter.net/viewtopic.php?id=47412

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

property |⌘| : a reference to current application

Germaine()

on Germaine()
	set f to choose folder
	--set f to ((path to desktop as text) & "dossier source:") as alias
	
	set theFolderURL to (|⌘|'s NSArray's arrayWithObject:(f as «class furl»))'s firstObject()
	
	set finalName to (|⌘|'s NSString's stringWithString:"FINAL MASTERS")
	set rootFolder to (theFolderURL's URLByDeletingLastPathComponent)
	set finalMASTERS to (rootFolder's URLByAppendingPathComponent:finalName)
	
	set fileManager to |⌘|'s NSFileManager's defaultManager()
	
	-- build an array of every visible items at first level of the source folder
	
	set requestedKeys to {}
	
	-- set skipsSubdirectoryDescendants to |⌘|'s NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer --> 1
	-- set skipsPackageDescendants to |⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
	set skipsHiddenFiles to |⌘|'s NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
	set theOptions to skipsHiddenFiles -- only option valid for contentsOfDirectoryAtURL
	set theNSURLs to (fileManager's contentsOfDirectoryAtURL:theFolderURL includingPropertiesForKeys:requestedKeys options:theOptions |error|:(missing value))
	-- create a mutable array so we will be able to remove moved URls
	set theNSURLs to |⌘|'s NSMutableArray's arrayWithArray: theNSURLs
	set theKeys to {"STREAMING MASTER", "VINYL MASTER", "MASTER"} -- leave "MASTER" at the very end
	
	repeat with aKey in theKeys
		set theFormat to "lastPathComponent CONTAINS '" & aKey & "'"
		-- extract the URLs matching a key
		set theURLs to (theNSURLs's filteredArrayUsingPredicate:(|⌘|'s NSPredicate's predicateWithFormat:theFormat))
		if (count theURLs) > 0 then
			set proposedName to (|⌘|'s NSString's stringWithString:(aKey & "s"))
			set destFolder to (finalMASTERS's URLByAppendingPathComponent:proposedName)
			-- create the key subfolder with the final "s"
			set {theResult, theError} to (fileManager's createDirectoryAtURL:destFolder withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
			if not (theResult as boolean) then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
			repeat with aURL in theURLs
				set itsName to (aURL's lastPathComponent())
				set theDestURL to (destFolder's URLByAppendingPathComponent:itsName)
				set {theResult, theError} to (fileManager's moveItemAtURL:aURL toURL:theDestURL |error|:(reference))
				(theNSURLs's removeObject:aURL)
			end repeat
		end if
	end repeat
end Germaine

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 14 février 2020 11:31:54

KniazidisR. Just as a point of information, the OP requested two sets of destination folders. The OP thus wrote:

FWIW, my script complies with this request and your script does not appear to.

Wow!

Firstly may I say huge thank you to you all. I am truly humbled by your response. I expected a few tips or pointers but to have fully coded scripts ready to go is just amazing.

It wasn’t until I saw the various scripts in action that I realised I hadn’t fully considered the consequences of such a workflow. For example, Peavine’s warning that a script may overwrite existing files is something I had not considered. Along with the fact that any new files placed in the original folder might not respond to the script on subsequent runs.

Peavine - thank you for your version using basic Applescript, as a noob this was the one that I could follow and understand. It gave the exact result I was looking for.

KniazidisR - I really appreciated the info on making the script in to a chosen folder Automator Service as this is how I am hoping to run the final version. Unfortunately I ran into the following error…

Yvan - Although your coding was beyond my level of understanding it seemed to be the most elegant solution. Lightning fast and repeatable on subsequent files added to the original folder - I’m guessing that’s partly down to skipping a step. But, as I am now questioning my original logic on this workflow, it might be the way to go.

Where I’m stuck now is converting scripts to a context menu, ’right-mousable’ service (Quick Action?) or a finder droplet that will work on the currently selected folder, rather than the prompt to choose a folder option. The hierarchy of some of my project folders can be layers deep.

Here is a version to be saved as an application (droplet).

-- https://macscripter.net/viewtopic.php?id=47412

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

property |⌘| : a reference to current application

on run -- entry point used when we double click the app's icon
	set f to choose folder
	my germaine(f)
end run

on open sel -- sel is the list of objets dropped upon the app's icon
	my germaine(item 1 of sel)
end open

on germaine(f)
	set theFolderURL to (|⌘|'s NSArray's arrayWithObject:(f as «class furl»))'s firstObject()
	
	set finalName to (|⌘|'s NSString's stringWithString:"FINAL MASTERS")
	set rootFolder to (theFolderURL's URLByDeletingLastPathComponent)
	set finalMASTERS to (rootFolder's URLByAppendingPathComponent:finalName)
	
	set fileManager to |⌘|'s NSFileManager's defaultManager()
	
	-- build an array of every visible items at first level of the source folder
	
	set requestedKeys to {}
	
	-- set skipsSubdirectoryDescendants to |⌘|'s NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer --> 1
	-- set skipsPackageDescendants to |⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
	set skipsHiddenFiles to |⌘|'s NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
	set theOptions to skipsHiddenFiles -- only option valid for contentsOfDirectoryAtURL
	
	set theNSURLs to (fileManager's contentsOfDirectoryAtURL:theFolderURL includingPropertiesForKeys:requestedKeys options:theOptions |error|:(missing value))
	
	set theKeys to {"STREAMING MASTER", "VINYL MASTER", "MASTER"} -- leave "MASTER" at the very end
	
	repeat with aKey in theKeys
		set theFormat to "lastPathComponent CONTAINS '" & aKey & "'"
		-- extract the URLs matching a key
		set theURLs to (theNSURLs's filteredArrayUsingPredicate:(|⌘|'s NSPredicate's predicateWithFormat:theFormat))
		if (count theURLs) > 0 then
			set proposedName to (|⌘|'s NSString's stringWithString:(aKey & "s"))
			set destFolder to (finalMASTERS's URLByAppendingPathComponent:proposedName)
			-- create the key subfolder with the final "s"
			set {theResult, theError} to (fileManager's createDirectoryAtURL:destFolder withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
			if not (theResult as boolean) then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
			repeat with aURL in theURLs
				set itsName to (aURL's lastPathComponent())
				set theDestURL to (destFolder's URLByAppendingPathComponent:itsName)
				set {theResult, theError} to (fileManager's moveItemAtURL:aURL toURL:theDestURL |error|:(reference))
			end repeat
			-- create a mutable set so we will be able to drop moved URls
			set mainSet to (|⌘|'s NSMutableSet's setWithArray:theNSURLs)
			(mainSet's minusSet:(|⌘|'s NSSet's setWithArray:theURLs)) -- remove the treated URLs from the main set
			set theNSURLs to (mainSet's allObjects()) -- convert the main set into an array
		end if
	end repeat
end germaine

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 14 février 2020 15:49:07

You are misleading the public. My script produces the same result. It’s just that there is no redundant code in it, and it seems to you that it is not doing something.

I just created and tested the service as described by me above. And it works fine…
See 9 steps more carefully. Don’t forget: you should use my second, Automator variant script. Not the first.

See here. 2 actions together:

KniazidisR. My script created one set of destination folders in the same folder as the source files and one set of destination folders in the FINAL MASTERS folder. It was my understanding that’s what the OP wanted. I ran your first script in Post 7 and it created one set of destination folders in the FINAL MASTERS folder only. That’s the differing result I was referring to.

BTW, when I ran your script, I received the following error message and perhaps that’s the reason for the differing result: