Move images into new folders based on extension

I would like to be able to drop a folder on a script and have it go through the selected folder and each subfolder and sort the raw (.cr2) images and jpg images into separate folders named after the sourceFolder & “_Raw” or “_Jpg”

Currently I’m using Hazel to sort raw and jpg images into separate folders based on a few rules.

Hazel is becoming a bit of a pain to run and point to the correct folders, and I think it would be a lot easier to create a droplet I could put in my finder toolbar that could do the same thing.

Right now my hazel rules are:

If Kind is folder
Name does not end with Raw (so that it will skip folders that have already been sorted)
Name does not end with Jpg (so that it will skip folders that have already been sorted)
Name does not end with Stitched (folders that end with Stitched don’t need to be sorted)
Name does not end with Lidar (folders that end with Lidar don’t need to be sorted)
then run rules on the contents of the folder. (Including subfolders)

Then:

if extension is .jpg sort into subfolder named (sourcefolder)_Jpg
if extension if .cr2 sort into subfolder named (sourcefolder)_Raw

Thanks!

You could probably do it slowly via Finder scripting, or using System Events, but if you’re running macOS 10.10 or later AppleScriptObjC is a good bet:

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

on open {theFile}
	set posixPath to POSIX path of theFile
	set theNSURL to current application's |NSURL|'s fileURLWithPath:posixPath
	my processFolderURL:theNSURL
	set theNSURL to missing value
	display dialog "Sorted." buttons {"OK"} default button 1
end open

on processFolderURL:folderURL
	-- make file manager
	set fileManager to current application's NSFileManager's new()
	-- get contents of folder
	set theURLs to fileManager's contentsOfDirectoryAtURL:folderURL includingPropertiesForKeys:{current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	set theFiles to {} -- holds plain files
	repeat with aURL in theURLs
		-- is it a directory?
		set {theResult, isDirectory} to (aURL's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
		if isDirectory as boolean then -- check it's not a package
			set {theResult, isPackage} to (aURL's getResourceValue:(reference) forKey:(current application's NSURLIsPackageKey) |error|:(missing value))
			-- is it not a package?
			if not isPackage as boolean then
				set theRange to (aURL's |path|()'s rangeOfString:"(Raw|Jpg|Stitched|Lidar)$" options:(current application's NSRegularExpressionSearch))
				if |length| of theRange = 0 then -- no match
					(my processFolderURL:aURL)
				end if
			end if
		else -- plain file
			set end of theFiles to aURL
		end if
	end repeat
	if (count of theFiles) > 0 then my sortURLs:theFiles inFolder:folderURL
end processFolderURL:

on sortURLs:theFiles inFolder:folderURL
	set theFiles to current application's NSArray's arrayWithArray:theFiles
	-- make predicates and file manager
	set predJpg to current application's NSPredicate's predicateWithFormat_("pathExtension ==[c]%@", "jpg")
	set predRaw to current application's NSPredicate's predicateWithFormat_("pathExtension ==[c]%@", "cr2")
	set fileManager to current application's NSFileManager's new()
	-- process results
	set theJpegs to (theFiles's filteredArrayUsingPredicate:predJpg) -- extract .jpg files
	if theJpegs's |count|() > 0 then
		-- create _Jpg folder if necessary
		set folderName to folderURL's lastPathComponent()
		set newFolderURL to (folderURL's URLByAppendingPathComponent:(folderName's stringByAppendingString:"_Jpg"))
		(fileManager's createDirectoryAtURL:newFolderURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value))
		-- move files
		repeat with aJpg in theJpegs
			(fileManager's moveItemAtURL:aJpg toURL:(newFolderURL's URLByAppendingPathComponent:(aJpg's lastPathComponent())) |error|:(missing value))
		end repeat
	end if
	set theRaws to (theFiles's filteredArrayUsingPredicate:predRaw) -- extract raw files
	if theRaws's |count|() > 0 then
		-- create _Raw folder if necessary
		set folderName to folderURL's lastPathComponent()
		set newFolderURL to (folderURL's URLByAppendingPathComponent:(folderName's stringByAppendingString:"_Raw"))
		(fileManager's createDirectoryAtURL:newFolderURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value))
		repeat with aRaw in theRaws
			(fileManager's moveItemAtURL:aRaw toURL:(newFolderURL's URLByAppendingPathComponent:(aRaw's lastPathComponent())) |error|:(missing value))
		end repeat
	end if
end sortURLs:inFolder:

Shane, this works beautifully! Thank you so much :smiley: