00001 <?php
00002
00003 class MythTV {
00004 var $VERSION = '.21svn';
00005 # MYTH_PROTO_VERSION is defined in libmyth in mythtv/libs/libmyth/mythcontext.h
00006 # and should be the current MythTV protocol version.
00007 var $PROTO_VER = 33;
00008 # NUMPROGRAMLINES is defined in mythtv/libs/libmythtv/programinfo.h and is
00009 # the number of items in a ProgramInfo QStringList group used by
00010 # ProgramInfo::ToSringList and ProgramInfo::FromStringList.
00011 var $NUMPROGRAMLINES = 43;
00012 var $BACKEND_SEP = '[]:[]';
00013
00014 var $RecordingTypes = array( 1 => 'Once',
00015 2 => 'Daily',
00016 3 => 'Channel',
00017 4 => 'Always',
00018 5 => 'Weekly',
00019 6 => 'Find Once',
00020 7 => 'Override',
00021 8 => 'Dont Record',
00022 9 => 'Find Daily',
00023 10 => 'Find Weekly' );
00024
00025 var $Searches = array( 1 => 'Power',
00026 2 => 'Title',
00027 3 => 'Keyword',
00028 4 => 'People',
00029 5 => 'Manual' );
00030
00031 # Reasons a recording wouldn't be happening (from libs/libmythtv/programinfo.h)
00032 var $RecordingStatus = array( -8 => 'TunerBusy',
00033 -7 => 'LowDiskSpace',
00034 -6 => 'Cancelled',
00035 -5 => 'Deleted',
00036 -4 => 'Aborted',
00037 -3 => 'Recorded',
00038 -2 => 'Recording',
00039 -1 => 'WillRecord',
00040 0 => 'Unknown',
00041 1 => 'DontRecord',
00042 2 => 'PreviousRecording',
00043 3 => 'CurrentRecording',
00044 4 => 'EarlierShowing',
00045 5 => 'TooManyRecordings',
00046 6 => 'NotListed',
00047 7 => 'Conflict',
00048 8 => 'LaterShowing',
00049 9 => 'Repeat',
00050 10 => 'Inactive',
00051 11 => 'NeverRecord' );
00052
00053
00054 var $ConfigFiles = array( '/usr/local/share/mythtv/mysql.txt',
00055 '/usr/share/mythtv/mysql.txt',
00056 '/usr/local/etc/mythtv/mysql.txt',
00057 '/etc/mythtv/mysql.txt',
00058 '~/.mythtv/mysql.txt',
00059 'mysql.txt' );
00060 var $ConfigFile = NULL;
00061
00062 var $Config = array();
00063
00064 var $DB = NULL;
00065
00066 var $SOCKETS = array();
00067
00068 var $Channels = array();
00069 var $Scheduled = array();
00070 var $Recorded = array();
00071
00072 function MythTV() {
00073 $this->__construct();
00074 }
00075
00076 function __construct() {
00077 if (isset($_ENV['MYTHCONFDIR']))
00078 $this->ConfigFiles[] = $_ENV['MYTHCONFDIR'];
00079
00080 $found = FALSE;
00081 foreach ($this->ConfigFiles as $ConfigFile) {
00082
00083 $ConfigFile = str_replace('~', $_ENV['HOME'], $ConfigFile);
00084 $this->ConfigFile = realpath($ConfigFile);
00085 if ($this->ConfigFile) {
00086 $found = TRUE;
00087 break;
00088 }
00089 }
00090 if (!$found)
00091 die("Can not find a valid config file.\n");
00092 $this->LoadConfig();
00093 $this->DB = Database::connect( $this->Config['DBName'],
00094 $this->Config['DBUserName'],
00095 $this->Config['DBPassword'],
00096 $this->Config['DBHostName'],
00097 null,
00098 'mysql' );
00099
00100 unset($this->Config['DBPassword']);
00101 }
00102
00103
00104 function LoadConfig() {
00105 $file = fopen($this->ConfigFile, 'r');
00106 while (!feof($file)) {
00107 $line = trim(fgets($file));
00108
00109 if (strlen($line) === 0 || strpos($line, '#') === 0)
00110 continue;
00111 list ($var, $value) = explode('=', $line, 2);
00112 $this->Config[$var] = $value;
00113 }
00114 fclose($file);
00115 }
00116
00117 function Setting($name, $hostname = NULL, $value = "old\0old") {
00118 static $CACHE;
00119 if ($value !== "old\0old") {
00120 if (is_null($hostname))
00121 $this->DB->query('DELETE FROM settings WHERE settings.value = ? AND settings.hostname IS NULL', $name);
00122 else
00123 $this->DB->query('DELETE FROM settings WHERE settings.value = ? AND settings.hostname = ?', $name, $hostname);
00124 $this->DB->query('INSERT INTO settings (value, hostname, data) VALUES ( ?, ?, ?)', $name, $hostname, $value);
00125 $CACHE[$hostname][$name] = $value;
00126 }
00127 elseif (!isset($CACHE[$hostname][$name])) {
00128 if (is_null($hostname))
00129 $CACHE[$hostname][$name] = $this->DB->query_col('SELECT settings.data FROM settings WHERE settings.value = ? AND settings.hostname IS NULL', $name);
00130 else
00131 $CACHE[$hostname][$name] = $this->DB->query_col('SELECT settings.data FROM settings WHERE settings.value = ? AND settings.hostname = ?', $name, $hostname);
00132 }
00133 return $CACHE[$hostname][$name];
00134 }
00135
00136 function Command($command, $host = NULL, $port = NULL, $sendonly = FALSE) {
00137 $this->Send($command, $host, $port);
00138 if (!$sendonly)
00139 return $this->Receive($host);
00140 }
00141
00142 function Send($command, $host, $port) {
00143 if (is_null($host))
00144 $host = $this->Setting('master_host');
00145 if (is_null($port))
00146 $port = $this->Setting('master_port');
00147 if (!isset($this->SOCKETS[$host])) {
00148 $this->SOCKETS[$host] = $this->CreateSocket($host, $port);
00149
00150 $proto_ver = $this->Command('MYTH_PROTO_VERSION', $host, $port);
00151 if ($this->PROTO_VER != $proto_ver)
00152 die("$host is proto version $proto_ver, but we expect $this->PROTO_VER\n");
00153 }
00154 $this->SendData($this->SOCKETS[$host], $command);
00155 }
00156
00157 function SendData($socket, $data = NULL) {
00158 if (is_null($data))
00159 return FALSE;
00160 if (is_array($data))
00161 $data = implode($this->BACKEND_SEP, $data);
00162 if (fwrite($socket, str_pad(strlen($data), 8, ' ', STR_PAD_RIGHT).$data) === FALSE)
00163 die("Failure sending $data\n");
00164 }
00165
00166 function CreateSocket($host, $port, $data = NULL) {
00167 $trys = 5;
00168 do {
00169 $socket = fsockopen( $host,
00170 $port,
00171 $errno,
00172 $errstr,
00173 25 );
00174 } while ($errno != 0 && $trys-- > 0);
00175 if ($trys == 0)
00176 die("Can't connect to $host:$port [$errno] $errstr\n");
00177 $this->SendData($socket, $data);
00178 return $socket;
00179 }
00180
00181 function Receive($host) {
00182 $data = '';
00183 $length = intval(fread($this->SOCKETS[$host], 8));
00184 do {
00185 $data .= fread($this->SOCKETS[$host], ($length - strlen($data)));
00186 } while (strlen($data) < $length);
00187 return $data;
00188 }
00189
00190 function StreamBackendFile($basename, $fh, $local_path = NULL, $host = NULL, $port = NULL, $seek = 0) {
00191 if (is_null($host))
00192 $host = $this->Setting('master_host');
00193 if (is_null($port))
00194 $port = $this->Setting('master_port');
00195 if (!is_resource($fh)) {
00196 $target_path = $fh;
00197 $fh = fopen($target_path, 'w+');
00198 }
00199 if (!is_resource($fh))
00200 die("Can't create file $target_path or write to fh $fh\n");
00201 if (!is_null($local_path) && file_exists($local_path)) {
00202 $infh = fopen($local_path, 'r');
00203 if (is_resource($infh)) {
00204 fseek($infh, $seek);
00205 while(!feof($infh))
00206 fwrite($fh, fread($infh, 2097152));
00207 fclose($fh);
00208 fclose($infh);
00209 return TRUE;
00210 }
00211 else
00212 fwrite(STDERR, "Can't read $local_path. Trying to stream from mythbackend instead.\n");
00213 }
00214 return FALSE;
00215 }
00216
00217 function BackendRows($command, $offset = 0) {
00218 $data = explode($this->BACKEND_SEP, $this->Command($command));
00219 $rows = array();
00220 $row = 0;
00221 $col = 0;
00222 foreach ($data as $entry) {
00223 $rows[$row][$col] = $entry;
00224 $col++;
00225 if ($col >= $this->NUMPROGRAMLINES) {
00226 $rows++;
00227 $col = 0;
00228 }
00229 }
00230
00231 for ($i = 0; $i < $offset; $i++)
00232 $rows['offset'][$i] = $data[$i];
00233 return $rows;
00234 }
00235
00236 function StatusCommand($path = '', $params = array(), $host = NULL, $port = NULL) {
00237 if (is_null($host))
00238 $host = $this->Setting('master_host');
00239 if (is_null($port))
00240 $port = $this->Setting('BackendStatusPort');
00241 $query = '';
00242 if (is_array($params) && count($params)) {
00243 foreach($params as $key => $value) {
00244 if (strlen($query))
00245 $query .= '&';
00246 $query.=$key.'='.urlencode($value);
00247 }
00248 }
00249 $socket = fsockopen($host, $port, $errno, $errstr);
00250 if (!$socket)
00251 die("Error connecting to $host:$port/$path to send status command\n");
00252 fwrite($socket, "POST /$path HTTP/1.1\n");
00253 fwrite($socket, "Host: $host\n");
00254 fwrite($socket, "Content-Type: application/x-www-form-urlencoded\n");
00255 fwrite($socket, 'Content-Length: '.strlen($query)."\n");
00256 fwrite($socket, "\n");
00257 fwrite($socket, "$query\n");
00258 while (!feof($socket))
00259 $result .= fread($socket);
00260 fclose($socket);
00261 return $result;
00262 }
00263
00264 function Channel($ChanID) {
00265 if (!isset($this->Channels[$ChanID]))
00266 $this->LoadChannels();
00267 return $this->Channels[$ChanID];
00268 }
00269
00270 function LoadChannels() {
00271 $channelids = $sh->query('SELECT channel.chanid
00272 FROM channel');
00273 while ($chanid = $channelids->fetch_col())
00274 if (!isset($this->Channels[$chanid]))
00275 $this->Channels[$chanid] = new MythTVChannel($this, $chanid);
00276 }
00277
00278 function UnixToMyth($timestamp) {
00279 if (!is_numeric($timestamp))
00280 $timestamp = strtotime($timestamp);
00281 return date('Y-m-d H:i:s', $timestamp);
00282 }
00283
00284 function MythToUnix($time) {
00285 return strtotime($time);
00286 }
00287
00288 function Recording($chanid = NULL, $starttime = NULL, $basename = NULL) {
00289 return FALSE;
00290 }
00291
00292 function GetRecordingDirs() {
00293 $dirs = array();
00294 $sh = $this->DB->query('SELECT DISTINCT storagegroup.dirname
00295 FROM storagegroup');
00296 while ($dir = $sh->fetch_col())
00297 $dirs[] = trim($dir);
00298 return $dirs;
00299 }
00300
00301 function GetLocalHostname() {
00302 static $hostname = NULL;
00303 if (!is_null($hostname))
00304 $hostname = `hostname`;
00305 return $hostname;
00306 }
00307 }