diff options
author | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
---|---|---|
committer | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
commit | fc7369835258467bf97eb64f184b93691f9a9fd5 (patch) | |
tree | daabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/EmbedVideo/classes |
first commit
Diffstat (limited to 'www/wiki/extensions/EmbedVideo/classes')
8 files changed, 2143 insertions, 0 deletions
diff --git a/www/wiki/extensions/EmbedVideo/classes/ApiEmbedVideo.php b/www/wiki/extensions/EmbedVideo/classes/ApiEmbedVideo.php new file mode 100644 index 00000000..d5186c2f --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/ApiEmbedVideo.php @@ -0,0 +1,104 @@ +<?php +/** + * EmbedVideo + * ApiEmbedVideo class + * + * @license MIT + * @package EmbedVideo + * @link https://github.com/HydraWiki/mediawiki-embedvideo + * + **/ +class ApiEmbedVideo extends ApiBase { + + /** + * Execute the API call. + */ + public function execute() { + + $getHTML = \EmbedVideoHooks::parseEV( + NULL, + $this->getMain()->getVal('service'), + $this->getMain()->getVal('id'), + $this->getMain()->getVal('dimensions'), + $this->getMain()->getVal('alignment'), + $this->getMain()->getVal('description'), + $this->getMain()->getVal('container'), + $this->getMain()->getVal('urlargs'), + $this->getMain()->getVal('autoresize'), + $this->getMain()->getVal('valignment') + ); + + if (is_array($getHTML)) { + $HTML = $getHTML[0]; + } else { + $HTML = "Unable to load video from API."; + } + + $this->getResult()->addValue(null, $this->getModuleName(), array ( 'html' => $HTML ) ); + return true; + } + + /** + * [getDescription description] + * @return [type] [description] + */ + public function getDescription() { + return 'Get generated embed code for given parameters'; + } + + /** + * Setup the allowed and required parameters + * @return array + */ + public function getAllowedParams() { + return array_merge( parent::getAllowedParams(), array( + 'service' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), + 'id' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), + 'dimensions' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false + ), + 'alignment' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false + ), + 'description' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false + ), + 'container' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false + ), + 'urlargs' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false + ), + 'autoresize' => array ( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false + ), + ) ); + } + + // Describe the parameter + public function getParamDescription() { + return array_merge( parent::getParamDescription(), array( + 'service' => 'Name of the service (youtube, twitch, ect)', + 'id' => 'The ID of the video for that service', + 'dimensions' => 'Either a numeric width (100) or width by height (100x100)', + 'alignment' => 'Alignment of video', + 'description' => 'Description of video', + 'container' => 'Accepts frame, or leave empty', + 'urlargs' => 'Additional arguments to pass in the video url (for some services)', + 'autoresize' => 'Auto resize video? (true or false)' + ) ); + } + +}
\ No newline at end of file diff --git a/www/wiki/extensions/EmbedVideo/classes/OEmbed.php b/www/wiki/extensions/EmbedVideo/classes/OEmbed.php new file mode 100644 index 00000000..3244a39e --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/OEmbed.php @@ -0,0 +1,240 @@ +<?php +/** + * EmbedVideo + * EmbedVideo OEmbed Class + * + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ +namespace EmbedVideo; + +class OEmbed { + /** + * Data from oEmbed service. + * + * @var array + */ + private $data = []; + + /** + * Main Constructor + * + * @access private + * @param array Data return from oEmbed service. + * @return void + */ + private function __construct( $data ) { + $this->data = $data; + } + + /** + * Create a new object from an oEmbed URL. + * + * @access public + * @param string Full oEmbed URL to process. + * @return mixed New OEmbed object or false on initialization failure. + */ + static public function newFromRequest( $url ) { + $data = self::curlGet( $url ); + if ( $data !== false ) { + // Error suppression is required as json_decode() tosses E_WARNING in contradiction to its documentation. + $data = @json_decode( $data, true ); + } + if ( !$data || !is_array( $data ) ) { + return false; + } + return new self( $data ); + } + + /** + * Return the HTML from the data, typically an iframe. + * + * @access public + * @return mixed String HTML or false on error. + */ + public function getHtml() { + if ( isset( $this->data['html'] ) ) { + // Remove any extra HTML besides the iframe. + $iframeStart = strpos( $this->data['html'], '<iframe' ); + $iframeEnd = strpos( $this->data['html'], '</iframe>' ); + if ( $iframeStart !== false ) { + // Only strip if an iframe was found. + $this->data['html'] = substr( $this->data['html'], $iframeStart, $iframeEnd + 9 ); + } + + return $this->data['html']; + } else { + return false; + } + } + + /** + * Return the title from the data. + * + * @access public + * @return mixed String or false on error. + */ + public function getTitle() { + if ( isset( $this->data['title'] ) ) { + return $this->data['title']; + } else { + return false; + } + } + + /** + * Return the author name from the data. + * + * @access public + * @return mixed String or false on error. + */ + public function getAuthorName() { + if ( isset( $this->data['author_name'] ) ) { + return $this->data['author_name']; + } else { + return false; + } + } + + /** + * Return the author URL from the data. + * + * @access public + * @return mixed String or false on error. + */ + public function getAuthorUrl() { + if ( isset( $this->data['author_url'] ) ) { + return $this->data['author_url']; + } else { + return false; + } + } + + /** + * Return the provider name from the data. + * + * @access public + * @return mixed String or false on error. + */ + public function getProviderName() { + if ( isset( $this->data['provider_name'] ) ) { + return $this->data['provider_name']; + } else { + return false; + } + } + + /** + * Return the provider URL from the data. + * + * @access public + * @return mixed String or false on error. + */ + public function getProviderUrl() { + if ( isset( $this->data['provider_url'] ) ) { + return $this->data['provider_url']; + } else { + return false; + } + } + + /** + * Return the width from the data. + * + * @access public + * @return mixed Integer or false on error. + */ + public function getWidth() { + if ( isset( $this->data['width'] ) ) { + return intval( $this->data['width'] ); + } else { + return false; + } + } + + /** + * Return the height from the data. + * + * @access public + * @return mixed Integer or false on error. + */ + public function getHeight() { + if ( isset( $this->data['height'] ) ) { + return intval( $this->data['height'] ); + } else { + return false; + } + } + + /** + * Return the thumbnail width from the data. + * + * @access public + * @return mixed Integer or false on error. + */ + public function getThumbnailWidth() { + if ( isset( $this->data['thumbnail_width'] ) ) { + return intval( $this->data['thumbnail_width'] ); + } else { + return false; + } + } + + /** + * Return the thumbnail height from the data. + * + * @access public + * @return mixed Integer or false on error. + */ + public function getThumbnailHeight() { + if ( isset( $this->data['thumbnail_height'] ) ) { + return intval( $this->data['thumbnail_height'] ); + } else { + return false; + } + } + + /** + * Perform a Curl GET request. + * + * @access private + * @param string URL + * @return mixed + */ + static private function curlGet( $location ) { + global $wgServer; + + $ch = curl_init(); + + $timeout = 10; + $useragent = "EmbedVideo/1.0/" . $wgServer; + $dateTime = gmdate( "D, d M Y H:i:s", time() ) . " GMT"; + $headers = ['Date: ' . $dateTime]; + + $curlOptions = [ + CURLOPT_TIMEOUT => $timeout, + CURLOPT_USERAGENT => $useragent, + CURLOPT_URL => $location, + CURLOPT_CONNECTTIMEOUT => $timeout, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 10, + CURLOPT_COOKIEFILE => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'curlget', + CURLOPT_COOKIEJAR => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'curlget', + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $headers + ]; + + curl_setopt_array( $ch, $curlOptions ); + + $page = curl_exec( $ch ); + + $response_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); + if ( $responseCode == 503 || $responseCode == 404 || $responseCode == 501 || $responseCode == 401 ) { + return false; + } + + return $page; + } +} diff --git a/www/wiki/extensions/EmbedVideo/classes/VideoService.php b/www/wiki/extensions/EmbedVideo/classes/VideoService.php new file mode 100644 index 00000000..1d610aa9 --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/VideoService.php @@ -0,0 +1,848 @@ +<?php +/** + * EmbedVideo + * EmbedVideo VideoService Class + * + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ +namespace EmbedVideo; + +class VideoService { + /** + * Available services. + * + * @var array + */ + static private $services = [ + 'archiveorg' => [ + 'embed' => '<iframe src="//archive.org/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.2994923857868, // (640 / 493) + 'https_enabled' => true, + 'url_regex' => [ + '#archive\.org/(?:details|embed)/([\d\w\-_][^/\?\#]+)#is' + ], + 'id_regex' => [ + '#^([\d\w\-_][^/\?\#]+)$#is' + ] + ], + 'bambuser' => [ + 'embed' => '<iframe src="//embed.bambuser.com/broadcast/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.2994923857868, // (640 / 493) + 'https_enabled' => true, + 'url_regex' => [ + '#bambuser\.com/(?:v|broadcast)/([\d\w\-\+]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w\-\+]+)$#is' + ] + ], + 'bambuser_channel' => [ + 'embed' => '<iframe src="//embed.bambuser.com/channel/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.2994923857868, // (640 / 493) + 'https_enabled' => true, + 'url_regex' => [ + '#bambuser\.com/channel/([\d\w\-\+]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w\-\+]+)$#is' + ] + ], + 'beam' => [ + 'embed' => '<iframe src="https://beam.pro/embed/player/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#beam.pro/([\d\w\-\+]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w\-\+]+)$#is' + ] + ], + 'disclose' => [ + 'embed' => '<iframe src="//www.disclose.tv/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (640 / 360) + 'https_enabled' => true, + 'url_regex' => [ + '#disclose.tv/embed/([\d]+)/([\w-]+)#is', + '#disclose.tv/action/viewvideo/([\d]+)/([\w-]+)/#is' + ], + 'id_regex' => [ + '#^([\d]+)$#is' + ] + ], + 'blip' => [ + 'default_width' => 640, + 'default_ratio' => 1.2994923857868, // (640 / 493) + 'https_enabled' => false, + 'url_regex' => [ + '#(http://blip\.tv/[\w\d\-]+?/[\w\d\-]+?-[\d]+)#is' + ], + 'oembed' => 'http://blip.tv/oembed/?url=%1$s&width=%2$d&maxwidth=%2$d' + ], + 'bing' => [ + 'embed' => '<iframe src="//hub.video.msn.com/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" scrolling="no" noscroll allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#bing.com/videos/watch/video/[\w\d\-]+?/([a-zA-Z0-9]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([a-zA-Z0-9]+)$#is' + ] + ], + 'collegehumor' => [ + 'embed' => '<iframe src="//www.collegehumor.com/e/%1$s" width="%2$d" height="%3$d" frameborder="0" allowFullScreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.6260162601626, // (600 / 369) + 'https_enabled' => true, + 'url_regex' => [ + '#collegehumor\.com/(?:video|e)/([\d]+)#is' + ], + 'id_regex' => [ + '#^([\d]+)$#is' + ] + ], + 'dailymotion' => [ + 'embed' => '<iframe src="//www.dailymotion.com/embed/video/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#dailymotion\.com/(?:video|embed/video)/([a-zA-Z0-9]+)(?:_\S+?)?#is' + ], + 'id_regex' => [ + '#^([a-zA-Z0-9]+)(?:_\S+?)?#is' + ] + ], + 'divshare' => [ + 'embed' => '<iframe src="//www.divshare.com/flash/video2?myId=%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true + ], + 'funnyordie' => [ + 'embed' => '<iframe src="http://www.funnyordie.com/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.64102564102564, // (640 / 390) + 'https_enabled' => false, + 'url_regex' => [ + '#funnyordie\.com/(?:videos|embed)/([a-zA-Z0-9]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([a-zA-Z0-9]+)$#is' + ] + ], + 'gfycat' => [ + 'embed' => '<iframe src="//gfycat.com/ifr/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true" scrolling="no" style="-webkit-backface-visibility: hidden;-webkit-transform: scale(1);" ></iframe>', + 'default_width' => 640, + 'https_enabled' => true, + 'url_regex' => [ + '#gfycat\.com/([a-zA-Z]+)#is' + ], + 'id_regex' => [ + '#^([a-zA-Z]+)$#is' + ] + ], + 'hitbox' => [ + 'embed' => '<iframe src="http://www.hitbox.tv/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => false, + 'url_regex' => [ + '#hitbox.tv/([\d\w]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w]+)$#is' + ] + ], + 'jwplayer' => [ + 'embed' => '<iframe src="//content.jwplatform.com/players/%1$s.html" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#content\.jwplatform\.com/players/([a-zA-Z0-9]+-[a-zA-Z0-9]+)(?:.)?#is' + ], + 'id_regex' => [ + '#([a-zA-Z0-9]+-[a-zA-Z0-9]+)#is' + ] + ], + 'kickstarter' => [ + 'embed' => '<iframe src="//www.kickstarter.com/projects/%1$s/widget/video.html" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#kickstarter\.com/projects/([\d\w-]+/[\d\w-]+)(?:/widget/video.html)?#is' + ], + 'id_regex' => [ + '#^([\d\w-]+/[\d\w-]+)$#is' + ] + ], + 'mediacccde' => [ + 'embed' => '<iframe src="https://media.ccc.de/v/%1$s/oembed" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true" scrolling="no"></iframe>', + 'default_width' => 660, + 'default_ratio' => 1.77777777777778, //(16 / 9), + 'https_enabled' => true, + 'url_regex' => [ + '#conferences/.*?([\d\w_-]+)(?:/oembed)?.html$#is' + ], + 'id_regex' => [ + '#^([\d\w_-]+)$#is' + ] + ], + 'metacafe' => [ + 'embed' => '<iframe src="http://www.metacafe.com/embed/%1$s/" width="%2$d" height="%3$d" frameborder="0" allowFullScreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => false, + 'url_regex' => [ + '#metacafe\.com/(?:watch|embed)/([\d]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d]+)$#is' + ] + ], + 'nico' => [ + 'embed' => '<iframe srcdoc="<script type="text/javascript" src="http://ext.nicovideo.jp/thumb_watch/%1$s?w=%2$d&h=%3$d"></script>" width="%2$d" height="%3$d" frameborder="0" allowFullScreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.59609120521173, // (490 / 307) + 'https_enabled' => false, + 'url_regex' => [ + '#nicovideo\.jp/watch/((?:[a-zA-Z]{2})?[\d]+)#is' + ], + 'id_regex' => [ + '#^((?:[a-zA-Z]{2})?[\d]+)$#is' + ] + ], + 'rutube' => [ + 'embed' => '<iframe src="//rutube.ru/play/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#rutube\.ru/video/([a-zA-Z0-9]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([a-zA-Z0-9]+)$#is' + ] + ], + 'soundcloud' => [ + 'embed' => '<iframe src="https://w.soundcloud.com/player/?url=%1$s&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true" width="%2$d" height="%3$d" scrolling="no" frameborder="no"></iframe>', + 'default_width' => 186, + 'default_ratio' => 2.66666, + 'https_enabled' => true, + 'url_regex' => [ + '#^(https://soundcloud\.com/.+?/.+?)$#is', + ] + ], + 'teachertube' => [ + 'embed' => '<iframe src="http://www.teachertube.com/embed/video/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.72972972972973, // (640 / 370) + 'https_enabled' => false, + 'url_regex' => [ + '#teachertube\.com/video/(?:[\w\d\-]+?-)?([\d]+)$#is', + ], + 'id_regex' => [ + '#^([\d]+)$#is' + ] + ], + 'ted' => [ + 'embed' => '<iframe src="//embed-ssl.ted.com/talks/%1$s.html" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#ted\.com/talks/([\d\w\-]+)(?:/\S+?)?#is', + ], + 'id_regex' => [ + '#^([\d\w\-]+)$#is' + ] + ], + 'tubitv' => [ + 'embed' => '<iframe src="//tubitv.com/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (640 / 360) + 'https_enabled' => true, + 'url_regex' => [ + '#tubitv.com/(?:video|embed)/([\d]+)(/[\w-]+)?$#is', + ], + 'id_regex' => [ + '#^([\d]+)$#is' + ] + ], + 'tudou' => [ + 'embed' => '<iframe src="http://www.tudou.com/programs/view/html5embed.action?code=%1$s&autoPlay=false&playType=AUTO" allowfullscreen="true" width="%2$d" height="%3$d" frameborder="0"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.6, + 'https_enabled' => false, + 'url_regex' => [ + '#tudou.com/listplay/([\d\w-]+)/([\d\w-]+).html#is', + '#tudou.com/listplay/([\d\w-]+).html#is' + ], + 'id_regex' => [ + '#^([\d\w-]+)$#is' + ] + ], + 'tvpot' => [ + 'embed' => '<iframe src="//videofarm.daum.net/controller/video/viewer/Video.html?vid=%1$s&play_loc=undefined&alert=true" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#tvpot\.daum\.net/v/([\d\w-%]+)?#is' + ], + 'id_regex' => [ + '#^([\d\w-%]+)$#is' + ] + ], + 'twitch' => [ + 'embed' => '<iframe src="https://player.twitch.tv/?channel=%1$s&%4$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.64021164021164, // (620 / 378) + 'https_enabled' => false, + 'url_regex' => [ + '#twitch\.tv/([\d\w-]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w-]+)$#is' + ] + ], + 'twitchvod' => [ + 'embed' => '<iframe src="https://player.twitch.tv/?autoplay=false&video=%1$s&%4$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.64021164021164, // (620 / 378) + 'https_enabled' => false, + 'url_regex' => [ + '#twitch\.tv/([\d\w-]+)(?:/\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w-]+)$#is' + ] + ], + 'videomaten' => [ + 'embed' => '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="%2$d" height="%3$d" id="videomat" align="middle"><param name="allowScriptAccess" value="sameDomain" /><param name="movie" value="http://89.160.51.62/recordMe/play.swf?id=%1$s" /><param name="loop" value="false" /><param name="quality" value="high" /><param name="bgcolor" value="#ffffff" /><embed src="http://89.160.51.62/recordMe/play.swf?id=%1$s" loop="false" quality="high" bgcolor="#ffffff" width="%2$d" height="%3$d" name="videomat" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /></object>', + 'default_ratio' => 1.5, // (300 / 200) + 'https_enabled' => false + ], + 'vimeo' => [ + 'embed' => '<iframe src="//player.vimeo.com/video/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (640 / 360) + 'https_enabled' => true, + 'url_regex' => [ + '#vimeo\.com/([\d]+)#is', + '#vimeo\.com/channels/[\d\w-]+/([\d]+)#is' + ], + 'id_regex' => [ + '#^([\d]+)$#is' + ], + 'oembed' => '%4$s//vimeo.com/api/oembed.json?url=%1$s&width=%2$d&maxwidth=%2$d' + ], + 'vine' => [ + 'embed' => '<iframe src="//vine.co/v/%1$s/embed/simple" width="%2$d" height="%3$d" frameborder="0"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1, // (1 / 1) + 'https_enabled' => true, + 'url_regex' => [ + '#vine\.co/v/([a-zA-Z0-9]+)#is' + ], + 'id_regex' => [ + '#^([a-zA-Z0-9]+)$#is' + ] + ], + 'yahoo' => [ + 'embed' => '<iframe src="//screen.yahoo.com/%1$s.html?format=embed" width="%2$d" height="%3$d" scrolling="no" frameborder="0" allowfullscreen="true" allowtransparency="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#screen\.yahoo\.com/([\w\d\-]+?-\d+).html#is' + ], + 'id_regex' => [ + '#^([\w\d\-]+?-\d+)$#is' + ] + ], + 'youtube' => [ + 'embed' => '<iframe src="//www.youtube.com/embed/%1$s?%4$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#v=([\d\w-]+)(?:&\S+?)?#is', + '#youtu\.be/([\d\w-]+)#is' + ], + 'id_regex' => [ + '#^([\d\w-]+)$#is' + ], + 'oembed' => [ + 'http' => 'http://www.youtube.com/oembed?url=%1$s&width=%2$d&maxwidth=%2$d', + 'https' => 'http://www.youtube.com/oembed?scheme=https&url=%1$s&width=%2$d&maxwidth=%2$d' + ] + ], + 'youtubeplaylist' => [ + 'embed' => '<iframe src="//www.youtube.com/embed/videoseries?list=%1$s&%4$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#list=([\d\w-]+)(?:&\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w-]+)$#is' + ] + ], + 'youtubevideolist' => [ + 'embed' => '<iframe src="//www.youtube.com/embed/%1$s?%4$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.77777777777778, // (16 / 9) + 'https_enabled' => true, + 'url_regex' => [ + '#playlist=([\d\w-]+)(?:&\S+?)?#is' + ], + 'id_regex' => [ + '#^([\d\w-]+)$#is' + ] + ], + 'youku' => [ + 'embed' => '<iframe src="https://player.youku.com/embed/%1$s" width="%2$d" height="%3$d" frameborder="0" allowfullscreen="true"></iframe>', + 'default_width' => 640, + 'default_ratio' => 1.6, + 'https_enabled' => false, + 'url_regex' => [ + '#id_([\d\w-]+).html#is', + ], + 'id_regex' => [ + '#^(?:id_)?([\d\w-]+)$#is' + ] + ] + ]; + + /** + * Mapping of host names to services + * @var array + */ + static private $serviceHostMap = [ + 'archive.org' => 'archiveorg', + 'embed.bambuser.com' => ['bambuser', 'bambuser_channel'], + 'beam.pro' => 'beam', + 'blip.tv' => 'blip', + 'bing.com' => 'bing', + 'collegehumor.com' => 'collegehumor', + 'dailymotion.com' => 'dailymotion', + 'divshare.com' => 'divshare', + 'funnyordie.com' => 'funnyordie', + 'gfycat.com' => 'gfycat', + 'hitbox.tv' => 'hitbox', + 'content.jwplatform.com' => 'jwplayer', + 'kickstarter.com' => 'kickstarter', + 'media.ccc.de' => 'mideacccde', + 'metacafe.com' => 'metacafe', + 'nicovideo.jp' => 'nico', + 'rutube.ru' => 'rutube', + 'teachertube.com' => 'teachertube', + 'ted.com' => 'ted', + 'tubitv.com' => 'tubitv', + 'tudou.com' => 'todou', + 'tvpot.daum.net' => 'tvpot', + 'twitch.tv' => ['twitch', 'twitchvod'], + '89.160.51.62' => 'videomaten', + 'vimeo.com' => 'vimeo', + 'vine.co' => 'vine', + 'screen.yahoo.com' => 'yahoo', + 'youtube.com' => ['youtube', 'youtubeplaylist', 'youtubevideolist'], + 'youku.com' => 'youku' + ]; + + /** + * This object instance's service information. + * + * @var array + */ + private $service = []; + + /** + * Video ID + * + * @var array + */ + private $id = false; + + /** + * Player Width + * + * @var integer + */ + private $width = false; + + /** + * Player Height + * + * @var integer + */ + private $height = false; + + /** + * Description Text + * + * @var string + */ + private $description = false; + + /** + * Extra IDs that some services require. + * + * @var array + */ + private $extraIDs = false; + + /** + * Extra URL Arguments that may be utilized by some services. + * + * @var array + */ + private $urlArgs = false; + + /** + * Main Constructor + * + * @access private + * @param string Service Name + * @return void + */ + private function __construct($service) { + $this->service = self::$services[$service]; + } + + /** + * Create a new object from a service name. + * + * @access public + * @param string Service Name + * @return mixed New VideoService object or false on initialization error. + */ + static public function newFromName($service) { + if (isset(self::$services[$service])) { + return new self($service); + } else { + return false; + } + } + + /** + * return the service host map array + * @return array $serviceHostMap + */ + public static function getServiceHostMap() { + return self::$serviceHostMap; + } + + /** + * return an array of defined services + * + * @return array $services + */ + public static function getAvailableServices() { + return array_keys(self::$services); + } + + /** + * Add a service + * + * @access public + * @param string Service Name + * @param mixed args + */ + static public function addService($service, $args) { + if (isset(self::$services[$service])) { + throw new MWException("Service already already exists: $service"); + } + self::$services[$service] = $args; + } + + /** + * Return built HTML. + * + * @access public + * @return mixed String HTML to output or false on error. + */ + public function getHtml() { + if ($this->getVideoID() === false || $this->getWidth() === false || $this->getHeight() === false) { + return false; + } + + $html = false; + if (isset($this->service['embed'])) { + // Embed can be generated locally instead of calling out to the service to get it. + $data = [ + $this->service['embed'], + htmlentities($this->getVideoID(), ENT_QUOTES), + $this->getWidth(), + $this->getHeight(), + ]; + + if ($this->getExtraIds() !== false) { + foreach ($this->getExtraIds() as $extraId) { + $data[] = htmlentities($extraId, ENT_QUOTES); + } + } + + $urlArgs = $this->getUrlArgs(); + if ($urlArgs !== false) { + $data[] = $urlArgs; + } + + $html = call_user_func_array('sprintf', $data); + } elseif (isset($this->service['oembed'])) { + // Call out to the service to get the embed HTML. + if ($this->service['https_enabled']) { + if (stristr($this->getVideoID(), 'https:') !== false) { + $protocol = 'https:'; + } else { + $protocol = 'http:'; + } + } + $url = sprintf( + $this->service['oembed'], + $this->getVideoID(), + $this->getWidth(), + $this->getHeight(), + $protocol + ); + $oEmbed = OEmbed::newFromRequest($url); + if ($oEmbed !== false) { + $html = $oEmbed->getHtml(); + } + } + + return $html; + } + + /** + * Return Video ID + * + * @access public + * @return mixed Parsed Video ID or false for one that is not set. + */ + public function getVideoID() { + return $this->id; + } + + /** + * Set the Video ID for this video. + * + * @access public + * @param string Video ID/URL + * @return boolean Success + */ + public function setVideoID($id) { + $id = $this->parseVideoID($id); + if ($id !== false) { + $this->id = $id; + return true; + } else { + return false; + } + } + + /** + * Parse the video ID/URL provided. + * + * @access public + * @param string Video ID/URL + * @return mixed Parsed Video ID or false on failure. + */ + public function parseVideoID($id) { + $id = trim($id); + // URL regexes are put into the array first to prevent cases where the ID regexes might accidentally match an incorrect portion of the URL. + $regexes = array_merge((array) $this->service['url_regex'], (array) $this->service['id_regex']); + if (is_array($regexes) && count($regexes)) { + foreach ($regexes as $regex) { + if (preg_match($regex, $id, $matches)) { + // Get rid of the full text match. + array_shift($matches); + + $id = array_shift($matches); + + if (count($matches)) { + $this->extraIDs = $matches; + } + + return $id; + } + } + // If nothing matches and matches are specified then return false for an invalid ID/URL. + return false; + } else { + // Service definition has not specified a sanitization/validation regex. + return $id; + } + } + + /** + * Return extra IDs. + * + * @access public + * @return boolean Array of extra information or false if not set. + */ + public function getExtraIDs() { + return $this->extraIDs; + } + + /** + * Return the width. + * + * @access public + * @return mixed Integer value or false for not set. + */ + public function getWidth() { + return $this->width; + } + + /** + * Set the width of the player. This also will set the height automatically. + * Width will be automatically constrained to the minimum and maximum widths. + * + * @access public + * @param integer Width + * @return void + */ + public function setWidth($width = null) { + global $wgEmbedVideoMinWidth, $wgEmbedVideoMaxWidth, $wgEmbedVideoDefaultWidth; + + if (!is_numeric($width)) { + if ($width === null && $this->getDefaultWidth() !== false && $wgEmbedVideoDefaultWidth < 1) { + $width = $this->getDefaultWidth(); + } else { + $width = ($wgEmbedVideoDefaultWidth > 0 ? $wgEmbedVideoDefaultWidth : 640); + } + } else { + $width = intval($width); + } + + if ($wgEmbedVideoMaxWidth > 0 && $width > $wgEmbedVideoMaxWidth) { + $width = $wgEmbedVideoMaxWidth; + } + + if ($wgEmbedVideoMinWidth > 0 && $width < $wgEmbedVideoMinWidth) { + $width = $wgEmbedVideoMinWidth; + } + $this->width = $width; + + if ($this->getHeight() === false) { + $this->setHeight(); + } + } + + /** + * Return the height. + * + * @access public + * @return mixed Integer value or false for not set. + */ + public function getHeight() { + return $this->height; + } + + /** + * Set the height automatically by a ratio of the width or use the provided value. + * + * @access public + * @param mixed [Optional] Height Value + * @return void + */ + public function setHeight($height = null) { + if ($height !== null && $height > 0) { + $this->height = intval($height); + return; + } + + $ratio = 16 / 9; + if ($this->getDefaultRatio() !== false) { + $ratio = $this->getDefaultRatio(); + } + $this->height = round($this->getWidth() / $ratio); + } + + /** + * Return the optional URL arguments. + * + * @access public + * @return mixed Integer value or false for not set. + */ + public function getUrlArgs() { + if ($this->urlArgs !== false) { + return http_build_query($this->urlArgs); + } + } + + /** + * Set URL Arguments to optionally add to the embed URL. + * + * @access public + * @param string Raw Arguments + * @return boolean Success + */ + public function setUrlArgs($urlArgs) { + if (!$urlArgs) { + return true; + } + + $urlArgs = urldecode($urlArgs); + $_args = explode('&', $urlArgs); + + if (is_array($_args)) { + foreach ($_args as $rawPair) { + list($key, $value) = explode("=", $rawPair, 2); + if (empty($key) || ($value === null || $value === '')) { + return false; + } + $arguments[$key] = htmlentities($value, ENT_QUOTES); + } + } else { + return false; + } + $this->urlArgs = $arguments; + return true; + } + + /** + * Is HTTPS enabled? + * + * @access public + * @return boolean + */ + public function isHttpsEnabled() { + return (bool) $this->service['https_enabled']; + } + + /** + * Return default width if set. + * + * @access public + * @return mixed Integer width or false if not set. + */ + public function getDefaultWidth() { + return ($this->service['default_width'] > 0 ? $this->service['default_width'] : false); + } + + /** + * Return default ratio if set. + * + * @access public + * @return mixed Integer ratio or false if not set. + */ + public function getDefaultRatio() { + return ($this->service['default_ratio'] > 0 ? $this->service['default_ratio'] : false); + } +} diff --git a/www/wiki/extensions/EmbedVideo/classes/media/AudioHandler.php b/www/wiki/extensions/EmbedVideo/classes/media/AudioHandler.php new file mode 100644 index 00000000..173a8816 --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/media/AudioHandler.php @@ -0,0 +1,239 @@ +<?php +/** + * EmbedVideo + * AudioHandler Class + * + * @author Alexia E. Smith + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ + +namespace EmbedVideo; + +class AudioHandler extends \MediaHandler { + /** + * Get an associative array mapping magic word IDs to parameter names. + * Will be used by the parser to identify parameters. + */ + public function getParamMap() { + return [ + 'img_width' => 'width', + 'ev_start' => 'start', + 'ev_end' => 'end' + ]; + } + + /** + * Validate a thumbnail parameter at parse time. + * Return true to accept the parameter, and false to reject it. + * If you return false, the parser will do something quiet and forgiving. + * + * @access public + * @param string $name + * @param mixed $value + */ + public function validateParam($name, $value) { + if ($name === 'width' || $name === 'width') { + return $value > 0; + } + + if ($name === 'start' || $name === 'end') { + if ($this->parseTimeString($value) === false) { + return false; + } + return true; + } + return false; + } + + /** + * Parse a time string into seconds. + * strtotime() will not handle this nicely since 1:30 could be one minute and thirty seconds OR one hour and thirty minutes. + * + * @access public + * @param string Time formatted as one of: ss, :ss, mm:ss, hh:mm:ss, or dd:hh:mm:ss + * @return mixed Integer seconds or false for a bad format. + */ + public function parseTimeString($time) { + $parts = explode(":", $time); + if ($parts === false) { + return false; + } + $parts = array_reverse($parts); + + $magnitude = [1, 60, 3600, 86400]; + $seconds = 0; + foreach ($parts as $index => $part) { + $seconds += $part * $magnitude[$index]; + } + return $seconds; + } + + /** + * Merge a parameter array into a string appropriate for inclusion in filenames + * + * @access public + * @param array Array of parameters that have been through normaliseParams. + * @return string + */ + public function makeParamString($parameters) { + return ''; //Width does not matter to video or audio. + } + + /** + * Parse a param string made with makeParamString back into an array + * + * @access public + * @param string The parameter string without file name (e.g. 122px) + * @return mixed Array of parameters or false on failure. + */ + public function parseParamString($string) { + return []; //Nothing to parse. See makeParamString above. + } + + /** + * Changes the parameter array as necessary, ready for transformation. + * Should be idempotent. + * Returns false if the parameters are unacceptable and the transform should fail + * + * @access public + * @param object File + * @param array Parameters + * @return boolean Success + */ + public function normaliseParams($file, &$parameters) { + global $wgEmbedVideoDefaultWidth; + + if (isset($parameters['width']) && $parameters['width'] > 0) { + $parameters['width'] = intval($parameters['width']); + } else { + $parameters['width'] = $wgEmbedVideoDefaultWidth; + } + + if (isset($parameters['start'])) { + $parameters['start'] = $this->parseTimeString($parameters['start']); + if ($parameters['start'] === false) { + unset($parameters['start']); + } + } + + if (isset($parameters['end'])) { + $parameters['end'] = $this->parseTimeString($parameters['end']); + if ($parameters['end'] === false) { + unset($parameters['end']); + } + } + + $parameters['page'] = 1; + + return true; + } + + /** + * Get an image size array like that returned by getimagesize(), or false if it + * can't be determined. + * + * This function is used for determining the width, height and bitdepth directly + * from an image. The results are stored in the database in the img_width, + * img_height, img_bits fields. + * + * @note If this is a multipage file, return the width and height of the + * first page. + * + * @access public + * @param File $image The image object, or false if there isn't one + * @param string $path The filename + * @return mixed An array following the format of PHP getimagesize() internal function or false if not supported. + */ + public function getImageSize($file, $path) { + return false; + } + + /** + * Get a MediaTransformOutput object representing the transformed output. Does the + * transform unless $flags contains self::TRANSFORM_LATER. + * + * @param File $image The image object + * @param string $dstPath Filesystem destination path + * @param string $dstUrl Destination URL to use in output HTML + * @param array $params Arbitrary set of parameters validated by $this->validateParam() + * Note: These parameters have *not* gone through $this->normaliseParams() + * @param integer $flags A bitfield, may contain self::TRANSFORM_LATER + * @return MediaTransformOutput + */ + public function doTransform($file, $dstPath, $dstUrl, $parameters, $flags = 0) { + $this->normaliseParams($file, $parameters); + + return new AudioTransformOutput($file, $parameters); + } + + /** + * Shown in file history box on image description page. + * + * @access public + * @param File $file + * @return string Dimensions + */ + public function getDimensionsString($file) { + global $wgLang; + + $probe = new FFProbe($file->getLocalRefPath()); + + $format = $probe->getFormat(); + $stream = $probe->getStream("a:0"); + + if ($format === false || $stream === false) { + return parent::getDimensionsString($file); + } + + return wfMessage('ev_audio_short_desc', $wgLang->formatTimePeriod($format->getDuration()))->text(); + } + + /** + * Short description. Shown on Special:Search results. + * + * @access public + * @param File $file + * @return string + */ + public function getShortDesc($file) { + global $wgLang; + + $probe = new FFProbe($file->getLocalRefPath()); + + $format = $probe->getFormat(); + $stream = $probe->getStream("a:0"); + + if ($format === false || $stream === false) { + return parent::getGeneralShortDesc($file); + } + + return wfMessage('ev_audio_short_desc', $wgLang->formatTimePeriod($format->getDuration()), $wgLang->formatSize($file->getSize()))->text(); + } + + /** + * Long description. Shown under image on image description page surounded by (). + * + * @access public + * @param File $file + * @return string + */ + public function getLongDesc($file) { + global $wgLang; + + $probe = new FFProbe($file->getLocalRefPath()); + + $format = $probe->getFormat(); + $stream = $probe->getStream("a:0"); + + if ($format === false || $stream === false) { + return parent::getGeneralLongDesc($file); + } + + $extension = pathinfo($file->getLocalRefPath(), PATHINFO_EXTENSION); + + return wfMessage('ev_audio_long_desc', strtoupper($extension), $stream->getCodecName(), $wgLang->formatTimePeriod($format->getDuration()), $wgLang->formatBitrate($format->getBitRate()))->text(); + } +} diff --git a/www/wiki/extensions/EmbedVideo/classes/media/AudioTransformOutput.php b/www/wiki/extensions/EmbedVideo/classes/media/AudioTransformOutput.php new file mode 100644 index 00000000..641224d4 --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/media/AudioTransformOutput.php @@ -0,0 +1,96 @@ +<?php +/** + * EmbedVideo + * AudioTransformOutput Class + * + * @author Alexia E. Smith + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ + +namespace EmbedVideo; + +class AudioTransformOutput extends \MediaTransformOutput { + /** + * Main Constructor + * + * @access public + * @param object File + * @param array Parameters for constructing HTML. + * @return void + */ + public function __construct($file, $parameters) { + $this->file = $file; + $this->parameters = $parameters; + $this->width = (isset($parameters['width']) ? $parameters['width'] : null); + $this->height = (isset($parameters['height']) ? $parameters['height'] : null); + $this->path = $file->getLocalRefPath(); + $this->lang = false; + $this->page = $parameters['page']; + $this->url = $file->getFullUrl(); + } + + /** + * Fetch HTML for this transform output + * + * @access public + * @param array $options Associative array of options. Boolean options + * should be indicated with a value of true for true, and false or + * absent for false. + * + * alt Alternate text or caption + * desc-link Boolean, show a description link + * file-link Boolean, show a file download link + * custom-url-link Custom URL to link to + * custom-title-link Custom Title object to link to + * valign vertical-align property, if the output is an inline element + * img-class Class applied to the "<img>" tag, if there is such a tag + * + * For images, desc-link and file-link are implemented as a click-through. For + * sounds and videos, they may be displayed in other ways. + * + * @return string HTML + */ + public function toHtml($options = []) { + $parameters = $this->parameters; + + $style = []; + $style[] = "max-width: 100%;"; + if (empty($options['no-dimensions'])) { + $parameters['width'] = $this->getWidth(); + $style[] = "width: {$this->getWidth()}px;"; + } + + if (!empty($options['valign'])) { + $style[] = "vertical-align: {$options['valign']};"; + } + + if (!empty($options['img-class'])) { + $class = $options['img-class']; + } + + if (!isset($parameters['start'])) { + $parameters['start'] = null; + } + if (!isset($parameters['end'])) { + $parameters['end'] = null; + } + + $inOut = false; + if ($parameters['start'] !== $parameters['end']) { + if (isset($parameters['start']) && $parameters['start'] !== false) { + $inOut[] = $parameters['start']; + } + + if (isset($parameters['end']) && $parameters['end'] !== false) { + $inOut[] = $parameters['end']; + } + } + + $html = "<audio src='{$this->url}".($inOut !== false ? '#t='.implode(',', $inOut) : '')."' width='{$this->getWidth()}'".(!empty($class) ? " class='{$class}'" : "").(!empty($style) ? " style='".implode(" ", $style)."'" : "")." controls><a href='{$parameters['descriptionUrl']}'>{$parameters['descriptionUrl']}</a></audio>"; + + return $html; + } +} diff --git a/www/wiki/extensions/EmbedVideo/classes/media/FFProbe.php b/www/wiki/extensions/EmbedVideo/classes/media/FFProbe.php new file mode 100644 index 00000000..ccc2a502 --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/media/FFProbe.php @@ -0,0 +1,317 @@ +<?php +/** + * EmbedVideo + * FFProbe + * + * @author Alexia E. Smith + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ + +namespace EmbedVideo; + +class FFProbe { + /** + * File Location + * + * @var string + */ + private $file; + + /** + * Meta Data Cache + * + * @var array + */ + private $metadata = null; + + /** + * Main Constructor + * + * @access public + * @param string File Location on Disk + * @return void + */ + public function __construct($file) { + $this->file = $file; + } + + /** + * Return the entire cache of meta data. + * + * @access public + * @return array Meta Data + */ + public function getMetaData() { + if (!is_array($this->metadata)) { + $this->invokeFFProbe(); + } + return $this->metadata; + } + + /** + * Get a selected stream. Follows ffmpeg's stream selection style. + * + * @access public + * @param string Stream identifier + * Examples: + * "v:0" - Select the first video stream + * "a:1" - Second audio stream + * "i:0" - First stream, whatever it is. + * "s:2" - Third subtitle + * "d:0" - First generic data stream + * "t:1" - Second attachment + * @return mixed StreamInfo object or false if does not exist. + */ + public function getStream($select) { + $this->getMetaData(); + + $types = [ + 'v' => 'video', + 'a' => 'audio', + 'i' => false, + 's' => 'subtitle', + 'd' => 'data', + 't' => 'attachment' + ]; + + if (!isset($this->metadata['streams'])) { + return false; + } + + list($type, $index) = explode(":", $select); + $index = intval($index); + + $type = (isset($types[$type]) ? $types[$type] : false); + + $i = 0; + foreach ($this->metadata['streams'] as $stream) { + if ($type !== false && isset($stream['codec_type'])) { + if ($index === $i && $stream['codec_type'] === $type) { + return new StreamInfo($stream); + } + } + if ($type === false || $stream['codec_type'] === $type) { + $i++; + } + } + return false; + } + + /** + * Get the FormatInfo object. + * + * @access public + * @return mixed FormatInfo object or false if does not exist. + */ + public function getFormat() { + $this->getMetaData(); + + if (!isset($this->metadata['format'])) { + return false; + } + + return new FormatInfo($this->metadata['format']); + } + + /** + * Invoke ffprobe on the command line. + * + * @access private + * @return boolean Success + */ + private function invokeFFProbe() { + global $wgFFprobeLocation; + + if (!file_exists($wgFFprobeLocation)) { + $this->metadata = []; + return false; + } + + $json = shell_exec(escapeshellcmd($wgFFprobeLocation.' -v quiet -print_format json -show_format -show_streams ').escapeshellarg($this->file)); + + $metadata = @json_decode($json, true); + + if (is_array($metadata)) { + $this->metadata = $metadata; + } else { + $this->metadata = []; + return false; + } + return true; + } +} + +class StreamInfo { + /** + * Stream Info + * + * @var array + */ + private $info = null; + + /** + * Main Constructor + * + * @access public + * @param array Stream Info from FFProbe + * @return void + */ + public function __construct($info) { + $this->info = $info; + } + + /** + * Simple helper instead of repeating an if statement everything. + * + * @access private + * @param string Field Name + * @return void + */ + private function getField($field) { + return (isset($this->info[$field]) ? $this->info[$field] : false); + } + + /** + * Return the codec type. + * + * @access public + * @return string Codec type or false if unavailable. + */ + public function getType() { + return $this->getField('codec_type'); + } + + /** + * Return the codec name. + * + * @access public + * @return string Codec name or false if unavailable. + */ + public function getCodecName() { + return $this->getField('codec_name'); + } + + /** + * Return the codec long name. + * + * @access public + * @return string Codec long name or false if unavailable. + */ + public function getCodecLongName() { + return $this->getField('codec_long_name'); + } + + /** + * Return the width of the stream. + * + * @access public + * @return integer Width or false if unavailable. + */ + public function getWidth() { + return $this->getField('width'); + } + + /** + * Return the height of the stream. + * + * @access public + * @return integer Height or false if unavailable. + */ + public function getHeight() { + return $this->getField('height'); + } + + /** + * Return bit depth for a video or thumbnail. + * + * @access public + * @return integer Bit Depth or false if unavailable. + */ + public function getBitDepth() { + return $this->getField('bits_per_raw_sample'); + } + + /** + * Get the duration in seconds. + * + * @access public + * @return mixed Duration in seconds or false if unavailable. + */ + public function getDuration() { + return $this->getField('duration'); + } + + /** + * Bit rate in bPS. + * + * @access public + * @return mixed Bite rate in bPS or false if unavailable. + */ + public function getBitRate() { + return $this->getField('bit_rate'); + } +} + +class FormatInfo { + /** + * Format Info + * + * @var array + */ + private $info = null; + + /** + * Main Constructor + * + * @access public + * @param array Format Info from FFProbe + * @return void + */ + public function __construct($info) { + $this->info = $info; + } + + /** + * Simple helper instead of repeating an if statement everything. + * + * @access private + * @param string Field Name + * @return void + */ + private function getField($field) { + return (isset($this->info[$field]) ? $this->info[$field] : false); + } + + /** + * Get the file path. + * + * @access public + * @return mixed File path or false if unavailable. + */ + public function getFilePath() { + return $this->getField('filename'); + } + + /** + * Get the duration in seconds. + * + * @access public + * @return mixed Duration in seconds or false if unavailable. + */ + public function getDuration() { + return $this->getField('duration'); + } + + /** + * Bit rate in bPS. + * + * @access public + * @return mixed Bite rate in bPS or false if unavailable. + */ + public function getBitRate() { + return $this->getField('bit_rate'); + } +}
\ No newline at end of file diff --git a/www/wiki/extensions/EmbedVideo/classes/media/VideoHandler.php b/www/wiki/extensions/EmbedVideo/classes/media/VideoHandler.php new file mode 100644 index 00000000..9e00d35c --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/media/VideoHandler.php @@ -0,0 +1,200 @@ +<?php +/** + * EmbedVideo + * VideoHandler Class + * + * @author Alexia E. Smith + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ + +namespace EmbedVideo; + +class VideoHandler extends AudioHandler { + /** + * Validate a thumbnail parameter at parse time. + * Return true to accept the parameter, and false to reject it. + * If you return false, the parser will do something quiet and forgiving. + * + * @access public + * @param string $name + * @param mixed $value + */ + public function validateParam($name, $value) { + if ($name === 'width' || $name === 'height') { + return $value > 0; + } + return parent::validateParam($name, $value); + } + + /** + * Changes the parameter array as necessary, ready for transformation. + * Should be idempotent. + * Returns false if the parameters are unacceptable and the transform should fail + * + * @access public + * @param object File + * @param array Parameters + * @return boolean Success + */ + public function normaliseParams($file, &$parameters) { + parent::normaliseParams($file, $parameters); + + list($width, $height) = $this->getImageSize($file, $file->getLocalRefPath()); + + if ($width === 0 && $height === 0) { + //Force a reset. + $width = 640; + $height = 360; + } + + if (isset($parameters['width']) && isset($parameters['height']) && $parameters['width'] > 0 && $parameters['height'] === $parameters['width']) { + // special allowance for square video embeds needed by some wikis, otherwise forced 16:9 ratios are followed. + return true; + } + + if (isset($parameters['width']) && $parameters['width'] > 0 && $parameters['width'] < $width) { + $parameters['width'] = intval($parameters['width']); + + if (!isset($parameters['height'])) { + //Page embeds do not specify thumbnail height so correct it here based on aspect ratio. + $parameters['height'] = round($height / $width * $parameters['width']); + } + } else { + $parameters['width'] = $width; + } + + if (isset($parameters['height']) && $parameters['height'] > 0 && $parameters['height'] < $height) { + $parameters['height'] = intval($parameters['height']); + } else { + $parameters['height'] = $height; + } + + if ($width > 0 && $parameters['width'] > 0) { + if (($height / $width) != ($parameters['height'] / $parameters['width'])) { + $parameters['height'] = round($height / $width * $parameters['width']); + } + } + + return true; + } + + /** + * Get an image size array like that returned by getimagesize(), or false if it + * can't be determined. + * + * This function is used for determining the width, height and bitdepth directly + * from an image. The results are stored in the database in the img_width, + * img_height, img_bits fields. + * + * @note If this is a multipage file, return the width and height of the + * first page. + * + * @access public + * @param File $image The image object, or false if there isn't one + * @param string $path The filename + * @return mixed An array following the format of PHP getimagesize() internal function or false if not supported. + */ + public function getImageSize($file, $path) { + $probe = new FFProbe($path); + + $stream = $probe->getStream("v:0"); + + if ($stream !== false) { + return [$stream->getWidth(), $stream->getHeight(), 0, "width=\"{$stream->getWidth()}\" height=\"{$stream->getHeight()}\"", 'bits' => $stream->getBitDepth()]; + } + return [0, 0, 0, 'width="0" height="0"', 'bits' => 0]; + } + + /** + * Get a MediaTransformOutput object representing the transformed output. Does the + * transform unless $flags contains self::TRANSFORM_LATER. + * + * @param File $image The image object + * @param string $dstPath Filesystem destination path + * @param string $dstUrl Destination URL to use in output HTML + * @param array $params Arbitrary set of parameters validated by $this->validateParam() + * Note: These parameters have *not* gone through $this->normaliseParams() + * @param integer $flags A bitfield, may contain self::TRANSFORM_LATER + * @return MediaTransformOutput + */ + public function doTransform($file, $dstPath, $dstUrl, $parameters, $flags = 0) { + $this->normaliseParams($file, $parameters); + + if (!($flags & self::TRANSFORM_LATER)) { + //@TODO: Thumbnail generation here. + } + + return new VideoTransformOutput($file, $parameters); + } + + /** + * Shown in file history box on image description page. + * + * @access public + * @param File $file + * @return string Dimensions + */ + public function getDimensionsString($file) { + global $wgLang; + + $probe = new FFProbe($file->getLocalRefPath()); + + $format = $probe->getFormat(); + $stream = $probe->getStream("v:0"); + + if ($format === false || $stream === false) { + return parent::getDimensionsString($file); + } + + return wfMessage('ev_video_short_desc', $wgLang->formatTimePeriod($format->getDuration()), $stream->getWidth(), $stream->getHeight())->text(); + } + + /** + * Short description. Shown on Special:Search results. + * + * @access public + * @param File $file + * @return string + */ + public function getShortDesc($file) { + global $wgLang; + + $probe = new FFProbe($file->getLocalRefPath()); + + $format = $probe->getFormat(); + $stream = $probe->getStream("v:0"); + + if ($format === false || $stream === false) { + return parent::getGeneralShortDesc($file); + } + + return wfMessage('ev_video_short_desc', $wgLang->formatTimePeriod($format->getDuration()), $stream->getWidth(), $stream->getHeight(), $wgLang->formatSize($file->getSize()))->text(); + } + + /** + * Long description. Shown under image on image description page surounded by (). + * + * @access public + * @param File $file + * @return string + */ + public function getLongDesc($file) { + global $wgLang; + + $probe = new FFProbe($file->getLocalRefPath()); + + $format = $probe->getFormat(); + $stream = $probe->getStream("v:0"); + + if ($format === false || $stream === false) { + return parent::getGeneralLongDesc($file); + } + + $extension = pathinfo($file->getLocalRefPath(), PATHINFO_EXTENSION); + + return wfMessage('ev_video_long_desc', strtoupper($extension), $stream->getCodecName(), $wgLang->formatTimePeriod($format->getDuration()), $stream->getWidth(), $stream->getHeight(), $wgLang->formatBitrate($format->getBitRate()))->text(); + } +} diff --git a/www/wiki/extensions/EmbedVideo/classes/media/VideoTransformOutput.php b/www/wiki/extensions/EmbedVideo/classes/media/VideoTransformOutput.php new file mode 100644 index 00000000..90408c70 --- /dev/null +++ b/www/wiki/extensions/EmbedVideo/classes/media/VideoTransformOutput.php @@ -0,0 +1,99 @@ +<?php +/** + * EmbedVideo + * VideoTransformOutput Class + * + * @author Alexia E. Smith + * @license MIT + * @package EmbedVideo + * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo + * + **/ + +namespace EmbedVideo; + +class VideoTransformOutput extends \MediaTransformOutput { + /** + * Main Constructor + * + * @access public + * @param object File + * @param array Parameters for constructing HTML. + * @return void + */ + public function __construct($file, $parameters) { + $this->file = $file; + $this->parameters = $parameters; + $this->width = (isset($parameters['width']) ? $parameters['width'] : null); + $this->height = (isset($parameters['height']) ? $parameters['height'] : null); + $this->path = $file->getLocalRefPath(); + $this->lang = false; + $this->page = $parameters['page']; + $this->url = $file->getFullUrl(); + } + + /** + * Fetch HTML for this transform output + * + * @access public + * @param array $options Associative array of options. Boolean options + * should be indicated with a value of true for true, and false or + * absent for false. + * + * alt Alternate text or caption + * desc-link Boolean, show a description link + * file-link Boolean, show a file download link + * custom-url-link Custom URL to link to + * custom-title-link Custom Title object to link to + * valign vertical-align property, if the output is an inline element + * img-class Class applied to the "<img>" tag, if there is such a tag + * + * For images, desc-link and file-link are implemented as a click-through. For + * sounds and videos, they may be displayed in other ways. + * + * @return string HTML + */ + public function toHtml($options = []) { + $parameters = $this->parameters; + + $style = []; + $style[] = "max-width: 100%;"; + $style[] = "max-height: 100%;"; + if (empty($options['no-dimensions'])) { + $parameters['width'] = $this->getWidth(); + $parameters['height'] = $this->getHeight(); + $style[] = "width: {$this->getWidth()}px;"; + $style[] = "height: {$this->getHeight()}px;"; + } + + if (!empty($options['valign'])) { + $style[] = "vertical-align: {$options['valign']};"; + } + + if (!empty($options['img-class'])) { + $class = $options['img-class']; + } + + if (!isset($parameters['start'])) { + $parameters['start'] = null; + } + if (!isset($parameters['end'])) { + $parameters['end'] = null; + } + + $inOut = false; + if ($parameters['start'] !== $parameters['end']) { + if (isset($parameters['start']) && $parameters['start'] !== false) { + $inOut[] = $parameters['start']; + } + + if (isset($parameters['end']) && $parameters['end'] !== false) { + $inOut[] = $parameters['end']; + } + } + + $html = "<video src='{$this->url}".($inOut !== false ? '#t='.implode(',', $inOut) : '')."' width='{$this->getWidth()}' height='{$this->getHeight()}'".(!empty($class) ? " class='{$class}'" : "").(!empty($style) ? " style='".implode(" ", $style)."'" : "")." controls><a href='{$parameters['descriptionUrl']}'>{$parameters['descriptionUrl']}</a></video>"; + + return $html; + } +} |