Great software! - Quick question though *Automated file moving

I’ve been looking for a replacement for MusicBrainz - as that GUI can only handle about 20 - 30 albums at once before crashing and burning in the sand. I’m also a fan of command driven software so it’s easier to throw in a batch / automation script.

This software fits the bill PERFECTLY.

I do have a quick question about the auto tagging/ file moving, and I haven’t been able to find any good write-ups that go over the same issue. The software is able to tag and move everything great, but when an artist features another singer, the software creates a folder such as this: “original artist featuring secondary artist”.

It moves the mp3 into the new folder, so everything is great. Except, how do I make it so it stops adding the “featuring secondary artist” stuff to the folder name? i.e. Moving into the folder based solely on the primary artist?

. . . Also, I love the walkthrough you guys put together on how to get it up and running, but it would be helpful to include some sample config files. I do understand why you don’t include them though, as a million users could potentially cry in agony when the rules are applied wrongly on their music directory than they were expecting. Forcing them to read the documentation and build their own cfg, it means any errors in processing is on their own hands. - Just saying it would be nice to see a few more examples on the cfg file to base references on.

Please ignore the criticism, this software is the perfect solution.

Hi! You might want to try the ftintitle plugin.

As for config examples, I have noticed people like sharing their configurations. Maybe a thread on here for that kind of exchange would be useful.

Fast forward almost 2 years, browsing these forums again, and I just realized I never thanked you for the answer you provided.

Thanks for all the hard work. I still use this software extensively as it actually makes management of these massive libraries manageable. - For the original question, I did end up adding the “ftintitle” plugin, although no matter which option it had available (auto/drop/format) - the auto moving still continuously created the “featuring” folder as described above.

Not any issue at all though, as I just threw together a quick VBScript to rectify everything after the beets processing has finished. It’s not the most friendly to a processor, or I/O on a drive - but whatever. This “beets” software is absolute genius!

1 Like

Damn yea, I am having the same problem.

Matt,

If, you’re interested, here’s the VBScript I wrote that handles the exceptions this software leaves behind in my music library.

*Use this script at your own risk, it works perfect for my library, but I won’t take responsibility if it does things to somebody else’s, outside what they’d like their library to be organized.

  • My library is sorted only into artist folders. I do not include the Album folder. This makes it so bands who release the same song multiple times, that I don’t end up having a billion copies of the same song (i.e. “The Eagles”, etc.). Every artist folder exists in the same directory (i.e. C:\MP3) <-- I don’t really keep them on my local drive, this is just as an example.

  • It runs off these rules:

  1. Kill duplicates (identical name only).
    [SongName.1.mp3, SongName.2.mp3, SongName.3.mp3] … Up to # 9.
    [SongName (1).mp3, SongName (2).mp3, SongName (3).mp3] … Up to # 9.
    It checks for the existence of SongName.mp3 then acts accordlingly. [Rename/Copy/Delete]

  2. Deletes empty folders.

  3. Confirms for the existence of the following words within the Artist’s name (the folder that gets created for the artist I was referring to above):
    feat, presents, &, ;. with, and

    If any of those above words are present, it splits the folder name accordingly, and strips the unwanted crap from the folder name. (i.e. in the case of “Artist feat. Another Artist”, the script strips everything out and leaves only the first Artist. It checks if the folder already exists and moves everything over if it does, while it simply renames the folder to the correct version if it doesn’t exist.)

    For the “&” and “and” verification, I put some exceptions in that it ignores the split if the following word is “the”, or “his”, and a couple others [you can see from the script itself]. This ensures that it ignores folder names like “Artist and His Orchestra” or “Artist and the Band” etc.

    It also ignores if the # of words after “&” or “and” are only 1 that they get ignored too. (like artist & wife, or similar). There are probably a million better ways to handle this, but it works for what I need it to.

  4. I was going to add logging, but I never got around to it. It’d be easy enough to do, but I never had time to circle the wagons for it.

******* A FEW REQUIRED ITEMS FOR USING THIS SCRIPT *******

  1. Understand that I am NOT a programmer. I can just read VBScript and putting things together like this in that language is fairly easy. There are WAY better languages like PowerShell that could probably handle this way better and are better on drive I/O and processor power. The way I’m looping through everything, the script I believe uses more memory and read/writes to the drive than it should. Do not expect a miracle in processing speed here.

  2. Use this AT YOUR OWN RISK. It absolutely renames folders as required, and has served me since I wrote it back then. Just don’t scream if the script does something to your library you didn’t intend. Use it on a test folder first to make sure it has your desired results.

  3. As I stated above, my library is simply Artist/SongName.mp3 This script will not work without modification if you have the album title in your folder structure like Artist/AlbumTitle/Songname.mp3. It wouldn’t be hard to modify to work with album titles, but read above to the reason as why, why I don’t give 2 craps about what album a song came from. :slight_smile:

’ **********************************
’ *** MP3 File Management Script ***
’ **********************************

’ ==================================
’ ==================================

’ User Defined Variables

 strBaseFileShare = "<Base Directory _ Change Me!>"
 strFileExtension = ".mp3"

’ Standard Environment Variables

 Set objFSO = CreateObject("Scripting.FileSystemObject")
 Set objBaseDir = objFSO.GetFolder(strBaseFileShare)

’ Dictionaries

 Set dicSubFolders = CreateObject("Scripting.Dictionary")
 Set dicMP3s = CreateObject("Scripting.Dictionary")

’ ==================================

For Each objSubFolder in objBaseDir.SubFolders
If Not dicSubFolders.Exists(objSubFolder.Name) Then
strSubFolderName = objSubFolder.Name
strSubFolderPath = objSubFolder.Path
dicSubFolders.Add strSubFolderPath, strSubFolderName
Set colFiles = objSubFolder.Files
If colFiles.Count > 0 Then
For Each objFile In colFiles
If InStr(objFile.Name, strFileExtension) Then
If Not dicMP3s.Exists(objFile.Name) Then
dicMP3s.Add objFile.Path, objFile.Name
End If
End If
Next
Else
objFSO.DeleteFolder objSubfolder.Path
End If
End If
For Each strFilePath In dicMP3s
strFileName = dicMP3s.Item(strFilePath)
subRemoveDuplicates strFileName, strFilePath
Next
If objFSO.FolderExists(strSubFolderPath) Then
subValidateFolderName(objSubFolder.Path)
End If
dicMP3s.RemoveAll
Set colFiles = Nothing
Next

’ Process Complete =================

WScript.Echo “MP3 Processing Complete”
WScript.Quit

’ Functions ========================

Function fncWordCount(strEvaluationString)
If strEvaluationString <> “” Then
arrStringWords = Split(strEvaluationString, " ")
fncWordCount = UBound(arrStringWords) + 1
Else
fncWordCount = 0
End If
End Function

’ Sub Routines =====================

Sub subCorrectFolder(strFolderPath, strFolderBase)
If objFSO.FolderExists(strFolderPath) Then
Set objSourceDirectory = objFSO.GetFolder(strFolderPath)
If objFSO.FolderExists(strFolderBase) Then
For Each strSourceFile In dicMP3s
strDestFile = strFolderBase & “” & dicMP3s.Item(strSourceFile)
If objFSO.FileExists(strSourceFile) And Not objFSO.FileExists(strDestFile) Then
objFSO.CopyFile strSourceFile, strDestFile, True
objFSO.DeleteFile strSourceFile
End If
Next
objFSO.DeleteFolder objSourceDirectory.Path
Else
objFSO.MoveFolder strFolderPath, strFolderBase
End If
Set objSourceDirectory = Nothing
End If
End Sub

Sub subValidateFolderName(strFolderPath)
arrFolderRenameValues = Array( _
“feat”, _
“presents”, _
“&”, _
“;”, _
" with ", _
" and ")
For Each strFolderRenameValue In arrFolderRenameValues
blnActionRequired = False
If InStr(strFolderPath, strFolderRenameValue) Then
blnActionRequired = True
arrFolderDetails = Split(strFolderPath, strFolderRenameValue)
strFolderBase = Trim(arrFolderDetails(0))
If Right(strFolderBase, 1) = “.” Then
strFolderBase = Left(strFolderBase, Len(strFolderBase) - 1)
End If
strFolderExtension = Trim(arrFolderDetails(1))
intWordCount_FolderBase = fncWordCount(strFolderBase)
intWordCount_FolderExtension = fncWordCount(strFolderExtension)
Select Case strFolderRenameValue
Case “feat”, “with”, “presents”, “;”
arrRenameExceptions = Array( _
“Little Feat”, _
“Man With No Name”)
For Each strRenameException in arrRenameExceptions
If Left(LCase(strFolderPath), Len(strRenameException)) = LCase(strRenameException) Then
blnActionRequired = False
End If
Next
If blnActionRequired Then
subCorrectFolder strFolderPath, strFolderBase
End If
Case “&”, “and”
arrRenameExceptions = Array( _
“choir”, _
“drums”, _
“famous”, _
“farrar”, _
“friends”, _
“his”, _
“linda mccartney”, _
“lopez”, _
“the”)
For Each strRenameException in arrRenameExceptions
If Left(LCase(strFolderExtension), Len(strRenameException)) = LCase(strRenameException) Then
blnActionRequired = False
End If
Next
If IntWordCount_FolderBase = 0 Then
blnActionRequired = False
ElseIf intWordCount_FolderBase = 1 And intWordCount_FolderExtension = 1 Then
blnActionRequired = False
End If
If blnActionRequired Then
subCorrectFolder strFolderPath, strFolderBase
End If
End Select
End If
Next
End Sub

