31 October 2014

«Допиливаем» Asterisk CDR Viewer под себя

AsteriskDevelopment of communication systems
image

«Я профессионал, потому что не ленюсь искать информацию в google» — сказал мне однажды коллега.

А я поленился и начал «допиливать» CDR Viewer под себя, даже не посмотрев хотя бы вот это.
А может и не в лени дело, просто было интересно… в общем, что из этого вышло можно посмотреть под катом:)


Споры о том, что лучше использовать в качестве офисной АТС — asterisk (с веб-интерфейсом или без, хотя это отдельная тема для споров) или какую-то коробку типа Panasonic, которых на рынок выкинуто немеренное количество — не утихают до сих пор, но топик не об этом, лично для себя я уже давно определился. Хотелось бы поделиться с сообществом своим вариантом придания интерфейсу просмотра статистики дополнительного фунционала.

В качестве «подопытного» я использовал FreePBX Distro (FreePBX 2.11, Asterisk 11, CentOS 6.5), скачанный с официального сайта проекта. Выбор был продиктован тем, что разработчики FreePBX уже позаботились о прикручивании БД к Asterisk и структура хранения записей в общем-то меня устраивает. Хотя процедура «прикручивания» MySQL или какой-либо другой базы к Asterisk была описана ни раз и ни два, о чем можно почитать например здесь, все же в целях экономии времени я решил этого не делать.

За основу был взят Asterisk CDR Viewer (если не нужно каких-то сверхмудреных отчетов — то вполне себе пригодная и простенькая статистика), скачать можно тут.

Установка CDR Viewer не представляется какой-то нетривиальной задачей.

Переходим в нужную нам директорию, качаем архив, извлекаем файлы из архива:

cd /var/www
wget https://asterisk-cdr-viewer.googlecode.com/files/asterisk-cdr-viewer-1.0.2.tgz
tar -xzvf asterisk-cdr-viewer-1.0.2.tgz


Переносим файлик алиаса в папку с apache2:
cp /var/www/html/asterisk-cdr-viewer/contrib/httpd/asterisk-cdr-viewer.conf /etc/apache2/conf.d/asterisk-cdr-viewer.conf


Изменяем настройки подключения к БД для Asterisk-CDR-viewer

cd /var/www/asterisk-cdr-viewer/include/
vim config.inc.php


Нужно поменять параметры в соответствии с текущей конфигурацией вашей базы:

$db_user = '[MySQL пользователь]';
$db_pass = '[MySQL пароль]';
$db_name = '[Имя базы]';

Делаем рестарт веб-сервера:

service apache2 restart


Теперь в браузере набирая [адрес asteridk-сервера]/acdr/ попадаем на страницу статистики.

Первое, что мне захотелось сделать — прикрутить авторизацию для просмотра этой самой статистики, для этого воспользуемся htpasswd.
Если не установлена —
aptitude install apache2-utils


Переходим в /etc/apache2 и созадем юзер/пароль для статистики:
htpasswd -c passwordfile username


Вводим пароль в диалоге, который предлагает htpasswd и получаем файл «passwordfile» с юзером «username» и сгенерированным зашифрованным паролем.

Далее в /etc/apache2/conf.d изменяем asterisk-cdr-viewer.conf, раскомментрировав строки авторизации, в результате получаем:

Alias /acdr/ "/var/www/asterisk-cdr-viewer/"

<Location "/acdr/">
<------>AuthName «Asterisk-CDR-Stat»
<------>AuthType Basic
<------>AuthUserFile /etc/apache2/passwordfile
<------>AuthGroupFile /dev/null
<------>require valid-user


Рестартуем apache2 и при входе на страницу видим окно авторизации:

image

Следующее, что был сделано — это прослушивание разговоров из веб-интерфейса.

1) Для прослушивания звонков добавляем две иконки в каталог /var/www/asterisk-cdr-viewer/templates/images (play и stop)

2) Добавляем в /var/www/asterisk-cdr-viewer/templates/header.tpl.php объявление js-функции для оптимизации производительности;

В результате получаем:

<head>
	<title>Asterisk Call Detail Records</title>
	<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
	<link rel="stylesheet" href="style/screen.css" type="text/css" media="screen" />
	<link rel="shortcut icon" href="templates/images/favicon.ico" />
	<script type="text/javascript"/>
