BxDolTranscoder.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  4. * CC-BY License - http://creativecommons.org/licenses/by/3.0/
  5. *
  6. * @defgroup TridentCore Trident Core
  7. * @{
  8. */
  9. define('BX_DOL_QUEUE_PENDING', 'pending'); ///< file is pending for processing
  10. define('BX_DOL_QUEUE_PROCESSING', 'processing'); ///< file is in the process
  11. define('BX_DOL_QUEUE_COMPLETE', 'complete'); ///< file is already processed
  12. define('BX_DOL_QUEUE_FAILED', 'failed'); ///< file processing was failed
  13. define('BX_DOL_QUEUE_DELETE', 'delete'); ///< file is subject to delete
  14. /**
  15. * Base class for @see BxDolTranscoderImage and @see BxDolTranscoderVideo transcoders
  16. */
  17. class BxDolTranscoder extends BxDolFactory implements iBxDolFactoryObject
  18. {
  19. protected $_sLog = ''; ///< file conversion log
  20. protected $_aObject; ///< object properties
  21. protected $_oStorage; ///< storage object, transcoded files are stored here
  22. protected $_oDb; ///< database queries object
  23. protected $_sRetinaSuffix = '@2x'; ///< handler suffix for retina image file
  24. protected $_sQueueTable = ''; ///< table with queued files for transcoding
  25. protected $_sQueueStorage = ''; ///< storage object to storage temporary files
  26. protected $_iDevicePixelRatio = false; ///< force device pixel ratio, 1 or 2
  27. /**
  28. * constructor
  29. */
  30. protected function __construct($aObject, $oStorage)
  31. {
  32. parent::__construct();
  33. $this->_aObject = $aObject;
  34. $this->_oStorage = $oStorage;
  35. $this->_oDb = null;
  36. }
  37. /**
  38. * Get transcode object instance.
  39. * @param $sObject - name of transcode object.
  40. * @return false on error or instance of transcoder class.
  41. */
  42. public static function getObjectInstance($sObject)
  43. {
  44. if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '!' . $sObject]))
  45. return $GLOBALS['bxDolClasses'][__CLASS__ . '!'.$sObject];
  46. // get transcode object
  47. $aObject = BxDolTranscoderQuery::getTranscoderObject($sObject);
  48. if (!$aObject || !is_array($aObject))
  49. return false;
  50. if ($aObject['source_params'])
  51. $aObject['source_params'] = unserialize($aObject['source_params']);
  52. $aObject['filters'] = false; // filters are initialized on demand
  53. // create storage object to store transcoded data
  54. $oStorage = BxDolStorage::getObjectInstance($aObject['storage_object']);
  55. if (!$oStorage)
  56. return false;
  57. // create instance
  58. $sClass = $aObject['override_class_name'] ? $aObject['override_class_name'] : 'BxDolTranscoderImage';
  59. if (!empty($aObject['override_class_file']))
  60. require_once(BX_DIRECTORY_PATH_ROOT . $aObject['override_class_file']);
  61. $o = new $sClass ($aObject, $oStorage);
  62. return ($GLOBALS['bxDolClasses'][__CLASS__ . '!' . $sObject] = $o);
  63. }
  64. /**
  65. * Delete outdated transcoed data from all transcoeeed objects, by last access time.
  66. * It called on cron, usually every day.
  67. * @return total number of pruned/deleted files
  68. */
  69. static public function pruning ()
  70. {
  71. $iCount = 0;
  72. $aObjects = self::getTranscoderObjects();
  73. foreach ($aObjects as $aObject) {
  74. if (!$aObject['atime_tracking'] || !$aObject['atime_pruning'])
  75. continue;
  76. $oTranscoder = self::getObjectInstance($aObject['object']);
  77. if (!$oTranscoder)
  78. continue;
  79. $iCount += $oTranscoder->prune();
  80. }
  81. return $iCount;
  82. }
  83. /**
  84. * Get transcoder objects array
  85. * @return array
  86. */
  87. static protected function getTranscoderObjects ()
  88. {
  89. return BxDolTranscoderQuery::getTranscoderObjects ();
  90. }
  91. /**
  92. * Process files queued for transcoding
  93. * Can be performed on separate server.
  94. */
  95. static public function processQueue ($bQueuePruning = true)
  96. {
  97. if ($bQueuePruning)
  98. self::pruneQueue();
  99. $a = BxDolTranscoderQuery::getNextInQueue (gethostname());
  100. foreach ($a as $r) {
  101. $o = self::getObjectInstance($r['transcoder_object']);
  102. $aQueue = $o->getDb()->getFromQueue($r['file_id_source']);
  103. if (!$aQueue || $aQueue['status'] != BX_DOL_QUEUE_PENDING)
  104. continue;
  105. $o->transcode($aQueue['file_id_source'], $aQueue['profile_id']);
  106. }
  107. }
  108. static public function pruneQueue ()
  109. {
  110. $a = BxDolTranscoderQuery::getForDeletionFromQueue (gethostname());
  111. if (!$a)
  112. return false;
  113. foreach ($a as $aQueue) {
  114. $o = self::getObjectInstance($aQueue['transcoder_object']);
  115. if ($o)
  116. $o->deleteFromQueue($aQueue['file_id_source']);
  117. }
  118. }
  119. /**
  120. * Store completed files from tmp queue storage to the final storage
  121. * Must be performed on the actual site.
  122. */
  123. static public function processCompleted ()
  124. {
  125. $a = BxDolTranscoderQuery::getCompletedFromQueue ();
  126. foreach ($a as $r) {
  127. $o = self::getObjectInstance($r['transcoder_object']);
  128. $aQueue = $o->getDb()->getFromQueue($r['file_id_source']);
  129. if (!$aQueue || $aQueue['status'] != BX_DOL_QUEUE_COMPLETE)
  130. continue;
  131. if ($aQueue['file_url_result'] && $aQueue['file_id_result']) {
  132. // get transcoded file from provided URL
  133. $sFileData = bx_file_get_contents ($aQueue['file_url_result']);
  134. if (false === $sFileData) {
  135. $o->getDb()->updateQueueStatus($aQueue['file_id_source'], BX_DOL_QUEUE_FAILED, "download file failed: {$aQueue['file_url_result']}\n");
  136. continue;
  137. }
  138. $sTmpFile = $o->getTmpFilename ('.' . $aQueue['file_ext_result']);
  139. if (!file_put_contents($sTmpFile, $sFileData)) {
  140. $o->getDb()->updateQueueStatus($aQueue['file_id_source'], BX_DOL_QUEUE_FAILED, "store downloaded file failed\n");
  141. @unlink($sTmpFile);
  142. continue;
  143. }
  144. // store downloaded file in final storage
  145. if ($o->storeTranscodedFile($aQueue['file_id_source'], $sTmpFile, $aQueue['profile_id']))
  146. $o->getDb()->updateQueueStatus($aQueue['file_id_source'], BX_DOL_QUEUE_DELETE); // mark to delete it (deletion is performed on the server where it was transcoded)
  147. else
  148. $o->getDb()->updateQueueStatus($aQueue['file_id_source'], BX_DOL_QUEUE_FAILED, "store file failed:\n" . $o->getLog());
  149. // delete tmp local file
  150. @unlink($sTmpFile);
  151. }
  152. }
  153. }
  154. /**
  155. * Register handlers array
  156. * It can be called upon module enable event.
  157. * @param $mixed array of transcoders objects, or just one object
  158. */
  159. static public function registerHandlersArray ($mixed)
  160. {
  161. self::_callFuncForObjectsArray ($mixed, 'registerHandlers');
  162. }
  163. /**
  164. * Unregister handlers array
  165. * It can be called upon module disbale event.
  166. * @param $mixed array of transcoders objects, or just one object
  167. */
  168. static public function unregisterHandlersArray ($mixed)
  169. {
  170. self::_callFuncForObjectsArray ($mixed, 'unregisterHandlers');
  171. }
  172. /**
  173. * Cleanup (queue for deletion all resized files)
  174. * It can be called upon module disbale event.
  175. * @param $mixed array of transcoders objects, or just one object
  176. */
  177. static public function cleanupObjectsArray ($mixed)
  178. {
  179. self::_callFuncForObjectsArray ($mixed, 'cleanup');
  180. }
  181. /**
  182. * Called automatically, upon local(transcoded) file deletetion.
  183. */
  184. static public function onAlertResponseFileDeleteLocal ($oAlert, $sObject)
  185. {
  186. $oTranscoder = self::getObjectInstance($sObject);
  187. if (!$oTranscoder)
  188. return;
  189. if ($oAlert->sAction != 'file_deleted')
  190. return;
  191. $oTranscoder->onDeleteFileLocal($oAlert->iObject);
  192. }
  193. /**
  194. * Called automatically, upon original file deletetion.
  195. */
  196. static public function onAlertResponseFileDeleteOrig ($oAlert, $sObject)
  197. {
  198. $oTranscoder = self::getObjectInstance($sObject);
  199. if (!$oTranscoder)
  200. return;
  201. if ($oAlert->sAction != 'file_deleted')
  202. return;
  203. $oTranscoder->onDeleteFileOrig($oAlert->iObject);
  204. }
  205. /**
  206. * Called automatically, upon local(transcoded) file deletetion.
  207. */
  208. public function onDeleteFileLocal($iFileId)
  209. {
  210. return $this->_oDb->deleteFileTraces($iFileId);
  211. }
  212. /**
  213. * Called automatically, upon original file deletetion.
  214. */
  215. public function onDeleteFileOrig($mixedHandler)
  216. {
  217. // deleted files queued for transcoding
  218. $this->_oDb->deleteFromQueue ($mixedHandler);
  219. // delete main file
  220. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  221. if (!$iFileId)
  222. return false;
  223. if (!$this->_oStorage->deleteFile($iFileId))
  224. return false;
  225. // delete retina file
  226. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler . $this->_sRetinaSuffix);
  227. if ($iFileId)
  228. $this->_oStorage->deleteFile($iFileId);
  229. return true;
  230. }
  231. /**
  232. * Register necessary alert handlers for automatic deletetion of transcoded data if source file is deleted.
  233. * Make sure that you call it once, before first usage, for example upon module installation.
  234. */
  235. public function registerHandlers ()
  236. {
  237. if (!$this->_oDb->registerHandlers ())
  238. return false;
  239. return $this->clearCacheDB();
  240. }
  241. /**
  242. * Unregister alert handlers for automatic deletetion of transcoded data if source file is deleted.
  243. * Make sure that you call it once, for example upon module uninstallation.
  244. */
  245. public function unregisterHandlers ()
  246. {
  247. if (!$this->_oDb->unregisterHandlers ())
  248. return false;
  249. return $this->clearCacheDB();
  250. }
  251. /**
  252. * Delete (queue for deletion) all resized files for the current object
  253. */
  254. public function cleanup ()
  255. {
  256. return $this->_oStorage->queueFilesForDeletionFromObject();
  257. }
  258. /**
  259. * Get storage object where transcoded data is stored
  260. */
  261. public function getStorage()
  262. {
  263. return $this->_oStorage;
  264. }
  265. /**
  266. * Get database object instance
  267. */
  268. public function getDb()
  269. {
  270. return $this->_oDb;
  271. }
  272. /**
  273. * Get transcoded file url.
  274. * If transcoded file is ready then direct url to the file is returned.
  275. * If there is no transcoded data available, then special url is returned, upon opening this url image is transcoded automatically and redirects to the ready transcoed image.
  276. * @params $mixedHandler - file handler
  277. * @return file url, or false on error.
  278. */
  279. public function getFileUrl($mixedHandler)
  280. {
  281. if ($this->isFileReady($mixedHandler)) {
  282. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  283. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  284. if (!$iFileId)
  285. return false;
  286. $aFile = $this->_oStorage->getFile($iFileId);
  287. if (!$aFile)
  288. return false;
  289. if ($this->_aObject['atime_tracking'])
  290. $this->_oDb->updateAccessTime($mixedHandler);
  291. return $this->_oStorage->getFileUrlById($iFileId);
  292. }
  293. if ($this->_sQueueTable && !$this->addToQueue($mixedHandler)) // add file to queue (when queue is enabled) if it isn't transcoded yet
  294. return false;
  295. return $this->getFileUrlNotReady($mixedHandler);
  296. }
  297. /**
  298. * alias for @see getFileUrl
  299. */
  300. public function getFileUrlById($mixedHandler)
  301. {
  302. return $this->getFileUrl($mixedHandler);
  303. }
  304. /**
  305. * Check if transcoded data is available. No need to call it directly, it is called automatically when it is needed.
  306. * @params $mixedHandler - file handler
  307. * @params $isCheckOutdated - check if transcoded file is outdated
  308. * @return false if there is no ready transcoded file is available or it is outdated, true if file is ready
  309. */
  310. public function isFileReady ($mixedHandler, $isCheckOutdated = true)
  311. {
  312. $sMethodFileReady = 'isFileReady_' . $this->_aObject['source_type'];
  313. return $this->$sMethodFileReady($mixedHandler, $isCheckOutdated);
  314. }
  315. /**
  316. * Transcode file, no need to call it directly, it is called automatically when it is needed.
  317. * @params $mixedHandler - file handler
  318. * @params $iProfileId - optional profile id, to assign transcoded file ownership to, usually it is NOT assigned to any particular profile, so just leave it default
  319. * @return true on success, false on error
  320. */
  321. public function transcode ($mixedHandler, $iProfileId = 0)
  322. {
  323. $sExtChange = false;
  324. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_PROCESSING, '', gethostname());
  325. // create tmp file locally
  326. $sSuffix = $this->_sQueueStorage ? 'Queue' : $this->_aObject['source_type'];
  327. $sMethodStoreFile = 'storeFileLocally_' . $sSuffix;
  328. $sTmpFile = $this->$sMethodStoreFile($mixedHandler);
  329. if (!$sTmpFile) {
  330. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store local tmp file failed ($sMethodStoreFile)");
  331. return false;
  332. }
  333. // appply filters to tmp file
  334. $this->initFilters ();
  335. foreach ($this->_aObject['filters'] as $aParams) {
  336. $this->clearLog();
  337. $sMethodFilter = 'applyFilter_' . $aParams['filter'];
  338. if (!method_exists($this, $sMethodFilter))
  339. continue;
  340. if (!$this->$sMethodFilter($sTmpFile, $aParams['filter_params'])) {
  341. unlink($sTmpFile);
  342. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "filter failed: $sMethodFilter\n" . $this->getLog());
  343. return false;
  344. }
  345. if (!empty($aParams['filter_params']['force_type'])) {
  346. switch ($aParams['filter_params']['force_type']) {
  347. case 'jpeg':
  348. case 'jpg':
  349. $sExtChange = 'jpg';
  350. break;
  351. default:
  352. $sExtChange = $aParams['filter_params']['force_type'];
  353. }
  354. }
  355. if ($sExtChange) {
  356. $iDotPos = strrpos($sTmpFile, '.');
  357. $sExtOld = false === $iDotPos ? '' : substr($sTmpFile, $iDotPos+1);
  358. if ($sExtOld != $sExtChange) {
  359. $sTmpFileOld = $sTmpFile;
  360. $sTmpFile = false === $iDotPos ? $sTmpFile . '.' . $sExtChange : substr_replace($sTmpFile, $sExtChange, $iDotPos+1, strlen($sExtOld));
  361. if (!rename($sTmpFileOld, $sTmpFile)) {
  362. @unlink($sTmpFileOld);
  363. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "change file extension failed: $sTmpFileOld => $sTmpFile");
  364. return false;
  365. }
  366. }
  367. }
  368. }
  369. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  370. $bRet = true;
  371. if ($this->_sQueueStorage) {
  372. $sFileUrlResult = '';
  373. if (!($iFileIdResult = $this->storeTranscodedFileInQueueStorage($mixedHandler, $sTmpFile, $sFileUrlResult)))
  374. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store queue tmp file failed:\n" . $this->getLog());
  375. else
  376. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_COMPLETE, '', '', $iFileIdResult, $sFileUrlResult, $sExtChange);
  377. } else {
  378. $bRet = $this->storeTranscodedFile($mixedHandler, $sTmpFile, $iProfileId);
  379. if ($bRet)
  380. $this->deleteFromQueue($mixedHandler);
  381. else
  382. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store file failed:\n" . $this->getLog());
  383. }
  384. @unlink($sTmpFile);
  385. return $bRet;
  386. }
  387. /**
  388. * Delete outdated files by last access time.
  389. * @return number of pruned/deleted files
  390. */
  391. public function prune ()
  392. {
  393. $aFiles = $this->_oDb->getFilesForPruning();
  394. if (!$aFiles)
  395. return false;
  396. $iCount = 0;
  397. foreach ($aFiles as $r)
  398. $iCount += $this->_oStorage->deleteFile($r['file_id']) ? 1 : 0;
  399. return $iCount;
  400. }
  401. public function forceDevicePixelRatio ($i)
  402. {
  403. if (!$i)
  404. $this->_iDevicePixelRatio = false;
  405. elseif ($i >= 2)
  406. $this->_iDevicePixelRatio = 2;
  407. else
  408. $this->_iDevicePixelRatio = 1;
  409. }
  410. public function getDevicePixelRatio ()
  411. {
  412. if ($this->_iDevicePixelRatio)
  413. return $this->_iDevicePixelRatio;
  414. if (isset($_COOKIE['devicePixelRatio']) && (!isset($this->_aObject['source_params']['disable_retina']) || !$this->_aObject['source_params']['disable_retina'])) {
  415. $iDevicePixelRatio = intval($_COOKIE['devicePixelRatio']);
  416. if ($iDevicePixelRatio >= 2)
  417. return 2;
  418. }
  419. return 1;
  420. }
  421. public function getOrigFileUrl ($mixedHandler)
  422. {
  423. $sMethodStoreFile = 'getOrigFileUrl_' . $this->_aObject['source_type'];
  424. return $this->$sMethodStoreFile($mixedHandler);
  425. }
  426. /**
  427. * Get file url when file isn't transcoded yet
  428. */
  429. public function getFileUrlNotReady($mixedHandler)
  430. {
  431. return false;
  432. }
  433. public function getTmpFilename ($sOverrideName = false)
  434. {
  435. if ($sOverrideName)
  436. return BX_DIRECTORY_PATH_TMP . rand(10000, 99999) . $sOverrideName;
  437. return tempnam(BX_DIRECTORY_PATH_TMP, $this->_aObject['object']);
  438. }
  439. public function clearLog()
  440. {
  441. $this->_sLog = '';
  442. }
  443. public function addToLog($s)
  444. {
  445. $this->_sLog .= $s;
  446. }
  447. public function getLog()
  448. {
  449. return $this->_sLog;
  450. }
  451. public function deleteFromQueue ($mixedHandler)
  452. {
  453. $sErrMsg = "delete queue tmp file failed:\n";
  454. $a = $this->_oDb->getFromQueue($mixedHandler);
  455. if (!$a || $a['server'] != gethostname()) // deletion must be performed on the server where it was transcoded
  456. return false;
  457. if ($a['file_id_result'] && $this->_sQueueStorage && 0 != strncmp($a['log'], $sErrMsg, strlen($sErrMsg))) {
  458. $oStorage = BxDolStorage::getObjectInstance($this->_sQueueStorage);
  459. if (!$oStorage->deleteFile($a['file_id_result'])) {
  460. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, $sErrMsg . $this->getLog());
  461. return false;
  462. }
  463. }
  464. return $this->_oDb->deleteFromQueue($mixedHandler);
  465. }
  466. public function storeTranscodedFile ($mixedHandler, $sTmpFile, $iProfileId)
  467. {
  468. $sMethodIsPrivate = 'isPrivate_' . $this->_aObject['source_type'];
  469. $isPrivate = $this->$sMethodIsPrivate($mixedHandler);
  470. $iFileId = $this->_oStorage->storeFileFromPath ($sTmpFile, $isPrivate, $iProfileId);
  471. if (!$iFileId) {
  472. $this->addToLog($this->_oStorage->getErrorString());
  473. return false;
  474. }
  475. if (!$this->_oDb->updateHandler($iFileId, $mixedHandler)) {
  476. $this->_oStorage->deleteFile($iFileId);
  477. return false;
  478. }
  479. $this->_oStorage->afterUploadCleanup($iFileId, $iProfileId);
  480. return true;
  481. }
  482. protected function storeTranscodedFileInQueueStorage ($mixedHandler, $sTmpFile, &$sFileUrlResult)
  483. {
  484. if (!$this->_sQueueStorage)
  485. return false;
  486. $oStorage = BxDolStorage::getObjectInstance($this->_sQueueStorage);
  487. $iFileIdResult = $oStorage->storeFileFromPath ($sTmpFile, 1, 0);
  488. if (!$iFileIdResult) {
  489. $this->addToLog($oStorage->getErrorString());
  490. return false;
  491. }
  492. if (!($sFileUrlResult = $oStorage->getFileUrlById($iFileIdResult))) {
  493. $this->_oStorage->deleteFile($iFileIdResult);
  494. return false;
  495. }
  496. $oStorage->afterUploadCleanup($iFileIdResult, 0);
  497. return $iFileIdResult;
  498. }
  499. protected function addToQueue($mixedHandler, $iProfileId = 0)
  500. {
  501. if ($this->_oDb->getFromQueue($mixedHandler))
  502. return true;
  503. $sFileUrl = $this->getOrigFileUrl($mixedHandler);
  504. return $this->_oDb->addToQueue ($mixedHandler, $sFileUrl, $iProfileId);
  505. }
  506. protected function applyFilter_Resize ($sFile, $aParams)
  507. {
  508. $o = BxDolImageResize::getInstance();
  509. $o->removeCropOptions ();
  510. $o->setSize (isset($aParams['w']) ? round($aParams['w'] * $this->getDevicePixelRatio()) : null, isset($aParams['h']) ? round($aParams['h'] * $this->getDevicePixelRatio()) : null);
  511. if (isset($aParams['crop_resize']) && $aParams['crop_resize'])
  512. $o->setAutoCrop (true);
  513. elseif (isset($aParams['square_resize']) && $aParams['square_resize'])
  514. $o->setSquareResize (true);
  515. else
  516. $o->setSquareResize (false);
  517. if (IMAGE_ERROR_SUCCESS == $o->resize($sFile))
  518. return true;
  519. return false;
  520. }
  521. protected function isFileReady_Folder ($mixedHandlerOrig, $isCheckOutdated = true)
  522. {
  523. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  524. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  525. if (!$iFileId)
  526. return false;
  527. $aFile = $this->_oStorage->getFile($iFileId);
  528. if (!$aFile)
  529. return false;
  530. if ($isCheckOutdated) { // warning, $isCheckOutdated is partially supported for Folder source type - file modification is not checked
  531. if ($this->_aObject['ts'] > $aFile['modified'] || !$this->getFilePath_Folder($mixedHandlerOrig)) { // if we changed transcoder object params or original file is deleted
  532. // delete file, so it will be recreated next time
  533. if ($this->_oStorage->deleteFile($aFile['id']))
  534. return false;
  535. }
  536. }
  537. return true;
  538. }
  539. protected function isFileReady_Storage ($mixedHandlerOrig, $isCheckOutdated = true)
  540. {
  541. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  542. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  543. if (!$iFileId)
  544. return false;
  545. $aFile = $this->_oStorage->getFile($iFileId);
  546. if (!$aFile)
  547. return false;
  548. if ($isCheckOutdated) {
  549. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  550. if ($oStorageOriginal) {
  551. $aFileOriginal = $oStorageOriginal->getFile($mixedHandlerOrig);
  552. if (!$aFileOriginal || $aFileOriginal['modified'] > $aFile['modified'] || $this->_aObject['ts'] >= $aFile['modified']) { // if original file was changed OR we changed transcoder object params
  553. // delete file, so it will be recreated next time
  554. if ($this->_oStorage->deleteFile($aFile['id']))
  555. return false;
  556. }
  557. }
  558. }
  559. return true;
  560. }
  561. protected function isPrivate_Folder ($mixedHandler)
  562. {
  563. if ('no' == $this->_aObject['private'])
  564. return false;
  565. return true;
  566. }
  567. protected function isPrivate_Storage ($mixedHandler)
  568. {
  569. switch ($this->_aObject['private']) {
  570. case 'no':
  571. return false;
  572. case 'yes':
  573. return true;
  574. default:
  575. case 'auto':
  576. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  577. if (!$oStorageOriginal)
  578. return true; // in case of error - make sure that file is not public accidentally
  579. $aFile = $oStorageOriginal->getFile($mixedHandler);
  580. return $oStorageOriginal->isFilePrivate($aFile['id']);
  581. }
  582. }
  583. protected function getFilePath_Folder ($mixedHandler)
  584. {
  585. $sPath = $this->_aObject['source_params']['path'] . $mixedHandler;
  586. if (!file_exists($sPath))
  587. $sPath = BX_DIRECTORY_PATH_ROOT . $sPath;
  588. if (!file_exists($sPath))
  589. return false;
  590. return $sPath;
  591. }
  592. protected function storeFileLocally_Folder ($mixedHandler)
  593. {
  594. $sPath = $this->getFilePath_Folder($mixedHandler);
  595. if (!$sPath)
  596. return false;
  597. $sTmpFile = $this->getTmpFilename ($mixedHandler);
  598. if (!copy($sPath, $sTmpFile))
  599. return false;
  600. return $sTmpFile;
  601. }
  602. protected function storeFileLocally_Storage ($mixedHandler)
  603. {
  604. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  605. if (!$oStorageOriginal)
  606. return false;
  607. $aFile = $oStorageOriginal->getFile($mixedHandler);
  608. if (!$aFile)
  609. return false;
  610. $sUrl = $oStorageOriginal->getFileUrlById($mixedHandler);
  611. if (!$sUrl)
  612. return false;
  613. $sFileData = bx_file_get_contents ($sUrl);
  614. if (false === $sFileData)
  615. return false;
  616. $sTmpFile = $this->getTmpFilename ($aFile['file_name']);
  617. if (!file_put_contents($sTmpFile, $sFileData))
  618. return false;
  619. return $sTmpFile;
  620. }
  621. protected function storeFileLocally_Queue ($mixedHandler)
  622. {
  623. $aQueue = $this->_oDb->getFromQueue($mixedHandler);
  624. if (!$aQueue || !$aQueue['file_url_source'])
  625. return false;
  626. $sFileData = bx_file_get_contents ($aQueue['file_url_source']);
  627. if (false === $sFileData)
  628. return false;
  629. $sTmpFile = $this->getTmpFilename ($mixedHandler);
  630. if (!file_put_contents($sTmpFile, $sFileData))
  631. return false;
  632. return $sTmpFile;
  633. }
  634. protected function getOrigFileUrl_Folder ($mixedHandler)
  635. {
  636. $sPath = $this->_aObject['source_params']['path'] . $mixedHandler;
  637. if (!file_exists(BX_DIRECTORY_PATH_ROOT . $sPath))
  638. return false;
  639. return BX_DOL_URL_ROOT . $sPath;
  640. }
  641. protected function getOrigFileUrl_Storage ($mixedHandler)
  642. {
  643. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  644. if (!$oStorageOriginal)
  645. return false;
  646. return $oStorageOriginal->getFileUrlById($mixedHandler);
  647. }
  648. protected function initFilters ()
  649. {
  650. if (false !== $this->_aObject['filters']) // filters are already initilized
  651. return;
  652. // get transcoder filters
  653. $aFilters = $this->_oDb->getTranscoderFilters();
  654. $this->_aObject['filters'] = array();
  655. foreach ($aFilters as $aFilter) {
  656. $aFilter['filter_params'] = $aFilter['filter_params'] ? unserialize($aFilter['filter_params']) : array();
  657. $this->_aObject['filters'][] = $aFilter;
  658. }
  659. }
  660. protected function clearCacheDB()
  661. {
  662. $oCacheDb = $this->_oDb->getDbCacheObject();
  663. return $oCacheDb->removeAllByPrefix('db_');
  664. }
  665. public function getDevicePixelRatioHandlerSuffix ()
  666. {
  667. $iDevicePixelRatio = $this->getDevicePixelRatio ();
  668. if ($iDevicePixelRatio >= 2)
  669. return $this->_sRetinaSuffix;
  670. return '';
  671. }
  672. protected function processHandlerForRetinaDevice ($mixedHandler)
  673. {
  674. return '' . $mixedHandler . $this->getDevicePixelRatioHandlerSuffix ();
  675. }
  676. static protected function _callFuncForObjectsArray ($mixed, $sFunc)
  677. {
  678. if (!is_array($mixed))
  679. $mixed = array($mixed);
  680. foreach ($mixed as $sObject) {
  681. $oTranscoder = self::getObjectInstance($sObject);
  682. if ($oTranscoder)
  683. $oTranscoder->$sFunc();
  684. }
  685. }
  686. /**
  687. * Get filter params
  688. * @param $sName filter name
  689. * @return array of filter params if filter was found, or false if given filter isn't enabled for the transcoder
  690. */
  691. public function getFilterParams ($sName)
  692. {
  693. $this->initFilters ();
  694. foreach ($this->_aObject['filters'] as $aParams)
  695. if ($sName == $aParams['filter'])
  696. return $aParams['filter_params'];
  697. return false;
  698. }
  699. }
  700. /** @} */