summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/EmbedVideo
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
first commit
Diffstat (limited to 'www/wiki/extensions/EmbedVideo')
-rw-r--r--www/wiki/extensions/EmbedVideo/.gitignore1
-rw-r--r--www/wiki/extensions/EmbedVideo/CHANGES.md174
-rw-r--r--www/wiki/extensions/EmbedVideo/CREDITS.md6
-rw-r--r--www/wiki/extensions/EmbedVideo/EmbedVideo.hooks.php790
-rw-r--r--www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.magic.php29
-rw-r--r--www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.php35
-rw-r--r--www/wiki/extensions/EmbedVideo/EmbedVideo.php27
-rw-r--r--www/wiki/extensions/EmbedVideo/LICENSE21
-rw-r--r--www/wiki/extensions/EmbedVideo/README.md286
-rw-r--r--www/wiki/extensions/EmbedVideo/README.mediawiki675
-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
-rw-r--r--www/wiki/extensions/EmbedVideo/css/embedvideo.css56
-rw-r--r--www/wiki/extensions/EmbedVideo/extension.json88
-rw-r--r--www/wiki/extensions/EmbedVideo/i18n/de.json19
-rw-r--r--www/wiki/extensions/EmbedVideo/i18n/en.json22
-rw-r--r--www/wiki/extensions/EmbedVideo/i18n/ru.json19
-rw-r--r--www/wiki/extensions/EmbedVideo/i18n/sv.json18
-rw-r--r--www/wiki/extensions/EmbedVideo/img/play-dark-hover.pngbin0 -> 1563 bytes
-rw-r--r--www/wiki/extensions/EmbedVideo/img/play-dark.pngbin0 -> 1561 bytes
-rw-r--r--www/wiki/extensions/EmbedVideo/img/play-hover.pngbin0 -> 264 bytes
-rw-r--r--www/wiki/extensions/EmbedVideo/img/play.pngbin0 -> 260 bytes
-rw-r--r--www/wiki/extensions/EmbedVideo/js/autoResize.js83
-rw-r--r--www/wiki/extensions/EmbedVideo/js/evlPlayer.js36
30 files changed, 4528 insertions, 0 deletions
diff --git a/www/wiki/extensions/EmbedVideo/.gitignore b/www/wiki/extensions/EmbedVideo/.gitignore
new file mode 100644
index 00000000..e43b0f98
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/www/wiki/extensions/EmbedVideo/CHANGES.md b/www/wiki/extensions/EmbedVideo/CHANGES.md
new file mode 100644
index 00000000..e2a80fe8
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/CHANGES.md
@@ -0,0 +1,174 @@
+## Patch Notes
+### v2.7.4
+* Added support for playlist to evlplayer
+* Added support for youtube video list
+* Updated Documentation for evlplayer
+* Added missing dependency for evlplayer in extension.json
+
+### v2.7.3
+* Default Twitch VOD to autoplay=false by default
+* Allow videos to be sized in 1:1 aspect ratios for special use cases.
+
+### v2.7.2
+* Added feature to evlplayer to allow default video content
+
+### v2.7.1
+* Fixed issue with youku videos not embedding properly on https enabled wikis.
+
+### v2.7.0
+* Added SoundCloud support
+* Added ability to use service name as a parser tag (if not defined previously)
+
+### v2.6.1
+* Added new configuration options to disable adding media handlers and file extensions.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/issues/76
+* Fixed an uninitialized array.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/issues/77
+* Fixed undefined indexes in the AudioHandler class.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/issues/78
+* Fixed class properties that were unintentionally declared as static.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/issues/75
+* Fixed RTL language issues with CSS.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/pull/73
+
+### v2.6.0
+* Added support for vertically aligning videos.
+* Improved sizing of video and audio tags in Chrome and Firefox when using the media handler tags.
+* Fixed an undefined variable.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/issues/71
+
+### v2.5.2
+* If ffmpeg is not installed on the server the FFProbe class will no longer attempt to use and instead just return generic descriptions.
+* Fixed properties on FFProbe being incorrectly declared as static.
+* Fixed issues with not returning generic descriptions when the local file being accessed by ffprobe is not readable.
+
+### v2.5.1
+* Added URL argument support to Twitch services.
+
+### v2.5.0
+* Added support for VideoLink tags
+* Support for Disclose.tv added
+* Twitch VOD support updated.
+* Added #evu parser tag
+
+### v2.4.1
+* Merged Sophivorus' improvements and fixes.
+ * Support for TubiTV.com with the tubitv service identifier.
+ * Fixed vimeo aspect ratio.
+ * Fixed dailymotion ID regex.
+* Fixed error messages being double parsed.
+
+### v2.4.0
+* New media handlers to embed locally uploaded video and audio files.
+ * Requires ffmpeg and ffprobe binaries to be installed.
+ * Uses HTML5 <video> and <audio> tags.
+* Two new settings:
+ * $wgFFmpegLocation - Set the location of the ffmpeg binary.
+ * $wgFFprobeLocation - Set the location of the ffprobe binary.
+
+### v2.3.3
+* Support for JW Player.
+
+### v2.3.2
+* Fix auto resize breaking when leaving full screen.
+
+### v2.3.1
+* Fixed issue #54 (EmbedVideo doesn't work with mw-collapsible class)
+* Fixed issue #50 ("arg" should be "args" in addService)
+* Added "inline" alignment option
+* Fixed center alignment option css
+* Auto Resize now targets mobile as well
+
+### v2.3.0
+* Hard cut off of support for versions older then MediaWiki 1.25
+* Auto Resize attribute added
+* Reverted array_key_exists() regression to fix the `<embedvideo>` tag being broken.
+
+### v2.2.9
+* Fixed issue with Twitch.tv switching over to HTTPS.
+* Added support for http://media.ccc.de
+ * https://github.com/HydraWiki/mediawiki-embedvideo/pull/52
+* New services can now be added from other extensions programmatically.
+ * https://github.com/HydraWiki/mediawiki-embedvideo/pull/46
+
+### v2.2.8
+* Support for Daum TVPot
+ * https://github.com/Alexia/mediawiki-embedvideo/pull/38
+* Fix for URL arguments for youtube and youtubeplaylist.
+ * https://github.com/Alexia/mediawiki-embedvideo/pull/40
+* Support for Beam.pro.
+* Support for Hitbox.tv.
+
+### v2.2.7
+* Compatible with Mediawiki 1.24.0+
+ * https://github.com/Alexia/mediawiki-embedvideo/pull/35
+
+### v2.2.6
+* NicoNico Video ID fixes; will work with new ID formats.
+
+### v2.2.5
+* XSS flaws reported by [Mischanix](https://github.com/Mischanix/).
+
+### v2.2.4
+* Fix Bing to work with their new URLs.
+* Remove MSN as their new video service does not support embedding.
+* Standardize Tudou support per their wiki.
+
+### v2.2.3
+* Added support for Youku and Tudou.
+
+### v2.2.2
+* Updated regular expression replacement pattern for Twitch URLs. Old Twitch embed URLs do not automatically redirect.
+
+### v2.2.1
+* Fixed E_NOTICE being thrown for [undefined array indexes](https://github.com/Alexia/mediawiki-embedvideo/issues/25).
+* Back ported some [PHP 5.3 compatibility changes](https://github.com/Alexia/mediawiki-embedvideo/issues/23). Please note that future releases of EmbedVideo may not support PHP 5.3 as it is an outdated version. Upgrading to PHP 5.4 at a minimum is recommended.
+
+### v2.2.0
+* Fixed a bug with alignment that would cause the left align to not work similar to how Mediawiki handles images and other media.
+* New parser tag better suited for templates; #evt.
+* New HTML like tag format that can take parameters.
+
+### v2.1.8
+* Translations updated.
+* Fixed a PHP notice being thrown for the new mobile check.
+
+### v2.1.7
+* German translation thanks to [[User:Messerjokke79]].
+
+### v2.1.6
+* Added to the ability to add optional URL arguments to the generated embed URL.
+
+### v2.1.5
+* Fixed context in which resource modules are loaded. This resolves an issue with CSS not always applying.
+
+### v2.1.4
+* [Problem with Dailymotion videos // EmbedVideo 2.1.3 (running on MediaWiki 1.23.5)](https://github.com/Alexia/mediawiki-embedvideo/issues/16) Thanks to [Pierre-Yves](https://github.com/gentilvirus) for reporting this issue.
+
+### v2.1.3
+* [Accidental usage of PHP 5.4+ array syntax would cause a fatal error for older Mediawiki installations.](https://github.com/Alexia/mediawiki-embedvideo/pull/14) Thanks to [Rich Bowen](https://github.com/rbowen) for reporting and submitting a patch for this issue.
+* Fix for a CSS loading order issue on some wiki configurations.
+
+### v2.1.2
+* [Missing CSS for right alignment on the default container.](https://github.com/Alexia/mediawiki-embedvideo/issues/12)
+* [Parameters were not being reset between parses.](https://github.com/Alexia/mediawiki-embedvideo/issues/13)
+
+### v2.1.1
+* Fixed a logic issue where the $wgEmbedVideoDefaultWidth global override was not obeyed if the video service specified a default width.
+* Actually bumped the version number this time.
+
+### v2.1
+* The width parameter was changed to dimensions. See parameter documentation above.
+* New container parameter to use a standard Mediawiki thumb frame or default to a generic container.
+* The description parameter no longer forces the thumb frame to be used.
+* Added support for Archive.org, Blip.tv, CollegeHumor, Gfycat, Nico Nico Video, TED Talks, and Vine.
+* Ability to center align embeds.
+* CSS resource module.
+
+### v2.0
+* URLs from the player pages that contain the raw video ID can now be used as the ID parameter.
+* Validation of the raw IDs is improved.
+* Code base rewritten to have a VideoService class for future extensibility.
+* Switched to HTML5 iframes wherever possible for embeds.
+* All services overhauled to be up to date and working.
+* The 'auto' and 'center' alignment values were removed as they were not working. They are planned to be implement properly in the future.
diff --git a/www/wiki/extensions/EmbedVideo/CREDITS.md b/www/wiki/extensions/EmbedVideo/CREDITS.md
new file mode 100644
index 00000000..5b274457
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/CREDITS.md
@@ -0,0 +1,6 @@
+Original version developed by Jim R. Wilson (jimbojw)
+Subsequent improvements added by (alphabetical order)
+
+Alexia E. Smith (Alexia)
+Andrew Whitworth (Whiteknight)
+Mohammed Derakhshani (Mderakhs) \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/EmbedVideo.hooks.php b/www/wiki/extensions/EmbedVideo/EmbedVideo.hooks.php
new file mode 100644
index 00000000..0fb64d3c
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/EmbedVideo.hooks.php
@@ -0,0 +1,790 @@
+<?php
+/**
+ * EmbedVideo
+ * EmbedVideo Hooks
+ *
+ * @license MIT
+ * @package EmbedVideo
+ * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo
+ *
+ **/
+
+class EmbedVideoHooks {
+ /**
+ * Temporary storage for the current service object.
+ *
+ * @var object
+ */
+ static private $service;
+
+ /**
+ * Description Parameter
+ *
+ * @var string
+ */
+ static private $description = false;
+
+ /**
+ * Alignment Parameter
+ *
+ * @var string
+ */
+ static private $alignment = false;
+
+ /**
+ * Alignment Parameter
+ *
+ * @var string
+ */
+ static private $vAlignment = false;
+
+ /**
+ * Container Parameter
+ *
+ * @var string
+ */
+ static private $container = false;
+
+ /**
+ * Valid Arguments for the parseEV function hook.
+ *
+ * @var string
+ */
+ static private $validArguments = [
+ 'service' => null,
+ 'id' => null,
+ 'defaultid' => null,
+ 'dimensions' => null,
+ 'alignment' => null,
+ 'description' => null,
+ 'container' => null,
+ 'urlargs' => null,
+ 'autoresize' => null,
+ 'valignment' => null
+ ];
+
+ /**
+ * Hook to setup defaults.
+ *
+ * @access public
+ * @return void
+ */
+ public static function onExtension() {
+ global $wgEmbedVideoDefaultWidth, $wgMediaHandlers, $wgFileExtensions;
+
+ $config = ConfigFactory::getDefaultInstance()->makeConfig('main');
+
+ if (!isset($wgEmbedVideoDefaultWidth) && (isset($_SERVER['HTTP_X_MOBILE']) && $_SERVER['HTTP_X_MOBILE'] == 'true') && $_COOKIE['stopMobileRedirect'] != 1) {
+ //Set a smaller default width when in mobile view.
+ $wgEmbedVideoDefaultWidth = 320;
+ }
+
+ if ($config->get('EmbedVideoEnableAudioHandler')) {
+ $wgMediaHandlers['application/ogg'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/flac'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/ogg'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/mpeg'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/mp4'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/wav'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/webm'] = 'EmbedVideo\AudioHandler';
+ $wgMediaHandlers['audio/x-flac'] = 'EmbedVideo\AudioHandler';
+ }
+ if ($config->get('EmbedVideoEnableVideoHandler')) {
+ $wgMediaHandlers['video/mp4'] = 'EmbedVideo\VideoHandler';
+ $wgMediaHandlers['video/ogg'] = 'EmbedVideo\VideoHandler';
+ $wgMediaHandlers['video/quicktime'] = 'EmbedVideo\VideoHandler';
+ $wgMediaHandlers['video/webm'] = 'EmbedVideo\VideoHandler';
+ $wgMediaHandlers['video/x-matroska'] = 'EmbedVideo\VideoHandler';
+ }
+
+ if ($config->get('EmbedVideoAddFileExtensions')) {
+ $wgFileExtensions[] = 'flac';
+ $wgFileExtensions[] = 'mkv';
+ $wgFileExtensions[] = 'mov';
+ $wgFileExtensions[] = 'mp3';
+ $wgFileExtensions[] = 'mp4';
+ $wgFileExtensions[] = 'oga';
+ $wgFileExtensions[] = 'ogg';
+ $wgFileExtensions[] = 'ogv';
+ $wgFileExtensions[] = 'wav';
+ $wgFileExtensions[] = 'webm';
+ }
+ }
+
+ /**
+ * Sets up this extension's parser functions.
+ *
+ * @access public
+ * @param object Parser object passed as a reference.
+ * @return boolean true
+ */
+ static public function onParserFirstCallInit( Parser &$parser ) {
+ $parser->setFunctionHook( "ev", "EmbedVideoHooks::parseEV" );
+ $parser->setFunctionHook( "evt", "EmbedVideoHooks::parseEVT" );
+ $parser->setFunctionHook( "evp", "EmbedVideoHooks::parseEVP" );
+ $parser->setFunctionHook( "evu", "EmbedVideoHooks::parseEVU" );
+
+ $parser->setHook( "embedvideo", "EmbedVideoHooks::parseEVTag" );
+ $parser->setHook('evlplayer', "EmbedVideoHooks::parseEVLPlayer");
+ $parser->setFunctionHook( 'evl', "EmbedVideoHooks::parseEVL");
+
+ // don't step on VideoLink's toes.
+ if (!class_exists('FXVideoLink')) {
+ $parser->setHook('vplayer', "EmbedVideoHooks::parseEVLPlayer");
+ $parser->setFunctionHook( 'vlink', "EmbedVideoHooks::parseEVL");
+ }
+
+ // smart handling of service name tags (if they aren't already implamented)
+ $tags = $parser->getTags();
+ $services = \EmbedVideo\VideoService::getAvailableServices();
+ $create = array_diff( $services, $tags );
+ // We now have a list of services we can create tags for that aren't already implamented
+ foreach ($create as $service) {
+ $parser->setHook( $service, "EmbedVideoHooks::parseServiceTag{$service}" );
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle passing parseServiceTagSERVICENAME to the parseServiceTag method.
+ *
+ * @param string $name
+ * @param array $args
+ * @return void
+ */
+ public static function __callStatic( $name, $args ) {
+ if ( substr($name, 0, 15) == "parseServiceTag" ) {
+ $service = str_replace( "parseServiceTag", "", $name );
+ return self::parseServiceTag( $service, $args[0], $args[1], $args[2], $args[3] );
+ }
+ }
+
+ /**
+ * Parse tag with service name
+ *
+ * @access public
+ * @param string Raw User Input
+ * @param array Arguments on the tag.
+ * @param object Parser object.
+ * @param object PPFrame object.
+ * @return string Error Message
+ */
+ static public function parseServiceTag( $service, $input, array $args, Parser $parser, PPFrame $frame ) {
+ $args = array_merge( self::$validArguments, $args );
+
+ // accept input as default, but also allow url param.
+ if (empty($input) && isset($args['url'])) {
+ $input = $args['url'];
+ }
+
+ return self::parseEV(
+ $parser,
+ $service,
+ $input,
+ $args['dimensions'],
+ $args['alignment'],
+ $args['description'],
+ $args['container'],
+ $args['urlargs'],
+ $args['autoresize'],
+ $args['valignment']
+ );
+ }
+
+ /**
+ * Parse EVL (and vlink) Tags
+ * @param Parser $parser
+ * @return array
+ */
+ static public function parseEVL( Parser &$parser ) {
+ $args = func_get_args();
+ array_shift( $args );
+
+ // handle comma separated video id list
+ $ids = explode(',', $args[0]);
+ $id = isset($args[2]) && is_numeric($args[2]) ? $args[2] - 1 : false;
+ $video = $id !== false && isset($ids[$id]) ? $ids[$id] : array_shift($ids);
+
+ // standardize first 2 arguments into strings that parse_str can handle.
+ $args[0] = "id=" . $video;
+ $args[1] = "linktitle=" . $args[1];
+
+ $options = [];
+ parse_str( implode( "&", $args ), $options );
+
+ // default service to youtube for compatibility with vlink
+ $options['service'] = isset( $options['service'] ) ? $options['service'] : "youtube";
+
+ // force to youtubevidelink or youtube if video list is provided
+ if (count($ids) > 0) {
+ if ($options['service'] != 'youtube' && $options['service'] != 'youtubevideolist') {
+ $options['notice'] = "The video list feature only works with the youtube service. Your service is being overridden.";
+ }
+ $options['service'] = count($ids) > 0 && $id === false ? "youtubevideolist" : "youtube";
+ }
+
+ $options = array_merge( self::$validArguments, $options );
+
+ // fix for youtube ids that VideoLink would have handled.
+ if ($options['service'] == 'youtube' && strpos($options['id'], ';') !== false) {
+ // transform input like Oh8KRy2WV0o;C5rePhJktn0 into Oh8KRy2WV0o
+ $options['notice'] = "Use of semicolon delimited video lists is deprecated. Only the first video in this list will play.";
+ $options['id'] = strstr($options['id'], ';', true);
+ }
+
+ // force start time on youtube videos from "start".
+ if ($options['service'] == 'youtube' && isset($options['start']) && preg_match('/^([0-9]+:){0,2}[0-9]+(?:\.[0-9]+)?$/', $options['start'])) {
+ $te = explode(':', $options['start']);
+ $tc = count($te);
+ for($i=1, $startTime = floatval($te[0]); $i<$tc; $i++) {
+ $startTime = $startTime*60 + floatval($te[$i]);
+ }
+
+ if (!isset($options['urlargs']) || empty($options['urlargs'])) {
+ // just set the url args to the start time string
+ $options['urlargs'] = "start={$startTime}";
+ } else {
+ // break down the url args and inject the start time in.
+ $urlargs = [];
+ parse_str($options['urlargs'], $urlargs);
+ $urlargs['start'] = $startTime;
+ $options['urlargs'] = http_build_query($urlargs);
+ }
+ }
+
+ // handle adding playlist for video links for a play all link
+ if ($options['service'] == 'youtubevideolist' && count($ids) > 0) {
+ $playlist = implode(',', $ids);
+ if (!isset($options['urlargs']) || empty($options['urlargs'])) {
+ // just set the url args to the playlist
+ $options['urlargs'] = "playlist={$playlist}";
+ } else {
+ // break down the url args and inject the playlist.
+ $urlargs = [];
+ parse_str($options['urlargs'], $urlargs);
+ $urlargs['playlist'] = $playlist;
+ $options['urlargs'] = http_build_query($urlargs);
+ }
+ }
+
+ $json = json_encode($options);
+
+ $link = Xml::element('a', [
+ 'href' => '#',
+ 'data-video-json' => $json,
+ 'class' => 'embedvideo-evl vplink'
+ ], $options['linktitle']);
+
+ $parser->getOutput()->addModules( ['ext.embedVideo-evl', 'ext.embedVideo.styles'] );
+
+ return [ $link, 'noparse' => true, 'isHTML' => true ];
+ }
+
+ /**
+ * Parse EVLPlayer (and vplayer) Tags
+ * @param string $input
+ * @param array $args
+ * @param Parser $parser
+ * @param PPFrame $frame
+ * @return array
+ */
+ static public function parseEVLPlayer($input, array $args, Parser $parser, PPFrame $frame ) {
+ $args = array_merge( self::$validArguments, $args );
+
+ $pid = isset($args['id']) ? $args['id'] : 'default';
+ $w = min(2000, max(240, isset($args['w']) ? (int)$args['w'] : 800));
+ $h = min(1200, max(80, isset($args['h']) ? (int)$args['h'] : (9*$w/16)));
+ $style = isset($args['style']) ? ' '.$args['style'] : '';
+ $class = isset($args['class']) ? ' '.$args['class'] : '';
+
+ if ($args['defaultid'] && $args['service']) {
+ // so we don't have to deal with any screwy parsing of tags by the HTML class.
+ $input = "DEFAULT PLAYER REPLACEMENT";
+ }
+
+ // Parse internal content
+ $content = $parser->recursiveTagParse($input, $frame);
+
+ $div = Html::rawElement('div', array(
+ 'id' => 'vplayerbox-'.$pid,
+ 'class' => 'embedvideo-evlbox vplayerbox'.$class,
+ 'data-size' => $w.'x'.$h,
+ 'style' => $style,
+ ), $content);
+
+ if ($args['defaultid'] && $args['service']) {
+ $new = self::parseEV(
+ $parser,
+ $args['service'],
+ $args['defaultid'],
+ "${w}x${h}",
+ $args['alignment'],
+ $args['description'],
+ $args['container'],
+ $args['urlargs'],
+ $args['autoresize'],
+ $args['valignment']
+ )[0];
+
+ // replace the default content with the new content
+ $div = str_replace( $content, $new, $div );
+ }
+
+ return [ $div, 'noparse'=> true, 'isHTML'=> true ];
+ }
+
+ /**
+ * Embeds a video based on the URL
+ *
+ * @access public
+ * @param object Parser
+ * @return string Error Message
+ */
+ static public function parseEVU( $parser, $url = null ) {
+ if ( !$url ) {
+ return self::error( 'missingparams', $url );
+ }
+ $host = parse_url( $url, PHP_URL_HOST );
+ $host = strtolower($host);
+ $host = str_ireplace('www.', '', $host); // strip www from any hostname.
+
+ $map = \EmbedVideo\VideoService::getServiceHostMap();
+
+ $service = false;
+
+ if (isset($map[$host])) {
+ if (!is_array($map[$host])) {
+ // only one possible answer. Set it.
+ $service = $map[$host];
+ } else {
+ // map by array.
+ foreach ($map[$host] as $possibleService) {
+ $evs = \EmbedVideo\VideoService::newFromName($possibleService);
+ if ($evs) {
+ $test = $evs->parseVideoID($url);
+
+ if ($test !== false && $test !== $url) {
+ // successful parse - safe assumption that this is correct.
+ $service = $possibleService;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ return self::error( 'cantdecode_evu', $url );
+ }
+
+ if (!$service) {
+ return self::error( 'cantdecode_evu', $url );
+ }
+
+ $arguments = func_get_args();
+ array_shift( $arguments );
+
+ $args = [];
+ foreach ( $arguments as $argumentPair ) {
+ $argumentPair = trim( $argumentPair );
+ if ( !strpos( $argumentPair, '=' ) ) {
+ continue;
+ }
+
+ list( $key, $value ) = explode( '=', $argumentPair, 2 );
+
+ if (!array_key_exists($key, self::$validArguments)) {
+ continue;
+ }
+ $args[$key] = $value;
+ }
+
+ $args = array_merge( self::$validArguments, $args );
+
+ return self::parseEV(
+ $parser,
+ $service,
+ $url,
+ $args['dimensions'],
+ $args['alignment'],
+ $args['description'],
+ $args['container'],
+ $args['urlargs'],
+ $args['autoresize'],
+ $args['valignment']
+ );
+ }
+
+
+ /**
+ * Adapter to call the new style tag.
+ *
+ * @access public
+ * @param object Parser
+ * @return string Error Message
+ */
+ static public function parseEVP( $parser ) {
+ wfDeprecated( __METHOD__, '2.0', 'EmbedVideo' );
+ return self::error( 'evp_deprecated' );
+ }
+
+ /**
+ * Adapter to call the EV parser tag with template like calls.
+ *
+ * @access public
+ * @param object Parser
+ * @return string Error Message
+ */
+ static public function parseEVT( $parser ) {
+ $arguments = func_get_args();
+ array_shift( $arguments );
+
+ foreach ( $arguments as $argumentPair ) {
+ $argumentPair = trim( $argumentPair );
+ if ( !strpos( $argumentPair, '=' ) ) {
+ continue;
+ }
+
+ list( $key, $value ) = explode( '=', $argumentPair, 2 );
+
+ if (!array_key_exists($key, self::$validArguments)) {
+ continue;
+ }
+ $args[$key] = $value;
+ }
+
+ $args = array_merge( self::$validArguments, $args );
+
+ return self::parseEV(
+ $parser,
+ $args['service'],
+ $args['id'],
+ $args['dimensions'],
+ $args['alignment'],
+ $args['description'],
+ $args['container'],
+ $args['urlargs'],
+ $args['autoresize'],
+ $args['valignment']
+ );
+ }
+
+ /**
+ * Adapter to call the parser hook.
+ *
+ * @access public
+ * @param string Raw User Input
+ * @param array Arguments on the tag.
+ * @param object Parser object.
+ * @param object PPFrame object.
+ * @return string Error Message
+ */
+ static public function parseEVTag( $input, array $args, Parser $parser, PPFrame $frame ) {
+ $args = array_merge( self::$validArguments, $args );
+
+ return self::parseEV(
+ $parser,
+ $args['service'],
+ $input,
+ $args['dimensions'],
+ $args['alignment'],
+ $args['description'],
+ $args['container'],
+ $args['urlargs'],
+ $args['autoresize'],
+ $args['valignment']
+ );
+ }
+
+ /**
+ * Embeds a video of the chosen service.
+ *
+ * @access public
+ * @param object Parser
+ * @param string [Optional] Which online service has the video.
+ * @param string [Optional] Identifier Code or URL for the video on the service.
+ * @param string [Optional] Dimensions of video
+ * @param string [Optional] Description to show
+ * @param string [Optional] Horizontal Alignment of the embed container.
+ * @param string [Optional] Container to use.(Frame is currently the only option.)
+ * @param string [Optional] Extra URL Arguments
+ * @param string [Optional] Automatically Resize video that will break its parent container.
+ * @param string [Optional] Vertical Alignment of the embed container.
+ * @return string Encoded representation of input params (to be processed later)
+ */
+ static public function parseEV( $parser, $service = null, $id = null, $dimensions = null, $alignment = null, $description = null, $container = null, $urlArgs = null, $autoResize = null, $vAlignment = null ) {
+ self::resetParameters();
+
+ $service = trim( $service );
+ $id = trim( $id );
+ $alignment = trim( $alignment );
+ $description = trim( $description );
+ $dimensions = trim( $dimensions );
+ $urlArgs = trim( $urlArgs );
+ $width = null;
+ $height = null;
+ $autoResize = ( isset( $autoResize ) && strtolower( trim( $autoResize ) ) == "false" ) ? false : true;
+ $vAlignment = trim( $vAlignment );
+
+ // I am not using $parser->parseWidthParam() since it can not handle height only. Example: x100
+ if ( stristr( $dimensions, 'x' ) ) {
+ $dimensions = strtolower( $dimensions );
+ list( $width, $height ) = explode( 'x', $dimensions );
+ } elseif ( is_numeric( $dimensions ) ) {
+ $width = $dimensions;
+ }
+
+ /************************************/
+ /* Error Checking */
+ /************************************/
+ if ( !$service || !$id ) {
+ return self::error( 'missingparams', $service, $id );
+ }
+
+ self::$service = \EmbedVideo\VideoService::newFromName( $service );
+ if ( !self::$service ) {
+ return self::error( 'service', $service );
+ }
+
+ // Let the service automatically handle bad dimensional values.
+ self::$service->setWidth( $width );
+
+ self::$service->setHeight( $height );
+
+ // If the service has an ID pattern specified, verify the id number.
+ if ( !self::$service->setVideoID( $id ) ) {
+ return self::error( 'id', $service, $id );
+ }
+
+ if ( !self::$service->setUrlArgs( $urlArgs ) ) {
+ return self::error( 'urlargs', $service, $urlArgs );
+ }
+
+ if (!is_null($parser)) {
+ self::setDescription( $description, $parser );
+ } else {
+ self::setDescriptionNoParse( $description );
+ }
+
+
+ if ( !self::setContainer( $container ) ) {
+ return self::error( 'container', $container );
+ }
+
+ if ( !self::setAlignment( $alignment ) ) {
+ return self::error( 'alignment', $alignment );
+ }
+
+ if ( !self::setVerticalAlignment( $vAlignment ) ) {
+ return self::error( 'valignment', $vAlignment );
+ }
+
+ /************************************/
+ /* HMTL Generation */
+ /************************************/
+ $html = self::$service->getHtml();
+ if ( !$html ) {
+ return self::error( 'unknown', $service );
+ }
+
+ if ($autoResize) {
+ $html = self::generateWrapperHTML( $html, null, "autoResize" );
+ } else {
+ $html = self::generateWrapperHTML( $html );
+ }
+
+ if ($parser) {
+ // dont call this if parser is null (such as in API usage).
+ $out = $parser->getOutput();
+ $out->addModules( 'ext.embedVideo' );
+ $out->addModuleStyles( 'ext.embedVideo.styles' );
+ }
+
+ return [
+ $html,
+ 'noparse' => true,
+ 'isHTML' => true
+ ];
+ }
+
+ /**
+ * Generate the HTML necessary to embed the video with the given alignment
+ * and text description
+ *
+ * @access private
+ * @param string [Optional] Horizontal Alignment
+ * @param string [Optional] Description
+ * @param string [Optional] Additional Classes to add to the wrapper
+ * @return string
+ */
+ static private function generateWrapperHTML( $html, $description = null, $addClass = null ) {
+ $classString = "embedvideo";
+ $styleString = "";
+ $innerClassString = "embedvideowrap";
+
+ if ( self::getContainer() == 'frame' ) {
+ $classString .= " thumb";
+ $innerClassString .= " thumbinner";
+ }
+
+ if (self::getAlignment() !== false) {
+ $classString .= " ev_" . self::getAlignment();
+ $styleString .= " width: " . ( self::$service->getWidth() + 6 ) . "px;";
+ }
+
+ if (self::getVerticalAlignment() !== false) {
+ $classString .= " ev_" . self::getVerticalAlignment();
+ }
+
+ if ($addClass) {
+ $classString .= " " . $addClass;
+ }
+
+ $html = "<div class='" . $classString . "' style='" . $styleString . "'><div class='" . $innerClassString . "' style='width: " . self::$service->getWidth() . "px;'>{$html}" . ( self::getDescription() !== false ? "<div class='thumbcaption'>" . self::getDescription() . "</div>" : null ) . "</div></div>";
+
+ return $html;
+ }
+
+ /**
+ * Return the alignment parameter.
+ *
+ * @access public
+ * @return mixed Alignment or false for not set.
+ */
+ static private function getAlignment() {
+ return self::$alignment;
+ }
+
+ /**
+ * Set the align parameter.
+ *
+ * @access private
+ * @param string Alignment Parameter
+ * @return boolean Valid
+ */
+ static private function setAlignment( $alignment ) {
+ if ( !empty( $alignment ) && ( $alignment == 'left' || $alignment == 'right' || $alignment == 'center' || $alignment == 'inline' ) ) {
+ self::$alignment = $alignment;
+ } elseif ( !empty( $alignment ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the valignment parameter.
+ *
+ * @access public
+ * @return mixed Vertical Alignment or false for not set.
+ */
+ static private function getVerticalAlignment() {
+ return self::$vAlignment;
+ }
+
+ /**
+ * Set the align parameter.
+ *
+ * @access private
+ * @param string Alignment Parameter
+ * @return boolean Valid
+ */
+ static private function setVerticalAlignment( $vAlignment ) {
+ if ( !empty( $vAlignment ) && ( $vAlignment == 'top' || $vAlignment == 'middle' || $vAlignment == 'bottom' || $vAlignment == 'baseline' ) ) {
+ if ($vAlignment != 'baseline') {
+ self::$alignment = 'inline';
+ }
+ self::$vAlignment = $vAlignment;
+ } elseif ( !empty( $vAlignment ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return description text.
+ *
+ * @access private
+ * @return mixed String description or false for not set.
+ */
+ static private function getDescription() {
+ return self::$description;
+ }
+
+ /**
+ * Set the description.
+ *
+ * @access private
+ * @param string Description
+ * @param object Mediawiki Parser object
+ * @return void
+ */
+ static private function setDescription( $description, \Parser $parser ) {
+ self::$description = ( !$description ? false : $parser->recursiveTagParse( $description ) );
+ }
+
+ /**
+ * Set the description without using the parser
+ * @param string Description
+ */
+ static private function setDescriptionNoParse( $description ) {
+ self::$description = ( !$description ? false : $description );
+ }
+
+ /**
+ * Return container type.
+ *
+ * @access private
+ * @return mixed String container type or false for not set.
+ */
+ static private function getContainer() {
+ return self::$container;
+ }
+
+ /**
+ * Set the container type.
+ *
+ * @access private
+ * @param string Container
+ * @return boolean Success
+ */
+ static private function setContainer( $container ) {
+ if ( !empty( $container ) && ( $container == 'frame' ) ) {
+ self::$container = $container;
+ } elseif ( !empty( $container ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Reset parameters between parses.
+ *
+ * @access private
+ * @return void
+ */
+ static private function resetParameters() {
+ self::$description = false;
+ self::$alignment = false;
+ self::$container = false;
+ }
+
+ /**
+ * Error Handler
+ *
+ * @access private
+ * @param string [Optional] Error Type
+ * @param mixed [...] Multiple arguments to be retrieved with func_get_args().
+ * @return string Printable Error Message
+ */
+ static private function error( $type = 'unknown' ) {
+ $arguments = func_get_args();
+ array_shift( $arguments );
+
+ $message = wfMessage( 'error_embedvideo_' . $type, $arguments )->escaped();
+
+ return [
+ "<div class='errorbox'>{$message}</div>",
+ 'noparse' => true,
+ 'isHTML' => true
+ ];
+ }
+}
diff --git a/www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.magic.php b/www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.magic.php
new file mode 100644
index 00000000..198c1d8e
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.magic.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * EmbedVideo
+ * EmbedVideo Magic Words
+ *
+ * @license MIT
+ * @package EmbedVideo
+ * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo
+ *
+ **/
+
+$magicWords = [];
+
+$magicWords['en'] = [
+ 'ev' => [0, 'ev'],
+ 'evp' => [0, 'evp'],
+ 'evt' => [0, 'evt'],
+ 'evl' => [0, 'evl'],
+ 'vlink' => [0, 'vlink'],
+ 'evu' => [0, 'evu'],
+ 'ev_start' => [0, 'start=$1'],
+ 'ev_end' => [0, 'end=$1'],
+];
+
+foreach( \EmbedVideo\VideoService::getAvailableServices() as $service ) {
+ if ( !isset($magicWords['en'][$service]) ) {
+ $magicWords['en'][$service] = [0, $service];
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.php b/www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.php
new file mode 100644
index 00000000..1214ce84
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/EmbedVideo.i18n.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * This is a backwards-compatibility shim, generated by:
+ * https://git.wikimedia.org/blob/mediawiki%2Fcore.git/HEAD/maintenance%2FgenerateJsonI18n.php
+ *
+ * Beginning with MediaWiki 1.23, translation strings are stored in json files,
+ * and the EXTENSION.i18n.php file only exists to provide compatibility with
+ * older releases of MediaWiki. For more information about this migration, see:
+ * https://www.mediawiki.org/wiki/Requests_for_comment/Localisation_format
+ *
+ * This shim maintains compatibility back to MediaWiki 1.17.
+ */
+$messages = [];
+if (!function_exists('wfJsonI18nShim9b746919b3007182')) {
+ function wfJsonI18nShim9b746919b3007182($cache, $code, &$cachedData) {
+ $codeSequence = array_merge([$code], $cachedData['fallbackSequence']);
+ foreach ($codeSequence as $csCode) {
+ $fileName = dirname(__FILE__) . "/i18n/$csCode.json";
+ if (is_readable($fileName)) {
+ $data = FormatJson::decode(file_get_contents($fileName), true);
+ foreach (array_keys($data) as $key) {
+ if ($key === '' || $key[0] === '@') {
+ unset($data[$key]);
+ }
+ }
+ $cachedData['messages'] = array_merge($data, $cachedData['messages']);
+ }
+
+ $cachedData['deps'][] = new FileDependency($fileName);
+ }
+ return true;
+ }
+
+ $GLOBALS['wgHooks']['LocalisationCacheRecache'][] = 'wfJsonI18nShim9b746919b3007182';
+} \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/EmbedVideo.php b/www/wiki/extensions/EmbedVideo/EmbedVideo.php
new file mode 100644
index 00000000..523dee7f
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/EmbedVideo.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * EmbedVideo
+ * EmbedVideo Services List
+ * Adds a parser function embedding video from popular sources.
+ * See README for details. For licensing information, see LICENSE. For a
+ * complete list of contributors, see CREDITS
+ *
+ * @license MIT
+ * @package EmbedVideo
+ * @link https://www.mediawiki.org/wiki/Extension:EmbedVideo
+ *
+ **/
+
+if ( function_exists( 'wfLoadExtension' ) ) {
+ wfLoadExtension( 'EmbedVideo' );
+ // Keep i18n globals so mergeMessageFileList.php doesn't break
+ $wgMessagesDirs['EmbedVideo'] = __DIR__ . '/i18n';
+ $wgExtensionMessagesFiles['EmbedVideoMagic'] = __DIR__ . '/EmbedVideo.i18n.magic.php';
+ wfWarn(
+ 'Deprecated PHP entry point used for EmbedVideo extension. Please use wfLoadExtension instead, ' .
+ 'see https://www.mediawiki.org/wiki/Extension_registration for more details.'
+ );
+ return;
+} else {
+ die( 'This version of the EmbedVideo extension requires MediaWiki 1.25+' );
+} \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/LICENSE b/www/wiki/extensions/EmbedVideo/LICENSE
new file mode 100644
index 00000000..418109f3
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2007-2010 EmbedVideo contributors. See CREDITS for complete
+list of contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/www/wiki/extensions/EmbedVideo/README.md b/www/wiki/extensions/EmbedVideo/README.md
new file mode 100644
index 00000000..fb3c3999
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/README.md
@@ -0,0 +1,286 @@
+# About
+
+The EmbedVideo Extension is a MediaWiki extension which adds a parser function called #ev for embedding video clips from over 22 popular video sharing services in multiple languages and countries. It also adds video and audio media handlers to support transforming standard `[[File:Example.mp4]]` file links into embedded HTML5 `<video>` and `<audio>` tags.
+
+For more information about EmbedVideo, to download, to contribute, and to report bugs and problems, visit the GitHub project page:
+
+https://github.com/HydraWiki/mediawiki-embedvideo
+
+Issues, bug reports, and feature requests may be created at the issue tracker:
+
+https://github.com/HydraWiki/mediawiki-embedvideo/issues
+
+The MediaWiki extension page is located at:
+
+https://www.mediawiki.org/wiki/Extension:EmbedVideo
+
+## History
+
+The original version of EmbedVideo was created by Jim R. Wilson. That version was later forked by Mohammed Derakhshani as the EmbedVideoPlus extension. In early 2010 Andrew Whitworth took over active maintenance of both extensions and merged them together as "EmbedVideo". Much later on in September 2014 Alexia E. Smith forcefully took over being unable to contact a current maintainer.
+
+The newer versions of EmbedVideo are intended to be fully backwards-compatible with both older EmbedVideo and EmbedVideoPlus extensions.
+
+# License
+
+EmbedVideo is released under the MIT license
+
+http://www.opensource.org/licenses/mit-license.php
+
+See LICENSE for more details
+
+# Installation
+
+## Download
+
+There are three places to download the EmbedVideo extension. The first is directly from its GitHub project page, where active development takes place. If you have git, you can use this incantation to check out a read-only copy of the extension source:
+
+```
+git clone https://github.com/HydraWiki/mediawiki-embedvideo.git
+```
+
+Downloadable archive packages for numbered releases will also be available from the github project page.
+
+## Installation Instructions
+
+1. Download the contents of the extension, as outlined above.
+2. Create an EmbedVideo folder in the extensions/ folder of your MediaWiki installation.
+3. Copy the contents of this distribution into that folder
+
+For Mediawiki 1.19 through 1.23 add the following line to your LocalSettings.php:
+
+```php
+require_once("$IP/extensions/EmbedVideo/EmbedVideo.php");
+```
+
+For Mediawiki 1.24 and up add the following line to your LocalSettings.php:
+
+```php
+wfLoadExtension("EmbedVideo");
+```
+
+# Usage
+
+## Media Handler
+For locally uploaded content the process for displaying it on a page is the same as an image. [See the image syntax documentation](https://www.mediawiki.org/wiki/Help:Images#Syntax) on MediaWiki.org for complete reference on this feature.
+
+This example would display a video in page using a HTML5 `<video>` tag.
+
+ [[File:Example.mp4]]
+
+To specify the start and end timestamps in the media use the start and end parameters. The timestamp can be formatted as one of: ss, :ss, mm:ss, hh:mm:ss, or dd:hh:mm:ss.
+
+ [[File:Example.mp4|start=2|end=6]]
+
+## Tags
+
+The EmbedVideo parser function expects to be called in any of the following ways:
+
+### \#ev - Classic Parser Tag
+
+* `{{#ev:service|id}}`
+* `{{#ev:service|id|dimensions}}`
+* `{{#ev:service|id|dimensions|alignment}}`
+* `{{#ev:service|id|dimensions|alignment|description}}`
+* `{{#ev:service|id|dimensions|alignment|description|container}}`
+* `{{#ev:service|id|dimensions|alignment|description|container|urlargs}}`
+* `{{#ev:service|id|dimensions|alignment|description|container|urlargs|autoresize}}`
+
+However, if needed optional arguments may be left blank by not putting anything between the pipes:
+
+* `{{#ev:service|id|||description}}`
+
+### \#evt - Parser Tag for Templates
+
+The \#evt parser tag allows for key=value pairs which allows for easier templating and readability.
+
+ {{#evt:
+ service=youtube
+ |id=https://www.youtube.com/watch?v=pSsYTj9kCHE
+ |alignment=right
+ }}
+
+### \#evu - Parser Tag for URLs
+
+The \#evu parser tag is like the \#evt tag, but its first parameter is a URL that will be parsed to determine the service automatically.
+
+ {{#evu:https://www.youtube.com/watch?v=pSsYTj9kCHE
+ |alignment=right
+ }}
+
+### &lt;embedvideo&gt; - Tag Hook
+
+Videos can easily be embedded with the &lt;embedvideo&gt;&lt;/embedvideo&gt; tag hook. The ID/URL goes as the input between the tags and parameters can be added as the tag arguments.
+
+ <embedvideo service="youtube">https://www.youtube.com/watch?v=pSsYTj9kCHE</embedvideo>
+
+
+Alternativly, you can also use the service id as the tag (assuming another extension isn't already using this tag).
+
+ <youtube>https://www.youtube.com/watch?v=pSsYTj9kCHE</youtube>
+
+## Attributes for Parser Tags
+
+| Attribute | Required | Default | Description |
+|---------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `service="(See Supported Services below.)"` | yes | | The video service to call. |
+| `id="[id\|url]"` | yes | | The raw ID of the video or URL from the player page. |
+| `dimensions="[WIDTH\|WIDTHxHEIGHT\|xHEIGHT]"` | no | 640 | Dimensions in pixels to size the embed container. The standard format is width x height where either can be omitted, but the `x` must proceed height to indicate it as the height.<br/>Examples: `480`, `480x320`, `x320`. If the height is not provided it will be calculated automatically from the width and service default ratio.<br/>Some services such as *Gfycat* do not have standard heights and should be specified for each embed. `$wgEmbedVideoDefaultWidth` can be set in `LocalSettings.php` to override the default width. |
+| `alignment="[left\|center\|right\|inline]"` | no | none | Align the placement of the video either to the left, centered, or to the right. Inline will allow multiple videos to display side by side without forced line breaks. |
+| `description="[wiki text]"` | no | none | Display a description under the embed container. |
+| `container="[frame]"` | no | none | Specifies the container type to use for the embed.<br/>`frame`: Wrap the video player in a Mediawiki thumbnail box. |
+| `urlargs="modestbranding=1&version=3"` | no | none | Allows extra URL arguments to be appended to the generated embed URL. This is useful for obscure options only supported on one service. |
+| `autoresize="false"` | no | true | Automatically resize videos when their size will cause them to break outside of their container element |
+| `valignment="[top\|middle\|bottom\|baseline]"` | no | none | Align the vertical placement of the video either to the top, middle, bottom, or baseline of the parent element. Using this parameter forces the alignment parameter to be inline. |
+
+## Examples
+
+### Example #1
+
+For example, a video from YouTube use the 'youtube' service selector enter either the raw ID:
+
+ {{#ev:youtube|pSsYTj9kCHE}}
+
+Or the full URL:
+
+ {{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE}}
+
+### Example #2
+
+To display the same video as a right aligned large thumbnail with a description:
+
+ {{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE|1000|right|Let eet GO|frame}}
+
+For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments.
+
+ https://www.youtube.com/watch?v=pSsYTj9kCHE&start=76
+
+ {{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE|||||start=76}}
+
+### Example #3
+
+Creating a video list for Youtube. This allows you to queue a set of video in a temporary playlist. Use the 'youtubevideolist` service selector:
+
+ {{#ev:youtubevideolist|-D--GWwca0g|||||playlist=afpRzcAAZVM,gMEHZPZTAVc,lom_plwy9iA,BSWYMQEQhEo,EREaWhXj4_Q}}
+
+# Support for VideoLink Tags
+
+Support for the unmaintained VideoLink extension's tags has been added since version 2.5.
+
+From the original extension documentation:
+
+ The VideoLink extension allows embedding of YouTube videos in articles; allowing for multiple linked videos to be played in a single embedded video player, first shown when a user clicks on a video link.
+
+ The <vplayer /> specifies where the player should appear within the page, and the {{#vlink}} parser function allows creation of links that load a specific video.
+
+
+### &lt;evlplayer&gt; - Tag Hook for Video Container
+
+_Note that the use of the `<vplayer>` tag is also acceptable here for backwards compatibility._
+
+This evlplayer tag is used to position the video player container within the page.
+
+ <evlplayer id="player id" w="width" h="height" class="class" style="style">default content</evlplayer>
+
+A default video can be set to fill the container by default instead of `default content` as well.
+
+ <evlplayer id="player1" w="480" h="360" service="youtube" defaultid="pSsYTj9kCHE" />
+
+| Attributes | Required | Default | Description |
+|------------|----------|-------------------------|----------------------------------------------------------|
+| id | no | default | An optional unique identifier for this container |
+| w | no | 800 | Width to send to the embedded player when its generated |
+| h | no | achieve 16:9 from width | Height to send to the embedded player when its generated |
+| class | no | | Additional CSS class to add to the container div |
+| style | no | | Additional in-line CSS to apply to the container div |
+| defaultid | no | | Video ID of default video, if you want a default video. |
+| service | no | | Service of default video, if you want a default video. |
+
+An important caveat to make note of, is that the `w` and `h` attributes only effect the video that is being included into the container div, and not the actual container. For styling of the container, please use the `class` or `style` attributes.
+
+### \#evl - Parser Function for Video Links
+
+_Note that the use of the `{{#vlink}}` parser function is also acceptable here for backwards compatibility._
+
+
+ {{#evl:<video id>|<Link text>|<video to play>|service=youtube|player=<player id>}}
+
+In addition to all of the attributes supported by the `#evt` tag, these specific attributes apply to the `#evl` (and `#vlink`) tags. To maintain backwards compatibility, if you do not define a `service` then `youtube` is assumed. Passing a comma separated list of video ids is only supported for the `youtube` service.
+
+| Attributes | Required | Default | Description |
+|---------------|--------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| video id | yes | none | The ID of the video you would like to play or a comma separated list of videos to play. _Please note that the use of multiple video IDs separated by a semicolon is now deprecated._ |
+| link text | yes | none | The text to display inside the link |
+| video to play | no | none | The number that represents the video to play from video id if its is a comma separated list of ids.
+| player | no | 'default' | Player container to load video in. _Note that the ID 'default' will only exist if you've defined a player with no ID._ |
+| initial video | _deprecated_ | | In the original VideoLink, this would define what video to play first if multiple videos were define. Please see notes about in `video id` and `start`. |
+| start | _deprecated_ | 0:00 | In the original VideoLink, this defined the start time of a video. Since we support multiple video services, this feature can now be replicated with the `urlargs` parameter. For backwards compatibility, this attribute will be respect on videos with the service `youtube`. |
+
+### Video Link Playlist example:
+
+Creating a video list allows video links to create a playlist on the fly using the _youtube_ and _youtubevideolist_ service. _Note: even if you define a service the system will use youtube if a list of videos is provided._
+
+ <evlplayer style="position:relative; width: 800px; margin: 0.5em 0" id="example-player">default content</evlplayer>
+ {{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Play All|player=example-player}}
+ {{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Let eet Go|1|player=example-player}}
+ {{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Let eet Go|2|player=example-player}}
+ {{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Let eet Go|3|player=example-player}}
+
+## Supported Services
+
+As of version 2.x, EmbedVideo supports embedding video content from the following services:
+
+| Site | Service Name(s) | ID Example | URL Example(s) |
+|----------------------------------------------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
+| [Archive.org Videos](https://archive.org/details/movies) | `archiveorg` | electricsheep-flock-244-80000-6 | https://archive.org/details/electricsheep-flock-244-80000-6<br/>https://archive.org/embed/electricsheep-flock-244-80000-6 |
+| [Bambuser](http://bambuser.com/) | `bambuser` - Broadcasts | `bambuser_channel` - Channels | 5262334 |
+| [Beam](https://beam.pro/) | `beam` | RocketBear | https://beam.pro/RocketBear |
+| [Bing](http://www.bing.com/videos/) | `bing` | 31ncp9r7l | http://www.bing.com/videos/watch/video/adorable-cats-attempt-to-eat-invisible-tuna/31ncp9r7l |
+| [Blip.tv](http://www.blip.tv/) | `blip` - Blip requires the full URL to the video page and does not accept the raw ID. | | http://blip.tv/vinylrewind/review-6864612 |
+| [C3TV](https://media.ccc.de/) | `mediacccde` | 32c3-7305-quantum_cryptography | https://media.ccc.de/v/32c3-7305-quantum\_cryptography |
+| [CollegeHumor](http://www.collegehumor.com/) | `collegehumor` | 6875289 | http://www.collegehumor.com/video/6875289/batman-says-his-goodbyes |
+| [Dailymotion](http://www.dailymotion.com/) | `dailymotion` | x1adiiw\_archer-waking-up-as-h-jon-benjamin\_shortfilms | http://www.dailymotion.com/video/x1adiiw\_archer-waking-up-as-h-jon-benjamin\_shortfilms |
+| [Disclose.tv](http://www.disclose.tv/) | `disclose` | 150781 | http://www.disclose.tv/action/viewvideo/150781 |
+| [Daum TVPot](http://tvpot.daum.net/) | `tvpot` - Obtain the URL or ID from the share menu URL. | s9011HdLzYwpLwBodQzCHRB | http://tvpot.daum.net/v/s9011HdLzYwpLwBodQzCHRB |
+| [Div Share](http://www.divshare.com) | `divshare` | | |
+| [Edutopia](http://edutopia.org) | Edutopia content moved to YouTube. Please use the youtube service selector below. | | |
+| [FunnyOrDie](http://www.funnyordie.com/) | `funnyordie` | c61fb67ac9 | http://www.funnyordie.com/videos/c61fb67ac9/to-catch-a-predator-elastic-heart-edition |
+| [Gfycat](http://gfycat.com/) | `gfycat` | BruisedSilentAntarcticfurseal | http://www.gfycat.com/BruisedSilentAntarcticfurseal |
+| [Hitbox](http://www.hitbox.tv/) | `hitbox` | Washuu | http://www.hitbox.tv/Washuu |
+| [JW Player](https://www.jwplayer.com/) | `jwplayer` | cr5d8nbu-8ZpoNmmJs | https://content.jwplatform.com/players/cr5d8nbu-8ZpoNmmJ.html |
+| [Kickstarter](http://www.kickstarter.com/) | `kickstarter` | elanlee/exploding-kittens | https://www.kickstarter.com/projects/elanlee/exploding-kittens |
+| [Metacafe](http://www.metacafe.com/) | `metacafe` | 11404579 | http://www.metacafe.com/watch/11404579/lan\_party\_far\_cry\_4/ |
+| [Nico Nico Video](http://www.nicovideo.jp/) | `nico` | sm24394325 | http://www.nicovideo.jp/watch/sm24394325 |
+| [RuTube](http://rutube.ru/) | `rutube` | b698163ccb67498db74d50cb0f22e556 | http://rutube.ru/video/b698163ccb67498db74d50cb0f22e556/ |
+| [SoundCloud](http://soundcloud.com/) | `soundcloud` | | https://soundcloud.com/skrillex/skrillex-rick-ross-purple-lamborghini |
+| [TeacherTube](http://teachertube.com) | `teachertube` | 370511 | http://www.teachertube.com/video/thats-a-noun-sing-along-hd-version-370511 |
+| [TED Talks](http://www.ted.com/talks/browse/) | `ted` | bruce\_aylward\_humanity\_vs\_ebola\_the\_winning\_strategies\_in\_a\_terrifying\_war | http://www.ted.com/talks/bruce\_aylward\_humanity\_vs\_ebola\_the\_winning\_strategies\_in\_a\_terrifying\_war |
+| [Tubi TV](http://tubitv.com) | `tubitv` | 318409 | http://tubitv.com/video/318409 |
+| [Tudou](http://www.tudou.com/) | `tudou` | mfQXfumwiew | http://www.tudou.com/listplay/mfQXfumwiew.html |
+| [Twitch](http://www.twitch.tv) | `twitch` - Live Streams | `twitchvod` - Archived Videos on Demand | twitchplayspokemon |
+| [Videomaten](http://89.160.51.62/recordme/spelain.htm) | `videomaten` | | |
+| [Vimeo](http://www.vimeo.com) | `vimeo` | 105035718 | http://vimeo.com/105035718 |
+| [Vine](http://vine.co) | `vine` | h2B7WMtuX2t | https://vine.co/v/h2B7WMtuX2t |
+| [Yahoo Screen](http://screen.yahoo.com/) | `yahoo` | katy-perry-dances-sharks-2015-024409668 | https://screen.yahoo.com/videos-for-you/katy-perry-dances-sharks-2015-024409668.html |
+| [YouTube](http://www.youtube.com/) | `youtube` - Single Videos | pSsYTj9kCHE | https://www.youtube.com/watch?v=pSsYTj9kCHE |
+| [YouTube](http://www.youtube.com/) | `youtubeplaylist` - Playlists | PLY0KbDiiFYeNgQkjujixr7qD-FS8qecoP | https://www.youtube.com/embed/?listType=playlist&list=PLY0KbDiiFYeNgQkjujixr7qD-FS8qecoP |
+| [YouTube](http://www.youtube.com/) | `youtubevideolist` - Video List | pSsYTj9kCHE - urlargs=playlist=pSsYTj9kCHE,pSsYTj9kCHE | https://www.youtube.com/embed/pSsYTj9kCHE?playlist=pSsYTj9kCHE,pSsYTj9kCHE |
+| [Youku](http://www.youku.com/) | `youku` | XODc3NDgzMTY4 | http://v.youku.com/v\_show/id\_XODc3NDgzMTY4.html |
+
+# Configuration Settings
+
+| Variable | Default Value | Description |
+|---------------------------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
+| $wgEmbedVideoAddFileExtensions | true | Boolean - Enable or disable adding video/audio file extensions to the list of allowable files to be uploaded. |
+| $wgEmbedVideoEnableVideoHandler | true | Boolean - Enable or disable the video media handlers for displaying embedded video in articles. |
+| $wgEmbedVideoEnableAudioHandler | true | Boolean - Enable or disable the audio media handlers for displaying embedded audio in articles. |
+| $wgEmbedVideoDefaultWidth | | Integer - Globally override the default width of video players. When not set this uses the video service's default width which is typically 640 pixels. |
+| $wgEmbedVideoMinWidth | | Integer - Minimum width of video players. Widths specified below this value will be automatically bounded to it. |
+| $wgEmbedVideoMaxWidth | | Integer - Maximum width of video players. Widths specified above this value will be automatically bounded to it. |
+| $wgFFmpegLocation | /usr/bin/ffmpeg | String - Set the location of the ffmpeg binary. |
+| $wgFFprobeLocation | /usr/bin/ffprobe | String - Set the location of the ffprobe binary. |
+
+# Credits
+
+The original version of EmbedVideo was written by Jim R. Wilson. Additional major upgrades made by Andrew Whitworth, Alexia E. Smith, and other contributors.
+
+See CREDITS for details
diff --git a/www/wiki/extensions/EmbedVideo/README.mediawiki b/www/wiki/extensions/EmbedVideo/README.mediawiki
new file mode 100644
index 00000000..2520502a
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/README.mediawiki
@@ -0,0 +1,675 @@
+{{TNT|Extension
+|name = EmbedVideo
+|license = MIT
+|status = stable
+|type = parser function
+|author = Alexia E. Smith ([[User:Alexia E. Smith|Alexia E. Smith]]), Andrew Whitworth ([[User:Whiteknight|Whiteknight]]), Jim R. Wilson ([[User:Jimbojw|Jimbojw]]), and Mohammad Derakhshani ([[User:Mderakhs|Mderakhs]])
+|version = 2.7.4
+|update = 2018-07-16
+|mediawiki = 1.25+
+|php = 5.4+
+|download = {{TNT|GithubDownload|HydraWiki|mediawiki-embedvideo}}
+|image = EmbedVideoExample1.jpg
+|imagesize = 350px
+|hook1 = ParserFirstCallInit
+|description = Adds a parser function called <code>#ev</code> for embedding video clips from popular video sharing services.
+|example = [http://help.gamepedia.com/Extension:EmbedVideo/Example Gamepedia Help Wiki]
+|vagrant-role = embedvideo
+}}
+
+The '''EmbedVideo''' extension adds a parser function called <code>#ev</code> for embedding video clips from over 24 popular video sharing services in multiple languages and countries. It also adds video and audio media handlers to support transforming standard <nowiki>[[File:Example.mp4]]</nowiki> file links into embedded HTML5 <nowiki><video></nowiki> and <nowiki><audio></nowiki> tags.
+
+;Project Homepage: [https://github.com/HydraWiki/mediawiki-embedvideo Documentation at Github]
+;Source Code: [https://github.com/HydraWiki/mediawiki-embedvideo Source code at Github]
+;Bugs:[https://github.com/HydraWiki/mediawiki-embedvideo/issues issues at Github]
+;Licensing: EmbedVideo is released under [http://www.opensource.org/licenses/mit-license.php The MIT License].
+
+<span style="font-weight: bold; color: darkred;">Notice:</span> As of the 2.x series the EmbedVideo extension has a new maintainer and an entirely rewritten code base. It retains backwards compatibility with EmbedVideo 1.0 and EmbedVideoPlus.
+
+==Installation==
+
+{{TNT|ExtensionInstall
+ |registration=required
+ |download-link=[https://github.com/HydraWiki/mediawiki-embedvideo/archive/v2.7.3.zip Download]
+ |custom-steps=* [[#Configuration|Configure as required]]
+}}
+
+==Usage==
+
+=== Media Handler ===
+For locally uploaded content the process for displaying it on a page is the same as an image. [https://www.mediawiki.org/wiki/Help:Images#Syntax See the image syntax documentation] on MediaWiki.org for complete reference on this feature.
+
+This example would display a video in page using a HTML5 <code><nowiki><video></nowiki></code> tag.
+
+<pre><nowiki>[[File:Example.mp4]]</nowiki></pre>
+
+To specify the start and end timestamps in the media use the start and end parameters. The timestamp can be formatted as one of: ss, :ss, mm:ss, hh:mm:ss, or dd:hh:mm:ss.
+
+<pre><nowiki>[[File:Example.mp4|start=2|end=6]]</nowiki></pre>
+
+=== Tags ===
+
+The EmbedVideo parser function expects to be called in any of the following ways:
+
+====<code>#ev</code> - Classic Parser Tag====
+* <code><nowiki>{{#ev:service|id}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions|alignment}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions|alignment|description}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions|alignment|description|container}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions|alignment|description|container|urlargs}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions|alignment|description|container|urlargs|autoresize}}</nowiki></code>
+* <code><nowiki>{{#ev:service|id|dimensions|alignment|description|container|urlargs|autoresize|valignment}}</nowiki></code>
+
+However, if needed optional arguments may be left blank by not putting anything between the pipes:
+* <code><nowiki>{{#ev:service|id|||description}}</nowiki></code>
+
+====<code>#evt</code> - Parser Tag for Templates====
+The <nowiki>#evt</nowiki> parser tag allows for key=value pairs which allows for easier templating and readability.
+
+<pre><nowiki>{{#evt:
+service=youtube
+|id=https://www.youtube.com/watch?v=pSsYTj9kCHE
+|alignment=right
+}}</nowiki></pre>
+
+====<code>#evu</code> - Parser Tag for URLs====
+The <nowiki>#evu</nowiki> parser tag is like the <nowiki>#evt</nowiki> tag, but its first parameter is a URL that will be parsed to determine the service automatically.
+
+<pre><nowiki>{{#evu:https://www.youtube.com/watch?v=pSsYTj9kCHE
+|alignment=right
+}}</nowiki></pre>
+
+====<code><embedvideo></code> - Tag Hook====
+Videos can easily be embedded with the <nowiki><embedvideo></embedvideo></nowiki> tag hook. The ID/URL goes as the input between the tags and parameters can be added as the tag arguments.
+
+<pre><nowiki><embedvideo service="youtube">https://www.youtube.com/watch?v=pSsYTj9kCHE</embedvideo></nowiki></pre>
+
+Alternativly, you can also use the service id as the tag (assuming another extension isn't already using this tag).
+
+<pre><nowiki><youtube>https://www.youtube.com/watch?v=pSsYTj9kCHE</youtube></nowiki></pre>
+
+
+=== Attributes for Parser Tags ===
+
+{| class="wikitable"
+|-
+! Attribute
+! Description
+|-
+| <code>service="<nowiki>(See Supported Services below.)</nowiki>"</code>
+| '''Required:''' yes
+:The video service to call.
+|-
+| <code>id="<nowiki>[id|url]</nowiki>"</code>
+| '''Required:''' yes
+:The raw ID of the video or URL from the player page.
+|-
+| <code>dimensions="<nowiki>[WIDTH|WIDTHxHEIGHT|xHEIGHT]</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>640</code>
+:Dimensions in pixels to size the embed container. The standard format is width x height where either can be omitted, but the <code>x</code> must proceed height to indicate it as the height.
+:Examples: <code>480</code>, <code>480x320</code>, <code>x320</code>. If the height is not provided it will be calculated automatically from the width and service default ratio.
+:Some services such as ''Gfycat'' do not have standard heights and should be specified for each embed. <code>$wgEmbedVideoDefaultWidth</code> can be set in <code>LocalSettings.php</code> to override the default width.
+|-
+| <code>alignment="<nowiki>[left|center|right|inline]</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>none</code>
+:Align the placement of the video either to the left, centered, or to the right. Inline will allow multiple videos to display side by side without forced line breaks.
+|-
+| <code>description="<nowiki>[wiki text]</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>none</code>
+:Display a description under the embed container.
+|-
+| <code>container="<nowiki>[frame]</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>none</code>
+:Specifies the container type to use for the embed.
+::<code>frame</code>: Wrap the video player in a Mediawiki thumbnail box.
+|-
+| <code>urlargs="<nowiki>modestbranding=1&version=3</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>none</code>
+:Allows extra URL arguments to be appended to the generated embed URL. This is useful for obscure options only supported on one service.
+|-
+| <code>autoresize="<nowiki>false</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>true</code>
+:Automatically resize videos when their size will cause them to break outside of their container element.
+|-
+| <code>valignment="<nowiki>[top|middle|bottom|baseline]</nowiki>"</code>
+| '''Required:''' no, '''Default:''' <code>none</code>
+:Align the vertical placement of the video either to the top, middle, bottom, or baseline of the parent element. Using this parameter forces the alignment parameter to be inline.
+|}
+
+=== Examples ===
+
+[[File:EmbedVideoExample1.jpg|thumb|Example #1]]
+
+For example, a video from YouTube uses the 'youtube' service selector. You can specify either the raw ID:
+<pre>{{#ev:youtube|pSsYTj9kCHE}}</pre>
+Or specify the full URL:
+<pre>{{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE}}</pre>
+
+
+
+[[File:EmbedVideoExample2.jpg|thumb|Example #2]]
+
+
+
+
+
+
+To display the same video as a right aligned large thumbnail with a description:
+<pre>{{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE|1000|right|Let eet GO|frame}}</pre>
+
+For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments.
+<pre>https://www.youtube.com/watch?v=pSsYTj9kCHE&start=76</pre>
+<pre>{{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE|||||start=76}}</pre>
+
+URL arguments can be used to chop up a video and loop it:
+<pre>https://www.youtube.com/watch?v=pSsYTj9kCHE&start=160&end=180&loop=1</pre>
+<pre>{{#ev:youtube|https://www.youtube.com/watch?v=pSsYTj9kCHE|||||start=160&end=180&loop=1}}</pre>
+
+== Support for VideoLink Tags ==
+
+Support for the unmaintained VideoLink extension’s tags has been added since version 2.5.
+
+From the original extension documentation:
+
+<pre>The VideoLink extension allows embedding of YouTube videos in articles; allowing for multiple linked videos to be played in a single embedded video player, first shown when a user clicks on a video link.
+
+The &lt;evlplayer /&gt; specifies where the player should appear within the page, and the {{#vlink}} parser function allows creation of links that load a specific video.</pre>
+=== <code>&lt;evlplayer&gt;</code> - Tag Hook for Video Container ===
+
+''Note that the use of the <code>&lt;evlplayer&gt;</code> tag is also acceptable here for backwards compatibility.''
+
+This evlplayer tag is used to position the video player container within the page.
+
+<pre>&lt;evlplayer id=&quot;player id&quot; w=&quot;width&quot; h=&quot;height&quot; class=&quot;class&quot; style=&quot;style&quot;&gt;default content&lt;/evlplayer&gt;</pre>
+
+A default video can be set to fill the container by default instead of default content as well.
+
+<pre>&lt;evlplayer id=&quot;player1&quot; w=&quot;480&quot; h=&quot;360&quot; service=&quot;youtube&quot; defaultid=&quot;pSsYTj9kCHE&quot; /&gt;</pre>
+
+{| class="wikitable"
+!width="11%"| Attributes
+!width="10%"| Required
+!width="23%"| Default
+!width="54%"| Description
+|-
+| id
+| no
+| default
+| An optional unique identifier for this container
+|-
+| w
+| no
+| 800
+| Width to send to the embedded player when its generated
+|-
+| h
+| no
+| achieve 16:9 from width
+| Height to send to the embedded player when its generated
+|-
+| class
+| no
+|
+| Additional CSS class to add to the container div
+|-
+| style
+| no
+|
+| Additional in-line CSS to apply to the container div
+|-
+| defaultid
+| no
+|
+| Video ID of default video, if you want a default video.
+|-
+| service
+| no
+|
+| Service of default video, if you want a default video.
+|}
+
+An important caveat to make note of, is that the <code>w</code> and <code>h</code> attributes only effect the video that is being included into the container div, and not the actual container. For styling of the container, please use the <code>class</code> or <code>style</code> attributes.
+
+=== <code>#evl</code> - Parser Function for Video Links ===
+
+''Note that the use of the <code>{{#vlink}}</code> parser function is also acceptable here for backwards compatibility.''
+
+<pre>{{#evl:&lt;video id&gt;|&lt;Link text&gt;&gt;|&lt;video to play&gt;|service=youtube|player=&lt;player id&gt;}}</pre>
+In addition to all of the attributes supported by the <code>#evt</code> tag, these specific attributes apply to the <code>#evl</code> (and <code>#vlink</code>) tags. To maintain backwards compatibility, if you do not define a <code>service</code> then <code>youtube</code> is assumed.
+
+{|class="wikitable"
+!width="5%"| Attributes
+!width="4%"| Required
+!width="3%"| Default
+!width="86%"| Description
+|-
+| video id
+| yes
+| none
+| The ID of the video you would like to play or a comma separated list of videos to play. ''Please note that the use of multiple video IDs separated by a semicolon is now deprecated.''
+|-
+| link text
+| yes
+| none
+| The text to display inside the link
+|-
+| video to play
+| no
+| none
+| The number that represents the video to play from video id if its is a comma separated list of ids.
+|-
+| player
+| no
+| ‘default’
+| Player container to load video in. ''Note that the ID ‘default’ will only exist if you’ve defined a player with no ID.''
+|-
+| initial video
+| ''deprecated''
+|
+
+| In the original VideoLink, this would define what video to play first if multiple videos were define. Please see notes about in <code>video id</code> and <code>start</code>.
+|-
+| start
+| ''deprecated''
+| 0:00
+| In the original VideoLink, this defined the start time of a video. Since we support multiple video services, this feature can now be replicated with the <code>urlargs</code> parameter. For backwards compatibility, this attribute will be respect on videos with the service <code>youtube</code>.
+|}
+
+=== Video link Examples ===
+
+Creating a video list allows video links to create a playlist on the fly using the ''youtube'' and ''youtubevideolist'' service. ''Note: even if you define a service the system will use youtube if a list of videos is provided.''
+
+<pre>&lt;evlplayer id=&quot;player id&quot; w=&quot;width&quot; h=&quot;height&quot; class=&quot;class&quot; style=&quot;style&quot; id=&quot;example-player&quot;&gt;default content&lt;/evlplayer&gt;</pre>
+<pre>{{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Play All|player=example-player}}</pre>
+<pre>{{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Let eet Go|1|player=example-player}}</pre>
+<pre>{{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Let eet Go|2|player=example-player}}</pre>
+<pre>{{#evl:pSsYTj9kCHE,pSsYTj9kCHE,pSsYTj9kCHE|Let eet Go|3|player=example-player}}</pre>
+
+== Supported Services ==
+
+As of version 2.x, EmbedVideo supports embedding video content from the following services:
+{| class="wikitable"
+|-
+! Site
+! colspan="2"|Service Name(s)
+! ID Example
+! URL Example(s)
+|-
+! [https://archive.org/details/movies Archive.org Videos]
+| colspan="2"|<code>archiveorg</code>
+| <nowiki>electricsheep-flock-244-80000-6</nowiki>
+| <nowiki>https://archive.org/details/electricsheep-flock-244-80000-6</nowiki>
+ <nowiki>https://archive.org/embed/electricsheep-flock-244-80000-6</nowiki>
+|-
+! [http://bambuser.com/ Bambuser]
+| <code>bambuser</code> - Broadcasts
+| <code>bambuser_channel</code> - Channels
+| <nowiki>5262334</nowiki>
+| <nowiki>http://bambuser.com/v/5262334</nowiki>
+|-
+! [https://beam.pro/ Beam]
+| colspan="2"|<code>beam</code> - Streams
+| <nowiki>RocketBear</nowiki>
+| <nowiki>https://beam.pro/RocketBear</nowiki>
+|-
+! [http://www.bing.com/videos/ Bing]
+| colspan="2"|<code>bing</code>
+| <nowiki>31ncp9r7l</nowiki>
+| <nowiki>http://www.bing.com/videos/watch/video/adorable-cats-attempt-to-eat-invisible-tuna/31ncp9r7l</nowiki>
+|-
+! [http://www.blip.tv/ Blip.tv]
+| colspan="2"|<code>blip</code> - Blip requires the full URL to the video page and does not accept the raw ID.
+|
+|<nowiki>http://blip.tv/vinylrewind/review-6864612</nowiki>
+|-
+! [https://media.ccc.de/ C3TV]
+| colspan="2"|<code>mediacccde</code>
+| <nowiki>32c3-7305-quantum_cryptography</nowiki>
+| <nowiki>https://media.ccc.de/v/32c3-7305-quantum_cryptography</nowiki>
+|-
+! [http://www.collegehumor.com/ CollegeHumor]
+| colspan="2"|<code>collegehumor</code>
+| <nowiki>6875289</nowiki>
+| <nowiki>http://www.collegehumor.com/video/6875289/batman-says-his-goodbyes</nowiki>
+|-
+! [http://www.dailymotion.com/ Dailymotion]
+| colspan="2"|<code>dailymotion</code>
+| <nowiki>x1adiiw_archer-waking-up-as-h-jon-benjamin_shortfilms</nowiki>
+| <nowiki>http://www.dailymotion.com/video/x1adiiw_archer-waking-up-as-h-jon-benjamin_shortfilms</nowiki>
+|-
+! [http://tvpot.daum.net/ Daum TVPot]
+| colspan="2"|<code>tvpot</code> - Obtain the URL or ID from the share menu URL.
+| <nowiki>s9011HdLzYwpLwBodQzCHRB</nowiki>
+| <nowiki>http://tvpot.daum.net/v/s9011HdLzYwpLwBodQzCHRB</nowiki>
+|-
+! [http://www.divshare.com Div Share]
+| colspan="2"|<code>divshare</code>
+|
+|
+|-
+! [http://edutopia.org Edutopia]
+| colspan="2"|Edutopia content moved to YouTube. Please use the youtube service selector below.
+|
+|
+|-
+! [http://www.funnyordie.com/ FunnyOrDie]
+| colspan="2"|<code>funnyordie</code>
+| <nowiki>c61fb67ac9</nowiki>
+| <nowiki>http://www.funnyordie.com/videos/c61fb67ac9/to-catch-a-predator-elastic-heart-edition</nowiki>
+|-
+! [http://gfycat.com/ Gfycat]
+| colspan="2"|<code>gfycat</code>
+| <nowiki>BruisedSilentAntarcticfurseal</nowiki>
+| <nowiki>http://www.gfycat.com/BruisedSilentAntarcticfurseal</nowiki>
+|-
+! [http://www.hitbox.tv/ Hitbox]
+| colspan="2"|<code>hitbox</code>
+| <nowiki>Washuu</nowiki>
+| <nowiki>http://www.hitbox.tv/Washuu</nowiki>
+|-
+! [https://www.jwplayer.com/ JW Player]
+| colspan="2"|<code>jwplayer</code>
+| <nowiki>cr5d8nbu-8ZpoNmmJ</nowiki>
+| <nowiki>https://content.jwplatform.com/players/cr5d8nbu-8ZpoNmmJ.html</nowiki>
+|-
+! [http://www.kickstarter.com/ Kickstarter]
+| colspan="2"|<code>kickstarter</code>
+| <nowiki>elanlee/exploding-kittens</nowiki>
+| <nowiki>https://www.kickstarter.com/projects/elanlee/exploding-kittens</nowiki>
+|-
+! [http://www.metacafe.com/ Metacafe]
+| colspan="2"|<code>metacafe</code>
+| <nowiki>11404579</nowiki>
+| <nowiki>http://www.metacafe.com/watch/11404579/lan_party_far_cry_4/</nowiki>
+|-
+! [http://www.nicovideo.jp/ Nico Nico Video]
+| colspan="2"|<code>nico</code>
+| <nowiki>sm24394325</nowiki>
+| <nowiki>http://www.nicovideo.jp/watch/sm24394325</nowiki>
+|-
+! [http://rutube.ru/ RuTube]
+| colspan="2"|<code>rutube</code>
+| <nowiki>b698163ccb67498db74d50cb0f22e556</nowiki>
+| <nowiki>http://rutube.ru/video/b698163ccb67498db74d50cb0f22e556/</nowiki>
+|-
+! [http://soundcloud.com/ SoundCloud]
+| colspan="2"|<code>soundcloud</code>
+| <nowiki>use full url</nowiki>
+| <nowiki>https://soundcloud.com/skrillex/skrillex-rick-ross-purple-lamborghini</nowiki>
+|-
+! [http://teachertube.com TeacherTube]
+| colspan="2"|<code>teachertube</code>
+| <nowiki>370511</nowiki>
+| <nowiki>http://www.teachertube.com/video/thats-a-noun-sing-along-hd-version-370511</nowiki>
+|-
+! [http://www.ted.com/talks/browse/ TED Talks]
+| colspan="2"|<code>ted</code>
+| <nowiki>bruce_aylward_humanity_vs_ebola_the_winning_strategies_in_a_terrifying_war</nowiki>
+| <nowiki>http://www.ted.com/talks/bruce_aylward_humanity_vs_ebola_the_winning_strategies_in_a_terrifying_war</nowiki>
+|-
+! [https://tubitv.com Tubi TV]
+| colspan="2"|<code>tubitv</code>
+| <nowiki>318409</nowiki>
+| <nowiki>http://tubitv.com/video/318409</nowiki>
+|-
+! [http://www.tudou.com/ Tudou]
+| colspan="2"|<code>tudou</code>
+| <nowiki>mfQXfumwiew</nowiki>
+| <nowiki>http://www.tudou.com/listplay/mfQXfumwiew.html</nowiki>
+|-
+! [http://www.twitch.tv Twitch]
+| <code>twitch</code> - Live Streams
+| <code>twitchvod</code> - Archived Videos on Demand
+| <nowiki>twitchplayspokemon</nowiki>
+| <nowiki>http://www.twitch.tv/twitchplayspokemon</nowiki>
+|-
+! [http://89.160.51.62/recordme/spelain.htm Videomaten]
+| colspan="2"|<code>videomaten</code>
+|
+|
+|-
+! [http://www.vimeo.com Vimeo]
+| colspan="2"|<code>vimeo</code>
+| <nowiki>105035718</nowiki>
+| <nowiki>http://vimeo.com/105035718</nowiki>
+|-
+! [http://vine.co Vine]
+| colspan="2"|<code>vine</code>
+| <nowiki>h2B7WMtuX2t</nowiki>
+| <nowiki>https://vine.co/v/h2B7WMtuX2t</nowiki>
+|-
+! [http://screen.yahoo.com/ Yahoo Screen]
+| colspan="2"|<code>yahoo</code>
+| <nowiki>katy-perry-dances-sharks-2015-024409668</nowiki>
+| <nowiki>https://screen.yahoo.com/videos-for-you/katy-perry-dances-sharks-2015-024409668.html</nowiki>
+|-
+! [http://www.youtube.com/ YouTube]
+| <code>youtube</code> - Single Videos
+| <code>youtubeplaylist</code> - Playlists
+| <code>youtubevideolist</code> - Video List
+| <nowiki>pSsYTj9kCHE</nowiki>
+| <nowiki>https://www.youtube.com/watch?v=pSsYTj9kCHE</nowiki>
+ <nowiki>https://www.youtube.com/watch?v=CW_PbErQ_c8&list=PL76E80C7F756A2047</nowiki>
+ <nowiki>https://www.youtube.com/watch?v=CW_PbErQ_c8&playlist=CW_PbErQ_c8,CW_PbErQ_c8</nowiki>
+|-
+! [http://www.youku.com/ Youku]
+| colspan="2"|<code>youku</code>
+| <nowiki>XODc3NDgzMTY4</nowiki>
+| <nowiki>http://v.youku.com/v_show/id_XODc3NDgzMTY4.html</nowiki>
+|-
+|}
+
+==Configurations==
+
+{| class="wikitable"
+|--
+! Variable
+! Default Value
+! Description
+|--
+| <code>$wgEmbedVideoAddFileExtensions</code>
+| <code>true</code>
+| Boolean - Enable or disable adding video/audio file extensions to the list of allowable files to be uploaded.
+|--
+| <code>$wgEmbedVideoEnableVideoHandler</code>
+| <code>true</code>
+| Boolean - Enable or disable the video media handlers for displaying embedded video in articles.
+|--
+| <code>$wgEmbedVideoEnableAudioHandler</code>
+| <code>true</code>
+| Boolean - Enable or disable the audio media handlers for displaying embedded audio in articles.
+|--
+| <code>$wgEmbedVideoDefaultWidth</code>
+| <code>''</code>
+| Integer - Globally override the default width of video players. When not set this uses the video service's default width which is typically 640 pixels.
+|--
+| <code>$wgEmbedVideoMinWidth</code>
+| <code>''</code>
+| Integer - Minimum width of video players. Widths specified below this value will be automatically bounded to it.
+|--
+| <code>$wgEmbedVideoMaxWidth</code>
+| <code>''</code>
+| Integer - Maximum width of video players. Widths specified above this value will be automatically bounded to it.
+|--
+| <code>$wgFFmpegLocation</code>
+| <code>/usr/bin/ffmpeg</code>
+| String - Set the location of the ffmpeg binary.
+|--
+| <code>$wgFFprobeLocation</code>
+| <code>/usr/bin/ffprobe</code>
+| String - Set the location of the ffprobe binary.
+|}
+
+==Version history==
+; v2.7.4
+* Added support for playlist to evlplayer
+* Added support for youtube video list
+* Updated Documentation for evlplayer
+* Added missing dependency for evlplayer in extension.json
+
+; v2.7.3
+* Default Twitch VOD to autoplay=false by default
+* Allow videos to be sized in 1:1 aspect ratios for special use cases.
+
+; v2.7.2
+* Added feature to evlplayer to allow default video content
+
+; v2.7.1
+* Fixed issue with youku videos not embedding properly on https enabled wikis.
+
+; v2.7.0
+* Added SoundCloud support
+* Added ability to use service name as a parser tag (if not defined previously)
+
+; v2.6.1
+* Added new configuration options to disable adding media handlers and file extensions.
+** https://github.com/HydraWiki/mediawiki-embedvideo/issues/76
+* Fixed an uninitialized array.
+** https://github.com/HydraWiki/mediawiki-embedvideo/issues/77
+* Fixed undefined indexes in the AudioHandler class.
+** https://github.com/HydraWiki/mediawiki-embedvideo/issues/78
+* Fixed class properties that were unintentionally declared as static.
+** https://github.com/HydraWiki/mediawiki-embedvideo/issues/75
+* Fixed RTL language issues with CSS.
+** https://github.com/HydraWiki/mediawiki-embedvideo/pull/73
+
+; v2.6.0
+* Added support for vertically aligning videos.
+* Improved sizing of video and audio tags in Chrome and Firefox when using the media handler tags.
+* Fixed an undefined variable.
+** https://github.com/HydraWiki/mediawiki-embedvideo/issues/71
+
+; v2.5.2
+* If ffmpeg is not installed on the server the FFProbe class will no longer attempt to use and instead just return generic descriptions.
+* Fixed properties on FFProbe being incorrectly declared as static.
+* Fixed issues with not returning generic descriptions when the local file being accessed by ffprobe is not readable.
+
+; v2.5.1
+* Added URL argument support to Twitch services.
+
+; v2.5.0
+* Added support for VideoLink tags
+* Support for Disclose.tv added
+* Twitch VOD support updated.
+* Added <nowiki>#evu</nowiki> parser tag
+
+; v2.4.1
+* Merged Sophivorus' improvements and fixes.
+** Support for TubiTV.com with the tubitv service identifier.
+** Fixed vimeo aspect ratio.
+** Fixed dailymotion ID regex.
+* Fixed error messages being double parsed.
+
+; v2.4.0
+* New media handlers to embed locally uploaded video and audio files.
+** Requires ffmpeg and ffprobe binaries to be installed.
+** Uses HTML5 <nowiki><video></nowiki> and <nowiki><audio></nowiki> tags.
+* Two new settings:
+** $wgFFmpegLocation - Set the location of the ffmpeg binary.
+** $wgFFprobeLocation - Set the location of the ffprobe binary.
+
+; v2.3.3
+* Support for JW Player.
+
+; v2.3.2
+* Fix auto resize breaking when leaving full screen.
+
+; v2.3.1
+* Fixed issue #54 (EmbedVideo doesn't work with mw-collapsible class)
+* Fixed issue #50 ("arg" should be "args" in addService)
+* Added "inline" alignment option
+* Fixed center alignment option css
+* Auto Resize now targets mobile as well
+
+; v2.3.0
+* Hard cut off of support for versions older then MediaWiki 1.25
+* Auto Resize attribute added
+* Reverted array_key_exists() regression to fix the <code><nowiki><embedvideo></nowiki></code> tag being broken.
+
+; v2.2.9
+* Fixed issue with Twitch.tv switching over to HTTPS.
+* [https://github.com/HydraWiki/mediawiki-embedvideo/pull/52 Added support for http://media.ccc.de]
+* [https://github.com/HydraWiki/mediawiki-embedvideo/pull/46 New services can now be added from other extensions programmatically.]
+
+; v2.2.8
+* [https://github.com/HydraWiki/mediawiki-embedvideo/pull/38 Support for Daum TVPot]
+* [https://github.com/HydraWiki/mediawiki-embedvideo/pull/40 Fix for URL arguments for youtube and youtubeplaylist].
+* Support for Beam.pro.
+* Support for Hitbox.tv.
+
+; v2.2.7
+* [https://github.com/HydraWiki/mediawiki-embedvideo/pull/35 Compatible with MediaWiki 1.24.0+]
+
+; v2.2.6
+* NicoNico Video ID fixes; will work with new ID formats.
+
+; v2.2.5
+* XSS flaws reported by [https://github.com/Mischanix/ Mischanix].
+
+; v2.2.4
+* Fix Bing to work with their new URLs.
+* Remove MSN as their new video service does not support embedding.
+* Standardize Tudou support per their wiki.
+
+; v2.2.3
+* Added support for Youku and Tudou.
+
+; v2.2.2
+* Updated regular expression replacement pattern for Twitch URLs. Old Twitch embed URLs do not automatically redirect.
+
+; v2.2.1
+* Fixed E_NOTICE being thrown for [https://github.com/HydraWiki/mediawiki-embedvideo/issues/25 undefined array indexes].
+* Back ported some [https://github.com/HydraWiki/mediawiki-embedvideo/issues/23 PHP 5.3 compatibility changes]. Please note that future releases of EmbedVideo may not support PHP 5.3 as it is an outdated version. Upgrading to PHP 5.4 at a minimum is recommended.
+
+; v2.2.0
+* Fixed a bug with alignment that would cause the left align to not work similar to how MediaWiki handles images and other media.
+* New parser tag better suited for templates; #evt.
+* New HTML like tag format that can take parameters.
+
+; v2.1.0
+* The width parameter was changed to dimensions. See parameter documentation above.
+* New container parameter to use a standard MediaWiki thumb frame or default to a generic container.
+* The description parameter no longer forces the thumb frame to be used.
+* Added support for Archive.org, Blip.tv, CollegeHumor, Gfycat, Nico Nico Video, TED Talks, and Vine.
+* Ability to center align embeds.
+* CSS resource module.
+
+; v2.0
+* URLs from the player pages that contain the raw video ID can now be used as the ID parameter.
+* Validation of the raw IDs is improved.
+* Code base rewritten to have a VideoService class for future extensibility.
+* Switched to HTML5 iframes wherever possible for embeds.
+* All services overhauled to be up to date and working.
+* The 'auto' and 'center' alignment values were removed as they were not working. They are planned to be implement properly in the future.
+
+==Developer Notes==
+
+The major benefit of using a parser function as the injection mechanism for video clips (as opposed to an extension tag) is that parser functions can utilize template parameters (of the form <code><nowiki>{{{1}}}</nowiki></code>).
+
+For example, say you found yourself making a lot of YouTube videos in thumbnail size, right-aligned like an image. You might make a template called <code>Template:Youtube_tn</code> which could contain this:
+<pre>
+<div class="thumb tright">
+{{#ev:youtube|{{{1}}}|{{{2|100}}}}}
+</div>
+</pre>
+
+And then call it like this:
+<pre>
+{{youtube tn|someYoutubeId}}
+</pre>
+
+===Reporting Bugs===
+
+There is an issue tracker set up on the [https://github.com/HydraWiki/mediawiki-embedvideo/issues Github project page].
+
+===Contributing===
+
+Bug reports, feature requests, and patches are always welcome. New contributors can be added to the Github project if there is sufficient interest.
+
+==Weblinks==
+
+* [http://help.gamepedia.com/Extension:EmbedVideo/Example Gamepedia Help Wiki] / [http://fr.wikimini.org/wiki/Aide:Ins%C3%A9rer_une_vid%C3%A9o French] / [http://krefeld-wiki.de/wiki/Hilfe:Video German]
+
+[[Category:Video player extensions]]
+[[Category:YouTube extensions]]
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;
+ }
+}
diff --git a/www/wiki/extensions/EmbedVideo/css/embedvideo.css b/www/wiki/extensions/EmbedVideo/css/embedvideo.css
new file mode 100644
index 00000000..50c2c219
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/css/embedvideo.css
@@ -0,0 +1,56 @@
+div.tcenter.embedvideo,
+div.embedvideo.ev_center {
+ text-align: center;
+}
+
+div.embedvideo.ev_center > .embedvideowrap {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* @noflip */
+div.embedvideo.ev_left {
+ clear: left;
+ float: left;
+}
+
+/* @noflip */
+div.embedvideo.ev_right {
+ clear: right;
+ float: right;
+}
+
+div.embedvideo.ev_inline {
+ display: inline-block;
+}
+
+div.embedvideo.ev_top {
+ vertical-align: top;
+}
+
+div.embedvideo.ev_middle {
+ vertical-align: middle;
+}
+
+div.embedvideo.ev_bottom {
+ vertical-align: bottom;
+}
+
+div.embedvideo.ev_baseline {
+ vertical-align: baseline;
+}
+
+video.thumbborder, audio.thumbborder {
+ border: 1pt solid #ddd;
+}
+
+a.vplink {
+ padding-left: 18px;
+ background-image: url(../img/play.png);
+ background-repeat: no-repeat;
+ background-position: left center;
+}
+
+a.vplink:hover {
+ background-image: url(../img/play-hover.png);
+} \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/extension.json b/www/wiki/extensions/EmbedVideo/extension.json
new file mode 100644
index 00000000..77e66a6d
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/extension.json
@@ -0,0 +1,88 @@
+{
+ "name": "EmbedVideo",
+ "version": "2.7.3",
+ "author": [
+ "Jim R. Wilson",
+ "Andrew Whitworth",
+ "Alexia E. Smith",
+ "Cameron M. Chunn"
+ ],
+ "url": "https://www.mediawiki.org/wiki/Extension:EmbedVideo",
+ "descriptionmsg": "embedvideo_description",
+ "license-name": "MIT",
+ "type": "parserhook",
+ "callback": "EmbedVideoHooks::onExtension",
+ "MessagesDirs": {
+ "EmbedVideo": [
+ "i18n"
+ ]
+ },
+ "ExtensionMessagesFiles": {
+ "EmbedVideoMagic": "EmbedVideo.i18n.magic.php"
+ },
+ "AutoloadClasses": {
+ "EmbedVideoHooks": "EmbedVideo.hooks.php",
+ "ApiEmbedVideo": "classes/ApiEmbedVideo.php",
+ "EmbedVideo\\OEmbed": "classes/OEmbed.php",
+ "EmbedVideo\\VideoService": "classes/VideoService.php",
+ "EmbedVideo\\FFProbe": "classes/media/FFProbe.php",
+ "EmbedVideo\\AudioHandler": "classes/media/AudioHandler.php",
+ "EmbedVideo\\AudioTransformOutput": "classes/media/AudioTransformOutput.php",
+ "EmbedVideo\\VideoHandler": "classes/media/VideoHandler.php",
+ "EmbedVideo\\VideoTransformOutput": "classes/media/VideoTransformOutput.php"
+ },
+ "APIModules": {
+ "embedvideo": "ApiEmbedVideo"
+ },
+ "ResourceModules": {
+ "ext.embedVideo": {
+ "scripts": [
+ "js/autoResize.js"
+ ],
+ "position": "top",
+ "targets": [
+ "desktop",
+ "mobile"
+ ]
+ },
+ "ext.embedVideo-evl": {
+ "scripts": [
+ "js/evlPlayer.js"
+ ],
+ "position": "top",
+ "targets": [
+ "desktop",
+ "mobile"
+ ],
+ "dependencies": [
+ "mediawiki.api"
+ ]
+ },
+ "ext.embedVideo.styles": {
+ "styles": [
+ "css/embedvideo.css"
+ ],
+ "targets": [
+ "desktop",
+ "mobile"
+ ]
+ }
+ },
+ "ResourceFileModulePaths": {
+ "localBasePath": "",
+ "remoteExtPath": "EmbedVideo"
+ },
+ "config": {
+ "EmbedVideoAddFileExtensions": true,
+ "EmbedVideoEnableVideoHandler": true,
+ "EmbedVideoEnableAudioHandler": true,
+ "FFmpegLocation": "/usr/bin/ffmpeg",
+ "FFprobeLocation": "/usr/bin/ffprobe"
+ },
+ "Hooks": {
+ "ParserFirstCallInit": [
+ "EmbedVideoHooks::onParserFirstCallInit"
+ ]
+ },
+ "manifest_version": 1
+}
diff --git a/www/wiki/extensions/EmbedVideo/i18n/de.json b/www/wiki/extensions/EmbedVideo/i18n/de.json
new file mode 100644
index 00000000..526ac8f5
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/i18n/de.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Messerjokke79"
+ ]
+ },
+ "embedvideo_description": "Fügt eine Parserfunktion hinzu, mittels derer Videos populärer Dienste eingebettet werden können.",
+ "error_embedvideo_missingparams": "EmbedVideo fehlt ein anzugebender Parameter.",
+ "error_embedvideo_service": "EmbedVideo erkennt den Videodienst „$1“ nicht.",
+ "error_embedvideo_id": "EmbedVideo erhielt die unbrauchbare ID „$2“ für „$1“.",
+ "error_embedvideo_width": "EmbedVideo erhielt den unbrauchbaren width-Parameter „$1“.",
+ "error_embedvideo_height": "EmbedVideo erhielt den unbrauchbaren height-Parameter „$1“.",
+ "error_embedvideo_alignment": "EmbedVideo erhielt den unbrauchbaren alignment-Parameter „$1“; <span style=\"color: DarkRed\">gültig sind „left“, „center“ und „right“</span>.",
+ "error_embedvideo_valignment": "EmbedVideo erhielt den unbrauchbaren valignment-Parameter „$1“; <span style=\"color: DarkRed\">gültig sind „top“, „middle“, „bottom“ und „baseline“</span>.",
+ "error_embedvideo_urlargs": "EmbedVideo erhielt eine Liste von URL-Argumenten, die unbrauchbare Daten oder leere Argumente aufwiesen.",
+ "error_embedvideo_unknown": "EmbedVideo stieß auf einen Fehler während das Video eingebettet werden sollte. ($1)",
+ "error_embedvideo_evp_deprecated": "Die Parserfunktion „#evp“ wurde in EmbedVideo 2.0 verworfen; alle Parserfunktionen-Tags sind in „#ev“ zu ändern.",
+ "error_embedvideo_container": "Der Wert für „container“ ist ungültig."
+} \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/i18n/en.json b/www/wiki/extensions/EmbedVideo/i18n/en.json
new file mode 100644
index 00000000..1a5c38f3
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/i18n/en.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "embedvideo_description": "Adds a parser function embedding video from popular sources.",
+ "error_embedvideo_missingparams": "EmbedVideo is missing a required parameter.",
+ "error_embedvideo_service": "EmbedVideo does not recognize the video service \"$1\".",
+ "error_embedvideo_id": "EmbedVideo received the bad id \"$2\" for the service \"$1\".",
+ "error_embedvideo_width": "EmbedVideo received the bad width parameter \"$1\".",
+ "error_embedvideo_height": "EmbedVideo received the bad height parameter \"$1\".",
+ "error_embedvideo_alignment": "EmbedVideo was given an illegal value for the alignment parameter \"$1\". Valid values are \"left\", \"center\", or \"right\".",
+ "error_embedvideo_valignment": "EmbedVideo was given an illegal value for the valignment parameter \"$1\". Valid values are \"top\", \"middle\", \"bottom\", or \"baseline\".",
+ "error_embedvideo_urlargs": "EmbedVideo received a list of URL arguments that contained malformed data or blank arguments.",
+ "error_embedvideo_unknown": "EmbedVideo encountered an unknown error while trying to generate this video embed block. ($1)",
+ "error_embedvideo_evp_deprecated": "The #evp parser function was deprecated in EmbedVideo 2.0. Please convert your parser function tag to #ev.",
+ "error_embedvideo_container": "The parameter provided for 'container' is invalid.",
+ "ev_video_short_desc": "$1, $2x$3",
+ "ev_video_long_desc": "$1, $2 codec, length $3, $4x$5 pixels, $6 overall",
+ "ev_audio_short_desc": "$1",
+ "ev_audio_long_desc": "$1, $2 codec, length $3, $4 overall",
+ "error_embedvideo_cantdecode_evu": "Couldn't parse video from $1"
+}
diff --git a/www/wiki/extensions/EmbedVideo/i18n/ru.json b/www/wiki/extensions/EmbedVideo/i18n/ru.json
new file mode 100644
index 00000000..d201badd
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/i18n/ru.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "P@S@f"
+ ]
+ },
+ "embedvideo_description": "Добавляет функцию синтаксического анализатора для встраивания видео с популярных видеохостингов.",
+ "error_embedvideo_missingparams": "В теге EmbedVideo отуствует обязательный параметр.",
+ "error_embedvideo_service": "Тег EmbedVideo не может распознать сервис \"$1\".",
+ "error_embedvideo_id": "В тег EmbedVideo введен неверный id \"$2\" видео для сервиса \"$1\".",
+ "error_embedvideo_width": "В тег EmbedVideo введен неверный параметр ширины \"$1\".",
+ "error_embedvideo_height": "В тег EmbedVideo введен неверный параметр высота \"$1\".",
+ "error_embedvideo_alignment": "EmbedVideo was given an illegal value for the alignment parameter \"$1\". Valid values are \"left\", \"center\", or \"right\".",
+ "error_embedvideo_valignment": "EmbedVideo was given an illegal value for the valignment parameter \"$1\". Valid values are \"top\", \"middle\", \"bottom\", or \"baseline\".",
+ "error_embedvideo_urlargs": "EmbedVideo received a list of URL arguments that contained malformed data or blank arguments.",
+ "error_embedvideo_unknown": "EmbedVideo encountered an unknown error while trying to generate this video embed block. ($1)",
+ "error_embedvideo_evp_deprecated": "The #evp parser function was deprecated in EmbedVideo 2.0. Please convert your parser function tag to #ev.",
+ "error_embedvideo_container": "The parameter provided for 'container' is invalid."
+} \ No newline at end of file
diff --git a/www/wiki/extensions/EmbedVideo/i18n/sv.json b/www/wiki/extensions/EmbedVideo/i18n/sv.json
new file mode 100644
index 00000000..de0e2372
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/i18n/sv.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Andreas Jonsson"
+ ]
+ },
+ "embedvideo_description": "Adds a parser function embedding video from popular sources.",
+ "error_embedvideo_missingparams": "EmbedVideo - En obligatorisk parameter har utelämnats. De två första parametrarna, service och id, som anger videotjänst respektive videoidentitet är obligatoriska",
+ "error_embedvideo_service": "EmbedVideo - Känner inte till videotjänsten \"$1\".",
+ "error_embedvideo_id": "EmbedVideo - Felaktig id \"$2\" för tjänsten \"$1\" har angivits.",
+ "error_embedvideo_width": "EmbedVideo - Felaktigt värde \"$1\" har angivits som bredd.",
+ "error_embedvideo_height": "EmbedVideo - Felaktigt värde \"$1\" har angivits som höjd.",
+ "error_embedvideo_alignment": "EmbedVideo - Felaktigt värde har angivits som placering: \"$1\". Korrekta värden är \"right\" för högerställd, \"left\" för vänsterställd, \"center\" för centrerad.",
+ "error_embedvideo_urlargs": "EmbedVideo received a list of URL arguments that contained malformed data or blank arguments.",
+ "error_embedvideo_unknown": "EmbedVideo encountered an unknown error while trying to generate this video embed block. ($1)",
+ "error_embedvideo_evp_deprecated": "The #evp parser function was deprecated in EmbedVideo 2.0. Please convert your parser function tag to #ev.",
+ "error_embedvideo_container": "The parameter provided for 'container' is invalid."
+}
diff --git a/www/wiki/extensions/EmbedVideo/img/play-dark-hover.png b/www/wiki/extensions/EmbedVideo/img/play-dark-hover.png
new file mode 100644
index 00000000..20dfe7cb
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/img/play-dark-hover.png
Binary files differ
diff --git a/www/wiki/extensions/EmbedVideo/img/play-dark.png b/www/wiki/extensions/EmbedVideo/img/play-dark.png
new file mode 100644
index 00000000..25eef2d8
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/img/play-dark.png
Binary files differ
diff --git a/www/wiki/extensions/EmbedVideo/img/play-hover.png b/www/wiki/extensions/EmbedVideo/img/play-hover.png
new file mode 100644
index 00000000..af5f2ed1
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/img/play-hover.png
Binary files differ
diff --git a/www/wiki/extensions/EmbedVideo/img/play.png b/www/wiki/extensions/EmbedVideo/img/play.png
new file mode 100644
index 00000000..1986485f
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/img/play.png
Binary files differ
diff --git a/www/wiki/extensions/EmbedVideo/js/autoResize.js b/www/wiki/extensions/EmbedVideo/js/autoResize.js
new file mode 100644
index 00000000..294236a7
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/js/autoResize.js
@@ -0,0 +1,83 @@
+(function(mw, $) {
+ $(function() {
+ autoResizer(); // run first thing, because we dont need a resize to be broken.
+
+ var autoResizerRTime;
+ var autoResizerTimeout = false;
+ var autoResizerDelta = 200;
+
+ $(window).resize(function() {
+ autoResizerRTime = new Date();
+ if (autoResizerTimeout === false) {
+ autoResizerTimeout = true;
+ setTimeout(autoResizerResizeEnd, autoResizerDelta);
+ }
+ });
+
+ function isFullscreen() {
+ if (document.fullscreenElement != null || document.mozFullScreenElement != null || document.webkitFullscreenElement != null) {
+ return true;
+ }
+ return false;
+ }
+
+ function autoResizerResizeEnd() {
+ if (isFullscreen()) {
+ autoResizerTimeout = false;
+ return;
+ }
+ if (new Date() - autoResizerRTime < autoResizerDelta) {
+ setTimeout(autoResizerResizeEnd, autoResizerDelta);
+ } else {
+ autoResizerTimeout = false;
+ autoResizer();
+ }
+ }
+
+ function autoResizer() {
+ $('.autoResize').each(function(){
+ var parent = $(this).parent();
+ var self = $(this);
+ var iframe = self.find('iframe');
+ var wrap = self.find('.embedvideowrap');
+
+ if (iframe.width() > parent.width()) {
+ resizeHandler(self, iframe, parent, wrap);
+ } else {
+ self.removeClass('autoResized').css('width', '')
+ var originalWidth = iframe.attr("data-orig-width");
+ var originalHeight = iframe.attr("data-orig-height");
+ iframe.width(originalWidth).css('width', originalWidth).attr('width', originalWidth);
+ iframe.height(originalHeight).css('height', originalHeight).attr('height', originalHeight);
+ wrap.width(originalWidth).css('width', originalWidth).attr('width', originalWidth);
+ wrap.height(originalHeight).css('height', originalHeight).attr('height', originalHeight);
+ }
+
+ if (!self.hasClass('autoResized') && iframe.width() > parent.width()) {
+ resizeHandler(self, iframe, parent, wrap);
+ }
+ });
+ }
+
+ function resizeHandler(self, iframe, parent, wrap) {
+ self.addClass('autoResized');
+
+ if (typeof iframe.attr("data-orig-height") == 'undefined') {
+ iframe.attr("data-orig-height", iframe.height());
+ iframe.attr("data-orig-width", iframe.width());
+ }
+
+ var aspect = iframe.width() / iframe.height();
+ var newWidth = parent.width();
+ var newHeight = newWidth / aspect;
+
+ self.width(newWidth).css('width', newWidth);
+ iframe.width(newWidth).css('width', newWidth).attr('width', newWidth);
+ iframe.height(newHeight).css('height', newHeight).attr('height', newHeight);
+ wrap.width(newWidth).css('width', newWidth).attr('width', newWidth);
+ wrap.height(newHeight).css('height', newHeight).attr('height', newHeight);
+ }
+
+ });
+
+}(mediaWiki, jQuery));
diff --git a/www/wiki/extensions/EmbedVideo/js/evlPlayer.js b/www/wiki/extensions/EmbedVideo/js/evlPlayer.js
new file mode 100644
index 00000000..3419e608
--- /dev/null
+++ b/www/wiki/extensions/EmbedVideo/js/evlPlayer.js
@@ -0,0 +1,36 @@
+(function(mw, $) {
+ $(function() {
+ api = new mw.Api();
+ $('a.embedvideo-evl').click(function(e){
+ e.preventDefault();
+
+ var data = $(this).data('video-json');
+ data['action'] = 'embedvideo';
+ if (typeof data['player'] === 'undefined') {
+ data['player'] = 'default';
+ }
+ var player = data['player'];
+ var container = $('#vplayerbox-'+player);
+ if (!container.length) {
+ mw.debug('No matching vplayer tag found for this link!');
+ return;
+ }
+ // modify dimensions call to match container size if exists.
+ if (container.data('size')) {
+ data['dimensions'] = container.data('size');
+ }
+ api.get( data )
+ .done( function( res ) {
+ if (typeof res.embedvideo.html !== 'undefined') {
+ content = res.embedvideo.html;
+ } else {
+ content = "There was an error while loading this video";
+ }
+ if (data.notice) {
+ content = content + "<small>" + data.notice + "</small>";
+ }
+ container.html(content);
+ } );
+ });
+ });
+}(mediaWiki, jQuery));