function audioPreview(e,cnc) {
 var uri = e.attributes.getNamedItem('data-uri').value;
 var audioElement;
 // 1 if not exists audio control, then create it:
 if (!(audioElement = document.getElementById('au_preview'))) {
  audioElement = document.createElement('audio');
  audioElement.id = 'au_preview';
  audioElement.controls = true;
  audioElement.style.display = 'none';
  document.body.appendChild(audioElement);
 }
 else {
  // 2 need to stop and hide if playing:
  var prevIcon = audioElement.parentNode.previousSibling;
  prevIcon.src = 'templates/images/play.png';
  prevIcon.onclick = function(){ return audioPreview(prevIcon,false);};
 }

 if (('undefined'===typeof cnc)||(!cnc)) {
  //1. to show
  e.nextSibling.appendChild(audioElement);
  audioElement.src = uri;
  audioElement.style.display = 'block';
  audioElement.play();
  e.onclick = function(){ return audioPreview(e,true); };
  e.src = 'templates/images/stop.png';
 }
 else {
  //2. to hide
  audioElement.pause();
  audioElement.style.display = 'none';
  e.onclick = function(){ return audioPreview(e,false); };
  e.src = 'templates/images/play.png';
 }
}</script>
</head>
<body>
	<table id="header">
		<tr>
			<td id="header_logo" rowspan="2" align="left"><a href="/" title="Home"><img src="" alt="Asterisk CDR Viewer" /></a></td>
			<td id="header_title">Asterisk CDR Viewer</td>
			<td align='right'>
				<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=XVUVZY5D922JJ&lc=RU&item_name=i%2eo%2e&item_number=asterisk%2dcdr&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="http://habrastorage.org/getpro/habr/post_images/92a/7cd/b91/92a7cdb914a8e942ca23d0ec49367cc3.gif" align="center"/></a>
			</td>
		</tr>
		<tr>
		<td id="header_subtitle"> </td>
			<td align='right'>
			<?php
			if ( strlen(getenv('REMOTE_USER')) ) {
				echo "<a href='/acdr/index.php?action=logout'>logout: ". getenv('REMOTE_USER') ."</a>";
			}
			?>
		</td>
		</tr>
		</table>



3)
Изменяем /var/www/asterisk-cdr-viewer/include/functions.inc.php, модифицируя конструкции echo: к иконке динамика добавляем небольшой кусочек необходимой разметки
<head>
	<title>Asterisk Call Detail Records</title>
	<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
	<link rel="stylesheet" href="style/screen.css" type="text/css" media="screen" />
	<link rel="shortcut icon" href="templates/images/favicon.ico" />
	<script type="text/javascript"/>
function audioPreview(e,cnc) {
 var uri = e.attributes.getNamedItem('data-uri').value;
 var audioElement;
 // 1 if not exists audio control, then create it:
 if (!(audioElement = document.getElementById('au_preview'))) {
  audioElement = document.createElement('audio');
  audioElement.id = 'au_preview';
  audioElement.controls = true;
  audioElement.style.display = 'none';
  document.body.appendChild(audioElement);
 }
 else {
  // 2 need to stop and hide if playing:
  var prevIcon = audioElement.parentNode.previousSibling;
  prevIcon.src = 'templates/images/play.png';
  prevIcon.onclick = function(){ return audioPreview(prevIcon,false);};
 }

 if (('undefined'===typeof cnc)||(!cnc)) {
  //1. to show
  e.nextSibling.appendChild(audioElement);
  audioElement.src = uri;
  audioElement.style.display = 'block';
  audioElement.play();
  e.onclick = function(){ return audioPreview(e,true); };
  e.src = 'templates/images/stop.png';
 }
 else {
  //2. to hide
  audioElement.pause();
  audioElement.style.display = 'none';
  e.onclick = function(){ return audioPreview(e,false); };
  e.src = 'templates/images/play.png';
 }
}</script>
</head>
<body>
	<table id="header">
		<tr>
			<td id="header_logo" rowspan="2" align="left"><a href="/" title="Home"><img src="" alt="Asterisk CDR Viewer" /></a></td>
			<td id="header_title">Asterisk CDR Viewer</td>
			<td align='right'>
				<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=XVUVZY5D922JJ&lc=RU&item_name=i%2eo%2e&item_number=asterisk%2dcdr&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="" align="center"/></a>
			</td>
		</tr>
		<tr>
		<td id="header_subtitle"> </td>
			<td align='right'>
			<?php
			if ( strlen(getenv('REMOTE_USER')) ) {
				echo "<a href='/acdr/index.php?action=logout'>logout: ". getenv('REMOTE_USER') ."</a>";
			}
			?>
		</td>
		</tr>
		</table>