Sub subRemoveDuplicates(strFileName, strFilePath)
blnActionRequired = False
For intDuplicateCheck = 1 To 9
strIncorrectName_Parenth = “(” & intDuplicateCheck & “)” & strFileExtension
strIncorrectName_NumberOnly = “.” & intDuplicateCheck & strFileExtension
If InStr(strFileName, strIncorrectName_Parenth) Then
strCorrectedPath = Replace(strFilePath, strIncorrectName_Parenth, strFileExtension)
blnActionRequired = True
ElseIf InStr(strFileName, strIncorrectName_NumberOnly) Then
strCorrectedPath = Replace(strFilePath, strIncorrectName_NumberOnly, strFileExtension)
blnActionRequired = True
End If
Next
If InStr(strFileName, " - Copy") Then
arrFileDetails = Split(strFileName, " - Copy")
strCorrectedPath = arrFileDetails(0)
blnActionRequired = True
End If
If blnActionRequired Then
If objFSO.FileExists(strCorrectedPath) And objFSO.FileExists(strFilePath) Then
objFSO.DeleteFile strFilePath
Else
If objFSO.FileExists(strFilePath) Then
objFSO.MoveFile strFilePath, strCorrectedPath
End If
End If
End If
End Sub

*Apologies for script formatting, this discourse stripped all the tabs out.

Oh - forgot to mention - that if you hadn’t guessed, that since I wrote it in VBS, it’s a script that needs to be run from a windows box.

Please see Discourse’s Markdown formatting instructions to see how to demarcate code blocks so they display correctly.

' ===================================================
' ===================================================

' User Defined Variables

	strBaseFileShare = "<ChangeMe!_PathToMP3s>"
	strFileExtension = ".mp3"

' Standard Environment Variables

	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set objBaseDir = objFSO.GetFolder(strBaseFileShare)

' Dictionaries

	Set dicSubFolders = CreateObject("Scripting.Dictionary")
	Set dicMP3s = CreateObject("Scripting.Dictionary")

' ===================================================

For Each objSubFolder in objBaseDir.SubFolders
	If Not dicSubFolders.Exists(objSubFolder.Name) Then
		strSubFolderName = objSubFolder.Name
		strSubFolderPath = objSubFolder.Path
		dicSubFolders.Add strSubFolderPath, strSubFolderName
		Set colFiles = objSubFolder.Files
		If colFiles.Count > 0 Then
			For Each objFile In colFiles
				If InStr(objFile.Name, strFileExtension) Then
					If Not dicMP3s.Exists(objFile.Name) Then
						dicMP3s.Add objFile.Path, objFile.Name
					End If
				End If
			Next
		Else
			objFSO.DeleteFolder objSubfolder.Path
		End If	
	End If
	For Each strFilePath In dicMP3s
		strFileName = dicMP3s.Item(strFilePath)
		subRemoveDuplicates strFileName, strFilePath
	Next
	If objFSO.FolderExists(strSubFolderPath) Then
		subValidateFolderName(objSubFolder.Path)
	End If
	dicMP3s.RemoveAll
	Set colFiles = Nothing
Next

' Process Complete ==================================

WScript.Echo "MP3 Processing Complete"
WScript.Quit

' Functions =========================================

Function fncWordCount(strEvaluationString)
	If strEvaluationString <> "" Then
		arrStringWords = Split(strEvaluationString, " ")
		fncWordCount = UBound(arrStringWords) + 1
	Else
		fncWordCount = 0
	End If
End Function

' Sub Routines ======================================

Sub subCorrectFolder(strFolderPath, strFolderBase)
	If objFSO.FolderExists(strFolderPath) Then
		Set objSourceDirectory = objFSO.GetFolder(strFolderPath)
		If objFSO.FolderExists(strFolderBase) Then
			For Each strSourceFile In dicMP3s
				strDestFile = strFolderBase & "\" & dicMP3s.Item(strSourceFile)
				If objFSO.FileExists(strSourceFile) And Not objFSO.FileExists(strDestFile) Then
					objFSO.CopyFile strSourceFile, strDestFile, True
					objFSO.DeleteFile strSourceFile
				End If
			Next
			objFSO.DeleteFolder objSourceDirectory.Path
		Else
			objFSO.MoveFolder strFolderPath, strFolderBase
		End If
		Set objSourceDirectory = Nothing
	End If
End Sub

Sub subValidateFolderName(strFolderPath)
	arrFolderRenameValues = Array( _
		"feat", _
		"presents", _
		"&", _
		";", _
		" with ", _
		" and ")
	For Each strFolderRenameValue In arrFolderRenameValues
		blnActionRequired = False
		If InStr(strFolderPath, strFolderRenameValue) Then
			blnActionRequired = True
			arrFolderDetails = Split(strFolderPath, strFolderRenameValue)
			strFolderBase = Trim(arrFolderDetails(0))
			If Right(strFolderBase, 1) = "." Then
				strFolderBase = Left(strFolderBase, Len(strFolderBase) - 1)
			End If
			strFolderExtension = Trim(arrFolderDetails(1))
			intWordCount_FolderBase = fncWordCount(strFolderBase)
			intWordCount_FolderExtension = fncWordCount(strFolderExtension)
			Select Case strFolderRenameValue
				Case "feat", "with", "presents", ";"
					arrRenameExceptions = Array( _
						"Little Feat", _
						"Man With No Name")
					For Each strRenameException in arrRenameExceptions
						If Left(LCase(strFolderPath), Len(strRenameException)) = LCase(strRenameException) Then
							blnActionRequired = False
						End If
					Next
					If blnActionRequired Then
						subCorrectFolder strFolderPath, strFolderBase
					End If
				Case "&", "and"
					arrRenameExceptions = Array( _
						"choir", _
						"drums", _
						"famous", _
						"farrar", _
						"friends", _
						"his", _
						"linda mccartney", _
						"lopez", _
						"the")
					For Each strRenameException in arrRenameExceptions
						If Left(LCase(strFolderExtension), Len(strRenameException)) = LCase(strRenameException) Then
							blnActionRequired = False
						End If
					Next
						If IntWordCount_FolderBase = 0 Then
							blnActionRequired = False
						ElseIf intWordCount_FolderBase = 1 And intWordCount_FolderExtension = 1 Then
							blnActionRequired = False
						End If
					If blnActionRequired Then
						subCorrectFolder strFolderPath, strFolderBase
					End If
			End Select
		End If
	Next
End Sub

Sub subRemoveDuplicates(strFileName, strFilePath)
	blnActionRequired = False
	For intDuplicateCheck = 1 To 9
		strIncorrectName_Parenth = "(" & intDuplicateCheck & ")" & strFileExtension
		strIncorrectName_NumberOnly = "." & intDuplicateCheck & strFileExtension
		If InStr(strFileName, strIncorrectName_Parenth) Then
			strCorrectedPath = Replace(strFilePath, strIncorrectName_Parenth, strFileExtension)
			blnActionRequired = True
		ElseIf InStr(strFileName, strIncorrectName_NumberOnly) Then
			strCorrectedPath = Replace(strFilePath, strIncorrectName_NumberOnly, strFileExtension)
			blnActionRequired = True
		End If
	Next
	If InStr(strFileName, " - Copy") Then
		arrFileDetails = Split(strFileName, " - Copy")
		strCorrectedPath = arrFileDetails(0)
		blnActionRequired = True
	End If
	If blnActionRequired Then
		If objFSO.FileExists(strCorrectedPath) And objFSO.FileExists(strFilePath) Then
			objFSO.DeleteFile strFilePath
		Else
			If objFSO.FileExists(strFilePath) Then
				objFSO.MoveFile strFilePath, strCorrectedPath
			End If
		End If
	End If
End Sub

Thanks for the tip Adrian. - Keep up the great work!

Thanks for the info, but my setup is different then yours (folder layout and linux machine) so I’ll probably have to do something myself

No worries - I get it.

I’d imagine someone much smarter than I could convert the vbs to bash or something similar.

Hope you can get it figured out, would be nice to keep it in the shell as you could just execute it automatically after each beet run.

Doubt there’s much interest. But I went ahead and ripped my previous script apart.

I added logging and put in a metric TON of safeguards so it doesn’t do something it’s not supposed to. Feel free to use if you wish, but please make note that the same stipulations exist that I wrote above (Written in VBScript, Use on test folders first to see if it gives you the desired results, etc.).

Side note, is that the catalog literally crawls every folder / sub folder and all of their files. It can take a lengthy period to do so, as in the case of my jinormous library, it can take upwards of 2 hours each execution. Just heads up for anyone that wants to use it.

' *****************************
' *** MP3 Folder Processing ***
' *****************************

' =============================
' =============================

' User Defined Variables ------

	strBaseDirectory = "\\server\share\mp3"
	strTargetExtension = "mp3"

	blnProcessUnsorted = True
	strUnsortedDirectory = "\\server\share\Unsorted"


' Environment Variables -------

	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set objShell = CreateObject("WScript.Shell")

	Const ForAppending = 8

	strScriptPath = WScript.ScriptFullName
	Set objScript = objFSO.GetFile(strScriptPath)
	strScriptName = objScript.Name
	strLogName = Replace(strScriptName , ".vbs", ".log")
	strLogPath = Replace(strScriptPath, strScriptName, strLogName)
	Set objLogFile = objFSO.OpenTextFile(strLogPath,8,True)


