BxDolTranscoder.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  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. * check if transcoder suppors given file mime type
  290. */
  291. public function isMimeTypeSupported($sMimeType)
  292. {
  293. return false;
  294. }
  295. /**
  296. * Get transcoded file url.
  297. * If transcoded file is ready then direct url to the file is returned.
  298. * 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.
  299. * @param $mixedHandler - file handler
  300. * @return file url, or false on error.
  301. */
  302. public function getFileUrl($mixedHandler)
  303. {
  304. if ($this->isFileReady($mixedHandler)) {
  305. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  306. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  307. if (!$iFileId)
  308. return false;
  309. $aFile = $this->_oStorage->getFile($iFileId);
  310. if (!$aFile)
  311. return false;
  312. if ($this->_aObject['atime_tracking'])
  313. $this->_oDb->updateAccessTime($mixedHandler);
  314. return $this->_oStorage->getFileUrlById($iFileId);
  315. }
  316. if ($this->_sQueueTable && !$this->addToQueue($mixedHandler)) // add file to queue (when queue is enabled) if it isn't transcoded yet
  317. return false;
  318. return $this->getFileUrlNotReady($mixedHandler);
  319. }
  320. /**
  321. * alias for @see getFileUrl
  322. */
  323. public function getFileUrlById($mixedHandler)
  324. {
  325. return $this->getFileUrl($mixedHandler);
  326. }
  327. /**
  328. * Check if transcoded data is available. No need to call it directly, it is called automatically when it is needed.
  329. * @param $mixedHandler - file handler
  330. * @param $isCheckOutdated - check if transcoded file is outdated
  331. * @return false if there is no ready transcoded file is available or it is outdated, true if file is ready
  332. */
  333. public function isFileReady ($mixedHandler, $isCheckOutdated = true)
  334. {
  335. $sMethodFileReady = 'isFileReady_' . $this->_aObject['source_type'];
  336. return $this->$sMethodFileReady($mixedHandler, $isCheckOutdated);
  337. }
  338. /**
  339. * Transcode file, no need to call it directly, it is called automatically when it is needed.
  340. * @param $mixedHandler - file handler
  341. * @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
  342. * @return true on success, false on error
  343. */
  344. public function transcode ($mixedHandler, $iProfileId = 0)
  345. {
  346. $sExtChange = false;
  347. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_PROCESSING, '', gethostname());
  348. // create tmp file locally
  349. $sSuffix = $this->_sQueueStorage ? 'Queue' : $this->_aObject['source_type'];
  350. $sMethodStoreFile = 'storeFileLocally_' . $sSuffix;
  351. $sTmpFile = $this->$sMethodStoreFile($mixedHandler);
  352. if (!$sTmpFile) {
  353. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store local tmp file failed ($sMethodStoreFile)");
  354. return false;
  355. }
  356. // appply filters to tmp file
  357. $this->initFilters ();
  358. foreach ($this->_aObject['filters'] as $aParams) {
  359. $this->clearLog();
  360. $sMethodFilter = 'applyFilter_' . $aParams['filter'];
  361. if (!method_exists($this, $sMethodFilter))
  362. continue;
  363. if (!$this->$sMethodFilter($sTmpFile, $aParams['filter_params'])) {
  364. unlink($sTmpFile);
  365. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "filter failed: $sMethodFilter\n" . $this->getLog());
  366. return false;
  367. }
  368. if (!empty($aParams['filter_params']['force_type'])) {
  369. switch ($aParams['filter_params']['force_type']) {
  370. case 'jpeg':
  371. case 'jpg':
  372. $sExtChange = 'jpg';
  373. break;
  374. default:
  375. $sExtChange = $aParams['filter_params']['force_type'];
  376. }
  377. }
  378. if ($sExtChange) {
  379. $iDotPos = strrpos($sTmpFile, '.');
  380. $sExtOld = false === $iDotPos ? '' : substr($sTmpFile, $iDotPos+1);
  381. if ($sExtOld != $sExtChange) {
  382. $sTmpFileOld = $sTmpFile;
  383. $sTmpFile = false === $iDotPos ? $sTmpFile . '.' . $sExtChange : substr_replace($sTmpFile, $sExtChange, $iDotPos+1, strlen($sExtOld));
  384. if (!rename($sTmpFileOld, $sTmpFile)) {
  385. @unlink($sTmpFileOld);
  386. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "change file extension failed: $sTmpFileOld => $sTmpFile");
  387. return false;
  388. }
  389. }
  390. }
  391. }
  392. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  393. $bRet = true;
  394. if ($this->_sQueueStorage) {
  395. $sFileUrlResult = '';
  396. if (!($iFileIdResult = $this->storeTranscodedFileInQueueStorage($mixedHandler, $sTmpFile, $sFileUrlResult)))
  397. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store queue tmp file failed:\n" . $this->getLog());
  398. else
  399. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_COMPLETE, '', '', $iFileIdResult, $sFileUrlResult, $sExtChange);
  400. } else {
  401. $bRet = $this->storeTranscodedFile($mixedHandler, $sTmpFile, $iProfileId);
  402. if ($bRet)
  403. $this->deleteFromQueue($mixedHandler);
  404. else
  405. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, "store file failed:\n" . $this->getLog());
  406. }
  407. @unlink($sTmpFile);
  408. return $bRet;
  409. }
  410. /**
  411. * Delete outdated files by last access time.
  412. * @return number of pruned/deleted files
  413. */
  414. public function prune ()
  415. {
  416. $aFiles = $this->_oDb->getFilesForPruning();
  417. if (!$aFiles)
  418. return false;
  419. $iCount = 0;
  420. foreach ($aFiles as $r)
  421. $iCount += $this->_oStorage->deleteFile($r['file_id']) ? 1 : 0;
  422. return $iCount;
  423. }
  424. public function forceDevicePixelRatio ($i)
  425. {
  426. if (!$i)
  427. $this->_iDevicePixelRatio = false;
  428. elseif ($i >= 2)
  429. $this->_iDevicePixelRatio = 2;
  430. else
  431. $this->_iDevicePixelRatio = 1;
  432. }
  433. public function getDevicePixelRatio ()
  434. {
  435. if ($this->_iDevicePixelRatio)
  436. return $this->_iDevicePixelRatio;
  437. if (isset($_COOKIE['devicePixelRatio']) && (!isset($this->_aObject['source_params']['disable_retina']) || !$this->_aObject['source_params']['disable_retina'])) {
  438. $iDevicePixelRatio = intval($_COOKIE['devicePixelRatio']);
  439. if ($iDevicePixelRatio >= 2)
  440. return 2;
  441. }
  442. return 1;
  443. }
  444. public function getOrigFileUrl ($mixedHandler)
  445. {
  446. $sMethodStoreFile = 'getOrigFileUrl_' . $this->_aObject['source_type'];
  447. return $this->$sMethodStoreFile($mixedHandler);
  448. }
  449. /**
  450. * Get file url when file isn't transcoded yet
  451. */
  452. public function getFileUrlNotReady($mixedHandler)
  453. {
  454. return false;
  455. }
  456. public function getTmpFilename ($sOverrideName = false)
  457. {
  458. if ($sOverrideName)
  459. return BX_DIRECTORY_PATH_TMP . rand(10000, 99999) . $sOverrideName;
  460. return tempnam(BX_DIRECTORY_PATH_TMP, $this->_aObject['object']);
  461. }
  462. public function clearLog()
  463. {
  464. $this->_sLog = '';
  465. }
  466. public function addToLog($s)
  467. {
  468. $this->_sLog .= $s;
  469. }
  470. public function getLog()
  471. {
  472. return $this->_sLog;
  473. }
  474. public function deleteFromQueue ($mixedHandler)
  475. {
  476. $sErrMsg = "delete queue tmp file failed:\n";
  477. $a = $this->_oDb->getFromQueue($mixedHandler);
  478. if (!$a || $a['server'] != gethostname()) // deletion must be performed on the server where it was transcoded
  479. return false;
  480. if ($a['file_id_result'] && $this->_sQueueStorage && 0 !== strncmp($a['log'], $sErrMsg, strlen($sErrMsg))) {
  481. $oStorage = BxDolStorage::getObjectInstance($this->_sQueueStorage);
  482. if (!$oStorage->deleteFile($a['file_id_result'])) {
  483. $this->_oDb->updateQueueStatus($mixedHandler, BX_DOL_QUEUE_FAILED, $sErrMsg . $this->getLog());
  484. return false;
  485. }
  486. }
  487. return $this->_oDb->deleteFromQueue($mixedHandler);
  488. }
  489. public function storeTranscodedFile ($mixedHandler, $sTmpFile, $iProfileId)
  490. {
  491. $sMethodIsPrivate = 'isPrivate_' . $this->_aObject['source_type'];
  492. $isPrivate = $this->$sMethodIsPrivate($mixedHandler);
  493. $iFileId = $this->_oStorage->storeFileFromPath ($sTmpFile, $isPrivate, $iProfileId);
  494. if (!$iFileId) {
  495. $this->addToLog($this->_oStorage->getErrorString());
  496. return false;
  497. }
  498. if (!$this->_oDb->updateHandler($iFileId, $mixedHandler)) {
  499. $this->_oStorage->deleteFile($iFileId);
  500. return false;
  501. }
  502. $this->_oStorage->afterUploadCleanup($iFileId, $iProfileId);
  503. return true;
  504. }
  505. protected function storeTranscodedFileInQueueStorage ($mixedHandler, $sTmpFile, &$sFileUrlResult)
  506. {
  507. if (!$this->_sQueueStorage)
  508. return false;
  509. $oStorage = BxDolStorage::getObjectInstance($this->_sQueueStorage);
  510. $iFileIdResult = $oStorage->storeFileFromPath ($sTmpFile, 1, 0);
  511. if (!$iFileIdResult) {
  512. $this->addToLog($oStorage->getErrorString());
  513. return false;
  514. }
  515. if (!($sFileUrlResult = $oStorage->getFileUrlById($iFileIdResult))) {
  516. $this->_oStorage->deleteFile($iFileIdResult);
  517. return false;
  518. }
  519. $oStorage->afterUploadCleanup($iFileIdResult, 0);
  520. return $iFileIdResult;
  521. }
  522. protected function addToQueue($mixedHandler, $iProfileId = 0)
  523. {
  524. if ($this->_oDb->getFromQueue($mixedHandler))
  525. return true;
  526. $sFileUrl = $this->getOrigFileUrl($mixedHandler);
  527. return $this->_oDb->addToQueue ($mixedHandler, $sFileUrl, $iProfileId);
  528. }
  529. protected function applyFilter_Resize ($sFile, $aParams)
  530. {
  531. $o = BxDolImageResize::getInstance();
  532. $o->removeCropOptions ();
  533. $o->setSize (isset($aParams['w']) ? round($aParams['w'] * $this->getDevicePixelRatio()) : null, isset($aParams['h']) ? round($aParams['h'] * $this->getDevicePixelRatio()) : null);
  534. if (isset($aParams['crop_resize']) && $aParams['crop_resize'])
  535. $o->setAutoCrop (true);
  536. elseif (isset($aParams['square_resize']) && $aParams['square_resize'])
  537. $o->setSquareResize (true);
  538. else
  539. $o->setSquareResize (false);
  540. if (IMAGE_ERROR_SUCCESS == $o->resize($sFile))
  541. return true;
  542. return false;
  543. }
  544. protected function isFileReady_Folder ($mixedHandlerOrig, $isCheckOutdated = true)
  545. {
  546. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  547. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  548. if (!$iFileId)
  549. return false;
  550. $aFile = $this->_oStorage->getFile($iFileId);
  551. if (!$aFile)
  552. return false;
  553. if ($isCheckOutdated) { // warning, $isCheckOutdated is partially supported for Folder source type - file modification is not checked
  554. if ($this->_aObject['ts'] > $aFile['modified'] || !$this->getFilePath_Folder($mixedHandlerOrig)) { // if we changed transcoder object params or original file is deleted
  555. // delete file, so it will be recreated next time
  556. if ($this->_oStorage->deleteFile($aFile['id']))
  557. return false;
  558. }
  559. }
  560. return true;
  561. }
  562. protected function isFileReady_Storage ($mixedHandlerOrig, $isCheckOutdated = true)
  563. {
  564. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  565. $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
  566. if (!$iFileId)
  567. return false;
  568. $aFile = $this->_oStorage->getFile($iFileId);
  569. if (!$aFile)
  570. return false;
  571. if ($isCheckOutdated) {
  572. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  573. if ($oStorageOriginal) {
  574. $aFileOriginal = $oStorageOriginal->getFile($mixedHandlerOrig);
  575. if (!$aFileOriginal || $aFileOriginal['modified'] > $aFile['modified'] || $this->_aObject['ts'] >= $aFile['modified']) { // if original file was changed OR we changed transcoder object params
  576. // delete file, so it will be recreated next time
  577. if ($this->_oStorage->deleteFile($aFile['id']))
  578. return false;
  579. }
  580. }
  581. }
  582. return true;
  583. }
  584. protected function isPrivate_Folder ($mixedHandler)
  585. {
  586. if ('no' == $this->_aObject['private'])
  587. return false;
  588. return true;
  589. }
  590. protected function isPrivate_Storage ($mixedHandler)
  591. {
  592. switch ($this->_aObject['private']) {
  593. case 'no':
  594. return false;
  595. case 'yes':
  596. return true;
  597. default:
  598. case 'auto':
  599. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  600. if (!$oStorageOriginal)
  601. return true; // in case of error - make sure that file is not public accidentally
  602. $aFile = $oStorageOriginal->getFile($mixedHandler);
  603. return $oStorageOriginal->isFilePrivate($aFile['id']);
  604. }
  605. }
  606. protected function getFilePath_Folder ($mixedHandler)
  607. {
  608. $sPath = $this->_aObject['source_params']['path'] . $mixedHandler;
  609. if (!file_exists($sPath))
  610. $sPath = BX_DIRECTORY_PATH_ROOT . $sPath;
  611. if (!file_exists($sPath))
  612. return false;
  613. return $sPath;
  614. }
  615. protected function storeFileLocally_Folder ($mixedHandler)
  616. {
  617. $sPath = $this->getFilePath_Folder($mixedHandler);
  618. if (!$sPath)
  619. return false;
  620. $sTmpFile = $this->getTmpFilename ($mixedHandler);
  621. if (!copy($sPath, $sTmpFile))
  622. return false;
  623. return $sTmpFile;
  624. }
  625. protected function storeFileLocally_Storage ($mixedHandler)
  626. {
  627. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  628. if (!$oStorageOriginal)
  629. return false;
  630. $aFile = $oStorageOriginal->getFile($mixedHandler);
  631. if (!$aFile)
  632. return false;
  633. $sUrl = $oStorageOriginal->getFileUrlById($mixedHandler);
  634. if (!$sUrl)
  635. return false;
  636. $sFileData = bx_file_get_contents ($sUrl);
  637. if (false === $sFileData)
  638. return false;
  639. $sTmpFile = $this->getTmpFilename ($aFile['file_name']);
  640. if (!file_put_contents($sTmpFile, $sFileData))
  641. return false;
  642. return $sTmpFile;
  643. }
  644. protected function storeFileLocally_Queue ($mixedHandler)
  645. {
  646. $aQueue = $this->_oDb->getFromQueue($mixedHandler);
  647. if (!$aQueue || !$aQueue['file_url_source'])
  648. return false;
  649. $sFileData = bx_file_get_contents ($aQueue['file_url_source']);
  650. if (false === $sFileData)
  651. return false;
  652. $sTmpFile = $this->getTmpFilename ($mixedHandler);
  653. if (!file_put_contents($sTmpFile, $sFileData))
  654. return false;
  655. return $sTmpFile;
  656. }
  657. protected function getOrigFileUrl_Folder ($mixedHandler)
  658. {
  659. $sPath = $this->_aObject['source_params']['path'] . $mixedHandler;
  660. if (!file_exists(BX_DIRECTORY_PATH_ROOT . $sPath))
  661. return false;
  662. return BX_DOL_URL_ROOT . $sPath;
  663. }
  664. protected function getOrigFileUrl_Storage ($mixedHandler)
  665. {
  666. $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
  667. if (!$oStorageOriginal)
  668. return false;
  669. return $oStorageOriginal->getFileUrlById($mixedHandler);
  670. }
  671. protected function initFilters ()
  672. {
  673. if (false !== $this->_aObject['filters']) // filters are already initilized
  674. return;
  675. // get transcoder filters
  676. $aFilters = $this->_oDb->getTranscoderFilters();
  677. $this->_aObject['filters'] = array();
  678. foreach ($aFilters as $aFilter) {
  679. $aFilter['filter_params'] = $aFilter['filter_params'] ? unserialize($aFilter['filter_params']) : array();
  680. $this->_aObject['filters'][] = $aFilter;
  681. }
  682. }
  683. protected function clearCacheDB()
  684. {
  685. $oCacheDb = $this->_oDb->getDbCacheObject();
  686. return $oCacheDb->removeAllByPrefix('db_');
  687. }
  688. public function getDevicePixelRatioHandlerSuffix ()
  689. {
  690. $iDevicePixelRatio = $this->getDevicePixelRatio ();
  691. if ($iDevicePixelRatio >= 2)
  692. return $this->_sRetinaSuffix;
  693. return '';
  694. }
  695. protected function processHandlerForRetinaDevice ($mixedHandler)
  696. {
  697. return '' . $mixedHandler . $this->getDevicePixelRatioHandlerSuffix ();
  698. }
  699. static protected function _callFuncForObjectsArray ($mixed, $sFunc)
  700. {
  701. if (!is_array($mixed))
  702. $mixed = array($mixed);
  703. foreach ($mixed as $sObject) {
  704. $oTranscoder = self::getObjectInstance($sObject);
  705. if ($oTranscoder)
  706. $oTranscoder->$sFunc();
  707. }
  708. }
  709. /**
  710. * Get filter params
  711. * @param $sName filter name
  712. * @return array of filter params if filter was found, or false if given filter isn't enabled for the transcoder
  713. */
  714. public function getFilterParams ($sName)
  715. {
  716. $this->initFilters ();
  717. foreach ($this->_aObject['filters'] as $aParams)
  718. if ($sName == $aParams['filter'])
  719. return $aParams['filter_params'];
  720. return false;
  721. }
  722. }
  723. /** @} */