summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/EmbedVideo/classes
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/EmbedVideo/classes
first commit
Diffstat (limited to 'www/wiki/extensions/EmbedVideo/classes')
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/ApiEmbedVideo.php104
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/OEmbed.php240
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/VideoService.php848
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/media/AudioHandler.php239
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/media/AudioTransformOutput.php96
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/media/FFProbe.php317
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/media/VideoHandler.php200
-rw-r--r--www/wiki/extensions/EmbedVideo/classes/media/VideoTransformOutput.php99
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="&lt;script type=&quot;text/javascript&quot; src=&quot;http://ext.nicovideo.jp/thumb_watch/%1$s?w=%2$d&amp;h=%3$d&quot;&gt;&lt;/script&gt;" 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&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;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;
+ }
+}