api.cameras.php 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  1. <?php
  2. /**
  3. * Camera devices management
  4. */
  5. class Cameras {
  6. /**
  7. * Contains alter config as key=>value
  8. *
  9. * @var array
  10. */
  11. protected $altCfg = array();
  12. /**
  13. * Contains binpaths config as key=>value
  14. *
  15. * @var array
  16. */
  17. protected $binPaths = array();
  18. /**
  19. * Cameras database abstraction layer placeholder
  20. *
  21. * @var object
  22. */
  23. protected $camerasDb = '';
  24. /**
  25. * Custom cameras options database abstraction layer placeholder
  26. *
  27. * @var object
  28. */
  29. protected $camoptsDb = '';
  30. /**
  31. * Contains all available cameras as id=>cameraData
  32. *
  33. * @var array
  34. */
  35. protected $allCameras = array();
  36. /**
  37. * Contains all available cameras custom options as cameraid=>optsData
  38. *
  39. * @var array
  40. */
  41. protected $allCamOpts = array();
  42. /**
  43. * Camera models instnce placeholder
  44. *
  45. * @var object
  46. */
  47. protected $models = '';
  48. /**
  49. * Storages instance placeholder.
  50. *
  51. * @var object
  52. */
  53. protected $storages = '';
  54. /**
  55. * System messages helper instance placeholder
  56. *
  57. * @var object
  58. */
  59. protected $messages = '';
  60. /**
  61. * some predefined stuff here
  62. */
  63. const DATA_TABLE = 'cameras';
  64. const OPTS_TABLE = 'camopts';
  65. const URL_ME = '?module=cameras';
  66. const AJ_ARCHSTATS = 'archivestatscontainer';
  67. const PROUTE_NEWMODEL = 'newcameramodelid';
  68. const PROUTE_NEWIP = 'newcameraip';
  69. const PROUTE_NEWLOGIN = 'newcameralogin';
  70. const PROUTE_NEWPASS = 'newcamerapassword';
  71. const PROUTE_NEWACT = 'newcameraactive';
  72. const PROUTE_NEWSTORAGE = 'newcamerastorageid';
  73. const PROUTE_NEWCOMMENT = 'newcameracomment';
  74. const PROUTE_ED_CAMERAID = 'editcameraid';
  75. const PROUTE_ED_MODEL = 'editcameramodelid';
  76. const PROUTE_ED_IP = 'editcameraip';
  77. const PROUTE_ED_LOGIN = 'editcameralogin';
  78. const PROUTE_ED_PASS = 'editcamerapassword';
  79. const PROUTE_ED_CUSTPORT = 'editcamerartspport';
  80. const PROUTE_ED_STORAGE = 'editcamerastorageid';
  81. const PROUTE_ED_COMMENT = 'editcameracomment';
  82. const PROUTE_ED_CAMERAID_ACT = 'renamecameraid';
  83. const PROUTE_ED_COMMENT_ACT = 'renamecameracomment';
  84. const ROUTE_DEL = 'deletecameraid';
  85. const ROUTE_EDIT = 'editcameraid';
  86. const ROUTE_ACTIVATE = 'activatecameraid';
  87. const ROUTE_DEACTIVATE = 'deactivatecameraid';
  88. const ROUTE_AJ_ARCHSTATS = 'renderarchivestats';
  89. const CHANNEL_ID_LEN = 11; // 4.738 * 10^18
  90. /**
  91. * Dinosaurs are my best friends
  92. * Through thick and thin, until the very end
  93. * People tell me, do not pretend
  94. * Stop living in your made up world again
  95. * But the dinosaurs, they're real to me
  96. * They bring me up and make me happy
  97. * I wish that the world could see
  98. * The dinosaurs are a part of me
  99. */
  100. public function __construct() {
  101. $this->initMessages();
  102. $this->loadConfigs();
  103. $this->initCamerasDb();
  104. $this->initCamOptsDb();
  105. $this->loadAllCamOpts();
  106. $this->initStorages();
  107. $this->initModels();
  108. $this->loadAllCameras();
  109. }
  110. /**
  111. * Inits system messages helper
  112. *
  113. * @return void
  114. */
  115. protected function initMessages() {
  116. $this->messages = new UbillingMessageHelper();
  117. }
  118. /**
  119. * Loads all required configs
  120. *
  121. * @global object $ubillingConfig
  122. *
  123. * @return void
  124. */
  125. protected function loadConfigs() {
  126. global $ubillingConfig;
  127. $this->altCfg = $ubillingConfig->getAlter();
  128. $this->binPaths = $ubillingConfig->getBinPaths();
  129. }
  130. /**
  131. * Inits storages into protected prop for further usage
  132. *
  133. * @return void
  134. */
  135. protected function initStorages() {
  136. $this->storages = new Storages();
  137. }
  138. /**
  139. * Inits camera models in protected prop
  140. *
  141. * @return void
  142. */
  143. protected function initModels() {
  144. $this->models = new Models();
  145. }
  146. /**
  147. * Inits cameras database abstraction layer for further usage
  148. *
  149. * @return void
  150. */
  151. protected function initCamerasDb() {
  152. $this->camerasDb = new NyanORM(self::DATA_TABLE);
  153. }
  154. /**
  155. * Inits camopts abstraction layer for further usage
  156. *
  157. * @return void
  158. */
  159. protected function initCamOptsDb() {
  160. $this->camoptsDb = new NyanORM(self::OPTS_TABLE);
  161. }
  162. /**
  163. * Loads all existing cameras from database
  164. *
  165. * @return void
  166. */
  167. protected function loadAllCameras() {
  168. $this->camerasDb->orderBy('id', 'DESC');
  169. $this->allCameras = $this->camerasDb->getAll('id');
  170. }
  171. /**
  172. * Loads all existing cameras custom options from database
  173. *
  174. * @return void
  175. */
  176. protected function loadAllCamOpts() {
  177. $this->allCamOpts = $this->camoptsDb->getAll('cameraid');
  178. }
  179. /**
  180. * Changes comment for existing camera in database
  181. *
  182. * @param int $cameraId
  183. * @param string $comment
  184. *
  185. * @return void/string
  186. */
  187. public function saveComment($cameraId, $comment) {
  188. $result = '';
  189. $cameraId = ubRouting::filters($cameraId, 'int');
  190. $commentF = ubRouting::filters($comment, 'safe');
  191. if (isset($this->allCameras[$cameraId])) {
  192. $this->camerasDb->where('id', '=', $cameraId);
  193. $this->camerasDb->data('comment', $commentF);
  194. $this->camerasDb->save();
  195. log_register('CAMERA EDIT [' . $cameraId . '] COMMENT `' . $comment . '`');
  196. } else {
  197. $result .= __('Camera') . ' [' . $cameraId . '] ' . __('not exists');
  198. }
  199. return ($result);
  200. }
  201. /**
  202. * Saves changes in existing camera database record
  203. *
  204. * @param int $cameraId
  205. * @param int $modelId
  206. * @param string $ip
  207. * @param string $login
  208. * @param string $password
  209. * @param int $storageId
  210. * @param comment $comment
  211. *
  212. * @return void/string on error
  213. */
  214. public function save($cameraId, $modelId, $ip, $login, $password, $storageId, $comment = '') {
  215. $result = '';
  216. $cameraId = ubRouting::filters($cameraId, 'int');
  217. $modelId = ubRouting::filters($modelId, 'int');
  218. $ipF = ubRouting::filters($ip, 'mres');
  219. $loginF = ubRouting::filters($login, 'mres');
  220. $passwordF = ubRouting::filters($password, 'mres');
  221. $storageId = ubRouting::filters($storageId, 'int');
  222. $commentF = ubRouting::filters($comment, 'safe');
  223. if (isset($this->allCameras[$cameraId])) {
  224. $cameraData = $this->allCameras[$cameraId];
  225. if ($cameraData['active'] == 0) {
  226. $allStorages = $this->storages->getAllStorageNames();
  227. $allModels = $this->models->getAllModelNames();
  228. if (isset($allStorages[$storageId])) {
  229. $storageData = $this->storages->getStorageData($storageId);
  230. $storagePathValid = $this->storages->checkPath($storageData['path']);
  231. if ($storagePathValid) {
  232. if (isset($allModels[$modelId])) {
  233. if (zb_isIPValid($ipF)) {
  234. if (!empty($loginF) and !empty($passwordF)) {
  235. //storage migration?
  236. if ($cameraData['storageid'] != $storageId) {
  237. $this->storages->migrateChannel($storageId, $cameraData['channel']);
  238. }
  239. //updating db
  240. $this->camerasDb->where('id', '=', $cameraId);
  241. $this->camerasDb->data('modelid', $modelId);
  242. $this->camerasDb->data('ip', $ipF);
  243. $this->camerasDb->data('login', $loginF);
  244. $this->camerasDb->data('password', $passwordF);
  245. $this->camerasDb->data('storageid', $storageId);
  246. $this->camerasDb->data('comment', $commentF);
  247. $this->camerasDb->save();
  248. log_register('CAMERA EDIT [' . $cameraId . '] MODEL [' . $modelId . '] IP `' . $ip . '` STORAGE [' . $storageId . '] COMMENT `' . $comment . '`');
  249. } else {
  250. $result .= __('Login or password is empty');
  251. }
  252. } else {
  253. $result .= __('Wrong IP format') . ': `' . $ip . '`';
  254. }
  255. } else {
  256. $result .= __('Model') . ' [' . $modelId . '] ' . __('not exists');
  257. }
  258. } else {
  259. $result .= __('Storage path is not writable');
  260. }
  261. } else {
  262. $result .= __('Storage') . ' [' . $storageId . '] ' . __('not exists');
  263. }
  264. } else {
  265. $result .= __('Camera') . ' ' . __('Active') . '!';
  266. }
  267. } else {
  268. $result .= __('Camera') . ' [' . $cameraId . '] ' . __('not exists');
  269. }
  270. return ($result);
  271. }
  272. /**
  273. * Sets camera custom option value by its name
  274. *
  275. * @param int $cameraId
  276. * @param string $key
  277. * @param mixed $value
  278. *
  279. * @return void
  280. */
  281. protected function setCamOptsValue($cameraId, $key, $value) {
  282. $cameraId = ubRouting::filters($cameraId, 'int');
  283. $keyF = ubRouting::filters($key, 'mres');
  284. $valueF = ubRouting::filters($value, 'mres');
  285. if (isset($this->allCameras[$cameraId])) {
  286. $camOpts = $this->getCamOpts($cameraId);
  287. //no opts record exists?
  288. if (empty($camOpts)) {
  289. $this->createCamOpts($cameraId);
  290. }
  291. //setting new value
  292. $this->camoptsDb->data($keyF, $valueF);
  293. $this->camoptsDb->where('cameraid', '=', $cameraId);
  294. $this->camoptsDb->save();
  295. log_register('CAMOPTS CAMERA [' . $cameraId . '] SET `' . strtoupper($key) . '` ON `' . $value . '`');
  296. } else {
  297. log_register('CAMOPTS FAIL CAMERA [' . $cameraId . '] NOT EXISTS');
  298. }
  299. }
  300. /**
  301. * Sets camera custom RTSP port option
  302. *
  303. * @param int $cameraId
  304. * @param int $port
  305. * @return void
  306. */
  307. public function saveCamoptsRtspPort($cameraId, $port) {
  308. $cameraId = ubRouting::filters($cameraId, 'int');
  309. $port = ubRouting::filters($port, 'int');
  310. if (!is_numeric($port)) {
  311. $port = 0;
  312. }
  313. if (isset($this->allCameras[$cameraId])) {
  314. $this->setCamOptsValue($cameraId, 'rtspport', $port);
  315. }
  316. }
  317. /**
  318. * Returns unique channelId
  319. *
  320. * @return string
  321. */
  322. protected function getChannelId() {
  323. $result = '';
  324. $busyCnannelIds = array();
  325. if (!empty($this->allCameras)) {
  326. foreach ($this->allCameras as $io => $each) {
  327. $busyCnannelIds[$each['channel']] = $each['id'];
  328. }
  329. }
  330. $result = zb_rand_string(self::CHANNEL_ID_LEN);
  331. while (isset($busyCnannelIds[$result])) {
  332. $result = zb_rand_string(self::CHANNEL_ID_LEN);
  333. }
  334. return ($result);
  335. }
  336. /**
  337. * Returns all cameras channels as struct channelId=>cameraId
  338. *
  339. * @return array
  340. */
  341. public function getAllCamerasChannels() {
  342. $result = array();
  343. if (!empty($this->allCameras)) {
  344. foreach ($this->allCameras as $io => $each) {
  345. $result[$each['channel']] = $each['id'];
  346. }
  347. }
  348. return ($result);
  349. }
  350. /**
  351. * Returns camera ID by its channel
  352. *
  353. * @param string $channelId
  354. *
  355. * @return int/bool
  356. */
  357. public function getCameraIdByChannel($channelId) {
  358. $result = false;
  359. $allCameraChannels = $this->getAllCamerasChannels();
  360. if (isset($allCameraChannels[$channelId])) {
  361. $result = $allCameraChannels[$channelId];
  362. }
  363. return ($result);
  364. }
  365. /**
  366. * Checks is camera with some IP already registered or not?
  367. *
  368. * @param string $ip
  369. *
  370. * @return bool
  371. */
  372. protected function isCameraIpUsed($ip) {
  373. $result = false;
  374. if (!empty($this->allCameras)) {
  375. foreach ($this->allCameras as $io => $each) {
  376. if ($ip == $each['ip']) {
  377. $result = true;
  378. }
  379. }
  380. }
  381. return ($result);
  382. }
  383. /**
  384. * Creates new camera
  385. *
  386. * @param int $modelId
  387. * @param string $ip
  388. * @param string $login
  389. * @param string $password
  390. * @param bool $active
  391. * @param int $storageId
  392. * @param comment $comment
  393. *
  394. * @return void/string on error
  395. */
  396. public function create($modelId, $ip, $login, $password, $active, $storageId, $comment = '') {
  397. $result = '';
  398. $modelId = ubRouting::filters($modelId, 'int');
  399. $ipF = ubRouting::filters($ip, 'mres');
  400. $loginF = ubRouting::filters($login, 'mres');
  401. $passwordF = ubRouting::filters($password, 'mres');
  402. $actF = ($active) ? 1 : 0;
  403. $storageId = ubRouting::filters($storageId, 'int');
  404. //automatic storage selection?
  405. if ($storageId == 0) {
  406. $storageId = $this->storages->getLeastUsedStorage();
  407. }
  408. $commentF = ubRouting::filters($comment, 'safe');
  409. $channelId = $this->getChannelId();
  410. $allStorages = $this->storages->getAllStorageNames();
  411. $allModels = $this->models->getAllModelNames();
  412. if (isset($allStorages[$storageId])) {
  413. $storageData = $this->storages->getStorageData($storageId);
  414. $storagePathValid = $this->storages->checkPath($storageData['path']);
  415. if ($storagePathValid) {
  416. if (isset($allModels[$modelId])) {
  417. if (zb_isIPValid($ipF)) {
  418. if (!$this->isCameraIpUsed($ipF)) {
  419. if (!empty($loginF) and !empty($passwordF)) {
  420. $this->camerasDb->data('modelid', $modelId);
  421. $this->camerasDb->data('ip', $ipF);
  422. $this->camerasDb->data('login', $loginF);
  423. $this->camerasDb->data('password', $passwordF);
  424. $this->camerasDb->data('active', $actF);
  425. $this->camerasDb->data('storageid', $storageId);
  426. $this->camerasDb->data('channel', $channelId);
  427. $this->camerasDb->data('comment', $commentF);
  428. $this->camerasDb->create();
  429. $newId = $this->camerasDb->getLastId();
  430. log_register('CAMERA CREATE [' . $newId . '] MODEL [' . $modelId . '] IP `' . $ip . '` STORAGE [' . $storageId . '] COMMENT `' . $comment . '`');
  431. //custom options new empty record creation
  432. $this->createCamOpts($newId);
  433. } else {
  434. $result .= __('Login or password is empty');
  435. }
  436. } else {
  437. $result .= __('Camera IP already registered');
  438. }
  439. } else {
  440. $result .= __('Wrong IP format') . ': `' . $ip . '`';
  441. }
  442. } else {
  443. $result .= __('Model') . ' [' . $modelId . '] ' . __('not exists');
  444. }
  445. } else {
  446. $result .= __('Storage path is not writable');
  447. }
  448. } else {
  449. $result .= __('Storage') . ' [' . $storageId . '] ' . __('not exists');
  450. }
  451. return ($result);
  452. }
  453. /**
  454. * Returns running cameras recording processes as cameraId=>realPid
  455. *
  456. * @return array
  457. */
  458. protected function getRunningRecorders() {
  459. $result = array();
  460. $recorderPids = array();
  461. $command = $this->binPaths['PS'] . ' ax | ' . $this->binPaths['GREP'] . ' ' . $this->binPaths['FFMPG_PATH'] . ' | ' . $this->binPaths['GREP'] . ' -v grep';
  462. $rawResult = shell_exec($command);
  463. if (!empty($rawResult)) {
  464. $rawResult = explodeRows($rawResult);
  465. foreach ($rawResult as $io => $eachLine) {
  466. $eachLine = trim($eachLine);
  467. $rawLine = $eachLine;
  468. $eachLine = explode(' ', $eachLine);
  469. if (isset($eachLine[0])) {
  470. $eachPid = $eachLine[0];
  471. if (is_numeric($eachPid)) {
  472. //is this really capture process?
  473. if (ispos($rawLine, $this->binPaths['FFMPG_PATH']) and ispos($rawLine, 'segment_format')) {
  474. $recorderPids[$eachPid] = $rawLine;
  475. }
  476. }
  477. }
  478. }
  479. }
  480. if (!empty($this->allCameras)) {
  481. if (!empty($recorderPids)) {
  482. $fullCamerasData = $this->getAllCamerasFullData();
  483. foreach ($fullCamerasData as $eachCameraId => $eachCameraData) {
  484. foreach ($recorderPids as $eachPid => $eachProcess) {
  485. $camIp = $eachCameraData['CAMERA']['ip'];
  486. $camLogin = $eachCameraData['CAMERA']['login'];
  487. $camPass = $eachCameraData['CAMERA']['password'];
  488. $camPort = $eachCameraData['TEMPLATE']['RTSP_PORT'];
  489. if (isset($eachCameraData['OPTS'])) {
  490. if (!empty($eachCameraData['OPTS']['rtspport'])) {
  491. $camPort = $eachCameraData['OPTS']['rtspport'];
  492. }
  493. }
  494. //looks familiar?
  495. if (ispos($eachProcess, $camIp) and ispos($eachProcess, $camLogin) and ispos($eachProcess, $camPass) and ispos($eachProcess, ':' . $camPort)) {
  496. $result[$eachCameraId] = $eachPid;
  497. }
  498. }
  499. }
  500. }
  501. }
  502. return ($result);
  503. }
  504. /**
  505. * Returns running cameras live stream processes as cameraId=>realPid
  506. *
  507. * @return array
  508. */
  509. protected function getRunningStreams() {
  510. $result = array();
  511. $liveStreamsPids = array();
  512. $command = $this->binPaths['PS'] . ' ax | ' . $this->binPaths['GREP'] . ' ' . $this->binPaths['FFMPG_PATH'] . ' | ' . $this->binPaths['GREP'] . ' -v grep';
  513. $rawResult = shell_exec($command);
  514. if (!empty($rawResult)) {
  515. $rawResult = explodeRows($rawResult);
  516. foreach ($rawResult as $io => $eachLine) {
  517. $eachLine = trim($eachLine);
  518. $rawLine = $eachLine;
  519. $eachLine = explode(' ', $eachLine);
  520. if (isset($eachLine[0])) {
  521. $eachPid = $eachLine[0];
  522. if (is_numeric($eachPid)) {
  523. //is this really live stream process?
  524. if (ispos($rawLine, 'hls') and ispos($rawLine, LiveCams::STREAM_PLAYLIST)) {
  525. $liveStreamsPids[$eachPid] = $rawLine;
  526. }
  527. }
  528. }
  529. }
  530. }
  531. if (!empty($this->allCameras)) {
  532. if (!empty($liveStreamsPids)) {
  533. $fullCamerasData = $this->getAllCamerasFullData();
  534. foreach ($fullCamerasData as $eachCameraId => $eachCameraData) {
  535. foreach ($liveStreamsPids as $eachPid => $eachProcess) {
  536. $camIp = $eachCameraData['CAMERA']['ip'];
  537. $camLogin = $eachCameraData['CAMERA']['login'];
  538. $camPass = $eachCameraData['CAMERA']['password'];
  539. $camPort = $eachCameraData['TEMPLATE']['RTSP_PORT'];
  540. if (isset($eachCameraData['OPTS'])) {
  541. if (!empty($eachCameraData['OPTS']['rtspport'])) {
  542. $camPort = $eachCameraData['OPTS']['rtspport'];
  543. }
  544. }
  545. //looks familiar?
  546. if (ispos($eachProcess, $camIp) and ispos($eachProcess, $camLogin) and ispos($eachProcess, $camPass) and ispos($eachProcess, ':' . $camPort)) {
  547. $result[$eachCameraId] = $eachPid;
  548. }
  549. }
  550. }
  551. }
  552. }
  553. return ($result);
  554. }
  555. /**
  556. * Returns running cameras live sub-stream processes as cameraId=>realPid
  557. *
  558. * @return array
  559. */
  560. protected function getRunningSubStreams() {
  561. $result = array();
  562. $liveStreamsPids = array();
  563. $command = $this->binPaths['PS'] . ' ax | ' . $this->binPaths['GREP'] . ' ' . $this->binPaths['FFMPG_PATH'] . ' | ' . $this->binPaths['GREP'] . ' -v grep';
  564. $rawResult = shell_exec($command);
  565. if (!empty($rawResult)) {
  566. $rawResult = explodeRows($rawResult);
  567. foreach ($rawResult as $io => $eachLine) {
  568. $eachLine = trim($eachLine);
  569. $rawLine = $eachLine;
  570. $eachLine = explode(' ', $eachLine);
  571. if (isset($eachLine[0])) {
  572. $eachPid = $eachLine[0];
  573. if (is_numeric($eachPid)) {
  574. //is this really live stream process?
  575. if (ispos($rawLine, 'hls') and ispos($rawLine, LiveCams::SUBSTREAM_PLAYLIST)) {
  576. $liveStreamsPids[$eachPid] = $rawLine;
  577. }
  578. }
  579. }
  580. }
  581. }
  582. if (!empty($this->allCameras)) {
  583. if (!empty($liveStreamsPids)) {
  584. $fullCamerasData = $this->getAllCamerasFullData();
  585. foreach ($fullCamerasData as $eachCameraId => $eachCameraData) {
  586. foreach ($liveStreamsPids as $eachPid => $eachProcess) {
  587. $camIp = $eachCameraData['CAMERA']['ip'];
  588. $camLogin = $eachCameraData['CAMERA']['login'];
  589. $camPass = $eachCameraData['CAMERA']['password'];
  590. $camPort = $eachCameraData['TEMPLATE']['RTSP_PORT'];
  591. if (isset($eachCameraData['OPTS'])) {
  592. if (!empty($eachCameraData['OPTS']['rtspport'])) {
  593. $camPort = $eachCameraData['OPTS']['rtspport'];
  594. }
  595. }
  596. //looks familiar?
  597. if (ispos($eachProcess, $camIp) and ispos($eachProcess, $camLogin) and ispos($eachProcess, $camPass) and ispos($eachProcess, ':' . $camPort)) {
  598. $result[$eachCameraId] = $eachPid;
  599. }
  600. }
  601. }
  602. }
  603. }
  604. return ($result);
  605. }
  606. /**
  607. * Flushes camera custom options database record
  608. *
  609. * @param int $cameraId
  610. * @return void
  611. */
  612. protected function flushCamOpts($cameraId) {
  613. $cameraId = ubRouting::filters($cameraId, 'int');
  614. $this->camoptsDb->where('cameraid', '=', $cameraId);
  615. $this->camoptsDb->delete();
  616. log_register('CAMOPTS FLUSH CAMERA [' . $cameraId . ']');
  617. }
  618. /**
  619. * Deletes existing camera from database
  620. *
  621. * @param int $cameraId
  622. *
  623. * @return void/string on error
  624. */
  625. public function delete($cameraId) {
  626. $result = '';
  627. $cameraId = ubRouting::filters($cameraId, 'int');
  628. //TODO: do something around camera deactivation and checks for running recording
  629. if (isset($this->allCameras[$cameraId])) {
  630. $cameraData = $this->allCameras[$cameraId];
  631. if ($cameraData['active'] == 0) {
  632. //flushing camera ACLs
  633. $acl = new ACL();
  634. $acl->flushCamera($cameraId);
  635. //deleting camera database record
  636. $this->camerasDb->where('id', '=', $cameraId);
  637. $this->camerasDb->delete();
  638. log_register('CAMERA DELETE [' . $cameraId . ']');
  639. //flushing camera channel
  640. $this->storages->flushChannel($cameraData['storageid'], $cameraData['channel']);
  641. //flushing camera custom options
  642. $this->flushCamOpts($cameraId);
  643. } else {
  644. $result .= __('You cant delete camera which is now active');
  645. }
  646. } else {
  647. $result .= __('Camera') . ' [' . $cameraId . '] ' . __('not exists');
  648. }
  649. return ($result);
  650. }
  651. /**
  652. * Returns full cameras data with all info required for recorder as struct id=>[CAMERA,TEMPLATE,STORAGE]
  653. *
  654. * @return array
  655. */
  656. public function getAllCamerasFullData() {
  657. $result = array();
  658. if (!empty($this->allCameras)) {
  659. $allModelsTemplates = $this->models->getAllModelTemplates();
  660. $allStoragesData = $this->storages->getAllStoragesData();
  661. foreach ($this->allCameras as $io => $each) {
  662. $result[$each['id']]['CAMERA'] = $each;
  663. $result[$each['id']]['TEMPLATE'] = $allModelsTemplates[$each['modelid']];
  664. $result[$each['id']]['STORAGE'] = $allStoragesData[$each['storageid']];
  665. $result[$each['id']]['OPTS'] = $this->getCamOpts($each['id']);
  666. }
  667. }
  668. return ($result);
  669. }
  670. /**
  671. * Retrieves the camera options for a specific camera.
  672. *
  673. * @param int $cameraId The ID of the camera.
  674. * @return array The camera options for the specified camera.
  675. */
  676. public function getCamOpts($cameraId) {
  677. $result = array();
  678. if (isset($this->allCamOpts[$cameraId])) {
  679. $result = $this->allCamOpts[$cameraId];
  680. }
  681. return ($result);
  682. }
  683. /**
  684. * Shutdown camera to unlock its settings
  685. *
  686. * @param int $cameraId
  687. *
  688. * @return void/string
  689. */
  690. public function deactivate($cameraId) {
  691. $result = '';
  692. $cameraId = ubRouting::filters($cameraId, 'int');
  693. if (isset($this->allCameras[$cameraId])) {
  694. $recorder = new Recorder();
  695. //shutdown recording process if it runs
  696. $recorder->stopRecord($cameraId); //this method locks execution until capture process will be really killed
  697. //shutdown camera live streams if it runs
  698. $liveCams = new LiveCams();
  699. $streamStopResult = $liveCams->stopStream($cameraId);
  700. $subStreamStopResult = $liveCams->stopSubStream($cameraId);
  701. if ($streamStopResult) {
  702. log_register('LIVESTREAM STOPPED [' . $cameraId . ']');
  703. }
  704. if ($streamStopResult) {
  705. log_register('SUBSTREAM STOPPED [' . $cameraId . ']');
  706. }
  707. //disabling camera activity flag
  708. $this->camerasDb->where('id', '=', $cameraId);
  709. $this->camerasDb->data('active', 0);
  710. $this->camerasDb->save();
  711. log_register('CAMERA DEACTIVATE [' . $cameraId . ']');
  712. } else {
  713. $result = __('Camera') . ' [' . $cameraId . '] ' . __('not exists');
  714. }
  715. return ($result);
  716. }
  717. /**
  718. * Enables camera to lock its settings
  719. *
  720. * @param int $cameraId
  721. *
  722. * @return void/string
  723. */
  724. public function activate($cameraId) {
  725. $result = '';
  726. $cameraId = ubRouting::filters($cameraId, 'int');
  727. if (isset($this->allCameras[$cameraId])) {
  728. //enabling camera activity flag
  729. $this->camerasDb->where('id', '=', $cameraId);
  730. $this->camerasDb->data('active', 1);
  731. $this->camerasDb->save();
  732. $this->allCameras[$cameraId]['active'] = 1;
  733. log_register('CAMERA ACTIVATE [' . $cameraId . ']');
  734. //starting capture now if enabled
  735. if ($this->altCfg['RECORDER_ON_CAMERA_ACTIVATION']) {
  736. $recorder = new Recorder();
  737. $recorder->runRecordBackground($cameraId);
  738. }
  739. } else {
  740. $result = __('Camera') . ' [' . $cameraId . '] ' . __('not exists');
  741. }
  742. return ($result);
  743. }
  744. /**
  745. * Returns camera comment or IP by its channelId
  746. *
  747. * @param string $channelId
  748. *
  749. * @return string
  750. */
  751. public function getCameraComment($channelId) {
  752. $result = '';
  753. if (!empty($this->allCameras)) {
  754. foreach ($this->allCameras as $io => $each) {
  755. if ($each['channel'] == $channelId) {
  756. if (!empty($each['comment'])) {
  757. $result = $each['comment'];
  758. } else {
  759. $result = $each['ip'];
  760. }
  761. }
  762. }
  763. }
  764. if (empty($result)) {
  765. $result = __('Lost');
  766. }
  767. return ($result);
  768. }
  769. /**
  770. * Returns camera comment or IP by its cameraId
  771. *
  772. * @param string $cameraId
  773. *
  774. * @return string
  775. */
  776. public function getCameraCommentById($cameraId) {
  777. $result = '';
  778. if (!empty($this->allCameras)) {
  779. foreach ($this->allCameras as $io => $each) {
  780. if ($each['id'] == $cameraId) {
  781. if (!empty($each['comment'])) {
  782. $result = $each['comment'];
  783. } else {
  784. $result = $each['ip'];
  785. }
  786. }
  787. }
  788. }
  789. if (empty($result)) {
  790. $result = __('Lost');
  791. }
  792. return ($result);
  793. }
  794. /**
  795. * Checks is camera with some IP registered or not?
  796. *
  797. * @param string $ip
  798. *
  799. * @return int/bool
  800. */
  801. public function isRegisteredIp($ip) {
  802. $result = false;
  803. if (!empty($this->allCameras)) {
  804. foreach ($this->allCameras as $io => $each) {
  805. if ($each['ip'] == $ip) {
  806. $result = $each['id'];
  807. break;
  808. }
  809. }
  810. }
  811. return ($result);
  812. }
  813. /**
  814. * Creates new empty custom camera options record
  815. *
  816. * @param int $cameraId
  817. *
  818. * @return void
  819. */
  820. protected function createCamOpts($cameraId) {
  821. $cameraId = ubRouting::filters($cameraId, 'int');
  822. if (!isset($this->allCamOpts[$cameraId])) {
  823. $this->camoptsDb->data('cameraid', $cameraId);
  824. $this->camoptsDb->create();
  825. log_register('CAMOPTS CREATE [' . $cameraId . ']');
  826. }
  827. }
  828. /**
  829. * Returns camera creation form
  830. *
  831. * @return string
  832. */
  833. public function renderCreateForm() {
  834. $result = '';
  835. $allStorages = $this->storages->getAllStorageNames();
  836. $allModels = $this->models->getAllModelNames();
  837. if (!empty($allStorages)) {
  838. $storagesParams = array();
  839. $storagesParams = array(0 => __('Auto'));
  840. foreach ($allStorages as $eachStorageId => $eachStorageName) {
  841. $storagesParams[$eachStorageId] = __($eachStorageName);
  842. }
  843. if (!empty($allModels)) {
  844. $inputs = wf_Selector(self::PROUTE_NEWMODEL, $allModels, __('Model'), '', true) . ' ';
  845. $inputs .= wf_TextInput(self::PROUTE_NEWIP, __('IP'), '', true, 12, 'ip') . ' ';
  846. $inputs .= wf_TextInput(self::PROUTE_NEWLOGIN, __('Login'), '', true, 14, 'alphanumeric') . ' ';
  847. $inputs .= wf_PasswordInput(self::PROUTE_NEWPASS, __('Password'), '', true, 14) . ' ';
  848. $inputs .= wf_CheckInput(self::PROUTE_NEWACT, __('Enabled'), true, true) . ' ';
  849. $inputs .= wf_Selector(self::PROUTE_NEWSTORAGE, $storagesParams, __('Storage'), '', true) . ' ';
  850. $inputs .= wf_TextInput(self::PROUTE_NEWCOMMENT, __('Description'), '', true, 18, '') . ' ';
  851. $inputs .= wf_Submit(__('Create'));
  852. $result .= wf_Form('', 'POST', $inputs, 'glamour');
  853. } else {
  854. $result .= $this->messages->getStyledMessage(__('Any device models exists'), 'error');
  855. }
  856. } else {
  857. $result .= $this->messages->getStyledMessage(__('Any storages exists'), 'error');
  858. }
  859. return ($result);
  860. }
  861. /**
  862. * Returns camera editing form
  863. *
  864. * @param int $cameraId
  865. *
  866. * @return string
  867. */
  868. public function renderEditForm($cameraId) {
  869. $result = '';
  870. $cameraId = ubRouting::filters($cameraId, 'int');
  871. $allStorages = $this->storages->getAllStorageNames();
  872. $allModels = $this->models->getAllModelNames();
  873. if (isset($this->allCameras[$cameraId])) {
  874. $cameraData = $this->allCameras[$cameraId];
  875. $camOpts = $this->getCamOpts($cameraId);
  876. if (!empty($allStorages)) {
  877. $storagesParams = array();
  878. foreach ($allStorages as $eachStorageId => $eachStorageName) {
  879. $storagesParams[$eachStorageId] = __($eachStorageName);
  880. }
  881. if (!empty($allModels)) {
  882. $custRtspPort = (!empty($camOpts['rtspport'])) ? $camOpts['rtspport'] : '';
  883. $inputs = wf_HiddenInput(self::PROUTE_ED_CAMERAID, $cameraId);
  884. $inputs .= wf_Selector(self::PROUTE_ED_MODEL, $allModels, __('Model'), $cameraData['modelid'], true) . ' ';
  885. $inputs .= wf_TextInput(self::PROUTE_ED_IP, __('IP'), $cameraData['ip'], true, 12, 'ip') . ' ';
  886. $inputs .= wf_TextInput(self::PROUTE_ED_LOGIN, __('Login'), $cameraData['login'], true, 14, 'alphanumeric') . ' ';
  887. $inputs .= wf_PasswordInput(self::PROUTE_ED_PASS, __('Password'), $cameraData['password'], true, 14) . ' ';
  888. $inputs .= wf_TextInput(self::PROUTE_ED_CUSTPORT, __('Custom RTSP port'), $custRtspPort, true, 4, 'digits');
  889. $inputs .= wf_Selector(self::PROUTE_ED_STORAGE, $storagesParams, __('Storage'), $cameraData['storageid'], true) . ' ';
  890. $inputs .= wf_TextInput(self::PROUTE_ED_COMMENT, __('Description'), $cameraData['comment'], true, 18, '') . ' ';
  891. $inputs .= wf_Submit(__('Save'));
  892. $result .= wf_Form('', 'POST', $inputs, 'glamour');
  893. } else {
  894. $result .= $this->messages->getStyledMessage(__('Any device models exists'), 'error');
  895. }
  896. } else {
  897. $result .= $this->messages->getStyledMessage(__('Any storages exists'), 'error');
  898. }
  899. } else {
  900. $result .= $this->messages->getStyledMessage(__('Camera') . ' [' . $cameraId . '] ' . __('not exists'), 'error');
  901. }
  902. return ($result);
  903. }
  904. /**
  905. * Returns camera comments editing form
  906. *
  907. * @param int $cameraId
  908. *
  909. * @return string
  910. */
  911. public function renderRenameForm($cameraId) {
  912. $result = '';
  913. $cameraId = ubRouting::filters($cameraId, 'int');
  914. if (isset($this->allCameras[$cameraId])) {
  915. $cameraData = $this->allCameras[$cameraId];
  916. $inputs = wf_HiddenInput(self::PROUTE_ED_CAMERAID_ACT, $cameraId);
  917. $inputs .= wf_TextInput(self::PROUTE_ED_COMMENT_ACT, __('Description'), $cameraData['comment'], true, 20, '') . ' ';
  918. $inputs .= wf_Submit(__('Save'));
  919. $result .= wf_Form('', 'POST', $inputs, 'glamour');
  920. $result .= __('You can only change the description of cameras that are active at this moment');
  921. } else {
  922. $result .= $this->messages->getStyledMessage(__('Camera') . ' [' . $cameraId . '] ' . __('not exists'), 'error');
  923. }
  924. return ($result);
  925. }
  926. /**
  927. * Renders available cameras list
  928. *
  929. * @return string
  930. */
  931. public function renderList() {
  932. $result = '';
  933. if (!empty($this->allCameras)) {
  934. $totalCount = 0;
  935. $enabledCount = 0;
  936. $recorderCount = 0;
  937. $liveCount = 0;
  938. $allRunningRecorders = $this->getRunningRecorders();
  939. $allLiveStreams = $this->getRunningStreams();
  940. $cells = wf_TableCell(__('ID'));
  941. $cells .= wf_TableCell(__('IP'));
  942. $cells .= wf_TableCell(__('Enabled'));
  943. $cells .= wf_TableCell(__('Recording'));
  944. $cells .= wf_TableCell(__('Live'));
  945. $cells .= wf_TableCell(__('Description'));
  946. $cells .= wf_TableCell(__('Actions'));
  947. $rows = wf_TableRow($cells, 'row1');
  948. foreach ($this->allCameras as $io => $each) {
  949. $cells = wf_TableCell($each['id']);
  950. $cells .= wf_TableCell($each['ip'], '', '', 'sorttable_customkey="' . ip2int($each['ip']) . '"');
  951. $cells .= wf_TableCell(web_bool_led($each['active']), '', '', 'sorttable_customkey="' . $each['active'] . '"');
  952. $recordingFlag = isset($allRunningRecorders[$each['id']]) ? 1 : 0;
  953. $cells .= wf_TableCell(web_bool_led($recordingFlag), '', '', 'sorttable_customkey="' . $recordingFlag . '"');
  954. $liveFlag = isset($allLiveStreams[$each['id']]) ? 1 : 0;
  955. $cells .= wf_TableCell(web_bool_led($liveFlag), '', '', 'sorttable_customkey="' . $liveFlag . '"');
  956. $cells .= wf_TableCell($each['comment']);
  957. $actLinks = wf_Link(self::URL_ME . '&' . self::ROUTE_EDIT . '=' . $each['id'], web_edit_icon(), false);
  958. $cells .= wf_TableCell($actLinks);
  959. $rows .= wf_TableRow($cells, 'row5');
  960. $totalCount++;
  961. if ($each['active']) {
  962. $enabledCount++;
  963. if ($recordingFlag) {
  964. $recorderCount++;
  965. }
  966. if ($liveFlag) {
  967. $liveCount++;
  968. }
  969. }
  970. }
  971. $result .= wf_TableBody($rows, '100%', 0, 'sortable resp-table');
  972. $result .= wf_tag('b') . __('Total') . ': ' . $totalCount . wf_tag('b', true);
  973. if ($totalCount) {
  974. $result .= wf_delimiter(0);
  975. $result .= __('Enabled') . '/' . __('Recording') . '/' . __('Live') . ': (' . $enabledCount . '/' . $recorderCount . '/' . $liveCount . ')';
  976. }
  977. } else {
  978. $result .= $this->messages->getStyledMessage(__('Nothing to show'), 'info');
  979. }
  980. return ($result);
  981. }
  982. /**
  983. * Renders camera archive stats, like depth, bitrate, size...
  984. *
  985. * @param array $cameraId
  986. *
  987. * @return string
  988. */
  989. public function renderCameraArchiveStats($cameraId) {
  990. $result = '';
  991. $cameraId = ubRouting::filters($cameraId, 'int');
  992. if (isset($this->allCameras[$cameraId])) {
  993. $cameraData = $this->allCameras[$cameraId];
  994. $rows = '';
  995. //some channel data collecting
  996. $channelChunks = $this->storages->getChannelChunks($cameraData['storageid'], $cameraData['channel']);
  997. $chunksCount = sizeof($channelChunks);
  998. $archiveDepth = '-';
  999. $archiveSeconds = 0;
  1000. if ($chunksCount > 0) {
  1001. $archiveSeconds = $this->altCfg['RECORDER_CHUNK_TIME'] * $chunksCount;
  1002. $archiveDepth = wr_formatTimeArchive($archiveSeconds);
  1003. }
  1004. $chanSizeRaw = $this->storages->getChannelChunksSize($channelChunks);
  1005. $chanSizeLabel = wr_convertSize($chanSizeRaw);
  1006. $chanBitrateLabel = '-';
  1007. if ($archiveSeconds and $chanSizeRaw) {
  1008. $chanBitrate = ($chanSizeRaw * 8) / $archiveSeconds / 1024; // in kbits
  1009. $chanBitrateLabel = round(($chanBitrate / 1024), 2) . ' ' . __('Mbit/s');
  1010. }
  1011. $cells = wf_TableCell(__('Archive depth'), '40%', 'row2');
  1012. $cells .= wf_TableCell($archiveDepth);
  1013. $rows .= wf_TableRow($cells, 'row3');
  1014. $cells = wf_TableCell(__('Average bitrate'), '', 'row2');
  1015. $cells .= wf_TableCell($chanBitrateLabel);
  1016. $rows .= wf_TableRow($cells, 'row3');
  1017. $cells = wf_TableCell(__('Size'), '', 'row2');
  1018. $cells .= wf_TableCell($chanSizeLabel);
  1019. $rows .= wf_TableRow($cells, 'row3');
  1020. $result .= wf_TableBody($rows, '100%', 0, 'resp-table');
  1021. }
  1022. return ($result);
  1023. }
  1024. /**
  1025. * Renders camera profile
  1026. *
  1027. * @param int $cameraId
  1028. *
  1029. * @return string
  1030. */
  1031. public function renderCameraProfile($cameraId) {
  1032. $result = '';
  1033. $cameraControls = '';
  1034. $cameraId = ubRouting::filters($cameraId, 'int');
  1035. if (isset($this->allCameras[$cameraId])) {
  1036. $cameraData = $this->allCameras[$cameraId];
  1037. $allModels = $this->models->getAllModelNames();
  1038. $allStorages = $this->storages->getAllStorageNames();
  1039. $allTemplates = $this->models->getAllModelTemplates();
  1040. $camOpt = $this->getCamOpts($cameraId);
  1041. $cameraTemplate = $allTemplates[$cameraData['modelid']];
  1042. $acl = new ACL();
  1043. $portLabel = '';
  1044. //model template rtsp port
  1045. $rtspPort = (!empty($cameraTemplate['RTSP_PORT'])) ? $cameraTemplate['RTSP_PORT'] : 554;
  1046. //is custom rtsp port used?
  1047. if (!empty($camOpt)) {
  1048. if ($camOpt['rtspport']) {
  1049. $rtspPort = $camOpt['rtspport'];
  1050. $portLabel = ' ⚙️';
  1051. }
  1052. }
  1053. //recorder process now is running?
  1054. $allRunningRecorders = $this->getRunningRecorders();
  1055. $recordingFlag = (isset($allRunningRecorders[$cameraId])) ? 1 : 0;
  1056. //live stream process now is running?
  1057. $allRunningLiveStreams = $this->getRunningStreams();
  1058. $liveStreamFlag = (isset($allRunningLiveStreams[$cameraId])) ? 1 : 0;
  1059. //live substreams live-well process is running?
  1060. $allRunningSubStreams = $this->getRunningSubStreams();
  1061. $subStreamFlag = (isset($allRunningSubStreams[$cameraId])) ? 1 : 0;
  1062. $ajaxArchiveStatsUrl = self::URL_ME . '&' . self::ROUTE_AJ_ARCHSTATS . '=' . $cameraId;
  1063. $channelLabel = $cameraData['channel'];
  1064. if (cfr('ARCHIVE')) {
  1065. $channelLabel = wf_AjaxLink($ajaxArchiveStatsUrl, $cameraData['channel'], self::AJ_ARCHSTATS);
  1066. }
  1067. //ACL users access
  1068. $aclUsersList = '';
  1069. $rawAcls = $acl->getAllCameraAclsData();
  1070. if (!empty($rawAcls)) {
  1071. foreach ($rawAcls as $eachUser => $accessibleCameras) {
  1072. if (isset($accessibleCameras[$cameraId])) {
  1073. $aclUsersList .= $eachUser . ' ';
  1074. }
  1075. }
  1076. }
  1077. $aclUsersList = (empty($aclUsersList)) ? '-' : $aclUsersList;
  1078. //camera profile here
  1079. $cells = wf_TableCell(__('Model'), '40%', 'row2');
  1080. $cells .= wf_TableCell($allModels[$cameraData['modelid']]);
  1081. $rows = wf_TableRow($cells, 'row3');
  1082. $cells = wf_TableCell(__('IP'), '', 'row2');
  1083. $cells .= wf_TableCell($cameraData['ip']);
  1084. $rows .= wf_TableRow($cells, 'row3');
  1085. $cells = wf_TableCell(__('Login'), '', 'row2');
  1086. $cells .= wf_TableCell($cameraData['login']);
  1087. $rows .= wf_TableRow($cells, 'row3');
  1088. $cells = wf_TableCell(__('Password'), '', 'row2');
  1089. $cells .= wf_TableCell($cameraData['password']);
  1090. $rows .= wf_TableRow($cells, 'row3');
  1091. $cells = wf_TableCell(__('RTSP') . ' ' . __('Port'), '', 'row2');
  1092. $cells .= wf_TableCell($rtspPort . $portLabel);
  1093. $rows .= wf_TableRow($cells, 'row3');
  1094. $cells = wf_TableCell(__('Enabled'), '', 'row2');
  1095. $cells .= wf_TableCell(web_bool_led($cameraData['active']));
  1096. $rows .= wf_TableRow($cells, 'row3');
  1097. $cells = wf_TableCell(__('Recording'), '', 'row2');
  1098. $cells .= wf_TableCell(web_bool_led($recordingFlag));
  1099. $rows .= wf_TableRow($cells, 'row3');
  1100. $cells = wf_TableCell(__('Live'), '', 'row2');
  1101. $cells .= wf_TableCell(web_bool_led($liveStreamFlag));
  1102. $rows .= wf_TableRow($cells, 'row3');
  1103. $cells = wf_TableCell(__('Substream'), '', 'row2');
  1104. $cells .= wf_TableCell(web_bool_led($subStreamFlag));
  1105. $rows .= wf_TableRow($cells, 'row3');
  1106. $cells = wf_TableCell(__('Description'), '', 'row2');
  1107. $cells .= wf_TableCell($cameraData['comment']);
  1108. $rows .= wf_TableRow($cells, 'row3');
  1109. $cells = wf_TableCell(__('Storage'), '', 'row2');
  1110. $cells .= wf_TableCell(__($allStorages[$cameraData['storageid']]));
  1111. $rows .= wf_TableRow($cells, 'row3');
  1112. $cells = wf_TableCell(__('Channel'), '', 'row2');
  1113. $cells .= wf_TableCell($channelLabel);
  1114. $rows .= wf_TableRow($cells, 'row3');
  1115. $cells = wf_TableCell(__('Users access'), '', 'row2');
  1116. $cells .= wf_TableCell($aclUsersList);
  1117. $rows .= wf_TableRow($cells, 'row3');
  1118. $result .= wf_TableBody($rows, '100%', 0, 'resp-table');
  1119. //archive stats container here
  1120. $result .= wf_AjaxLoader();
  1121. $result .= wf_AjaxContainer(self::AJ_ARCHSTATS);
  1122. //some controls here
  1123. if ($cameraData['active']) {
  1124. $deactUrl = self::URL_ME . '&' . self::ROUTE_EDIT . '=' . $cameraData['id'] . '&' . self::ROUTE_DEACTIVATE . '=' . $cameraData['id'];
  1125. $cameraControls .= wf_Link($deactUrl, web_bool_led(0) . ' ' . __('Disable'), false, 'ubButton') . ' ';
  1126. } else {
  1127. $cameraControls .= wf_Link(self::URL_ME . '&' . self::ROUTE_ACTIVATE . '=' . $cameraData['id'], web_bool_led(1) . ' ' . __('Enable'), false, 'ubButton') . ' ';
  1128. }
  1129. if ($cameraData['active']) {
  1130. if (cfr('LIVECAMS')) {
  1131. $cameraControls .= wf_Link(LiveCams::URL_ME . '&' . LiveCams::ROUTE_VIEW . '=' . $cameraData['channel'], wf_img('skins/icon_live_small.png') . ' ' . __('Live'), false, 'ubButton');
  1132. }
  1133. }
  1134. if (cfr('ARCHIVE')) {
  1135. $cameraControls .= wf_Link(Archive::URL_ME . '&' . Archive::ROUTE_VIEW . '=' . $cameraData['channel'], wf_img('skins/icon_archive_small.png') . ' ' . __('Video from camera'), false, 'ubButton');
  1136. }
  1137. if (cfr('EXPORT')) {
  1138. $cameraControls .= wf_Link(Export::URL_ME . '&' . Export::ROUTE_CHANNEL . '=' . $cameraData['channel'], wf_img('skins/icon_export.png') . ' ' . __('Save record'), false, 'ubButton');
  1139. }
  1140. if (cfr('ARCHIVE')) {
  1141. $cameraControls .= wf_AjaxLink($ajaxArchiveStatsUrl, wf_img('skins/icon_charts.png') . ' ' . __('Archive'), self::AJ_ARCHSTATS, false, 'ubButton');
  1142. }
  1143. if (!$cameraData['active']) {
  1144. //editing interface here
  1145. $editingForm = $this->renderEditForm($cameraId);
  1146. $cameraControls .= wf_modalAuto(web_edit_icon() . ' ' . __('Edit'), __('Edit camera') . ': ' . $cameraData['comment'], $editingForm, 'ubButton');
  1147. //deletion interface here
  1148. $deletionUrl = self::URL_ME . '&' . self::ROUTE_DEL . '=' . $cameraId;
  1149. $cancelUrl = self::URL_ME . '&' . self::ROUTE_EDIT . '=' . $cameraId;
  1150. $deletionAlert = $this->messages->getDeleteAlert() . '. ' . wf_tag('br');
  1151. $deletionAlert .= __('Also all archive data for this camera will be destroyed permanently') . '.';
  1152. $deletionTitle = __('Delete') . ' ' . __('Camera') . ' ' . $cameraData['ip'] . '?';
  1153. $cameraControls .= wf_ConfirmDialog($deletionUrl, web_delete_icon() . ' ' . __('Delete'), $deletionAlert, 'ubButton', $cancelUrl, $deletionTitle) . ' ';
  1154. } else {
  1155. //only comments editing accessible for active cameras
  1156. $renameForm = $this->renderRenameForm($cameraId);
  1157. $cameraControls .= wf_modalAuto(web_edit_icon() . ' ' . __('Edit'), __('Edit camera') . ': ' . $cameraData['comment'], $renameForm, 'ubButton');
  1158. }
  1159. } else {
  1160. $result .= $this->messages->getStyledMessage(__('Camera') . ' [' . $cameraId . '] ' . __('not exists'), 'error');
  1161. }
  1162. $result .= wf_delimiter(0);
  1163. $result .= wf_BackLink(self::URL_ME) . ' ';
  1164. $result .= $cameraControls;
  1165. return ($result);
  1166. }
  1167. }