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

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($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(), '/') . $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.         return new Video\ImageThumbnail($this$thumbnailName$timeOffset$imageAsset);
  150.     }
  151.     /**
  152.      * @internal
  153.      *
  154.      * @param string|null $filePath
  155.      *
  156.      * @return float|null
  157.      *
  158.      * @throws \Exception
  159.      */
  160.     public function getDurationFromBackend(?string $filePath null)
  161.     {
  162.         if (\Pimcore\Video::isAvailable()) {
  163.             if (!$filePath) {
  164.                 $filePath $this->getLocalFile();
  165.             }
  166.             $converter \Pimcore\Video::getInstance();
  167.             $converter->load($filePath, ['asset' => $this]);
  168.             return $converter->getDuration();
  169.         }
  170.         return null;
  171.     }
  172.     /**
  173.      * @internal
  174.      *
  175.      * @return array|null
  176.      *
  177.      * @throws \Exception
  178.      */
  179.     public function getDimensionsFromBackend()
  180.     {
  181.         if (\Pimcore\Video::isAvailable()) {
  182.             $converter \Pimcore\Video::getInstance();
  183.             $converter->load($this->getLocalFile(), ['asset' => $this]);
  184.             return $converter->getDimensions();
  185.         }
  186.         return null;
  187.     }
  188.     /**
  189.      * @return int|null
  190.      */
  191.     public function getDuration()
  192.     {
  193.         $duration $this->getCustomSetting('duration');
  194.         if (!$duration) {
  195.             $duration $this->getDurationFromBackend();
  196.             if ($duration) {
  197.                 $this->setCustomSetting('duration'$duration);
  198.                 Model\Version::disable();
  199.                 $this->save(); // auto save
  200.                 Model\Version::enable();
  201.             }
  202.         }
  203.         return $duration;
  204.     }
  205.     /**
  206.      * @return array|null
  207.      */
  208.     public function getDimensions()
  209.     {
  210.         $dimensions null;
  211.         $width $this->getCustomSetting('videoWidth');
  212.         $height $this->getCustomSetting('videoHeight');
  213.         if (!$width || !$height) {
  214.             $dimensions $this->getDimensionsFromBackend();
  215.             if ($dimensions) {
  216.                 $this->setCustomSetting('videoWidth'$dimensions['width']);
  217.                 $this->setCustomSetting('videoHeight'$dimensions['height']);
  218.                 Model\Version::disable();
  219.                 $this->save(); // auto save
  220.                 Model\Version::enable();
  221.             }
  222.         } else {
  223.             $dimensions = [
  224.                 'width' => $width,
  225.                 'height' => $height,
  226.             ];
  227.         }
  228.         return $dimensions;
  229.     }
  230.     /**
  231.      * @return int|null
  232.      */
  233.     public function getWidth()
  234.     {
  235.         $dimensions $this->getDimensions();
  236.         if ($dimensions) {
  237.             return $dimensions['width'];
  238.         }
  239.         return null;
  240.     }
  241.     /**
  242.      * @return int|null
  243.      */
  244.     public function getHeight()
  245.     {
  246.         $dimensions $this->getDimensions();
  247.         if ($dimensions) {
  248.             return $dimensions['height'];
  249.         }
  250.         return null;
  251.     }
  252.     /**
  253.      * @internal
  254.      *
  255.      * @return array
  256.      */
  257.     public function getSphericalMetaData()
  258.     {
  259.         $data = [];
  260.         if (in_array(File::getFileExtension($this->getFilename()), ['mp4''webm'])) {
  261.             $chunkSize 1024;
  262.             $file_pointer $this->getStream();
  263.             $tag '<rdf:SphericalVideo';
  264.             $tagLength strlen($tag);
  265.             $buffer false;
  266.             // find open tag
  267.             $overlapString '';
  268.             while ($buffer === false && ($chunk fread($file_pointer$chunkSize)) !== false) {
  269.                 if (strlen($chunk) <= $tagLength) {
  270.                     break;
  271.                 }
  272.                 $chunk $overlapString $chunk;
  273.                 if (($position strpos($chunk$tag)) === false) {
  274.                     // if open tag not found, back up just in case the open tag is on the split.
  275.                     $overlapString substr($chunk$tagLength * -1);
  276.                 } else {
  277.                     $buffer substr($chunk$position);
  278.                 }
  279.             }
  280.             if ($buffer !== false) {
  281.                 $tag '</rdf:SphericalVideo>';
  282.                 $tagLength strlen($tag);
  283.                 $offset 0;
  284.                 while (($position strpos($buffer$tag$offset)) === false && ($chunk fread($file_pointer,
  285.                         $chunkSize)) !== false && !empty($chunk)) {
  286.                     $offset strlen($buffer) - $tagLength// subtract the tag size just in case it's split between chunks.
  287.                     $buffer .= $chunk;
  288.                 }
  289.                 if ($position === false) {
  290.                     // this would mean the open tag was found, but the close tag was not.  Maybe file corruption?
  291.                     throw new \RuntimeException('No close tag found.  Possibly corrupted file.');
  292.                 } else {
  293.                     $buffer substr($buffer0$position $tagLength);
  294.                 }
  295.                 $buffer preg_replace('/xmlns[^=]*="[^"]*"/i'''$buffer);
  296.                 $buffer preg_replace('@<(/)?([a-zA-Z]+):([a-zA-Z]+)@''<$1$2____$3'$buffer);
  297.                 $xml = @simplexml_load_string($buffer);
  298.                 $data object2array($xml);
  299.             }
  300.             fclose($file_pointer);
  301.         }
  302.         // remove namespace prefixes if possible
  303.         $resultData = [];
  304.         array_walk($data, function ($value$key) use (&$resultData) {
  305.             $parts explode('____'$key);
  306.             $length count($parts);
  307.             if ($length 1) {
  308.                 $name $parts[$length 1];
  309.                 if (!isset($resultData[$name])) {
  310.                     $key $name;
  311.                 }
  312.             }
  313.             $resultData[$key] = $value;
  314.         });
  315.         return $resultData;
  316.     }
  317. }