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/** 18 * This file is serving optimised JS and WASM for ogv.js. 19 * 20 * @package media_videojs 21 * @copyright 2021 Huong Nguyen <huongnv13@gmail.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25// Disable moodle specific debug messages and any errors in output, 26// comment out when debugging or better look into error log! 27define('NO_DEBUG_DISPLAY', true); 28 29// We need just the values from config.php and minlib.php. 30define('ABORT_AFTER_CONFIG', true); 31require_once('../../../config.php'); // This stops immediately at the beginning of lib/setup.php. 32require_once($CFG->dirroot . '/lib/jslib.php'); 33require_once($CFG->dirroot . '/lib/wasmlib.php'); 34 35$slashargument = min_get_slash_argument(); 36if (!$slashargument) { 37 // The above call to min_get_slash_argument should always work. 38 die('Invalid request'); 39} 40 41$slashargument = ltrim($slashargument, '/'); 42if (substr_count($slashargument, '/') < 1) { 43 header('HTTP/1.0 404 not found'); 44 die('Slash argument must contain both a revision and a file path'); 45} 46 47// Get all the library files (js and wasm) of the OGV. 48$basepath = $CFG->dirroot . '/media/player/videojs/ogvjs/'; 49$jsfiles = []; 50$files = glob("{$basepath}*.{js,wasm}", GLOB_BRACE); 51foreach ($files as $file) { 52 $jsfiles[] = basename($file); 53} 54 55// Split into revision and module name. 56list($rev, $file) = explode('/', $slashargument, 2); 57$rev = min_clean_param($rev, 'INT'); 58$file = min_clean_param($file, 'SAFEPATH'); 59 60if (empty($jsfiles) || !in_array($file, $jsfiles)) { 61 // We can't find the requested file. 62 header('HTTP/1.0 404 not found'); 63 exit(0); 64} 65 66// Check if the requesting file is Javascript or Web Assembly. 67$iswasm = media_videojs_ogvloader_is_wasm_file($file); 68 69// Use the caching only for meaningful revision numbers which prevents future cache poisoning. 70if ($rev > 0 and $rev < (time() + 60 * 60)) { 71 // We are lazy loading a single file - so include the filename in the etag. 72 $etag = sha1($rev . '/' . $file); 73 $candidate = $CFG->localcachedir . '/ogvloader/' . $etag; 74 75 if (file_exists($candidate)) { 76 // Cache exist. 77 if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { 78 // We do not actually need to verify the etag value because our files 79 // never change in cache because we increment the rev parameter. 80 media_videojs_ogvloader_send_unmodified($iswasm, $candidate, $etag); 81 } 82 media_videojs_ogvloader_send_cached($iswasm, $candidate, $etag); 83 exit(0); 84 } else { 85 // Cache does not exist. Create one. 86 $filecontent = file_get_contents($basepath . $file); 87 if ($filecontent === false) { 88 error_log('Failed to load the library ' . $file); 89 $filecontent = "/* Failed to load library file {$file}. */\n"; 90 } 91 92 $filecontent = media_videojs_ogvloader_add_module_module_name_if_necessary($iswasm, $filecontent); 93 media_videojs_ogvloader_write_cache_file_content($iswasm, $candidate, $filecontent); 94 // Verify nothing failed in cache file creation. 95 clearstatcache(); 96 if (file_exists($candidate)) { 97 media_videojs_ogvloader_send_cached($iswasm, $candidate, $etag); 98 exit(0); 99 } 100 } 101} 102 103// If we've made it here then we're in "dev mode" where everything is lazy loaded. 104// So all files will be served one at a time. 105$filecontent = file_get_contents($basepath . $file); 106$filecontent = rtrim($filecontent); 107$filecontent = media_videojs_ogvloader_add_module_module_name_if_necessary($iswasm, $filecontent); 108media_videojs_ogvloader_send_uncached($iswasm, $filecontent); 109 110/** 111 * Check the given file is a Web Assembly file or not 112 * 113 * @param string $filename File name to check 114 * @return bool Whether the file is Web Assembly or not 115 */ 116function media_videojs_ogvloader_is_wasm_file(string $filename): bool { 117 $ext = pathinfo($filename, PATHINFO_EXTENSION); 118 return $ext == 'wasm'; 119} 120 121/** 122 * Add Moodle module name to the Javascript module if necessary 123 * 124 * @param bool $iswasm Whether the file is Web Assembly or not 125 * @param string $content File content 126 * @return string 127 */ 128function media_videojs_ogvloader_add_module_module_name_if_necessary(bool $iswasm, string $content): string { 129 if (!$iswasm && preg_match('/define\(\s*(\[|function)/', $content)) { 130 // If the JavaScript module has been defined without specifying a name then we'll 131 // add the Moodle module name now. 132 $replace = 'define(\'media_videojs/video-lazy\', '; 133 $search = 'define('; 134 // Replace only the first occurrence. 135 $content = implode($replace, explode($search, $content, 2)); 136 } 137 138 return $content; 139} 140 141/** 142 * Create cache file content 143 * 144 * @param bool $iswasm Whether the file is Web Assembly or not 145 * @param string $candidate Full file path to cache file 146 * @param string $filecontent File content 147 */ 148function media_videojs_ogvloader_write_cache_file_content(bool $iswasm, string $candidate, string $filecontent): void { 149 $iswasm ? wasm_write_cache_file_content($candidate, $filecontent) : js_write_cache_file_content($candidate, $filecontent); 150} 151 152/** 153 * Send file content with as much caching as possible 154 * 155 * @param bool $iswasm Whether the file is Web Assembly or not 156 * @param string $candidate Full file path to cache file 157 * @param string $etag Etag 158 */ 159function media_videojs_ogvloader_send_cached(bool $iswasm, string $candidate, string $etag): void { 160 $iswasm ? wasm_send_cached($candidate, $etag, 'ogvloader.php') : js_send_cached($candidate, $etag, 'ogvloader.php'); 161} 162 163/** 164 * Send file without any caching 165 * 166 * @param bool $iswasm Whether the file is Web Assembly or not 167 * @param string $ilecontent File content 168 */ 169function media_videojs_ogvloader_send_uncached(bool $iswasm, string $ilecontent): void { 170 $iswasm ? wasm_send_uncached($ilecontent, 'ogvloader.php') : js_send_uncached($ilecontent, 'ogvloader.php'); 171} 172 173/** 174 * Send the file not modified headers 175 * 176 * @param bool $iswasm Whether the file is Web Assembly or not 177 * @param int $candidate Full file path to cache file 178 * @param string $etag Etag 179 */ 180function media_videojs_ogvloader_send_unmodified(bool $iswasm, int $candidate, string $etag): void { 181 $iswasm ? wasm_send_unmodified(filemtime($candidate), $etag) : js_send_unmodified(filemtime($candidate), $etag); 182} 183