From da64aae2676a0e5132323cef5378072e025c6ede Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sun, 13 Sep 2026 11:21:06 +0800 Subject: [PATCH] refactor(multiple-subtitles): [PKG-MULTI-SUB-04] type runtime modules and vendor boundaries --- .../artplayer-plugin-multiple-subtitles.js | 2 +- ...player-plugin-multiple-subtitles.legacy.js | 2 +- .../artplayer-plugin-multiple-subtitles.mjs | 7 +- package.json | 2 +- .../.npmignore | 3 +- .../ARCHITECTURE.md | 19 +- .../artplayer-plugin-multiple-subtitles.js | 2 +- ...player-plugin-multiple-subtitles.legacy.js | 2 +- .../artplayer-plugin-multiple-subtitles.mjs | 7 +- .../src/{index.js => index.ts} | 17 +- .../src/{lifetime.js => lifetime.ts} | 14 +- .../src/merge.js | 30 - .../src/merge.ts | 33 + .../src/parser.d.ts | 56 + .../src/{render.js => render.ts} | 24 +- .../src/{request.js => request.ts} | 17 +- .../src/types.ts | 35 + .../tsconfig.json | 12 + .../multiple-subtitles-runtime-types.json | 6655 +++++++++++++++++ ...26-09-13-PKG-MULTI-SUB-04-runtime-types.md | 45 + refactor/plan.md | 12 +- refactor/progress.md | 15 + refactor/risk-table.md | 1 + refactor/risks.json | 17 + .../multiple-subtitles-runtime-types.test.mjs | 37 + refactor/tasks.json | 24 +- test/multiple-subtitles-failures.test.js | 6 + test/types/multiple-subtitles-runtime.ts | 65 + 28 files changed, 7075 insertions(+), 86 deletions(-) rename packages/artplayer-plugin-multiple-subtitles/src/{index.js => index.ts} (65%) rename packages/artplayer-plugin-multiple-subtitles/src/{lifetime.js => lifetime.ts} (73%) delete mode 100644 packages/artplayer-plugin-multiple-subtitles/src/merge.js create mode 100644 packages/artplayer-plugin-multiple-subtitles/src/merge.ts create mode 100644 packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts rename packages/artplayer-plugin-multiple-subtitles/src/{render.js => render.ts} (69%) rename packages/artplayer-plugin-multiple-subtitles/src/{request.js => request.ts} (53%) create mode 100644 packages/artplayer-plugin-multiple-subtitles/src/types.ts create mode 100644 packages/artplayer-plugin-multiple-subtitles/tsconfig.json create mode 100644 refactor/baselines/multiple-subtitles-runtime-types.json create mode 100644 refactor/changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md create mode 100644 refactor/scripts/multiple-subtitles-runtime-types.test.mjs create mode 100644 test/types/multiple-subtitles-runtime.ts diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.js b/docs/compiled/artplayer-plugin-multiple-subtitles.js index 83ae8cdbb..6b450f1dc 100644 --- a/docs/compiled/artplayer-plugin-multiple-subtitles.js +++ b/docs/compiled/artplayer-plugin-multiple-subtitles.js @@ -55,4 +55,4 @@ * * d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},t=function(t){t||(t={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=t,this.parse=function(s,a){s=s.replace(/\0/g,"\ufffd");var r=Date.now(),o=0,l=s.split(/\r\n|\r|\n/),c=!1,f=[],u=[],d=[];function p(e,t){d.push({message:e,line:o+1,col:t})}var m=l[o],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&p('No valid signature. (File needs to start with "WEBVTT".)'),o++;""!=l[o]&&void 0!=l[o];){if(p("No blank line after the signature."),-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}for(;void 0!=l[o];){for(var x;!c&&""==l[o];)o++;if(!c&&void 0==l[o])break;x=Object.assign({},e,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var T=!0;if(-1==l[o].indexOf("--\x3e")){if(x.id=l[o],/^NOTE($|[ \t])/.test(x.id)){for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&p("Cannot have timestamp in a comment."),o++;continue}if(/^STYLE($|[ \t])/.test(x.id)){var y=[],w=!1;for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&(p("Cannot have timestamp in a style block."),w=!0),y.push(l[o]),o++;if(u.length){p("Style blocks cannot appear after the first cue.");continue}w||f.push(y.join("\n"));continue}if(""==l[++o]||void 0==l[o]){p("Cue identifier cannot be standalone.");continue}if(-1==l[o].indexOf("--\x3e")){T=!1,p("Cue identifier needs to be followed by timestamp.");continue}}c=!1;var O=new n(l[o],p),A=0;if(u.length>0&&(A=u[u.length-1].startTime),!T||O.parse(x,A)){for(o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){p("Blank line missing before cue."),c=!0;break}""!=x.text&&(x.text+="\n"),x.text+=l[o],o++}var L=new i(x.text,p,a,t);x.tree=L.parse(x.startTime,x.endTime),u.push(x)}else for(x=null,o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}}return u.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[s])if(s++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[s]){if(":"!=e[s])return void a("No seconds found or minutes is greater than 59.");if(s++,2!=(i=o(/\d/)).length)return void a("Must be exactly two digits.")}else{if(2!=t.length)return void a("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[s])if(s++,3==(r=o(/\d/)).length)if(parseInt(n,10)>59)a("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(r,10)/1e3;a("You cannot have more than 59 seconds.")}else a("Milliseconds must be given in three digits.");else a('No decimal separator (".") found.')}else a("Must be exactly two digits.");else a("No time unit separator found.");else a("Timestamp must start with a character in the range 0-9.");else a("No timestamp found.")}this.parse=function(t,o){if(r(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[s]){if(s++,i.test(e[s])&&a("'--\x3e' not separated from timestamp by whitespace."),r(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&a("End timestamp is not greater than start timestamp."),i.test(e[s]),r(n),function(e,t){for(var i=e.split(n),s=[],r=0;r100)){a("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){a("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){a("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){a("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){a("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){a("Size must be a percentage.");continue}if(parseInt(c,10)>100){a("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){a("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){a("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){a("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else a("Invalid setting.")}}(e.substring(s),t),!0}else a("No valid timestamp separator found.");else a("No valid timestamp separator found.");else a("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[s])return t;a("Timestamp must not have trailing characters.")}},i=function(e,t,i,s){this.entities=s;var a=this,r=(e=e,0),o=function(e){"metadata"!=i&&t(e,r+1)};function l(){for(var t="data",n="",i="",l=[];void 0!=e[r-1]||0==r;){var c=e[r];if("data"==t)if("&"==c)i=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return o("Incorrect escape."),(e=i.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):a.entities[i]?n+=a.entities[i]:n+=i,["text",n]}if("&"==c)o("Incorrect escape."),n+=i,i=c;else if(/[a-z#0-9]/i.test(c))i+=c;else if(";"==c){let e;(e=i.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):a.entities[i+c]?n+=a.entities[i+c]:(e=Object.keys(s).find(e=>i.startsWith(e)))?n+=a.entities[e]+i.slice(e.length)+c:(o("Incorrect escape."),n+=i+";"),t="data"}else o("Incorrect escape."),n+=i+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)i=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)i&&l.push(i),i="",t="start tag annotation";else if("\n"==c)i&&l.push(i),i=c,t="start tag annotation";else if("."==c)i&&l.push(i),i="";else{if(">"==c||void 0==c)return">"==c&&r++,i&&l.push(i),["start tag",n,l,""];i+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,l,i=i.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];i+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["timestamp",n];n+=c}else o("Never happens.");r++}}this.parse=function(t,s){var a={children:[]},c=a,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function d(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[r];){var p=l();if("text"==p[0])c.children.push({type:"text",value:p[1],parent:c});else if("start tag"==p[0]){"chapters"==i&&o("Start tags not allowed in chapter title text.");var m=p[1];"v"!=m&&"lang"!=m&&""!=p[3]&&o("Only and can have an annotation."),"c"==m||"i"==m||"b"==m||"u"==m||"ruby"==m||"rt"==m&&"ruby"==c.name?u(p):"v"==m?(d("v")&&o(" cannot be nested inside itself."),u(p),c.value=p[3],p[3]||o(" requires an annotation.")):"lang"==m?(u(p),c.value=p[3]):o("Incorrect start tag.")}else if("end tag"==p[0])"chapters"==i&&o("End tags not allowed in chapter title text."),p[1]==c.name?c=c.parent:"ruby"==p[1]&&"rt"==c.name?c=c.parent.parent:o("Incorrect end tag.");else if("timestamp"==p[0]){"chapters"==i&&o("Timestamp not allowed in chapter title text.");var g=new n(p[1],o).parseTimestamp();void 0!=g&&((g<=t||g>=s)&&o("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=g&&o("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:g,parent:c}),f.push(g))}}for(;c.parent;)"v"!=c.name&&o("Required end tag missing."),c=c.parent;return function e(t){const n={...t};return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(a)}},s=function(){function t(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,s=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),s=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+s).padStart(2,"0")+"."+t}function n(e){for(var i="",s=0;s/g,">");else if("object"==a.type){if(i+="<"+a.name,a.classes)for(var r=0;r"}else"timestamp"==a.type?i+="<"+t(a.value)+">":i+="<"+a.value+">"}return i}function i(i){return(void 0!==i.id?i.id+"\n":"")+t(i.startTime)+" --\x3e "+t(i.endTime)+function(t){var n="";const i=Object.keys(e).filter(n=>t[n]!==e[n]);return i.includes("direction")&&(n+=" vertical:"+t.direction),i.includes("alignment")&&(n+=" align:"+t.alignment),i.includes("size")&&(n+=" size:"+t.size+"%"),(i.includes("lineAlign")||i.includes("linePosition"))&&(n+=" line:"+t.linePosition+(t.snapToLines?"":"%")+(t.lineAlign&&t.lineAlign!=e.lineAlign?","+t.lineAlign:"")),(i.includes("textPosition")||i.includes("positionAlign"))&&(n+=" position:"+t.textPosition+"%"+(t.positionAlign&&t.positionAlign!==e.positionAlign?","+t.positionAlign:"")),n}(i)+"\n"+n(i.tree.children)+"\n\n"}function s(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var n="WEBVTT\n\n";if(t)for(var a=0;a{if(t.closed)return;const a=URL.createObjectURL(new Blob([s],{type:"text/vtt"})),r=function(e){let n=!1,i=()=>{};const s={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(s.free),s}(a);if(t.closed)return;const o=i;let l,c,f=!1;i=r;try{if(l=e.option.subtitle,c=l.escape,t.closed||i!==r)return;l.escape=!1,f=!0;const s={...l,url:a,type:"vtt",onVttLoad:n};if(t.closed||i!==r)return;const o=e.subtitle.init(s);Promise.resolve(o).catch(e=>{i===r&&(i=null,r.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(u){throw i===r&&(i=o,f&&!1===l.escape&&(l.escape=c)),r.free(),u}finally{o!==i&&o?.free(),r!==i&&r.free()}}}return function({subtitles:e=[]}){return async n=>{const{unescape:i,getExt:r,srtToVtt:o,assToVtt:l}=n.constructor.utils,c=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const s=new Promise(e=>{i=e});function a(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const r={get closed(){return t},own:e=>(t?a(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,s]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)a(t)}};if(!t){r.own(()=>e.off("destroy",r.dispose));try{e.on("destroy",r.dispose)}catch(o){throw r.dispose(),o}}return r}(n),f=a(n,c,i);let u=[];function d(e){c.closed||f(function(e){const t=[];for(const n of e)for(const e of n.cues)t.push({...e,tree:{...e.tree,children:e.tree.children.map(e=>({...e,value:`
${e.value}
`}))}});return(new s).serialize(t)}(e))}const p={name:"multipleSubtitles",tracks(e=[]){c.closed||d(e.map(e=>u.find(t=>t.name===e)))},reset(){d(u)}};try{if(c.closed)return p;const n=await Promise.all(e.map(e=>async function(e,{getExt:t,srtToVtt:n,assToVtt:i},s){if(s.closed)return;const a="function"===typeof AbortController?new AbortController:null,r=s.own(()=>a?.abort());try{const r=e.url;if(s.closed)return;const o=await s.wait(a?fetch(r,{signal:a.signal}):fetch(r));if(s.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=await s.wait(o.arrayBuffer());if(s.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw a?.abort(),o}finally{r()}}(e,{getExt:r,srtToVtt:o,assToVtt:l},c)));return c.closed||(u=function(e,n){const i=new t;return e.map((e,t)=>{const s=i.parse(e,"metadata");return s.url=n[t].url,s.name=n[t].name,s})}(n,e),d(u)),p}catch(m){throw c.dispose(),m}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},t=function(t){t||(t={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=t,this.parse=function(s,a){s=s.replace(/\0/g,"\ufffd");var r=Date.now(),o=0,l=s.split(/\r\n|\r|\n/),c=!1,f=[],u=[],d=[];function p(e,t){d.push({message:e,line:o+1,col:t})}var m=l[o],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&p('No valid signature. (File needs to start with "WEBVTT".)'),o++;""!=l[o]&&void 0!=l[o];){if(p("No blank line after the signature."),-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}for(;void 0!=l[o];){for(var x;!c&&""==l[o];)o++;if(!c&&void 0==l[o])break;x=Object.assign({},e,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var T=!0;if(-1==l[o].indexOf("--\x3e")){if(x.id=l[o],/^NOTE($|[ \t])/.test(x.id)){for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&p("Cannot have timestamp in a comment."),o++;continue}if(/^STYLE($|[ \t])/.test(x.id)){var y=[],w=!1;for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&(p("Cannot have timestamp in a style block."),w=!0),y.push(l[o]),o++;if(u.length){p("Style blocks cannot appear after the first cue.");continue}w||f.push(y.join("\n"));continue}if(""==l[++o]||void 0==l[o]){p("Cue identifier cannot be standalone.");continue}if(-1==l[o].indexOf("--\x3e")){T=!1,p("Cue identifier needs to be followed by timestamp.");continue}}c=!1;var O=new n(l[o],p),A=0;if(u.length>0&&(A=u[u.length-1].startTime),!T||O.parse(x,A)){for(o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){p("Blank line missing before cue."),c=!0;break}""!=x.text&&(x.text+="\n"),x.text+=l[o],o++}var L=new i(x.text,p,a,t);x.tree=L.parse(x.startTime,x.endTime),u.push(x)}else for(x=null,o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}}return u.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[s])if(s++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[s]){if(":"!=e[s])return void a("No seconds found or minutes is greater than 59.");if(s++,2!=(i=o(/\d/)).length)return void a("Must be exactly two digits.")}else{if(2!=t.length)return void a("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[s])if(s++,3==(r=o(/\d/)).length)if(parseInt(n,10)>59)a("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(r,10)/1e3;a("You cannot have more than 59 seconds.")}else a("Milliseconds must be given in three digits.");else a('No decimal separator (".") found.')}else a("Must be exactly two digits.");else a("No time unit separator found.");else a("Timestamp must start with a character in the range 0-9.");else a("No timestamp found.")}this.parse=function(t,o){if(r(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[s]){if(s++,i.test(e[s])&&a("'--\x3e' not separated from timestamp by whitespace."),r(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&a("End timestamp is not greater than start timestamp."),i.test(e[s]),r(n),function(e,t){for(var i=e.split(n),s=[],r=0;r100)){a("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){a("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){a("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){a("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){a("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){a("Size must be a percentage.");continue}if(parseInt(c,10)>100){a("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){a("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){a("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){a("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else a("Invalid setting.")}}(e.substring(s),t),!0}else a("No valid timestamp separator found.");else a("No valid timestamp separator found.");else a("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[s])return t;a("Timestamp must not have trailing characters.")}},i=function(e,t,i,s){this.entities=s;var a=this,r=(e=e,0),o=function(e){"metadata"!=i&&t(e,r+1)};function l(){for(var t="data",n="",i="",l=[];void 0!=e[r-1]||0==r;){var c=e[r];if("data"==t)if("&"==c)i=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return o("Incorrect escape."),(e=i.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):a.entities[i]?n+=a.entities[i]:n+=i,["text",n]}if("&"==c)o("Incorrect escape."),n+=i,i=c;else if(/[a-z#0-9]/i.test(c))i+=c;else if(";"==c){let e;(e=i.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):a.entities[i+c]?n+=a.entities[i+c]:(e=Object.keys(s).find(e=>i.startsWith(e)))?n+=a.entities[e]+i.slice(e.length)+c:(o("Incorrect escape."),n+=i+";"),t="data"}else o("Incorrect escape."),n+=i+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)i=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)i&&l.push(i),i="",t="start tag annotation";else if("\n"==c)i&&l.push(i),i=c,t="start tag annotation";else if("."==c)i&&l.push(i),i="";else{if(">"==c||void 0==c)return">"==c&&r++,i&&l.push(i),["start tag",n,l,""];i+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,l,i=i.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];i+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["timestamp",n];n+=c}else o("Never happens.");r++}}this.parse=function(t,s){var a={children:[]},c=a,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function d(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[r];){var p=l();if("text"==p[0])c.children.push({type:"text",value:p[1],parent:c});else if("start tag"==p[0]){"chapters"==i&&o("Start tags not allowed in chapter title text.");var m=p[1];"v"!=m&&"lang"!=m&&""!=p[3]&&o("Only and can have an annotation."),"c"==m||"i"==m||"b"==m||"u"==m||"ruby"==m||"rt"==m&&"ruby"==c.name?u(p):"v"==m?(d("v")&&o(" cannot be nested inside itself."),u(p),c.value=p[3],p[3]||o(" requires an annotation.")):"lang"==m?(u(p),c.value=p[3]):o("Incorrect start tag.")}else if("end tag"==p[0])"chapters"==i&&o("End tags not allowed in chapter title text."),p[1]==c.name?c=c.parent:"ruby"==p[1]&&"rt"==c.name?c=c.parent.parent:o("Incorrect end tag.");else if("timestamp"==p[0]){"chapters"==i&&o("Timestamp not allowed in chapter title text.");var g=new n(p[1],o).parseTimestamp();void 0!=g&&((g<=t||g>=s)&&o("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=g&&o("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:g,parent:c}),f.push(g))}}for(;c.parent;)"v"!=c.name&&o("Required end tag missing."),c=c.parent;return function e(t){const n={...t};return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(a)}},s=function(){function t(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,s=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),s=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+s).padStart(2,"0")+"."+t}function n(e){for(var i="",s=0;s/g,">");else if("object"==a.type){if(i+="<"+a.name,a.classes)for(var r=0;r"}else"timestamp"==a.type?i+="<"+t(a.value)+">":i+="<"+a.value+">"}return i}function i(i){return(void 0!==i.id?i.id+"\n":"")+t(i.startTime)+" --\x3e "+t(i.endTime)+function(t){var n="";const i=Object.keys(e).filter(n=>t[n]!==e[n]);return i.includes("direction")&&(n+=" vertical:"+t.direction),i.includes("alignment")&&(n+=" align:"+t.alignment),i.includes("size")&&(n+=" size:"+t.size+"%"),(i.includes("lineAlign")||i.includes("linePosition"))&&(n+=" line:"+t.linePosition+(t.snapToLines?"":"%")+(t.lineAlign&&t.lineAlign!=e.lineAlign?","+t.lineAlign:"")),(i.includes("textPosition")||i.includes("positionAlign"))&&(n+=" position:"+t.textPosition+"%"+(t.positionAlign&&t.positionAlign!==e.positionAlign?","+t.positionAlign:"")),n}(i)+"\n"+n(i.tree.children)+"\n\n"}function s(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var n="WEBVTT\n\n";if(t)for(var a=0;a{if(t.closed)return;const a=URL.createObjectURL(new Blob([s],{type:"text/vtt"})),r=function(e){let n=!1,i=()=>{};const s={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(s.free),s}(a);if(t.closed)return;const o=i;let l,c,f=!1;i=r;try{if(l=e.option.subtitle,c=l.escape,t.closed||i!==r)return;l.escape=!1,f=!0;const s={...l,url:a,type:"vtt",onVttLoad:n};if(t.closed||i!==r)return;const o=e.subtitle.init(s);Promise.resolve(o).catch(e=>{i===r&&(i=null,r.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(u){throw i===r&&(i=o,f&&!1===l.escape&&(l.escape=c)),r.free(),u}finally{o!==i&&o?.free(),r!==i&&r.free()}}}return function({subtitles:e=[]}){return async n=>{const{unescape:i,getExt:r,srtToVtt:o,assToVtt:l}=n.constructor.utils,c=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const s=new Promise(e=>{i=e});function a(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const r={get closed(){return t},own:e=>(t?a(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,s]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)a(t)}};if(!t){r.own(()=>e.off("destroy",r.dispose));try{e.on("destroy",r.dispose)}catch(o){throw r.dispose(),o}}return r}(n),f=a(n,c,i);let u=[];function d(e){c.closed||f(function(e){const t=[];for(const n of e){const e=n;for(const n of e.cues)t.push({...n,tree:{...n.tree,children:n.tree.children.map(t=>({...t,value:`
${t.value}
`}))}})}return(new s).serialize(t)}(e))}const p={name:"multipleSubtitles",tracks(e=[]){c.closed||d(e.map(e=>u.find(t=>t.name===e)))},reset(){d(u)}};try{if(c.closed)return p;const n=await Promise.all(e.map(e=>async function(e,{getExt:t,srtToVtt:n,assToVtt:i},s){if(s.closed)return;const a="function"===typeof AbortController?new AbortController:null,r=s.own(()=>a?.abort());try{const r=e.url;if(s.closed)return;const o=await s.wait(a?fetch(r,{signal:a.signal}):fetch(r));if(s.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=await s.wait(o.arrayBuffer());if(s.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw a?.abort(),o}finally{r()}}(e,{getExt:r,srtToVtt:o,assToVtt:l},c)));return c.closed||(u=function(e,n){const i=new t;return e.map((e,t)=>({...i.parse(e,"metadata"),url:n[t].url,name:n[t].name}))}(n,e),d(u)),p}catch(m){throw c.dispose(),m}}}}); diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js b/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js index 8724c0863..c9276f959 100644 --- a/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js +++ b/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js @@ -55,4 +55,4 @@ * * d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,n=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,a=(t,n,i)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[n]=i,o=(e,t)=>{for(var n in t||(t={}))r.call(t,n)&&a(e,n,t[n]);if(i)for(var n of i(t))s.call(t,n)&&a(e,n,t[n]);return e},l=(e,i)=>t(e,n(i)),c=(e,t,n)=>new Promise((i,r)=>{var s=e=>{try{o(n.next(e))}catch(t){r(t)}},a=e=>{try{o(n.throw(e))}catch(t){r(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(s,a);o((n=n.apply(e,t)).next())});var f={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},u=function(e){e||(e={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=e,this.parse=function(t,n){t=t.replace(/\0/g,"\ufffd");var i=Date.now(),r=0,s=t.split(/\r\n|\r|\n/),a=!1,o=[],l=[],c=[];function u(e,t){c.push({message:e,line:r+1,col:t})}var m=s[r],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&u('No valid signature. (File needs to start with "WEBVTT".)'),r++;""!=s[r]&&void 0!=s[r];){if(u("No blank line after the signature."),-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}for(;void 0!=s[r];){for(var y;!a&&""==s[r];)r++;if(!a&&void 0==s[r])break;y=Object.assign({},f,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var x=!0;if(-1==s[r].indexOf("--\x3e")){if(y.id=s[r],/^NOTE($|[ \t])/.test(y.id)){for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&u("Cannot have timestamp in a comment."),r++;continue}if(/^STYLE($|[ \t])/.test(y.id)){var T=[],w=!1;for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&(u("Cannot have timestamp in a style block."),w=!0),T.push(s[r]),r++;if(l.length){u("Style blocks cannot appear after the first cue.");continue}w||o.push(T.join("\n"));continue}if(""==s[++r]||void 0==s[r]){u("Cue identifier cannot be standalone.");continue}if(-1==s[r].indexOf("--\x3e")){x=!1,u("Cue identifier needs to be followed by timestamp.");continue}}a=!1;var O=new d(s[r],u),P=0;if(l.length>0&&(P=l[l.length-1].startTime),!x||O.parse(y,P)){for(r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){u("Blank line missing before cue."),a=!0;break}""!=y.text&&(y.text+="\n"),y.text+=s[r],r++}var A=new p(y.text,u,n,e);y.tree=A.parse(y.startTime,y.endTime),l.push(y)}else for(y=null,r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}}return l.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[r])if(r++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[r]){if(":"!=e[r])return void s("No seconds found or minutes is greater than 59.");if(r++,2!=(i=o(/\d/)).length)return void s("Must be exactly two digits.")}else{if(2!=t.length)return void s("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[r])if(r++,3==(a=o(/\d/)).length)if(parseInt(n,10)>59)s("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(a,10)/1e3;s("You cannot have more than 59 seconds.")}else s("Milliseconds must be given in three digits.");else s('No decimal separator (".") found.')}else s("Must be exactly two digits.");else s("No time unit separator found.");else s("Timestamp must start with a character in the range 0-9.");else s("No timestamp found.")}this.parse=function(t,o){if(a(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[r]){if(r++,i.test(e[r])&&s("'--\x3e' not separated from timestamp by whitespace."),a(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&s("End timestamp is not greater than start timestamp."),i.test(e[r]),a(n),function(e,t){for(var i=e.split(n),r=[],a=0;a100)){s("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){s("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){s("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){s("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){s("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){s("Size must be a percentage.");continue}if(parseInt(c,10)>100){s("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){s("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){s("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){s("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else s("Invalid setting.")}}(e.substring(r),t),!0}else s("No valid timestamp separator found.");else s("No valid timestamp separator found.");else s("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[r])return t;s("Timestamp must not have trailing characters.")}},p=function(e,t,n,i){this.entities=i;var r=this,s=(e=e,0),a=function(e){"metadata"!=n&&t(e,s+1)};function l(){for(var t="data",n="",o="",l=[];void 0!=e[s-1]||0==s;){var c=e[s];if("data"==t)if("&"==c)o=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return a("Incorrect escape."),(e=o.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):r.entities[o]?n+=r.entities[o]:n+=o,["text",n]}if("&"==c)a("Incorrect escape."),n+=o,o=c;else if(/[a-z#0-9]/i.test(c))o+=c;else if(";"==c){let e;(e=o.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):r.entities[o+c]?n+=r.entities[o+c]:(e=Object.keys(i).find(e=>o.startsWith(e)))?n+=r.entities[e]+o.slice(e.length)+c:(a("Incorrect escape."),n+=o+";"),t="data"}else a("Incorrect escape."),n+=o+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)o=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)o&&l.push(o),o="",t="start tag annotation";else if("\n"==c)o&&l.push(o),o=c,t="start tag annotation";else if("."==c)o&&l.push(o),o="";else{if(">"==c||void 0==c)return">"==c&&s++,o&&l.push(o),["start tag",n,l,""];o+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,l,o=o.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];o+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["timestamp",n];n+=c}else a("Never happens.");s++}}this.parse=function(t,i){var r={children:[]},c=r,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function p(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[s];){var m=l();if("text"==m[0])c.children.push({type:"text",value:m[1],parent:c});else if("start tag"==m[0]){"chapters"==n&&a("Start tags not allowed in chapter title text.");var g=m[1];"v"!=g&&"lang"!=g&&""!=m[3]&&a("Only and can have an annotation."),"c"==g||"i"==g||"b"==g||"u"==g||"ruby"==g||"rt"==g&&"ruby"==c.name?u(m):"v"==g?(p("v")&&a(" cannot be nested inside itself."),u(m),c.value=m[3],m[3]||a(" requires an annotation.")):"lang"==g?(u(m),c.value=m[3]):a("Incorrect start tag.")}else if("end tag"==m[0])"chapters"==n&&a("End tags not allowed in chapter title text."),m[1]==c.name?c=c.parent:"ruby"==m[1]&&"rt"==c.name?c=c.parent.parent:a("Incorrect end tag.");else if("timestamp"==m[0]){"chapters"==n&&a("Timestamp not allowed in chapter title text.");var h=new d(m[1],a).parseTimestamp();void 0!=h&&((h<=t||h>=i)&&a("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=h&&a("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:h,parent:c}),f.push(h))}}for(;c.parent;)"v"!=c.name&&a("Required end tag missing."),c=c.parent;return function e(t){const n=o({},t);return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(r)}},m=function(){function e(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,r=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),r=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+r).padStart(2,"0")+"."+t}function t(n){for(var i="",r=0;r/g,">");else if("object"==s.type){if(i+="<"+s.name,s.classes)for(var a=0;a"}else"timestamp"==s.type?i+="<"+e(s.value)+">":i+="<"+s.value+">"}return i}function n(n){return(void 0!==n.id?n.id+"\n":"")+e(n.startTime)+" --\x3e "+e(n.endTime)+function(e){var t="";const n=Object.keys(f).filter(t=>e[t]!==f[t]);return n.includes("direction")&&(t+=" vertical:"+e.direction),n.includes("alignment")&&(t+=" align:"+e.alignment),n.includes("size")&&(t+=" size:"+e.size+"%"),(n.includes("lineAlign")||n.includes("linePosition"))&&(t+=" line:"+e.linePosition+(e.snapToLines?"":"%")+(e.lineAlign&&e.lineAlign!=f.lineAlign?","+e.lineAlign:"")),(n.includes("textPosition")||n.includes("positionAlign"))&&(t+=" position:"+e.textPosition+"%"+(e.positionAlign&&e.positionAlign!==f.positionAlign?","+e.positionAlign:"")),t}(n)+"\n"+t(n.tree.children)+"\n\n"}function i(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var r="WEBVTT\n\n";if(t)for(var s=0;s{if(t.closed)return;const s=URL.createObjectURL(new Blob([r],{type:"text/vtt"})),a=function(e){let n=!1,i=()=>{};const r={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(r.free),r}(s);if(t.closed)return;const c=i;let f,u,d=!1;i=a;try{if(f=e.option.subtitle,u=f.escape,t.closed||i!==a)return;f.escape=!1,d=!0;const r=l(o({},f),{url:s,type:"vtt",onVttLoad:n});if(t.closed||i!==a)return;const c=e.subtitle.init(r);Promise.resolve(c).catch(e=>{i===a&&(i=null,a.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(p){throw i===a&&(i=c,d&&!1===f.escape&&(f.escape=u)),a.free(),p}finally{c!==i&&(null==c||c.free()),a!==i&&a.free()}}}return function({subtitles:e=[]}){return t=>c(null,null,function*(){const{unescape:n,getExt:i,srtToVtt:r,assToVtt:s}=t.constructor.utils,a=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const r=new Promise(e=>{i=e});function s(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const a={get closed(){return t},own:e=>(t?s(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,r]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)s(t)}};if(!t){a.own(()=>e.off("destroy",a.dispose));try{e.on("destroy",a.dispose)}catch(o){throw a.dispose(),o}}return a}(t),f=g(t,a,n);let d=[];function p(e){a.closed||f(function(e){const t=[];for(const n of e)for(const e of n.cues)t.push(l(o({},e),{tree:l(o({},e.tree),{children:e.tree.children.map(e=>l(o({},e),{value:`
${e.value}
`}))})}));return(new m).serialize(t)}(e))}const h={name:"multipleSubtitles",tracks(e=[]){a.closed||p(e.map(e=>d.find(t=>t.name===e)))},reset(){p(d)}};try{if(a.closed)return h;const t=yield Promise.all(e.map(e=>function(e,t,n){return c(this,arguments,function*(e,{getExt:t,srtToVtt:n,assToVtt:i},r){if(r.closed)return;const s="function"===typeof AbortController?new AbortController:null,a=r.own(()=>null==s?void 0:s.abort());try{const a=e.url;if(r.closed)return;const o=yield r.wait(s?fetch(a,{signal:s.signal}):fetch(a));if(r.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=yield r.wait(o.arrayBuffer());if(r.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw null==s||s.abort(),o}finally{a()}})}(e,{getExt:i,srtToVtt:r,assToVtt:s},a)));return a.closed||(d=function(e,t){const n=new u;return e.map((e,i)=>{const r=n.parse(e,"metadata");return r.url=t[i].url,r.name=t[i].name,r})}(t,e),p(d)),h}catch(v){throw a.dispose(),v}})}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,n=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,a=(t,n,i)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[n]=i,o=(e,t)=>{for(var n in t||(t={}))r.call(t,n)&&a(e,n,t[n]);if(i)for(var n of i(t))s.call(t,n)&&a(e,n,t[n]);return e},l=(e,i)=>t(e,n(i)),c=(e,t,n)=>new Promise((i,r)=>{var s=e=>{try{o(n.next(e))}catch(t){r(t)}},a=e=>{try{o(n.throw(e))}catch(t){r(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(s,a);o((n=n.apply(e,t)).next())});var f={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},u=function(e){e||(e={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=e,this.parse=function(t,n){t=t.replace(/\0/g,"\ufffd");var i=Date.now(),r=0,s=t.split(/\r\n|\r|\n/),a=!1,o=[],l=[],c=[];function u(e,t){c.push({message:e,line:r+1,col:t})}var m=s[r],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&u('No valid signature. (File needs to start with "WEBVTT".)'),r++;""!=s[r]&&void 0!=s[r];){if(u("No blank line after the signature."),-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}for(;void 0!=s[r];){for(var y;!a&&""==s[r];)r++;if(!a&&void 0==s[r])break;y=Object.assign({},f,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var x=!0;if(-1==s[r].indexOf("--\x3e")){if(y.id=s[r],/^NOTE($|[ \t])/.test(y.id)){for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&u("Cannot have timestamp in a comment."),r++;continue}if(/^STYLE($|[ \t])/.test(y.id)){var T=[],w=!1;for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&(u("Cannot have timestamp in a style block."),w=!0),T.push(s[r]),r++;if(l.length){u("Style blocks cannot appear after the first cue.");continue}w||o.push(T.join("\n"));continue}if(""==s[++r]||void 0==s[r]){u("Cue identifier cannot be standalone.");continue}if(-1==s[r].indexOf("--\x3e")){x=!1,u("Cue identifier needs to be followed by timestamp.");continue}}a=!1;var O=new d(s[r],u),P=0;if(l.length>0&&(P=l[l.length-1].startTime),!x||O.parse(y,P)){for(r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){u("Blank line missing before cue."),a=!0;break}""!=y.text&&(y.text+="\n"),y.text+=s[r],r++}var A=new p(y.text,u,n,e);y.tree=A.parse(y.startTime,y.endTime),l.push(y)}else for(y=null,r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}}return l.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[r])if(r++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[r]){if(":"!=e[r])return void s("No seconds found or minutes is greater than 59.");if(r++,2!=(i=o(/\d/)).length)return void s("Must be exactly two digits.")}else{if(2!=t.length)return void s("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[r])if(r++,3==(a=o(/\d/)).length)if(parseInt(n,10)>59)s("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(a,10)/1e3;s("You cannot have more than 59 seconds.")}else s("Milliseconds must be given in three digits.");else s('No decimal separator (".") found.')}else s("Must be exactly two digits.");else s("No time unit separator found.");else s("Timestamp must start with a character in the range 0-9.");else s("No timestamp found.")}this.parse=function(t,o){if(a(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[r]){if(r++,i.test(e[r])&&s("'--\x3e' not separated from timestamp by whitespace."),a(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&s("End timestamp is not greater than start timestamp."),i.test(e[r]),a(n),function(e,t){for(var i=e.split(n),r=[],a=0;a100)){s("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){s("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){s("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){s("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){s("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){s("Size must be a percentage.");continue}if(parseInt(c,10)>100){s("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){s("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){s("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){s("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else s("Invalid setting.")}}(e.substring(r),t),!0}else s("No valid timestamp separator found.");else s("No valid timestamp separator found.");else s("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[r])return t;s("Timestamp must not have trailing characters.")}},p=function(e,t,n,i){this.entities=i;var r=this,s=(e=e,0),a=function(e){"metadata"!=n&&t(e,s+1)};function l(){for(var t="data",n="",o="",l=[];void 0!=e[s-1]||0==s;){var c=e[s];if("data"==t)if("&"==c)o=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return a("Incorrect escape."),(e=o.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):r.entities[o]?n+=r.entities[o]:n+=o,["text",n]}if("&"==c)a("Incorrect escape."),n+=o,o=c;else if(/[a-z#0-9]/i.test(c))o+=c;else if(";"==c){let e;(e=o.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):r.entities[o+c]?n+=r.entities[o+c]:(e=Object.keys(i).find(e=>o.startsWith(e)))?n+=r.entities[e]+o.slice(e.length)+c:(a("Incorrect escape."),n+=o+";"),t="data"}else a("Incorrect escape."),n+=o+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)o=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)o&&l.push(o),o="",t="start tag annotation";else if("\n"==c)o&&l.push(o),o=c,t="start tag annotation";else if("."==c)o&&l.push(o),o="";else{if(">"==c||void 0==c)return">"==c&&s++,o&&l.push(o),["start tag",n,l,""];o+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,l,o=o.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];o+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["timestamp",n];n+=c}else a("Never happens.");s++}}this.parse=function(t,i){var r={children:[]},c=r,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function p(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[s];){var m=l();if("text"==m[0])c.children.push({type:"text",value:m[1],parent:c});else if("start tag"==m[0]){"chapters"==n&&a("Start tags not allowed in chapter title text.");var g=m[1];"v"!=g&&"lang"!=g&&""!=m[3]&&a("Only and can have an annotation."),"c"==g||"i"==g||"b"==g||"u"==g||"ruby"==g||"rt"==g&&"ruby"==c.name?u(m):"v"==g?(p("v")&&a(" cannot be nested inside itself."),u(m),c.value=m[3],m[3]||a(" requires an annotation.")):"lang"==g?(u(m),c.value=m[3]):a("Incorrect start tag.")}else if("end tag"==m[0])"chapters"==n&&a("End tags not allowed in chapter title text."),m[1]==c.name?c=c.parent:"ruby"==m[1]&&"rt"==c.name?c=c.parent.parent:a("Incorrect end tag.");else if("timestamp"==m[0]){"chapters"==n&&a("Timestamp not allowed in chapter title text.");var h=new d(m[1],a).parseTimestamp();void 0!=h&&((h<=t||h>=i)&&a("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=h&&a("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:h,parent:c}),f.push(h))}}for(;c.parent;)"v"!=c.name&&a("Required end tag missing."),c=c.parent;return function e(t){const n=o({},t);return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(r)}},m=function(){function e(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,r=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),r=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+r).padStart(2,"0")+"."+t}function t(n){for(var i="",r=0;r/g,">");else if("object"==s.type){if(i+="<"+s.name,s.classes)for(var a=0;a"}else"timestamp"==s.type?i+="<"+e(s.value)+">":i+="<"+s.value+">"}return i}function n(n){return(void 0!==n.id?n.id+"\n":"")+e(n.startTime)+" --\x3e "+e(n.endTime)+function(e){var t="";const n=Object.keys(f).filter(t=>e[t]!==f[t]);return n.includes("direction")&&(t+=" vertical:"+e.direction),n.includes("alignment")&&(t+=" align:"+e.alignment),n.includes("size")&&(t+=" size:"+e.size+"%"),(n.includes("lineAlign")||n.includes("linePosition"))&&(t+=" line:"+e.linePosition+(e.snapToLines?"":"%")+(e.lineAlign&&e.lineAlign!=f.lineAlign?","+e.lineAlign:"")),(n.includes("textPosition")||n.includes("positionAlign"))&&(t+=" position:"+e.textPosition+"%"+(e.positionAlign&&e.positionAlign!==f.positionAlign?","+e.positionAlign:"")),t}(n)+"\n"+t(n.tree.children)+"\n\n"}function i(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var r="WEBVTT\n\n";if(t)for(var s=0;s{if(t.closed)return;const s=URL.createObjectURL(new Blob([r],{type:"text/vtt"})),a=function(e){let n=!1,i=()=>{};const r={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(r.free),r}(s);if(t.closed)return;const c=i;let f,u,d=!1;i=a;try{if(f=e.option.subtitle,u=f.escape,t.closed||i!==a)return;f.escape=!1,d=!0;const r=l(o({},f),{url:s,type:"vtt",onVttLoad:n});if(t.closed||i!==a)return;const c=e.subtitle.init(r);Promise.resolve(c).catch(e=>{i===a&&(i=null,a.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(p){throw i===a&&(i=c,d&&!1===f.escape&&(f.escape=u)),a.free(),p}finally{c!==i&&(null==c||c.free()),a!==i&&a.free()}}}return function({subtitles:e=[]}){return t=>c(null,null,function*(){const{unescape:n,getExt:i,srtToVtt:r,assToVtt:s}=t.constructor.utils,a=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const r=new Promise(e=>{i=e});function s(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const a={get closed(){return t},own:e=>(t?s(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,r]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)s(t)}};if(!t){a.own(()=>e.off("destroy",a.dispose));try{e.on("destroy",a.dispose)}catch(o){throw a.dispose(),o}}return a}(t),f=g(t,a,n);let d=[];function p(e){a.closed||f(function(e){const t=[];for(const n of e){const e=n;for(const n of e.cues)t.push(l(o({},n),{tree:l(o({},n.tree),{children:n.tree.children.map(t=>l(o({},t),{value:`
${t.value}
`}))})}))}return(new m).serialize(t)}(e))}const h={name:"multipleSubtitles",tracks(e=[]){a.closed||p(e.map(e=>d.find(t=>t.name===e)))},reset(){p(d)}};try{if(a.closed)return h;const t=yield Promise.all(e.map(e=>function(e,t,n){return c(this,arguments,function*(e,{getExt:t,srtToVtt:n,assToVtt:i},r){if(r.closed)return;const s="function"===typeof AbortController?new AbortController:null,a=r.own(()=>null==s?void 0:s.abort());try{const a=e.url;if(r.closed)return;const o=yield r.wait(s?fetch(a,{signal:s.signal}):fetch(a));if(r.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=yield r.wait(o.arrayBuffer());if(r.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw null==s||s.abort(),o}finally{a()}})}(e,{getExt:i,srtToVtt:r,assToVtt:s},a)));return a.closed||(d=function(e,t){const n=new u;return e.map((e,i)=>{const r=n.parse(e,"metadata");return l(o({},r),{url:t[i].url,name:t[i].name})})}(t,e),p(d)),h}catch(v){throw a.dispose(),v}})}}); diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.mjs b/docs/compiled/artplayer-plugin-multiple-subtitles.mjs index fe8eb5a22..06d31ac62 100644 --- a/docs/compiled/artplayer-plugin-multiple-subtitles.mjs +++ b/docs/compiled/artplayer-plugin-multiple-subtitles.mjs @@ -853,14 +853,13 @@ function parseTracks(vtts, subtitles) { const parser = new WebVTTParser(); return vtts.map((vtt, index) => { const tree = parser.parse(vtt, "metadata"); - tree.url = subtitles[index].url; - tree.name = subtitles[index].name; - return tree; + return { ...tree, url: subtitles[index].url, name: subtitles[index].name }; }); } function serializeTracks(trees) { const cues = []; - for (const tree of trees) { + for (const selected of trees) { + const tree = selected; for (const cue of tree.cues) { cues.push({ ...cue, diff --git a/package.json b/package.json index 14f12f8dc..a53cf3ecc 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "test:auto-thumbnail": "node --test test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js refactor/scripts/auto-thumbnail-contract.test.mjs", "test:vtt-thumbnail": "node --test test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js refactor/scripts/vtt-thumbnail-contract.test.mjs refactor/scripts/vtt-thumbnail-types.test.mjs", "test:vtt-thumbnail-types-package": "node refactor/scripts/vtt-thumbnail-package-types.mjs", - "test:multiple-subtitles": "node --test test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js refactor/scripts/multiple-subtitles-contract.test.mjs" + "test:multiple-subtitles": "node --test test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js refactor/scripts/multiple-subtitles-contract.test.mjs refactor/scripts/multiple-subtitles-runtime-types.test.mjs" }, "browserslist": "last 1 Chrome version", "devDependencies": { diff --git a/packages/artplayer-plugin-multiple-subtitles/.npmignore b/packages/artplayer-plugin-multiple-subtitles/.npmignore index 4439c6874..2b272d565 100644 --- a/packages/artplayer-plugin-multiple-subtitles/.npmignore +++ b/packages/artplayer-plugin-multiple-subtitles/.npmignore @@ -2,4 +2,5 @@ node_modules .DS_Store yarn.lock package-lock.json -src \ No newline at end of file +src +tsconfig.json diff --git a/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md b/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md index d736c8c86..116a03b20 100644 --- a/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md +++ b/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md @@ -8,15 +8,20 @@ task PKG-MULTI-SUB-04 owns that compatibility work. Do not infer runtime behavio ## Current module boundaries -- `src/index.js` composes registration, named selection and the unchanged public result. -- `src/request.js` owns each fetch through byte decoding and delegates SRT/ASS conversion +- `src/index.ts` composes registration, named selection and the unchanged public result. +- `src/request.ts` owns each fetch through byte decoding and delegates SRT/ASS conversion to the existing core utils. Failure aborts the request; registration failure closes siblings. -- `src/merge.js` parses metadata and serializes selected cues without mutating parsed trees. +- `src/merge.ts` parses metadata and serializes selected cues without mutating parsed trees. It depends only on the vendor parser, not the player or browser resource APIs. -- `src/lifetime.js` owns the destroy listener, request cancellation and resource callbacks. +- `src/lifetime.ts` owns the destroy listener, request cancellation and resource callbacks. Its cancellation Promise also settles pending operations without AbortController support. -- `src/render.js` owns generated Blob URLs and synchronous/async host installation outcomes. +- `src/render.ts` owns generated Blob URLs and synchronous/async host installation outcomes. Reentrant selections and stale host rejections cannot release the latest selected resource. +- `src/types.ts` describes the internal Promise result, selected tracks and narrow host/resource + boundaries; option fields reuse the existing public declaration. It is not a published API. +- `src/parser.d.ts` describes the vendored parse result and discriminated cue nodes. Parsed + numeric timestamps are distinct from the historically wrapped string nodes accepted by the + serializer. That wrapping produces invalid inline times; PKG-MULTI-SUB-07 owns its repair. - `src/parser.js` is vendored WebVTT parser/serializer code with a CC0 dedication. It is not owned plugin code to mechanically migrate to TypeScript. Keep it distinct from task 04. The pinned upstream reference and full adaptation check are in @@ -77,5 +82,9 @@ Candidate lifecycle and merge suites cover cancellation, failures, resource owne reentrancy and immutable serialization. The candidate browser suite adds real video source switching, HTTP rejection and Blob revocation. For main/legacy validation, set the artifact environment variable above before running it; source builds are used when it is absent. +The package tsconfig checks only authored TS and declarations, with strict/noUncheckedIndexedAccess +and allowJs=false. TS 5.9.3/5.1.6 positive and negative fixtures cover internal runtime types. +Do not remove cancellation/index boundary assertions by changing historical optional inputs. +The public synchronous declaration mismatch remains an explicit task 04 migration boundary. Tasks 05/06 still own complete core/device combinations and demo acceptance. See `../../refactor/baselines/multiple-subtitles-contract.md` for exact historical differences. diff --git a/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js b/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js index 83ae8cdbb..6b450f1dc 100644 --- a/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js +++ b/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js @@ -55,4 +55,4 @@ * * d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},t=function(t){t||(t={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=t,this.parse=function(s,a){s=s.replace(/\0/g,"\ufffd");var r=Date.now(),o=0,l=s.split(/\r\n|\r|\n/),c=!1,f=[],u=[],d=[];function p(e,t){d.push({message:e,line:o+1,col:t})}var m=l[o],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&p('No valid signature. (File needs to start with "WEBVTT".)'),o++;""!=l[o]&&void 0!=l[o];){if(p("No blank line after the signature."),-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}for(;void 0!=l[o];){for(var x;!c&&""==l[o];)o++;if(!c&&void 0==l[o])break;x=Object.assign({},e,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var T=!0;if(-1==l[o].indexOf("--\x3e")){if(x.id=l[o],/^NOTE($|[ \t])/.test(x.id)){for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&p("Cannot have timestamp in a comment."),o++;continue}if(/^STYLE($|[ \t])/.test(x.id)){var y=[],w=!1;for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&(p("Cannot have timestamp in a style block."),w=!0),y.push(l[o]),o++;if(u.length){p("Style blocks cannot appear after the first cue.");continue}w||f.push(y.join("\n"));continue}if(""==l[++o]||void 0==l[o]){p("Cue identifier cannot be standalone.");continue}if(-1==l[o].indexOf("--\x3e")){T=!1,p("Cue identifier needs to be followed by timestamp.");continue}}c=!1;var O=new n(l[o],p),A=0;if(u.length>0&&(A=u[u.length-1].startTime),!T||O.parse(x,A)){for(o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){p("Blank line missing before cue."),c=!0;break}""!=x.text&&(x.text+="\n"),x.text+=l[o],o++}var L=new i(x.text,p,a,t);x.tree=L.parse(x.startTime,x.endTime),u.push(x)}else for(x=null,o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}}return u.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[s])if(s++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[s]){if(":"!=e[s])return void a("No seconds found or minutes is greater than 59.");if(s++,2!=(i=o(/\d/)).length)return void a("Must be exactly two digits.")}else{if(2!=t.length)return void a("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[s])if(s++,3==(r=o(/\d/)).length)if(parseInt(n,10)>59)a("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(r,10)/1e3;a("You cannot have more than 59 seconds.")}else a("Milliseconds must be given in three digits.");else a('No decimal separator (".") found.')}else a("Must be exactly two digits.");else a("No time unit separator found.");else a("Timestamp must start with a character in the range 0-9.");else a("No timestamp found.")}this.parse=function(t,o){if(r(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[s]){if(s++,i.test(e[s])&&a("'--\x3e' not separated from timestamp by whitespace."),r(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&a("End timestamp is not greater than start timestamp."),i.test(e[s]),r(n),function(e,t){for(var i=e.split(n),s=[],r=0;r100)){a("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){a("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){a("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){a("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){a("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){a("Size must be a percentage.");continue}if(parseInt(c,10)>100){a("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){a("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){a("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){a("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else a("Invalid setting.")}}(e.substring(s),t),!0}else a("No valid timestamp separator found.");else a("No valid timestamp separator found.");else a("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[s])return t;a("Timestamp must not have trailing characters.")}},i=function(e,t,i,s){this.entities=s;var a=this,r=(e=e,0),o=function(e){"metadata"!=i&&t(e,r+1)};function l(){for(var t="data",n="",i="",l=[];void 0!=e[r-1]||0==r;){var c=e[r];if("data"==t)if("&"==c)i=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return o("Incorrect escape."),(e=i.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):a.entities[i]?n+=a.entities[i]:n+=i,["text",n]}if("&"==c)o("Incorrect escape."),n+=i,i=c;else if(/[a-z#0-9]/i.test(c))i+=c;else if(";"==c){let e;(e=i.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):a.entities[i+c]?n+=a.entities[i+c]:(e=Object.keys(s).find(e=>i.startsWith(e)))?n+=a.entities[e]+i.slice(e.length)+c:(o("Incorrect escape."),n+=i+";"),t="data"}else o("Incorrect escape."),n+=i+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)i=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)i&&l.push(i),i="",t="start tag annotation";else if("\n"==c)i&&l.push(i),i=c,t="start tag annotation";else if("."==c)i&&l.push(i),i="";else{if(">"==c||void 0==c)return">"==c&&r++,i&&l.push(i),["start tag",n,l,""];i+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,l,i=i.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];i+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["timestamp",n];n+=c}else o("Never happens.");r++}}this.parse=function(t,s){var a={children:[]},c=a,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function d(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[r];){var p=l();if("text"==p[0])c.children.push({type:"text",value:p[1],parent:c});else if("start tag"==p[0]){"chapters"==i&&o("Start tags not allowed in chapter title text.");var m=p[1];"v"!=m&&"lang"!=m&&""!=p[3]&&o("Only and can have an annotation."),"c"==m||"i"==m||"b"==m||"u"==m||"ruby"==m||"rt"==m&&"ruby"==c.name?u(p):"v"==m?(d("v")&&o(" cannot be nested inside itself."),u(p),c.value=p[3],p[3]||o(" requires an annotation.")):"lang"==m?(u(p),c.value=p[3]):o("Incorrect start tag.")}else if("end tag"==p[0])"chapters"==i&&o("End tags not allowed in chapter title text."),p[1]==c.name?c=c.parent:"ruby"==p[1]&&"rt"==c.name?c=c.parent.parent:o("Incorrect end tag.");else if("timestamp"==p[0]){"chapters"==i&&o("Timestamp not allowed in chapter title text.");var g=new n(p[1],o).parseTimestamp();void 0!=g&&((g<=t||g>=s)&&o("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=g&&o("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:g,parent:c}),f.push(g))}}for(;c.parent;)"v"!=c.name&&o("Required end tag missing."),c=c.parent;return function e(t){const n={...t};return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(a)}},s=function(){function t(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,s=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),s=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+s).padStart(2,"0")+"."+t}function n(e){for(var i="",s=0;s/g,">");else if("object"==a.type){if(i+="<"+a.name,a.classes)for(var r=0;r"}else"timestamp"==a.type?i+="<"+t(a.value)+">":i+="<"+a.value+">"}return i}function i(i){return(void 0!==i.id?i.id+"\n":"")+t(i.startTime)+" --\x3e "+t(i.endTime)+function(t){var n="";const i=Object.keys(e).filter(n=>t[n]!==e[n]);return i.includes("direction")&&(n+=" vertical:"+t.direction),i.includes("alignment")&&(n+=" align:"+t.alignment),i.includes("size")&&(n+=" size:"+t.size+"%"),(i.includes("lineAlign")||i.includes("linePosition"))&&(n+=" line:"+t.linePosition+(t.snapToLines?"":"%")+(t.lineAlign&&t.lineAlign!=e.lineAlign?","+t.lineAlign:"")),(i.includes("textPosition")||i.includes("positionAlign"))&&(n+=" position:"+t.textPosition+"%"+(t.positionAlign&&t.positionAlign!==e.positionAlign?","+t.positionAlign:"")),n}(i)+"\n"+n(i.tree.children)+"\n\n"}function s(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var n="WEBVTT\n\n";if(t)for(var a=0;a{if(t.closed)return;const a=URL.createObjectURL(new Blob([s],{type:"text/vtt"})),r=function(e){let n=!1,i=()=>{};const s={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(s.free),s}(a);if(t.closed)return;const o=i;let l,c,f=!1;i=r;try{if(l=e.option.subtitle,c=l.escape,t.closed||i!==r)return;l.escape=!1,f=!0;const s={...l,url:a,type:"vtt",onVttLoad:n};if(t.closed||i!==r)return;const o=e.subtitle.init(s);Promise.resolve(o).catch(e=>{i===r&&(i=null,r.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(u){throw i===r&&(i=o,f&&!1===l.escape&&(l.escape=c)),r.free(),u}finally{o!==i&&o?.free(),r!==i&&r.free()}}}return function({subtitles:e=[]}){return async n=>{const{unescape:i,getExt:r,srtToVtt:o,assToVtt:l}=n.constructor.utils,c=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const s=new Promise(e=>{i=e});function a(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const r={get closed(){return t},own:e=>(t?a(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,s]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)a(t)}};if(!t){r.own(()=>e.off("destroy",r.dispose));try{e.on("destroy",r.dispose)}catch(o){throw r.dispose(),o}}return r}(n),f=a(n,c,i);let u=[];function d(e){c.closed||f(function(e){const t=[];for(const n of e)for(const e of n.cues)t.push({...e,tree:{...e.tree,children:e.tree.children.map(e=>({...e,value:`
${e.value}
`}))}});return(new s).serialize(t)}(e))}const p={name:"multipleSubtitles",tracks(e=[]){c.closed||d(e.map(e=>u.find(t=>t.name===e)))},reset(){d(u)}};try{if(c.closed)return p;const n=await Promise.all(e.map(e=>async function(e,{getExt:t,srtToVtt:n,assToVtt:i},s){if(s.closed)return;const a="function"===typeof AbortController?new AbortController:null,r=s.own(()=>a?.abort());try{const r=e.url;if(s.closed)return;const o=await s.wait(a?fetch(r,{signal:a.signal}):fetch(r));if(s.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=await s.wait(o.arrayBuffer());if(s.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw a?.abort(),o}finally{r()}}(e,{getExt:r,srtToVtt:o,assToVtt:l},c)));return c.closed||(u=function(e,n){const i=new t;return e.map((e,t)=>{const s=i.parse(e,"metadata");return s.url=n[t].url,s.name=n[t].name,s})}(n,e),d(u)),p}catch(m){throw c.dispose(),m}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},t=function(t){t||(t={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=t,this.parse=function(s,a){s=s.replace(/\0/g,"\ufffd");var r=Date.now(),o=0,l=s.split(/\r\n|\r|\n/),c=!1,f=[],u=[],d=[];function p(e,t){d.push({message:e,line:o+1,col:t})}var m=l[o],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&p('No valid signature. (File needs to start with "WEBVTT".)'),o++;""!=l[o]&&void 0!=l[o];){if(p("No blank line after the signature."),-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}for(;void 0!=l[o];){for(var x;!c&&""==l[o];)o++;if(!c&&void 0==l[o])break;x=Object.assign({},e,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var T=!0;if(-1==l[o].indexOf("--\x3e")){if(x.id=l[o],/^NOTE($|[ \t])/.test(x.id)){for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&p("Cannot have timestamp in a comment."),o++;continue}if(/^STYLE($|[ \t])/.test(x.id)){var y=[],w=!1;for(o++;""!=l[o]&&void 0!=l[o];)-1!=l[o].indexOf("--\x3e")&&(p("Cannot have timestamp in a style block."),w=!0),y.push(l[o]),o++;if(u.length){p("Style blocks cannot appear after the first cue.");continue}w||f.push(y.join("\n"));continue}if(""==l[++o]||void 0==l[o]){p("Cue identifier cannot be standalone.");continue}if(-1==l[o].indexOf("--\x3e")){T=!1,p("Cue identifier needs to be followed by timestamp.");continue}}c=!1;var O=new n(l[o],p),A=0;if(u.length>0&&(A=u[u.length-1].startTime),!T||O.parse(x,A)){for(o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){p("Blank line missing before cue."),c=!0;break}""!=x.text&&(x.text+="\n"),x.text+=l[o],o++}var L=new i(x.text,p,a,t);x.tree=L.parse(x.startTime,x.endTime),u.push(x)}else for(x=null,o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){c=!0;break}o++}}return u.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[s])if(s++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[s]){if(":"!=e[s])return void a("No seconds found or minutes is greater than 59.");if(s++,2!=(i=o(/\d/)).length)return void a("Must be exactly two digits.")}else{if(2!=t.length)return void a("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[s])if(s++,3==(r=o(/\d/)).length)if(parseInt(n,10)>59)a("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(r,10)/1e3;a("You cannot have more than 59 seconds.")}else a("Milliseconds must be given in three digits.");else a('No decimal separator (".") found.')}else a("Must be exactly two digits.");else a("No time unit separator found.");else a("Timestamp must start with a character in the range 0-9.");else a("No timestamp found.")}this.parse=function(t,o){if(r(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[s]){if(s++,i.test(e[s])&&a("'--\x3e' not separated from timestamp by whitespace."),r(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&a("End timestamp is not greater than start timestamp."),i.test(e[s]),r(n),function(e,t){for(var i=e.split(n),s=[],r=0;r100)){a("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){a("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){a("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){a("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){a("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){a("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){a("Size must be a percentage.");continue}if(parseInt(c,10)>100){a("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){a("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){a("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){a("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else a("Invalid setting.")}}(e.substring(s),t),!0}else a("No valid timestamp separator found.");else a("No valid timestamp separator found.");else a("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[s])return t;a("Timestamp must not have trailing characters.")}},i=function(e,t,i,s){this.entities=s;var a=this,r=(e=e,0),o=function(e){"metadata"!=i&&t(e,r+1)};function l(){for(var t="data",n="",i="",l=[];void 0!=e[r-1]||0==r;){var c=e[r];if("data"==t)if("&"==c)i=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return o("Incorrect escape."),(e=i.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):a.entities[i]?n+=a.entities[i]:n+=i,["text",n]}if("&"==c)o("Incorrect escape."),n+=i,i=c;else if(/[a-z#0-9]/i.test(c))i+=c;else if(";"==c){let e;(e=i.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):a.entities[i+c]?n+=a.entities[i+c]:(e=Object.keys(s).find(e=>i.startsWith(e)))?n+=a.entities[e]+i.slice(e.length)+c:(o("Incorrect escape."),n+=i+";"),t="data"}else o("Incorrect escape."),n+=i+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)i=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)i&&l.push(i),i="",t="start tag annotation";else if("\n"==c)i&&l.push(i),i=c,t="start tag annotation";else if("."==c)i&&l.push(i),i="";else{if(">"==c||void 0==c)return">"==c&&r++,i&&l.push(i),["start tag",n,l,""];i+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&r++,["start tag",n,l,i=i.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];i+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&r++,["timestamp",n];n+=c}else o("Never happens.");r++}}this.parse=function(t,s){var a={children:[]},c=a,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function d(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[r];){var p=l();if("text"==p[0])c.children.push({type:"text",value:p[1],parent:c});else if("start tag"==p[0]){"chapters"==i&&o("Start tags not allowed in chapter title text.");var m=p[1];"v"!=m&&"lang"!=m&&""!=p[3]&&o("Only and can have an annotation."),"c"==m||"i"==m||"b"==m||"u"==m||"ruby"==m||"rt"==m&&"ruby"==c.name?u(p):"v"==m?(d("v")&&o(" cannot be nested inside itself."),u(p),c.value=p[3],p[3]||o(" requires an annotation.")):"lang"==m?(u(p),c.value=p[3]):o("Incorrect start tag.")}else if("end tag"==p[0])"chapters"==i&&o("End tags not allowed in chapter title text."),p[1]==c.name?c=c.parent:"ruby"==p[1]&&"rt"==c.name?c=c.parent.parent:o("Incorrect end tag.");else if("timestamp"==p[0]){"chapters"==i&&o("Timestamp not allowed in chapter title text.");var g=new n(p[1],o).parseTimestamp();void 0!=g&&((g<=t||g>=s)&&o("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=g&&o("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:g,parent:c}),f.push(g))}}for(;c.parent;)"v"!=c.name&&o("Required end tag missing."),c=c.parent;return function e(t){const n={...t};return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(a)}},s=function(){function t(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,s=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),s=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+s).padStart(2,"0")+"."+t}function n(e){for(var i="",s=0;s/g,">");else if("object"==a.type){if(i+="<"+a.name,a.classes)for(var r=0;r"}else"timestamp"==a.type?i+="<"+t(a.value)+">":i+="<"+a.value+">"}return i}function i(i){return(void 0!==i.id?i.id+"\n":"")+t(i.startTime)+" --\x3e "+t(i.endTime)+function(t){var n="";const i=Object.keys(e).filter(n=>t[n]!==e[n]);return i.includes("direction")&&(n+=" vertical:"+t.direction),i.includes("alignment")&&(n+=" align:"+t.alignment),i.includes("size")&&(n+=" size:"+t.size+"%"),(i.includes("lineAlign")||i.includes("linePosition"))&&(n+=" line:"+t.linePosition+(t.snapToLines?"":"%")+(t.lineAlign&&t.lineAlign!=e.lineAlign?","+t.lineAlign:"")),(i.includes("textPosition")||i.includes("positionAlign"))&&(n+=" position:"+t.textPosition+"%"+(t.positionAlign&&t.positionAlign!==e.positionAlign?","+t.positionAlign:"")),n}(i)+"\n"+n(i.tree.children)+"\n\n"}function s(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var n="WEBVTT\n\n";if(t)for(var a=0;a{if(t.closed)return;const a=URL.createObjectURL(new Blob([s],{type:"text/vtt"})),r=function(e){let n=!1,i=()=>{};const s={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(s.free),s}(a);if(t.closed)return;const o=i;let l,c,f=!1;i=r;try{if(l=e.option.subtitle,c=l.escape,t.closed||i!==r)return;l.escape=!1,f=!0;const s={...l,url:a,type:"vtt",onVttLoad:n};if(t.closed||i!==r)return;const o=e.subtitle.init(s);Promise.resolve(o).catch(e=>{i===r&&(i=null,r.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(u){throw i===r&&(i=o,f&&!1===l.escape&&(l.escape=c)),r.free(),u}finally{o!==i&&o?.free(),r!==i&&r.free()}}}return function({subtitles:e=[]}){return async n=>{const{unescape:i,getExt:r,srtToVtt:o,assToVtt:l}=n.constructor.utils,c=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const s=new Promise(e=>{i=e});function a(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const r={get closed(){return t},own:e=>(t?a(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,s]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)a(t)}};if(!t){r.own(()=>e.off("destroy",r.dispose));try{e.on("destroy",r.dispose)}catch(o){throw r.dispose(),o}}return r}(n),f=a(n,c,i);let u=[];function d(e){c.closed||f(function(e){const t=[];for(const n of e){const e=n;for(const n of e.cues)t.push({...n,tree:{...n.tree,children:n.tree.children.map(t=>({...t,value:`
${t.value}
`}))}})}return(new s).serialize(t)}(e))}const p={name:"multipleSubtitles",tracks(e=[]){c.closed||d(e.map(e=>u.find(t=>t.name===e)))},reset(){d(u)}};try{if(c.closed)return p;const n=await Promise.all(e.map(e=>async function(e,{getExt:t,srtToVtt:n,assToVtt:i},s){if(s.closed)return;const a="function"===typeof AbortController?new AbortController:null,r=s.own(()=>a?.abort());try{const r=e.url;if(s.closed)return;const o=await s.wait(a?fetch(r,{signal:a.signal}):fetch(r));if(s.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=await s.wait(o.arrayBuffer());if(s.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw a?.abort(),o}finally{r()}}(e,{getExt:r,srtToVtt:o,assToVtt:l},c)));return c.closed||(u=function(e,n){const i=new t;return e.map((e,t)=>({...i.parse(e,"metadata"),url:n[t].url,name:n[t].name}))}(n,e),d(u)),p}catch(m){throw c.dispose(),m}}}}); diff --git a/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js b/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js index 8724c0863..c9276f959 100644 --- a/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js +++ b/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js @@ -55,4 +55,4 @@ * * d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,n=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,a=(t,n,i)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[n]=i,o=(e,t)=>{for(var n in t||(t={}))r.call(t,n)&&a(e,n,t[n]);if(i)for(var n of i(t))s.call(t,n)&&a(e,n,t[n]);return e},l=(e,i)=>t(e,n(i)),c=(e,t,n)=>new Promise((i,r)=>{var s=e=>{try{o(n.next(e))}catch(t){r(t)}},a=e=>{try{o(n.throw(e))}catch(t){r(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(s,a);o((n=n.apply(e,t)).next())});var f={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},u=function(e){e||(e={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=e,this.parse=function(t,n){t=t.replace(/\0/g,"\ufffd");var i=Date.now(),r=0,s=t.split(/\r\n|\r|\n/),a=!1,o=[],l=[],c=[];function u(e,t){c.push({message:e,line:r+1,col:t})}var m=s[r],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&u('No valid signature. (File needs to start with "WEBVTT".)'),r++;""!=s[r]&&void 0!=s[r];){if(u("No blank line after the signature."),-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}for(;void 0!=s[r];){for(var y;!a&&""==s[r];)r++;if(!a&&void 0==s[r])break;y=Object.assign({},f,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var x=!0;if(-1==s[r].indexOf("--\x3e")){if(y.id=s[r],/^NOTE($|[ \t])/.test(y.id)){for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&u("Cannot have timestamp in a comment."),r++;continue}if(/^STYLE($|[ \t])/.test(y.id)){var T=[],w=!1;for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&(u("Cannot have timestamp in a style block."),w=!0),T.push(s[r]),r++;if(l.length){u("Style blocks cannot appear after the first cue.");continue}w||o.push(T.join("\n"));continue}if(""==s[++r]||void 0==s[r]){u("Cue identifier cannot be standalone.");continue}if(-1==s[r].indexOf("--\x3e")){x=!1,u("Cue identifier needs to be followed by timestamp.");continue}}a=!1;var O=new d(s[r],u),P=0;if(l.length>0&&(P=l[l.length-1].startTime),!x||O.parse(y,P)){for(r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){u("Blank line missing before cue."),a=!0;break}""!=y.text&&(y.text+="\n"),y.text+=s[r],r++}var A=new p(y.text,u,n,e);y.tree=A.parse(y.startTime,y.endTime),l.push(y)}else for(y=null,r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}}return l.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[r])if(r++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[r]){if(":"!=e[r])return void s("No seconds found or minutes is greater than 59.");if(r++,2!=(i=o(/\d/)).length)return void s("Must be exactly two digits.")}else{if(2!=t.length)return void s("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[r])if(r++,3==(a=o(/\d/)).length)if(parseInt(n,10)>59)s("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(a,10)/1e3;s("You cannot have more than 59 seconds.")}else s("Milliseconds must be given in three digits.");else s('No decimal separator (".") found.')}else s("Must be exactly two digits.");else s("No time unit separator found.");else s("Timestamp must start with a character in the range 0-9.");else s("No timestamp found.")}this.parse=function(t,o){if(a(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[r]){if(r++,i.test(e[r])&&s("'--\x3e' not separated from timestamp by whitespace."),a(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&s("End timestamp is not greater than start timestamp."),i.test(e[r]),a(n),function(e,t){for(var i=e.split(n),r=[],a=0;a100)){s("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){s("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){s("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){s("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){s("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){s("Size must be a percentage.");continue}if(parseInt(c,10)>100){s("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){s("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){s("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){s("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else s("Invalid setting.")}}(e.substring(r),t),!0}else s("No valid timestamp separator found.");else s("No valid timestamp separator found.");else s("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[r])return t;s("Timestamp must not have trailing characters.")}},p=function(e,t,n,i){this.entities=i;var r=this,s=(e=e,0),a=function(e){"metadata"!=n&&t(e,s+1)};function l(){for(var t="data",n="",o="",l=[];void 0!=e[s-1]||0==s;){var c=e[s];if("data"==t)if("&"==c)o=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return a("Incorrect escape."),(e=o.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):r.entities[o]?n+=r.entities[o]:n+=o,["text",n]}if("&"==c)a("Incorrect escape."),n+=o,o=c;else if(/[a-z#0-9]/i.test(c))o+=c;else if(";"==c){let e;(e=o.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):r.entities[o+c]?n+=r.entities[o+c]:(e=Object.keys(i).find(e=>o.startsWith(e)))?n+=r.entities[e]+o.slice(e.length)+c:(a("Incorrect escape."),n+=o+";"),t="data"}else a("Incorrect escape."),n+=o+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)o=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)o&&l.push(o),o="",t="start tag annotation";else if("\n"==c)o&&l.push(o),o=c,t="start tag annotation";else if("."==c)o&&l.push(o),o="";else{if(">"==c||void 0==c)return">"==c&&s++,o&&l.push(o),["start tag",n,l,""];o+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,l,o=o.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];o+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["timestamp",n];n+=c}else a("Never happens.");s++}}this.parse=function(t,i){var r={children:[]},c=r,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function p(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[s];){var m=l();if("text"==m[0])c.children.push({type:"text",value:m[1],parent:c});else if("start tag"==m[0]){"chapters"==n&&a("Start tags not allowed in chapter title text.");var g=m[1];"v"!=g&&"lang"!=g&&""!=m[3]&&a("Only and can have an annotation."),"c"==g||"i"==g||"b"==g||"u"==g||"ruby"==g||"rt"==g&&"ruby"==c.name?u(m):"v"==g?(p("v")&&a(" cannot be nested inside itself."),u(m),c.value=m[3],m[3]||a(" requires an annotation.")):"lang"==g?(u(m),c.value=m[3]):a("Incorrect start tag.")}else if("end tag"==m[0])"chapters"==n&&a("End tags not allowed in chapter title text."),m[1]==c.name?c=c.parent:"ruby"==m[1]&&"rt"==c.name?c=c.parent.parent:a("Incorrect end tag.");else if("timestamp"==m[0]){"chapters"==n&&a("Timestamp not allowed in chapter title text.");var h=new d(m[1],a).parseTimestamp();void 0!=h&&((h<=t||h>=i)&&a("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=h&&a("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:h,parent:c}),f.push(h))}}for(;c.parent;)"v"!=c.name&&a("Required end tag missing."),c=c.parent;return function e(t){const n=o({},t);return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(r)}},m=function(){function e(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,r=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),r=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+r).padStart(2,"0")+"."+t}function t(n){for(var i="",r=0;r/g,">");else if("object"==s.type){if(i+="<"+s.name,s.classes)for(var a=0;a"}else"timestamp"==s.type?i+="<"+e(s.value)+">":i+="<"+s.value+">"}return i}function n(n){return(void 0!==n.id?n.id+"\n":"")+e(n.startTime)+" --\x3e "+e(n.endTime)+function(e){var t="";const n=Object.keys(f).filter(t=>e[t]!==f[t]);return n.includes("direction")&&(t+=" vertical:"+e.direction),n.includes("alignment")&&(t+=" align:"+e.alignment),n.includes("size")&&(t+=" size:"+e.size+"%"),(n.includes("lineAlign")||n.includes("linePosition"))&&(t+=" line:"+e.linePosition+(e.snapToLines?"":"%")+(e.lineAlign&&e.lineAlign!=f.lineAlign?","+e.lineAlign:"")),(n.includes("textPosition")||n.includes("positionAlign"))&&(t+=" position:"+e.textPosition+"%"+(e.positionAlign&&e.positionAlign!==f.positionAlign?","+e.positionAlign:"")),t}(n)+"\n"+t(n.tree.children)+"\n\n"}function i(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var r="WEBVTT\n\n";if(t)for(var s=0;s{if(t.closed)return;const s=URL.createObjectURL(new Blob([r],{type:"text/vtt"})),a=function(e){let n=!1,i=()=>{};const r={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(r.free),r}(s);if(t.closed)return;const c=i;let f,u,d=!1;i=a;try{if(f=e.option.subtitle,u=f.escape,t.closed||i!==a)return;f.escape=!1,d=!0;const r=l(o({},f),{url:s,type:"vtt",onVttLoad:n});if(t.closed||i!==a)return;const c=e.subtitle.init(r);Promise.resolve(c).catch(e=>{i===a&&(i=null,a.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(p){throw i===a&&(i=c,d&&!1===f.escape&&(f.escape=u)),a.free(),p}finally{c!==i&&(null==c||c.free()),a!==i&&a.free()}}}return function({subtitles:e=[]}){return t=>c(null,null,function*(){const{unescape:n,getExt:i,srtToVtt:r,assToVtt:s}=t.constructor.utils,a=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const r=new Promise(e=>{i=e});function s(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const a={get closed(){return t},own:e=>(t?s(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,r]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)s(t)}};if(!t){a.own(()=>e.off("destroy",a.dispose));try{e.on("destroy",a.dispose)}catch(o){throw a.dispose(),o}}return a}(t),f=g(t,a,n);let d=[];function p(e){a.closed||f(function(e){const t=[];for(const n of e)for(const e of n.cues)t.push(l(o({},e),{tree:l(o({},e.tree),{children:e.tree.children.map(e=>l(o({},e),{value:`
${e.value}
`}))})}));return(new m).serialize(t)}(e))}const h={name:"multipleSubtitles",tracks(e=[]){a.closed||p(e.map(e=>d.find(t=>t.name===e)))},reset(){p(d)}};try{if(a.closed)return h;const t=yield Promise.all(e.map(e=>function(e,t,n){return c(this,arguments,function*(e,{getExt:t,srtToVtt:n,assToVtt:i},r){if(r.closed)return;const s="function"===typeof AbortController?new AbortController:null,a=r.own(()=>null==s?void 0:s.abort());try{const a=e.url;if(r.closed)return;const o=yield r.wait(s?fetch(a,{signal:s.signal}):fetch(a));if(r.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=yield r.wait(o.arrayBuffer());if(r.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw null==s||s.abort(),o}finally{a()}})}(e,{getExt:i,srtToVtt:r,assToVtt:s},a)));return a.closed||(d=function(e,t){const n=new u;return e.map((e,i)=>{const r=n.parse(e,"metadata");return r.url=t[i].url,r.name=t[i].name,r})}(t,e),p(d)),h}catch(v){throw a.dispose(),v}})}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginMultipleSubtitles=t(),define(function(){return e.artplayerPluginMultipleSubtitles})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginMultipleSubtitles=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,n=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,a=(t,n,i)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[n]=i,o=(e,t)=>{for(var n in t||(t={}))r.call(t,n)&&a(e,n,t[n]);if(i)for(var n of i(t))s.call(t,n)&&a(e,n,t[n]);return e},l=(e,i)=>t(e,n(i)),c=(e,t,n)=>new Promise((i,r)=>{var s=e=>{try{o(n.next(e))}catch(t){r(t)}},a=e=>{try{o(n.throw(e))}catch(t){r(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(s,a);o((n=n.apply(e,t)).next())});var f={direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center"},u=function(e){e||(e={"&":"&","<":"<",">":">","&lrm":"\u200e","&rlm":"\u200f"," ":"\xa0"}),this.entities=e,this.parse=function(t,n){t=t.replace(/\0/g,"\ufffd");var i=Date.now(),r=0,s=t.split(/\r\n|\r|\n/),a=!1,o=[],l=[],c=[];function u(e,t){c.push({message:e,line:r+1,col:t})}var m=s[r],g=m.length,h="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(gb&&" "!==m[b]&&"\t"!==m[b])&&u('No valid signature. (File needs to start with "WEBVTT".)'),r++;""!=s[r]&&void 0!=s[r];){if(u("No blank line after the signature."),-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}for(;void 0!=s[r];){for(var y;!a&&""==s[r];)r++;if(!a&&void 0==s[r])break;y=Object.assign({},f,{id:"",startTime:0,endTime:0,pauseOnExit:!1,direction:"horizontal",snapToLines:!0,linePosition:"auto",lineAlign:"start",textPosition:"auto",positionAlign:"auto",size:100,alignment:"center",text:"",tree:null});var x=!0;if(-1==s[r].indexOf("--\x3e")){if(y.id=s[r],/^NOTE($|[ \t])/.test(y.id)){for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&u("Cannot have timestamp in a comment."),r++;continue}if(/^STYLE($|[ \t])/.test(y.id)){var T=[],w=!1;for(r++;""!=s[r]&&void 0!=s[r];)-1!=s[r].indexOf("--\x3e")&&(u("Cannot have timestamp in a style block."),w=!0),T.push(s[r]),r++;if(l.length){u("Style blocks cannot appear after the first cue.");continue}w||o.push(T.join("\n"));continue}if(""==s[++r]||void 0==s[r]){u("Cue identifier cannot be standalone.");continue}if(-1==s[r].indexOf("--\x3e")){x=!1,u("Cue identifier needs to be followed by timestamp.");continue}}a=!1;var O=new d(s[r],u),P=0;if(l.length>0&&(P=l[l.length-1].startTime),!x||O.parse(y,P)){for(r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){u("Blank line missing before cue."),a=!0;break}""!=y.text&&(y.text+="\n"),y.text+=s[r],r++}var A=new p(y.text,u,n,e);y.tree=A.parse(y.startTime,y.endTime),l.push(y)}else for(y=null,r++;""!=s[r]&&void 0!=s[r];){if(-1!=s[r].indexOf("--\x3e")){a=!0;break}r++}}return l.sort(function(e,t){return e.startTimet.startTime?1:e.endTime>t.endTime?-1:e.endTime2||parseInt(t,10)>59)&&(l="hours"),":"==e[r])if(r++,2==(n=o(/\d/)).length){if("hours"==l||":"==e[r]){if(":"!=e[r])return void s("No seconds found or minutes is greater than 59.");if(r++,2!=(i=o(/\d/)).length)return void s("Must be exactly two digits.")}else{if(2!=t.length)return void s("Must be exactly two digits.");i=n,n=t,t="0"}if("."==e[r])if(r++,3==(a=o(/\d/)).length)if(parseInt(n,10)>59)s("You cannot have more than 59 minutes.");else{if(!(parseInt(i,10)>59))return 60*parseInt(t,10)*60+60*parseInt(n,10)+parseInt(i,10)+parseInt(a,10)/1e3;s("You cannot have more than 59 seconds.")}else s("Milliseconds must be given in three digits.");else s('No decimal separator (".") found.')}else s("Must be exactly two digits.");else s("No time unit separator found.");else s("Timestamp must start with a character in the range 0-9.");else s("No timestamp found.")}this.parse=function(t,o){if(a(n),t.startTime=l(),void 0!=t.startTime)if(t.startTime"==e[r]){if(r++,i.test(e[r])&&s("'--\x3e' not separated from timestamp by whitespace."),a(n),t.endTime=l(),void 0!=t.endTime)return t.endTime<=t.startTime&&s("End timestamp is not greater than start timestamp."),i.test(e[r]),a(n),function(e,t){for(var i=e.split(n),r=[],a=0;a100)){s("Line position cannot be >100%.");continue}if(""===d||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==u){if(!["start","center","end"].includes(u)){s("Line alignment needs to be one of start, center or end");continue}t.lineAlign=u}t.snapToLines=!p,t.linePosition=parseFloat(d),parseFloat(d).toString()!==d&&(t.nonSerializable=!0)}else if("position"==l){if(/,/.test(c)){f=c.split(","),c=f[0];var m=f[1]}if("%"!=c[c.length-1]){s("Text position must be a percentage.");continue}if(parseInt(c,10)>100||parseInt(c,10)<0){s("Text position needs to be between 0 and 100%.");continue}if(""===(d=c.slice(0,c.length-1))||isNaN(d)||!isFinite(d)){s("Line position needs to be a number");continue}if(void 0!==m){if(!["line-left","center","line-right"].includes(m)){s("Position alignment needs to be one of line-left, center or line-right");continue}t.positionAlign=m}t.textPosition=parseFloat(d)}else if("size"==l){if("%"!=c[c.length-1]){s("Size must be a percentage.");continue}if(parseInt(c,10)>100){s("Size cannot be >100%.");continue}var g=c.slice(0,c.length-1);if(void 0===g||""===g||isNaN(g)){s("Size needs to be a number"),g=100;continue}if((g=parseFloat(g))<0||g>100){s("Size needs to be between 0 and 100%.");continue}t.size=g}else if("align"==l){var h=["start","center","end","left","right"];if(-1==h.indexOf(c)){s("Alignment can only be set to one of "+h.join(", ")+".");continue}t.alignment=c}else s("Invalid setting.")}}(e.substring(r),t),!0}else s("No valid timestamp separator found.");else s("No valid timestamp separator found.");else s("No valid timestamp separator found.")},this.parseTimestamp=function(){var t=l();if(void 0==e[r])return t;s("Timestamp must not have trailing characters.")}},p=function(e,t,n,i){this.entities=i;var r=this,s=(e=e,0),a=function(e){"metadata"!=n&&t(e,s+1)};function l(){for(var t="data",n="",o="",l=[];void 0!=e[s-1]||0==s;){var c=e[s];if("data"==t)if("&"==c)o=c,t="escape";else if("<"==c&&""==n)t="tag";else{if("<"==c||void 0==c)return["text",n];n+=c}else if("escape"==t){if("<"==c||void 0==c){let e;return a("Incorrect escape."),(e=o.match(/^&#([0-9]+)$/))?n+=String.fromCharCode(e[1]):r.entities[o]?n+=r.entities[o]:n+=o,["text",n]}if("&"==c)a("Incorrect escape."),n+=o,o=c;else if(/[a-z#0-9]/i.test(c))o+=c;else if(";"==c){let e;(e=o.match(/^&#(x?[0-9]+)$/))?n+=String.fromCharCode("0"+e[1]):r.entities[o+c]?n+=r.entities[o+c]:(e=Object.keys(i).find(e=>o.startsWith(e)))?n+=r.entities[e]+o.slice(e.length)+c:(a("Incorrect escape."),n+=o+";"),t="data"}else a("Incorrect escape."),n+=o+c,t="data"}else if("tag"==t)if("\t"==c||"\n"==c||"\f"==c||" "==c)t="start tag annotation";else if("."==c)t="start tag class";else if("/"==c)t="end tag";else if(/\d/.test(c))n=c,t="timestamp tag";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag","",[],""];n=c,t="start tag"}else if("start tag"==t)if("\t"==c||"\f"==c||" "==c)t="start tag annotation";else if("\n"==c)o=c,t="start tag annotation";else if("."==c)t="start tag class";else{if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,[],""];n+=c}else if("start tag class"==t)if("\t"==c||"\f"==c||" "==c)o&&l.push(o),o="",t="start tag annotation";else if("\n"==c)o&&l.push(o),o=c,t="start tag annotation";else if("."==c)o&&l.push(o),o="";else{if(">"==c||void 0==c)return">"==c&&s++,o&&l.push(o),["start tag",n,l,""];o+=c}else if("start tag annotation"==t){if(">"==c||void 0==c)return">"==c&&s++,["start tag",n,l,o=o.split(/[\u0020\t\f\r\n]+/).filter(function(e){if(e)return!0}).join(" ")];o+=c}else if("end tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["end tag",n];n+=c}else if("timestamp tag"==t){if(">"==c||void 0==c)return">"==c&&s++,["timestamp",n];n+=c}else a("Never happens.");s++}}this.parse=function(t,i){var r={children:[]},c=r,f=[];function u(e){c.children.push({type:"object",name:e[1],classes:e[2],children:[],parent:c}),c=c.children[c.children.length-1]}function p(e){for(var t=c;t;){if(t.name==e)return!0;t=t.parent}}for(;void 0!=e[s];){var m=l();if("text"==m[0])c.children.push({type:"text",value:m[1],parent:c});else if("start tag"==m[0]){"chapters"==n&&a("Start tags not allowed in chapter title text.");var g=m[1];"v"!=g&&"lang"!=g&&""!=m[3]&&a("Only and can have an annotation."),"c"==g||"i"==g||"b"==g||"u"==g||"ruby"==g||"rt"==g&&"ruby"==c.name?u(m):"v"==g?(p("v")&&a(" cannot be nested inside itself."),u(m),c.value=m[3],m[3]||a(" requires an annotation.")):"lang"==g?(u(m),c.value=m[3]):a("Incorrect start tag.")}else if("end tag"==m[0])"chapters"==n&&a("End tags not allowed in chapter title text."),m[1]==c.name?c=c.parent:"ruby"==m[1]&&"rt"==c.name?c=c.parent.parent:a("Incorrect end tag.");else if("timestamp"==m[0]){"chapters"==n&&a("Timestamp not allowed in chapter title text.");var h=new d(m[1],a).parseTimestamp();void 0!=h&&((h<=t||h>=i)&&a("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=h&&a("Timestamp must be greater than any previous timestamp."),c.children.push({type:"timestamp",value:h,parent:c}),f.push(h))}}for(;c.parent;)"v"!=c.name&&a("Required end tag missing."),c=c.parent;return function e(t){const n=o({},t);return t.children&&(n.children=t.children.map(e)),n.parent&&delete n.parent,n}(r)}},m=function(){function e(e){const t=("00"+1e3*(e-Math.floor(e)).toFixed(3)).slice(-3);let n=0,i=0,r=0;return e>=3600&&(n=Math.floor(e/3600)),i=Math.floor((e-3600*n)/60),r=Math.floor(e-3600*n-60*i),(n?n+":":"")+(""+i).padStart(2,"0")+":"+(""+r).padStart(2,"0")+"."+t}function t(n){for(var i="",r=0;r/g,">");else if("object"==s.type){if(i+="<"+s.name,s.classes)for(var a=0;a"}else"timestamp"==s.type?i+="<"+e(s.value)+">":i+="<"+s.value+">"}return i}function n(n){return(void 0!==n.id?n.id+"\n":"")+e(n.startTime)+" --\x3e "+e(n.endTime)+function(e){var t="";const n=Object.keys(f).filter(t=>e[t]!==f[t]);return n.includes("direction")&&(t+=" vertical:"+e.direction),n.includes("alignment")&&(t+=" align:"+e.alignment),n.includes("size")&&(t+=" size:"+e.size+"%"),(n.includes("lineAlign")||n.includes("linePosition"))&&(t+=" line:"+e.linePosition+(e.snapToLines?"":"%")+(e.lineAlign&&e.lineAlign!=f.lineAlign?","+e.lineAlign:"")),(n.includes("textPosition")||n.includes("positionAlign"))&&(t+=" position:"+e.textPosition+"%"+(e.positionAlign&&e.positionAlign!==f.positionAlign?","+e.positionAlign:"")),t}(n)+"\n"+t(n.tree.children)+"\n\n"}function i(e){return"STYLE\n"+e+"\n\n"}this.serialize=function(e,t){var r="WEBVTT\n\n";if(t)for(var s=0;s{if(t.closed)return;const s=URL.createObjectURL(new Blob([r],{type:"text/vtt"})),a=function(e){let n=!1,i=()=>{};const r={free(){n||(n=!0,i(),URL.revokeObjectURL(e))}};return i=t.own(r.free),r}(s);if(t.closed)return;const c=i;let f,u,d=!1;i=a;try{if(f=e.option.subtitle,u=f.escape,t.closed||i!==a)return;f.escape=!1,d=!0;const r=l(o({},f),{url:s,type:"vtt",onVttLoad:n});if(t.closed||i!==a)return;const c=e.subtitle.init(r);Promise.resolve(c).catch(e=>{i===a&&(i=null,a.free()),t.closed||console.warn("Failed to initialize multiple subtitles:",e)})}catch(p){throw i===a&&(i=c,d&&!1===f.escape&&(f.escape=u)),a.free(),p}finally{c!==i&&(null==c||c.free()),a!==i&&a.free()}}}return function({subtitles:e=[]}){return t=>c(null,null,function*(){const{unescape:n,getExt:i,srtToVtt:r,assToVtt:s}=t.constructor.utils,a=function(e){let t=Boolean(e.isDestroy);const n=new Set;let i;const r=new Promise(e=>{i=e});function s(e){try{e()}catch(t){console.warn("Failed to clean up multiple subtitles:",t)}}const a={get closed(){return t},own:e=>(t?s(e):n.add(e),()=>n.delete(e)),wait:e=>Promise.race([e,r]),dispose(){if(t)return;t=!0,i();const e=[...n].reverse();n.clear();for(const t of e)s(t)}};if(!t){a.own(()=>e.off("destroy",a.dispose));try{e.on("destroy",a.dispose)}catch(o){throw a.dispose(),o}}return a}(t),f=g(t,a,n);let d=[];function p(e){a.closed||f(function(e){const t=[];for(const n of e){const e=n;for(const n of e.cues)t.push(l(o({},n),{tree:l(o({},n.tree),{children:n.tree.children.map(t=>l(o({},t),{value:`
${t.value}
`}))})}))}return(new m).serialize(t)}(e))}const h={name:"multipleSubtitles",tracks(e=[]){a.closed||p(e.map(e=>d.find(t=>t.name===e)))},reset(){p(d)}};try{if(a.closed)return h;const t=yield Promise.all(e.map(e=>function(e,t,n){return c(this,arguments,function*(e,{getExt:t,srtToVtt:n,assToVtt:i},r){if(r.closed)return;const s="function"===typeof AbortController?new AbortController:null,a=r.own(()=>null==s?void 0:s.abort());try{const a=e.url;if(r.closed)return;const o=yield r.wait(s?fetch(a,{signal:s.signal}):fetch(a));if(r.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch multiple subtitles: HTTP ${o.status}`);const l=yield r.wait(o.arrayBuffer());if(r.closed)return;const c=new TextDecoder(e.encoding||"utf-8").decode(l);switch(e.type||t(e.url)){case"srt":return n(c);case"ass":return i(c);case"vtt":return c;default:return""}}catch(o){throw null==s||s.abort(),o}finally{a()}})}(e,{getExt:i,srtToVtt:r,assToVtt:s},a)));return a.closed||(d=function(e,t){const n=new u;return e.map((e,i)=>{const r=n.parse(e,"metadata");return l(o({},r),{url:t[i].url,name:t[i].name})})}(t,e),p(d)),h}catch(v){throw a.dispose(),v}})}}); diff --git a/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs b/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs index fe8eb5a22..06d31ac62 100644 --- a/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs +++ b/packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs @@ -853,14 +853,13 @@ function parseTracks(vtts, subtitles) { const parser = new WebVTTParser(); return vtts.map((vtt, index) => { const tree = parser.parse(vtt, "metadata"); - tree.url = subtitles[index].url; - tree.name = subtitles[index].name; - return tree; + return { ...tree, url: subtitles[index].url, name: subtitles[index].name }; }); } function serializeTracks(trees) { const cues = []; - for (const tree of trees) { + for (const selected of trees) { + const tree = selected; for (const cue of tree.cues) { cues.push({ ...cue, diff --git a/packages/artplayer-plugin-multiple-subtitles/src/index.js b/packages/artplayer-plugin-multiple-subtitles/src/index.ts similarity index 65% rename from packages/artplayer-plugin-multiple-subtitles/src/index.js rename to packages/artplayer-plugin-multiple-subtitles/src/index.ts index 7fca6c715..13ec9c6e6 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/index.js +++ b/packages/artplayer-plugin-multiple-subtitles/src/index.ts @@ -1,19 +1,21 @@ +import type Artplayer from 'artplayer' +import type { Option, Result, Track } from './types' import createLifetime from './lifetime' import { parseTracks, serializeTracks } from './merge' import createRenderer from './render' import loadVtt from './request' -export default function artplayerPluginMultipleSubtitles({ subtitles = [] }) { - return async (art) => { - const { unescape, getExt, srtToVtt, assToVtt } = art.constructor.utils +export default function artplayerPluginMultipleSubtitles({ subtitles = [] }: Option) { + return async (art: Artplayer): Promise => { + const { unescape, getExt, srtToVtt, assToVtt } = (art.constructor as typeof Artplayer).utils const lifetime = createLifetime(art) const render = createRenderer(art, lifetime, unescape) - let trees = [] - function setTracks(selected) { + let trees: Track[] = [] + function setTracks(selected: readonly (Track | undefined)[]): void { if (!lifetime.closed) render(serializeTracks(selected)) } - const result = { + const result: Result = { name: 'multipleSubtitles', tracks(names = []) { if (!lifetime.closed) @@ -29,7 +31,8 @@ export default function artplayerPluginMultipleSubtitles({ subtitles = [] }) { const vtts = await Promise.all(subtitles.map(option => loadVtt(option, { getExt, srtToVtt, assToVtt }, lifetime))) if (lifetime.closed) return result - trees = parseTracks(vtts, subtitles) + // A live completed request yields text; cancellation was checked above. + trees = parseTracks(vtts as string[], subtitles) setTracks(trees) return result } diff --git a/packages/artplayer-plugin-multiple-subtitles/src/lifetime.js b/packages/artplayer-plugin-multiple-subtitles/src/lifetime.ts similarity index 73% rename from packages/artplayer-plugin-multiple-subtitles/src/lifetime.js rename to packages/artplayer-plugin-multiple-subtitles/src/lifetime.ts index 232120f70..dca512d76 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/lifetime.js +++ b/packages/artplayer-plugin-multiple-subtitles/src/lifetime.ts @@ -1,11 +1,13 @@ -export default function createLifetime(art) { +import type { Cleanup, Lifetime, LifetimeHost } from './types' + +export default function createLifetime(art: LifetimeHost): Lifetime { let closed = Boolean(art.isDestroy) - const cleanups = new Set() - let cancel - const cancelled = new Promise((resolve) => { + const cleanups = new Set() + let cancel!: Cleanup + const cancelled = new Promise((resolve) => { cancel = resolve }) - function run(cleanup) { + function run(cleanup: Cleanup): void { try { cleanup() } @@ -13,7 +15,7 @@ export default function createLifetime(art) { console.warn('Failed to clean up multiple subtitles:', error) } } - const lifetime = { + const lifetime: Lifetime = { get closed() { return closed }, own(cleanup) { if (closed) diff --git a/packages/artplayer-plugin-multiple-subtitles/src/merge.js b/packages/artplayer-plugin-multiple-subtitles/src/merge.js deleted file mode 100644 index 2d90773a1..000000000 --- a/packages/artplayer-plugin-multiple-subtitles/src/merge.js +++ /dev/null @@ -1,30 +0,0 @@ -import { WebVTTParser, WebVTTSerializer } from './parser' - -export function parseTracks(vtts, subtitles) { - const parser = new WebVTTParser() - return vtts.map((vtt, index) => { - const tree = parser.parse(vtt, 'metadata') - tree.url = subtitles[index].url - tree.name = subtitles[index].name - return tree - }) -} - -export function serializeTracks(trees) { - const cues = [] - for (const tree of trees) { - for (const cue of tree.cues) { - cues.push({ - ...cue, - tree: { - ...cue.tree, - children: cue.tree.children.map(child => ({ - ...child, - value: `
${child.value}
`, - })), - }, - }) - } - } - return new WebVTTSerializer().serialize(cues) -} diff --git a/packages/artplayer-plugin-multiple-subtitles/src/merge.ts b/packages/artplayer-plugin-multiple-subtitles/src/merge.ts new file mode 100644 index 000000000..4fd1b42d7 --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/src/merge.ts @@ -0,0 +1,33 @@ +import type { Cue, WrappedNode } from './parser' +import type { Track, TrackOption } from './types' +import { WebVTTParser, WebVTTSerializer } from './parser' + +export function parseTracks(vtts: readonly string[], subtitles: readonly TrackOption[]): Track[] { + const parser = new WebVTTParser() + return vtts.map((vtt, index) => { + const tree = parser.parse(vtt, 'metadata') + // Options and completed downloads share the same Promise.all index. + return { ...tree, url: subtitles[index]!.url, name: subtitles[index]!.name } + }) +} + +export function serializeTracks(trees: readonly (Track | undefined)[]): string { + const cues: Cue[] = [] + for (const selected of trees) { + // Unknown names retain the historical TypeError at the property access below. + const tree = selected! + for (const cue of tree.cues) { + cues.push({ + ...cue, + tree: { + ...cue.tree, + children: cue.tree.children.map(child => ({ + ...child, + value: `
${child.value}
`, + })), + }, + }) + } + } + return new WebVTTSerializer().serialize(cues) +} diff --git a/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts b/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts new file mode 100644 index 000000000..64e1b6f17 --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts @@ -0,0 +1,56 @@ +export interface TextNode { + type: 'text' + value: string +} + +export interface ObjectNode { + type: 'object' + name: string + classes: string[] + value?: string + children: CueNode[] +} + +export interface TimestampNode { + type: 'timestamp' + value: number +} + +export type CueNode = TextNode | ObjectNode | TimestampNode +// The historical plugin wraps every top-level value, including timestamp nodes. +export type WrappedNode = TextNode | (Omit & { value: string }) | { type: 'timestamp', value: string } + +export interface Cue { + id: string + startTime: number + endTime: number + pauseOnExit: boolean + direction: 'horizontal' | 'rl' | 'lr' + snapToLines: boolean + linePosition: number | 'auto' + lineAlign: 'start' | 'center' | 'end' + textPosition: number | 'auto' + positionAlign: 'auto' | 'line-left' | 'center' | 'line-right' + size: number + alignment: 'start' | 'center' | 'end' | 'left' | 'right' + text: string + tree: { children: Node[] } + nonSerializable?: boolean +} + +export interface ParseResult { + cues: Cue[] + errors: { message: string, line: number, col: number | undefined }[] + styles: string[] + time: number +} + +// Private declaration for the vendored exports used by this package, not a new public API. +export declare class WebVTTParser { + constructor(entities?: Record) + parse(input: string, mode: 'metadata'): ParseResult +} + +export declare class WebVTTSerializer { + serialize(cues: readonly Cue[], styles?: readonly string[]): string +} diff --git a/packages/artplayer-plugin-multiple-subtitles/src/render.js b/packages/artplayer-plugin-multiple-subtitles/src/render.ts similarity index 69% rename from packages/artplayer-plugin-multiple-subtitles/src/render.js rename to packages/artplayer-plugin-multiple-subtitles/src/render.ts index 7ed0c8402..e2d57c0f6 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/render.js +++ b/packages/artplayer-plugin-multiple-subtitles/src/render.ts @@ -1,9 +1,12 @@ -export default function createRenderer(art, lifetime, unescape) { - let current = null - function own(url) { +import type { Subtitle, Utils } from 'artplayer' +import type { Lifetime, OwnedURL, RenderHost } from './types' + +export default function createRenderer(art: RenderHost, lifetime: Lifetime, unescape: Utils['unescape']): (vtt: string) => void { + let current: OwnedURL | null = null + function own(url: string): OwnedURL { let freed = false - let release = () => {} - const entry = { + let release: () => unknown = () => {} + const entry: OwnedURL = { free() { if (freed) return @@ -24,12 +27,13 @@ export default function createRenderer(art, lifetime, unescape) { if (lifetime.closed) return const previous = current - let option - let escape + let option: Subtitle | undefined + let escape: boolean | undefined let configured = false current = entry try { - option = art.option.subtitle + // ArtPlayer merges subtitle defaults; preserve errors for invalid custom hosts. + option = art.option.subtitle! escape = option.escape if (lifetime.closed || current !== entry) return @@ -52,8 +56,8 @@ export default function createRenderer(art, lifetime, unescape) { catch (error) { if (current === entry) { current = previous - if (configured && option.escape === false) - option.escape = escape + if (configured && option!.escape === false) + option!.escape = escape } entry.free() throw error diff --git a/packages/artplayer-plugin-multiple-subtitles/src/request.js b/packages/artplayer-plugin-multiple-subtitles/src/request.ts similarity index 53% rename from packages/artplayer-plugin-multiple-subtitles/src/request.js rename to packages/artplayer-plugin-multiple-subtitles/src/request.ts index 670ab54cc..4373dff25 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/request.js +++ b/packages/artplayer-plugin-multiple-subtitles/src/request.ts @@ -1,4 +1,6 @@ -export default async function loadVtt(option, { getExt, srtToVtt, assToVtt }, lifetime) { +import type { Converters, Lifetime, TrackOption } from './types' + +export default async function loadVtt(option: TrackOption, { getExt, srtToVtt, assToVtt }: Converters, lifetime: Lifetime): Promise { if (lifetime.closed) return const controller = typeof AbortController === 'function' ? new AbortController() : null @@ -7,16 +9,17 @@ export default async function loadVtt(option, { getExt, srtToVtt, assToVtt }, li const url = option.url if (lifetime.closed) return - const response = await lifetime.wait(controller ? fetch(url, { signal: controller.signal }) : fetch(url)) + // Keep native fetch coercion for historically optional URLs; do not add a default URL. + const response = await lifetime.wait(controller ? fetch(url!, { signal: controller.signal }) : fetch(url!)) if (lifetime.closed) return - if (response.ok === false) - throw new Error(`Failed to fetch multiple subtitles: HTTP ${response.status}`) - const buffer = await lifetime.wait(response.arrayBuffer()) + if (response!.ok === false) + throw new Error(`Failed to fetch multiple subtitles: HTTP ${response!.status}`) + const buffer = await lifetime.wait(response!.arrayBuffer()) if (lifetime.closed) return - const text = new TextDecoder(option.encoding || 'utf-8').decode(buffer) - switch (option.type || getExt(option.url)) { + const text = new TextDecoder(option.encoding || 'utf-8').decode(buffer!) + switch (option.type || getExt(option.url!)) { case 'srt': return srtToVtt(text) case 'ass': return assToVtt(text) case 'vtt': return text diff --git a/packages/artplayer-plugin-multiple-subtitles/src/types.ts b/packages/artplayer-plugin-multiple-subtitles/src/types.ts new file mode 100644 index 000000000..f73348463 --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/src/types.ts @@ -0,0 +1,35 @@ +import type Artplayer from 'artplayer' +import type { Subtitle, Utils } from 'artplayer' +import type legacyFactory from '../types/artplayer-plugin-multiple-subtitles' +import type { ParseResult } from './parser' + +export type LegacyOption = Parameters[0] +export type TrackOption = LegacyOption['subtitles'][number] +export interface Option { + subtitles?: TrackOption[] +} +export interface Result { + name: 'multipleSubtitles' + tracks: (names?: string[]) => void + reset: () => void +} +export interface Track extends ParseResult { + url: string | undefined + name: string | undefined +} +export type Converters = Pick +export type Cleanup = () => void +export interface Lifetime { + readonly closed: boolean + own: (cleanup: Cleanup) => () => boolean + wait: (value: Value | PromiseLike) => Promise + dispose: Cleanup +} +export type LifetimeHost = Pick +export interface RenderHost { + option: { subtitle?: Subtitle } + subtitle: { init: (option: Subtitle) => unknown } +} +export interface OwnedURL { + free: Cleanup +} diff --git a/packages/artplayer-plugin-multiple-subtitles/tsconfig.json b/packages/artplayer-plugin-multiple-subtitles/tsconfig.json new file mode 100644 index 000000000..d22a92187 --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "allowJs": false + }, + "include": [ + "src/**/*.ts", + "types/**/*.d.ts", + "../../types/assets.d.ts", + "../../test/types/multiple-subtitles-runtime.ts" + ] +} diff --git a/refactor/baselines/multiple-subtitles-runtime-types.json b/refactor/baselines/multiple-subtitles-runtime-types.json new file mode 100644 index 000000000..8541733a5 --- /dev/null +++ b/refactor/baselines/multiple-subtitles-runtime-types.json @@ -0,0 +1,6655 @@ +{ + "schemaVersion": 1, + "task": "PKG-MULTI-SUB-04", + "status": "runtime-types-checkpoint-validated", + "parentCommit": "beaa93d6cc796120018c6cc5f83eb19a1fec4990", + "checks": [ + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "source", + "tests": 31, + "file": "refactor/.cache/multiple-subtitles04-runtime-source.log", + "sha256": "1cd920b4cd9c861a4fe9cc8f68f6e2dc05dd2ea1199d75e4b9dcdb28dab0bce2", + "scope": "Includes one pure merge test against source; the remaining tests use the selected entry." + }, + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "main", + "tests": 31, + "file": "refactor/.cache/multiple-subtitles04-runtime-main.log", + "sha256": "6ded810318937bab7151fde178f843e913170a16d1006d54f262f6a6c8dbc00c", + "scope": "Includes one pure merge test against source; the remaining tests use the selected entry." + }, + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "legacy", + "tests": 31, + "file": "refactor/.cache/multiple-subtitles04-runtime-legacy.log", + "sha256": "f80e9a3af11a1a55d230e150d89dc75411f3fa75be43d91c2db22a3a95d339d9", + "scope": "Includes one pure merge test against source; the remaining tests use the selected entry." + }, + { + "command": "yarn test:multiple-subtitles", + "file": "refactor/.cache/multiple-subtitles04-runtime-package.log", + "sha256": "e4f19e9f02b57eb71c3c9e3d5b614029fc9b03d920c154ea9cdafd05f05f619a", + "tests": 252, + "pass": 252, + "fail": 0 + }, + { + "command": "node --test test/multiple-subtitles-failures.test.js refactor/scripts/multiple-subtitles-runtime-types.test.mjs", + "file": "refactor/.cache/multiple-subtitles04-runtime-types.log", + "sha256": "1c63bbc7f27c1f5cb909860026be3ac211b1f3d8ce03a28ab827bef9ba8c0f67", + "tests": 173, + "pass": 173, + "historicalFailures": 171, + "compilers": [ + "5.9.3", + "5.1.6" + ], + "negativeErrorsPerCompiler": 8 + }, + { + "command": "Explicit runtime source, declaration, fixture and runner lint", + "file": "refactor/.cache/multiple-subtitles04-runtime-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "command": "yarn build artplayer-plugin-multiple-subtitles", + "file": "refactor/.cache/multiple-subtitles04-runtime-build.log", + "sha256": "035148b38d31bc47ac7599dfd32b603128da4cd0fae52f95b111abba083d72cb" + }, + { + "command": "Actual ESM import", + "file": "refactor/.cache/multiple-subtitles04-runtime-esm.log", + "sha256": "223f8ad57505f9c8e7825dcc40afae18238a4944f02c4dfc17aabf225f517587", + "scope": "Default-only module shape, named factory and registrar; not installed-consumer acceptance." + }, + { + "command": "yarn ci:check", + "file": "refactor/.cache/multiple-subtitles04-runtime-ci.log", + "sha256": "b03a74c262e24aad576247a367f7edacc240d4f379f48536a70dbdd21670187c", + "tests": { + "unit": 1972, + "engineering": 14, + "baseline": 336, + "total": 2322 + }, + "strictProductionTypeScriptFiles": 352, + "warnings": [ + "Existing generated docs/assets/ts/artplayer.d.ts unused ts/no-namespace suppression.", + "Yarn Classic DEP0169 url.parse deprecation." + ] + } + ], + "sources": [ + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/index.ts", + "sha256": "19a9e9ee958017ab4c12b9478c10c335a2f74f8c64102f64d8150626b7fce735" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/lifetime.ts", + "sha256": "469cec6a9f87311aa433ad2d74925e39d56324b8136975e818388315200f1e58" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/merge.ts", + "sha256": "93ba280d5da6209cad64863e9c68188ed9033688afd1ac2cddcea61e91b44ecf" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts", + "sha256": "12fec1bc655c64f6467ffbbce473b60b30fa104856d15158ba39ad2b61a9730b" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/parser.js", + "sha256": "ba582074d57fa2d72880e6e4253fe14646787916b2b68e2a1e748e685cf96ab1" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/render.ts", + "sha256": "fc8df5637e7904fb4b21512ac22d80b580eca93f576e9f01a799418b3d9afc84" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/request.ts", + "sha256": "68ae50354de8d2f8ca32f77d79b7f406d19275e173fbdc5807a27b8b975e6759" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/types.ts", + "sha256": "a30a5b697302a909c02f26b3ff21aa1f561135b5458c27df9a67e1bab3b6c3e3" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/tsconfig.json", + "sha256": "d61dc548239fd2bff16fe9e27c6081b5f9e06355540ad0a4e93e4f1a8d023576" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/.npmignore", + "sha256": "771df7bdc177956bd2f3493875159425bf1c2dd11d7d7386cb494b07620c0786" + }, + { + "file": "test/types/multiple-subtitles-runtime.ts", + "sha256": "ca490df9f660d03a8d31425c41df7fff76601c7853f70325ee3c479deb71597d" + }, + { + "file": "refactor/scripts/multiple-subtitles-runtime-types.test.mjs", + "sha256": "6ca784caa2ddd7a2383bc3d73c5654a34c143eccdf5d5b51e3cb01b83634a8da" + }, + { + "file": "test/multiple-subtitles-failures.test.js", + "sha256": "9001d424cf5f3da443be7cf989cdbcf842211da77089822357c0fcb2db7667ff" + } + ], + "browser": [ + { + "command": "yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js", + "candidate": "source", + "file": "refactor/.cache/multiple-subtitles04-runtime-browser-source.json", + "sha256": "5cf035b17d3445d75a93058ecad6412c0cba791764c700e5a45cc2d66f31645a", + "resultsDirectory": "refactor/.cache/multiple-subtitles04-runtime-browser-source-results", + "stats": { + "startTime": "2026-09-13T03:09:25.051Z", + "duration": 19351.031000000003, + "expected": 30, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/28920307-fc19-4865-a195-be3009a79ec3", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/f65e28e7-3bcf-4074-b180-18a6e33173b7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9f4fcac0-5fea-49f4-a5aa-b63f10f919f6", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/52e6a1be-661c-488e-a46b-31ce78a3674a", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/28920307-fc19-4865-a195-be3009a79ec3", + "blob:http://127.0.0.1:8084/f3d37687-6968-4094-9bfe-73ffada67684", + "blob:http://127.0.0.1:8084/f65e28e7-3bcf-4074-b180-18a6e33173b7", + "blob:http://127.0.0.1:8084/73e1c677-fbbf-42cc-ab59-e430f34bfb16", + "blob:http://127.0.0.1:8084/9f4fcac0-5fea-49f4-a5aa-b63f10f919f6", + "blob:http://127.0.0.1:8084/6e49378c-1d74-4627-be71-cfaa83cb243c", + "blob:http://127.0.0.1:8084/52e6a1be-661c-488e-a46b-31ce78a3674a" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: net::ERR_FILE_NOT_FOUND" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/c3d4247c-602a-4fbe-87bd-f80f0aa01b85", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/c3d4247c-602a-4fbe-87bd-f80f0aa01b85", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/c3d4247c-602a-4fbe-87bd-f80f0aa01b85" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-error.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/48ed1fb3-4d8a-4b3f-890d-4db173439f34", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/f685c633-ebff-47f9-ad70-36358aaa5359", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/48ed1fb3-4d8a-4b3f-890d-4db173439f34", + "blob:http://127.0.0.1:8084/1c4daca6-2c93-4678-8583-71d2cb347f9c", + "blob:http://127.0.0.1:8084/f685c633-ebff-47f9-ad70-36358aaa5359" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/7ee35be4-3796-4bb4-ab0a-2c0de6a59964", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/6d6f0c5d-7b47-4c0e-a032-abab76d2d971", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b4478ee6-fa4a-48e1-8661-138aa7e7a701", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/98680a64-eaf0-460d-9b2f-502d55ed32fa", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7ee35be4-3796-4bb4-ab0a-2c0de6a59964", + "blob:http://127.0.0.1:8084/fb3f19a0-ecc9-477c-97de-c2a618c0e1d8", + "blob:http://127.0.0.1:8084/6d6f0c5d-7b47-4c0e-a032-abab76d2d971", + "blob:http://127.0.0.1:8084/ba711e9f-160e-4bd4-afc1-8622a50e129a", + "blob:http://127.0.0.1:8084/b4478ee6-fa4a-48e1-8661-138aa7e7a701", + "blob:http://127.0.0.1:8084/a3c74605-352b-4e7f-9867-028236b5202c", + "blob:http://127.0.0.1:8084/02fae238-d8a8-452f-b392-47c2b0c1f92e", + "blob:http://127.0.0.1:8084/98680a64-eaf0-460d-9b2f-502d55ed32fa" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: net::ERR_FILE_NOT_FOUND" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/61debde8-ade7-4a76-9c1f-0617b8b95b50", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/61debde8-ade7-4a76-9c1f-0617b8b95b50", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/857acf2e-8da4-42f0-879d-a64cc0614059", + "blob:http://127.0.0.1:8084/61debde8-ade7-4a76-9c1f-0617b8b95b50" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-error.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/a8183493-46e8-4c36-a34a-549317adad10", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/77e2efcc-8853-42c7-943a-5a414e360699", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a8183493-46e8-4c36-a34a-549317adad10", + "blob:http://127.0.0.1:8084/baf931f7-d0bb-4f03-8a9d-737bb70b8da1", + "blob:http://127.0.0.1:8084/ea6f399c-726c-4498-9935-c32778feaf56", + "blob:http://127.0.0.1:8084/77e2efcc-8853-42c7-943a-5a414e360699" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/1516e8c9-df1a-4c5c-87e2-ccbff1a88dff", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a415fe73-2e59-4cad-86e2-5fdd22326fd2", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3797f42d-64b7-4c0f-81b6-ffc913c4c9ac", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/05a9c64c-6c54-40b2-b782-1b6cc4fe82ad", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/1516e8c9-df1a-4c5c-87e2-ccbff1a88dff", + "blob:http://127.0.0.1:8084/13081ea8-4bbf-4f9f-955a-dbae705dd92b", + "blob:http://127.0.0.1:8084/a415fe73-2e59-4cad-86e2-5fdd22326fd2", + "blob:http://127.0.0.1:8084/9a5852f8-c56d-4ca9-8bf3-1bb060bc0db4", + "blob:http://127.0.0.1:8084/3797f42d-64b7-4c0f-81b6-ffc913c4c9ac", + "blob:http://127.0.0.1:8084/2c8bc936-c2d0-4a75-98c4-453bcf20aa46", + "blob:http://127.0.0.1:8084/05a9c64c-6c54-40b2-b782-1b6cc4fe82ad" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/904c82b2-4a75-4842-8f6b-3358746a1ca3", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/904c82b2-4a75-4842-8f6b-3358746a1ca3" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/fef6b18e-8bd1-4ed0-a924-df297a17e7b7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/0c7e28a8-b02a-423c-a72b-35d5ea21abdc", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/fef6b18e-8bd1-4ed0-a924-df297a17e7b7", + "blob:http://127.0.0.1:8084/804952c5-ab54-4d31-baee-84b634685b11", + "blob:http://127.0.0.1:8084/0c7e28a8-b02a-423c-a72b-35d5ea21abdc" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3953042f-4e9c-4223-8545-681ad8395caf", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ec6444b4-d419-4de0-bc36-a020c2406ff7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/eeafe764-5fd0-4b01-b14c-1162da362342", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9989a36c-19d5-41a2-8c3e-70130469c2b6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3953042f-4e9c-4223-8545-681ad8395caf", + "blob:http://127.0.0.1:8084/b9cb0f48-b658-4d81-9dec-fb418f2549ab", + "blob:http://127.0.0.1:8084/ec6444b4-d419-4de0-bc36-a020c2406ff7", + "blob:http://127.0.0.1:8084/7c115e69-8f39-45ad-b4ac-600fd841e58c", + "blob:http://127.0.0.1:8084/eeafe764-5fd0-4b01-b14c-1162da362342", + "blob:http://127.0.0.1:8084/8ab270eb-3441-4389-93d0-135cde717fe2", + "blob:http://127.0.0.1:8084/76bf5d11-9482-488c-a579-e15ea482612a", + "blob:http://127.0.0.1:8084/9989a36c-19d5-41a2-8c3e-70130469c2b6" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/4e0bca92-11fe-47df-9ef9-c2a210976b75", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/473c19a6-bdbc-4389-9972-0586088dbb54", + "blob:http://127.0.0.1:8084/4e0bca92-11fe-47df-9ef9-c2a210976b75" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/08e781c3-4aa5-4387-81db-6743f21a2a97", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b77ef9eb-754d-4f6f-9fdb-79c271ff2c91", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/08e781c3-4aa5-4387-81db-6743f21a2a97", + "blob:http://127.0.0.1:8084/5a609d98-9ce5-47ec-905a-513bf7b92c73", + "blob:http://127.0.0.1:8084/e156a950-de9a-415f-b5a7-00163120e050", + "blob:http://127.0.0.1:8084/b77ef9eb-754d-4f6f-9fdb-79c271ff2c91" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/475767d5-9f13-41ff-85a2-45a673c53704", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/49ec1680-9ccf-4b60-82b8-e010166a478f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c8a759e5-7661-4d1e-92fa-fada6a639f54", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/28514017-34d5-45ea-ba86-9c7831bd3b22", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/aa3485ea-843d-4494-a747-2020f9edd1eb", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/49ec1680-9ccf-4b60-82b8-e010166a478f", + "blob:http://127.0.0.1:8084/e890980f-48ed-4f3d-b5be-f68945d496fb", + "blob:http://127.0.0.1:8084/c8a759e5-7661-4d1e-92fa-fada6a639f54", + "blob:http://127.0.0.1:8084/52ff0f57-7780-435b-b6b0-1298c3e9a9c6", + "blob:http://127.0.0.1:8084/28514017-34d5-45ea-ba86-9c7831bd3b22", + "blob:http://127.0.0.1:8084/475767d5-9f13-41ff-85a2-45a673c53704", + "blob:http://127.0.0.1:8084/aa3485ea-843d-4494-a747-2020f9edd1eb" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/2e5c077f-0462-4f6e-8ba2-50fa7af04980", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/2e5c077f-0462-4f6e-8ba2-50fa7af04980", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/2e5c077f-0462-4f6e-8ba2-50fa7af04980" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/badc47e9-35c8-4061-be90-7edd28d2b5ec", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/0899339c-6e7e-4547-bcbe-4f73c3e35b4d", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/badc47e9-35c8-4061-be90-7edd28d2b5ec", + "blob:http://127.0.0.1:8084/e64f7839-f8aa-4849-8647-8b98979c0a58", + "blob:http://127.0.0.1:8084/0899339c-6e7e-4547-bcbe-4f73c3e35b4d" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/dca5ad90-ada4-44e8-82d1-ebda85fbcf0c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/acce5ced-2762-4c6e-90b8-340db07597b3", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/29f12399-b526-46af-a7b8-1583d77eb092", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e1be1ef1-6b7c-40dc-bf31-0f76e373450e", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/dca5ad90-ada4-44e8-82d1-ebda85fbcf0c", + "blob:http://127.0.0.1:8084/41a46147-6fd3-4769-a4cd-502c25f8b5ce", + "blob:http://127.0.0.1:8084/acce5ced-2762-4c6e-90b8-340db07597b3", + "blob:http://127.0.0.1:8084/ccc3fa9c-95d3-4f9b-8f0b-6c0d31c473cb", + "blob:http://127.0.0.1:8084/29f12399-b526-46af-a7b8-1583d77eb092", + "blob:http://127.0.0.1:8084/b29f6428-2e0f-4c1f-9216-1ed278c76db4", + "blob:http://127.0.0.1:8084/92522645-fa3f-4701-bb77-ffb77634efce", + "blob:http://127.0.0.1:8084/e1be1ef1-6b7c-40dc-bf31-0f76e373450e" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/cb481fb7-838c-4c79-ba52-770945974e98", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/cb481fb7-838c-4c79-ba52-770945974e98", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/4085be10-8467-48b1-8e19-5e808ad94463", + "blob:http://127.0.0.1:8084/cb481fb7-838c-4c79-ba52-770945974e98" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "3392acdc5b34bdd85e0ae82e22b95a829af9cdf6d3dd6a7dad8e7988cd8ac788" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/0c2d94dc-4f2b-48cc-9daf-5eabf91e0c80", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/1e6a79c3-8ddd-4a24-b48d-553a3513bb2a", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/0c2d94dc-4f2b-48cc-9daf-5eabf91e0c80", + "blob:http://127.0.0.1:8084/86aee3be-8ac2-4347-a960-73997d05ed6c", + "blob:http://127.0.0.1:8084/41bd41ac-d2bb-47c0-a15d-9addbb51de5a", + "blob:http://127.0.0.1:8084/1e6a79c3-8ddd-4a24-b48d-553a3513bb2a" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + } + ] + }, + { + "command": "yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js", + "candidate": "main", + "file": "refactor/.cache/multiple-subtitles04-runtime-browser-main.json", + "sha256": "e2329eb3ff4a32ebc849c7595c402769ad378d22360691d64199ec6c3437d95b", + "resultsDirectory": "refactor/.cache/multiple-subtitles04-runtime-browser-main-results", + "stats": { + "startTime": "2026-09-13T03:09:45.142Z", + "duration": 18614.318, + "expected": 30, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/8eaa56a2-2a02-4e0b-adb1-48a44724ce11", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/71aa81b5-a61d-4ccb-8739-6e376d38ae2d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/eb293f0a-c565-449c-bbd6-f86892712ab9", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ce461dcd-a8fe-4e96-8628-fae40195f2ff", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/8eaa56a2-2a02-4e0b-adb1-48a44724ce11", + "blob:http://127.0.0.1:8084/f4926db3-cf96-4aee-b768-be3a16473f79", + "blob:http://127.0.0.1:8084/71aa81b5-a61d-4ccb-8739-6e376d38ae2d", + "blob:http://127.0.0.1:8084/f1ae894c-9763-4970-9521-e1e3c8412641", + "blob:http://127.0.0.1:8084/eb293f0a-c565-449c-bbd6-f86892712ab9", + "blob:http://127.0.0.1:8084/48bf81b4-40ec-47ac-9a37-aa2048043a72", + "blob:http://127.0.0.1:8084/ce461dcd-a8fe-4e96-8628-fae40195f2ff" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: net::ERR_FILE_NOT_FOUND" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/bc1dff98-df87-45ff-99cf-b3ca203c589a", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/bc1dff98-df87-45ff-99cf-b3ca203c589a", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/bc1dff98-df87-45ff-99cf-b3ca203c589a" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-error.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/dd372ac0-13d2-41ba-8cfe-885573cd375d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/92634934-e76e-42c9-8409-da80dec70a7c", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/dd372ac0-13d2-41ba-8cfe-885573cd375d", + "blob:http://127.0.0.1:8084/fb344e6d-b8f8-4468-8a8a-0a623c99c1b0", + "blob:http://127.0.0.1:8084/92634934-e76e-42c9-8409-da80dec70a7c" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/8f8bc136-d109-4b59-bce7-e165d94db6d6", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/cbf02b4d-6637-4cb6-b478-e5b4d3e353fa", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e11921c7-40d0-4f9d-88ca-850ae0e4a8c9", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/d84d0c76-c801-4d29-965f-9bdd60db811c", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/8f8bc136-d109-4b59-bce7-e165d94db6d6", + "blob:http://127.0.0.1:8084/3221446a-6bf9-4639-a695-0debcc6f83f9", + "blob:http://127.0.0.1:8084/cbf02b4d-6637-4cb6-b478-e5b4d3e353fa", + "blob:http://127.0.0.1:8084/c8b56b37-613a-473a-a693-274dc476b6db", + "blob:http://127.0.0.1:8084/e11921c7-40d0-4f9d-88ca-850ae0e4a8c9", + "blob:http://127.0.0.1:8084/9b51feea-2453-48ed-88c1-271f6c9a039d", + "blob:http://127.0.0.1:8084/91717460-ee44-49bd-998c-919a79015a20", + "blob:http://127.0.0.1:8084/d84d0c76-c801-4d29-965f-9bdd60db811c" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: net::ERR_FILE_NOT_FOUND" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/9b416ce3-c485-4852-9fe2-8643aa68397c", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/9b416ce3-c485-4852-9fe2-8643aa68397c", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/158c33ae-9848-4318-b9f9-eb67372fe73b", + "blob:http://127.0.0.1:8084/9b416ce3-c485-4852-9fe2-8643aa68397c" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-error.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/a8a1ac06-240c-4ab8-89be-99439e9621d2", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b8c6d44a-be33-438c-85ac-1de6a836f557", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a8a1ac06-240c-4ab8-89be-99439e9621d2", + "blob:http://127.0.0.1:8084/9d749deb-420c-4777-9993-d0bc80b0e067", + "blob:http://127.0.0.1:8084/46e6e768-60fb-4b27-af65-1e731d4a24f3", + "blob:http://127.0.0.1:8084/b8c6d44a-be33-438c-85ac-1de6a836f557" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/34d19795-1c27-47e4-b316-8f61a5ae427c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b33ca566-40e5-4516-94c3-1b4cd9daef47", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9ad712c0-990a-48c6-b070-9bbfa4c6638c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c9a65bb9-dc45-409a-8da8-6c4e12739993", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/34d19795-1c27-47e4-b316-8f61a5ae427c", + "blob:http://127.0.0.1:8084/39452058-902e-43bf-92bf-ed651fcef8d2", + "blob:http://127.0.0.1:8084/b33ca566-40e5-4516-94c3-1b4cd9daef47", + "blob:http://127.0.0.1:8084/d2406819-0b18-4852-a1fa-157cdee3a341", + "blob:http://127.0.0.1:8084/9ad712c0-990a-48c6-b070-9bbfa4c6638c", + "blob:http://127.0.0.1:8084/9bbadc9b-04e0-47d6-9ec1-851479ef41af", + "blob:http://127.0.0.1:8084/c9a65bb9-dc45-409a-8da8-6c4e12739993" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/e61a996e-363c-44e5-b05f-c2e44717aa88", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/e61a996e-363c-44e5-b05f-c2e44717aa88" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/0447c1be-462b-46aa-a571-ea06e2192e7d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/33da3244-b9af-4394-a22a-0069cad1ad9e", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/0447c1be-462b-46aa-a571-ea06e2192e7d", + "blob:http://127.0.0.1:8084/2c21c11b-c536-4f2c-b4b5-6045f1e14938", + "blob:http://127.0.0.1:8084/33da3244-b9af-4394-a22a-0069cad1ad9e" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3ad03d94-86ff-4608-809c-bbaddedd3608", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/7b19253c-053a-45b1-94fa-aa1c70228302", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/138035d0-2374-4ae8-8c42-e4becbfb525f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/1f93e655-dcc3-422b-8a33-0283670a810c", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3ad03d94-86ff-4608-809c-bbaddedd3608", + "blob:http://127.0.0.1:8084/ba7ef28f-ef21-4a75-8c12-4bc767955609", + "blob:http://127.0.0.1:8084/7b19253c-053a-45b1-94fa-aa1c70228302", + "blob:http://127.0.0.1:8084/8fa84a7d-ca72-4cb2-9053-e49ca3ce0fcb", + "blob:http://127.0.0.1:8084/138035d0-2374-4ae8-8c42-e4becbfb525f", + "blob:http://127.0.0.1:8084/0183a633-2355-4a63-bab6-db1a503899c7", + "blob:http://127.0.0.1:8084/b557f3f6-bbf3-4d75-a568-a9d5be7dda0c", + "blob:http://127.0.0.1:8084/1f93e655-dcc3-422b-8a33-0283670a810c" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/5de93944-3afe-4814-87ef-e5398666d2e0", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7244af0c-e30f-46c8-809a-da7e3f2afec3", + "blob:http://127.0.0.1:8084/5de93944-3afe-4814-87ef-e5398666d2e0" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/cd9206c1-8979-4592-b3f5-922eac1283d3", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/8a44e53b-caee-4b22-bf2f-c396e3c2a893", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/cd9206c1-8979-4592-b3f5-922eac1283d3", + "blob:http://127.0.0.1:8084/126d3979-24d2-4168-856f-ed40e2fb5680", + "blob:http://127.0.0.1:8084/c8c618ce-e1ad-4fd6-8266-06240db1d746", + "blob:http://127.0.0.1:8084/8a44e53b-caee-4b22-bf2f-c396e3c2a893" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/34a0fba2-0fb8-4f8c-a8fd-12fbcf1057f9", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/f5185429-5b17-42df-ab55-393230464e19", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/cc4b65b2-a812-4b35-b9cd-f9e61152d795", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/0042c530-24c1-4a5b-94e7-ab776a1d3632", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/34a0fba2-0fb8-4f8c-a8fd-12fbcf1057f9", + "blob:http://127.0.0.1:8084/a42b49c7-de45-4be9-bac7-da0d2e233784", + "blob:http://127.0.0.1:8084/f5185429-5b17-42df-ab55-393230464e19", + "blob:http://127.0.0.1:8084/ef29e067-8f3a-4a9a-a99b-0bcfea2292cc", + "blob:http://127.0.0.1:8084/cc4b65b2-a812-4b35-b9cd-f9e61152d795", + "blob:http://127.0.0.1:8084/e1ec7a10-e523-49a9-aa2e-be3a9b3c6425", + "blob:http://127.0.0.1:8084/0042c530-24c1-4a5b-94e7-ab776a1d3632" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/673b0d92-8ca7-43b5-a437-b9c738512b4e", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/673b0d92-8ca7-43b5-a437-b9c738512b4e", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/673b0d92-8ca7-43b5-a437-b9c738512b4e" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/624c1669-77e9-44ee-b09f-732a20f323b3", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/18cb8193-fca8-4650-8f50-1a970dd244d0", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/624c1669-77e9-44ee-b09f-732a20f323b3", + "blob:http://127.0.0.1:8084/c89f1951-76c5-45df-a7f7-225769478e68", + "blob:http://127.0.0.1:8084/18cb8193-fca8-4650-8f50-1a970dd244d0" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/ec86d072-6050-45df-a553-652eb3857092", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/b2a4d698-3302-41ce-a80f-c20015b0cdc5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/d6226b94-8edd-431c-a0aa-8ec18dc6af48", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/498e75c5-47d7-481c-97c0-ebbf5d4e5c40", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/2ba2f611-7f8b-4c6b-8637-e84f2fe0caef", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/b2a4d698-3302-41ce-a80f-c20015b0cdc5", + "blob:http://127.0.0.1:8084/08ae17dc-3215-4d05-b761-c492f9d13d07", + "blob:http://127.0.0.1:8084/d6226b94-8edd-431c-a0aa-8ec18dc6af48", + "blob:http://127.0.0.1:8084/a6311d04-05cd-4b5d-a2b3-527d2dd2cfad", + "blob:http://127.0.0.1:8084/498e75c5-47d7-481c-97c0-ebbf5d4e5c40", + "blob:http://127.0.0.1:8084/ec86d072-6050-45df-a553-652eb3857092", + "blob:http://127.0.0.1:8084/f7d8bf80-a4ed-4007-bec5-e9aaeca8b62b", + "blob:http://127.0.0.1:8084/2ba2f611-7f8b-4c6b-8637-e84f2fe0caef" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/5fb56f78-c2a0-4ff7-9a42-1bda6278bb77", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/5fb56f78-c2a0-4ff7-9a42-1bda6278bb77", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/bfe56c6b-f8f5-495a-b3b9-67cc73bd57f7", + "blob:http://127.0.0.1:8084/5fb56f78-c2a0-4ff7-9a42-1bda6278bb77" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/5519736c-66a8-4b37-b4c1-7b09428449f6", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/80296de9-0937-4277-8227-b7c10764f9c5", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5519736c-66a8-4b37-b4c1-7b09428449f6", + "blob:http://127.0.0.1:8084/e17cf010-f261-4546-ae6c-3a5b2bd5a28c", + "blob:http://127.0.0.1:8084/5dc13801-99ca-43e4-a3f9-cc35e7b03e79", + "blob:http://127.0.0.1:8084/80296de9-0937-4277-8227-b7c10764f9c5" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + } + ] + }, + { + "command": "yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js", + "candidate": "legacy", + "file": "refactor/.cache/multiple-subtitles04-runtime-browser-legacy.json", + "sha256": "f74441638230a6a7c34b5be52c37ad8b8743979d98b1710f683cec1e3c66a837", + "resultsDirectory": "refactor/.cache/multiple-subtitles04-runtime-browser-legacy-results", + "stats": { + "startTime": "2026-09-13T03:10:04.462Z", + "duration": 18787.552000000003, + "expected": 30, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/c8fdfd5a-ce01-46c8-a2b8-d1a09ea23416", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/193b780b-df0b-4932-a92e-6ff19c300387", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a99df677-643d-43f3-a6ac-9e924c8ba7e7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b14a315d-38df-4c43-bab7-edb6f4dca202", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/c8fdfd5a-ce01-46c8-a2b8-d1a09ea23416", + "blob:http://127.0.0.1:8084/1cdf5cf1-b872-4b38-866b-21df443965d3", + "blob:http://127.0.0.1:8084/193b780b-df0b-4932-a92e-6ff19c300387", + "blob:http://127.0.0.1:8084/e9739237-166d-4da3-8e6a-48f21319c7db", + "blob:http://127.0.0.1:8084/a99df677-643d-43f3-a6ac-9e924c8ba7e7", + "blob:http://127.0.0.1:8084/61a99a27-8672-46fc-97de-b532a83eecde", + "blob:http://127.0.0.1:8084/b14a315d-38df-4c43-bab7-edb6f4dca202" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: net::ERR_FILE_NOT_FOUND" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/296f3462-2c12-4855-beea-5819341dddd7", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/296f3462-2c12-4855-beea-5819341dddd7", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/296f3462-2c12-4855-beea-5819341dddd7" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-error.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/b6e28bbe-5508-4d37-9575-be7ce886c7ed", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/19eabf98-cef1-4762-93cc-99dd9449c5bf", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/b6e28bbe-5508-4d37-9575-be7ce886c7ed", + "blob:http://127.0.0.1:8084/7618bff6-40fa-4594-99ca-034686a4ab50", + "blob:http://127.0.0.1:8084/19eabf98-cef1-4762-93cc-99dd9449c5bf" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/e060296d-b45d-4f0e-a9af-b8c2fa768b95", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/18a66aef-afa7-4b86-a87a-3899ef5cf80b", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a5f75dd3-5f2f-4d49-a7c2-a214f3c20ce3", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/6bfb9b3c-b2dd-4571-8df0-18b0536ae830", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/e060296d-b45d-4f0e-a9af-b8c2fa768b95", + "blob:http://127.0.0.1:8084/a0e012f4-5500-4dec-b8ff-cb6b65033bbe", + "blob:http://127.0.0.1:8084/18a66aef-afa7-4b86-a87a-3899ef5cf80b", + "blob:http://127.0.0.1:8084/88ea0462-7bb7-41fc-8fe0-86a78243e1c8", + "blob:http://127.0.0.1:8084/a5f75dd3-5f2f-4d49-a7c2-a214f3c20ce3", + "blob:http://127.0.0.1:8084/872c8748-6a01-45a1-8f68-804c75b510a6", + "blob:http://127.0.0.1:8084/bb0d65c8-a800-4396-96e6-4a52145279d0", + "blob:http://127.0.0.1:8084/6bfb9b3c-b2dd-4571-8df0-18b0536ae830" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: net::ERR_FILE_NOT_FOUND" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/a59ed1ac-4ea7-48a5-af12-dd0046d7e0b2", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/a59ed1ac-4ea7-48a5-af12-dd0046d7e0b2", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/2baed010-5f77-46c0-89fe-b7f1d77baa18", + "blob:http://127.0.0.1:8084/a59ed1ac-4ea7-48a5-af12-dd0046d7e0b2" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/multiple-error.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "chromium", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "153.0.8010.12", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/e8869478-d07c-4474-96be-d77b5895b3f5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/da2450d5-a32e-4c64-b9f3-715a4c07b175", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/e8869478-d07c-4474-96be-d77b5895b3f5", + "blob:http://127.0.0.1:8084/988d691d-6430-4363-b02f-c58db2508ff8", + "blob:http://127.0.0.1:8084/ba1634c1-3e68-4a1a-b2c8-3f561277d096", + "blob:http://127.0.0.1:8084/da2450d5-a32e-4c64-b9f3-715a4c07b175" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/a561a3b9-eda6-4ec8-9a50-55f9e2681880", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/27f63939-719b-4869-bd4c-789439b76d88", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/8f32936b-95e4-4704-b851-c99a060216d2", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9dc34211-ab73-4fe3-ad9b-15cbf5773e13", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/a561a3b9-eda6-4ec8-9a50-55f9e2681880", + "blob:http://127.0.0.1:8084/26c816f8-3df8-4cff-bb19-dcc44ed883bf", + "blob:http://127.0.0.1:8084/27f63939-719b-4869-bd4c-789439b76d88", + "blob:http://127.0.0.1:8084/d903bd62-2cf5-49e0-a7cc-c80d3a234ee0", + "blob:http://127.0.0.1:8084/8f32936b-95e4-4704-b851-c99a060216d2", + "blob:http://127.0.0.1:8084/2e1e1cc5-3dfb-4fd8-9b26-7e644ca66d17", + "blob:http://127.0.0.1:8084/9dc34211-ab73-4fe3-ad9b-15cbf5773e13" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/f10911f8-188c-4d25-a403-ddbd20fccf9e", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/f10911f8-188c-4d25-a403-ddbd20fccf9e" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/24098cf1-d5ab-4ba8-8b57-c57f1e39867d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/80faddfa-322b-4a13-9fe8-078f7ca82a68", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/24098cf1-d5ab-4ba8-8b57-c57f1e39867d", + "blob:http://127.0.0.1:8084/50450f00-c2ce-4629-81de-48634f4ecf30", + "blob:http://127.0.0.1:8084/80faddfa-322b-4a13-9fe8-078f7ca82a68" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/c06d47a7-f239-4f5d-bad8-6e34daaf591f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3ecc57b4-52e2-4102-8823-ca482d31b29f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a3477cfc-d4bf-42a8-b840-6c4cccfee8db", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/935a718c-f652-4622-bca1-ffc4bdc03afb", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c06d47a7-f239-4f5d-bad8-6e34daaf591f", + "blob:http://127.0.0.1:8084/85044327-7378-425c-b6ad-b32ca0e85893", + "blob:http://127.0.0.1:8084/3ecc57b4-52e2-4102-8823-ca482d31b29f", + "blob:http://127.0.0.1:8084/c748a782-e868-4c6e-985f-e5a3ff480dde", + "blob:http://127.0.0.1:8084/a3477cfc-d4bf-42a8-b840-6c4cccfee8db", + "blob:http://127.0.0.1:8084/3719a2f8-42c1-460d-864d-425fe756829e", + "blob:http://127.0.0.1:8084/ba05c3bb-5c59-4a7d-98df-da00ce295af8", + "blob:http://127.0.0.1:8084/935a718c-f652-4622-bca1-ffc4bdc03afb" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/a7e3c2de-a645-4852-8607-358e1e68e1b6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c3f594d0-5646-4661-a042-d7fad547874d", + "blob:http://127.0.0.1:8084/a7e3c2de-a645-4852-8607-358e1e68e1b6" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "firefox", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "155.0", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3f529a07-02f2-42bb-8387-e0d2cd472903", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/8c3c2b65-caf2-450a-89ce-5257029189d3", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3f529a07-02f2-42bb-8387-e0d2cd472903", + "blob:http://127.0.0.1:8084/6cd65ca6-a86f-4842-b1e3-5be9a33fbd5f", + "blob:http://127.0.0.1:8084/36a2f6c3-b0c3-409e-b5a7-47c312c1ea49", + "blob:http://127.0.0.1:8084/8c3c2b65-caf2-450a-89ce-5257029189d3" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/36dba21a-e990-465f-b397-0eba9cf6233f", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/bf4d1f24-3543-41fb-ae89-6ec2686c4d10", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/5822a7a9-7fa0-4fb0-b545-0f2bdf54ff1e", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/674d2400-b012-49ed-a6bc-f0810568ae33", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/f4e51c32-af51-42ce-8375-bdab97230544", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/bf4d1f24-3543-41fb-ae89-6ec2686c4d10", + "blob:http://127.0.0.1:8084/98ea0e49-152c-40e9-b436-2a76007a7290", + "blob:http://127.0.0.1:8084/5822a7a9-7fa0-4fb0-b545-0f2bdf54ff1e", + "blob:http://127.0.0.1:8084/6925b2c1-ec83-4228-adca-b48e0c9c8ed5", + "blob:http://127.0.0.1:8084/674d2400-b012-49ed-a6bc-f0810568ae33", + "blob:http://127.0.0.1:8084/36dba21a-e990-465f-b397-0eba9cf6233f", + "blob:http://127.0.0.1:8084/f4e51c32-af51-42ce-8375-bdab97230544" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/79a3a777-9453-4a6b-8b0c-730caa7fc57a", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/79a3a777-9453-4a6b-8b0c-730caa7fc57a", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/79a3a777-9453-4a6b-8b0c-730caa7fc57a" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/d54a5f65-5524-45c6-b3aa-fee9cafba3fd", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/fe91e86c-da25-4fae-bad5-88d7b9fb453f", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "http://127.0.0.1:8084/test/pattern.mp4", + "blob:http://127.0.0.1:8084/d54a5f65-5524-45c6-b3aa-fee9cafba3fd", + "blob:http://127.0.0.1:8084/e36502bd-2ccc-4f20-af05-088029eba3d4", + "blob:http://127.0.0.1:8084/fe91e86c-da25-4fae-bad5-88d7b9fb453f" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/12442794-2861-47bc-9eea-1c00172eb232", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/e8baedd0-2cc0-436a-b241-0789a66a4215", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c795f36b-0ea3-485b-90a1-e7c771fbfd93", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/42c3909f-104d-4dbd-9959-2b2dc309f36d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/076f04d6-69af-426a-a9b5-c45444f9ded9", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/e8baedd0-2cc0-436a-b241-0789a66a4215", + "blob:http://127.0.0.1:8084/dccc58fc-fdbd-4eb9-bd61-c3ce9a2e483e", + "blob:http://127.0.0.1:8084/c795f36b-0ea3-485b-90a1-e7c771fbfd93", + "blob:http://127.0.0.1:8084/1514e9ab-6731-4be9-b991-79e9a92e5aac", + "blob:http://127.0.0.1:8084/42c3909f-104d-4dbd-9959-2b2dc309f36d", + "blob:http://127.0.0.1:8084/12442794-2861-47bc-9eea-1c00172eb232", + "blob:http://127.0.0.1:8084/8a3e3338-9a8b-42ec-b2a3-721903af9bea", + "blob:http://127.0.0.1:8084/076f04d6-69af-426a-a9b5-c45444f9ded9" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-en.srt", + "/test/multiple-jp.srt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/multiple-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": { + "aborted": true + }, + "errors": [], + "requests": [ + "/test/multiple-held.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource" + ], + "failedRequests": [ + { + "url": "blob:http://127.0.0.1:8084/0a90cf97-7646-409b-9b4d-5ecd0afa23f1", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/0a90cf97-7646-409b-9b4d-5ecd0afa23f1", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/d1d4fe4d-34dc-4b57-aaeb-d1454029f480", + "blob:http://127.0.0.1:8084/0a90cf97-7646-409b-9b4d-5ecd0afa23f1" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-ready.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [ + "Failed to load resource: the server responded with a status of 404 (Not Found)" + ], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "probe": { + "initialized": [], + "revoked": [], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-error.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "project": "webkit", + "status": "passed", + "manifest": "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015", + "environment": { + "browser": "26.6", + "platform": "win32", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "events": [ + "video:loadedmetadata", + "ready", + "video:loadedmetadata", + "video:seeked", + "video:playing", + "video:pause", + "destroy" + ], + "media": { + "source": "http://127.0.0.1:8084/test/pattern.mp4?multiple-restart=1", + "width": 0, + "height": 0, + "duration": null, + "currentTime": 0 + } + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3281e94b-becf-461c-846f-6c235a246be4", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/db2d3f7b-851e-483c-9732-10b65d784808", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3281e94b-becf-461c-846f-6c235a246be4", + "blob:http://127.0.0.1:8084/00845945-3858-47a9-94ba-41f6c44f2a3d", + "blob:http://127.0.0.1:8084/2cd371e5-e588-4f79-b810-4970a781b797", + "blob:http://127.0.0.1:8084/db2d3f7b-851e-483c-9732-10b65d784808" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + } + ] + } + ], + "manifests": { + "29a2e98ce35bc23aff2d9b1b325fd4a7148b42f2c0657a8c8022e6ddb5df9015": { + "kind": "artplayer-browser-test", + "resources": { + "/candidate/artplayer.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + }, + "/compiled/artplayer.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073, + "aliasOf": "/candidate/artplayer.js" + }, + "/uncompiled/artplayer/index.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073, + "aliasOf": "/candidate/artplayer.js" + }, + "/candidate/artplayer-plugin-chapter.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529 + }, + "/compiled/artplayer-plugin-chapter.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529, + "aliasOf": "/candidate/artplayer-plugin-chapter.js" + }, + "/uncompiled/artplayer-plugin-chapter/index.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529, + "aliasOf": "/candidate/artplayer-plugin-chapter.js" + }, + "/published/artplayer.js": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "/published/artplayer-plugin-chapter.js": { + "source": { + "kind": "npm-release", + "version": "1.1.0", + "integrity": "sha512-ji66Lr2K2S+OjFMuMby3yq+pefWlOC51Sbpop/DIr2N6w3F0smrF+YZbshN1Dy8/Tx1TYpT7N6WkkZWy5Jix1w==", + "member": "package/dist/artplayer-plugin-chapter.js" + }, + "sha256": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "bytes": 4973 + }, + "/test/pattern.mp4": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/pattern.mp4" + }, + "sha256": "0ad1d7ad286aec6e0fa9e27fc1e487e7277df2e603e30040fb942365763d0233", + "bytes": 251538 + }, + "/test/pattern.webm": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/pattern.webm" + }, + "sha256": "9d3c2b3dec9581a599de2d6732992ef7846790e0d1368e6e131bd339b39e984f", + "bytes": 162073 + }, + "/published-4.5.5/artplayer.js": { + "source": { + "kind": "npm-release", + "version": "4.5.5", + "integrity": "sha512-HeAKbe3od9ZgFQVSBCg5FlixJsefDEBC8txVG19yZgvzlcJuiurxPlasap+GHuex/ftG+jmSXnTRtuNrTki2Kw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "3467e626ca29d2f7884e75eb75b8b90de74a25761ffa2e272576c9b5a98fa0a4", + "bytes": 153582 + }, + "/published-5.1.7/artplayer.js": { + "source": { + "kind": "npm-release", + "version": "5.1.7", + "integrity": "sha512-N/QdNlTwsIHUsWOHMPj0pq97v3qmgXZHfhy1q7I+8YN3/XakJiJJyVJgGaeBjU+y/fUIrzBhwpBaX+NFwK9/qw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "484ccc3f22446ec6fd0dd8b8a5602c740b601d55097a1ae043262d92aab35357", + "bytes": 155940 + }, + "/published-4.5.9/artplayer.js": { + "source": { + "kind": "npm-release", + "version": "4.5.9", + "integrity": "sha512-x5JzGdXkw4GFdztvPkVnm8TOWrGDcvGKKMPb9wuP6UfyftlLM3UQctmmalfAMnQSxugJwq2Kb9ix9OG2SgbJJA==", + "member": "package/dist/artplayer.js" + }, + "sha256": "09a9f09e3a64a02ca7c27af710d7254f886f0bfb09fd54bf3a728ce863e5a497", + "bytes": 156408 + }, + "/test/audio-tone.m4a": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/audio-tone.m4a" + }, + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "/test/thumbnail-pattern.mp4": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/thumbnail-pattern.mp4" + }, + "sha256": "4c977be04d97f6ac1342ac4959b8e61678f390d88a088430e4d0200137a03d1c", + "bytes": 752916 + }, + "/test/auto-thumbnail-timeline.mp4": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/auto-thumbnail-timeline.mp4" + }, + "sha256": "10370156cffada462d3ea00aae0caadd62752369cd6538cc55248b3ddd7bf994", + "bytes": 8602 + }, + "/test/declaration-cues.vtt": { + "source": { + "kind": "test-subtitles", + "file": "test/browser/media/declaration-cues.vtt" + }, + "sha256": "5d149d4e641ce41cbb83376835c03daf1fb89f3b9f568fa59e064d51e2b57704", + "bytes": 54 + }, + "/test/thumbnail-grid.svg": { + "source": { + "kind": "test-thumbnail-grid", + "file": "test/browser/media/thumbnail-grid.svg" + }, + "sha256": "bb3f1ec9895d37def77ca4b192d3cd73577a2668558cc99de1588f511d65d3e6", + "bytes": 14969 + }, + "/test/legacy-safe-area.js": { + "source": { + "kind": "frozen-own-source", + "file": "test/helpers/legacy-safe-area.js", + "commit": "ccf77c4e" + }, + "sha256": "ce0368618ef6363b78271b8bcc7973abd0478e797cc464ddd912929e6dfa501a", + "bytes": 795 + }, + "/test/iframe-boundary-child.html": { + "source": { + "kind": "iframe-fixture", + "file": "test/browser/iframe-boundary-child.html" + }, + "sha256": "4721de185f606de7d576e71395127257e476765455e2a947ded79df4ebed901a", + "bytes": 1451 + }, + "/assets/sample/video.mp4": { + "source": { + "kind": "docs-media", + "file": "docs/assets/sample/video.mp4" + }, + "sha256": "30a2de667ba267b6bc605edcee1c4bd728c048ed76f500ea0e73d1f1a6f8f980", + "bytes": 5818706 + }, + "/test/performance.js": { + "source": { + "kind": "adapted-baseline-fixture", + "file": "refactor/fixtures/performance.js" + }, + "sha256": "2318f39bdb5f3a3348d755cb4335907ae4adc944be3a435839c1bba201e81500", + "bytes": 8994 + } + } + } + }, + "unchangedPublicDeclaration": { + "file": "packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts", + "sha256": "ab5dae1d0bdfcf2515cb842b1d465edf21dd195fe2fc5b0cfe351a8c06eb965c" + }, + "package": { + "file": "refactor/.cache/multiple-subtitles04-runtime-package.tgz", + "sha256": "20be9dd9981157b91f27749fc2c85b196b317c38672fde130361e2c6ad81bd07", + "manifest": { + "name": "artplayer-plugin-multiple-subtitles", + "version": "1.2.0", + "description": "multiple subtitles plugin for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + ".": { + "types": "./types/artplayer-plugin-multiple-subtitles.d.ts", + "import": "./dist/artplayer-plugin-multiple-subtitles.mjs", + "require": "./dist/artplayer-plugin-multiple-subtitles.js" + }, + "./legacy": { + "types": "./types/artplayer-plugin-multiple-subtitles.d.ts", + "import": "./dist/artplayer-plugin-multiple-subtitles.legacy.js", + "require": "./dist/artplayer-plugin-multiple-subtitles.legacy.js" + } + }, + "main": "./dist/artplayer-plugin-multiple-subtitles.js", + "module": "./dist/artplayer-plugin-multiple-subtitles.mjs", + "types": "./types/artplayer-plugin-multiple-subtitles.d.ts", + "legacy": "./dist/artplayer-plugin-multiple-subtitles.legacy.js", + "browserslist": "last 1 Chrome version" + }, + "members": { + "package/ARCHITECTURE.md": "52839537a608ac3f99e6fe31acd7e5b52bd6f9c8aa516e5e1c18d7479a8aef59", + "package/README.md": "6b969577a9a842aded237d75de7b502e5a589b93ba6aca1f51d6d64ff8cc5c47", + "package/THIRD_PARTY_NOTICES": "50755f38458d41151c7f0b1675c744aa365f0510dc11f53832691ef5c14ce6b6", + "package/dist/artplayer-plugin-multiple-subtitles.js": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87", + "package/dist/artplayer-plugin-multiple-subtitles.legacy.js": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712", + "package/dist/artplayer-plugin-multiple-subtitles.mjs": "29a761b9f788562997f34543fd0890ec180e6f93e764e640c0d05362512c0054", + "package/package.json": "77c93bc94154db46bdc2b2a3d02a03421ea0a49af54728bead91896dfae1092a", + "package/types/artplayer-plugin-multiple-subtitles.d.ts": "ab5dae1d0bdfcf2515cb842b1d465edf21dd195fe2fc5b0cfe351a8c06eb965c" + } + }, + "artifacts": [ + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js", + "sha256": "0e9d448a551145b81ea411971be95e5ad7ddfeafcd259c84c5bdcb67dfbbea87", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.js" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js", + "sha256": "c34ead456e5eadf753d82e304294fe5ef2ff1a4700d7b729c511f823f4109712", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs", + "sha256": "29a761b9f788562997f34543fd0890ec180e6f93e764e640c0d05362512c0054", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.mjs" + } + ], + "inlineTimestamp": [ + { + "implementation": "published-1.2.0-main", + "input": "WEBVTT\n\n00:00.000 --> 00:08.000\nBefore <00:02.000> after\n", + "output": "WEBVTT\n\n\n00:00.000 --> 00:08.000\n<div class=\"art-subtitle-a\">Before </div><div class=\"art-subtitle-a\"> after</div>\n\n" + }, + { + "implementation": "candidate", + "input": "WEBVTT\n\n00:00.000 --> 00:08.000\nBefore <00:02.000> after\n", + "output": "WEBVTT\n\n\n00:00.000 --> 00:08.000\n<div class=\"art-subtitle-a\">Before </div><div class=\"art-subtitle-a\"> after</div>\n\n" + } + ], + "remaining": "This checkpoint does not finish task 04. Public synchronous declarations, historical module forms and installed consumer compatibility remain. Task 07 fixes the newly reproduced inline timestamp corruption before task 05. Physical Safari/mobile, full supported cores, native ASS/encodings and editor/consumer coverage remain 05/06." +} diff --git a/refactor/changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md b/refactor/changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md new file mode 100644 index 000000000..315954cf8 --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md @@ -0,0 +1,45 @@ +# PKG-MULTI-SUB-04 严格运行时 TS 检查点 + +五个自有 JS 模块迁为 TS,新增 types.ts 描述真实 Promise、轨道、生命周期、 +converter 和宿主安装边界。parser.js 原始 vendor 执行体保持;parser.d.ts 区分文本、 +标签和数字时间戳节点,描述 cue settings、错误/样式结果及序列化输入。 +分包 tsconfig 使用 strict/noUncheckedIndexedAccess/allowJs=false,既有浏览器目标不变。 + +## 兼容与断言 + +- 内部 TrackOption 复用旧公开配置字段,内部 Option 允许运行时已有的 `{}`;工厂参数 + 本身仍必需。真实返回 Promise,tracks/reset 明确 void。 +- 公开 types、导出路径和版本尚未改动。旧同步声明与真实异步结果的冲突保留为编译负例; + 04 未完成,后续必须验证旧提取/替换、模块形式及安装消费者再处理公开入口。 +- `(art.constructor as typeof Artplayer)` 只恢复构造器静态类型;可选 URL 的 `!` 保留 + 原生 fetch 的旧值转换,不偷偷加默认 URL;关闭状态检查后的 `!`/string[] 对应取消边界。 +- Promise.all 索引与选项索引配对。未知名称仍在 cue 属性读取时抛 TypeError,不能因 + 类型严格化过滤掉无效项。宿主默认 subtitle 的断言也不新增运行时默认。 +- parseTracks 返回拥有 url/name 的明确新对象,其 cues/errors/styles 保持相同引用; + 没有改变 cue 输出、选择顺序和生命周期策略。 + +## 类型检查发现的独立缺陷 + +旧包装逻辑把所有顶层节点的 value 都改成 div 字符串,包括应该保留数字的时间戳。 +实际已发布 1.2.0 和本次候选对 `Before <00:02.000> after` 都输出 ``。 +九份历史输入新增复现,全部确认同一缺陷;原有 02 验证是当时 162 项,本次历史失败 +文件扩为 171 项,不改写旧报告数字。实际探针输入/输出写入本次验证记录。 + +新增 PKG-MULTI-SUB-07 专门修复并验证内嵌时间戳的序列化,作为 05 的前置;风险保持 +open。本次 WrappedNode 明确表达这个历史错误,不把类型通过解释为字幕内容已正确。 +下一步先处理 07,再继续 04 的公开声明;修复需同时更新旧/新差异和原生显示证据。 + +## 验证与发布边界 + +TS 5.9.3/5.1.6 严格正例通过,移除 expect-error 后各 8 个错误;注册返回、选择名称、 +格式、只读关闭状态、数字时间戳、序列化返回和旧同步替换均有明确约束。 +源码/main/legacy 的既有 31 项正常/资源/转换测试分别重跑;三入口原生五场景各 30 项。 +vendor 没有机械 TS 改写,不计为已迁移生产 TS。实际结果与限制见 +[验证记录](../baselines/multiple-subtitles-runtime-types.json)。 + +正常构建更新三份 dist 及 docs/compiled。新增 tsconfig 加入 .npmignore,并用实际 +yarn pack 验证自有源码、私有 parser 声明和 tsconfig 未泄漏;公开声明/入口原样保留。 +最终本包 252 项、完整 CI 2322 项通过(1972 单元、14 工程、336 基线),严格生产 +TS 增至 352 文件。三入口各 31 项及原生共 90 项通过,状态更新后另有 9 项元数据 +检查通过。新增源文件/声明/fixture/runner 显式 lint 通过;全量 lint 保留既有生成声明警告。 +无新依赖,未推送或发布。本次为 04 的独立本地检查点提交,任务继续 doing。 diff --git a/refactor/plan.md b/refactor/plan.md index ffbc36d40..73eed1e74 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -2,9 +2,9 @@ > 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。 -基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。 +基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 223 项,范围 22 个包及工作区/示例。 -状态:todo 89 / doing 13 / blocked 0 / done 120 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 89 / doing 14 / blocked 0 / done 120 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -28,7 +28,7 @@ | artplayer-plugin-document-pip | 1.1.0 | PKG-DPIP-01, PKG-DPIP-02, PKG-DPIP-03, PKG-DPIP-04, PKG-DPIP-05, PKG-DPIP-06 | | artplayer-plugin-hls-control | 1.1.0 | PKG-HLS-01, PKG-HLS-02, PKG-HLS-03, PKG-HLS-04, PKG-HLS-SDK-01, PKG-HLS-05, PKG-HLS-06 | | artplayer-plugin-jassub | 1.1.0 | PKG-JASSUB-01, PKG-JASSUB-02, PKG-JASSUB-03, PKG-JASSUB-04, PKG-JASSUB-05, PKG-JASSUB-06 | -| artplayer-plugin-multiple-subtitles | 1.2.0 | PKG-MULTI-SUB-01, PKG-MULTI-SUB-02, PKG-MULTI-SUB-03, PKG-MULTI-SUB-04, PKG-MULTI-SUB-05, PKG-MULTI-SUB-06 | +| artplayer-plugin-multiple-subtitles | 1.2.0 | PKG-MULTI-SUB-01, PKG-MULTI-SUB-02, PKG-MULTI-SUB-03, PKG-MULTI-SUB-04, PKG-MULTI-SUB-07, PKG-MULTI-SUB-05, PKG-MULTI-SUB-06 | | artplayer-plugin-vast | 1.2.0 | PKG-VAST-01, PKG-VAST-02, PKG-VAST-03, PKG-VAST-04, PKG-VAST-05, PKG-VAST-06 | | artplayer-plugin-vtt-thumbnail | 1.1.0 | PKG-VTT-THUMB-01, PKG-VTT-THUMB-02, PKG-VTT-THUMB-03, PKG-VTT-THUMB-04, PKG-VTT-THUMB-05, PKG-VTT-THUMB-06 | | artplayer-proxy-canvas | 1.1.0 | PKG-AMBILIGHT-PROXY-01, PKG-CANVAS-01, PKG-CANVAS-02, PKG-CANVAS-03, PKG-CANVAS-04, PKG-CANVAS-05, PKG-CANVAS-06, PKG-FACTORY-01 | @@ -219,8 +219,9 @@ | PKG-MULTI-SUB-01 | artplayer-plugin-multiple-subtitles
核对包契约与历史用法 | BASE-05 | subtitles/onParser、multipleSubtitles 名称及实际返回方法 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结;接续 VENDOR-03,定位 WebVTT parser 来源/修订及 CC0 头,保留解析/序列化行为 | H | done | | PKG-MULTI-SUB-02 | artplayer-plugin-multiple-subtitles
建立特有行为与错误测试 | PKG-MULTI-SUB-01, ENG-03, ENG-05 | VTT/SRT/ASS、编码/重叠 cue/排序/空轨/失败与切换 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | | PKG-MULTI-SUB-03 | artplayer-plugin-multiple-subtitles
整理内部职责与资源 | PKG-MULTI-SUB-02, CORE-15 | 解析与合并/获取/渲染分离,过期请求及对象 URL 清理 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | -| PKG-MULTI-SUB-04 | artplayer-plugin-multiple-subtitles
迁移自有源码和公开类型 | PKG-MULTI-SUB-03, ENG-04, ENG-06, CORE-07 | 准确 parser/cue/tree/result 类型与真实异步形状 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | -| PKG-MULTI-SUB-05 | artplayer-plugin-multiple-subtitles
验证新旧核心和组合 | PKG-MULTI-SUB-04, CORE-22 | 与核心字幕/偏移/全屏组合,旧名称调用不变 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | +| PKG-MULTI-SUB-04 | artplayer-plugin-multiple-subtitles
迁移自有源码和公开类型 | PKG-MULTI-SUB-03, ENG-04, ENG-06, CORE-07 | 准确 parser/cue/tree/result 类型与真实异步形状 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | doing | +| PKG-MULTI-SUB-07 | artplayer-plugin-multiple-subtitles
修复字幕内嵌时间戳序列化 | PKG-MULTI-SUB-03 | 保留数字时间戳节点、修正无效 NaN 输出、明确旧/新字幕输出差异 | 旧九份实现可复现,候选有效内嵌时间戳及文字/CSS/原生字幕通过,公开工厂与选择接口不变 | H | todo | +| PKG-MULTI-SUB-05 | artplayer-plugin-multiple-subtitles
验证新旧核心和组合 | PKG-MULTI-SUB-04, CORE-22, PKG-MULTI-SUB-07 | 与核心字幕/偏移/全屏组合,旧名称调用不变 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | | PKG-MULTI-SUB-06 | artplayer-plugin-multiple-subtitles
验证分发并同步文档 | PKG-MULTI-SUB-05, ENG-07 | multiple.subtitles.js、parser 来源许可及打包边界 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | ## 5 包迁移:artplayer-plugin-jassub @@ -514,6 +515,7 @@ - PKG-MULTI-SUB-01: [记录](baselines/multiple-subtitles-release.json) [记录](baselines/multiple-subtitles-contract.md) [记录](baselines/multiple-subtitles-contract-validation.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-01-contract.md) - PKG-MULTI-SUB-02: [记录](baselines/multiple-subtitles-failures.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-02-failures.md) - PKG-MULTI-SUB-03: [记录](baselines/multiple-subtitles-resources.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-03-resources.md) +- PKG-MULTI-SUB-04: [记录](baselines/multiple-subtitles-runtime-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md) - PKG-ADS-01: [记录](baselines/ads-release.json) [记录](baselines/ads-contract.md) [记录](baselines/ads-contract-validation.json) [记录](changes/2026-09-12-PKG-ADS-01-contract.md) [记录](scripts/ads-contract.test.mjs) - PKG-ADS-02: [记录](changes/2026-09-12-PKG-ADS-02-tests.md) [记录](ads-validation.md) [记录](baselines/ads-validation.json) - PKG-ADS-03: [记录](changes/2026-09-12-PKG-ADS-03-lifecycle.md) [记录](ads-validation.md) [记录](baselines/ads-lifecycle-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index 5c9cff4bf..507491644 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,20 @@ # 进度与证据 +## PKG-MULTI-SUB-04 严格运行时 TS 检查点(doing) + +五个自有模块迁为 TS,新增明确的 Option/Promise Result/Track/host/lifetime 类型; +parser.js 保持原 vendor,私有 parser.d.ts 区分 cue 与节点,分包 allowJs=false。 +TS 5.9.3/5.1.6 严格正例通过,移除标记后各 8 个负例准确报错;公开旧声明未改。 +源码/main/legacy 各 31 项通过,三入口原生各 30 项共 90 项通过;实际构建/ESM 导入通过。 +实际 yarn pack 检查入口、成员及产物字节,私有源码、parser 声明和 tsconfig 未泄漏。 +本包 252 项通过,完整 CI 2322 项通过(1972 单元、14 工程、336 基线), +严格生产 TS 增至 352 文件。见[检查点](changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md) +和[验证](baselines/multiple-subtitles-runtime-types.json)。公开异步/旧类型及安装消费者仍待 04。 +类型梳理复现旧/候选内嵌时间戳输出 NaN;九份历史实现均复现,新建 TIMESTAMP 风险与 +PKG-MULTI-SUB-07,并将 07 加入 05 前置。此问题未修复,下一步先做 07,再继续公开类型。 +223 项:120 done、14 doing、89 todo;197 项风险。独立本地检查点提交,不推送、不发布。 + + ## PKG-MULTI-SUB-03 模块与资源修复完成 生产源码拆为入口、请求、纯解析合并、生命周期和宿主安装五个模块,vendor parser 不变。 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 023ccdd83..805a5bf8b 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -200,3 +200,4 @@ | MULTI-SUB-EXPORT-01 | open / 已复现 | Older CommonJS default objects conflict with export-assignment declarations; latest exports a direct factory | PKG-MULTI-SUB-04, PKG-MULTI-SUB-06 | | MULTI-SUB-HTTP-01 | resolved / 已复现 | HTTP error responses with parseable bodies are installed as subtitles | PKG-MULTI-SUB-03, PKG-MULTI-SUB-05 | | MULTI-SUB-HOST-01 | resolved / 已复现 | Subtitle init rejections are ignored and failed installation or URL replacement leaks or invalidates resources | PKG-MULTI-SUB-03, PKG-MULTI-SUB-05 | +| MULTI-SUB-TIMESTAMP-01 | open / 已复现 | Text wrappers replace numeric inline cue timestamps and serialize NaN times | PKG-MULTI-SUB-07, PKG-MULTI-SUB-05 | diff --git a/refactor/risks.json b/refactor/risks.json index 8fa7db94b..fc1e49cfc 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -4398,6 +4398,23 @@ "test/browser/multiple-subtitles-lifecycle.spec.js" ], "resolutionRationale": "Owned installations retain prior URLs on synchronous failure, observe async rejections, release the failed active URL without affecting newer installs, and clean up on destroy. Controlled sync/async/reentrant/getter failures and native Blob revocation pass; void methods and registration timing are preserved." + }, + { + "id": "MULTI-SUB-TIMESTAMP-01", + "title": "Text wrappers replace numeric inline cue timestamps and serialize NaN times", + "confirmation": "reproduced", + "status": "open", + "owners": [ + "PKG-MULTI-SUB-07", + "PKG-MULTI-SUB-05" + ], + "evidence": [ + "refactor/baselines/multiple-subtitles-runtime-types.json", + "refactor/changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md", + "test/multiple-subtitles-failures.test.js" + ], + "compatibleResolution": "Preserve numeric timestamp nodes while retaining subtitle text, track wrappers and selection contracts; record the intentional repair of invalid legacy output.", + "closureCriteria": "Valid inline timestamps and surrounding text survive serialization and native subtitle playback; frozen historical failure remains reproducible and ordinary markup regressions pass." } ] } diff --git a/refactor/scripts/multiple-subtitles-runtime-types.test.mjs b/refactor/scripts/multiple-subtitles-runtime-types.test.mjs new file mode 100644 index 000000000..82eca7722 --- /dev/null +++ b/refactor/scripts/multiple-subtitles-runtime-types.test.mjs @@ -0,0 +1,37 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +// eslint-disable-next-line test/no-import-node-test -- Internal runtime compiler checks use the repository runner. +import test from 'node:test' +import ts from 'typescript' +import runtimeCompat from 'typescript-runtime-compat' + +for (const compiler of [ts, runtimeCompat]) { + test(`Multiple subtitles strict runtime modules and negative consumers: TS ${compiler.version}`, () => { + const file = path.resolve('packages/artplayer-plugin-multiple-subtitles/tsconfig.json') + const config = compiler.readConfigFile(file, compiler.sys.readFile) + assert.equal(config.error, undefined) + const parsed = compiler.parseJsonConfigFileContent(config.config, compiler.sys, path.dirname(file)) + assert.equal(parsed.options.strict, true) + assert.equal(parsed.options.allowJs, false) + assert.equal(parsed.options.skipLibCheck, false) + assert.equal(parsed.options.noEmit, true) + assert.deepEqual(parsed.options.types, []) + const fixture = path.resolve('test/types/multiple-subtitles-runtime.ts') + assert(parsed.fileNames.some(file => path.resolve(file) === fixture)) + function diagnostics(negative) { + const host = compiler.createCompilerHost(parsed.options) + const read = host.readFile.bind(host) + host.readFile = file => path.resolve(file) === fixture && negative + ? fs.readFileSync(fixture, 'utf8').replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '') + : read(file) + const program = compiler.createProgram(parsed.fileNames, parsed.options, host) + assert(program.getSourceFile(fixture)) + assert(!program.getSourceFiles().some(file => /multiple-subtitles\/src\/.*\.js$/.test(file.fileName.replaceAll('\\', '/'))), 'Vendored JS must be represented by its explicit declaration') + return compiler.getPreEmitDiagnostics(program).map(error => ({ code: error.code, text: compiler.flattenDiagnosticMessageText(error.messageText, '\n') })) + } + assert.deepEqual(diagnostics(false), []) + const rejected = diagnostics(true) + assert.equal(rejected.length, 8, JSON.stringify(rejected)) + }) +} diff --git a/refactor/tasks.json b/refactor/tasks.json index cd8c138ab..85ee93230 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2319,10 +2319,29 @@ "ENG-06", "CORE-07" ], - "status": "todo", + "status": "doing", "risk": "H", "deliverable": "准确 parser/cue/tree/result 类型与真实异步形状", "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容", + "evidence": [ + "baselines/multiple-subtitles-runtime-types.json", + "changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md" + ] + }, + { + "id": "PKG-MULTI-SUB-07", + "phase": "5 包迁移:artplayer-plugin-multiple-subtitles", + "title": "修复字幕内嵌时间戳序列化", + "scope": [ + "artplayer-plugin-multiple-subtitles" + ], + "dependsOn": [ + "PKG-MULTI-SUB-03" + ], + "status": "todo", + "risk": "H", + "deliverable": "保留数字时间戳节点、修正无效 NaN 输出、明确旧/新字幕输出差异", + "acceptance": "旧九份实现可复现,候选有效内嵌时间戳及文字/CSS/原生字幕通过,公开工厂与选择接口不变", "evidence": [] }, { @@ -2334,7 +2353,8 @@ ], "dependsOn": [ "PKG-MULTI-SUB-04", - "CORE-22" + "CORE-22", + "PKG-MULTI-SUB-07" ], "status": "todo", "risk": "H", diff --git a/test/multiple-subtitles-failures.test.js b/test/multiple-subtitles-failures.test.js index 20e7c6ab5..21100bc5a 100644 --- a/test/multiple-subtitles-failures.test.js +++ b/test/multiple-subtitles-failures.test.js @@ -257,4 +257,10 @@ for (const implementation of implementations) { assert(env.utils.unescape(await env.latestText()).includes('art-subtitle-renamed')) assert.deepEqual(env.requests, ['a.vtt']) }) + + test(`Multiple-subtitles ${implementation.name} failure baseline: inline timestamp nodes become invalid serialized times`, async () => { + const env = create({ responses: { 'a.vtt': 'WEBVTT\n\n00:00.000 --> 00:08.000\nBefore <00:02.000> after\n' } }) + await env.factory(options(subtitle()))(env.art) + assert((await env.latestText()).includes('')) + }) } diff --git a/test/types/multiple-subtitles-runtime.ts b/test/types/multiple-subtitles-runtime.ts new file mode 100644 index 000000000..3e64bc3ad --- /dev/null +++ b/test/types/multiple-subtitles-runtime.ts @@ -0,0 +1,65 @@ +import type Artplayer from 'artplayer' +import type { CueNode, TimestampNode } from '../../packages/artplayer-plugin-multiple-subtitles/src/parser' +import type { Option, Result } from '../../packages/artplayer-plugin-multiple-subtitles/src/types' +import factory from '../../packages/artplayer-plugin-multiple-subtitles/src/index' +import createLifetime from '../../packages/artplayer-plugin-multiple-subtitles/src/lifetime' +import { parseTracks, serializeTracks } from '../../packages/artplayer-plugin-multiple-subtitles/src/merge' +import { WebVTTParser } from '../../packages/artplayer-plugin-multiple-subtitles/src/parser' +import loadVtt from '../../packages/artplayer-plugin-multiple-subtitles/src/request' +import legacyFactory from '../../packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles' + +declare const art: Artplayer +const option: Option = { subtitles: [{ url: '/a.vtt', name: 'a', type: 'vtt' }] } +const register = factory(option) +const pending: Promise = register(art) +pending.then((result) => { + result.name satisfies 'multipleSubtitles' + const returned: void = result.tracks(['a']) + result.reset() + void returned + // @ts-expect-error Selection names are strings. + result.tracks([1]) +}) +factory({})(art) +factory({ subtitles: [{}] })(art) +// @ts-expect-error Factory options remain required. +factory() +// @ts-expect-error Registration is asynchronous, with no synchronous name. +void pending.name +// @ts-expect-error Invalid subtitle format is rejected. +factory({ subtitles: [{ type: 'xml' }] }) + +const lifetime = createLifetime(art) +const waited: Promise = lifetime.wait(Promise.resolve(3)) +const text: Promise = loadVtt({}, (art.constructor as typeof Artplayer).utils, lifetime) +void waited +void text +// @ts-expect-error Lifetime state cannot be assigned. +lifetime.closed = false + +const parser = new WebVTTParser() +const parsed = parser.parse('WEBVTT\n\n', 'metadata') +parsed.errors.forEach(error => error.message.toUpperCase()) +const nodes: CueNode[] = parsed.cues[0]!.tree.children +for (const node of nodes) { + if (node.type === 'timestamp') + node.value.toFixed() + if (node.type === 'text') + node.value.toUpperCase() +} +const timestamp: TimestampNode = { type: 'timestamp', value: 2 } +void timestamp +// @ts-expect-error Parsed timestamps are numbers, not wrapped serialized values. +const wrongTimestamp: TimestampNode = { type: 'timestamp', value: '2' } +void wrongTimestamp +const trees = parseTracks(['WEBVTT\n\n'], [{}]) +const vtt: string = serializeTracks(trees) +void vtt +// @ts-expect-error The pure serializer returns text. +const number: number = serializeTracks(trees) +void number + +legacyFactory({ subtitles: [] })(art).name satisfies 'multipleSubtitles' +// @ts-expect-error Historical synchronous return extraction cannot be silently replaced. +const oldRegistration: ReturnType = register +void oldRegistration