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