BxDolTranscoder.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA 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. * Register system handlers
  165. */
  166. static public function registerHandlersSystem()
  167. {
  168. $sPrefix = 'sys_';
  169. $aResults = array();
  170. $aTranscoders = BxDolTranscoderQuery::getTranscoderObjects();
  171. foreach($aTranscoders as $aTranscoder) {
  172. if(strncasecmp($aTranscoder['object'], $sPrefix, strlen($sPrefix)) !== 0)
  173. continue;
  174. $aResults[] = $aTranscoder['object'];
  175. }
  176. if(!empty($aResults))
  177. self::registerHandlersArray($aResults);
  178. }
  179. /**
  180. * Unregister handlers array
  181. * It can be called upon module disbale event.
  182. * @param $mixed array of transcoders objects, or just one object
  183. */
  184. static public function unregisterHandlersArray ($mixed)
  185. {
  186. self::_callFuncForObjectsArray ($mixed, 'unregisterHandlers');
  187. }
  188. /**
  189. * Cleanup (queue for deletion all resized files)
  190. * It can be called upon module disbale event.
  191. * @param $mixed array of transcoders objects, or just one object
  192. */
  193. static public function cleanupObjectsArray ($mixed)
  194. {
  195. self::_callFuncForObjectsArray ($mixed, 'cleanup');
  196. }
  197. /**
  198. * Called automatically, upon local(transcoded) file deletetion.
  199. */
  200. static public function onAlertResponseFileDeleteLocal ($oAlert, $sObject)
  201. {
  202. $oTranscoder = self::getObjectInstance($sObject);
  203. if (!$oTranscoder)
  204. return;
  205. if ($oAlert->sAction != 'file_deleted')
  206. return;
  207. $oTranscoder->onDeleteFileLocal($oAlert->iObject);
  208. }
  209. /**
  210. * Called automatically, upon original file deletetion.
  211. */
  212. static public function onAlertResponseFileDeleteOrig ($oAlert, $sObject)
  213. {
  214. $oTranscoder = self::getObjectInstance($sObject);
  215. if (!$oTranscoder)
  216. return;
  217. if ($oAlert->sAction != 'file_deleted')
  218. return;
  219. $oTranscoder->onDeleteFileOrig($oAlert->iObject);
  220. }
  221. /**
  222. * Called automatically, upon local(transcoded) file deletetion.
  223. */
  224. public function onDeleteFileLocal($iFileId)
  225. {
  226. return $this->_oDb->deleteFileTraces($iFileId);
  227. }
  228. /**
  229. * Called automatically, upon original file deletetion.
  230. */
  231. public function onDeleteFileOrig($mixedHandler)
  232. {
  233. // deleted files queued for transcoding
  234. $this->_oDb->deleteFromQueue ($mixedHandler);
  235. // delete main file
  236. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  237. if (!$iFileId)
  238. return false;
  239. if (!$this->_oStorage->deleteFile($iFileId))
  240. return false;
  241. // delete retina file
  242. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler . $this->_sRetinaSuffix);
  243. if ($iFileId)
  244. $this->_oStorage->deleteFile($iFileId);
  245. return true;
  246. }
  247. /**
  248. * Register necessary alert handlers for automatic deletetion of transcoded data if source file is deleted.
  249. * Make sure that you call it once, before first usage, for example upon module installation.
  250. */
  251. public function registerHandlers ()
  252. {
  253. if (!$this->_oDb->registerHandlers ())
  254. return false;
  255. return $this->clearCacheDB();
  256. }
  257. /**
  258. * Unregister alert handlers for automatic deletetion of transcoded data if source file is deleted.
  259. * Make sure that you call it once, for example upon module uninstallation.
  260. */
  261. public function unregisterHandlers ()
  262. {
  263. if (!$this->_oDb->unregisterHandlers ())
  264. return false;
  265. return $this->clearCacheDB();
  266. }
  267. /**
  268. * Delete (queue for deletion) all resized files for the current object
  269. */
  270. public function cleanup ()
  271. {
  272. return $this->_oStorage->queueFilesForDeletionFromObject();
  273. }
  274. /**
  275. * Get storage object where transcoded data is stored
  276. */
  277. public function getStorage()
  278. {
  279. return $this->_oStorage;
  280. }
  281. /**
  282. * Get database object instance
  283. */
  284. public function getDb()
  285. {
  286. return $this->_oDb;
  287. }
  288. /**
  289. * Get transcoded file url.
  290. * If transcoded file is ready then direct url to the file is returned.
  291. * 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.
  292. * @param $mixedHandler - file handler
  293. * @return file url, or false on error.
  294. */
  295. public function getFileUrl($mixedHandler)
  296. {
  297. if ($this->isFileReady($mixedHandler)) {
  298. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  299. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  300. if (!$iFileId)
  301. return false;
  302. $aFile = $this->_oStorage->getFile($iFileId);
  303. if (!$aFile)
  304. return false;
  305. if ($this->_aObject['atime_tracking'])
  306. $this->_oDb->updateAccessTime($mixedHandler);
  307. return $this->_oStorage->getFileUrlById($iFileId);
  308. }
  309. if ($this->_sQueueTable && !$this->addToQueue($mixedHandler)) // add file to queue (when queue is enabled) if it isn't transcoded yet
  310. return false;
  311. return $this->getFileUrlNotReady($mixedHandler);
  312. }
  313. /**
  314. * alias for @see getFileUrl
  315. */
  316. public function getFileUrlById($mixedHandler)
  317. {
  318. return $this->getFileUrl($mixedHandler);
  319. }
  320. /**
  321. * Check if transcoded data is available. No need to call it directly, it is called automatically when it is needed.
  322. * @param $mixedHandler - file handler
  323. * @param $isCheckOutdated - check if transcoded file is outdated
  324. * @return false if there is no ready transcoded file is available or it is outdated, true if file is ready
  325. */
  326. public function isFileReady ($mixedHandler, $isCheckOutdated = true)
  327. {
  328. $sMethodFileReady = 'isFileReady_' . $this->_aObject['source_type'];
  329. return $this->$sMethodFileReady($mixedHandler, $isCheckOutdated);
  330. }
  331. /**
  332. * Transcode file, no need to call it directly, it is called automatically when it is needed.
  333. * @param $mixedHandler - file handler
  334. * @param $iProfileId - optional profile id, to assign transcoded file ownership to, usually it is NOT assigned to any particular profile, so just leave it default
  335. * @return true on success, false on error
  336. */
  337. public function transcode ($mixedHandler, $iProfileId = 0)
  338. {
  339. $sExtChange = false;
  340. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_PROCESSING, '', gethostname());
  341. // create tmp file locally
  342. $sSuffix = $this->_sQueueStorage ? 'Queue' : $this->_aObject['source_type'];
  343. $sMethodStoreFile = 'storeFileLocally_' . $sSuffix;
  344. $sTmpFile = $this->$sMethodStoreFile($mixedHandler);
  345. if (!$sTmpFile) {
  346. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store local tmp file failed ($sMethodStoreFile)");
  347. return false;
  348. }
  349. // appply filters to tmp file
  350. $this->initFilters ();
  351. foreach ($this->_aObject['filters'] as $aParams) {
  352. $this->clearLog();
  353. $sMethodFilter = 'applyFilter_' . $aParams['filter'];
  354. if (!method_exists($this, $sMethodFilter))
  355. continue;
  356. if (!$this->$sMethodFilter($sTmpFile, $aParams['filter_params'])) {
  357. unlink($sTmpFile);
  358. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "filter failed: $sMethodFilter\n" . $this->getLog());
  359. return false;
  360. }
  361. if (!empty($aParams['filter_params']['force_type'])) {
  362. switch ($aParams['filter_params']['force_type']) {
  363. case 'jpeg':
  364. case 'jpg':
  365. $sExtChange = 'jpg';
  366. break;
  367. default:
  368. $sExtChange = $aParams['filter_params']['force_type'];
  369. }
  370. }
  371. if ($sExtChange) {
  372. $iDotPos = strrpos($sTmpFile, '.');
  373. $sExtOld = false === $iDotPos ? '' : substr($sTmpFile, $iDotPos+1);
  374. if ($sExtOld != $sExtChange) {
  375. $sTmpFileOld = $sTmpFile;
  376. $sTmpFile = false === $iDotPos ? $sTmpFile . '.' . $sExtChange : substr_replace($sTmpFile, $sExtChange, $iDotPos+1, strlen($sExtOld));
  377. if (!rename($sTmpFileOld, $sTmpFile)) {
  378. @unlink($sTmpFileOld);
  379. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "change file extension failed: $sTmpFileOld => $sTmpFile");
  380. return false;
  381. }
  382. }
  383. }
  384. }
  385. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  386. $bRet = true;
  387. if ($this->_sQueueStorage) {
  388. $sFileUrlResult = '';
  389. if (!($iFileIdResult = $this->storeTranscodedFileInQueueStorage($mixedHandler, $sTmpFile, $sFileUrlResult)))
  390. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store queue tmp file failed:\n" . $this->getLog());
  391. else
  392. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_COMPLETE, '', '', $iFileIdResult, $sFileUrlResult, $sExtChange);
  393. } else {
  394. $bRet = $this->storeTranscodedFile($mixedHandler, $sTmpFile, $iProfileId);
  395. if ($bRet)
  396. $this->deleteFromQueue($mixedHandler);
  397. else
  398. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store file failed:\n" . $this->getLog());
  399. }
  400. @unlink($sTmpFile);
  401. return $bRet;
  402. }
  403. /**
  404. * Delete outdated files by last access time.
  405. * @return number of pruned/deleted files
  406. */
  407. public function prune ()
  408. {
  409. $aFiles = $this->_oDb->getFilesForPruning();
  410. if (!$aFiles)
  411. return false;
  412. $iCount = 0;
  413. foreach ($aFiles as $r)
  414. $iCount += $this->_oStorage->deleteFile($r['file_id']) ? 1 : 0;
  415. return $iCount;
  416. }
  417. public function forceDevicePixelRatio ($i)
  418. {
  419. if (!$i)
  420. $this->_iDevicePixelRatio = false;
  421. elseif ($i >= 2)
  422. $this->_iDevicePixelRatio = 2;
  423. else
  424. $this->_iDevicePixelRatio = 1;
  425. }
  426. public function getDevicePixelRatio ()
  427. {
  428. if ($this->_iDevicePixelRatio)
  429. return $this->_iDevicePixelRatio;
  430. if (isset($_COOKIE['devicePixelRatio']) && (!isset($this->_aObject['source_params']['disable_retina']) || !$this->_aObject['source_params']['disable_retina'])) {
  431. $iDevicePixelRatio = intval($_COOKIE['devicePixelRatio']);
  432. if ($iDevicePixelRatio >= 2)
  433. return 2;
  434. }
  435. return 1;
  436. }
  437. public function getOrigFileUrl ($mixedHandler)
  438. {
  439. $sMethodStoreFile = 'getOrigFileUrl_' . $this->_aObject['source_type'];
  440. return $this->$sMethodStoreFile($mixedHandler);
  441. }
  442. /**
  443. * Get file url when file isn't transcoded yet
  444. */
  445. public function getFileUrlNotReady($mixedHandler)
  446. {
  447. return false;
  448. }
  449. public function getTmpFilename ($sOverrideName = false)
  450. {
  451. if ($sOverrideName)
  452. return BX_DIRECTORY_PATH_TMP . rand(10000, 99999) . $sOverrideName;
  453. return tempnam(BX_DIRECTORY_PATH_TMP, $this->_aObject['object']);
  454. }
  455. public function clearLog()
  456. {
  457. $this->_sLog = '';
  458. }
  459. public function addToLog($s)
  460. {
  461. $this->_sLog .= $s;
  462. }
  463. public function getLog()
  464. {
  465. return $this->_sLog;
  466. }
  467. public function deleteFromQueue ($mixedHandler)
  468. {
  469. $sErrMsg = "delete queue tmp file failed:\n";
  470. $a = $this->_oDb->getFromQueue($mixedHandler);
  471. if (!$a || $a['server'] != gethostname()) // deletion must be performed on the server where it was transcoded
  472. return false;
  473. if ($a['file_id_result'] && $this->_sQueueStorage && 0 !== strncmp($a['log'], $sErrMsg, strlen($sErrMsg))) {
  474. $oStorage = BxDolStorage::getObjectInstance($this->_sQueueStorage);
  475. if (!$oStorage->deleteFile($a['file_id_result'])) {
  476. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, $sErrMsg . $this->getLog());
  477. return false;
  478. }
  479. }
  480. return $this->_oDb->deleteFromQueue($mixedHandler);
  481. }
  482. public function storeTranscodedFile ($mixedHandler, $sTmpFile, $iProfileId)
  483. {
  484. $sMethodIsPrivate = 'isPrivate_' . $this->_aObject['source_type'];
  485. $isPrivate = $this->$sMethodIsPrivate($mixedHandler);
  486. $iFileId = $this->_oStorage->storeFileFromPath ($sTmpFile, $isPrivate, $iProfileId);
  487. if (!$iFileId) {
  488. $this->addToLog($this->_oStorage->getErrorString());
  489. return false;
  490. }
  491. if (!$this->_oDb->updateHandler($iFileId, $mixedHandler)) {
  492. $this->_oStorage->deleteFile($iFileId);
  493. return false;
  494. }
  495. $this->_oStorage->afterUploadCleanup($iFileId, $iProfileId);
  496. return true;
  497. }
  498. protected function storeTranscodedFileInQueueStorage ($mixedHandler, $sTmpFile, &$sFileUrlResult)
  499. {
  500. if (!$this->_sQueueStorage)
  501. return false;
  502. $oStorage = BxDolStorage::getObjectInstance($this->_sQueueStorage);
  503. $iFileIdResult = $oStorage->storeFileFromPath ($sTmpFile, 1, 0);
  504. if (!$iFileIdResult) {
  505. $this->addToLog($oStorage->getErrorString());
  506. return false;
  507. }
  508. if (!($sFileUrlResult = $oStorage->getFileUrlById($iFileIdResult))) {
  509. $this->_oStorage->deleteFile($iFileIdResult);
  510. return false;
  511. }
  512. $oStorage->afterUploadCleanup($iFileIdResult, 0);
  513. return $iFileIdResult;
  514. }
  515. protected function addToQueue($mixedHandler, $iProfileId = 0)
  516. {
  517. if ($this->_oDb->getFromQueue($mixedHandler))
  518. return true;
  519. $sFileUrl = $this->getOrigFileUrl($mixedHandler);
  520. return $this->_oDb->addToQueue ($mixedHandler, $sFileUrl, $iProfileId);
  521. }
  522. protected function applyFilter_Resize ($sFile, $aParams)
  523. {
  524. $o = BxDolImageResize::getInstance();
  525. $o->removeCropOptions ();
  526. $o->setSize (isset($aParams['w']) ? round($aParams['w'] * $this->getDevicePixelRatio()) : null, isset($aParams['h']) ? round($aParams['h'] * $this->getDevicePixelRatio()) : null);
  527. if (isset($aParams['crop_resize']) && $aParams['crop_resize'])
  528. $o->setAutoCrop (true);
  529. elseif (isset($aParams['square_resize']) && $aParams['square_resize'])
  530. $o->setSquareResize (true);
  531. else
  532. $o->setSquareResize (false);
  533. if (IMAGE_ERROR_SUCCESS == $o->resize($sFile))
  534. return true;
  535. return false;
  536. }
  537. protected function isFileReady_Folder ($mixedHandlerOrig, $isCheckOutdated = true)
  538. {
  539. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  540. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  541. if (!$iFileId)
  542. return false;
  543. $aFile = $this->_oStorage->getFile($iFileId);
  544. if (!$aFile)
  545. return false;
  546. if ($isCheckOutdated) { // warning, $isCheckOutdated is partially supported for Folder source type - file modification is not checked
  547. if ($this->_aObject['ts'] > $aFile['modified'] || !$this->getFilePath_Folder($mixedHandlerOrig)) { // if we changed transcoder object params or original file is deleted
  548. // delete file, so it will be recreated next time
  549. if ($this->_oStorage->deleteFile($aFile['id']))
  550. return false;
  551. }
  552. }
  553. return true;
  554. }
  555. protected function isFileReady_Storage ($mixedHandlerOrig, $isCheckOutdated = true)
  556. {
  557. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  558. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  559. if (!$iFileId)
  560. return false;
  561. $aFile = $this->_oStorage->getFile($iFileId);
  562. if (!$aFile)
  563. return false;
  564. if ($isCheckOutdated) {
  565. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  566. if ($oStorageOriginal) {
  567. $aFileOriginal = $oStorageOriginal->getFile($mixedHandlerOrig);
  568. if (!$aFileOriginal || $aFileOriginal['modified'] > $aFile['modified'] || $this->_aObject['ts'] >= $aFile['modified']) { // if original file was changed OR we changed transcoder object params
  569. // delete file, so it will be recreated next time
  570. if ($this->_oStorage->deleteFile($aFile['id']))
  571. return false;
  572. }
  573. }
  574. }
  575. return true;
  576. }
  577. protected function isPrivate_Folder ($mixedHandler)
  578. {
  579. if ('no' == $this->_aObject['private'])
  580. return false;
  581. return true;
  582. }
  583. protected function isPrivate_Storage ($mixedHandler)
  584. {
  585. switch ($this->_aObject['private']) {
  586. case 'no':
  587. return false;
  588. case 'yes':
  589. return true;
  590. default:
  591. case 'auto':
  592. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  593. if (!$oStorageOriginal)
  594. return true; // in case of error - make sure that file is not public accidentally
  595. $aFile = $oStorageOriginal->getFile($mixedHandler);
  596. return $oStorageOriginal->isFilePrivate($aFile['id']);
  597. }
  598. }
  599. protected function getFilePath_Folder ($mixedHandler)
  600. {
  601. $sPath = $this->_aObject['source_params']['path'] . $mixedHandler;
  602. if (!file_exists($sPath))
  603. $sPath = BX_DIRECTORY_PATH_ROOT . $sPath;
  604. if (!file_exists($sPath))
  605. return false;
  606. return $sPath;
  607. }
  608. protected function storeFileLocally_Folder ($mixedHandler)
  609. {
  610. $sPath = $this->getFilePath_Folder($mixedHandler);
  611. if (!$sPath)
  612. return false;
  613. $sTmpFile = $this->getTmpFilename ($mixedHandler);
  614. if (!copy($sPath, $sTmpFile))
  615. return false;
  616. return $sTmpFile;
  617. }
  618. protected function storeFileLocally_Storage ($mixedHandler)
  619. {
  620. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  621. if (!$oStorageOriginal)
  622. return false;
  623. $aFile = $oStorageOriginal->getFile($mixedHandler);
  624. if (!$aFile)
  625. return false;
  626. $sUrl = $oStorageOriginal->getFileUrlById($mixedHandler);
  627. if (!$sUrl)
  628. return false;
  629. $sFileData = bx_file_get_contents ($sUrl);
  630. if (false === $sFileData)
  631. return false;
  632. $sTmpFile = $this->getTmpFilename ($aFile['file_name']);
  633. if (!file_put_contents($sTmpFile, $sFileData))
  634. return false;
  635. return $sTmpFile;
  636. }
  637. protected function storeFileLocally_Queue ($mixedHandler)
  638. {
  639. $aQueue = $this->_oDb->getFromQueue($mixedHandler);
  640. if (!$aQueue || !$aQueue['file_url_source'])
  641. return false;
  642. $sFileData = bx_file_get_contents ($aQueue['file_url_source']);
  643. if (false === $sFileData)
  644. return false;
  645. $sTmpFile = $this->getTmpFilename ($mixedHandler);
  646. if (!file_put_contents($sTmpFile, $sFileData))
  647. return false;
  648. return $sTmpFile;
  649. }
  650. protected function getOrigFileUrl_Folder ($mixedHandler)
  651. {
  652. $sPath = $this->_aObject['source_params']['path'] . $mixedHandler;
  653. if (!file_exists(BX_DIRECTORY_PATH_ROOT . $sPath))
  654. return false;
  655. return BX_DOL_URL_ROOT . $sPath;
  656. }
  657. protected function getOrigFileUrl_Storage ($mixedHandler)
  658. {
  659. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  660. if (!$oStorageOriginal)
  661. return false;
  662. return $oStorageOriginal->getFileUrlById($mixedHandler);
  663. }
  664. protected function initFilters ()
  665. {
  666. if (false !== $this->_aObject['filters']) // filters are already initilized
  667. return;
  668. // get transcoder filters
  669. $aFilters = $this->_oDb->getTranscoderFilters();
  670. $this->_aObject['filters'] = array();
  671. foreach ($aFilters as $aFilter) {
  672. $aFilter['filter_params'] = $aFilter['filter_params'] ? unserialize($aFilter['filter_params']) : array();
  673. $this->_aObject['filters'][] = $aFilter;
  674. }
  675. }
  676. protected function clearCacheDB()
  677. {
  678. $oCacheDb = $this->_oDb->getDbCacheObject();
  679. return $oCacheDb->removeAllByPrefix('db_');
  680. }
  681. public function getDevicePixelRatioHandlerSuffix ()
  682. {
  683. $iDevicePixelRatio = $this->getDevicePixelRatio ();
  684. if ($iDevicePixelRatio >= 2)
  685. return $this->_sRetinaSuffix;
  686. return '';
  687. }
  688. protected function processHandlerForRetinaDevice ($mixedHandler)
  689. {
  690. return '' . $mixedHandler . $this->getDevicePixelRatioHandlerSuffix ();
  691. }
  692. static protected function _callFuncForObjectsArray ($mixed, $sFunc)
  693. {
  694. if (!is_array($mixed))
  695. $mixed = array($mixed);
  696. foreach ($mixed as $sObject) {
  697. $oTranscoder = self::getObjectInstance($sObject);
  698. if ($oTranscoder)
  699. $oTranscoder->$sFunc();
  700. }
  701. }
  702. /**
  703. * Get filter params
  704. * @param $sName filter name
  705. * @return array of filter params if filter was found, or false if given filter isn't enabled for the transcoder
  706. */
  707. public function getFilterParams ($sName)
  708. {
  709. $this->initFilters ();
  710. foreach ($this->_aObject['filters'] as $aParams)
  711. if ($sName == $aParams['filter'])
  712. return $aParams['filter_params'];
  713. return false;
  714. }
  715. }
  716. /** @} */