Pull to refresh

Хранение записей разговоров в mp3 во FreePBX/Asterisk

Reading time7 min
Views57K
Ныне FreePBX — крайне популярная обертка к Asterisk, который не менее популярен как телефонный цифровой сервер. Отдельный плюс таких систем — возможность развернуть на недорогих vds-серверах (я клиентам разворачиваю на vds стоимостью 299р/месяц, 2Гб ОЗУ, 2,8 GHZ процессор, 20Гб место на диске). Такая система запросто обслуживает 10-20 одновременных звонков, пишет аудио, позволяет внедрять телефонную часть бизнес-процесса в остальную логику (взаимодействие с crm, звонки с сайта/браузера, автоинформаторы на основе данных в субд, выяснение поисковых данных по номеру о звонящем за доли секунд, даже распознавание и синтезация речи!).

Все бы хорошо, но при «обильных» обзвонах через полмесяца заканчивается место на диске. Те самые 20Гб улетают у некоторых клиентов за неделю! А хостер, к сожалению, не предоставляет fuse на тарифах OpenVZ, который крайне необходим для работы «реалтайм — сетевых файловых систем», вроде ftpfs.

Ниже изложу мой комплекс мер по борьбе с проблемой. Работать будем с каталогом /var/spool/asterisk/monitor, где лежат .wav файлы, заботливо разложенные asterisk по каталогам: год, месяц и день.

Изначально я пошел не с того края — стал перебрасывать часть данных на webdav (ya.disk) и ftp. Делалось это скриптом, запускаемым раз в месяц по крону. Скрипт оставлял указанное количество месяцев на сервере, остальное переносил на удаленный ресурс.

Для yandex.disk он дополнительно паковал данные «помесячно», что требовало свободного места для создания архива. Но, тем не менее, кое-где он прижился:

#settings
#for disk.yandex.ru
YAUSER=login@yandex.ru
YAPASSWORD=verystrongpassword

MONPATH=/var/spool/asterisk/monitor
LASTMONTH=1 #сколько последних месяцев оставляем на сервере
DIR=astsounds #название удаленной директории, где будем хранить архивы

#start
MONTH=$(date +"%m")
for YEAR in $( ls $MONPATH ); do
    
    for MONTH in $( ls $MONPATH/$YEAR/ ); do
	if [ $MONTH -le $(($(date +"%m")-$LASTMONTH)) ] || [ $YEAR -ne $(date +"%Y") ]; then
	    tar -zcvf $MONPATH/$YEAR/$MONTH/$YEAR-$MONTH.tar.gz $MONPATH/$YEAR/$MONTH/ >/dev/null
	    curl -T $MONPATH/$YEAR/$MONTH/$YEAR-$MONTH.tar.gz --user $YAUSER:$YAPASSWORD https://webdav.yandex.ru/$DIR/$YEAR-$MONTH.tar.gz && rm -f -r $MONPATH/$YEAR/$MONTH >/dev/null
	fi
    done
    #remove old YEAR dir
    if [ $YEAR -ne $(date +"%Y") ]; then
	rm -f -r $MONPATH/$YEAR
    fi
    
done

Когда писал подобный скрипт для ftp, решил отказаться от архивирования аудио. И доставать проще, и ссылки формировать на сторонний ресурс можно, для онлайн-прослушивания из CDR-отчета. Требует lftp-утилиту.

#move audio records to XXX.ru free FTP server
#settings
FTPSRV=node0.XXXX.ru
FTPUSER=username_ftpserver
FTPPWD=passwd_ftpserver
MONPATH=/var/spool/asterisk/monitor
LASTMONTH=1
DIR=recordings

#log and cd
ftpcommand() {
lftp -u $FTPUSER,$FTPPWD -e "$1" $FTPSRV
}

ftpcommand "mkdir public_http; mkdir public_http/$DIR; quit;"

