03 июля 2016

Массовая конвертация wav -> ogg

Возникла задача сконвертировать очень много файлов из одного формата в другой. Данный скрипт делает это с распараллеливанием, что ускоряет процесс. Плюс, есть прогресс выполнения и логгирования. Можно с лёгкостью адаптировать под что угодно, где требуется параллельное выполнение типовых задач по списку файлов =)

#!/bin/bash

# Многопоточная перекодировка файлов asterisk
# Для работы в Ubuntu требует наличия vorbis-tools

# количество потоков обработки
THREADS=8
# рабочая папка где будут искаться исходные файлы
SCAN_DIR='/var/spool/asterisk/monitor'

WORK_DIR="$(dirname $(readlink -f $0))"
SCAN_FILE="$WORK_DIR/files_to_convert"
LOG_FILE="$WORK_DIR/convert.log"
LOGTMP_FILE="$WORK_DIR/convert.tmp"
RATIO_FILE="$WORK_DIR/ratio.tmp"
renice="
ionice -c3 nice -n20"

# если нет файла со списком файлов для конвертации - создадим
if [ ! -f "$SCAN_FILE" ]; then
   printf '[%s] Collecting files to convert...' "`date`" >> "$LOG_FILE"
   # ищем файлы, с расширением .wav и последним изменением более суток назад
   eval "$renice find \"$SCAN_DIR\" -type f -mtime +0 -daystart -iname '*.wav' > \"$SCAN_FILE\""
else
   printf '[%s] Previous scan result used...' "`date`" >> "$LOG_FILE"
fi

# общее количество файлов для обработки = числу строк в файле скана
files_total=$(cat "$SCAN_FILE" | wc -l)
printf ' %s found total\n' "$files_total" >> "$LOG_FILE"

# основная функция конвертации, вызывается дочерними процессами
process_file(){
   local num="${1% $'\t'*}"
   local record="${1##*$'\t' }"
   # если исходный файл ненулевой
   if [ -s "$record" ]; then
       local newfile="${record%.*}.ogg"
       
eval "$renice /usr/bin/oggenc -Q -q1 \"$record\" -o \"$newfile\""
       # если конвертация прошла без ошибок, то выставим права, время создания исходного файла и удалим старый
       if [ $? -eq 0 ]; then
           # запишем статистику по исходному и получившемуся файлам
           printf '%s\t%s\n' "$(stat --printf="%s" "$record")" "$(stat --printf="%s" "$
newfile")" >> "$RATIO_FILE"
           chown --reference="$record" "$newfile"
           chmod --reference="$record" "$newfile"
           touch -r "$record" "$newfile"
           rm -f "$record"
           printf '[%s] (%s of %s) %s converted\n' "`date`" "$num" "$files_total" "$rec
ord">> "$LOGTMP_FILE"
       fi
   # исходный файл нулевой - удалим его
   else
       rm -f "$record"
       printf '[%s] (%s of %s) %s is null file, removed\n' "`date`" "$num" "$files_tota
l" "$record">> "$LOGTMP_FILE"
   fi
}

# даём доступ новым процессам к некоторым переменным из этого скрипта
export -f process_file
export files_total
export LOGTMP_FILE
export RATIO_FILE

# если есть что конвертировать, запускаем ацкий конверт
if [ -s "$SCAN_FILE" ]; then
   awk '{print NR, "\t", $0}' "$SCAN_FILE" | xargs -d'\n' -L1 -n1 -P $THREADS /bin/bash
-c 'process_file "$@"' _
   # по итогу, посчитаем суммарный исходный и сконвертированный размер файлов
awk '
   function hum(input,v,s) {
       split("KB MB GB TB", v, " ")
       if(input + 0.0 == input){
           while( input > 1024 ){
               input /= 1024 ; s++
           }
           return sprintf("%0.2f %s", input, v[s])
       } else {return input}
   }
   BEGIN {FS="\t";OFS=""}
       {orig+=$1;conv+=$2}
   END {
       print ("[",
           strftime(),
           "] Files converted, source/result summary: ",
           hum(orig),
           " / ",
           hum(conv));
   }' "$RATIO_FILE" >> "$LOG_FILE"
fi

rm -f "$SCAN_FILE"
rm -f "$LOGTMP_FILE"
rm -f "$RATIO_FILE"


Версия 2 с ренайсами =)

Комментариев нет:

Отправить комментарий