* @license BSD License http://fr.wikipedia.org/wiki/Licence_BSD * @link http://www.linuxtv.org/vdrwiki/index.php/Svdrp */ require_once ('firephp.php'); $a = new SVDRP ( null, null, null, false ); $a->connect (); $a->listChannels (); /** * SVDRP class * * This class provide access to VDR SVDRP service * * @package utilities * @author Philippe Gaultier * @since 1.0 */ class SVDRP { private $_server = "localhost"; private $_port = 2001; private $_timeOut = 30; private $_handle = false; private $_debug = false; private $_firePhp = null; /** * Class constructor * * @param string $server the server to connect to (IP or name) * @param integer $port the port number * @param integer $timeOut timeout before closing the connection * @param boolean $debug de/activate debug functions * * @return SVDRP */ public function __construct($server = null, $port = null, $timeOut = null, $debug = null) { if ($server !== null) { $this->_server = $server; } if ($port !== null) { $this->_port = $port; } if ($timeOut !== null) { $this->_timeOut = $timeOut; } if ($debug !== null) { $this->_debug = $debug; } if (class_exists ( 'FirePHP' )) { $this->_firePhp = FirePHP::getInstance ( true ); $this->_firePhp->setEnabled ( $this->_debug ); $this->group ( 'new ' . __CLASS__ . '($server, $port, $timeOut, $debug)' ); $this->info ( $this->_server, '$server' ); $this->info ( $this->_port, '$port' ); $this->info ( $this->_timeOut, '$timeOut' ); $this->info ( $this->_debug, '$debug' ); $this->trace ( 'Object created' ); $this->groupEnd (); } else { $this->_debug = false; } } private function group($title) { if ($this->_debug === true) { $this->_firePhp->group ( $title ); } } private function groupEnd() { if ($this->_debug === true) { $this->_firePhp->groupEnd (); } } private function trace($message) { if ($this->_debug === true) { $this->_firePhp->trace ( 'Class ' . __CLASS__ . ' Trace : ' . $message ); } } private function dump($name, $var) { if ($this->_debug === true) { $this->_firePhp->dump ( $name, $var ); } } private function log($message, $label = null) { if ($this->_debug === true) { $this->_firePhp->log ( $message, $label ); } } private function info($message, $label = null) { if ($this->_debug === true) { $this->_firePhp->info ( $message, $label ); } } private function warn($message, $label = null) { if ($this->_debug === true) { $this->_firePhp->warn ( $message, $label ); } } private function error($message, $label = null) { if ($this->_debug === true) { $this->_firePhp->error ( $message, $label ); } } public function debugMessage($msg) { if ($this->_debug === true) { //TODO: do a better debug (use firePHP or something else) // echo ($msg); } } public function connect() { $result = false; if ($this->_handle !== false) { $this->disconnect (); } $errno = 0; $errstr = ""; // we use @ in order to avoid warning if server does not answer / allow the connection $this->_handle = @fsockopen ( $this->_server, $this->_port, &$errno, &$errstr, $this->_timeOut ); $this->group ( __METHOD__ . '()' ); if ($this->_handle !== false) { $this->info ( 'Handle created', 'SVDRP' ); $input = fgets ( $this->_handle, 128 ); if ((preg_match ( "/^220 /", $input ) === true) && ($input != "")) { $this->info ( 'Answer OK : ' . $input, 'SVDRP' ); $result = true; } else { $this->warn ( 'Answer KO : ' . $input, 'SVDRP' ); $this->disconnect (); } } else { $this->error ( 'fsockopen error ' . $errno . ' (' . $errstr . ')', 'SVDRP' ); } $this->groupEnd (); return $result; } public function sendCommand($cmd) { $result = false; if ($this->_handle !== false) { $result = array (); $this->group ( __METHOD__ . '($cmd)' ); $this->info ( $cmd, '$cmd' ); fputs ( $this->_handle, $cmd . "\n" ); $answer = ""; $nbLines = 0; while ( $answer .= fgets ( $this->_handle, 2048 ) ) { $nbLines ++; $this->info ( $answer ); $data = null; if (preg_match ( "/^(\\d{3})([ -])(.*)$/", $answer, $data ) === false) { continue; } $number = $data [1]; $result [] = trim ( $data [3] ); /** if(($data[2] !== "-") && ($nbLines === 1)) { $result = trim($data[3]) ; } **/ if ($data [2] != "-") { break; } $answer = ""; } $this->trace ( 'Command sent, answer received' ); $this->groupEnd (); } return $result; } public function listChannels($channelNumberOrName = "") { $lines = $this->sendCommand ( 'LSTC ' . $channelNumberOrName ); if ($lines !== false) { $channels = array (); foreach ( $lines as $l ) { list ( $fullName, $frequency, $parameters, $source, $symbolRate, $videoPid, $audioPid, $teletextPid, $conditionalAccess, $serviceId, $networkId, $transportId, $radioId ) = split ( ":", $l ); list ( $shortName, $provider ) = split ( ";", $fullName ); $channels [] = array ('shortName' => $shortName, 'provider' => $provider, 'frequency' => $frequency, 'parameters' => $parameters, 'source' => $source, 'symbolRate' => $symbolRate, 'videoPid' => $videoPid, 'audioPid' => $audioPid, 'teletextPid' => $teletextPid, 'conditionalAccess' => $conditionalAccess, 'serviceId' => $serviceId, 'networkId' => $networkId, 'transportId' => $transportId, 'radioId' => $radioId, 'group' => $provider, //XXX: Compatibility 'name' => $shortName );//XXX: Compatibility } $lines = $channels; } return $lines; } public function help() { return $this->sendCommand ( 'HELP' ); } public function disconnect() { if ($this->_handle !== false) { $this->sendCommand ( 'QUIT' ); fclose ( $this->_handle ); $this->_handle = false; $this->debugMessage ( "disconnected" ); } } public function clearEpg() { $result = $this->sendCommand ( 'CLRE' ); if ($result !== false) { $result = true; } return $result; } public function switchUp() { return $this->switchChannel ( '+' ); } public function switchDown() { return $this->switchChannel ( '-' ); } public function switchChannel($channel) { $result = $this->sendCommand ( 'CHAN ' . $channel ); if ($result !== false) { $result = true; } return $result; } public function deleteChannel($channelId) { $result = $this->sendCommand ( 'DELC ' . $channelId ); if ($result !== false) { $result = true; } return $result; } public function deleteRecord($recordId) { $result = $this->sendCommand ( 'DELR ' . $recordId ); if ($result !== false) { $result = true; } return $result; } public function grabImage($fileName, $type = 'jpeg', $quality = '', $width = '', $height) { $result = $this->sendCommand ( 'GRAB ' . $fileName . ' ' . $type . ' ' . $quality . ' ' . $width . ' ' . $height ); if ($result !== false) { $result = true; } return $result; } public function hitKey($key) { $result = $this->sendCommand ( 'HITK ' . $key ); if ($result !== false) { $result = true; } return $result; } public function powerOff() { return $this->hitKey ( 'HITK Power' ); } public function getKeys() { $lines = $this->sendCommand ( 'HITK' ); if ($lines !== false) { $keys = array (); foreach ( $lines as $l ) { if (preg_match ( "/^ {4}(.*)$/", $l, $m ) === false) { continue; } $keys [] = $m [1]; } $lines = $keys; } return $lines; } public function listEPG($pStrChannel = "", $pStrTime = "") { $lines = $this->sendCommand ( "LSTE" ); if ($lines !== false) { $epg = array (); $channel = array (); $event = array (); $channelname = ""; foreach ( $lines as $line ) { if (preg_match ( "/^(.)\\s*(.*)$/", $line, $matches ) === true) { $type = $matches [1]; $text = $matches [2]; switch ($type) { case 'C' : // Channel list ( $channeldata, $channelname ) = explode ( ' ', $text, 2 ); break; case 'E' : // new Event sscanf ( $text, "%u %ld %d %X", $event ["EventID"], $event ["StartTime"], $event ["Duration"], $event ["TableID"] ); break; case 'T' : // Title $event ["Title"] = $text; break; case 'S' : // Short text $event ["Shottext"] = $text; break; case 'D' : // Description $event ["Desc"] = $text; break; case 'V' : // VPS $event ["VPS"] = $text; break; case 'e' : // Event end if ((trim ( $pStrTime ) != '') && (($event ['StartTime'] > $pStrTime) || ($event ['StartTime'] + $event ["Duration"] < $pStrTime))) { continue; } $channel [] = $event; $event = array (); break; case 'c' : // Channel end if ((trim ( $pStrChannel ) != '') && ($channelname != $pStrChannel)) { continue; } $epg [$channelname] = $channel; $channel = array (); break; } } } if ((trim ( $pStrTime ) == '') || (($event ['StartTime'] < $pStrTime) && ($event ['StartTime'] + $event ["Duration"] > $pStrTime))) { $channel [] = $event; } if ((trim ( $pStrChannel ) != '') || ($channelname == $pStrChannel)) { $epg [$channelname] = $channel; } $lines = $epg; } return $lines; } public function sendMessage($message) { $result = $this->sendCommand ( 'MESG ' . $message ); if ($result !== false) { $result = true; } return $result; } /** * Toggle volume * * @return boolean */ public function toggleMute() { return $this->setVolume ( 'mute' ); } /** * Raise sound volume * * @return boolean */ public function setVolumeUp() { return $this->setVolume ( '+' ); } /** * lower sound volume * * @return boolean */ public function setVolumeDown() { return $this->setVolume ( '-' ); } /** * Set volume to a specific value * * @param integer $volume new sound volume value * * @return boolean */ public function setVolume($volume) { $result = $this->sendCommand ( 'VOLU ' . $volume ); if ($result !== false) { $result = true; } return $result; } /** * Get current volume value * * @return boolean|integer */ public function getVolume() { $result = $this->sendCommand ( 'VOLU' ); if ($result !== false) { $volumeData = array_shift ( $result ); if ($volumeData == "Audio is mute") { $result = 0; } elseif (preg_match ( "/Audio volume is (.*)/", $volumeData, $matches ) === true) { $result = $matches [1]; } else { $result = false; } } return $result; } /** * Get current disk information * * @return boolean|array */ public function getDiskInfo() { $res = $this->sendCommand ( 'STAT DISK' ); $result = $this->sendCommand ( 'STAT DISK' ); if ($result !== false) { $result = array_shift ( $result ); sscanf ( $result, "%dMB %dMB %d%%", $data ['overall'], $data ['free'], $data ['percent'] ); $data ['used'] = $data ['overall'] - $data ['free']; $result = $data; } return $result; } /** * Force a new scan * * @return boolean */ public function startScan() { $result = $this->sendCommand ( 'SCAN' ); if ($result !== false) { $result = true; } return $result; } public function moveChannel($channelNumber, $target) { $result = $this->sendCommand ( 'MOVC ' . $channelNumber . ' ' . $target ); if ($result !== false) { $result = true; } return $result; } public function deleteTimer($timerId) { $result = $this->sendCommand ( 'DELT ' . $timerId ); if ($result !== false) { $result = true; } return $result; } public function moveTimer($timerNumber, $target) { $result = $this->sendCommand ( 'MOVT ' . $timerNumber . ' ' . $target ); if ($result !== false) { $result = true; } return $result; } /** * Change the status of a specific timer * * @param string $timerId timer id * @param boolean $state status to set (true : on / false : off) * * @return boolean */ public function toggleTimer($timerId, $state = true) { switch ($state) { case false : case 'off' : case 0 : $state = 'off'; break; default : $state = 'on'; break; } $result = $this->sendCommand ( 'MODT ' . $timerId . ' ' . $state ); if ($result !== false) { $result = true; } return $result; } /** * List all timers * * @return void */ public function listTimers() { //TODO: implement timer listing } /** * List all recordings with specific data * * @return array */ public function listRecords() { $lines = $this->sendCommand ( 'LSTR' ); if ($lines !== false) { $records = array (); foreach ( $lines as $line ) { if (preg_match ( '/^(\\d)\s(\\d*)\\.(\\d*)\\.(\\d*) (\\d*)\\:(\\d*).\s(.*)$/', $line, $matches ) == false) { continue; } $records [$matches [1]] = array ('id' => $matches [1], 'day' => $matches [2], 'month' => $matches [3], 'year' => $matches [4], 'hour' => $matches [5], 'minute' => $matches [6], 'desc' => $matches [7] ); } $lines = $records; } return $lines; } /** * List recording by Id * * @param string $recordId * * @return string */ public function listRecord($recordId) { //TODO: perhaps better implementation and check if we do not need array_shift $lines = $this->sendCommand ( 'LSTR ' . $recordId ); if ($lines !== false) { $lines = array_shift ( $lines ); $lines = $lines [0]; } return $lines; } //TODO: Implement following commands: /** LSTT MODT NEWT UPDT MODC NEWC NEXT PUTE **/ }