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