vendor/pimcore/pimcore/models/Asset/Video.php line 107

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\Asset;
  15. use Pimcore\Event\FrontendEvents;
  16. use Pimcore\File;
  17. use Pimcore\Logger;
  18. use Pimcore\Model;
  19. use Pimcore\Tool;
  20. use Symfony\Component\EventDispatcher\GenericEvent;
  21. /**
  22.  * @method \Pimcore\Model\Asset\Dao getDao()
  23.  */
  24. class Video extends Model\Asset
  25. {
  26.     use Model\Asset\MetaData\EmbeddedMetaDataTrait;
  27.     /**
  28.      * {@inheritdoc}
  29.      */
  30.     protected $type 'video';
  31.     /**
  32.      * {@inheritdoc}
  33.      */
  34.     protected function update($params = [])
  35.     {
  36.         if ($this->getDataChanged()) {
  37.             foreach (['duration''videoWidth''videoHeight'] as $key) {
  38.                 $this->removeCustomSetting($key);
  39.             }
  40.         }
  41.         if ($params['isUpdate']) {
  42.             $this->clearThumbnails();
  43.         }
  44.         parent::update($params);
  45.     }
  46.     /**
  47.      * {@inheritdoc}
  48.      */
  49.     public function clearThumbnails($force false)
  50.     {
  51.         if ($this->getDataChanged() || $force) {
  52.             // clear the thumbnail custom settings
  53.             $this->setCustomSetting('thumbnails'null);
  54.             parent::clearThumbnails($force);
  55.         }
  56.     }
  57.     /**
  58.      * @internal
  59.      *
  60.      * @param string|Video\Thumbnail\Config $config
  61.      *
  62.      * @return Video\Thumbnail\Config|null
  63.      *
  64.      * @throws Model\Exception\NotFoundException
  65.      */
  66.     public function getThumbnailConfig($config)
  67.     {
  68.         $thumbnail null;
  69.         if (is_string($config)) {
  70.             $thumbnail Video\Thumbnail\Config::getByName($config);
  71.             if ($thumbnail === null) {
  72.                 throw new Model\Exception\NotFoundException('Video Thumbnail definition "' $config '" does not exist');
  73.             }
  74.         } elseif ($config instanceof Video\Thumbnail\Config) {
  75.             $thumbnail $config;
  76.         }
  77.         return $thumbnail;
  78.     }
  79.     /**
  80.      * Returns a path to a given thumbnail or an thumbnail configuration
  81.      *
  82.      * @param string|Video\Thumbnail\Config $thumbnailName
  83.      * @param array $onlyFormats
  84.      *
  85.      * @return array|null
  86.      */
  87.     public function getThumbnail($thumbnailName$onlyFormats = [])
  88.     {
  89.         $thumbnail $this->getThumbnailConfig($thumbnailName);
  90.         if ($thumbnail) {
  91.             try {
  92.                 Video\Thumbnail\Processor::process($this$thumbnail$onlyFormats);
  93.                 // check for existing videos
  94.                 $customSetting $this->getCustomSetting('thumbnails');
  95.                 if (is_array($customSetting) && array_key_exists($thumbnail->getName(), $customSetting)) {
  96.                     foreach ($customSetting[$thumbnail->getName()]['formats'] as $pathKey => &$path) {
  97.                         if ($pathKey == 'medias') {
  98.                             foreach ($path as &$format) {
  99.                                 foreach ($format as &$f) {
  100.                                     $f $this->enrichThumbnailPath($f);
  101.                                 }
  102.                             }
  103.                         } else {
  104.                             $path $this->enrichThumbnailPath($path);
  105.                         }
  106.                     }
  107.                     return $customSetting[$thumbnail->getName()];
  108.                 }
  109.             } catch (\Exception $e) {
  110.                 Logger::error("Couldn't create thumbnail of video " $this->getRealFullPath());
  111.                 Logger::error((string) $e);
  112.             }
  113.         }
  114.         return null;
  115.     }
  116.     /**
  117.      * @param string $path
  118.      *
  119.      * @return string
  120.      */
  121.     private function enrichThumbnailPath($path)
  122.     {
  123.         $fullPath rtrim($this->getRealPath(), '/') . '/' ltrim($path'/');
  124.         if (Tool::isFrontend()) {
  125.             $path urlencode_ignore_slash($fullPath);
  126.             $prefix \Pimcore::getContainer()->getParameter('pimcore.config')['assets']['frontend_prefixes']['thumbnail'];
  127.             $path $prefix $path;
  128.         }
  129.         $event = new GenericEvent($this, [
  130.             'filesystemPath' => $fullPath,
  131.             'frontendPath' => $path,
  132.         ]);
  133.         \Pimcore::getEventDispatcher()->dispatch($eventFrontendEvents::ASSET_VIDEO_THUMBNAIL);
  134.         return $event->getArgument('frontendPath');
  135.     }
  136.     /**
  137.      * @param string|array|Image\Thumbnail\Config $thumbnailName
  138.      * @param int|null $timeOffset
  139.      * @param Image|null $imageAsset
  140.      *
  141.      * @return Video\ImageThumbnail
  142.      */
  143.     public function getImageThumbnail($thumbnailName$timeOffset null$imageAsset null)
  144.     {
  145.         if (!\Pimcore\Video::isAvailable()) {
  146.             Logger::error("Couldn't create image-thumbnail of video " $this->getRealFullPath() . ' no video adapter is available');
  147.             return new Video\ImageThumbnail(null); // returns error image
  148.         }
  149.         if (!$this->getCustomSetting('videoWidth') || !$this->getCustomSetting('videoHeight')) {
  150.             Logger::info('Image thumbnail not yet available, processing is done asynchronously.');
  151.             $this->addToUpdateTaskQueue();
  152.             return new Video\ImageThumbnail(null); // returns error image
  153.         }
  154.         return new Video\ImageThumbnail($this$thumbnailName$timeOffset$imageAsset);
  155.     }
  156.     /**
  157.      * @internal
  158.      *
  159.      * @param string|null $filePath
  160.      *
  161.      * @return float|null
  162.      */
  163.     public function getDurationFromBackend(?string $filePath null)
  164.     {
  165.         if (\Pimcore\Video::isAvailable()) {
  166.             if (!$filePath) {
  167.                 $filePath $this->getLocalFile();
  168.             }
  169.             $converter \Pimcore\Video::getInstance();
  170.             $converter->load($filePath, ['asset' => $this]);
  171.             return $converter->getDuration();
  172.         }
  173.         return null;
  174.     }
  175.     /**
  176.      * @internal
  177.      *
  178.      * @return array|null
  179.      */
  180.     public function getDimensionsFromBackend()
  181.     {
  182.         if (\Pimcore\Video::isAvailable()) {
  183.             $converter \Pimcore\Video::getInstance();
  184.             $converter->load($this->getLocalFile(), ['asset' => $this]);
  185.             return $converter->getDimensions();
  186.         }
  187.         return null;
  188.     }
  189.     /**
  190.      * @return int|null
  191.      */
  192.     public function getDuration()
  193.     {
  194.         $duration $this->getCustomSetting('duration');
  195.         if (!$duration) {
  196.             $duration $this->getDurationFromBackend();
  197.             if ($duration) {
  198.                 $this->setCustomSetting('duration'$duration);
  199.                 Model\Version::disable();
  200.                 $this->save(); // auto save
  201.                 Model\Version::enable();
  202.             }
  203.         }
  204.         return $duration;
  205.     }
  206.     /**
  207.      * @return array|null
  208.      */
  209.     public function getDimensions()
  210.     {
  211.         $dimensions null;
  212.         $width $this->getCustomSetting('videoWidth');
  213.         $height $this->getCustomSetting('videoHeight');
  214.         if (!$width || !$height) {
  215.             $dimensions $this->getDimensionsFromBackend();
  216.             if ($dimensions) {
  217.                 $this->setCustomSetting('videoWidth'$dimensions['width']);
  218.                 $this->setCustomSetting('videoHeight'$dimensions['height']);
  219.                 Model\Version::disable();
  220.                 $this->save(); // auto save
  221.                 Model\Version::enable();
  222.             }
  223.         } else {
  224.             $dimensions = [
  225.                 'width' => $width,
  226.                 'height' => $height,
  227.             ];
  228.         }
  229.         return $dimensions;
  230.     }
  231.     /**
  232.      * @return int|null
  233.      */
  234.     public function getWidth()
  235.     {
  236.         $dimensions $this->getDimensions();
  237.         if ($dimensions) {
  238.             return $dimensions['width'];
  239.         }
  240.         return null;
  241.     }
  242.     /**
  243.      * @return int|null
  244.      */
  245.     public function getHeight()
  246.     {
  247.         $dimensions $this->getDimensions();
  248.         if ($dimensions) {
  249.             return $dimensions['height'];
  250.         }
  251.         return null;
  252.     }
  253.     /**
  254.      * @internal
  255.      *
  256.      * @return array
  257.      */
  258.     public function getSphericalMetaData()
  259.     {
  260.         $data = [];
  261.         if (in_array(File::getFileExtension($this->getFilename()), ['mp4''webm'])) {
  262.             $chunkSize 1024;
  263.             $file_pointer $this->getStream();
  264.             $tag '<rdf:SphericalVideo';
  265.             $tagLength strlen($tag);
  266.             $buffer false;
  267.             // find open tag
  268.             $overlapString '';
  269.             while ($buffer === false && ($chunk fread($file_pointer$chunkSize)) !== false) {
  270.                 if (strlen($chunk) <= $tagLength) {
  271.                     break;
  272.                 }
  273.                 $chunk $overlapString $chunk;
  274.                 if (($position strpos($chunk$tag)) === false) {
  275.                     // if open tag not found, back up just in case the open tag is on the split.
  276.                     $overlapString substr($chunk$tagLength * -1);
  277.                 } else {
  278.                     $buffer substr($chunk$position);
  279.                 }
  280.             }
  281.             if ($buffer !== false) {
  282.                 $tag '</rdf:SphericalVideo>';
  283.                 $tagLength strlen($tag);
  284.                 $offset 0;
  285.                 while (($position strpos($buffer$tag$offset)) === false && ($chunk fread($file_pointer,
  286.                     $chunkSize)) !== false && !empty($chunk)) {
  287.                     $offset strlen($buffer) - $tagLength// subtract the tag size just in case it's split between chunks.
  288.                     $buffer .= $chunk;
  289.                 }
  290.                 if ($position === false) {
  291.                     // this would mean the open tag was found, but the close tag was not.  Maybe file corruption?
  292.                     throw new \RuntimeException('No close tag found.  Possibly corrupted file.');
  293.                 } else {
  294.                     $buffer substr($buffer0$position $tagLength);
  295.                 }
  296.                 $buffer preg_replace('/xmlns[^=]*="[^"]*"/i'''$buffer);
  297.                 $buffer preg_replace('@<(/)?([a-zA-Z]+):([a-zA-Z]+)@''<$1$2____$3'$buffer);
  298.                 $xml = @simplexml_load_string($buffer);
  299.                 $data object2array($xml);
  300.             }
  301.             fclose($file_pointer);
  302.         }
  303.         // remove namespace prefixes if possible
  304.         $resultData = [];
  305.         array_walk($data, function ($value$key) use (&$resultData) {
  306.             $parts explode('____'$key);
  307.             $length count($parts);
  308.             if ($length 1) {
  309.                 $name $parts[$length 1];
  310.                 if (!isset($resultData[$name])) {
  311.                     $key $name;
  312.                 }
  313.             }
  314.             $resultData[$key] = $value;
  315.         });
  316.         return $resultData;
  317.     }
  318. }