MONTH=$(date +"%m")
for YEAR in $( ls $MONPATH ); do
    for MONTH in $( ls $MONPATH/$YEAR/ ); do
    if [ $MONTH -le $(($(date +"%m")-$LASTMONTH)) ] || [ $YEAR -ne $(date +"%Y") ]; then
	    echo "saving $YEAR/$MONTH"
	    ftpcommand "mkdir public_http/$DIR/$YEAR; mkdir public_http/$DIR/$YEAR/$MONTH; quit;"
	    for DAY in $( ls $MONPATH/$YEAR/$MONTH/ ); do
          ftpcommand "mkdir public_http/$DIR/$YEAR/$MONTH/$DAY; quit;"
          for FILE in $( ls $MONPATH/$YEAR/$MONTH/$DAY ); do
            ftpcommand "cd  /public_http/$DIR/$YEAR/$MONTH/$DAY/; put $MONPATH/$YEAR/$MONTH/$DAY/$FILE; quit;"
          done
	    done
      rm -f -r $MONPATH/$YEAR/$MONTH
    fi
    done
    #remove old YEAR dir
    if [ $YEAR -ne $(date +"%Y") ]; then
    rm -f -r $MONPATH/$YEAR
    fi
done

Данный скрипт также забрасывался в месячный крон.

Но кое-кому, как я уже писал, этого не хватало. Данные забивали диск за неделю. Тогда пришла мысль хранить все в mp3. По факту — выигрыш составлял от 4-х раз. Потребуется lame.

Для начала положим в /etc/asterisk такой вот скрипт mixmon_mp3.sh и дадим ему одного с asterisk хозяина: chown asterisk. mixmon_mp3.sh. Также дадим право на выполнение: chmod +x mixmon_mp3.sh.

#!/bin/sh
#convert wav to mp3 asterisk recordings
cdrdb="asteriskcdrdb"
cdrtable="cdr"
astdbuser="asteriskuser" #пользователь субд для отчетов
astdbuserpass="asteriskuserpassword" #пароль пользователя субд

for i in `find /var/spool/asterisk/monitor -type f -name "$1"`
do
 if [ -e "$i" ]; then
    file=`basename "$i" .wav`;
    dir=`dirname "$i"`;
    lame -h -b 192 "$i" "$dir/$file.mp3";
    rm -f "$dir/$file.wav";
    sleep 3
    mysql --user="$astdbuser" --password="$astdbuserpass" --database="$cdrdb" --execute='UPDATE '$cdrtable' SET recordingfile="'$file'.mp3" WHERE recordingfile="'$file'.wav";';
 fi
done


Далее в вебморде FreePBX, в разделе Settings — Advanced Settings включаем Display Readonly Settings и Override Readonly Settings, после чего становится доступна настройка Post Call Recording Script в разделе Developer and Customization. Туда и прописываем строку вызова скрипта с параметром:

/etc/asterisk/mixmon_mp3.sh ^{CALLFILENAME}.^{MIXMON_FORMAT}

К сожалению, переменная MIXMON_DIR оказалась пустой, поэтому приходится довольствоваться именем файла и отыскивать его в каталоге monitor. Скрипт находит файл, конвертирует его в mp3, удаляет wav и правит запись в таблице cdr, где хранятся отчеты. Это нужно для нормального прослушивания и скачивания файлов в вебморде с отчетами.

Также нам понадобится допилить сам модуль вебморды cdr:
/var/www/html/admin/modules/cdr/
cdr_audio.php
<?php
if (!defined('FREEPBX_IS_AUTH')) { die('No direct script access allowed'); }
/**
 * @file
 * plays recording file
 */
