PageRenderTime 42ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/medialib.php

https://bitbucket.org/ngmares/moodle
PHP | 1231 lines | 635 code | 149 blank | 447 comment | 59 complexity | b60a996d721df754d44392f69e421fa9 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-3.0, Apache-2.0, BSD-3-Clause
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Classes for handling embedded media (mainly audio and video).
  18. *
  19. * These are used only from within the core media renderer.
  20. *
  21. * To embed media from Moodle code, do something like the following:
  22. *
  23. * $mediarenderer = $PAGE->get_renderer('core', 'media');
  24. * echo $mediarenderer->embed_url(new moodle_url('http://example.org/a.mp3'));
  25. *
  26. * You do not need to require this library file manually. Getting the renderer
  27. * (the first line above) requires this library file automatically.
  28. *
  29. * @package core_media
  30. * @copyright 2012 The Open University
  31. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32. */
  33. defined('MOODLE_INTERNAL') || die();
  34. if (!defined('CORE_MEDIA_VIDEO_WIDTH')) {
  35. // Default video width if no width is specified; some players may do something
  36. // more intelligent such as use real video width.
  37. // May be defined in config.php if required.
  38. define('CORE_MEDIA_VIDEO_WIDTH', 400);
  39. }
  40. if (!defined('CORE_MEDIA_VIDEO_HEIGHT')) {
  41. // Default video height. May be defined in config.php if required.
  42. define('CORE_MEDIA_VIDEO_HEIGHT', 300);
  43. }
  44. if (!defined('CORE_MEDIA_AUDIO_WIDTH')) {
  45. // Default audio width if no width is specified.
  46. // May be defined in config.php if required.
  47. define('CORE_MEDIA_AUDIO_WIDTH', 300);
  48. }
  49. /**
  50. * Constants and static utility functions for use with core_media_renderer.
  51. *
  52. * @copyright 2011 The Open University
  53. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  54. */
  55. abstract class core_media {
  56. /**
  57. * Option: Disable text link fallback.
  58. *
  59. * Use this option if you are going to print a visible link anyway so it is
  60. * pointless to have one as fallback.
  61. *
  62. * To enable, set value to true.
  63. */
  64. const OPTION_NO_LINK = 'nolink';
  65. /**
  66. * Option: When embedding, if there is no matching embed, do not use the
  67. * default link fallback player; instead return blank.
  68. *
  69. * This is different from OPTION_NO_LINK because this option still uses the
  70. * fallback link if there is some kind of embedding. Use this option if you
  71. * are going to check if the return value is blank and handle it specially.
  72. *
  73. * To enable, set value to true.
  74. */
  75. const OPTION_FALLBACK_TO_BLANK = 'embedorblank';
  76. /**
  77. * Option: Enable players which are only suitable for use when we trust the
  78. * user who embedded the content.
  79. *
  80. * At present, this option enables the SWF player.
  81. *
  82. * To enable, set value to true.
  83. */
  84. const OPTION_TRUSTED = 'trusted';
  85. /**
  86. * Option: Put a div around the output (if not blank) so that it displays
  87. * as a block using the 'resourcecontent' CSS class.
  88. *
  89. * To enable, set value to true.
  90. */
  91. const OPTION_BLOCK = 'block';
  92. /**
  93. * Given a string containing multiple URLs separated by #, this will split
  94. * it into an array of moodle_url objects suitable for using when calling
  95. * embed_alternatives.
  96. *
  97. * Note that the input string should NOT be html-escaped (i.e. if it comes
  98. * from html, call html_entity_decode first).
  99. *
  100. * @param string $combinedurl String of 1 or more alternatives separated by #
  101. * @param int $width Output variable: width (will be set to 0 if not specified)
  102. * @param int $height Output variable: height (0 if not specified)
  103. * @return array Array of 1 or more moodle_url objects
  104. */
  105. public static function split_alternatives($combinedurl, &$width, &$height) {
  106. $urls = explode('#', $combinedurl);
  107. $width = 0;
  108. $height = 0;
  109. $returnurls = array();
  110. foreach ($urls as $url) {
  111. $matches = null;
  112. // You can specify the size as a separate part of the array like
  113. // #d=640x480 without actually including a url in it.
  114. if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) {
  115. $width = $matches[1];
  116. $height = $matches[2];
  117. continue;
  118. }
  119. // Can also include the ?d= as part of one of the URLs (if you use
  120. // more than one they will be ignored except the last).
  121. if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) {
  122. $width = $matches[1];
  123. $height = $matches[2];
  124. // Trim from URL.
  125. $url = str_replace($matches[0], '', $url);
  126. }
  127. // Clean up url.
  128. $url = clean_param($url, PARAM_URL);
  129. if (empty($url)) {
  130. continue;
  131. }
  132. // Turn it into moodle_url object.
  133. $returnurls[] = new moodle_url($url);
  134. }
  135. return $returnurls;
  136. }
  137. /**
  138. * Returns the file extension for a URL.
  139. * @param moodle_url $url URL
  140. */
  141. public static function get_extension(moodle_url $url) {
  142. // Note: Does not use textlib (. is UTF8-safe).
  143. $filename = self::get_filename($url);
  144. $dot = strrpos($filename, '.');
  145. if ($dot === false) {
  146. return '';
  147. } else {
  148. return strtolower(substr($filename, $dot + 1));
  149. }
  150. }
  151. /**
  152. * Obtains the filename from the moodle_url.
  153. * @param moodle_url $url URL
  154. * @return string Filename only (not escaped)
  155. */
  156. public static function get_filename(moodle_url $url) {
  157. global $CFG;
  158. // Use the 'file' parameter if provided (for links created when
  159. // slasharguments was off). If not present, just use URL path.
  160. $path = $url->get_param('file');
  161. if (!$path) {
  162. $path = $url->get_path();
  163. }
  164. // Remove everything before last / if present. Does not use textlib as / is UTF8-safe.
  165. $slash = strrpos($path, '/');
  166. if ($slash !== false) {
  167. $path = substr($path, $slash + 1);
  168. }
  169. return $path;
  170. }
  171. /**
  172. * Guesses MIME type for a moodle_url based on file extension.
  173. * @param moodle_url $url URL
  174. * @return string MIME type
  175. */
  176. public static function get_mimetype(moodle_url $url) {
  177. return mimeinfo('type', self::get_filename($url));
  178. }
  179. }
  180. /**
  181. * Base class for media players.
  182. *
  183. * Media players return embed HTML for a particular way of playing back audio
  184. * or video (or another file type).
  185. *
  186. * In order to make the code more lightweight, this is not a plugin type
  187. * (players cannot have their own settings, database tables, capabilities, etc).
  188. * These classes are used only by core_media_renderer in outputrenderers.php.
  189. * If you add a new class here (in core code) you must modify the
  190. * get_players_raw function in that file to include it.
  191. *
  192. * If a Moodle installation wishes to add extra player objects they can do so
  193. * by overriding that renderer in theme, and overriding the get_players_raw
  194. * function. The new player class should then of course be defined within the
  195. * custom theme or other suitable location, not in this file.
  196. *
  197. * @copyright 2011 The Open University
  198. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  199. */
  200. abstract class core_media_player {
  201. /**
  202. * Placeholder text used to indicate where the fallback content is placed
  203. * within a result.
  204. */
  205. const PLACEHOLDER = '<!--FALLBACK-->';
  206. /**
  207. * Generates code required to embed the player.
  208. *
  209. * The returned code contains a placeholder comment '<!--FALLBACK-->'
  210. * (constant core_media_player::PLACEHOLDER) which indicates the location
  211. * where fallback content should be placed in the event that this type of
  212. * player is not supported by user browser.
  213. *
  214. * The $urls parameter includes one or more alternative media formats that
  215. * are supported by this player. It does not include formats that aren't
  216. * supported (see list_supported_urls).
  217. *
  218. * The $options array contains key-value pairs. See OPTION_xx constants
  219. * for documentation of standard option(s).
  220. *
  221. * @param array $urls URLs of media files
  222. * @param string $name Display name; '' to use default
  223. * @param int $width Optional width; 0 to use default
  224. * @param int $height Optional height; 0 to use default
  225. * @param array $options Options array
  226. * @return string HTML code for embed
  227. */
  228. public abstract function embed($urls, $name, $width, $height, $options);
  229. /**
  230. * Gets the list of file extensions supported by this media player.
  231. *
  232. * Note: This is only required for the default implementation of
  233. * list_supported_urls. If you override that function to determine
  234. * supported URLs in some way other than by extension, then this function
  235. * is not necessary.
  236. *
  237. * @return array Array of strings (extension not including dot e.g. 'mp3')
  238. */
  239. public function get_supported_extensions() {
  240. return array();
  241. }
  242. /**
  243. * Lists keywords that must be included in a url that can be embedded with
  244. * this player. Any such keywords should be added to the array.
  245. *
  246. * For example if this player supports FLV and F4V files then it should add
  247. * '.flv' and '.f4v' to the array. (The check is not case-sensitive.)
  248. *
  249. * Default handling calls the get_supported_extensions function and adds
  250. * a dot to each of those values, so players only need to override this
  251. * if they don't implement get_supported_extensions.
  252. *
  253. * This is used to improve performance when matching links in the media filter.
  254. *
  255. * @return array Array of keywords to add to the embeddable markers list
  256. */
  257. public function get_embeddable_markers() {
  258. $markers = array();
  259. foreach ($this->get_supported_extensions() as $extension) {
  260. $markers[] = '.' . $extension;
  261. }
  262. return $markers;
  263. }
  264. /**
  265. * Gets the ranking of this player. This is an integer used to decide which
  266. * player to use (after applying other considerations such as which ones
  267. * the user has disabled).
  268. *
  269. * Rank must be unique (no two players should have the same rank).
  270. *
  271. * Rank zero has a special meaning, indicating that this 'player' does not
  272. * really embed the video.
  273. *
  274. * Rank is not a user-configurable value because it needs to be defined
  275. * carefully in order to ensure that the embedding fallbacks actually work.
  276. * It might be possible to have some user options which affect rank, but
  277. * these would be best defined as e.g. checkboxes in settings that have
  278. * a particular effect on the rank of a couple of plugins, rather than
  279. * letting users generally alter rank.
  280. *
  281. * Note: Within medialib.php, players are listed in rank order (highest
  282. * rank first).
  283. *
  284. * @return int Rank (higher is better)
  285. */
  286. public abstract function get_rank();
  287. /**
  288. * @return bool True if player is enabled
  289. */
  290. public function is_enabled() {
  291. global $CFG;
  292. // With the class core_media_player_html5video it is enabled
  293. // based on $CFG->core_media_enable_html5video.
  294. $setting = str_replace('_player_', '_enable_', get_class($this));
  295. return !empty($CFG->{$setting});
  296. }
  297. /**
  298. * Given a list of URLs, returns a reduced array containing only those URLs
  299. * which are supported by this player. (Empty if none.)
  300. * @param array $urls Array of moodle_url
  301. * @param array $options Options (same as will be passed to embed)
  302. * @return array Array of supported moodle_url
  303. */
  304. public function list_supported_urls(array $urls, array $options = array()) {
  305. $extensions = $this->get_supported_extensions();
  306. $result = array();
  307. foreach ($urls as $url) {
  308. if (in_array(core_media::get_extension($url), $extensions)) {
  309. $result[] = $url;
  310. }
  311. }
  312. return $result;
  313. }
  314. /**
  315. * Obtains suitable name for media. Uses specified name if there is one,
  316. * otherwise makes one up.
  317. * @param string $name User-specified name ('' if none)
  318. * @param array $urls Array of moodle_url used to make up name
  319. * @return string Name
  320. */
  321. protected function get_name($name, $urls) {
  322. // If there is a specified name, use that.
  323. if ($name) {
  324. return $name;
  325. }
  326. // Get filename of first URL.
  327. $url = reset($urls);
  328. $name = core_media::get_filename($url);
  329. // If there is more than one url, strip the extension as we could be
  330. // referring to a different one or several at once.
  331. if (count($urls) > 1) {
  332. $name = preg_replace('~\.[^.]*$~', '', $name);
  333. }
  334. return $name;
  335. }
  336. /**
  337. * Compares by rank order, highest first. Used for sort functions.
  338. * @param core_media_player $a Player A
  339. * @param core_media_player $b Player B
  340. * @return int Negative if A should go before B, positive for vice versa
  341. */
  342. public static function compare_by_rank(core_media_player $a, core_media_player $b) {
  343. return $b->get_rank() - $a->get_rank();
  344. }
  345. /**
  346. * Utility function that sets width and height to defaults if not specified
  347. * as a parameter to the function (will be specified either if, (a) the calling
  348. * code passed it, or (b) the URL included it).
  349. * @param int $width Width passed to function (updated with final value)
  350. * @param int $height Height passed to function (updated with final value)
  351. */
  352. protected static function pick_video_size(&$width, &$height) {
  353. if (!$width) {
  354. $width = CORE_MEDIA_VIDEO_WIDTH;
  355. $height = CORE_MEDIA_VIDEO_HEIGHT;
  356. }
  357. }
  358. }
  359. /**
  360. * Base class for players which handle external links (YouTube etc).
  361. *
  362. * As opposed to media files.
  363. *
  364. * @copyright 2011 The Open University
  365. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  366. */
  367. abstract class core_media_player_external extends core_media_player {
  368. /**
  369. * Array of matches from regular expression - subclass can assume these
  370. * will be valid when the embed function is called, to save it rerunning
  371. * the regex.
  372. * @var array
  373. */
  374. protected $matches;
  375. /**
  376. * Part of a regular expression, including ending ~ symbol (note: these
  377. * regexes use ~ instead of / because URLs and HTML code typically include
  378. * / symbol and makes harder to read if you have to escape it).
  379. * Matches the end part of a link after you have read the 'important' data
  380. * including optional #d=400x300 at end of url, plus content of <a> tag,
  381. * up to </a>.
  382. * @var string
  383. */
  384. const END_LINK_REGEX_PART = '[^#]*(#d=([\d]{1,4})x([\d]{1,4}))?~si';
  385. public function embed($urls, $name, $width, $height, $options) {
  386. return $this->embed_external(reset($urls), $name, $width, $height, $options);
  387. }
  388. /**
  389. * Obtains HTML code to embed the link.
  390. * @param moodle_url $url Single URL to embed
  391. * @param string $name Display name; '' to use default
  392. * @param int $width Optional width; 0 to use default
  393. * @param int $height Optional height; 0 to use default
  394. * @param array $options Options array
  395. * @return string HTML code for embed
  396. */
  397. protected abstract function embed_external(moodle_url $url, $name, $width, $height, $options);
  398. public function list_supported_urls(array $urls, array $options = array()) {
  399. // These only work with a SINGLE url (there is no fallback).
  400. if (count($urls) != 1) {
  401. return array();
  402. }
  403. $url = reset($urls);
  404. // Check against regex.
  405. if (preg_match($this->get_regex(), $url->out(false), $this->matches)) {
  406. return array($url);
  407. }
  408. return array();
  409. }
  410. /**
  411. * Returns regular expression used to match URLs that this player handles
  412. * @return string PHP regular expression e.g. '~^https?://example.org/~'
  413. */
  414. protected function get_regex() {
  415. return '~^unsupported~';
  416. }
  417. /**
  418. * Annoyingly, preg_match $matches result does not always have the same
  419. * number of parameters - it leaves out optional ones at the end. WHAT.
  420. * Anyway, this function can be used to fix it.
  421. * @param array $matches Array that should be adjusted
  422. * @param int $count Number of capturing groups (=6 to make $matches[6] work)
  423. */
  424. protected static function fix_match_count(&$matches, $count) {
  425. for ($i = count($matches); $i <= $count; $i++) {
  426. $matches[$i] = false;
  427. }
  428. }
  429. }
  430. /**
  431. * Player that embeds Vimeo links.
  432. *
  433. * @copyright 2011 The Open University
  434. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  435. */
  436. class core_media_player_vimeo extends core_media_player_external {
  437. protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
  438. $videoid = $this->matches[1];
  439. $info = s($name);
  440. // Note: resizing via url is not supported, user can click the fullscreen
  441. // button instead. iframe embedding is not xhtml strict but it is the only
  442. // option that seems to work on most devices.
  443. self::pick_video_size($width, $height);
  444. $output = <<<OET
  445. <span class="mediaplugin mediaplugin_vimeo">
  446. <iframe title="$info" src="http://player.vimeo.com/video/$videoid"
  447. width="$width" height="$height" frameborder="0"></iframe>
  448. </span>
  449. OET;
  450. return $output;
  451. }
  452. protected function get_regex() {
  453. // Initial part of link.
  454. $start = '~^http://vimeo\.com/';
  455. // Middle bit: either watch?v= or v/.
  456. $middle = '([0-9]+)';
  457. return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
  458. }
  459. public function get_rank() {
  460. return 1010;
  461. }
  462. public function get_embeddable_markers() {
  463. return array('vimeo.com/');
  464. }
  465. }
  466. /**
  467. * Player that creates YouTube embedding.
  468. *
  469. * @copyright 2011 The Open University
  470. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  471. */
  472. class core_media_player_youtube extends core_media_player_external {
  473. protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
  474. global $CFG;
  475. $site = $this->matches[1];
  476. $videoid = $this->matches[3];
  477. $info = trim($name);
  478. if (empty($info) or strpos($info, 'http') === 0) {
  479. $info = get_string('siteyoutube', 'core_media');
  480. }
  481. $info = s($info);
  482. self::pick_video_size($width, $height);
  483. if (empty($CFG->xmlstrictheaders)) {
  484. return <<<OET
  485. <iframe title="$info" width="$width" height="$height"
  486. src="$site/embed/$videoid?rel=0&wmode=transparent" frameborder="0" allowfullscreen></iframe>
  487. OET;
  488. }
  489. // NOTE: we can not use any link fallback because it breaks built-in
  490. // player on iOS devices.
  491. $output = <<<OET
  492. <span class="mediaplugin mediaplugin_youtube">
  493. <object title="$info" type="application/x-shockwave-flash"
  494. data="$site/v/$videoid&amp;fs=1&amp;rel=0" width="$width" height="$height">
  495. <param name="movie" value="$site/v/$videoid&amp;fs=1&amp;rel=0" />
  496. <param name="FlashVars" value="playerMode=embedded" />
  497. <param name="allowFullScreen" value="true" />
  498. </object>
  499. </span>
  500. OET;
  501. return $output;
  502. }
  503. protected function get_regex() {
  504. // Initial part of link.
  505. $start = '~^(https?://www\.youtube(-nocookie)?\.com)/';
  506. // Middle bit: either watch?v= or v/.
  507. $middle = '(?:watch\?v=|v/)([a-z0-9\-_]+)';
  508. return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
  509. }
  510. public function get_rank() {
  511. // I decided to make the link-embedding ones (that don't handle file
  512. // formats) have ranking in the 1000 range.
  513. return 1001;
  514. }
  515. public function get_embeddable_markers() {
  516. return array('youtube');
  517. }
  518. }
  519. /**
  520. * Player that creates YouTube playlist embedding.
  521. *
  522. * @copyright 2011 The Open University
  523. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  524. */
  525. class core_media_player_youtube_playlist extends core_media_player_external {
  526. public function is_enabled() {
  527. global $CFG;
  528. // Use the youtube on/off flag.
  529. return $CFG->core_media_enable_youtube;
  530. }
  531. protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
  532. $site = $this->matches[1];
  533. $playlist = $this->matches[3];
  534. $info = trim($name);
  535. if (empty($info) or strpos($info, 'http') === 0) {
  536. $info = get_string('siteyoutube', 'core_media');
  537. }
  538. $info = s($info);
  539. self::pick_video_size($width, $height);
  540. // TODO: iframe HTML 5 video not implemented and object does not work
  541. // on iOS devices.
  542. $fallback = core_media_player::PLACEHOLDER;
  543. $output = <<<OET
  544. <span class="mediaplugin mediaplugin_youtube">
  545. <object title="$info" type="application/x-shockwave-flash"
  546. data="$site/p/$playlist&amp;fs=1&amp;rel=0" width="$width" height="$height">
  547. <param name="movie" value="$site/v/$playlist&amp;fs=1&amp;rel=0" />
  548. <param name="FlashVars" value="playerMode=embedded" />
  549. <param name="allowFullScreen" value="true" />
  550. $fallback</object>
  551. </span>
  552. OET;
  553. return $output;
  554. }
  555. protected function get_regex() {
  556. // Initial part of link.
  557. $start = '~^(https?://www\.youtube(-nocookie)?\.com)/';
  558. // Middle bit: either view_play_list?p= or p/ (doesn't work on youtube) or playlist?list=.
  559. $middle = '(?:view_play_list\?p=|p/|playlist\?list=)([a-z0-9\-_]+)';
  560. return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
  561. }
  562. public function get_rank() {
  563. // I decided to make the link-embedding ones (that don't handle file
  564. // formats) have ranking in the 1000 range.
  565. return 1000;
  566. }
  567. public function get_embeddable_markers() {
  568. return array('youtube');
  569. }
  570. }
  571. /**
  572. * MP3 player inserted using JavaScript.
  573. *
  574. * @copyright 2011 The Open University
  575. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  576. */
  577. class core_media_player_mp3 extends core_media_player {
  578. public function embed($urls, $name, $width, $height, $options) {
  579. // Use first url (there can actually be only one unless some idiot
  580. // enters two mp3 files as alternatives).
  581. $url = reset($urls);
  582. // Unique id even across different http requests made at the same time
  583. // (for AJAX, iframes).
  584. $id = 'core_media_mp3_' . md5(time() . '_' . rand());
  585. // When Flash or JavaScript are not available only the fallback is displayed,
  586. // using span not div because players are inline elements.
  587. $spanparams = array('id' => $id, 'class' => 'mediaplugin mediaplugin_mp3');
  588. if ($width) {
  589. $spanparams['style'] = 'width: ' . $width . 'px';
  590. }
  591. $output = html_writer::tag('span', core_media_player::PLACEHOLDER, $spanparams);
  592. // We can not use standard JS init because this may be cached
  593. // note: use 'small' size unless embedding in block mode.
  594. $output .= html_writer::script(js_writer::function_call(
  595. 'M.util.add_audio_player', array($id, $url->out(false),
  596. empty($options[core_media::OPTION_BLOCK]))));
  597. return $output;
  598. }
  599. public function get_supported_extensions() {
  600. return array('mp3');
  601. }
  602. public function get_rank() {
  603. return 80;
  604. }
  605. }
  606. /**
  607. * Flash video player inserted using JavaScript.
  608. *
  609. * @copyright 2011 The Open University
  610. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  611. */
  612. class core_media_player_flv extends core_media_player {
  613. public function embed($urls, $name, $width, $height, $options) {
  614. // Use first url (there can actually be only one unless some idiot
  615. // enters two mp3 files as alternatives).
  616. $url = reset($urls);
  617. // Unique id even across different http requests made at the same time
  618. // (for AJAX, iframes).
  619. $id = 'core_media_flv_' . md5(time() . '_' . rand());
  620. // Compute width and height.
  621. $autosize = false;
  622. if (!$width && !$height) {
  623. $width = CORE_MEDIA_VIDEO_WIDTH;
  624. $height = CORE_MEDIA_VIDEO_HEIGHT;
  625. $autosize = true;
  626. }
  627. // Fallback span (will normally contain link).
  628. $output = html_writer::tag('span', core_media_player::PLACEHOLDER,
  629. array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
  630. // We can not use standard JS init because this may be cached.
  631. $output .= html_writer::script(js_writer::function_call(
  632. 'M.util.add_video_player', array($id, addslashes_js($url->out(false)),
  633. $width, $height, $autosize)));
  634. return $output;
  635. }
  636. public function get_supported_extensions() {
  637. return array('flv', 'f4v');
  638. }
  639. public function get_rank() {
  640. return 70;
  641. }
  642. }
  643. /**
  644. * Embeds Windows Media Player using object tag.
  645. *
  646. * @copyright 2011 The Open University
  647. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  648. */
  649. class core_media_player_wmp extends core_media_player {
  650. public function embed($urls, $name, $width, $height, $options) {
  651. // Get URL (we just use first, probably there is only one).
  652. $firsturl = reset($urls);
  653. $url = $firsturl->out(false);
  654. // Work out width.
  655. if (!$width || !$height) {
  656. // Object tag has default size.
  657. $mpsize = '';
  658. $size = 'width="' . CORE_MEDIA_VIDEO_WIDTH .
  659. '" height="' . (CORE_MEDIA_VIDEO_HEIGHT+64) . '"';
  660. $autosize = 'true';
  661. } else {
  662. $size = 'width="' . $width . '" height="' . ($height + 15) . '"';
  663. $mpsize = 'width="' . $width . '" height="' . ($height + 64) . '"';
  664. $autosize = 'false';
  665. }
  666. // MIME type for object tag.
  667. $mimetype = core_media::get_mimetype($firsturl);
  668. $fallback = core_media_player::PLACEHOLDER;
  669. // Embed code.
  670. return <<<OET
  671. <span class="mediaplugin mediaplugin_wmp">
  672. <object classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" $mpsize
  673. standby="Loading Microsoft(R) Windows(R) Media Player components..."
  674. type="application/x-oleobject">
  675. <param name="Filename" value="$url" />
  676. <param name="src" value="$url" />
  677. <param name="url" value="$url" />
  678. <param name="ShowControls" value="true" />
  679. <param name="AutoRewind" value="true" />
  680. <param name="AutoStart" value="false" />
  681. <param name="Autosize" value="$autosize" />
  682. <param name="EnableContextMenu" value="true" />
  683. <param name="TransparentAtStart" value="false" />
  684. <param name="AnimationAtStart" value="false" />
  685. <param name="ShowGotoBar" value="false" />
  686. <param name="EnableFullScreenControls" value="true" />
  687. <param name="uimode" value="full" />
  688. <!--[if !IE]>-->
  689. <object data="$url" type="$mimetype" $size>
  690. <param name="src" value="$url" />
  691. <param name="controller" value="true" />
  692. <param name="autoplay" value="false" />
  693. <param name="autostart" value="false" />
  694. <param name="resize" value="scale" />
  695. <!--<![endif]-->
  696. $fallback
  697. <!--[if !IE]>-->
  698. </object>
  699. <!--<![endif]-->
  700. </object>
  701. </span>
  702. OET;
  703. }
  704. public function get_supported_extensions() {
  705. return array('wmv', 'avi');
  706. }
  707. public function get_rank() {
  708. return 60;
  709. }
  710. }
  711. /**
  712. * Media player using object tag and QuickTime player.
  713. *
  714. * @copyright 2011 The Open University
  715. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  716. */
  717. class core_media_player_qt extends core_media_player {
  718. public function embed($urls, $name, $width, $height, $options) {
  719. // Show first URL.
  720. $firsturl = reset($urls);
  721. $url = $firsturl->out(true);
  722. // Work out size.
  723. if (!$width || !$height) {
  724. $size = 'width="' . CORE_MEDIA_VIDEO_WIDTH .
  725. '" height="' . (CORE_MEDIA_VIDEO_HEIGHT + 15) . '"';
  726. } else {
  727. $size = 'width="' . $width . '" height="' . ($height + 15) . '"';
  728. }
  729. // MIME type for object tag.
  730. $mimetype = core_media::get_mimetype($firsturl);
  731. $fallback = core_media_player::PLACEHOLDER;
  732. // Embed code.
  733. return <<<OET
  734. <span class="mediaplugin mediaplugin_qt">
  735. <object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
  736. codebase="http://www.apple.com/qtactivex/qtplugin.cab" $size>
  737. <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
  738. <param name="src" value="$url" />
  739. <param name="controller" value="true" />
  740. <param name="loop" value="true" />
  741. <param name="autoplay" value="false" />
  742. <param name="autostart" value="false" />
  743. <param name="scale" value="aspect" />
  744. <!--[if !IE]>-->
  745. <object data="$url" type="$mimetype" $size>
  746. <param name="src" value="$url" />
  747. <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
  748. <param name="controller" value="true" />
  749. <param name="loop" value="true" />
  750. <param name="autoplay" value="false" />
  751. <param name="autostart" value="false" />
  752. <param name="scale" value="aspect" />
  753. <!--<![endif]-->
  754. $fallback
  755. <!--[if !IE]>-->
  756. </object>
  757. <!--<![endif]-->
  758. </object>
  759. </span>
  760. OET;
  761. }
  762. public function get_supported_extensions() {
  763. return array('mpg', 'mpeg', 'mov', 'mp4', 'm4v', 'm4a');
  764. }
  765. public function get_rank() {
  766. return 50;
  767. }
  768. }
  769. /**
  770. * Media player using object tag and RealPlayer.
  771. *
  772. * Hopefully nobody is using this obsolete format any more!
  773. *
  774. * @copyright 2011 The Open University
  775. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  776. */
  777. class core_media_player_rm extends core_media_player {
  778. public function embed($urls, $name, $width, $height, $options) {
  779. // Show first URL.
  780. $firsturl = reset($urls);
  781. $url = $firsturl->out(true);
  782. // Get name to use as title.
  783. $info = s($this->get_name($name, $urls));
  784. // The previous version of this code has the following comment, which
  785. // I don't understand, but trust it is correct:
  786. // Note: the size is hardcoded intentionally because this does not work anyway!
  787. $width = CORE_MEDIA_VIDEO_WIDTH;
  788. $height = CORE_MEDIA_VIDEO_HEIGHT;
  789. $fallback = core_media_player::PLACEHOLDER;
  790. return <<<OET
  791. <span class="mediaplugin mediaplugin_real">
  792. <object title="$info" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"
  793. data="$url" width="$width" height="$height"">
  794. <param name="src" value="$url" />
  795. <param name="controls" value="All" />
  796. <!--[if !IE]>-->
  797. <object title="$info" type="audio/x-pn-realaudio-plugin"
  798. data="$url" width="$width" height="$height">
  799. <param name="src" value="$url" />
  800. <param name="controls" value="All" />
  801. <!--<![endif]-->
  802. $fallback
  803. <!--[if !IE]>-->
  804. </object>
  805. <!--<![endif]-->
  806. </object>
  807. </span>
  808. OET;
  809. }
  810. public function get_supported_extensions() {
  811. return array('ra', 'ram', 'rm', 'rv');
  812. }
  813. public function get_rank() {
  814. return 40;
  815. }
  816. }
  817. /**
  818. * Media player for Flash SWF files.
  819. *
  820. * This player contains additional security restriction: it will only be used
  821. * if you add option core_media_player_swf::ALLOW = true.
  822. *
  823. * Code should only set this option if it has verified that the data was
  824. * embedded by a trusted user (e.g. in trust text).
  825. *
  826. * @copyright 2011 The Open University
  827. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  828. */
  829. class core_media_player_swf extends core_media_player {
  830. public function embed($urls, $name, $width, $height, $options) {
  831. self::pick_video_size($width, $height);
  832. $firsturl = reset($urls);
  833. $url = $firsturl->out(true);
  834. $fallback = core_media_player::PLACEHOLDER;
  835. $output = <<<OET
  836. <span class="mediaplugin mediaplugin_swf">
  837. <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$width" height="$height">
  838. <param name="movie" value="$url" />
  839. <param name="autoplay" value="true" />
  840. <param name="loop" value="true" />
  841. <param name="controller" value="true" />
  842. <param name="scale" value="aspect" />
  843. <param name="base" value="." />
  844. <param name="allowscriptaccess" value="never" />
  845. <!--[if !IE]>-->
  846. <object type="application/x-shockwave-flash" data="$url" width="$width" height="$height">
  847. <param name="controller" value="true" />
  848. <param name="autoplay" value="true" />
  849. <param name="loop" value="true" />
  850. <param name="scale" value="aspect" />
  851. <param name="base" value="." />
  852. <param name="allowscriptaccess" value="never" />
  853. <!--<![endif]-->
  854. $fallback
  855. <!--[if !IE]>-->
  856. </object>
  857. <!--<![endif]-->
  858. </object>
  859. </span>
  860. OET;
  861. return $output;
  862. }
  863. public function get_supported_extensions() {
  864. return array('swf');
  865. }
  866. public function list_supported_urls(array $urls, array $options = array()) {
  867. // Not supported unless the creator is trusted.
  868. if (empty($options[core_media::OPTION_TRUSTED])) {
  869. return array();
  870. }
  871. return parent::list_supported_urls($urls, $options);
  872. }
  873. public function get_rank() {
  874. return 30;
  875. }
  876. }
  877. /**
  878. * Player that creates HTML5 <video> tag.
  879. *
  880. * @copyright 2011 The Open University
  881. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  882. */
  883. class core_media_player_html5video extends core_media_player {
  884. public function embed($urls, $name, $width, $height, $options) {
  885. // Special handling to make videos play on Android devices pre 2.3.
  886. // Note: I tested and 2.3.3 (in emulator) works without, is 533.1 webkit.
  887. $oldandroid = check_browser_version('WebKit Android') &&
  888. !check_browser_version('WebKit Android', '533.1');
  889. // Build array of source tags.
  890. $sources = array();
  891. foreach ($urls as $url) {
  892. $mimetype = core_media::get_mimetype($url);
  893. $source = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
  894. if ($mimetype === 'video/mp4') {
  895. if ($oldandroid) {
  896. // Old Android fails if you specify the type param.
  897. $source = html_writer::tag('source', '', array('src' => $url));
  898. }
  899. // Better add m4v as first source, it might be a bit more
  900. // compatible with problematic browsers.
  901. array_unshift($sources, $source);
  902. } else {
  903. $sources[] = $source;
  904. }
  905. }
  906. $sources = implode("\n", $sources);
  907. $title = s($this->get_name($name, $urls));
  908. if (!$width) {
  909. // No width specified, use system default.
  910. $width = CORE_MEDIA_VIDEO_WIDTH;
  911. }
  912. if (!$height) {
  913. // Let browser choose height automatically.
  914. $size = "width=\"$width\"";
  915. } else {
  916. $size = "width=\"$width\" height=\"$height\"";
  917. }
  918. $sillyscript = '';
  919. $idtag = '';
  920. if ($oldandroid) {
  921. // Old Android does not support 'controls' option.
  922. $id = 'core_media_html5v_' . md5(time() . '_' . rand());
  923. $idtag = 'id="' . $id . '"';
  924. $sillyscript = <<<OET
  925. <script type="text/javascript">
  926. document.getElementById('$id').addEventListener('click', function() {
  927. this.play();
  928. }, false);
  929. </script>
  930. OET;
  931. }
  932. $fallback = core_media_player::PLACEHOLDER;
  933. return <<<OET
  934. <span class="mediaplugin mediaplugin_html5video">
  935. <video $idtag controls="true" $size preload="metadata" title="$title">
  936. $sources
  937. $fallback
  938. </video>
  939. $sillyscript
  940. </span>
  941. OET;
  942. }
  943. public function get_supported_extensions() {
  944. return array('m4v', 'webm', 'ogv', 'mp4');
  945. }
  946. public function list_supported_urls(array $urls, array $options = array()) {
  947. $extensions = $this->get_supported_extensions();
  948. $result = array();
  949. foreach ($urls as $url) {
  950. $ext = core_media::get_extension($url);
  951. if (in_array($ext, $extensions)) {
  952. // Unfortunately html5 video does not handle fallback properly.
  953. // https://www.w3.org/Bugs/Public/show_bug.cgi?id=10975
  954. // That means we need to do browser detect and not use html5 on
  955. // browsers which do not support the given type, otherwise users
  956. // will not even see the fallback link.
  957. // Based on http://en.wikipedia.org/wiki/HTML5_video#Table - this
  958. // is a simplified version, does not take into account old browser
  959. // versions or manual plugins.
  960. if ($ext === 'ogv' || $ext === 'webm') {
  961. // Formats .ogv and .webm are not supported in IE or Safari.
  962. if (check_browser_version('MSIE') || check_browser_version('Safari')) {
  963. continue;
  964. }
  965. } else {
  966. // Formats .m4v and .mp4 are not supported in Firefox or Opera.
  967. if (check_browser_version('Firefox') || check_browser_version('Opera')) {
  968. continue;
  969. }
  970. }
  971. $result[] = $url;
  972. }
  973. }
  974. return $result;
  975. }
  976. public function get_rank() {
  977. return 20;
  978. }
  979. }
  980. /**
  981. * Player that creates HTML5 <audio> tag.
  982. *
  983. * @copyright 2011 The Open University
  984. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  985. */
  986. class core_media_player_html5audio extends core_media_player {
  987. public function embed($urls, $name, $width, $height, $options) {
  988. // Build array of source tags.
  989. $sources = array();
  990. foreach ($urls as $url) {
  991. $mimetype = core_media::get_mimetype($url);
  992. $sources[] = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
  993. }
  994. $sources = implode("\n", $sources);
  995. $title = s($this->get_name($name, $urls));
  996. // Default to not specify size (so it can be changed in css).
  997. $size = '';
  998. if ($width) {
  999. $size = 'width="' . $width . '"';
  1000. }
  1001. $fallback = core_media_player::PLACEHOLDER;
  1002. return <<<OET
  1003. <audio controls="true" $size class="mediaplugin mediaplugin_html5audio" preload="no" title="$title">
  1004. $sources
  1005. $fallback
  1006. </audio>
  1007. OET;
  1008. }
  1009. public function get_supported_extensions() {
  1010. return array('ogg', 'oga', 'aac', 'm4a', 'mp3');
  1011. }
  1012. public function list_supported_urls(array $urls, array $options = array()) {
  1013. $extensions = $this->get_supported_extensions();
  1014. $result = array();
  1015. foreach ($urls as $url) {
  1016. $ext = core_media::get_extension($url);
  1017. if (in_array($ext, $extensions)) {
  1018. if ($ext === 'ogg' || $ext === 'oga') {
  1019. // Formats .ogg and .oga are not supported in IE or Safari.
  1020. if (check_browser_version('MSIE') || check_browser_version('Safari')) {
  1021. continue;
  1022. }
  1023. } else {
  1024. // Formats .aac, .mp3, and .m4a are not supported in Firefox or Opera.
  1025. if (check_browser_version('Firefox') || check_browser_version('Opera')) {
  1026. continue;
  1027. }
  1028. }
  1029. // Old Android versions (pre 2.3.3) 'support' audio tag but no codecs.
  1030. if (check_browser_version('WebKit Android') &&
  1031. !check_browser_version('WebKit Android', '533.1')) {
  1032. continue;
  1033. }
  1034. $result[] = $url;
  1035. }
  1036. }
  1037. return $result;
  1038. }
  1039. public function get_rank() {
  1040. return 10;
  1041. }
  1042. }
  1043. /**
  1044. * Special media player class that just puts a link.
  1045. *
  1046. * Always enabled, used as the last fallback.
  1047. *
  1048. * @copyright 2011 The Open University
  1049. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1050. */
  1051. class core_media_player_link extends core_media_player {
  1052. public function embed($urls, $name, $width, $height, $options) {
  1053. // If link is turned off, return empty.
  1054. if (!empty($options[core_media::OPTION_NO_LINK])) {
  1055. return '';
  1056. }
  1057. // Build up link content.
  1058. $output = '';
  1059. foreach ($urls as $url) {
  1060. $title = core_media::get_filename($url);
  1061. $printlink = html_writer::link($url, $title, array('class' => 'mediafallbacklink'));
  1062. if ($output) {
  1063. // Where there are multiple available formats, there are fallback links
  1064. // for all formats, separated by /.
  1065. $output .= ' / ';
  1066. }
  1067. $output .= $printlink;
  1068. }
  1069. return $output;
  1070. }
  1071. public function list_supported_urls(array $urls, array $options = array()) {
  1072. // Supports all URLs.
  1073. return $urls;
  1074. }
  1075. public function is_enabled() {
  1076. // Cannot be disabled.
  1077. return true;
  1078. }
  1079. public function get_rank() {
  1080. return 0;
  1081. }
  1082. }