root@sip:/var/www/asterisk-cdr-viewer/templates# 
root@sip:/var/www/asterisk-cdr-viewer/include# cat functions.inc.php
<?php

/* Recorded file */
function formatFiles($row) {
	global $system_monitor_dir, $system_fax_archive_dir, $system_audio_format, $system_arch_audio_format;

	/* File name formats, please specify: */
	
	/* 
		caller-called-timestamp.wav 
	*/
	/* 
	$recorded_file = $row['src'] .'-'. $row['dst'] .'-'. $row['call_timestamp']
	*/
	/* ============================================================================ */	

	/* 
		ends at the uniqueid.wav, for example: 
												date-time-uniqueid.wav 
	
		thanks to Beto Reyes
	*/
	/*
	$recorded_file = glob($system_monitor_dir . '/*' . $row['uniqueid'] . '.' . $system_audio_format);
	if (count($recorded_file)>0) {
		$recorded_file = basename($recorded_file[0],".$system_audio_format");
	} else {
		$recorded_file = $row['uniqueid'];
	}
	*/
	/* ============================================================================ */

	/*      This example for multi-directory archive without uniqueid, named by format:
			<date>/<time>_<caller>-<destination>.<filetype>

			example: (tree /var/spool/asterisk/monitor)

		|-- 2012.09.12
		|   |-- 10-37_4952704601-763245.ogg
		|   `-- 10-43_106-79236522173.ogg
		`-- 2012.09.13
			|-- 11-42_101-79016410692.ogg
			|-- 12-43_104-671554.ogg
			`-- 15-49_109-279710.ogg

		Added by BAXMAH (pcm@ritm.omsk.ru)
	*/
	/*
	   $record_datetime = DateTime::createFromFormat('Y-m-d G:i:s', $row['calldate']);

	   $recorded_file = date_format($record_datetime, 'Y.m.d/G-i') .'_'. $row['src'] .'-'. $row['dst'];
	*/
	/* ============================================================================ */

	/*
		This is a multi-dir search script for filenames like "/var/spool/asterisk/monitor/dir1/dir2/dir3/*uniqueid*.*"
		Doesn't matter, WAV, MP3 or other file format, only UNIQID  is  required at the end of the filename 
		;---------------------------------------------------------------------------  
	   example: (tree /var/spool/asterisk/monitor)

    |-- in
    |   |-- 4951234567
    |   |   `-- 20120101_234231_4956401234_to_74951234567_1307542950.0.wav
    |   `-- 4997654321
    |       `-- 20120202_234231_4956401234_to_74997654321_1303542950.0.wav
    `-- out
        |-- msk
        |   `-- 20120125_211231_4956401234_to_74951234567_1307542950.0.wav
        `-- region
            `-- 20120112_211231_4956405570_to_74952210533_1307542950.0.wav

      6 directories, 4 files
		;----------------------------------------------------------------------------
	   added by Dein admin@sadmin.ru         
	*/
	
	
	//************ Get a list of subdirectories as array to search by glob function  **************
	if (!function_exists('get_dir_list')) {
		function get_dir_list($dir){
			global $dirlist;			
			$dirlist=array();
			if (!function_exists('find_dirs_recursive')) {
				function find_dirs_recursive($sdir) {
					global $dirlist;
					foreach(glob($sdir) as $filename) {
						//echo $filename;
						if(is_dir($filename)) {
							$dirlist[]=$filename;
							find_dirs_recursive($filename."/*");
						};//endif
					};//endforeach
				}; //endfunc                                                                                               
			};//endif exists
			find_dirs_recursive($dir."/*");
		};//endfunc
	}

	//*************** Main function  ************
	if (!function_exists('find_record_by_uniqid')) {
		function find_record_by_uniqid($path,$uniqid){
			global $dirlist;
			if (sizeof($dirlist) == 0 ){
				get_dir_list($path);
			};//endif size==0

			if (sizeof($dirlist) == 0 ) {return "SOME ERROR, dirlist is empty";};

			$found = "NOTHING FOUND";
			foreach ($dirlist as $curdir) {
				$res=glob($curdir."/*".$uniqid.".*");
				if ($res) {$found=$res[0]; break;};
			};//endforeach

			$res=str_replace($path,"",$found);	//cut $path from full filename 
			
			return $res;			//to be compartable with func. formatFiles($row)

		};//endfunc
	}
	
	$recorded_file = find_record_by_uniqid($system_monitor_dir,$row['uniqueid']);
	
	
	/* ============================================================================ */

	/* 
		uniqueid.wav 
	*/
//	$recorded_file = $row['filename'];
	/* ============================================================================ */	

	if (file_exists("$system_monitor_dir/$recorded_file.$system_audio_format")) {
		// insert here:
		echo "    <td class=\"record_col\"><div class=\"record_ctrl\"><a href=\"download.php?audio=$recorded_file.$system_audio_format\" title=\"Listen to call recording\"><img src=  recording\" /></a><img class=\"record_preview_icon\" src=  onclick=\"audioPreview(this,false);\"/><div class=\"record_preview_lt\"></div></div></td>\n";
	} elseif ( isset($system_arch_audio_format) and file_exists("$system_monitor_dir/$recorded_file.$system_audio_format.$system_arch_audio_format")) {
		echo "    <td class=\"record_col\"><a href=\"download.php?arch=$recorded_file.$system_audio_format.$system_arch_audio_format\" title=\"Download archive\"><img src=  recording\" /></a></td>\n";
	} elseif (file_exists("$system_fax_archive_dir/$recorded_file.tif")) {
		echo "    <td class=\"record_col\"><a href=\"download.php?fax=$recorded_file.tif\" title=\"View FAX image\"><img src=  image\" /></a></td>\n";
	} elseif (file_exists("$system_monitor_dir/$recorded_file")) 
{		// insert here:
		echo "    <td class=\"record_col\"><div class=\"record_ctrl\"><a href=\"download.php?audio=$recorded_file\" title=\"Listen to call recording\"><img src=  recording\" /></a><img class=\"record_preview_icon\" src=  onclick=\"audioPreview(this,false);\"/><div class=\"record_preview_lt\"></div></div></td>\n";
	} else {
		echo "    <td class=\"record_col\"></td>\n";
	}
}

