This worked for me in a quick test. But do try it out on some smaller folders first to see that it works as expected on your files, since mp3 tags and exif data can be complicated and I don't know if the function I use handles all corner cases correctly.
#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
#SingleInstance force
ListLines, Off
SetBatchLines, -1
; sort mp3 files into folders based on bitrate and if they are VBR or not
; AutoHotkey script
; by nod5
; 2020-05-14
; ---------------------------------------
; folder with mp3 files that we want to sort move
sourcefolder := "C:\some\folder"
; also recurse in sourcefolder? 1 means yes, 0 means no
recurse := 1
; target folder where we want the files to end up in subfolders
; for example VBR files go to \VBR subfolder, 128kbs CBR files got to \128 subfolder
destinationfolder := "D:\this other\location"
; ---------------------------------------
; disable recurse if in/out folders are same, to avoid endless loop
if (sourcefolder = destinationfolder)
recurse := 0
; recursively loop sourcefolder for mp3 files and move to a destinationfolder subfolder
Loop, Files, % sourcefolder "\*.mp3", % recurse ? "R" : ""
{
ToolTip, % A_LoopFileName
; get audio properties as object
; we need these two properties:
; System.Audio.EncodingBitrate examples: 128000 (which is 128kbs), 320000, 263704, ...
; System.Audio.IsVariableBitRate value -1 if VBR otherwise value 0
Props := ["System.Audio.IsVariableBitRate", "System.Audio.EncodingBitrate"]
obj := Filexpro(A_LoopFilePath,, Props*) ; v.90 By SKAN on D1CC @ goo.gl/jyXFo9
; if VBR then move to subfolder "\VBR"
if (obj["System.Audio.IsVariableBitRate"] = "-1")
subfolder := "VBR"
else
{
; else move CBR to matching bitrate folder, for example "\128"
; get bitrate in kbs, for example 128000 -> 128
subfolder := SubStr(obj["System.Audio.EncodingBitrate"],1,-3)
}
if subfolder
{
if !FileExist(destinationfolder "\" subfolder)
FileCreateDir, % destinationfolder "\" subfolder
FileMove, % A_LoopFilePath, % destinationfolder "\" subfolder
}
}
ToolTip DONE
sleep 2000
ExitApp
; function: Filexpro
; get extended properties from file
; by SKAN 2018-12-11
; https://www.autohotkey.com/boards/viewtopic.php?t=59882
Filexpro( sFile := "", Kind := "", P* ) { ; v.90 By SKAN on D1CC @ goo.gl/jyXFo9
Local
Static xDetails
If ( sFile = "" )
{ ; Deinit static variable
xDetails := ""
Return
}
fex := {}, _FileExt := ""
Loop, Files, % RTrim(sfile,"\*/."), DF
{
If not FileExist( sFile:=A_LoopFileLongPath )
{
Return
}
SplitPath, sFile, _FileExt, _Dir, _Ext, _File, _Drv
If ( p[p.length()] = "xInfo" ) ; Last parameter is xInfo
{
p.Pop() ; Delete parameter
fex.SetCapacity(11) ; Make room for Extra info
fex["_Attrib"] := A_LoopFileAttrib
fex["_Dir"] := _Dir
fex["_Drv"] := _Drv
fex["_Ext"] := _Ext
fex["_File"] := _File
fex["_File.Ext"] := _FileExt
fex["_FilePath"] := sFile
fex["_FileSize"] := A_LoopFileSize
fex["_FileTimeA"] := A_LoopFileTimeAccessed
fex["_FileTimeC"] := A_LoopFileTimeCreated
fex["_FileTimeM"] := A_LoopFileTimeModified
}
Break
}
If Not ( _FileExt ) ; Filepath not resolved
{
Return
}
objShl := ComObjCreate("Shell.Application")
objDir := objShl.NameSpace(_Dir)
objItm := objDir.ParseName(_FileExt)
If ( VarSetCapacity(xDetails) = 0 ) ; Init static variable
{
i:=-1, xDetails:={}, xDetails.SetCapacity(309)
While ( i++ < 309 )
{
xDetails[ objDir.GetDetailsOf(0,i) ] := i
}
xDetails.Delete("")
}
If ( Kind and Kind <> objDir.GetDetailsOf(objItm,11) ) ; File isn't desired kind
{
Return
}
i:=0, nParams:=p.Count(), fex.SetCapacity(nParams + 11)
While ( i++ < nParams )
{
Prop := p[i]
If ( (Dot:=InStr(Prop,".")) and (Prop:=(Dot=1 ? "System":"") . Prop) )
{
fex[Prop] := objItm.ExtendedProperty(Prop)
Continue
}
If ( PropNum := xDetails[Prop] ) > -1
{
fex[Prop] := ObjDir.GetDetailsOf(objItm,PropNum)
Continue
}
}
fex.SetCapacity(-1)
Return fex
}
Alternatively if you want a GUI approach you could drag all the mp3 files into Mp3Tag, sort by columns bitrate and VBR, select a range of files e.g. all with 128 kbs and use right click context menu action "move...".