1/* AIO shared player fixes v12: preserve native media-ticket authorization; defer legacy probes until authorized; retain legacy subtitle behavior only for legacy players. */ 2;(function(){ 3 function cleanUrl(value){ 4 return String(value||"").trim().replace(/^['"]|['"]$/g,""); 5 } 6 function renditionLabelFromUrl(value){ 7 var clean=cleanUrl(value).split("?")[0].split("#")[0]; 8 var match=clean.match(/-(360p|480p|720p|1080p|1440p|2160p|4K)(?:\.[^.]+)?$/i); 9 if(!match)return ""; 10 var label=match[1]; 11 return label.toLowerCase()==="4k"?"4K":label.toLowerCase(); 12 } 13 function renditionRank(label){ 14 return {"360p":1,"480p":2,"720p":3,"1080p":4,"1440p":5,"2160p":6,"4K":6}[String(label||"")]||99; 15 } 16 function comparableUrl(value){ 17 var clean=cleanUrl(value); 18 if(!clean)return ""; 19 try{return new URL(clean,window.location.href).href}catch(_e){return clean} 20 } 21 function normalizedVariants(value){ 22 var seen={}; 23 return (Array.isArray(value)?value:[]).map(function(variant){ 24 var next=Object.assign({},variant||{}); 25 next.url=cleanUrl(next.url||next.src); 26 next.label=String(next.label||renditionLabelFromUrl(next.url)||"").trim(); 27 return next; 28 }).filter(function(variant){ 29 var key=comparableUrl(variant.url); 30 if(!key||seen[key])return false; 31 seen[key]=true; 32 return true; 33 }).sort(function(left,right){ 34 return renditionRank(left.label)-renditionRank(right.label); 35 }); 36 } 37 function mediaBaseName(url){ 38 var clean=cleanUrl(url).split("?")[0].split("#")[0]; 39 var file=(clean.split("/").pop()||"").trim(); 40 return file.replace(/\.[^.]+$/,"").replace(/-(?:360p|480p|720p|1080p|1440p|2160p|4K|raw)$/i,""); 41 } 42 function localAudioUrl(src){ 43 var raw=cleanUrl(src); 44 var base=mediaBaseName(raw); 45 if(!base)return ""; 46 if(raw.indexOf("/media/")===0)return "/media/"+base+"-audio.ogg"; 47 return raw.replace(/\/[^/?#]+(?=([?#]|$))/,"/"+base+"-audio.ogg"); 48 } 49 function isDerivedAudioUrl(url){ 50 var clean=cleanUrl(url).split("?")[0].split("#")[0]; 51 return /-audio\.ogg$/i.test(clean); 52 } 53 function isLocalMediaUrl(url){ 54 var raw=cleanUrl(url); 55 if(!raw)return false; 56 if(raw.indexOf("/media/")===0)return true; 57 try{ 58 var parsed=new URL(raw,window.location.href); 59 return parsed.origin===window.location.origin&&parsed.pathname.indexOf("/media/")===0; 60 }catch(_e){return false} 61 } 62 function isPermanentlyMissingResponse(response){ 63 return !!response&&(response.status===404||response.status===410); 64 } 65 function normalizeOptions(opts){ 66 var next=Object.assign({},opts||{}); 67 if(next.mode==="video"&&next.src){ 68 next.variants=normalizedVariants(next.variants); 69 var sourceUrl=comparableUrl(next.src); 70 var matchingVariant=next.variants.find(function(variant){return comparableUrl(variant.url)===sourceUrl}); 71 var actualSourceLabel=matchingVariant&&matchingVariant.label||renditionLabelFromUrl(next.src); 72 if(actualSourceLabel)next.resolution=actualSourceLabel; 73 var base=mediaBaseName(next.src); 74 if(base){ 75 if(!next.subtitleBase||/-(?:360p|480p|720p|1080p|1440p|2160p|4K|raw)$/i.test(next.subtitleBase))next.subtitleBase=base; 76 if(!next.audioSrc||isDerivedAudioUrl(next.audioSrc)){ 77 next.derivedAudioSrc=localAudioUrl(next.src)||next.audioSrc||""; 78 if(isDerivedAudioUrl(next.audioSrc))next.audioSrc=""; 79 } 80 } 81 } 82 return next; 83 } 84 function syncActualQualityLabel(player){ 85 if(!player||!player.media||!player.qualityDisplayEl)return; 86 var current=comparableUrl(player.media.currentSrc||player.media.src||player.opts&&player.opts.src); 87 var variants=normalizedVariants(player.variants||player.opts&&player.opts.variants); 88 var match=variants.find(function(variant){return comparableUrl(variant.url)===current}); 89 var label=match&&match.label||renditionLabelFromUrl(current)||player.__aioSelectedRendition&&player.__aioSelectedRendition.label||player.opts&&player.opts.resolution; 90 if(label)player.qualityDisplayEl.textContent=label; 91 } 92 function installRenditionFallback(player){ 93 if(!player||player.__aioRenditionFallbackInstalled||!player.media||typeof player.media.addEventListener!=="function")return; 94 player.__aioRenditionFallbackInstalled=true; 95 var variants=normalizedVariants(player.variants||player.opts&&player.opts.variants); 96 if(!variants.length)return; 97 var selected=variants.find(function(variant){ 98 return comparableUrl(variant.url)===comparableUrl(player.opts&&player.opts.src); 99 })||variants[variants.length-1]; 100 player.__aioSelectedRendition=selected; 101 function fallBackFrom(failed){ 102 var current=player.__aioSelectedRendition; 103 if(failed&&comparableUrl(current&¤t.url)!==comparableUrl(failed.url))return null; 104 var index=variants.findIndex(function(variant){ 105 return comparableUrl(variant.url)===comparableUrl(current&¤t.url); 106 }); 107 if(index<0)index=variants.length; 108 var fallback=index>0?variants[index-1]:null; 109 if(!fallback||!fallback.url)return null; 110 player.__aioSelectedRendition=fallback; 111 player.opts.src=fallback.url; 112 player.opts.resolution=fallback.label||renditionLabelFromUrl(fallback.url)||player.opts.resolution; 113 if(typeof player.setQuality==="function")player.setQuality(fallback.url,player.opts.resolution); 114 else{ 115 player.media.src=fallback.url; 116 if(typeof player.media.load==="function")player.media.load(); 117 } 118 syncActualQualityLabel(player); 119 return fallback; 120 } 121 function verifyLocalRendition(rendition){ 122 if(!rendition||!isLocalMediaUrl(rendition.url))return; 123 fetch(rendition.url,{method:"HEAD",cache:"no-store"}).then(function(response){ 124 if(!isPermanentlyMissingResponse(response))return; 125 var fallback=fallBackFrom(rendition); 126 if(fallback)verifyLocalRendition(fallback); 127 }).catch(function(){ 128 /* A network-level HEAD failure is inconclusive; native media errors still drive fallback. */ 129 }); 130 } 131 player.media.addEventListener("error",function(){ 132 fallBackFrom(player.__aioSelectedRendition); 133 }); 134 verifyLocalRendition(selected); 135 } 136 function parseCueTime(value){ 137 var m=String(value||"").trim().match(/(?:(\d{2,}):)?(\d{2}):(\d{2})[,.](\d{3})/); 138 if(!m)return NaN; 139 return (Number(m[1]||0)*3600)+(Number(m[2]||0)*60)+Number(m[3]||0)+Number(m[4]||0)/1000; 140 } 141 function parseSubtitleText(text){ 142 var blocks=String(text||"").replace(/^\uFEFF/,"").replace(/^WEBVTT[^\n]*(?
142:\n|$)/i,"").trim().split(/\n\s*\n/); 143 var cues=[]; 144 blocks.forEach(function(block){ 145 var lines=block.trim().split(/\r?\n/).filter(Boolean); 146 if(!lines.length)return; 147 var timeIndex=lines.findIndex(function(line){return line.indexOf("-->")>=0;}); 148 if(timeIndex<0)return; 149 var parts=lines[timeIndex].split("-->"); 150 var start=parseCueTime(parts[0]); 151 var end=parseCueTime(parts[1]); 152 if(!isFinite(start)||!isFinite(end)||end<=start)return; 153 var body=lines.slice(timeIndex+1).join("\n").replace(/<[^>]+>/g,"").trim(); 154 if(body)cues.push({start:start,end:end,text:body}); 155 }); 156 return cues; 157 } 158 async function fetchText(candidate){ 159 var url=typeof candidate==="string"?candidate:candidate&&candidate.url; 160 var headers=typeof candidate==="object"&&candidate?candidate.headers:{}; 161 var res=await fetch(url,{headers:headers||{},cache:"no-store"}); 162 if(!res.ok)throw new Error("subtitle not found"); 163 return await res.text(); 164 } 165 async function robustLoadSubtitles(lang){ 166 var base=this.opts&&this.opts.subtitleBase; 167 if(!base||!lang||lang==="off"||this._destroyed)return; 168 var generation=this._subtitleGeneration=(this._subtitleGeneration||0)+1; 169 this._subtitleLoadingLang=lang; 170 var enc=encodeURIComponent(base); 171 var path=this.opts.subtitlePath||"/media/"; 172 var version=this._subtitleVersion||Date.now().toString(36); 173 var candidates=[]; 174 try{ 175 if(typeof API!=="undefined"&&typeof publicAuthHeaders==="function"){ 176 var headers=publicAuthHeaders()||{}; 177 if(headers.Authorization)candidates.push({url:API+"/media/subtitles/"+enc+"?lang="+encodeURIComponent(lang),headers:headers}); 178 } 179 }catch(_e){} 180 var protectedUrl=typeof this._protectedSubtitleUrl==="function"?this._protectedSubtitleUrl(base+"."+lang+".srt"):""; 181 if(protectedUrl)candidates.push(protectedUrl); 182 else{ 183 var publicUrl=path+enc+"."+lang+".srt"; 184 publicUrl+=(publicUrl.indexOf("?")>=0?"&":"?")+"v="+encodeURIComponent(version); 185 candidates.push(publicUrl); 186 if(path.indexOf("/api/public/srt/")!==0){ 187 var vttUrl=path+enc+"."+lang+".vtt"; 188 vttUrl+=(vttUrl.indexOf("?")>=0?"&":"?")+"v="+encodeURIComponent(version); 189 candidates.push(vttUrl); 190 } 191 } 192 try{for(var i=0;i<candidates.length;i++){
193 try{ 194 var text=await fetchText(candidates[i]); 195 if(this._destroyed||generation!==this._subtitleGeneration)return; 196 var cues=parseSubtitleText(text); 197 if(!cues.length&&typeof this._parseSrt==="function")cues=this._parseSrt(text)||[]; 198 if(cues.length){ 199 this.subtitleCues=cues; 200 this.subtitleLang=lang; 201 this.subtitleOn=true; 202 if(typeof this._buildCcMenu==="function")this._buildCcMenu(); 203 if(typeof this._updateSubtitlePosition==="function")this._updateSubtitlePosition(); 204 if(typeof this._updateSubtitle==="function")this._updateSubtitle(); 205 return; 206 } 207 }catch(_err){if(this._destroyed||generation!==this._subtitleGeneration)return;} 208 } 209 if(this._destroyed||generation!==this._subtitleGeneration)return; 210 this.subtitleCues=null; 211 this.subtitleOn=false; 212 if(typeof this._hideSubtitle==="function")this._hideSubtitle(); 213 if(typeof this._buildCcMenu==="function")this._buildCcMenu(); 214 }finally{if(generation===this._subtitleGeneration)this._subtitleLoadingLang=null;} 215 } 216 function sizeWaveCanvas(canvas, player){ 217 if(!canvas)return; 218 var dpr=window.devicePixelRatio||1; 219 var rect=typeof canvas.getBoundingClientRect==="function"?canvas.getBoundingClientRect():null; 220 var fallbackH=player&&player.mode==="audio"?64:player&&player.mode==="compact"?32:36; 221 var cssW=Math.max(1,Math.floor(((rect&&rect.width)||canvas.offsetWidth||200)*dpr)); 222 var cssH=Math.max(1,Math.floor(((rect&&rect.height)||canvas.offsetHeight||fallbackH)*dpr)); 223 if(Math.abs(canvas.width-cssW)>2||Math.abs(canvas.height-cssH)>2){ 224 canvas.width=cssW; 225 canvas.height=cssH; 226 } 227 } 228 function patchWaveform(player){ 229 if(!player||player.__aioWavePatched)return; 230 player.__aioWavePatched=true; 231 var origDecode=typeof player._decodeWaveformBuffer==="function"?player._decodeWaveformBuffer.bind(player):null; 232 if(origDecode){ 233 player._decodeWaveformBuffer=function(buffer){ 234 try{this.__aioWaveBuffer=buffer&&buffer.slice?buffer.slice(0):buffer}catch(_e){} 235 return origDecode(buffer); 236 }; 237 } 238 var origDraw=typeof player._drawWave==="function"?player._drawWave.bind(player):null; 239 if(origDraw){ 240 player._drawWave=function(progress){ 241 sizeWaveCanvas(this.waveCanvas,this); 242 return origDraw(progress); 243 }; 244 } 245 var origLoad=typeof player._loadWaveform==="function"?player._loadWaveform.bind(player):null; 246 if(origLoad){ 247 player._loadWaveform=function(){ 248 if(this._destroyed || (this.opts&&this.opts.deferMediaAccess&&!this._mediaAccessReady))return; 249 var videoSrc=(this.opts&&this.opts.src)||this.media&&this.media.src||""; 250 var audioSrc=(this.opts&&this.opts.audioSrc)||""; 251 if(this.mode==="video"&&isDerivedAudioUrl(audioSrc)&&isLocalMediaUrl(audioSrc)){ 252 this.opts.audioSrc=""; 253 audioSrc=""; 254 } 255 if(this.mode==="video"&&!audioSrc&&isLocalMediaUrl(videoSrc)){ 256 this._waveformFallback=videoSrc; 257 if(typeof this._fetchPartialWaveform==="function")return this._fetchPartialWaveform(videoSrc); 258 if(typeof this._generateSyntheticWaveform==="function")return this._generateSyntheticWaveform(); 259 return; 260 } 261 return origLoad(); 262 }; 263 } 264 if(window.ResizeObserver&&player.waveCanvas){ 265 var timer=null; 266 var resize=new ResizeObserver(function(){ 267 clearTimeout(timer); 268 timer=setTimeout(function(){ 269 if(player._destroyed)return; 270 try{ 271 if(player.__aioWaveBuffer&&origDecode)origDecode(player.__aioWaveBuffer.slice(0)); 272 else if(typeof player._generateSyntheticWaveform==="function")player._generateSyntheticWaveform(); 273 }catch(_e){ 274 try{player._drawWave(player.media&&player.media.duration?player.media.currentTime/player.media.duration:0)}catch(_e2){} 275 } 276 },120); 277 }); 278 resize.observe(player.waveCanvas); 279 var oldDestroy=typeof player.destroy==="function"?player.destroy.bind(player):null; 280 if(oldDestroy){ 281 player.destroy=function(){ 282 try{resize.disconnect()}catch(_e){} 283 return oldDestroy(); 284 }; 285 } 286 } 287 } 288 function patchPlayer(player, opts){ 289 if(!player||player.__aioPlayerFixes)return player; 290 player.__aioPlayerFixes=true; 291 player.opts=Object.assign({},player.opts||{},opts||{}); 292 // v18 owns the native media-cookie exchange for subtitle paths. Never replace it. 293 var ticketAware=!!(player.opts.deferMediaAccess&&player._mediaTicketReady); 294 if(!ticketAware)player.loadSubtitles=robustLoadSubtitles; 295 patchWaveform(player); 296 if(!ticketAware&&typeof player.getPlaybackSnapshot!=="function")installRenditionFallback(player); 297 syncActualQualityLabel(player); 298 if(player.media&&typeof player.media.addEventListener==="function"){ 299 player.media.addEventListener("loadedmetadata",function(){syncActualQualityLabel(player)}); 300 player.media.addEventListener("canplay",function(){syncActualQualityLabel(player)}); 301 } 302 function startAuxiliaryMedia(){ 303 if(player._destroyed || (ticketAware&&!player._mediaAccessReady))return;
304 try{ 305 syncActualQualityLabel(player); 306 if(player.opts&&player.opts.subtitleBase&&(!player.subtitleLangs||!player.subtitleLangs.length)&&typeof player._loadSubtitleLangs==="function")player._loadSubtitleLangs(); 307 if(typeof player._loadWaveform==="function")player._loadWaveform(); 308 }catch(_e){} 309 } 310 if(ticketAware)player._mediaTicketReady.then(function(ok){if(ok)startAuxiliaryMedia();}).catch(function(){}); 311 else setTimeout(startAuxiliaryMedia,180); 312 return player; 313 } 314 function install(){ 315 var original=window.initMediaPlayer; 316 if(typeof original!=="function"||original.__aioFixWrapped)return; 317 function wrappedInitMediaPlayer(container, opts){ 318 var normalized=normalizeOptions(opts||{}); 319 var variants=normalized.variants||[]; 320 var player=original(container,normalized); 321 if(!player)return player; 322 player.variants=variants; 323 return patchPlayer(player,normalized); 324 } 325 wrappedInitMediaPlayer.__aioFixWrapped=true; 326 window.initMediaPlayer=wrappedInitMediaPlayer; 327 } 328 if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",install,{once:true}); 329 install(); 330})(); 331 332/* At most one live streaming episode player per course accordion. */ 333;(function(){ 334 function install(){ 335 var toggle=window.toggleEpisode,init=window.initStreamingEpisodePlayer; 336 if(typeof toggle!=="function"||typeof init!=="function"||typeof window.destroyStreamingEpisodePlayer!=="function"||toggle.__aioSingleEpisode)return; 337 function clearClosedPlayer(epId){ 338 // v18 destroy replaces the container with a clone. Keep its data-* inputs 339 // for reopening, but discard the cloned empty media elements and controls. 340 var container=document.getElementById("mp-"+epId); 341 if(container)container.innerHTML=""; 342 } 343 function closeOthers(current){ 344 var scope=current.closest(".sp-episodes")||document; 345 scope.querySelectorAll(".sp-ep-content[id]").forEach(function(content){ 346 if(content===current)return; 347 var player=typeof window.getMediaPlayer==="function"?window.getMediaPlayer("mp-"+content.id):null; 348 if(!content.classList.contains("open")&&!player)return; 349 // Existing teardown flushes this principal's progress before pausing, 350 // unloading media, removing timers and releasing its media-ticket paths. 351 window.destroyStreamingEpisodePlayer(content.id); 352 clearClosedPlayer(content.id); 353 content.classList.remove("open"); 354 content.style.display="none"; 355 var header=content.previousElementSibling; 356 if(header){header.setAttribute("aria-expanded","false");var svg=header.querySelector("svg");if(svg)svg.style.transform="";} 357 }); 358 } 359 function singleEpisodeToggle(epId){ 360 var content=document.getElementById(epId); 361 var wasOpen=content&&content.classList.contains("open"); 362 if(content&&!wasOpen)closeOthers(content); 363 var result=toggle.apply(this,arguments); 364 if(wasOpen)clearClosedPlayer(epId); 365 return result; 366 } 367 function singleEpisodeInit(epId){ 368 var content=document.getElementById(epId),container=document.getElementById("mp-"+epId); 369 if(!content||!container||!container.dataset.mpMode)return init.apply(this,arguments); 370 closeOthers(content); 371 var current=typeof window.getMediaPlayer==="function"?window.getMediaPlayer(container.id):null; 372 if(current&&!current._destroyed&¤t.container===container)return current; 373 return init.apply(this,arguments); 374 } 375 singleEpisodeToggle.__aioSingleEpisode=true; 376 window.toggleEpisode=singleEpisodeToggle; 377 window.initStreamingEpisodePlayer=singleEpisodeInit; 378 } 379 install(); 380 if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",install,{once:true}); 381})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.