AppleScript to return condition depending on audio file length
I'd like to use Hazel (a rule-based file system automator) for macOS to automatically move mixtape audio files from one folder to another, if their duration is longer than 30 minutes.
Hazel doesn't provide a built-in method of checking for audio file length (that I know of), but it provides the ability to run rules if an AppleScript condition is passed, i.e. return true
.
Judging from the screenshot below, I think it works if length of theFile > 30*60 seconds: return true
(pseudo code).
I'm not however sure how to accomplish this via AppleScript. Any ideas how to do it, or where to start? A search online didn't provide many basic ideas, but it seems to be possible.
Note: Giving advice on AppleScripts is outside the scope of the Hazel support AFAIK, so I can't get any help from them.
Okay, I've downloaded Hazel and tested the following AppleScript code on a New Rule to validate it works. The following code when used in an embedded script will set theResult
to true
on theFile
, an audio file that is longer than 30 minutes. Any file 30 minutes or less, or a non-audio file will set theResult
to false
:
set theResult to (do shell script "[[ $(afinfo -r '" & POSIX path of theFile & "' | awk '/estimated duration:/{print int($3/60)}') -gt 30 ]] && echo 'true' || echo 'false'") as boolean
return theResult
- Note that from my first comment before posting this answer, I've changed
theFileName
toPOSIX path of theFile
, and as you can see in the image below, it validated the rule. It then also processed the rule, as I defined it, successfully. - To change the number of minutes, change the value on the right side of the operator, e.g.
-gt 30
to the desired time, e.g.-gt 20
. The operator can also be changed to any one of the following:-eq
,-ne
,-lt
,-le
,-gt
or-ge
Understanding, a little bit, the content of the do shell script
command:
-
afinfo
doesn't have much in the way of an manual page, so just typeafinfo
in Terminal and press enter for its options. -
afinfo -r
-{-r --real} get the estimated duration after obtaining the real packet count
-
Example output of
afinfo -r
:$ afinfo -r '09 Karn Evil 9.mp3' File: 09 Karn Evil 9.mp3 File type ID: MPG3 Num Tracks: 1 ---- Data format: 2 ch, 44100 Hz, '.mp3' (0x00000000) 0 bits/channel, 0 bytes/packet, 1152 frames/packet, 0 bytes/frame no channel layout. audio bytes: 85117515 audio packets: 81460 estimated duration: 2127.935 sec bit rate: 320000 bits per second packet size upper bound: 1052 maximum packet size: 1045 audio data file offset: 275530 optimized ---- $
- The above output gets piped (
|
) toawk
.
- The above output gets piped (
-
awk '/estimated duration:/{print int($3/60)}'
-
awk
finds the record (line) containingestimated duration:
in the output ofafinfo
piped to it and prints the third field,$3
,2127.935
as an integer, having been divided by60
to get the number of minutes, as the time value is in seconds. In the example output,awk
returned:35
-
-
$(...)
The command substitution portion passed35
to the test[[ $(...) -gt 30 ]]
, so it equated to[[ 35 -gt 30 ]]
and as a result, using logical operators, echoedtrue
. Had the value been equal to or less than 30, or a non-audio file, it would have echoedfalse
.
If you want the content of do shell script
command to run faster, then use the following:
set theResult to (do shell script "[[ $(afinfo -b '" & POSIX path of theFile & "' | awk 'FNR == 3 {print int($1/60)}') -gt 30 ]] && echo 'true' || echo 'false'") as boolean
return theResult
-
afinfo -b
-{-b --brief} print a brief (one line) description of the audio file
- This runs faster then using
-r
as it's not counting the packets, but getting the info from embedded data. Note that while the help says "(one line)", the output is actually three lines, but still faster then the long form, especially if not using-r
.
- This runs faster then using
-
Example output of
afinfo -b
:$ afinfo -b '09 Karn Evil 9.mp3' 09 Karn Evil 9.mp3, MPG3, Num Tracks: 1 ---- 2127.938 sec, format: 2 ch, 44100 Hz, '.mp3' (0x00000000) 0 bits/channel, 0 bytes/packet, 1152 frames/packet, 0 bytes/frame $
- The above output gets piped (
|
) toawk
.
- The above output gets piped (
-
awk 'FNR == 3 {print int($1/60)}'
-
awk
prints the first field ($1
) of the third record (line) of output piped fromafinfo -b
,2127.935
as an integer, having been divided by60
to get the number of minutes, as the time value is in seconds. In the example output,awk
returned:35
-