В прошлой статье я рассмотрел задачу и привёл сценарий PowerShell для определения начальной и конечной тишины в аудиофайле. Сценарий работает для одного файла, но как быть, если требуется автоматизировать анализ нескольких файлов? Например, всех файлов, хранящихся в директории.
Самый простой и правильный вариант решения задачи - не изменять исходный сценарий, а использовать возможности PowerShell для перебора файлов. Сценарий можно запускать для каждого найденного аудиофайла по очереди, передавая его полное имя в параметр -InputFile. Таким образом, для пакетного анализа используется конвейерная обработка данных, встроенная в PowerShell.
Предположим, в директории D:\tmp-audio\ находятся следующие аудиофайлы:
audio-01.mp3
audio-02.mp3
audio-03.mp3
audio-04.mp3
audio-05.mp3
audio-06.m4a
audio-07.ogg
audio-08.wma
audio-09.wma
audio-10.m4a
Если код сценария сохранён в файле с именем Get-AudioContentBounds.ps1, выполнить его для всех файлов в директории можно следующим образом:
Get-ChildItem -Path 'D:\tmp-audio\' -File | ForEach-Object {
.\Get-AudioContentBounds.ps1 -InputFile $_.FullName
}
Если в директории окажутся файлы, которые не могут быть проанализированы утилитами FFmpeg, сценарий завершится ошибкой. Для анализа файлов одного типа можно использоваться параметр -Filter командлета Get-ChildItem. Но этот пример слишком простой, поэтому предлагаю усложнить задачу и выполнить сценарий только для mp3 и m4a файлов:
$files = Get-ChildItem -Path 'D:\tmp-audio\' -File | Where-Object {
$_.Extension -in '.mp3', '.m4a'
}
$files | ForEach-Object {
.\Get-AudioContentBounds.ps1 -InputFile $_.FullName
}
В результате выполнения этого кода в консоль будут выведены данные анализа всех найденных файлов. Однако такой вывод не слишком информативен: в нём нет сопоставления результата анализа с именем файла.
Это легко исправить. Вместо непосредственного вывода результата работы сценария можно создать новый объект, добавив к данным полное имя исходного файла:
$files | ForEach-Object {
$result = .\Get-AudioContentBounds.ps1 -InputFile $_.FullName
[PSCustomObject]@{
'File' = $_.FullName
'Duration' = $result.Duration
'Start Time' = $result.'Start Time'
'Start Silence Duration' = $result.'Start Silence Duration'
'End Time' = $result.'End Time'
'End Silence Duration' = $result.'End Silence Duration'
}
}
Теперь всегда понятно, к какому файлу относится конкретный результат.
Чтобы каждый раз не выполнять анализ файлов, а если файлов много, то это затратно по времени, можно сохранить результат работы сценария для последующей работы с ним. Например, в файл в формате CSV.
Для этого достаточно передать результат выполнения конвейера командлету Export-Csv:
$files | ForEach-Object {
$result = .\Get-AudioContentBounds.ps1 -InputFile $_.FullName
[PSCustomObject]@{
'File' = $_.FullName
'Duration' = $result.Duration
'Start Time' = $result.'Start Time'
'Start Silence Duration' = $result.'Start Silence Duration'
'End Time' = $result.'End Time'
'End Silence Duration' = $result.'End Silence Duration'
}
} | Export-Csv -Path 'D:\tmp-audio\AudioContentBounds.csv' -NoTypeInformation -Encoding UTF8
В этих примерах хорошо раскрываются удобство и возможности PowerShell для конвейерной обработки данных. Поэтому для пакетного анализа необязательно усложнять логику исходного сценария - достаточно использовать его как отдельный этап конвейера, а обработку множества файлов и сохранение результатов поручить самому PowerShell.