/* CDR Table Display Functions */
function formatCallDate($calldate,$uniqueid) {
	echo "    <td class=\"record_col\"><abbr title=\"UniqueID: $uniqueid\">$calldate</abbr></td>\n";
}

function formatChannel($channel) {
	$trunk_name = preg_replace('/(.*)-[^-]+$/','$1',$channel);
	echo "    <td class=\"record_col\"><abbr title=\"Channel: $channel\">$trunk_name</abbr></td>\n";
}

function formatClid($clid) {
	$clid_only = explode(' <', $clid, 2);
	$clid = htmlspecialchars($clid_only[0]);
	echo "    <td class=\"record_col\">$clid</td>\n";
}

function formatSrc($src,$clid) {
	if (empty($src)) {
		echo "    <td class=\"record_col\">UNKNOWN</td>\n";
	} else {
		$src = htmlspecialchars($src);
		$clid = htmlspecialchars($clid);
		echo "    <td class=\"record_col\"><abbr title=\"Caller*ID: $clid\">$src</abbr></td>\n";
	}
}

function formatApp($app, $lastdata) {
	echo "    <td class=\"record_col\"><abbr title=\"Application: $app($lastdata)\">$app</abbr></td>\n";
}

function formatDst($dst, $dcontext) {
	global $rev_lookup_url;
	if (strlen($dst) == 11 and strlen($rev_lookup_url) > 0 ) {
		$rev = str_replace('%n', $dst, $rev_lookup_url);
		echo "    <td class=\"record_col\"><abbr title=\"Destination Context: $dcontext\"><a href=\"$rev\" target=\"reverse\">$dst</a></abbr></td>\n";
	} else {
		echo "    <td class=\"record_col\"><abbr title=\"Destination Context: $dcontext\">$dst</abbr></td>\n";
	}
}

