Automatic MP3 organization using AutoIt

Somebody on the Something Awful forums asked me about my code that automatically organizes MP3’s I download, so here’s how it works…

I have a catch-all script that runs on my system all the time into which I insert various functions I write to automate tasks on my computer. In the main loop of this scrip I have a sub-loop which runs every 60 seconds like this:

$DelayBegin = TimerInit()

While 1
	; Items in this loop only run every 60 seconds
	If TimerDiff($DelayBegin) > 60000 Then
		MoveMP3()
		$DelayBegin = TimerInit()
	EndIf
	Sleep(100)
WEnd

This is pretty self-explanatory I think. Basically every time the difference between now and when $DelayBegin is initiated is greater than 60,000 milliseconds, the MoveMP3() function gets executed and then $DelayBegin gets reinitialized.

Before we get in to the MoveMP3() function, let me talk about a couple of the other functions which help MoveMP3() do it’s job. The first is GetExtProperty().

Func _GetExtProperty($sPath, $iProp)
	Local $iExist, $sFile, $sDir, $oShellApp, $oDir, $oFile, $aProperty, $sProperty
	$iExist = FileExists($sPath)
	If $iExist = 0 Then
		SetError(1)
		Return 0
	Else
		$sFile = StringTrimLeft($sPath, StringInStr($sPath, "\", 0, -1))
		$sDir = StringTrimRight($sPath, (StringLen($sPath) - StringInStr($sPath, "\", 0, -1)))
		$oShellApp = ObjCreate("shell.application")
		$oDir = $oShellApp.NameSpace($sDir)
		$oFile = $oDir.Parsename($sFile)
		If $iProp = -1 Then
			Local $aProperty[35]
			For $i = 0 To 34
				$aProperty[$i] = $oDir.GetDetailsOf($oFile, $i)
			Next
			Return $aProperty
		Else
			$sProperty = $oDir.GetDetailsOf($oFile, $iProp)
			If $sProperty = "" Then
				Return 0
			Else
				Return $sProperty
			EndIf
		EndIf
	EndIf
EndFunc

Now, I didn’t write GetExtProperty(), but for the life of me I can’t remember where I found it. If anyone knows where it came from, I’d be glad to point others to that place. I do know the notes credit the author Simucal (simucal[at]gmail.com)…

Anway, this function takes a file path and then the index to the extended property you want to get and returns the value of that field. This index seems to vary between the various versions of Windows, so you’ll have to do some testing on your target installations to see which index returns the values you want. To aid in your testing, if you give it a -1 for the index it will return a 1-dimensional array containing all of the properties.

The next function I use is _FileListToArrayEx().

Func _FileListToArrayEx($sPath, $sFilter = '*.*', $iFlag = 0, $sExclude = '', $iRecurse = False)
	If Not FileExists($sPath) Then Return SetError(1, 1, '')
	If $sFilter = -1 Or $sFilter = Default Then $sFilter = '*.*'
	If $iFlag = -1 Or $iFlag = Default Then $iFlag = 0
	If $sExclude = -1 Or $sExclude = Default Then $sExclude = ''
	Local $aBadChar[6] = ['\', '/', ':', '>', '< ', '|']
	$sFilter = StringRegExpReplace($sFilter, '\s*;\s*', ';')
	If StringRight($sPath, 1) <> '\' Then $sPath &= '\'
	For $iCC = 0 To 5
		If StringInStr($sFilter, $aBadChar[$iCC]) Or _
				StringInStr($sExclude, $aBadChar[$iCC]) Then Return SetError(2, 2, '')
	Next
	If StringStripWS($sFilter, 8) = '' Then Return SetError(2, 2, '')
	If Not ($iFlag = 0 Or $iFlag = 1 Or $iFlag = 2) Then Return SetError(3, 3, '')
	Local $oFSO = ObjCreate("Scripting.FileSystemObject"), $sTFolder
	$sTFolder = $oFSO.GetSpecialFolder(2)
	Local $hOutFile = @TempDir & $oFSO.GetTempName
	If Not StringInStr($sFilter, ';') Then $sFilter &= ';'
	Local $aSplit = StringSplit(StringStripWS($sFilter, 8), ';'), $sRead, $sHoldSplit
	For $iCC = 1 To $aSplit[0]
		If StringStripWS($aSplit[$iCC], 8) = '' Then ContinueLoop
		If StringLeft($aSplit[$iCC], 1) = '.' And _
				UBound(StringSplit($aSplit[$iCC], '.')) - 2 = 1 Then $aSplit[$iCC] = '*' & $aSplit[$iCC]
		$sHoldSplit &= '"' & $sPath & $aSplit[$iCC] & '" '
	Next
	$sHoldSplit = StringTrimRight($sHoldSplit, 1)
	If $iRecurse Then
		RunWait(@ComSpec & ' /c dir /b /s /a ' & $sHoldSplit & ' > "' & $hOutFile & '"', '', @SW_HIDE)
	Else
		RunWait(@ComSpec & ' /c dir /b /a ' & $sHoldSplit & ' /o-e /od > "' & $hOutFile & '"', '', @SW_HIDE)
	EndIf
	$sRead &= FileRead($hOutFile)
	If Not FileExists($hOutFile) Then Return SetError(4, 4, '')
	FileDelete($hOutFile)
	If StringStripWS($sRead, 8) = '' Then SetError(4, 4, '')
	Local $aFSplit = StringSplit(StringTrimRight(StringStripCR($sRead), 1), @LF)
	Local $sHold
	For $iCC = 1 To $aFSplit[0]
		If $sExclude And StringLeft($aFSplit[$iCC], _
				StringLen(StringReplace($sExclude, '*', ''))) = StringReplace($sExclude, '*', '') Then ContinueLoop
		Switch $iFlag
			Case 0
				If StringRegExp($aFSplit[$iCC], '\w:\\') = 0 Then
					$sHold &= $sPath & $aFSplit[$iCC] & Chr(1)
				Else
					$sHold &= $aFSplit[$iCC] & Chr(1)
				EndIf
			Case 1
				If StringInStr(FileGetAttrib($sPath & '\' & $aFSplit[$iCC]), 'd') = 0 And _
						StringInStr(FileGetAttrib($aFSplit[$iCC]), 'd') = 0 Then
					If StringRegExp($aFSplit[$iCC], '\w:\\') = 0 Then
						$sHold &= $sPath & $aFSplit[$iCC] & Chr(1)
					Else
						$sHold &= $aFSplit[$iCC] & Chr(1)
					EndIf
				EndIf
			Case 2
				If StringInStr(FileGetAttrib($sPath & '\' & $aFSplit[$iCC]), 'd') Or _
						StringInStr(FileGetAttrib($aFSplit[$iCC]), 'd') Then
					If StringRegExp($aFSplit[$iCC], '\w:\\') = 0 Then
						$sHold &= $sPath & $aFSplit[$iCC] & Chr(1)
					Else
						$sHold &= $aFSplit[$iCC] & Chr(1)
					EndIf
				EndIf
		EndSwitch
	Next
	If StringTrimRight($sHold, 1) Then Return StringSplit(StringTrimRight($sHold, 1), Chr(1))
	Return SetError(4, 4, '')
EndFunc   ;==>_FileListToArrayEx

This function by SmOke_N of the AutoIt forums returns a list of files in a folder and can be used with wildcards as you can see in the MoveMP3() function.

Func MoveMP3()
	; Indexes for file properties returned by GetExtProperty
	$idxArtist = 13
	$idxAlbum = 14
	$idxTrack = 26
	$idxName = 21
	; Source directory
	$source_dir = "C:\Users\Therms\Documents\allTunes\My Music"
	; Destination root (include trailing slash)
	$mp3_dest = "Y:\"

	; If destination not available then we quit
	If Not FileExists($mp3_dest) Then Return

	; Get list of new mp3 files
	$mp3_source = _FileListToArrayEx($source_dir, "*.mp3", 0, "", True)

	; Check if any mp3's are there
	If $mp3_source = "" Then
		Return
	EndIf
	If $mp3_source[0] = 1 Then
		If StringInStr(FileGetAttrib($mp3_source[1]), "D") Then
			$ret = DirRemove($mp3_source[1], 1)
			Return
		EndIf
	EndIf

	; Get track info and move files appropriately
	Dim $fileinfos[4][$mp3_source[0]]
	For $i = 1 To $mp3_source[0]
		$artist = StringStripWS(_GetExtProperty($mp3_source[$i], $idxArtist), 3)
		$album = StringStripWS(_GetExtProperty($mp3_source[$i], $idxAlbum), 3)
		$track = StringStripWS(_GetExtProperty($mp3_source[$i], $idxTrack), 3)
		If Int($track) < 10 Then $track = "0" & $track
		$trackname = StringStripWS(_GetExtProperty($mp3_source[$i], $idxName), 3)
		$dest_path = StringFormat('%s%s\\%s\\%s - %s.mp3', $mp3_dest, $artist, $album, $track, $trackname)
		$result = FileMove($mp3_source[$i], $dest_path, 8)
		FileModTimeNow($dest_path)
	Next
EndFunc   ;==>MoveMP3

Hopefully this helps you out if you’d like to do something similar.

  1. I’m interested in the script, but it’s almost impossible to read it because of the broken html tags. Could you re-post the code without tags, please? Thanks

  2. Hmm, I don’t know what happened with those tags…

    Give me a few minutes and I’ll fix it up.

  3. Ok, I somewhat fixed it. See my note at the beginning of the post now.

  4. Bravo, que frase…, el pensamiento excelente