' Dictionaries ---------------

	Set dicDirectories = CreateObject("Scripting.Dictionary")
	Set dicSubDirectoryCount = CreateObject("Scripting.Dictionary")
	Set dicFileCount = CreateObject("Scripting.Dictionary")
	Set dicTargetFiles = CreateObject("Scripting.Dictionary")
	Set dicTempFileList = CreateObject("Scripting.Dictionary")
	Set dicInvalidFiles = CreateObject("Scripting.Dictionary")
	Set dicDuplicateFiles = CreateObject("Scripting.Dictionary")
	Set dicDirectoryCorrections = CreateObject("Scripting.Dictionary")
	Set dicDirectoryDeletions = CreateObject("Scripting.Dictionary")
	Set dicUnMatchedDirectories = CreateObject("Scripting.Dictionary")
	Set dicDirectoryDeletionFailures = CreateObject("Scripting.Dictionary")
	Set dicFileDeletionFailures = CreateObject("Scripting.Dictionary")
	

' Primary Execution ---------

	dtmTimeStart = Now()

	objLogFile.WriteLine "*** Script Execution Began On " & dtmTimeStart & "***"
	objLogFile.WriteLine vbTab & "Creating " & UCase(strTargetExtension) & " Catalog"

	subCatalogDirectory strBaseDirectory

	objLogFile.WriteLine vbTab & "Processing Invalid Extensions:  (" & dicInvalidFiles.Count & ")"
	For Each strInvalidFilePath In dicInvalidFiles
		blnActionRequired = True
		strInvalidFileExtension = objFSO.GetExtensionName(strInvalidFilePath)
		arrInvalidFileExceptions = Array( _
			"8svx", _
			"aax", _
			"act", _
			"ai", _
			"aif", _
			"aifc", _
			"aiff", _
			"alac", _
			"amr", _
			"ape", _
			"au", _
			"awb", _
			"dct", _
			"dss", _
			"dvf", _
			"ea", _
			"eps", _
			"flac", _
			"gif", _
			"gsm", _
			"iklax", _
			"indd", _
			"ivs", _
			"jpeg", _
			"jpg", _
			"m4a", _
			"m4b", _
			"m4p", _
			"midi", _
			"mmf", _
			"mp3", _
			"mpc", _
			"msv", _
			"nsf", _
			"ogg", _
			"ogg", _
			"opus", _
			"pdf", _
			"png", _
			"psd", _
			"ra", _
			"raw", _
			"sln", _
			"tif", _
			"tiff", _
			"tta", _
			"vox", _
			"wav", _
			"wave", _
			"webm", _
			"wma", _
			"wv")
		For Each strInvalidFileException In arrInvalidFileExceptions
			If strInvalidFileExtension = strInvalidFileException Then
				blnActionRequired = False
				Exit For
			End If
		Next
		If blnActionRequired Then
			objLogFile.WriteLine vbTab & vbTab & fncQuote(strInvalidFilePath)
			strInvalidFileName = dicInvalidFiles.Item(strInvalidFilePath)
			strCorrectFileName = Left(strInvalidFileName, Len(strInvalidFileName) - Len(strInvalidFileExtension)) & strTargetExtension
			strCorrectFilePath = Replace(strInvalidFilePath, strInvalidFileName, strCorrectFileName)
			subEvaluateFileCopyMove strInvalidFilePath, strCorrectFilePath
		Else
			objLogFile.WriteLine vbTab & vbTab & fncQuote(strInvalidFilePath) & " [Current Extension On Exclusion List]"
		End If
	Next

	objLogFile.WriteLine vbTab & "Processing Duplicate Files:  (" & dicDuplicateFiles.Count & ")"
	For Each strDuplicateFilePath In dicDuplicateFiles
		strDuplicateFileNameCorrected = dicDuplicateFiles.Item(strDuplicateFilePath)
		objLogFile.WriteLine vbTab & vbTab & fncQuote(strDuplicateFilePath) & " --> " & fncQuote(strDuplicateFileNameCorrected)
		If dicTargetFiles.Exists(strDuplicateFileNameCorrected) Then
			subFileDelete(strDuplicateFilePath)
		Else
			subEvaluateFileCopyMove	strDuplicateFilePath, strDuplicateFileNameCorrected
		End If
	Next

	objLogFile.WriteLine vbTab & "Processing Directory Corrections:  (" & dicDirectoryCorrections.Count & ")"
	For Each strSourcePath In dicDirectoryCorrections
		strDestinationPath = dicDirectoryCorrections.Item(strSourcePath)
		objLogFile.WriteLine vbTab & vbTab & fncQuote(strSourcePath) & " --> " & fncQuote(strDestinationPath)
		If strDestionationPath <> strBaseDirectory Then
			If fncDirectoryExists(strDestinationPath) Then
				subCatalogFiles(strSourcePath)
				For Each strSourceFilePath In dicTempFileList
					strSourceFileName = dicTempFileList.Item(strSourceFilePath)
					strDestFilePath = strDestinationPath & "\" & strSourceFileName
					subEvaluateFileCopyMove strSourceFilePath, strDestFilePath
				Next
				dicTempFileList.RemoveAll
				subDirectoryDelete(strSourcePath)
			Else
				subDirectoryMove strSourcePath, strDestinationPath
			End If
		End If
	Next

	If blnProcessUnsorted Then
		objLogFile.WriteLine vbTab & "Processing Unmatched Directories:  (" & dicUnMatchedDirectories.Count & ")"
		For Each strSourcePath In dicUnMatchedDirectories
			strDestinationPath = dicUnMatchedDirectories.Item(strSourcePath)
			objLogFile.WriteLine vbTab & vbTab & fncQuote(strSourcePath) & " --> " & fncQuote(strDestinationPath)
			If fncDirectoryExists(strDestinationPath) Then
				subCatalogFiles(strSourcePath)
				For Each strSourceFilePath In dicTempFileList
					strSourceFileName = dicTempFileList.Item(strSourceFilePath)
					strDestFilePath = strDestinationPath & "\" & strSourceFileName
					subEvaluateFileCopyMove strSourceFilePath, strDestFilePath
				Next
				dicTempFileList.RemoveAll
				subDirectoryDelete(strSourcePath)
			Else
				subDirectoryMove strSourcePath, strDestinationPath
			End If
		Next
	End If

	objLogFile.WriteLine vbTab & "Processing Directory Deletion Validations:  (" & dicDirectoryDeletions.Count & ")"
	For Each strDirectoryPath In dicDirectoryDeletions
		If dicFileCount.Exists(strDirectoryPath) Then
			intFileCount = dicFileCount.Item(strDirectoryPath)
			intSubDirectoryCount = dicSubDirectoryCount.Item(strDirectoryPath)
			If intFileCount = 0 And intSubDirectoryCount = 0 Then
				objLogFile.WriteLine vbTab & vbTab & fncQuote(strDirectoryPath) & " [Directory Empty As Per Catalog]"
			Else
				objLogFile.WriteLine vbTab & vbTab & fncQuote(strDirectoryPath) & " [Directory Not Empty As Per Catalog]"
			End If
			subDirectoryDelete(strDirectoryPath)
		End If
	Next

	objLogFile.WriteLine vbTab & "Directory Deletion Failures:  (" & dicDirectoryDeletionFailures.Count & ")"
	For Each strDirectoryPath In dicDirectoryDeletionFailures
		strFailureReason = dicDirectoryDeletionFailures.Item(strDirectoryPath)
		objLogFile.WriteLine vbTab & vbTab & fncQuote(strDirectoryPath) & " [" & strFailureReason & "]"
	Next

	objLogFile.WriteLine vbTab & "File Deletion Failures:  (" & dicFileDeletionFailures.Count & ")"
	For Each strFilePath In dicFileDeletionFailures
		strFailureReason = dicFileDeletionFailures.Item(strFilePath)
		objLogFile.WriteLine vbTab & vbTab & fncQuote(strFilePath) & " [" & strFailureReason & "]"
	Next

	dtmTimeEnd = Now()
	intScriptExecutionTime = DateDiff("n", dtmTimeStart, dtmTimeEnd)
	objLogFile.WriteLine "*** Script Execution Completed On " & dtmTimeEnd & ", Total Run Time = " & intScriptExecutionTime & " minutes***"
	objLogFile.Close

	WScript.Echo UCase(strTargetExtension) & " processing complete." & vbCrLf & vbCrLf & _
		"Directory Corrections:  " & dicDirectoryCorrections.Count & vbCrLf & _
		"Directory Removals:  " & dicDirectoryDeletions.Count & vbCrLf & _
		"Unmatched Directories:  " & dicUnMatchedDirectories.Count & vbCrLf & _
		"Duplicate " & UCase(strTargetExtension) & " Removals:  " & dicDuplicateFiles.Count & vbCrLf & _
		"Invalid File Extensions:  " & dicInvalidFiles.Count & vbCrLf & vbCrLf & _
		"Run Time:  " & intScriptExecutionTime & " minutes"