if (isset($_REQUEST['cdr_file'])) {
	include_once("crypt.php");

	$REC_CRYPT_PASSWORD = (isset($amp_conf['AMPPLAYKEY']) && trim($amp_conf['AMPPLAYKEY']) != "")?trim($amp_conf['AMPPLAYKEY']):'TheWindCriesMary';
	$crypt = new Crypt();
	$opath = $_REQUEST['cdr_file'];
	$path = $crypt->decrypt($opath,$REC_CRYPT_PASSWORD);

	// Gather relevent info about file
	$size = filesize($path);
	$name = basename($path);
	$extension = strtolower(substr(strrchr($name,"."),1));
	// This will set the Content-Type to the appropriate setting for the file
	$ctype ='';
	switch( $extension ) {
		case "WAV":
			$ctype="audio/x-wav";
			break;
		case "wav":
			$ctype="audio/x-wav";
			break;
#---------------------
		case "mp3":
			$ctype="audio/mpeg";
			break;
#---------------------
		case "ulaw":
			$ctype="audio/basic";
			break;
		case "alaw":
			$ctype="audio/x-alaw-basic";
			break;
		case "sln":
			$ctype="audio/x-wav";
			break;
		case "gsm":
			$ctype="audio/x-gsm";
			break;
		case "g729":
			$ctype="audio/x-g729";
			break;
		default: //not downloadable
			// echo ("<b>404 File not found! foo</b>");
			// TODO: what to do if none of the above work?
		break ;
	}

  $fp=fopen($path, "rb");
  if ($size && $ctype && $fp) {
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: public");
    header("Content-Description: audio file");
    header("Content-Type: " . $ctype);
    header("Content-Disposition: attachment; filename=" . $name);
    header("Content-Transfer-Encoding: binary");
#---------------------
    header("Accept-Ranges: bytes");
    header("Connection: close");
    header("Content-Length: $size");
    header("Content-Range:bytes 0-$size/$size");
    header("Content-length: " . $size);
#---------------------    
    $chunksize = 1*(1024*1024);
    while (!feof($fp)) {
        $buffer = fread($fp, $chunksize);
        echo $buffer;
        ob_flush();
        flush();
    }
    fclose($fp);
  }
}

?>

cdr_play.php

<?php
if (!defined('FREEPBX_IS_AUTH')) { die('No direct script access allowed'); }

/**
 * @file
 * popup window for playing recording
 */
include_once("crypt.php");
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <TITLE>CDR Viewer</TITLE>
			<style type="text/css">
				.popup_download {
					color: #105D90; 
					margin: 5px; 
					font-size: 12px; 
					text-align: left;
				}
			</style>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
<?php

	$crypt = new Crypt();

	$REC_CRYPT_PASSWORD = (isset($amp_conf['AMPPLAYKEY']) && trim($amp_conf['AMPPLAYKEY']) != "")?trim($amp_conf['AMPPLAYKEY']):'TheWindCriesMary';
	$path = $crypt->decrypt($_REQUEST['recordingpath'],$REC_CRYPT_PASSWORD);
	$file = urlencode($crypt->encrypt($path,$REC_CRYPT_PASSWORD));
	if (isset($file)) {
#---------------------
		echo("<audio controls>\n<source src=\"config.php?skip_astman=1&quietmode=1&handler=file&module=cdr&file=cdr_audio.php&cdr_file=$file\" type=\"audio/mpeg\">\n</audio>");
#---------------------
	}
?>
  </body>
</html>

Теперь можно конвертнуть имеющиеся файлы в mp3 и положить на всякий случай этот скрипт в дневной крон:

#!/bin/sh
#convert wav to mp3 asterisk recordings
cdrdb="asteriskcdrdb"
cdrtable="cdr"
astdbuser="asteriskuser"
astdbuserpass="asteriskuserpassword"
for i in `find /var/spool/asterisk/monitor -type f -name "*.wav"`
do
 if [ -e "$i" ]; then
    file=`basename "$i" .wav`;
    dir=`dirname "$i"`;
    lame -h -b 192 "$i" "$dir/$file.mp3";
    rm -f "$dir/$file.wav";
    mysql --user="$astdbuser" --password="$astdbuserpass" --database="$cdrdb" --execute='UPDATE '$cdrtable' SET recordingfile="'$file'.mp3" WHERE recordingfile="'$file'.wav";';
 fi
done


Процесс небыстрый, лучше запускать на ночь. Теперь у нас намного экономнее расходуется место, прослушивание отчетов не требует QuickTime и файл нам проигрывает браузер через HTML5-тег .
Tags:
Hubs:
Total votes 10: ↑10 and ↓0+10
Comments16

Articles