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

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