' Functions ---------------

	Function fncConjunctionReplacement(strText)
		If strText <> "" Then
			strOutput = strText
			arrUnwantedConjunctions = Array( _
				" And ", _
				" and ")
			For Each strUnwantedConjunction In arrUnwantedConjunctions
				strOutput = Replace(strOutput, strUnwantedConjunction, " & ")
			Next
			fncConjunctionReplacement = strOutput
		Else
			fncConjunctionReplacement = strText
		End If
	End Function

	Function fncDirectoryExists(strDirectory)
		If strDirectory <> "" Then
			If objFSO.FolderExists(strDirectory) Then
				fncDirectoryExists = True
			Else
				fncDirectoryExists = False
			End If
		Else
			fncDirectoryExists = False
		End If
	End Function

	Function fncFileExists(strFile)
		If strFile <> "" Then
			If objFSO.FileExists(strFile) Then
				fncFileExists = True
			Else
				fncFileExists = False
			End If
		Else
			fncFileExists = False
		End If
	End Function
	
	Function fncParseNameSplit(strDirectoryName)
		If strDirectoryName <> "" Then
			strOutput = Trim(strDirectoryName)
			arrSplitEvals = Array( _
				";", _
				",", _
				" - ", _
				" Avec ", _
				" avec ", _
				" CD ", _
				" cd ", _
				"Duet", _
				"duet", _
				" En ", _
				" en ", _
				"Feat", _
				"feat", _
				" (Ft", _
				" (ft", _
				" [Ft", _
				" [ft", _
				" Ft", _
				" ft", _
				" Med ", _
				" med ", _
				" Mit ", _
				" mit ", _
				"Original", _
				"original", _
				" Read by ", _
				" read by ", _
				"Soundtrack", _
				"soundtrack", _
				" Und ", _
				" und ", _
				" Vs", _
				" vs", _
				" part ", _
				" Part ", _
				"Presents", _
				"presents", _
				"+", _
				"&", _
				" And ", _
				" and ", _
				"With", _
				"with")
			For Each strSplitEval In arrSplitEvals
				If InStr(strDirectoryName, strSplitEval) Then
					arrSplitDetails = Split(strDirectoryName, strSplitEval)
					strPreSplitData = Trim(arrSplitDetails(0))
					strPostSplitData = Trim(arrSplitDetails(UBound(arrSplitDetails)))
					If strPreSplitData <> "" Then
						If Not dicDirectories.Exists(strBaseDirectory & "\" & strPreSplitData) Then
							Select Case strSplitEval
								Case "+", "&", " And ", " and "
									blnException = False
									arrSplitExceptions = Array( _
										"family", _
										"his", _
										"her", _
										"jr", _
										"the", _
										"nine stories", _
										"z")
									For Each strSplitException In arrSplitExceptions
										If Left(Trim(LCase(strPostSplitData)), Len(strSplitException)) = LCase(strSplitException) Then
											blnException = True
											Exit For
										End If
									Next
									If fncSplitException(strPreSplitData, strPostSplitData) Then
										blnException = True
									End If
									If blnException Then
										For Each strOverrideEval In arrSplitEvals
											If InStr(strPreSplitData, strOverrideEval) Then
												If fncSplitException(strPreSplitData, strPostSplitData) Then
													strOutput = Trim(strOutput)
													Exit For
												Else
													strOutput = fncParseNameSplit(Trim(strPreSplitData))
												End If
											Else
												strOutput = Trim(strOutput)
												Exit For
											End If
										Next
									Else
										strOutput = fncParseNameSplit(Trim(strPreSplitData))
									End If
								Case Else
									strOutput = fncParseNameSplit(Trim(strPreSplitData))
									Exit For
							End Select
						Else
							strOutput = Trim(strPreSplitData)
						End If
					Else
						strOutput = Trim(strDirectoryName)
					End If
				End If
			Next
			fncParseNameSplit = Trim(fncConjunctionReplacement(strOutput))
		End If
	End Function

	Function fncQuote(strText)
		If strText <> "" Then
			fncQuote = chr(34) & strText & chr(34)
		End If
	End Function

	Function fncReplaceDoubleChars(strInputText)
		If strInputText <> "" Then
			strOutputText = Trim(strInputText)
			arrDoubleChars = Array( _
				" ", _
				".")
			For Each strEvaluationChar In arrDoubleChars
				strSingleChar = strEvaluationChar
				strDoubleChar = strSingleChar & strSingleChar
				Do While InStr(strOutputText, strDoubleChar)
					strOutputText = Replace(strOutputText, strDoubleChar, strSingleChar)
				Loop
			Next
			fncReplaceDoubleChars = Trim(strOutputText)
		Else
			fncReplaceDoubleChars = Trim(strInputText)
		End If
	End Function

	Function fncRightCleanup(strText)
		If strText <> "" Then
			strOutput = Trim(strText)
			arrInvalidChars = Array( _
				".", _
				"_", _
				"(")
			For Each strInvalidChar In arrInvalidChars
				Do While Right(strOutput, Len(strInvalidChar)) = strInvalidChar
					strOutput = Left(strOutput, Len(strOutput) - 1)
				Loop
			Next
			fncRightCleanup = Trim(strOutput)
		Else
			fncRightCleanup = Trim(strText)
		End If
	End Function

	Function fncSplitException(strPreSplitData, strPostSplitData)
		If strPreSplitData <> "" And strPostSplitData <> "" Then
			If fncWordCount(strPreSplitData) = 1 And fncWordCount(strPostSplitData) >= 2 Then
				fncSplitException = True
			ElseIf fncWordCount(strPreSplitData) <= 2 And fncWordCount(strPostSplitData) = 1 Then
				fncSplitException = True
			ElseIf fncWordCount(strPreSplitData) = 2 And fncWordCount(strPostSplitData) = 2 Then
				fncSplitException = False
			Else
				fncSplitException = False
			End If
		Else
			fncSplitException = False
		End If
	End Function

	Function fncWordCount(strText)
		If strText <> "" Then
			arrTextDetails = Split(strText, " ")
			fncWordCount = UBound(arrTextDetails) + 1
		Else
			fncWordCount = 1
		End If
	End Function


' Subroutines -------------

	Sub subCatalogDirectory(strDirectory)
		If strDirectory <> "" Then
			If fncDirectoryExists(strDirectory) Then
				Set objParentDirectory = objFSO.GetFolder(strDirectory)
				For Each objSubDirectory In objParentDirectory.SubFolders
					If Not dicDirectories.Exists(objSubDirectory.Path) Then
						dicDirectories.Add objSubDirectory.Path, objSubDirectory.Name
						dicSubDirectoryCount.Add objSubDirectory.Path, objSubDirectory.SubFolders.Count
						dicFileCount.Add objSubDirectory.Path, objSubDirectory.Files.Count
						If objSubDirectory.Files.Count >= 1 Then
							For Each objFile In objSubDirectory.Files
								If Right(objFile.Path, Len(strTargetExtension)) = strTargetExtension Then
									If Not dicTargetFiles.Exists(objFile.Path) Then
										dicTargetFiles.Add objFile.Path, objFile.Name
									End If
								Else
									If Not dicInvalidFiles.Exists(objFile.Path) Then
										dicInvalidFiles.Add objFile.Path, objFile.Name
									End If
								End If
								subEvaluateDuplicateFileName objFile.Path
							Next
						End If
						subEvaluateDirectoryName objSubDirectory.Path, objSubDirectory.Name
						SubCatalogDirectory objSubDirectory.Path
					End If
				Next
				Set objParentDirectory = Nothing
			End If
		End If
	End Sub

	Sub subCatalogFiles(strDirectory)
		If strDirectory <> "" Then
			dicTempFileList.RemoveAll
			If fncDirectoryExists(strDirectory) Then
				Set objParentDirectory = objFSO.GetFolder(strDirectory)
				Set colFiles = objParentDirectory.Files
				For Each objFile In colFiles
					If Not dicTempFileList.Exists(objFile.Path) Then
						dicTempFileList.Add objFile.Path, objFile.Name
					End If
				Next
				Set objParentDirectory = Nothing
			End If
		End If
	End Sub

	Sub subDirectoryCreate(strDirectoryPath)
		If strDirectoryPath <> "" Then
			If Not fncDirectoryExists(strDirectoryPath) Then
				objFSO.CreateFolder(strDirectoryPath)
			End If
		End If
	End Sub

	Sub subDirectoryDelete(strDirectoryPath)
		If strDirectoryPath <> "" And strDirectoryPath <> strBaseDirectory Then
			If fncDirectoryExists(strDirectoryPath) Then
				subCatalogFiles(strDirectoryPath)
				If dicTempFileList.Count = 0 Then
					If Not dicDirectoryDeletions.Exists(strDirectoryPath) Then
						On Error Resume Next
							objFSO.DeleteFolder(strDirectoryPath)
							If Err.Number <> 0 Then
								dicDirectoryDeletionFailures.Add strDirectoryPath, Err.Description
							Else
								dicDirectoryDeletions.Add strDirectoryPath, Now()
							End If
							Err.Clear
						On Error Goto 0
					End If
				Else
					If Not dicDirectoryDeletionFailures.Exists(strDirectoryPath) Then
						dicDirectoryDeletionFailures.Add strDirectoryPath, "Path Not Empty"
					End If
					dicTempFileList.RemoveAll
				End If
			Else
				If Not dicDirectoryDeletionFailures.Exists(strDirectoryPath) Then
					dicDirectoryDeletionFailures.Add strDirectoryPath, "Path Not Found"
				End If
			End If
		Else
			If Not dicDirectoryDeletionFailures.Exists(strDirectoryPath) Then
				dicDirectoryDeletionFailures.Add strDirectoryPath, "Invalid Data Passed To Function:  " & fncQuote(strDirectoryPath)
			End If
		End If
	End Sub
	
	Sub subDirectoryMove(strSourcePath, strDestinationPath)
		If strSourcePath <> "" And strDestinationPath <> "" And strDestinationPath <> strBaseDirectory Then
			If fncDirectoryExists(strSourcePath) And Not fncDirectoryExists(strDestinationPath) Then
				objFSO.MoveFolder strSourcePath, strDestinationPath
			End If
		End If
	End Sub

	Sub subEvaluateDirectoryName(strDirectoryPath, strDirectoryName)
		If strDirectoryPath <> "" And strDirectoryName <> "" Then
			strOutputName = fncParseNameSplit(strDirectoryName)
			strOutputName = fncReplaceDoubleChars(strOutputName)
			strOutputName = fncRightCleanup(strOutputName)
			strOutputName = Trim(strOutputName)
			If InStr(strOutputName, " ") Then
				arrOutputNameDetails = Split(strOutputName, " ")
				strFirstOutputName = arrOutputNameDetails(0)
			Else
				strFirstOutputName = strDirectoryName
			End If
			If strDirectoryName <> strOutputName Then
				If Not dicDirectoryCorrections.Exists(strDirectoryPath) Then
					dicDirectoryCorrections.Add strDirectoryPath, strBaseDirectory & "\" & strOutputName
				End If
			ElseIf Len(strDirectoryName) <= 2 And IsNumeric(strDirectoryName) Then
				If Not dicUnMatchedDirectories.Exists(strDirectoryPath) Then
					dicUnMatchedDirectories.Add strDirectoryPath, strUnsortedDirectory & "\" & strOutputName
				End If
			ElseIf Len(strFirstOutputName) = 3 And InStr(strFirstOutputName, ".") And IsNumeric(fncRightCleanup(strFirstOutputName)) Then
				If Not dicUnMatchedDirectories.Exists(strDirectoryPath) Then
					dicUnMatchedDirectories.Add strDirectoryPath, strUnsortedDirectory & "\" & strOutputName
				End If
			End If
		End If
	End Sub

	Sub subEvaluateDuplicateFileName(strFilePath)
		If strFilePath <> "" Then
			blnDuplicate = False
			If InStr(strFilePath, " - Copy." & strTargetExtension) Then
				strCorrectFilePath = Replace(strFilePath, " - Copy." & strTargetExtension, "." & strTargetExtension)
				blnDuplicate = True
			Else
				For intDupLevel = 1 to 9
					arrAdditiveStrings = Array( _
						".", _
						"(/)", _
						"[/]")
					For Each strAdditiveString In arrAdditiveStrings
						Select Case strAdditiveString
							Case "."
								strEvaluationValue = strAdditiveString & intDupLevel & strAdditiveString & strTargetExtension
							Case Else
								arrAdditiveStringDetails = Split(strAdditiveString, "/")
								strPreValue = arrAdditiveStringDetails(0)
								strPostValue = arrAdditiveStringDetails(1)
								strEvaluationValue = strPreValue & intDupLevel & strPostValue & "." & strTargetExtension
						End Select
						If InStr(strFilePath, strEvaluationValue) Then
							strCorrectFilePath = Replace(strFilePath, strEvaluationValue, "." & strTargetExtension)
							blnDuplicate = True
							Exit For
						End If
					Next
					If blnDuplicate Then
						Exit For
					End If
				Next
			End If
			If blnDuplicate Then
				If Not dicDuplicateFiles.Exists(strFilePath) Then
					dicDuplicateFiles.Add strFilePath, strCorrectFilePath
				End If
			End If
		End If
	End Sub

	Sub subEvaluateFileCopyMove(strSourceFile, strDestFile)
		If strSourceFile <> "" And strDestFile <> "" Then
			If fncFileExists(strSourceFile) And fncFileExists(strDestFile) Then
				subFileDelete strSourceFile
			ElseIf fncFileExists(strSourceFile) And Not fncFileExists(strDestFile) Then
				subFileMove strSourceFile, strDestFile
			End If
		End If
	End Sub

	Sub subFileDelete(strFilePath)
		If strFilePath <> "" Then
			If fncFileExists(strFilePath) Then
				On Error Resume Next
					objFSO.DeleteFile(strFilePath)
					If Err.Number <> 0 Then
						dicFileDeletionFailures.Add strFilePath, Err.Description
					End If
					Err.Clear
				On Error Goto 0
			Else
				If Not dicFileDeletionFailures.Exists(strDirectoryPath) Then
					dicFileDeletionFailures.Add strFilePath, "File Not Found"
				End If
			End If
		Else
			If Not dicFileDeletionFailures.Exists(strDirectoryPath) Then
				dicFileDeletionFailures.Add strFilePath, "Invalid Data Passed To Function:  " & fncQuote(strFilePath)
			End If
		End If
	End Sub

	Sub subFileMove(strSourceFilePath, strDestFilePath)
		If strSourceFilePath <> "" And strDestFilePath <> "" And InStr(strDestFilePath, "\") Then
			arrDestFileDetails = Split(strDestFilePath, "\")
			strDestFileName = arrDestFileDetails(UBound(arrDestFileDetails))
			strDestDirectory = Replace(strDestFilePath, strDestFileName, "")
			subDirectoryCreate(strDestDirectory)
			If fncFileExists(strSourceFilePath) And Not fncFileExists(strDestFilePath) Then
				objFSO.MoveFile strSourceFilePath, strDestFilePath
			End If
		End If
	End Sub

I can’t leave well enough alone. Latest version of this thing… I played with Powershell a bit and if I keep doing changes like this, I’ll probably end up going with that language in the future, just not right this second.

  • I added more safeguards to prevent sweeping mistakes. Can’t have too many.
  • Added a test mode to have it just dump the output of what it would have done.
  • Cleaned up the duplicate code I had by throwing in dynamic code blocks when able.
  • Redid the logging entirely to make it much more functional.

** Use it on test folders prior to playing with it, same as before.

' *****************************
' *** MP3 Folder Processing ***
' *****************************

' =============================
' =============================

' User Defined Variables ------

	strBaseDirectory = "\\server\share"
	strTargetExtension = "mp3"
	strAlbumArtName = "cover.jpg"

	blnProcessUnsorted = True
	strUnsortedDirectory = "\\server\share"

	blnTestMode = False

	intCorrectionAbortPercentage = 7.5

' Environment Variables -------

	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set objShell = CreateObject("WScript.Shell")

	Const ForAppending = 8

	strScriptPath = WScript.ScriptFullName
	Set objScript = objFSO.GetFile(strScriptPath)
	strScriptName = objScript.Name
	strLogName = Replace(strScriptName , ".vbs", ".log")
	strLogPath = Replace(strScriptPath, strScriptName, strLogName)
	Set objLogFile = objFSO.OpenTextFile(strLogPath,ForAppending,True)


' Dictionaries ---------------

	arrDictionaries = Array( _
		"Directories", _
		"InvalidFiles", _
		"DuplicateFiles", _
		"SimilarArtistMultipleDirectory", _
		"DirectoryCorrections", _
		"UnmatchedDirectories", _
		"DirectoryDeletions", _
		"DirectoryDeletionFailures", _
		"FileCount", _
		"FileDeletions", _
		"FileDeletionFailures", _
		"SubDirectoryCount", _
		"TargetFiles", _
		"TempFileList", _
		"TempWorkFlow")
	For Each strDictionary In arrDictionaries
		strTargetDictionary = "dic" & strDictionary
		strExecCmd = "Set " & strTargetDictionary & " = CreateObject(""Scripting.Dictionary"")"
		Execute strExecCmd
		Select Case strDictionary
			Case "Directories", "SimilarArtistMultipleDirectory"
				strExecCmd = strTargetDictionary & ".CompareMode = VBTextCompare"
				Execute strExecCmd
		End Select
	Next

' Primary Execution ---------

	dtmTimeStart = Now()
	subLog "Script execution begin.  Test mode = " & blnTestMode
	subLog "Creating catalog"
	subCatalogDirectory strBaseDirectory
	dtmCatalogComplete = Now()
	subLog "Catalog creation complete"

	intDirectoryCorrectionPercentage = Round(dicDirectoryCorrections.Count / dicDirectories.Count * 100, 2)
	If intDirectoryCorrectionPercentage >= intCorrectionAbortPercentage Then
		strMessage = "Error:  Directory correction percentage threshold reached (" & intDirectoryCorrectionPercentage & "%).  Terminating script as safeguard."
		subLog strMessage
		objLogFile.Close
		MsgBox strMessage, 0, UCase(strTargetExtension) & " Processing"
		WScript.Quit
	Else
		subLog "Directory correction percentage threshold within defined limits (" & intDirectoryCorrectionPercentage & "%).  Continuing Script."
	End If

	For Each strDictionary In arrDictionaries
		strTargetDictionary = "dic" & strDictionary
		Execute "intTargetDictionaryCount = " & strTargetDictionary & ".Count"
		If intTargetDictionaryCount >= 1 Then
			subCloneDictionary strTargetDictionary, "dicTempWorkFlow"
			subLog "Processing dictionary object " & fncQuote(strTargetDictionary) & ", Total Records = " & intTargetDictionaryCount
			Select Case strDictionary
				Case "DirectoryCorrections", "SimilarArtistMultipleDirectory", "UnmatchedDirectories"
					strExecCmd = _
						"If " & strDictionary & " <> ""UnmatchedDirectories"" Or blnProcessUnsorted Then" & vbCrLf & _
							"blnActionRequired = True" & vbCrLf & _
						"End If"
					Execute strExecCmd
					If blnActionRequired Then
						For Each strKey In dicTempWorkFlow
							strValue = dicTempWorkFlow.Item(strKey)
							If strValue <> strBaseDirectory Then
								If fncDirectoryExists(strValue) Then
									subCatalogFiles(strKey)
									For Each strSourceFilePath In dicTempFileList
										strSourceFileName = dicTempFileList.Item(strSourceFilePath)
										strDestFilePath = strValue & "\" & strSourceFileName
										subEvaluateFileCopyMove strSourceFilePath, strDestFilePath
									Next
									dicTempFileList.RemoveAll
									subDirectoryDelete(strKey)
								Else
									subDirectoryMove strKey, strValue
								End If
							End If
						Next
					End If
				Case "DirectoryDeletionFailures", "FileDeletionFailures"
					strFSOType = ""
					If InStr(strDictionary, "Directory") Then
						strFSOType = "Directory"
					ElseIf InStr(strDictionary, "File") Then
						strFSOType = "File"
					End If
					If strFSOType <> "" Then
						For Each strKey in dicTempWorkFlow
							strValue = dicTempWorkFlow.Item(strKey)
							If InStr(strValue, "Path Not Empty") Or InStr(strValue, "File Not Found") Then
								strExecCmd = _
									"If fnc" & strFSOType & "Exists(" & strKey & ") Then" & vbCrLf & _
										"blnObjectExists = True" & vbCrLf & _
									"Else" & vbCrLf & _
										"blnObjectExists = False" & vbCrLf & _
									"End If"
								Execute strExecCmd
								If blnObjectExists Then
									subLog "Deletion failure against " & fncQuote(strKey)
								Else
									subLog "Validated successful removal of " & fncQuote(strKey)
								End If
							End If
						Next
					End If
				Case "DirectoryDeletions"
					For Each strKey In dicTempWorkFlow
						strValue = dicTempWorkFlow.Item(strKey)
						If dicFileCount.Exists(strKey) And dicSubDirectoryCount.Exists(strKey) Then
							intFileCount = dicFileCount.Item(strKey)
							intSubDirectoryCount = dicSubDirectoryCount.Item(strKey)
							If intFileCount = 0 And intSubDirectoryCount = 0 Then
								subDirectoryDelete(strKey)
							End If
						End If
					Next
				Case "DuplicateFiles"
					For Each strKey In dicTempWorkFlow
						strValue = dicTempWorkFlow.Item(strKey)
						If dicTargetFiles.Exists(strValue) Then
							subFileDelete(strKey)
						Else
							subEvaluateFileCopyMove strKey, strValue
						End If
					Next
				Case "InvalidFiles"
					For Each strKey In dicTempWorkFlow
						strValue = dicTempWorkFlow.Item(strKey)
						strActionRequired = fncEvaluateExtension(strKey)
						Select Case strActionRequired
							Case "CORRECT"
								strInvalidFileExtension = objFSO.GetExtensionName(strKey)
								strCorrectFileName = Left(strValue, Len(strValue) - Len(strInvalidFileExtension)) & strTargetExtension
								strCorrectFilePath = Replace(strKey, strValue, strCorrectFileName)
								subEvaluateFileCopyMove strKey, strCorrectFilePath
							Case "DELETE"
								subFileDelete(strKey)
							Case "EXCLUDE"
								subLog "File extension exclusion:  " & fncQuote(strKey)
						End Select
					Next
			End Select
		End If
		dicTempWorkFlow.RemoveAll
	Next

	dtmTimeEnd = Now()
	intCatalogExecutionTime = DateDiff("n", dtmTimeStart, dtmCatalogComplete)
	intCorrectiveActionExecutionTime = DateDiff("n", dtmCatalogComplete, dtmTimeEnd)
	intScriptExecutionTime = DateDiff("n", dtmTimeStart, dtmTimeEnd)
	subLog "Script Execution Complete.  Total Run Time = " & intScriptExecutionTime & " minutes."
	objLogFile.Close
	
	strCompletionMessage = UCase(strTargetExtension) & " processing complete.  (Total = " & dicTargetFiles.Count & ")" & vbCrLf & vbCrLf & _
		"Directory Corrections:  " & dicDirectoryCorrections.Count & vbCrLf & _
		"Directory Removals:  " & dicDirectoryDeletions.Count & vbCrLf & _
		"Similar Directories:  " & dicSimilarArtistMultipleDirectory.Count & vbCrLf & _
		"Unmatched Directories:  " & dicUnMatchedDirectories.Count & vbCrLf & _
		"Duplicate " & UCase(strTargetExtension) & " Removals:  " & dicDuplicateFiles.Count & vbCrLf & _
		"Invalid File Extensions:  " & dicInvalidFiles.Count & vbCrLf & vbCrLf & _
		"Run Time:  " & intScriptExecutionTime & " minutes (Catalog:  " & intCatalogExecutionTime & " / Corrections:  " & intCorrectiveActionExecutionTime & ")"

	MsgBox strCompletionMessage, 0, UCase(strTargetExtension) & " Processing"
	

' Functions ---------------

	Function fncConjunctionReplacement(strText)
		If strText <> "" Then
			strOutput = strText
			arrUnwantedConjunctions = Array( _
				" And ", _
				" and ")
			For Each strUnwantedConjunction In arrUnwantedConjunctions
				strOutput = Replace(strOutput, strUnwantedConjunction, " & ")
			Next
			fncConjunctionReplacement = strOutput
		Else
			fncConjunctionReplacement = strText
		End If
	End Function

	Function fncDirectoryExists(strDirectory)
		If strDirectory <> "" Then
			If objFSO.FolderExists(strDirectory) Then
				fncDirectoryExists = True
			Else
				fncDirectoryExists = False
			End If
		Else
			fncDirectoryExists = False
		End If
	End Function

	Function fncEvaluateExtension(strFilePath)
		If strFilePath <> "" Then
			blnActionRequired = True
			strInvalidFileExtension = objFSO.GetExtensionName(strFilePath)
			arrInvalidFileExceptions = Array( _
				"8svx", _
				"aax", _
				"act", _
				"ai", _
				"aif", _
				"aifc", _
				"aiff", _
				"alac", _
				"amr", _
				"ape", _
				"au", _
				"awb", _
				"dct", _
				"dss", _
				"dvf", _
				"ea", _
				"eps", _
				"flac", _
				"gif", _
				"gsm", _
				"iklax", _
				"indd", _
				"ivs", _
				"jpeg", _
				"jpg", _
				"m4a", _
				"m4b", _
				"m4p", _
				"m4u", _
				"midi", _
				"mmf", _
				"mp3", _
				"mpc", _
				"msv", _
				"nsf", _
				"ogg", _
				"ogg", _
				"opus", _
				"pdf", _
				"png", _
				"psd", _
				"ra", _
				"raw", _
				"sln", _
				"tif", _
				"tiff", _
				"tta", _
				"vox", _
				"wav", _
				"wave", _
				"webm", _
				"wma", _
				"wv")
			For Each strInvalidFileException In arrInvalidFileExceptions
				If strInvalidFileExtension = strInvalidFileException And Not Right(strInvalidFilePath, Len(strAlbumArtName)) = strAlbumArtName Then
					blnActionRequired = False
					Exit For
				End If
			Next
			If Right(strInvalidFilePath, Len(strAlbumArtName)) = strAlbumArtName Then
				fncEvaluateExtension = "DELETE"
			Else
				If blnActionRequired Then
					fncEvaluateExtension = "CORRECT"
				Else
					fncEvaluateExtension = "EXCLUDE"
				End If
			End If
		Else
			fncEvaluateExtension = "INVALID"
		End If
	End Function

	Function fncFileExists(strFile)
		If strFile <> "" Then
			If objFSO.FileExists(strFile) Then
				fncFileExists = True
			Else
				fncFileExists = False
			End If
		Else
			fncFileExists = False
		End If
	End Function
	
	Function fncParseNameSplit(strDirectoryName)
		If strDirectoryName <> "" Then
			strOutput = Trim(strDirectoryName)
			arrSplitEvals = Array( _
				";", _
				",", _
				" - ", _
				" Avec ", _
				" avec ", _
				" CD ", _
				" cd ", _
				" Der ", _
				" der ", _
				"Duet", _
				"duet", _
				" En ", _
				" en ", _
				"Feat", _
				"feat", _
				" (Ft", _
				" (ft", _
				" [Ft", _
				" [ft", _
				" Ft", _
				" ft", _
				" Med ", _
				" med ", _
				" Met ", _
				" met ", _
				" Meet ", _
				" meet ", _
				" Mit ", _
				" mit ", _
				"Original", _
				"original", _
				"Pheaturing", _
				"pheaturing", _
				" Read by ", _
				" read by ", _
				"Soundtrack", _
				"soundtrack", _
				" Und ", _
				" und ", _
				" Vs", _
				" vs", _
				" part ", _
				" Part ", _
				"Presents", _
				"presents", _
				"+", _
				"&", _
				" And ", _
				" and ", _
				"With", _
				"with")
			For Each strSplitEval In arrSplitEvals
				If InStr(strDirectoryName, strSplitEval) Then
					arrSplitDetails = Split(strDirectoryName, strSplitEval)
					strPreSplitData = Trim(arrSplitDetails(0))
					strPostSplitData = Trim(arrSplitDetails(UBound(arrSplitDetails)))
					If strPreSplitData <> "" Then
						If Not dicDirectories.Exists(strBaseDirectory & "\" & strPreSplitData) Then
							Select Case strSplitEval
								Case "+", "&", " And ", " and "
									blnException = False
									arrSplitExceptions = Array( _
										"family", _
										"his", _
										"her", _
										"jr", _
										"the", _
										"nine stories", _
										"z")
									For Each strSplitException In arrSplitExceptions
										If Left(Trim(LCase(strPostSplitData)), Len(strSplitException)) = LCase(strSplitException) Then
											blnException = True
											Exit For
										End If
									Next
									If fncSplitException(strPreSplitData, strPostSplitData) Then
										blnException = True
									End If
									If blnException Then
										For Each strOverrideEval In arrSplitEvals
											If InStr(strPreSplitData, strOverrideEval) Then
												If fncSplitException(strPreSplitData, strPostSplitData) Then
													strOutput = Trim(strOutput)
													Exit For
												Else
													strOutput = fncParseNameSplit(Trim(strPreSplitData))
												End If
											Else
												strOutput = Trim(strOutput)
												Exit For
											End If
										Next
									Else
										strOutput = fncParseNameSplit(Trim(strPreSplitData))
									End If
								Case ","
									blnException = False
									arrSplitExceptions = Array( _
										"Earth, Wind & Fire")
									For Each strSplitException In arrSplitExceptions
										If Left(Trim(LCase(strDirectoryName)), Len(strSplitException)) = LCase(strSplitException) Then
											blnException = True
											Exit For
										End If
									Next
									If Not blnException Then
										If fncWordCount(strDirectoryName) <= 2 Then
											strOutput = Trim(strDirectoryName)
										Else
											strOutput = fncParseNameSplit(Trim(strPreSplitData))
										End If
									Else
										strOutput = Trim(strDirectoryName)
									End If
								Case Else
									strOutput = fncParseNameSplit(Trim(strPreSplitData))
									Exit For
							End Select
						Else
							strOutput = Trim(strPreSplitData)
						End If
					Else
						strOutput = Trim(strDirectoryName)
					End If
				End If
			Next
			fncParseNameSplit = Trim(fncConjunctionReplacement(strOutput))
		End If
	End Function

	Function fncQuote(strText)
		If strText <> "" And Not InStr(strText, Chr(34)) Then
			fncQuote = Chr(34) & strText & Chr(34)
		Else
			fncQuote = strText
		End If
	End Function

	Function fncRemovePlural(strText)
		If strText <> "" And InStr(strText, "'s") Then
			fncRemovePlural = Replace(strText, "'s", "")
		Else
			fncRemovePlural = strText
		End If
	End Function

	Function fncReplaceDoubleChars(strInputText)
		If strInputText <> "" Then
			strOutputText = Trim(strInputText)
			arrDoubleChars = Array( _
				" ", _
				".")
			For Each strEvaluationChar In arrDoubleChars
				strSingleChar = strEvaluationChar
				strDoubleChar = strSingleChar & strSingleChar
				Do While InStr(strOutputText, strDoubleChar)
					strOutputText = Replace(strOutputText, strDoubleChar, strSingleChar)
				Loop
			Next
			fncReplaceDoubleChars = Trim(strOutputText)
		Else
			fncReplaceDoubleChars = Trim(strInputText)
		End If
	End Function

	Function fncRightCleanup(strText)
		If strText <> "" Then
			strOutput = Trim(strText)
			arrInvalidChars = Array( _
				".", _
				"_", _
				"(")
			For Each strInvalidChar In arrInvalidChars
				Do While Right(strOutput, Len(strInvalidChar)) = strInvalidChar
					strOutput = Left(strOutput, Len(strOutput) - 1)
				Loop
			Next
			fncRightCleanup = Trim(strOutput)
		Else
			fncRightCleanup = Trim(strText)
		End If
	End Function

	Function fncSplitException(strPreSplitData, strPostSplitData)
		If strPreSplitData <> "" And strPostSplitData <> "" Then
			If fncWordCount(strPreSplitData) + fncWordCountPostSplitData <= 3 Then
				fncSplitException = True
			Else
				If fncWordCount(strPreSplitData) = 1 And fncWordCount(strPostSplitData) >= 2 Then
					fncSplitException = True
				ElseIf fncWordCount(strPreSplitData) <= 2 And fncWordCount(strPostSplitData) = 1 Then
					fncSplitException = True
				ElseIf fncWordCount(strPreSplitData) = 2 And fncWordCount(strPostSplitData) = 2 Then
					fncSplitException = False
				Else
					fncSplitException = False
				End If
			End If
		Else
			fncSplitException = False
		End If
	End Function

	Function fncWordCount(strText)
		If strText <> "" Then
			arrTextDetails = Split(strText, " ")
			fncWordCount = UBound(arrTextDetails) + 1
		Else
			fncWordCount = 1
		End If
	End Function


' Subroutines -------------

	Sub subCatalogDirectory(strDirectory)
		If strDirectory <> "" Then
			If fncDirectoryExists(strDirectory) Then
				Set objParentDirectory = objFSO.GetFolder(strDirectory)
				For Each objSubDirectory In objParentDirectory.SubFolders
					If Not dicDirectories.Exists(objSubDirectory.Path) Then
						dicDirectories.Add objSubDirectory.Path, objSubDirectory.Name
						dicSubDirectoryCount.Add objSubDirectory.Path, objSubDirectory.SubFolders.Count
						dicFileCount.Add objSubDirectory.Path, objSubDirectory.Files.Count
						If objSubDirectory.Files.Count >= 1 Then
							For Each objFile In objSubDirectory.Files
								If Right(objFile.Path, Len(strTargetExtension)) = strTargetExtension Then
									If Not dicTargetFiles.Exists(objFile.Path) Then
										dicTargetFiles.Add objFile.Path, objFile.Name
									End If
								Else
									If Not dicInvalidFiles.Exists(objFile.Path) Then
										dicInvalidFiles.Add objFile.Path, objFile.Name
									End If
								End If
								subEvaluateFileDuplicate objFile.Path
							Next
						End If
						subEvaluateDirectoryName objSubDirectory.Path, objSubDirectory.Name
						SubCatalogDirectory objSubDirectory.Path
					End If
				Next
				Set objParentDirectory = Nothing
			End If
		End If
	End Sub

	Sub subCatalogFiles(strDirectory)
		If strDirectory <> "" Then
			dicTempFileList.RemoveAll
			If fncDirectoryExists(strDirectory) Then
				Set objParentDirectory = objFSO.GetFolder(strDirectory)
				Set colFiles = objParentDirectory.Files
				For Each objFile In colFiles
					If Not dicTempFileList.Exists(objFile.Path) Then
						dicTempFileList.Add objFile.Path, objFile.Name
					End If
				Next
				Set objParentDirectory = Nothing
			End If
		End If
	End Sub

	Sub subCloneDictionary(strSourceDic, strDestDic)
		If strSourceDic <> "" And strDestDic <> "" Then
			strExecCmd = _
				strDestDic & ".RemoveAll" & vbCrLf & _
				"arrDicKeys = " & strSourceDic & ".Keys" & vbCrLf & _
				"arrDicValues = " & strSourceDic & ".Items"
			Execute strExecCmd
			For intCurrentRecord = LBound(arrDicKeys) To UBound(arrDicKeys)
				strExecCmd = _
					"If Not " & strDestDic & ".Exists(""" & arrDicKeys(intCurrentRecord) & """) Then" & vbCrLf & _
						strDestDic & ".Add """ & arrDicKeys(intCurrentRecord) & """, """ & arrDicValues(intCurrentRecord) & """" & vbCrLf & _
					"End If"
				Execute strExecCmd
			Next
		End If
	End Sub

	Sub subDirectoryCreate(strDirectoryPath)
		If strDirectoryPath <> "" Then
			If Not fncDirectoryExists(strDirectoryPath) Then
				subLog "Directory creation:  " & fncQuote(strDirectoryPath)
				If Not blnTestMode Then
					objFSO.CreateFolder(strDirectoryPath)
				End If
			End If
		End If
	End Sub

	Sub subDirectoryDelete(strDirectoryPath)
		If strDirectoryPath <> "" And strDirectoryPath <> strBaseDirectory Then
			If fncDirectoryExists(strDirectoryPath) Then
				subCatalogFiles(strDirectoryPath)
				If dicTempFileList.Count = 0 Then
					If Not dicDirectoryDeletions.Exists(strDirectoryPath) Then
						subLog "Directory deletion:  " & fncQuote(strDirectoryPath)
						If Not blnTestMode Then
							Set objPhysicalFolderValidation = objFSO.GetFolder(strDirectoryPath)
							intFSFileSystemCount = objPhysicalFolderValidation.Files.Count
							intFSFolderSystemCount = objPhysicalFolderValidation.SubFolders.Count
							If intFSFileSystemCount = 0 And intFSFolderSystemCount = 0 Then
								On Error Resume Next
									objFSO.DeleteFolder(strDirectoryPath)
									If Err.Number <> 0 Then
										dicDirectoryDeletionFailures.Add strDirectoryPath, Err.Description
									Else
										dicDirectoryDeletions.Add strDirectoryPath, Now()
									End If
									Err.Clear
								On Error Goto 0
							Else
								subLog "Directory deletion failed final physical validation test.  " & fncQuote(strDirectoryPath) & " not empty!"
							End If
							Set objPhysicalFolderValidation = Nothing
						End If
					End If
				Else
					If Not dicDirectoryDeletionFailures.Exists(strDirectoryPath) Then
						dicDirectoryDeletionFailures.Add strDirectoryPath, "Path Not Empty"
					End If
					dicTempFileList.RemoveAll
				End If
			Else
				If Not dicDirectoryDeletionFailures.Exists(strDirectoryPath) Then
					dicDirectoryDeletionFailures.Add strDirectoryPath, "Path Not Found"
				End If
			End If
		Else
			If Not dicDirectoryDeletionFailures.Exists(strDirectoryPath) Then
				dicDirectoryDeletionFailures.Add strDirectoryPath, "Invalid Data Passed To Function:  " & fncQuote(strDirectoryPath)
			End If
		End If
	End Sub
	
	Sub subDirectoryMove(strSourcePath, strDestinationPath)
		If strSourcePath <> "" And strDestinationPath <> "" And strDestinationPath <> strBaseDirectory Then
			If fncDirectoryExists(strSourcePath) And Not fncDirectoryExists(strDestinationPath) Then
				subLog "Directory move:  " & fncQuote(strSourcePath) & " --> " & fncQuote(strDestinationPath)
				If Not blnTestMode Then
					objFSO.MoveFolder strSourcePath, strDestinationPath
				End If
			End If
		End If
	End Sub

	Sub subEvaluateDirectoryMultiples(strDirectoryPath, strDirectoryName)
		If strDirectoryPath <> "" And strDirectoryName <> "" Then
			blnActionRequired = True
			blnSimilarMatch = False
			arrDuplicateExceptions = Array( _
				"Dee Dee", _
				"DJ", _
				"New World", _
				"South Park Mexican")
			For Each strDuplicateException In arrDuplicateExceptions
				If InStr(LCase(strDirectoryName), LCase(strDuplicateException)) Then
					blnActionRequired = False
				End If
			Next
			If blnActionRequired Then
				intDirectoryWordCount = fncWordCount(strDirectoryName)
				If intDirectoryWordCount >= 2 Then
					arrDirectoryDetails = Split(strDirectoryName, " ")
					intDirectoryDetailsUpper = UBound(arrDirectoryDetails)
					strCurrentEvaluationPath = Trim(fncRemovePlural(strBaseDirectory & "\" & arrDirectoryDetails(0) & " " & arrDirectoryDetails(1)))
					If dicDirectories.Exists(strCurrentEvaluationPath) And LCase(strDirectoryPath) <> LCase(strCurrentEvaluationPath) And LCase(strCurrentEvaluationPath) <> LCase(strBaseDirectory) Then
						If Not dicSimilarArtistMultipleDirectory.Exists(strDirectoryPath) Then
							dicSimilarArtistMultipleDirectory.Add strDirectoryPath, strCurrentEvaluationPath
							blnSimilarMatch = True
						End If
					End If
					If intDirectoryDetailsUpper > 2 And Not blnSimilarMatch And LCase(strDirectoryPath) <> LCase(strCurrentEvaluationPath) And LCase(strCurrentEvaluationPath) <> LCase(strBaseDirectory) Then
						For intCurrentDirectoryEvaluation = 2 To intDirectoryDetailsUpper
							strCurrentEvaluationPath = strCurrentEvaluationPath & " " & arrDirectoryDetails(intCurrentDirectoryEvaluation)
							If dicDirectories.Exists(strCurrentEvaluationPath) And LCase(strDirectoryPath) <> LCase(strCurrentEvaluationPath) And LCase(strCurrentEvaluationPath) <> LCase(strBaseDirectory) Then
								If Not dicSimilarArtistMultipleDirectory.Exists(strDirectoryPath) Then
									dicSimilarArtistMultipleDirectory.Add strDirectoryPath, strCurrentEvaluationPath
									Exit For
								End If
							End If
						Next
					End If
				End If
			End If
		End If
	End Sub

	Sub subEvaluateDirectoryName(strDirectoryPath, strDirectoryName)
		If strDirectoryPath <> "" And strDirectoryName <> "" Then
			strOutputName = fncParseNameSplit(strDirectoryName)
			strOutputName = fncReplaceDoubleChars(strOutputName)
			strOutputName = fncRightCleanup(strOutputName)
			strOutputName = Trim(strOutputName)
			subEvaluateDirectoryMultiples strDirectoryPath, strDirectoryName
			If Not dicSimilarArtistMultipleDirectory.Exists(strDirectoryPath) Then
				If InStr(strOutputName, " ") Then
					arrOutputNameDetails = Split(strOutputName, " ")
					strFirstOutputName = arrOutputNameDetails(0)
				Else
					strFirstOutputName = strDirectoryName
				End If
				If strDirectoryName <> strOutputName Then
					If Not dicDirectoryCorrections.Exists(strDirectoryPath) Then
						dicDirectoryCorrections.Add strDirectoryPath, strBaseDirectory & "\" & strOutputName
					End If
				ElseIf Len(strDirectoryName) <= 2 And IsNumeric(strDirectoryName) Then
					If Not dicUnMatchedDirectories.Exists(strDirectoryPath) Then
						dicUnMatchedDirectories.Add strDirectoryPath, strUnsortedDirectory & "\" & strOutputName
					End If
				ElseIf Len(strFirstOutputName) = 3 And InStr(strFirstOutputName, ".") And IsNumeric(fncRightCleanup(strFirstOutputName)) Then
					If Not dicUnMatchedDirectories.Exists(strDirectoryPath) Then
						dicUnMatchedDirectories.Add strDirectoryPath, strUnsortedDirectory & "\" & strOutputName
					End If
				End If
			End If
		End If
	End Sub

	Sub subEvaluateFileCopyMove(strSourceFile, strDestFile)
		If strSourceFile <> "" And strDestFile <> "" Then
			If fncFileExists(strSourceFile) And fncFileExists(strDestFile) Then
				subFileDelete strSourceFile
			ElseIf fncFileExists(strSourceFile) And Not fncFileExists(strDestFile) Then
				subFileMove strSourceFile, strDestFile
			End If
		End If
	End Sub

	Sub subFileDelete(strFilePath)
		If strFilePath <> "" Then
			subLog "File deletion:  " & fncQuote(strFilePath)
			If Not blnTestMode Then
				If fncFileExists(strFilePath) Then
					On Error Resume Next
						objFSO.DeleteFile(strFilePath)
						If Err.Number <> 0 Then
							dicFileDeletionFailures.Add strFilePath, Err.Description
						End If
						Err.Clear
					On Error Goto 0
				Else
					If Not dicFileDeletionFailures.Exists(strFilePath) Then
						dicFileDeletionFailures.Add strFilePath, "File Not Found"
					End If
				End If
			End If
		Else
			If Not dicFileDeletionFailures.Exists(strFilePath) Then
				dicFileDeletionFailures.Add strFilePath, "Invalid Data Passed To Function:  " & fncQuote(strFilePath)
			End If
		End If
	End Sub

	Sub subEvaluateFileDuplicate(strFilePath)
		If strFilePath <> "" Then
			blnDuplicate = False
			If InStr(strFilePath, " - Copy." & strTargetExtension) Then
				strCorrectFilePath = Replace(strFilePath, " - Copy." & strTargetExtension, "." & strTargetExtension)
				blnDuplicate = True
			Else
				For intDupLevel = 1 to 99
					arrAdditiveStrings = Array( _
						".", _
						"_", _
						"(/)", _
						"[/]")
					For Each strAdditiveString In arrAdditiveStrings
						Select Case strAdditiveString
							Case ".", "_"
								strEvaluationValue = strAdditiveString & intDupLevel & strAdditiveString & strTargetExtension
							Case Else
								arrAdditiveStringDetails = Split(strAdditiveString, "/")
								strPreValue = arrAdditiveStringDetails(0)
								strPostValue = arrAdditiveStringDetails(1)
								strEvaluationValue = strPreValue & intDupLevel & strPostValue & "." & strTargetExtension
						End Select
						If InStr(strFilePath, strEvaluationValue) Then
							strCorrectFilePath = Replace(strFilePath, strEvaluationValue, "." & strTargetExtension)
							blnDuplicate = True
							Exit For
						End If
					Next
					If blnDuplicate Then
						Exit For
					End If
				Next
			End If
			If blnDuplicate Then
				If Not dicDuplicateFiles.Exists(strFilePath) Then
					dicDuplicateFiles.Add strFilePath, strCorrectFilePath
				End If
			End If
		End If
	End Sub

	Sub subFileMove(strSourceFilePath, strDestFilePath)
		If strSourceFilePath <> "" And strDestFilePath <> "" And InStr(strDestFilePath, "\") Then
			arrDestFileDetails = Split(strDestFilePath, "\")
			strDestFileName = arrDestFileDetails(UBound(arrDestFileDetails))
			strDestDirectory = Replace(strDestFilePath, strDestFileName, "")
			subDirectoryCreate(strDestDirectory)
			If fncFileExists(strSourceFilePath) And Not fncFileExists(strDestFilePath) Then
				subLog "File move:  " & fncQuote(strSourceFilePath) & " --> " & fncQuote(strDestFilePath)
				If Not blnTestMode Then
					objFSO.MoveFile strSourceFilePath, strDestFilePath
				End If
			End If
		End If
	End Sub

	Sub subLog(strText)
		If strText <> "" Then
			On Error Resume Next
				strText = Replace(strText, vbCrLf, " <BR> ")
				objLogFile.WriteLine Now() & " - " & strText
				If Err.Number <> 0 Then
					objLogFile.WriteLine Now() & "- Error:  Log write attempt resulted in " & fncQuote(Err.Description)
				End If
				Err.Clear
			On Error Goto 0
		End If
	End Sub