function formatDisposition($disposition, $amaflags) {
	switch ($amaflags) {
		case 0:
			$amaflags = 'DOCUMENTATION';
			break;
		case 1:
			$amaflags = 'IGNORE';
			break;
		case 2:
			$amaflags = 'BILLING';
			break;
		case 3:
		default:
			$amaflags = 'DEFAULT';
	}
	echo "    <td class=\"record_col\"><abbr title=\"AMA Flag: $amaflags\">$disposition</abbr></td>\n";
}

function formatDuration($duration, $billsec) {
	$duration = sprintf('%02d', intval($duration/60)).':'.sprintf('%02d', intval($duration%60));
	$billduration = sprintf('%02d', intval($billsec/60)).':'.sprintf('%02d', intval($billsec%60));
	echo "    <td class=\"record_col\"><abbr title=\"Billing Duration: $billduration\">$duration</abbr></td>\n";
}

function formatUserField($userfield) {
	echo "    <td class=\"record_col\">$userfield</td>\n";
}

function formatAccountCode($accountcode) {
	echo "    <td class=\"record_col\">$accountcode</td>\n";
}

/* Asterisk RegExp parser */
function asteriskregexp2sqllike( $source_data, $user_num ) {
	$number = $user_num;
	if ( strlen($number) < 1 ) {
		$number = $_REQUEST[$source_data];
	}
	if ( '__' == substr($number,0,2) ) {
		$number = substr($number,1);
	} elseif ( '_' == substr($number,0,1) ) {
		$number_chars = preg_split('//', substr($number,1), -1, PREG_SPLIT_NO_EMPTY);
		$number = '';
		foreach ($number_chars as $chr) {
			if ( $chr == 'X' ) {
				$number .= '[0-9]';
			} elseif ( $chr == 'Z' ) {
				$number .= '[1-9]';
			} elseif ( $chr == 'N' ) {
				$number .= '[2-9]';
			} elseif ( $chr == '.' ) {
				$number .= '.+';
			} elseif ( $chr == '!' ) {
				$_REQUEST[ $source_data .'_neg' ] = 'true';
			} else {
				$number .= $chr;
			}
		}
		$_REQUEST[ $source_data .'_mod' ] = 'asterisk-regexp';
	}
	return $number;
}

/* empty() wrapper. Thanks to Mikael Carlsson. */
function is_blank($value) {
	return empty($value) && !is_numeric($value);
}

/* 
	Money format

	thanks to Shiena Tadeo
*/ 
function formatMoney($number, $cents = 2) { // cents: 0=never, 1=if needed, 2=always
	global $callrate_currency;
	if (is_numeric($number)) { // a number
		if (!$number) { // zero
			$money = ($cents == 2 ? '0.00' : '0'); // output zero
		} else { // value
			if (floor($number) == $number) { // whole number
				$money = number_format($number, ($cents == 2 ? 2 : 0)); // format
			} else { // cents
				$money = number_format(round($number, 2), ($cents == 0 ? 0 : 2)); // format
			} // integer or decimal
		} // value
		echo   "<td class=\"chart_data\">$callrate_currency<span>$money</span></td>\n";
	} else {
		echo   "<td class=\"chart_data\"> </td>\n";
	}
} // formatMoney

