vendor/pimcore/pimcore/models/Document/Editable/Video.php line 27

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\Document\Editable;
  15. use Pimcore\Bundle\CoreBundle\EventListener\Frontend\FullPageCacheListener;
  16. use Pimcore\Logger;
  17. use Pimcore\Model;
  18. use Pimcore\Model\Asset;
  19. use Pimcore\Tool;
  20. /**
  21.  * @method \Pimcore\Model\Document\Editable\Dao getDao()
  22.  */
  23. class Video extends Model\Document\Editable implements IdRewriterInterface
  24. {
  25.     public const TYPE_ASSET 'asset';
  26.     public const TYPE_YOUTUBE 'youtube';
  27.     public const TYPE_VIMEO 'vimeo';
  28.     public const TYPE_DAILYMOTION 'dailymotion';
  29.     public const ALLOWED_TYPES = [
  30.         self::TYPE_ASSET,
  31.         self::TYPE_YOUTUBE,
  32.         self::TYPE_VIMEO,
  33.         self::TYPE_DAILYMOTION,
  34.     ];
  35.     /**
  36.      * contains depending on the type of the video the unique identifier eg. "http://www.youtube.com", "789", ...
  37.      *
  38.      * @internal
  39.      *
  40.      * @var int|string|null
  41.      */
  42.     protected $id;
  43.     /**
  44.      * one of self::ALLOWED_TYPES
  45.      *
  46.      * @internal
  47.      *
  48.      * @var string|null
  49.      */
  50.     protected $type;
  51.     /**
  52.      * asset ID of poster image
  53.      *
  54.      * @internal
  55.      *
  56.      * @var int|null
  57.      */
  58.     protected $poster;
  59.     /**
  60.      * @internal
  61.      *
  62.      * @var string
  63.      */
  64.     protected $title '';
  65.     /**
  66.      * @internal
  67.      *
  68.      * @var string
  69.      */
  70.     protected $description '';
  71.     /**
  72.      * @internal
  73.      *
  74.      * @var array|null
  75.      */
  76.     protected $allowedTypes;
  77.     /**
  78.      * @param int|string|null $id
  79.      *
  80.      * @return Video
  81.      */
  82.     public function setId($id)
  83.     {
  84.         $this->id $id;
  85.         return $this;
  86.     }
  87.     /**
  88.      * @return int|string|null
  89.      */
  90.     public function getId()
  91.     {
  92.         return $this->id;
  93.     }
  94.     /**
  95.      * @param string $title
  96.      *
  97.      * @return $this
  98.      */
  99.     public function setTitle($title)
  100.     {
  101.         $this->title $title;
  102.         return $this;
  103.     }
  104.     /**
  105.      * @return string
  106.      */
  107.     public function getTitle()
  108.     {
  109.         if (!$this->title && $this->getVideoAsset()) {
  110.             // default title for microformats
  111.             return $this->getVideoAsset()->getFilename();
  112.         }
  113.         return $this->title;
  114.     }
  115.     /**
  116.      * @param string $description
  117.      *
  118.      * @return $this
  119.      */
  120.     public function setDescription($description)
  121.     {
  122.         $this->description $description;
  123.         return $this;
  124.     }
  125.     /**
  126.      * @return string
  127.      */
  128.     public function getDescription()
  129.     {
  130.         if (!$this->description) {
  131.             // default description for microformats
  132.             return $this->getTitle();
  133.         }
  134.         return $this->description;
  135.     }
  136.     /**
  137.      * @param int|null $id
  138.      *
  139.      * @return $this
  140.      */
  141.     public function setPoster($id)
  142.     {
  143.         $this->poster $id;
  144.         return $this;
  145.     }
  146.     /**
  147.      * @return int|null
  148.      */
  149.     public function getPoster()
  150.     {
  151.         return $this->poster;
  152.     }
  153.     /**
  154.      * @param string $type
  155.      *
  156.      * @return $this
  157.      */
  158.     public function setType($type)
  159.     {
  160.         $this->type $type;
  161.         return $this;
  162.     }
  163.     /**
  164.      * {@inheritdoc}
  165.      */
  166.     public function getType()
  167.     {
  168.         return 'video';
  169.     }
  170.     /**
  171.      * @param array $allowedTypes
  172.      *
  173.      * @return $this
  174.      */
  175.     public function setAllowedTypes($allowedTypes)
  176.     {
  177.         $this->allowedTypes $allowedTypes;
  178.         return $this;
  179.     }
  180.     /**
  181.      * @return array
  182.      */
  183.     public function getAllowedTypes()
  184.     {
  185.         if ($this->allowedTypes === null) {
  186.             $this->updateAllowedTypesFromConfig($this->getConfig());
  187.         }
  188.         return $this->allowedTypes;
  189.     }
  190.     /**
  191.      * {@inheritdoc}
  192.      */
  193.     public function getData()
  194.     {
  195.         $path $this->id;
  196.         if ($this->type === self::TYPE_ASSET && ($video Asset::getById($this->id))) {
  197.             $path $video->getFullPath();
  198.         }
  199.         $allowedTypes $this->getAllowedTypes();
  200.         if (
  201.             empty($this->type) === true
  202.             || in_array($this->type$allowedTypestrue) === false
  203.         ) {
  204.             // Set the first type in array as default selection for dropdown
  205.             $this->type $allowedTypes[0];
  206.             // Reset "id" and "path" to prevent invalid references
  207.             $this->id   '';
  208.             $path       '';
  209.         }
  210.         $poster Asset::getById($this->poster);
  211.         return [
  212.             'id'           => $this->id,
  213.             'type'         => $this->type,
  214.             'allowedTypes' => $allowedTypes,
  215.             'title'        => $this->title,
  216.             'description'  => $this->description,
  217.             'path'         => $path,
  218.             'poster'       => $poster $poster->getFullPath() : '',
  219.         ];
  220.     }
  221.     /**
  222.      * {@inheritdoc}
  223.      */
  224.     protected function getDataEditmode()
  225.     {
  226.         $data $this->getData();
  227.         $poster Asset::getById($this->poster);
  228.         if ($poster) {
  229.             $data['poster'] = $poster->getRealFullPath();
  230.         }
  231.         if ($this->type === self::TYPE_ASSET && ($video Asset::getById($this->id))) {
  232.             $data['path'] = $video->getRealFullPath();
  233.         }
  234.         return $data;
  235.     }
  236.     /**
  237.      * {@inheritdoc}
  238.      */
  239.     public function getDataForResource()
  240.     {
  241.         return [
  242.             'id'           => $this->id,
  243.             'type'         => $this->type,
  244.             'allowedTypes' => $this->getAllowedTypes(),
  245.             'title'        => $this->title,
  246.             'description'  => $this->description,
  247.             'poster'       => $this->poster,
  248.         ];
  249.     }
  250.     /**
  251.      * {@inheritdoc}
  252.      */
  253.     public function frontend()
  254.     {
  255.         $inAdmin false;
  256.         $args    func_get_args();
  257.         if (array_key_exists(0$args)) {
  258.             $inAdmin $args[0];
  259.         }
  260.         if (
  261.             empty($this->id) === true
  262.             || empty($this->type) === true
  263.             || in_array($this->type$this->getAllowedTypes(), true) === false
  264.         ) {
  265.             return $this->getEmptyCode();
  266.         } elseif ($this->type === self::TYPE_ASSET) {
  267.             return $this->getAssetCode($inAdmin);
  268.         } elseif ($this->type === self::TYPE_YOUTUBE) {
  269.             return $this->getYoutubeCode($inAdmin);
  270.         } elseif ($this->type === self::TYPE_VIMEO) {
  271.             return $this->getVimeoCode($inAdmin);
  272.         } elseif ($this->type === self::TYPE_DAILYMOTION) {
  273.             return $this->getDailymotionCode($inAdmin);
  274.         } elseif ($this->type === 'url') {
  275.             return $this->getUrlCode();
  276.         }
  277.         return $this->getEmptyCode();
  278.     }
  279.     /**
  280.      * {@inheritdoc}
  281.      */
  282.     public function resolveDependencies()
  283.     {
  284.         $dependencies = [];
  285.         if ($this->type === self::TYPE_ASSET) {
  286.             $asset Asset::getById($this->id);
  287.             if ($asset instanceof Asset) {
  288.                 $key 'asset_' $asset->getId();
  289.                 $dependencies[$key] = [
  290.                     'id' => $asset->getId(),
  291.                     'type' => self::TYPE_ASSET,
  292.                 ];
  293.             }
  294.         }
  295.         if ($poster Asset::getById($this->poster)) {
  296.             $key 'asset_' $poster->getId();
  297.             $dependencies[$key] = [
  298.                 'id' => $poster->getId(),
  299.                 'type' => self::TYPE_ASSET,
  300.             ];
  301.         }
  302.         return $dependencies;
  303.     }
  304.     /**
  305.      * {@inheritdoc}
  306.      */
  307.     public function checkValidity()
  308.     {
  309.         $valid true;
  310.         if ($this->type === self::TYPE_ASSET && !empty($this->id)) {
  311.             $el Asset::getById($this->id);
  312.             if (!$el instanceof Asset) {
  313.                 $valid false;
  314.                 Logger::notice('Detected invalid relation, removing reference to non existent asset with id ['.$this->id.']');
  315.                 $this->id   null;
  316.                 $this->type null;
  317.             }
  318.         }
  319.         if (!($poster Asset::getById($this->poster))) {
  320.             $valid false;
  321.             Logger::notice('Detected invalid relation, removing reference to non existent asset with id ['.$this->id.']');
  322.             $this->poster null;
  323.         }
  324.         return $valid;
  325.     }
  326.     /**
  327.      * {@inheritdoc}
  328.      */
  329.     public function admin()
  330.     {
  331.         $html parent::admin();
  332.         // get frontendcode for preview
  333.         // put the video code inside the generic code
  334.         $html str_replace('</div>'$this->frontend(true) . '</div>'$html);
  335.         return $html;
  336.     }
  337.     /**
  338.      * {@inheritdoc}
  339.      */
  340.     public function setDataFromResource($data)
  341.     {
  342.         if (!empty($data)) {
  343.             $data \Pimcore\Tool\Serialize::unserialize($data);
  344.         }
  345.         $this->id $data['id'];
  346.         $this->type $data['type'];
  347.         $this->poster $data['poster'];
  348.         $this->title $data['title'];
  349.         $this->description $data['description'];
  350.         return $this;
  351.     }
  352.     /**
  353.      * {@inheritdoc}
  354.      */
  355.     public function setDataFromEditmode($data)
  356.     {
  357.         if (isset($data['type'])
  358.             && in_array($data['type'], self::ALLOWED_TYPEStrue) === true
  359.         ) {
  360.             $this->type $data['type'];
  361.         }
  362.         if (isset($data['title'])) {
  363.             $this->title $data['title'];
  364.         }
  365.         if (isset($data['description'])) {
  366.             $this->description $data['description'];
  367.         }
  368.         // this is to be backward compatible to <= v 1.4.7
  369.         if (isset($data['id']) && $data['id']) {
  370.             $data['path'] = $data['id'];
  371.         }
  372.         $video Asset::getByPath($data['path']);
  373.         if ($video instanceof Asset\Video) {
  374.             $this->id $video->getId();
  375.         } else {
  376.             $this->id $data['path'];
  377.         }
  378.         $this->poster null;
  379.         $poster Asset::getByPath($data['poster']);
  380.         if ($poster instanceof Asset\Image) {
  381.             $this->poster $poster->getId();
  382.         }
  383.         return $this;
  384.     }
  385.     /**
  386.      * @return int|string
  387.      */
  388.     public function getWidth()
  389.     {
  390.         return $this->getConfig()['width'] ?? '100%';
  391.     }
  392.     /**
  393.      * @return string
  394.      */
  395.     private function getWidthWithUnit()
  396.     {
  397.         $width $this->getWidth();
  398.         if (is_numeric($width)) {
  399.             $width .= 'px';
  400.         }
  401.         return $width;
  402.     }
  403.     /**
  404.      * @return string
  405.      */
  406.     private function getHeightWithUnit()
  407.     {
  408.         $height $this->getHeight();
  409.         if (is_numeric($height)) {
  410.             $height .= 'px';
  411.         }
  412.         return $height;
  413.     }
  414.     /**
  415.      * @return int|string
  416.      */
  417.     public function getHeight()
  418.     {
  419.         return $this->getConfig()['height'] ?? 300;
  420.     }
  421.     private function getAssetCode(bool $inAdmin false): string
  422.     {
  423.         $asset Asset::getById($this->id);
  424.         $config $this->getConfig();
  425.         $thumbnailConfig $config['thumbnail'] ?? null;
  426.         // compatibility mode when FFMPEG is not present or no thumbnail config is given
  427.         if (!\Pimcore\Video::isAvailable() || !$thumbnailConfig) {
  428.             if ($asset instanceof Asset\Video && preg_match("/\.(f4v|flv|mp4)/i"$asset->getFullPath())) {
  429.                 $image $this->getPosterThumbnailImage($asset);
  430.                 return $this->getHtml5Code(['mp4' => (string) $asset], $image);
  431.             }
  432.             return $this->getErrorCode('Asset is not a video, or missing thumbnail configuration');
  433.         }
  434.         if ($asset instanceof Asset\Video) {
  435.             $thumbnail $asset->getThumbnail($thumbnailConfig);
  436.             if ($thumbnail) {
  437.                 $image $this->getPosterThumbnailImage($asset);
  438.                 if ($inAdmin && isset($config['editmodeImagePreview']) && $config['editmodeImagePreview']) {
  439.                     $code '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">';
  440.                     $code .= '<img width="' $this->getWidth() . '" src="' $image '" />';
  441.                     $code .= '</div>';
  442.                     return $code;
  443.                 }
  444.                 if ($thumbnail['status'] === 'finished') {
  445.                     return $this->getHtml5Code($thumbnail['formats'], $image);
  446.                 }
  447.                 if ($thumbnail['status'] === 'inprogress') {
  448.                     // disable the output-cache if enabled
  449.                     $cacheService \Pimcore::getContainer()->get(FullPageCacheListener::class);
  450.                     $cacheService->disable('Video rendering in progress');
  451.                     return $this->getProgressCode($image);
  452.                 }
  453.                 return $this->getErrorCode('The video conversion failed, please see the log files in /var/log for more details.');
  454.             }
  455.             return $this->getErrorCode("The given thumbnail doesn't exist: '" $thumbnailConfig "'");
  456.         }
  457.         return $this->getEmptyCode();
  458.     }
  459.     /**
  460.      * @param Asset\Video $asset
  461.      *
  462.      * @return Asset\Image\Thumbnail|Asset\Video\ImageThumbnail
  463.      */
  464.     private function getPosterThumbnailImage(Asset\Video $asset)
  465.     {
  466.         $config $this->getConfig();
  467.         if (!array_key_exists('imagethumbnail'$config) || empty($config['imagethumbnail'])) {
  468.             $thumbnailConfig $asset->getThumbnailConfig($config['thumbnail'] ?? null);
  469.             if ($thumbnailConfig instanceof Asset\Video\Thumbnail\Config) {
  470.                 // try to get the dimensions out ouf the video thumbnail
  471.                 $imageThumbnailConf $thumbnailConfig->getEstimatedDimensions();
  472.                 $imageThumbnailConf['format'] = 'JPEG';
  473.             }
  474.         } else {
  475.             $imageThumbnailConf $config['imagethumbnail'];
  476.         }
  477.         if (empty($imageThumbnailConf)) {
  478.             $imageThumbnailConf['width'] = 800;
  479.             $imageThumbnailConf['format'] = 'JPEG';
  480.         }
  481.         if ($this->poster && ($poster Asset\Image::getById($this->poster))) {
  482.             return $poster->getThumbnail($imageThumbnailConf);
  483.         }
  484.         if (
  485.             $asset->getCustomSetting('image_thumbnail_asset') &&
  486.             ($customPreviewAsset Asset\Image::getById($asset->getCustomSetting('image_thumbnail_asset')))
  487.         ) {
  488.             return $customPreviewAsset->getThumbnail($imageThumbnailConf);
  489.         }
  490.         return $asset->getImageThumbnail($imageThumbnailConf);
  491.     }
  492.     /**
  493.      * @return string
  494.      */
  495.     private function getUrlCode()
  496.     {
  497.         return $this->getHtml5Code(['mp4' => (string) $this->id]);
  498.     }
  499.     /**
  500.      * @param string $message
  501.      *
  502.      * @return string
  503.      */
  504.     private function getErrorCode($message '')
  505.     {
  506.         $width $this->getWidth();
  507.         // If contains at least one digit (0-9), then assume it is a value that can be calculated,
  508.         // otherwise it is likely be `auto`,`inherit`,etc..
  509.         if (preg_match('/[\d]/'$width)) {
  510.             // when is numeric, assume there are no length units nor %, and considering the value as pixels
  511.             if (is_numeric($width)) {
  512.                 $width .= 'px';
  513.             }
  514.             $width 'calc(' $width ' - 1px)';
  515.         }
  516.         $height $this->getHeight();
  517.         if (preg_match('/[\d]/'$height)) {
  518.             if (is_numeric($height)) {
  519.                 $height .= 'px';
  520.             }
  521.             $height 'calc(' $height ' - 1px)';
  522.         }
  523.         // only display error message in debug mode
  524.         if (!\Pimcore::inDebugMode()) {
  525.             $message '';
  526.         }
  527.         $code '
  528.         <div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video">
  529.             <div class="pimcore_editable_video_error" style="text-align:center; width: ' $width '; height: ' $height '; border:1px solid #000; background: url(/bundles/pimcoreadmin/img/filetype-not-supported.svg) no-repeat center center #fff;">
  530.                 ' $message '
  531.             </div>
  532.         </div>';
  533.         return $code;
  534.     }
  535.     /**
  536.      * @return string
  537.      */
  538.     private function parseYoutubeId()
  539.     {
  540.         $youtubeId '';
  541.         if ($this->type === self::TYPE_YOUTUBE) {
  542.             if ($youtubeId $this->id) {
  543.                 if (strpos($youtubeId'//') !== false) {
  544.                     $parts parse_url($this->id);
  545.                     if (array_key_exists('query'$parts)) {
  546.                         parse_str($parts['query'], $vars);
  547.                         if (isset($vars['v']) && $vars['v']) {
  548.                             $youtubeId $vars['v'];
  549.                         }
  550.                     }
  551.                     //get youtube id if form urls like  http://www.youtube.com/embed/youtubeId
  552.                     if (strpos($this->id'embed') !== false) {
  553.                         $explodedPath explode('/'$parts['path']);
  554.                         $youtubeId $explodedPath[array_search('embed'$explodedPath) + 1];
  555.                     }
  556.                     if (isset($parts['host']) && $parts['host'] === 'youtu.be') {
  557.                         $youtubeId trim($parts['path'], ' /');
  558.                     }
  559.                 }
  560.             }
  561.         }
  562.         return $youtubeId;
  563.     }
  564.     private function getYoutubeCode(bool $inAdmin false): string
  565.     {
  566.         if (!$this->id) {
  567.             return $this->getEmptyCode();
  568.         }
  569.         $config $this->getConfig();
  570.         $code '';
  571.         $youtubeId $this->parseYoutubeId();
  572.         if (!$youtubeId) {
  573.             return $this->getEmptyCode();
  574.         }
  575.         if ($inAdmin && isset($config['editmodeImagePreview']) && $config['editmodeImagePreview'] === true) {
  576.             return '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">
  577.                 <img src="https://img.youtube.com/vi/' $youtubeId '/0.jpg">
  578.             </div>';
  579.         }
  580.         $width '100%';
  581.         if (array_key_exists('width'$config)) {
  582.             $width $config['width'];
  583.         }
  584.         $height '300';
  585.         if (array_key_exists('height'$config)) {
  586.             $height $config['height'];
  587.         }
  588.         $wmode '?wmode=transparent';
  589.         $seriesPrefix '';
  590.         if (strpos($youtubeId'PL') === 0) {
  591.             $wmode '';
  592.             $seriesPrefix 'videoseries?list=';
  593.         }
  594.         $valid_youtube_prams = [ 'autohide',
  595.             'autoplay',
  596.             'cc_load_policy',
  597.             'color',
  598.             'controls',
  599.             'disablekb',
  600.             'enablejsapi',
  601.             'end',
  602.             'fs',
  603.             'playsinline',
  604.             'hl',
  605.             'iv_load_policy',
  606.             'list',
  607.             'listType',
  608.             'loop',
  609.             'modestbranding',
  610.             'mute',
  611.             'origin',
  612.             'playerapiid',
  613.             'playlist',
  614.             'rel',
  615.             'showinfo',
  616.             'start',
  617.             'theme',
  618.         ];
  619.         $additional_params '';
  620.         $clipConfig = [];
  621.         if (isset($config['config']['clip']) && is_array($config['config']['clip'])) {
  622.             $clipConfig $config['config']['clip'];
  623.         }
  624.         // this is to be backward compatible to <= v 1.4.7
  625.         $configurations $clipConfig;
  626.         if (array_key_exists(self::TYPE_YOUTUBE$config) && is_array($config[self::TYPE_YOUTUBE])) {
  627.             $configurations array_merge($clipConfig$config[self::TYPE_YOUTUBE]);
  628.         }
  629.         if (!empty($configurations)) {
  630.             foreach ($configurations as $key => $value) {
  631.                 if (in_array($key$valid_youtube_prams)) {
  632.                     if (is_bool($value)) {
  633.                         if ($value) {
  634.                             $additional_params .= '&'.$key.'=1';
  635.                         } else {
  636.                             $additional_params .= '&'.$key.'=0';
  637.                         }
  638.                     } else {
  639.                         $additional_params .= '&'.$key.'='.$value;
  640.                     }
  641.                 }
  642.             }
  643.         }
  644.         $code .= '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">
  645.             <iframe width="' $width '" height="' $height '" src="https://www.youtube-nocookie.com/embed/' $seriesPrefix $youtubeId $wmode $additional_params .'" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen allow="fullscreen" data-type="pimcore_video_editable"></iframe>
  646.         </div>';
  647.         return $code;
  648.     }
  649.     private function getVimeoCode(bool $inAdmin false): string
  650.     {
  651.         if (!$this->id) {
  652.             return $this->getEmptyCode();
  653.         }
  654.         $config $this->getConfig();
  655.         $code '';
  656.         $uid 'video_' uniqid();
  657.         // get vimeo id
  658.         if (preg_match("@vimeo.*/([\d]+)@i"$this->id$matches)) {
  659.             $vimeoId = (int)$matches[1];
  660.         } else {
  661.             // for object-videos
  662.             $vimeoId $this->id;
  663.         }
  664.         if (ctype_digit($vimeoId)) {
  665.             if ($inAdmin && isset($config['editmodeImagePreview']) && $config['editmodeImagePreview'] === true) {
  666.                 return '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">
  667.                     <img src="https://vumbnail.com/' $vimeoId '.jpg">
  668.                 </div>';
  669.             }
  670.             $width '100%';
  671.             if (array_key_exists('width'$config)) {
  672.                 $width $config['width'];
  673.             }
  674.             $height '300';
  675.             if (array_key_exists('height'$config)) {
  676.                 $height $config['height'];
  677.             }
  678.             $valid_vimeo_prams = [
  679.                 'autoplay',
  680.                 'background',
  681.                 'loop',
  682.                 'muted',
  683.             ];
  684.             $additional_params '';
  685.             $clipConfig = [];
  686.             if (isset($config['config']['clip']) && is_array($config['config']['clip'])) {
  687.                 $clipConfig $config['config']['clip'];
  688.             }
  689.             // this is to be backward compatible to <= v 1.4.7
  690.             $configurations $clipConfig;
  691.             if (isset($config[self::TYPE_VIMEO]) && is_array($config[self::TYPE_VIMEO])) {
  692.                 $configurations array_merge($clipConfig$config[self::TYPE_VIMEO]);
  693.             }
  694.             if (!empty($configurations)) {
  695.                 foreach ($configurations as $key => $value) {
  696.                     if (in_array($key$valid_vimeo_prams)) {
  697.                         if (is_bool($value)) {
  698.                             if ($value) {
  699.                                 $additional_params .= '&'.$key.'=1';
  700.                             } else {
  701.                                 $additional_params .= '&'.$key.'=0';
  702.                             }
  703.                         } else {
  704.                             $additional_params .= '&'.$key.'='.$value;
  705.                         }
  706.                     }
  707.                 }
  708.             }
  709.             $code .= '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">
  710.                 <iframe src="https://player.vimeo.com/video/' $vimeoId '?dnt=1&title=0&amp;byline=0&amp;portrait=0'$additional_params .'" width="' $width '" height="' $height '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen allow="fullscreen" data-type="pimcore_video_editable"></iframe>
  711.             </div>';
  712.             return $code;
  713.         }
  714.         // default => return the empty code
  715.         return $this->getEmptyCode();
  716.     }
  717.     private function getDailymotionCode(bool $inAdmin false): string
  718.     {
  719.         if (!$this->id) {
  720.             return $this->getEmptyCode();
  721.         }
  722.         $config $this->getConfig();
  723.         $code '';
  724.         $uid 'video_' uniqid();
  725.         // get dailymotion id
  726.         if (preg_match('@dailymotion.*/video/([^_]+)@i'$this->id$matches)) {
  727.             $dailymotionId $matches[1];
  728.         } else {
  729.             // for object-videos
  730.             $dailymotionId $this->id;
  731.         }
  732.         if ($dailymotionId) {
  733.             if ($inAdmin && isset($config['editmodeImagePreview']) && $config['editmodeImagePreview'] === true) {
  734.                 return '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">
  735.                     <img src="https://www.dailymotion.com/thumbnail/video/' $dailymotionId '">
  736.                 </div>';
  737.             }
  738.             $width $config['width'] ?? '100%';
  739.             $height $config['height'] ?? '300';
  740.             $valid_dailymotion_prams = [
  741.                 'autoplay',
  742.                 'loop',
  743.                 'mute', ];
  744.             $additional_params '';
  745.             $clipConfig = isset($config['config']['clip']) && is_array($config['config']['clip']) ? $config['config']['clip'] : [];
  746.             // this is to be backward compatible to <= v 1.4.7
  747.             $configurations $clipConfig;
  748.             if (isset($config[self::TYPE_DAILYMOTION]) && is_array($config[self::TYPE_DAILYMOTION])) {
  749.                 $configurations array_merge($clipConfig$config[self::TYPE_DAILYMOTION]);
  750.             }
  751.             if (!empty($configurations)) {
  752.                 foreach ($configurations as $key => $value) {
  753.                     if (in_array($key$valid_dailymotion_prams)) {
  754.                         if (is_bool($value)) {
  755.                             if ($value) {
  756.                                 $additional_params .= '&'.$key.'=1';
  757.                             } else {
  758.                                 $additional_params .= '&'.$key.'=0';
  759.                             }
  760.                         } else {
  761.                             $additional_params .= '&'.$key.'='.$value;
  762.                         }
  763.                     }
  764.                 }
  765.             }
  766.             $code .= '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video '. ($config['class'] ?? '') .'">
  767.                 <iframe src="https://www.dailymotion.com/embed/video/' $dailymotionId '?' $additional_params .'" width="' $width '" height="' $height '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen allow="fullscreen" data-type="pimcore_video_editable"></iframe>
  768.             </div>';
  769.             return $code;
  770.         }
  771.         // default => return the empty code
  772.         return $this->getEmptyCode();
  773.     }
  774.     /**
  775.      * @param array $urls
  776.      * @param Asset\Image\Thumbnail|Asset\Video\ImageThumbnail|null $thumbnail
  777.      *
  778.      * @return string
  779.      */
  780.     private function getHtml5Code($urls = [], $thumbnail null)
  781.     {
  782.         $code '';
  783.         $video $this->getVideoAsset();
  784.         if ($video) {
  785.             $duration ceil($video->getDuration());
  786.             $durationParts = ['PT'];
  787.             // hours
  788.             if ($duration 3600 >= 1) {
  789.                 $hours floor($duration 3600);
  790.                 $durationParts[] = $hours 'H';
  791.                 $duration $duration $hours 3600;
  792.             }
  793.             // minutes
  794.             if ($duration 60 >= 1) {
  795.                 $minutes floor($duration 60);
  796.                 $durationParts[] = $minutes 'M';
  797.                 $duration $duration $minutes 60;
  798.             }
  799.             $durationParts[] = $duration 'S';
  800.             $durationString implode(''$durationParts);
  801.             $code .= '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video">' "\n";
  802.             $uploadDate = new \DateTime();
  803.             $uploadDate->setTimestamp($video->getCreationDate());
  804.             $jsonLd = [
  805.                 '@context' => 'https://schema.org',
  806.                 '@type' => 'VideoObject',
  807.                 'name' => $this->getTitle(),
  808.                 'description' => $this->getDescription(),
  809.                 'uploadDate' => $uploadDate->format('Y-m-d\TH:i:sO'),
  810.                 'duration' => $durationString,
  811.                 //'contentUrl' => Tool::getHostUrl() . $urls['mp4'],
  812.                 //"embedUrl" => "http://www.example.com/videoplayer.swf?video=123",
  813.                 //"interactionCount" => "1234",
  814.             ];
  815.             if (!$thumbnail) {
  816.                 $thumbnail $video->getImageThumbnail([]);
  817.             }
  818.             $jsonLd['contentUrl'] = $urls['mp4'];
  819.             if (!preg_match('@https?://@'$urls['mp4'])) {
  820.                 $jsonLd['contentUrl'] = Tool::getHostUrl() . $urls['mp4'];
  821.             }
  822.             $thumbnailUrl = (string)$thumbnail;
  823.             $jsonLd['thumbnailUrl'] = $thumbnailUrl;
  824.             if (!preg_match('@https?://@'$thumbnailUrl)) {
  825.                 $jsonLd['thumbnailUrl'] = Tool::getHostUrl() . $thumbnailUrl;
  826.             }
  827.             $code .= "\n\n<script type=\"application/ld+json\">\n" json_encode($jsonLd) . "\n</script>\n\n";
  828.             // default attributes
  829.             $attributesString '';
  830.             $attributes = [
  831.                 'width' => $this->getWidth(),
  832.                 'height' => $this->getHeight(),
  833.                 'poster' => $thumbnailUrl,
  834.                 'controls' => 'controls',
  835.                 'class' => 'pimcore_video',
  836.             ];
  837.             $config $this->getConfig();
  838.             if (array_key_exists('attributes'$config)) {
  839.                 $attributes array_merge($attributes$config['attributes']);
  840.             }
  841.             if (isset($config['removeAttributes']) && is_array($config['removeAttributes'])) {
  842.                 foreach ($config['removeAttributes'] as $attribute) {
  843.                     unset($attributes[$attribute]);
  844.                 }
  845.             }
  846.             // do not allow an empty controls editable
  847.             if (isset($attributes['controls']) && !$attributes['controls']) {
  848.                 unset($attributes['controls']);
  849.             }
  850.             if (isset($urls['mpd'])) {
  851.                 $attributes['data-dashjs-player'] = null;
  852.             }
  853.             foreach ($attributes as $key => $value) {
  854.                 $attributesString .= ' ' $key;
  855.                 if (!empty($value)) {
  856.                     $quoteChar '"';
  857.                     if (strpos($value'"')) {
  858.                         $quoteChar "'";
  859.                     }
  860.                     $attributesString .= '=' $quoteChar $value $quoteChar;
  861.                 }
  862.             }
  863.             $code .= '<video' $attributesString '>' "\n";
  864.             foreach ($urls as $type => $url) {
  865.                 if ($type == 'medias') {
  866.                     foreach ($url as $format => $medias) {
  867.                         foreach ($medias as $media => $mediaUrl) {
  868.                             $code .= '<source type="video/' $format '" src="' $mediaUrl '" media="' $media '"  />' "\n";
  869.                         }
  870.                     }
  871.                 } else {
  872.                     $code .= '<source type="video/' $type '" src="' $url '" />' "\n";
  873.                 }
  874.             }
  875.             $code .= '</video>' "\n";
  876.             $code .= '</div>' "\n";
  877.         }
  878.         return $code;
  879.     }
  880.     /**
  881.      * @param string|null $thumbnail
  882.      *
  883.      * @return string
  884.      */
  885.     private function getProgressCode($thumbnail null)
  886.     {
  887.         $uid 'video_' uniqid();
  888.         $code '
  889.         <div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video">
  890.             <style type="text/css">
  891.                 #' $uid ' .pimcore_editable_video_progress_status {
  892.                     box-sizing:content-box;
  893.                     background:#fff url(/bundles/pimcoreadmin/img/video-loading.gif) center center no-repeat;
  894.                     width:66px;
  895.                     height:66px;
  896.                     padding:20px;
  897.                     border:1px solid #555;
  898.                     box-shadow: 2px 2px 5px #333;
  899.                     border-radius:20px;
  900.                     margin: 0 20px 0 20px;
  901.                     top: calc(50% - 66px);
  902.                     left: calc(50% - 66px);
  903.                     position:absolute;
  904.                     opacity: 0.8;
  905.                 }
  906.             </style>
  907.             <div class="pimcore_editable_video_progress" id="' $uid '">
  908.                 <img src="' $thumbnail '" style="width: ' $this->getWidthWithUnit() . '; height: ' $this->getHeightWithUnit() . ';">
  909.                 <div class="pimcore_editable_video_progress_status"></div>
  910.             </div>
  911.         </div>';
  912.         return $code;
  913.     }
  914.     /**
  915.      * @return string
  916.      */
  917.     private function getEmptyCode(): string
  918.     {
  919.         $uid 'video_' uniqid();
  920.         return '<div id="pimcore_video_' $this->getName() . '" class="pimcore_editable_video"><div class="pimcore_editable_video_empty" id="' $uid '" style="width: ' $this->getWidthWithUnit() . '; height: ' $this->getHeightWithUnit() . ';"></div></div>';
  921.     }
  922.     private function updateAllowedTypesFromConfig(array $config): void
  923.     {
  924.         $this->allowedTypes self::ALLOWED_TYPES;
  925.         if (
  926.             isset($config['allowedTypes']) === true
  927.             && empty($config['allowedTypes']) === false
  928.             && empty(array_diff($config['allowedTypes'], self::ALLOWED_TYPES))
  929.         ) {
  930.             $this->allowedTypes $config['allowedTypes'];
  931.         }
  932.     }
  933.     /**
  934.      * {@inheritdoc}
  935.      */
  936.     public function isEmpty()
  937.     {
  938.         if ($this->id) {
  939.             return false;
  940.         }
  941.         return true;
  942.     }
  943.     /**
  944.      * @return string
  945.      */
  946.     public function getVideoType()
  947.     {
  948.         if (empty($this->type) === true) {
  949.             $this->type $this->getAllowedTypes()[0];
  950.         }
  951.         return $this->type;
  952.     }
  953.     /**
  954.      * @return Asset\Video|null
  955.      */
  956.     public function getVideoAsset()
  957.     {
  958.         if ($this->getVideoType() === self::TYPE_ASSET) {
  959.             return Asset\Video::getById($this->id);
  960.         }
  961.         return null;
  962.     }
  963.     /**
  964.      * @return Asset\Image|null
  965.      */
  966.     public function getPosterAsset()
  967.     {
  968.         return Asset\Image::getById($this->poster);
  969.     }
  970.     /**
  971.      * @param string|Asset\Video\Thumbnail\Config $config
  972.      *
  973.      * @return Asset\Image\Thumbnail|Asset\Video\ImageThumbnail|string
  974.      *
  975.      * TODO Pimcore 11: Change empty string return to null
  976.      */
  977.     public function getImageThumbnail($config)
  978.     {
  979.         if ($this->poster && ($poster Asset\Image::getById($this->poster))) {
  980.             return $poster->getThumbnail($config);
  981.         }
  982.         if ($this->getVideoAsset()) {
  983.             return $this->getVideoAsset()->getImageThumbnail($config);
  984.         }
  985.         return '';
  986.     }
  987.     /**
  988.      * @param string|Asset\Video\Thumbnail\Config $config
  989.      *
  990.      * @return array
  991.      */
  992.     public function getThumbnail($config)
  993.     {
  994.         if ($this->getVideoAsset()) {
  995.             return $this->getVideoAsset()->getThumbnail($config);
  996.         }
  997.         return [];
  998.     }
  999.     /**
  1000.      * { @inheritdoc }
  1001.      */
  1002.     public function rewriteIds($idMapping/** : void */
  1003.     {
  1004.         if ($this->type == self::TYPE_ASSET && array_key_exists(self::TYPE_ASSET$idMapping) && array_key_exists($this->getId(), $idMapping[self::TYPE_ASSET])) {
  1005.             $this->setId($idMapping[self::TYPE_ASSET][$this->getId()]);
  1006.         }
  1007.     }
  1008. }