/* 
	CallRate
	return callrate array [ areacode, rate, description, bill type, total_rate] 
*/
function callrates($dst,$duration,$file) {
	global $callrate_csv_file, $callrate_cache;

	if ( strlen($file) == 0 ) {
		$file = $callrate_csv_file;
		if ( strlen($file) == 0 ) {
			return array('','','','','');
		}
	}
	
	if ( ! array_key_exists( $file, $callrate_cache ) ) {
		$callrate_cache[$file] = array();
		$fr = fopen($file, "r") or die("Can not open callrate file ($file).");
		while(($fr_data = fgetcsv($fr, 1000, ",")) !== false) {
			$callrate_cache[$file]["$fr_data[0]"] = array( $fr_data[1], $fr_data[2], $fr_data[3] );
		}
		fclose($fr);
	}

	for ( $i = strlen($dst); $i > 0; $i-- ) {
		if ( array_key_exists( substr($dst,0,$i), $callrate_cache[$file] ) ) {
			$call_rate = 0;
			if ( $callrate_cache[$file][substr($dst,0,$i)][2] == 's' ) {
				// per second
				$call_rate = $duration * ($callrate_cache[$file][substr($dst,0,$i)][0] / 60);
			} elseif ( $callrate_cache[$file][substr($dst,0,$i)][2] == 'c' ) {
				// per call
				$call_rate = $callrate_cache[$file][substr($dst,0,$i)][0];
			} elseif ( $callrate_cache[$file][substr($dst,0,$i)][2] == '1m+s' ) {
				// 1 minute + per second
				if ( $duration < 60) {
					$call_rate = $callrate_cache[$file][substr($dst,0,$i)][0];
				} else {
					$call_rate = $callrate_cache[$file][substr($dst,0,$i)][0] + ( ($duration-60) * ($callrate_cache[$file][substr($dst,0,$i)][0] / 60) );
				}
			} else {
				//( $callrate_cache[substr($dst,0,$i)][2] == 'm' ) {
				// per minute
				$call_rate = intval($duration/60);
				if ( $duration%60 > 0 ) {
					$call_rate++;
				}
				$call_rate = $call_rate*$callrate_cache[$file][substr($dst,0,$i)][0];
			}
			return array(substr($dst,0,$i),$callrate_cache[$file][substr($dst,0,$i)][0],$callrate_cache[$file][substr($dst,0,$i)][1],$callrate_cache[$file][substr($dst,0,$i)][2],$call_rate);
		}
	}

	return array (0,0,'unknown','unknown',0);
}

?>



4)
Дополняем файл стилей /var/www/asterisk-cdr-viewer/style/screen.css

/* HTML tag styles */
a {
	cursor : pointer;
}


abbr[title] {
	border-style : none none dashed;
	border-width : medium medium 1px;
	border-bottom-color : #000000;
	cursor : help;
	 white-space : nowrap;
}


body {
	background-color : #fff;
	/* Fix for M$ IE < 7 CSS hover */ behavior : url('style/csshover.htc');
	color : #000;
	font : 65% Verdana, Arial, Helvetica, sans-serif;
	margin : 0;
	padding : 0;
}


img {
	border-width : 0;
}


/* ID styles */
#header {
	background-image : url('../templates/images/header_gradient.png');
	background-repeat : repeat-x;
	margin : 0;
	padding-left : 5%;
	padding-right : 10%;
	position : fixed;
	width : 100%;
	z-index : 50;
}

#header_logo {
	height: 105px;
	width: 121px;
	vertical-align: top;
}

#header_title {
	color: #000000;
	font-family: serif;
	font-size: 32pt;
	font-variant: small-caps;
	font-weight: bold;
	height: 60px;
}

#header_subtitle {
	color: #68878a;
	font-family: Verdana,Arial,'Bitstream Vera Sans',Helvetica,sans-serif;
	font-size: 12pt;
	font-weight: bold;
	height: 60px;
	padding-left: 10px;
	vertical-align: top;
}

#main {
	margin : 0;
	padding-top : 115px;
}

#footer {
	padding : 5px;
	border-width : 0;
	text-align : center;
}


/* Class styles */
.bar_calls {
	background-color : #aaf5d0;
	float : none;
	padding : 0 0 0 2px;
}


.bar_duration {
	background-color : #e5edf9;
	float : none;
	padding : 0 0 0 2px;
}


.cdr {
	margin : 0 2%;
	border-width : 0;
	white-space : nowrap;
	width : 96%;
}


.cdr th {
	background-color : #5ebeff;
	border-color : #000;
	border-width : 2px;
	text-align : center;
}


.cdr .center_col {
	width : 78%;
	padding : 2px;
}


.cdr .end_col {
	width : 11%;
	padding : 1px;
}

.cdr .chart_data {
	padding : 0px;
	text-align : right;
}


.cdr .img_col {
	width : 16px;
	height : 16px;
}


.form legend, .title, .title a {
	color: #777;
	font-size: 2em;
	font-weight: bold;
}


.record {
	background-color : #fff;
	empty-cells : hide;
}


.record:hover {
	background : #ffdca8;
	color : #000;
	empty-cells : hide;
}


.record_col {
	padding-left : 2px;
	padding-right : 2px;
	border-width : 0;
}


.center {
	text-align : center;
}

.right {
	padding-right : 80px;
	text-align : right;
	font-size: 9pt;
}

.record_ctrl {
	position:relative
}

.record_preview_icon {
	width:16px;
	height:16px;
	cursor:pointer;
	margin-left:3px;
}

.record_preview_lt {
	position:absolute;
	top:0;
	left:38px;
	z-index:99;
}



5)
Дополняем файл /var/www/asterisk-cdr-viewer/download.php (обработка range-запросов для аудио-файлов)


<?php

require_once 'include/config.inc.php';

header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');

function partial_download($range_header, $request_file, $mime) {
	$file_size = filesize($request_file);
	list($b, $range) = explode('=', $range_header);
	list($first_range) = explode(',', $range);
	list($part_start,$part_end) = explode('-', $first_range);
	$start = intval($part_start);
	if ($part_end)
		$end = intval($part_end);
	else
		$end = $file_size - 1;
	$chunksize = ($end-$start)+1;

	header('HTTP/1.1 206 Partial Content');
	header("Content-Type: $mime");
        header('Content-Transfer-Encoding: binary');
	header("Content-Range: bytes $start-$end/$file_size");
        header('Content-Length: '.$chunksize);
#       header("Content-Disposition: attachment; filename=\"$_REQUEST[audio]\"");

	$fp = fopen($request_file, 'r');
	fseek($fp, $start, SEEK_SET);
	echo fread($fp, $chunksize);
	fclose($fp);
}

if (isset($_REQUEST['audio'])) {
	$extension = strtolower(substr(strrchr($_REQUEST['audio'],"."),1));
	$ctype ='';
	switch( $extension ) {
		case "wav16":
			$ctype="audio/x-wav";
			break;
		case "wav":
			$ctype="audio/x-wav";
			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: 
			$ctype="application/$system_audio_format";
			break ;
	}
	
	header("Accept-Ranges: bytes");

	if (!isset($_SERVER['HTTP_RANGE'])) {
		header("Content-Type: $ctype");
		header('Content-Transfer-Encoding: binary');
		header('Content-Length: '.filesize("$system_monitor_dir/$_REQUEST[audio]"));
		header("Content-Disposition: attachment; filename=\"$_REQUEST[audio]\"");
		readfile("$system_monitor_dir/$_REQUEST[audio]");
	}
	else {
		partial_download($_SERVER['HTTP_RANGE'], "$system_monitor_dir/$_REQUEST[audio]", $ctype);
	}
} elseif (isset($_REQUEST['fax'])) {
	header('Content-Type: image/tiff');
	header('Content-Transfer-Encoding: binary');
	header('Content-Length: '.filesize("$system_fax_archive_dir/$_REQUEST[fax]"));
	header("Content-Disposition: attachment; filename=\"$_REQUEST[fax]\"");
	readfile("$system_fax_archive_dir/$_REQUEST[fax]");
} elseif (isset($_REQUEST['csv'])) {
	header('Content-Type: text/csv');
	header('Content-Transfer-Encoding: binary');
	header('Content-Length: '.filesize("/tmp/$_REQUEST[csv]"));
	header("Content-Disposition: attachment; filename=\"$_REQUEST[csv]\"");
	readfile("$system_tmp_dir/$_REQUEST[csv]");
} elseif (isset($_REQUEST['arch'])) {
	header('Content-Type: application/x-download');
	header('Content-Transfer-Encoding: binary');
	header('Content-Length: '.filesize("$system_monitor_dir/$_REQUEST[arch]"));
	header("Content-Disposition: attachment; filename=\"$_REQUEST[arch]\"");
	readfile("$system_monitor_dir/$_REQUEST[arch]");
}

exit();
?>




В результате получаем возможность прослушивания разговоров в веб-интерфейсе (мотать по бегунку прокрутки можно)



Надеюсь кому-нибудь пригодится:)
Tags:asteriskcdr
Hubs: Asterisk Development of communication systems
+2
56.3k 114
Comments 4