From 7f226ff8229e91d8cdc17053926cbbf1071474c4 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sun, 13 Sep 2026 11:50:50 +0800 Subject: [PATCH] fix(multiple-subtitles): [PKG-MULTI-SUB-07] preserve inline timestamps and caption text --- .../artplayer-plugin-multiple-subtitles.js | 2 +- ...player-plugin-multiple-subtitles.legacy.js | 2 +- .../artplayer-plugin-multiple-subtitles.mjs | 138 +- .../ARCHITECTURE.md | 17 +- .../artplayer-plugin-multiple-subtitles.js | 2 +- ...player-plugin-multiple-subtitles.legacy.js | 2 +- .../artplayer-plugin-multiple-subtitles.mjs | 138 +- .../src/caption.ts | 21 + .../src/index.ts | 2 + .../src/merge.ts | 28 +- .../src/parser.d.ts | 3 +- .../multiple-subtitles-timestamps.json | 8218 +++++++++++++++++ .../2026-09-13-PKG-MULTI-SUB-07-timestamps.md | 49 + refactor/plan.md | 5 +- refactor/progress.md | 16 + refactor/risk-table.md | 3 +- refactor/risks.json | 34 +- .../multiple-subtitles-runtime-types.test.mjs | 2 +- refactor/tasks.json | 7 +- .../multiple-subtitles-lifecycle.spec.js | 45 + test/multiple-subtitles-failures.test.js | 6 + test/multiple-subtitles-lifecycle.test.js | 2 +- test/multiple-subtitles-merge.test.js | 45 + test/types/multiple-subtitles-runtime.ts | 5 +- 24 files changed, 8660 insertions(+), 132 deletions(-) create mode 100644 packages/artplayer-plugin-multiple-subtitles/src/caption.ts create mode 100644 refactor/baselines/multiple-subtitles-timestamps.json create mode 100644 refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.js b/docs/compiled/artplayer-plugin-multiple-subtitles.js index 6b450f1dc..a42f27e79 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){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}}}}); +!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],h=m.length,g="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(hb&&" "!==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 y;!c&&""==l[o];)o++;if(!c&&void 0==l[o])break;y=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(y.id=l[o],/^NOTE($|[ \t])/.test(y.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(y.id)){var x=[],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),x.push(l[o]),o++;if(u.length){p("Style blocks cannot appear after the first cue.");continue}w||f.push(x.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 A=new n(l[o],p),O=0;if(u.length>0&&(O=u[u.length-1].startTime),!T||A.parse(y,O)){for(o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){p("Blank line missing before cue."),c=!0;break}""!=y.text&&(y.text+="\n"),y.text+=l[o],o++}var N=new i(y.text,p,a,t);y.tree=N.parse(y.startTime,y.endTime),u.push(y)}else for(y=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 h=c.slice(0,c.length-1);if(void 0===h||""===h||isNaN(h)){a("Size needs to be a number"),h=100;continue}if((h=parseFloat(h))<0||h>100){a("Size needs to be between 0 and 100%.");continue}t.size=h}else if("align"==l){var g=["start","center","end","left","right"];if(-1==g.indexOf(c)){a("Alignment can only be set to one of "+g.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 h=new n(p[1],o).parseTimestamp();void 0!=h&&((h<=t||h>=s)&&o("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=h&&o("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&&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:c,srtToVtt:f,assToVtt:u}=n.constructor.utils,d=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),p=l(n,d,i);let m=[];function h(e){d.closed||p(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=>{if("timestamp"===t.type)return r(t);const n=`
${t.value}
`;return"object"===t.type?{...t,value:n,children:t.children.map(o)}:{...t,value:n}})}})}return(new s).serialize(t)}(e))}const g={name:"multipleSubtitles",tracks(e=[]){d.closed||h(e.map(e=>m.find(t=>t.name===e)))},reset(){h(m)}};try{if(d.closed)return g;const i=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:c,srtToVtt:f,assToVtt:u},d)));return d.closed||(m=function(e,n){const i=new t;return e.map((e,t)=>({...i.parse(e,"metadata"),url:n[t].url,name:n[t].name}))}(i,e),function(e,t){function n(){if(!t.closed)for(const t of Array.from(e.template.$subtitle.getElementsByTagName(`c.${a}`))){const e=t.firstChild;3===e?.nodeType&&(e.nodeValue=(e.nodeValue||"").replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/,"")),t.replaceWith(...Array.from(t.childNodes))}}t.closed||(t.own(()=>e.off("subtitleAfterUpdate",n)),e.on("subtitleAfterUpdate",n))}(n,d),h(m)),g}catch(v){throw d.dispose(),v}}}}); diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js b/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js index c9276f959..9910ed915 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){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}})}}); +!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())}),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],h=m.length,g="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(hb&&" "!==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 h=c.slice(0,c.length-1);if(void 0===h||""===h||isNaN(h)){s("Size needs to be a number"),h=100;continue}if((h=parseFloat(h))<0||h>100){s("Size needs to be between 0 and 100%.");continue}t.size=h}else if("align"==l){var g=["start","center","end","left","right"];if(-1==g.indexOf(c)){s("Alignment can only be set to one of "+g.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 h=m[1];"v"!=h&&"lang"!=h&&""!=m[3]&&a("Only and can have an annotation."),"c"==h||"i"==h||"b"==h||"u"==h||"ruby"==h||"rt"==h&&"ruby"==c.name?u(m):"v"==h?(p("v")&&a(" cannot be nested inside itself."),u(m),c.value=m[3],m[3]||a(" requires an annotation.")):"lang"==h?(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 g=new d(m[1],a).parseTimestamp();void 0!=g&&((g<=t||g>=i)&&a("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=g&&a("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&&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=b(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=>{if("timestamp"===t.type)return g(t);const n=`
${t.value}
`;return"object"===t.type?l(o({},t),{value:n,children:t.children.map(v)}):l(o({},t),{value:n})})})}))}return(new m).serialize(t)}(e))}const y={name:"multipleSubtitles",tracks(e=[]){a.closed||p(e.map(e=>d.find(t=>t.name===e)))},reset(){p(d)}};try{if(a.closed)return y;const n=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})})}(n,e),function(e,t){function n(){if(!t.closed)for(const t of Array.from(e.template.$subtitle.getElementsByTagName(`c.${h}`))){const e=t.firstChild;3===(null==e?void 0:e.nodeType)&&(e.nodeValue=(e.nodeValue||"").replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/,"")),t.replaceWith(...Array.from(t.childNodes))}}t.closed||(t.own(()=>e.off("subtitleAfterUpdate",n)),e.on("subtitleAfterUpdate",n))}(t,a),p(d)),y}catch(x){throw a.dispose(),x}})}}); diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.mjs b/docs/compiled/artplayer-plugin-multiple-subtitles.mjs index 06d31ac62..e043c9c70 100644 --- a/docs/compiled/artplayer-plugin-multiple-subtitles.mjs +++ b/docs/compiled/artplayer-plugin-multiple-subtitles.mjs @@ -55,56 +55,6 @@ * * 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 createLifetime(art) { - let closed = Boolean(art.isDestroy); - const cleanups = /* @__PURE__ */ new Set(); - let cancel; - const cancelled = new Promise((resolve) => { - cancel = resolve; - }); - function run(cleanup) { - try { - cleanup(); - } catch (error) { - console.warn("Failed to clean up multiple subtitles:", error); - } - } - const lifetime = { - get closed() { - return closed; - }, - own(cleanup) { - if (closed) - run(cleanup); - else - cleanups.add(cleanup); - return () => cleanups.delete(cleanup); - }, - wait(value) { - return Promise.race([value, cancelled]); - }, - dispose() { - if (closed) - return; - closed = true; - cancel(); - const pending = [...cleanups].reverse(); - cleanups.clear(); - for (const cleanup of pending) - run(cleanup); - } - }; - if (!closed) { - lifetime.own(() => art.off("destroy", lifetime.dispose)); - try { - art.on("destroy", lifetime.dispose); - } catch (error) { - lifetime.dispose(); - throw error; - } - } - return lifetime; -} var defaultCueSettings = { direction: "horizontal", snapToLines: true, @@ -849,6 +799,17 @@ var WebVTTSerializer = function() { return result; }; }; +const TIMESTAMP_CLASS = "artplayer-multiple-subtitles-timestamp"; +function markTimestamp(node) { + return { type: "object", name: "c", classes: [TIMESTAMP_CLASS], children: [node] }; +} +function markNestedTimestamps(node) { + if (node.type === "timestamp") + return markTimestamp(node); + if (node.type === "object") + return { ...node, children: node.children.map(markNestedTimestamps) }; + return node; +} function parseTracks(vtts, subtitles) { const parser = new WebVTTParser(); return vtts.map((vtt, index) => { @@ -865,16 +826,84 @@ function serializeTracks(trees) { ...cue, tree: { ...cue.tree, - children: cue.tree.children.map((child) => ({ - ...child, - value: `
${child.value}
` - })) + children: cue.tree.children.map((child) => { + if (child.type === "timestamp") + return markTimestamp(child); + const value = `
${child.value}
`; + return child.type === "object" ? { ...child, value, children: child.children.map(markNestedTimestamps) } : { ...child, value }; + }) } }); } } return new WebVTTSerializer().serialize(cues); } +function installCaptionView(art, lifetime) { + function update() { + if (lifetime.closed) + return; + for (const marker of Array.from(art.template.$subtitle.getElementsByTagName(`c.${TIMESTAMP_CLASS}`))) { + const first = marker.firstChild; + if (first?.nodeType === 3) + first.nodeValue = (first.nodeValue || "").replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/, ""); + marker.replaceWith(...Array.from(marker.childNodes)); + } + } + if (lifetime.closed) + return; + lifetime.own(() => art.off("subtitleAfterUpdate", update)); + art.on("subtitleAfterUpdate", update); +} +function createLifetime(art) { + let closed = Boolean(art.isDestroy); + const cleanups = /* @__PURE__ */ new Set(); + let cancel; + const cancelled = new Promise((resolve) => { + cancel = resolve; + }); + function run(cleanup) { + try { + cleanup(); + } catch (error) { + console.warn("Failed to clean up multiple subtitles:", error); + } + } + const lifetime = { + get closed() { + return closed; + }, + own(cleanup) { + if (closed) + run(cleanup); + else + cleanups.add(cleanup); + return () => cleanups.delete(cleanup); + }, + wait(value) { + return Promise.race([value, cancelled]); + }, + dispose() { + if (closed) + return; + closed = true; + cancel(); + const pending = [...cleanups].reverse(); + cleanups.clear(); + for (const cleanup of pending) + run(cleanup); + } + }; + if (!closed) { + lifetime.own(() => art.off("destroy", lifetime.dispose)); + try { + art.on("destroy", lifetime.dispose); + } catch (error) { + lifetime.dispose(); + throw error; + } + } + return lifetime; +} function createRenderer(art, lifetime, unescape) { let current = null; function own(url) { @@ -1002,6 +1031,7 @@ function artplayerPluginMultipleSubtitles({ subtitles = [] }) { if (lifetime.closed) return result; trees = parseTracks(vtts, subtitles); + installCaptionView(art, lifetime); setTracks(trees); return result; } catch (error) { diff --git a/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md b/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md index 116a03b20..4e1154653 100644 --- a/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md +++ b/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md @@ -17,11 +17,12 @@ task PKG-MULTI-SUB-04 owns that compatibility work. Do not infer runtime behavio Its cancellation Promise also settles pending operations without AbortController support. - `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/caption.ts` owns a subtitleAfterUpdate listener. It unwraps internal timestamp markers + in the custom HTML caption layer, preserving subsequent nodes and literal user text. - `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. + timestamp values must remain numeric in both parsed and serialized trees. - `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 @@ -52,6 +53,18 @@ The historical failure suite deliberately retains the old outcomes; the candidat suite verifies their fixes. Preserve parser best-effort behavior independently: invalid-header diagnostics can coexist with usable cues, and metadata is intentionally read after download. +Inline timestamp nodes keep their numeric values. The merge layer puts them in valid VTT +class spans (`c.artplayer-multiple-subtitles-timestamp`), including nested timestamps. Native +getCueAsHTML retains the processing instructions. ArtPlayer's HTML layer does not interpret +those tags, so caption.ts removes only the leading instruction within the generated marker +and unwraps its remaining children. HTML does not pair `` with ``; removing the +whole element would incorrectly discard following caption text. The original cue stays intact. +This keeps the existing whole-cue display behavior; it does not add karaoke highlighting. + +The vendor entity decoder still leaves extra semicolons after standard named entities. Nine +historical implementations reproduce it; MULTI-SUB-ENTITY-01 remains open for task 05. Do not +silently alter vendor source or declare entity rendering correct because timestamp tests pass. + ## Vendor boundary and validation The comparison revision is w3c/webvtt.js `380cfcce34ba8b472d3a31474874eb72a0e5f460`. 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 6b450f1dc..a42f27e79 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){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}}}}); +!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],h=m.length,g="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(hb&&" "!==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 y;!c&&""==l[o];)o++;if(!c&&void 0==l[o])break;y=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(y.id=l[o],/^NOTE($|[ \t])/.test(y.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(y.id)){var x=[],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),x.push(l[o]),o++;if(u.length){p("Style blocks cannot appear after the first cue.");continue}w||f.push(x.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 A=new n(l[o],p),O=0;if(u.length>0&&(O=u[u.length-1].startTime),!T||A.parse(y,O)){for(o++;""!=l[o]&&void 0!=l[o];){if(-1!=l[o].indexOf("--\x3e")){p("Blank line missing before cue."),c=!0;break}""!=y.text&&(y.text+="\n"),y.text+=l[o],o++}var N=new i(y.text,p,a,t);y.tree=N.parse(y.startTime,y.endTime),u.push(y)}else for(y=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 h=c.slice(0,c.length-1);if(void 0===h||""===h||isNaN(h)){a("Size needs to be a number"),h=100;continue}if((h=parseFloat(h))<0||h>100){a("Size needs to be between 0 and 100%.");continue}t.size=h}else if("align"==l){var g=["start","center","end","left","right"];if(-1==g.indexOf(c)){a("Alignment can only be set to one of "+g.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 h=new n(p[1],o).parseTimestamp();void 0!=h&&((h<=t||h>=s)&&o("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=h&&o("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&&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:c,srtToVtt:f,assToVtt:u}=n.constructor.utils,d=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),p=l(n,d,i);let m=[];function h(e){d.closed||p(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=>{if("timestamp"===t.type)return r(t);const n=`
${t.value}
`;return"object"===t.type?{...t,value:n,children:t.children.map(o)}:{...t,value:n}})}})}return(new s).serialize(t)}(e))}const g={name:"multipleSubtitles",tracks(e=[]){d.closed||h(e.map(e=>m.find(t=>t.name===e)))},reset(){h(m)}};try{if(d.closed)return g;const i=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:c,srtToVtt:f,assToVtt:u},d)));return d.closed||(m=function(e,n){const i=new t;return e.map((e,t)=>({...i.parse(e,"metadata"),url:n[t].url,name:n[t].name}))}(i,e),function(e,t){function n(){if(!t.closed)for(const t of Array.from(e.template.$subtitle.getElementsByTagName(`c.${a}`))){const e=t.firstChild;3===e?.nodeType&&(e.nodeValue=(e.nodeValue||"").replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/,"")),t.replaceWith(...Array.from(t.childNodes))}}t.closed||(t.own(()=>e.off("subtitleAfterUpdate",n)),e.on("subtitleAfterUpdate",n))}(n,d),h(m)),g}catch(v){throw d.dispose(),v}}}}); 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 c9276f959..9910ed915 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){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}})}}); +!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())}),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],h=m.length,g="WEBVTT",v=0,b=6;for("\ufeff"===m[0]&&(v=1,b+=1),(hb&&" "!==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 h=c.slice(0,c.length-1);if(void 0===h||""===h||isNaN(h)){s("Size needs to be a number"),h=100;continue}if((h=parseFloat(h))<0||h>100){s("Size needs to be between 0 and 100%.");continue}t.size=h}else if("align"==l){var g=["start","center","end","left","right"];if(-1==g.indexOf(c)){s("Alignment can only be set to one of "+g.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 h=m[1];"v"!=h&&"lang"!=h&&""!=m[3]&&a("Only and can have an annotation."),"c"==h||"i"==h||"b"==h||"u"==h||"ruby"==h||"rt"==h&&"ruby"==c.name?u(m):"v"==h?(p("v")&&a(" cannot be nested inside itself."),u(m),c.value=m[3],m[3]||a(" requires an annotation.")):"lang"==h?(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 g=new d(m[1],a).parseTimestamp();void 0!=g&&((g<=t||g>=i)&&a("Timestamp must be between start timestamp and end timestamp."),f.length>0&&f[f.length-1]>=g&&a("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&&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=b(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=>{if("timestamp"===t.type)return g(t);const n=`
${t.value}
`;return"object"===t.type?l(o({},t),{value:n,children:t.children.map(v)}):l(o({},t),{value:n})})})}))}return(new m).serialize(t)}(e))}const y={name:"multipleSubtitles",tracks(e=[]){a.closed||p(e.map(e=>d.find(t=>t.name===e)))},reset(){p(d)}};try{if(a.closed)return y;const n=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})})}(n,e),function(e,t){function n(){if(!t.closed)for(const t of Array.from(e.template.$subtitle.getElementsByTagName(`c.${h}`))){const e=t.firstChild;3===(null==e?void 0:e.nodeType)&&(e.nodeValue=(e.nodeValue||"").replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/,"")),t.replaceWith(...Array.from(t.childNodes))}}t.closed||(t.own(()=>e.off("subtitleAfterUpdate",n)),e.on("subtitleAfterUpdate",n))}(t,a),p(d)),y}catch(x){throw a.dispose(),x}})}}); 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 06d31ac62..e043c9c70 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 @@ -55,56 +55,6 @@ * * 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 createLifetime(art) { - let closed = Boolean(art.isDestroy); - const cleanups = /* @__PURE__ */ new Set(); - let cancel; - const cancelled = new Promise((resolve) => { - cancel = resolve; - }); - function run(cleanup) { - try { - cleanup(); - } catch (error) { - console.warn("Failed to clean up multiple subtitles:", error); - } - } - const lifetime = { - get closed() { - return closed; - }, - own(cleanup) { - if (closed) - run(cleanup); - else - cleanups.add(cleanup); - return () => cleanups.delete(cleanup); - }, - wait(value) { - return Promise.race([value, cancelled]); - }, - dispose() { - if (closed) - return; - closed = true; - cancel(); - const pending = [...cleanups].reverse(); - cleanups.clear(); - for (const cleanup of pending) - run(cleanup); - } - }; - if (!closed) { - lifetime.own(() => art.off("destroy", lifetime.dispose)); - try { - art.on("destroy", lifetime.dispose); - } catch (error) { - lifetime.dispose(); - throw error; - } - } - return lifetime; -} var defaultCueSettings = { direction: "horizontal", snapToLines: true, @@ -849,6 +799,17 @@ var WebVTTSerializer = function() { return result; }; }; +const TIMESTAMP_CLASS = "artplayer-multiple-subtitles-timestamp"; +function markTimestamp(node) { + return { type: "object", name: "c", classes: [TIMESTAMP_CLASS], children: [node] }; +} +function markNestedTimestamps(node) { + if (node.type === "timestamp") + return markTimestamp(node); + if (node.type === "object") + return { ...node, children: node.children.map(markNestedTimestamps) }; + return node; +} function parseTracks(vtts, subtitles) { const parser = new WebVTTParser(); return vtts.map((vtt, index) => { @@ -865,16 +826,84 @@ function serializeTracks(trees) { ...cue, tree: { ...cue.tree, - children: cue.tree.children.map((child) => ({ - ...child, - value: `
${child.value}
` - })) + children: cue.tree.children.map((child) => { + if (child.type === "timestamp") + return markTimestamp(child); + const value = `
${child.value}
`; + return child.type === "object" ? { ...child, value, children: child.children.map(markNestedTimestamps) } : { ...child, value }; + }) } }); } } return new WebVTTSerializer().serialize(cues); } +function installCaptionView(art, lifetime) { + function update() { + if (lifetime.closed) + return; + for (const marker of Array.from(art.template.$subtitle.getElementsByTagName(`c.${TIMESTAMP_CLASS}`))) { + const first = marker.firstChild; + if (first?.nodeType === 3) + first.nodeValue = (first.nodeValue || "").replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/, ""); + marker.replaceWith(...Array.from(marker.childNodes)); + } + } + if (lifetime.closed) + return; + lifetime.own(() => art.off("subtitleAfterUpdate", update)); + art.on("subtitleAfterUpdate", update); +} +function createLifetime(art) { + let closed = Boolean(art.isDestroy); + const cleanups = /* @__PURE__ */ new Set(); + let cancel; + const cancelled = new Promise((resolve) => { + cancel = resolve; + }); + function run(cleanup) { + try { + cleanup(); + } catch (error) { + console.warn("Failed to clean up multiple subtitles:", error); + } + } + const lifetime = { + get closed() { + return closed; + }, + own(cleanup) { + if (closed) + run(cleanup); + else + cleanups.add(cleanup); + return () => cleanups.delete(cleanup); + }, + wait(value) { + return Promise.race([value, cancelled]); + }, + dispose() { + if (closed) + return; + closed = true; + cancel(); + const pending = [...cleanups].reverse(); + cleanups.clear(); + for (const cleanup of pending) + run(cleanup); + } + }; + if (!closed) { + lifetime.own(() => art.off("destroy", lifetime.dispose)); + try { + art.on("destroy", lifetime.dispose); + } catch (error) { + lifetime.dispose(); + throw error; + } + } + return lifetime; +} function createRenderer(art, lifetime, unescape) { let current = null; function own(url) { @@ -1002,6 +1031,7 @@ function artplayerPluginMultipleSubtitles({ subtitles = [] }) { if (lifetime.closed) return result; trees = parseTracks(vtts, subtitles); + installCaptionView(art, lifetime); setTracks(trees); return result; } catch (error) { diff --git a/packages/artplayer-plugin-multiple-subtitles/src/caption.ts b/packages/artplayer-plugin-multiple-subtitles/src/caption.ts new file mode 100644 index 000000000..2c5f09d1b --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/src/caption.ts @@ -0,0 +1,21 @@ +import type Artplayer from 'artplayer' +import type { Lifetime } from './types' +import { TIMESTAMP_CLASS } from './merge' + +export default function installCaptionView(art: Pick, lifetime: Lifetime): void { + function update(): void { + if (lifetime.closed) + return + // HTML does not pair with ; unwrap while preserving following caption nodes. + for (const marker of Array.from(art.template.$subtitle.getElementsByTagName(`c.${TIMESTAMP_CLASS}`))) { + const first = marker.firstChild + if (first?.nodeType === 3) + first.nodeValue = (first.nodeValue || '').replace(/^<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/, '') + marker.replaceWith(...Array.from(marker.childNodes)) + } + } + if (lifetime.closed) + return + lifetime.own(() => art.off('subtitleAfterUpdate', update)) + art.on('subtitleAfterUpdate', update) +} diff --git a/packages/artplayer-plugin-multiple-subtitles/src/index.ts b/packages/artplayer-plugin-multiple-subtitles/src/index.ts index 13ec9c6e6..3890b3c7e 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/index.ts +++ b/packages/artplayer-plugin-multiple-subtitles/src/index.ts @@ -1,5 +1,6 @@ import type Artplayer from 'artplayer' import type { Option, Result, Track } from './types' +import installCaptionView from './caption' import createLifetime from './lifetime' import { parseTracks, serializeTracks } from './merge' import createRenderer from './render' @@ -33,6 +34,7 @@ export default function artplayerPluginMultipleSubtitles({ subtitles = [] }: Opt return result // A live completed request yields text; cancellation was checked above. trees = parseTracks(vtts as string[], subtitles) + installCaptionView(art, lifetime) setTracks(trees) return result } diff --git a/packages/artplayer-plugin-multiple-subtitles/src/merge.ts b/packages/artplayer-plugin-multiple-subtitles/src/merge.ts index 4fd1b42d7..d1d0cce03 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/merge.ts +++ b/packages/artplayer-plugin-multiple-subtitles/src/merge.ts @@ -1,7 +1,21 @@ -import type { Cue, WrappedNode } from './parser' +import type { Cue, CueNode, TimestampNode, WrappedNode } from './parser' import type { Track, TrackOption } from './types' import { WebVTTParser, WebVTTSerializer } from './parser' +export const TIMESTAMP_CLASS = 'artplayer-multiple-subtitles-timestamp' + +function markTimestamp(node: TimestampNode): CueNode { + return { type: 'object', name: 'c', classes: [TIMESTAMP_CLASS], children: [node] } +} + +function markNestedTimestamps(node: CueNode): CueNode { + if (node.type === 'timestamp') + return markTimestamp(node) + if (node.type === 'object') + return { ...node, children: node.children.map(markNestedTimestamps) } + return node +} + export function parseTracks(vtts: readonly string[], subtitles: readonly TrackOption[]): Track[] { const parser = new WebVTTParser() return vtts.map((vtt, index) => { @@ -21,10 +35,14 @@ export function serializeTracks(trees: readonly (Track | undefined)[]): string { ...cue, tree: { ...cue.tree, - children: cue.tree.children.map(child => ({ - ...child, - value: `
${child.value}
`, - })), + children: cue.tree.children.map((child) => { + if (child.type === 'timestamp') + return markTimestamp(child) + const value = `
${child.value}
` + return child.type === 'object' + ? { ...child, value, children: child.children.map(markNestedTimestamps) } + : { ...child, value } + }), }, }) } diff --git a/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts b/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts index 64e1b6f17..ff1e9d4c1 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts +++ b/packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts @@ -17,8 +17,7 @@ export interface TimestampNode { } 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 type WrappedNode = CueNode export interface Cue { id: string diff --git a/refactor/baselines/multiple-subtitles-timestamps.json b/refactor/baselines/multiple-subtitles-timestamps.json new file mode 100644 index 000000000..7db64c4e2 --- /dev/null +++ b/refactor/baselines/multiple-subtitles-timestamps.json @@ -0,0 +1,8218 @@ +{ + "schemaVersion": 1, + "task": "PKG-MULTI-SUB-07", + "status": "done", + "parentCommit": "da64aae2676a0e5132323cef5378072e025c6ede", + "checks": [ + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "source", + "tests": 37, + "file": "refactor/.cache/multiple-subtitles07-source.log", + "sha256": "d90e52c13298ae7586f4bbfb929467975ecf344a386a766b06017336199e97f7", + "scope": "Includes pure frozen-tree tests against source; other regressions use the selected entry." + }, + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "main", + "tests": 37, + "file": "refactor/.cache/multiple-subtitles07-main.log", + "sha256": "4d6525b4c86b65c338d19adc8c384bc9973d5a7a9a26b48dbb4ec0792fec8a3c", + "scope": "Includes pure frozen-tree tests against source; other regressions use the selected entry." + }, + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "legacy", + "tests": 37, + "file": "refactor/.cache/multiple-subtitles07-legacy.log", + "sha256": "8cb13ff589247fd3ad5e1a8623822a2e01f3ec782ef978f5fe3ec04b3097954d", + "scope": "Includes pure frozen-tree tests against source; other regressions use the selected entry." + }, + { + "command": "yarn test:multiple-subtitles", + "file": "refactor/.cache/multiple-subtitles07-package.log", + "sha256": "ae95ea272984970edc5328e26829a79075cf60236ddad2c2f1559881814021ae", + "tests": 267, + "pass": 267, + "fail": 0, + "strictCompilers": [ + "5.9.3", + "5.1.6" + ], + "negativeErrorsPerCompiler": 9 + }, + { + "command": "node --test test/multiple-subtitles-failures.test.js", + "file": "refactor/.cache/multiple-subtitles07-historical.log", + "sha256": "1ad193e79bcbe5f8625c3d01f41674e6e138a3fe273f5de0d067e12733181584", + "tests": 180, + "pass": 180, + "note": "Includes nine historical entity-corruption cases; this separate defect remains open." + }, + { + "command": "Explicit changed source, declaration, fixtures and browser test lint", + "file": "refactor/.cache/multiple-subtitles07-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "command": "yarn build artplayer-plugin-multiple-subtitles", + "file": "refactor/.cache/multiple-subtitles07-build.log", + "sha256": "896f9311e199a4e554a6c5c7b666d997a97d3bccc6502713be48219da5a078c2" + }, + { + "command": "Actual ESM import", + "file": "refactor/.cache/multiple-subtitles07-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-subtitles07-ci.log", + "sha256": "7985e8b7d7fb2cf1d1449ed235601bd2b7e195ad51a8d5b8a92f752888852fff", + "tests": { + "unit": 1987, + "engineering": 14, + "baseline": 336, + "total": 2337 + }, + "strictProductionTypeScriptFiles": 353, + "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/caption.ts", + "sha256": "cbc44cd8c9171685c0fcba059d6868799ef195a13379dbb7da791582ba5b3402" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/index.ts", + "sha256": "f8e23b0b9d68f949389825b657f9abe200bb129ebe20b7650a6a71619de147b2" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/lifetime.ts", + "sha256": "469cec6a9f87311aa433ad2d74925e39d56324b8136975e818388315200f1e58" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/merge.ts", + "sha256": "38bde420334987b3f3546e2e032c2c98071987a3a1779ba191f4d9b6448bfafa" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/parser.d.ts", + "sha256": "22a596dfb02aaa00e333e9bf3285ececfa94eda3a699726cba190ae858f7c120" + }, + { + "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": "test/multiple-subtitles-lifecycle.test.js", + "sha256": "422ea77c4171356efadbf0118193b6ced058021c43e7cbe2d691efdca43f02a1" + }, + { + "file": "test/multiple-subtitles-merge.test.js", + "sha256": "b04c961cbf04c05a10985edf29e12a278f6edcbc31e0b89337da487123acac8a" + }, + { + "file": "test/multiple-subtitles-failures.test.js", + "sha256": "6c06d4043b337c11fec4270c797e2059a29adaa73a078a400109ebd27ba60849" + }, + { + "file": "test/browser/multiple-subtitles-lifecycle.spec.js", + "sha256": "35e5b8b39e88e01ce7600708fc66d22111a16ee432c48acc956ab4f7445e6629" + }, + { + "file": "test/types/multiple-subtitles-runtime.ts", + "sha256": "5957929a7f1e60498d3b27cf0c15eca5610db9c1b5f6a8664cafbb1b60c00249" + }, + { + "file": "refactor/scripts/multiple-subtitles-runtime-types.test.mjs", + "sha256": "2043fe1fcc9213486d6343341bbec777046139b5b93694f46598e0739853b3f9" + } + ], + "browser": [ + { + "command": "yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js", + "candidate": "source", + "file": "refactor/.cache/multiple-subtitles07-browser-source.json", + "sha256": "6cd2c16f99e6e12704069ceb3519490bc30983c6b0df0d6b415ff063e4029382", + "resultsDirectory": "refactor/.cache/multiple-subtitles07-browser-source-results", + "stats": { + "startTime": "2026-09-13T03:41:20.718Z", + "duration": 22110.292999999998, + "expected": 36, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/ccab6f37-3bd0-44bf-bfde-c3815e0ef1a8", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c3af644b-100c-4897-a4d8-ddace3b6fe74", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a6b931a3-a669-4672-8cad-6bd9dc996945", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/458a089d-af12-4dc2-baca-d3db664fdff6", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/ccab6f37-3bd0-44bf-bfde-c3815e0ef1a8", + "blob:http://127.0.0.1:8084/77da888d-d4dd-4283-84d8-b40e6d147d03", + "blob:http://127.0.0.1:8084/c3af644b-100c-4897-a4d8-ddace3b6fe74", + "blob:http://127.0.0.1:8084/a3ea5dfe-fb9d-475a-bee4-2757a4911d8f", + "blob:http://127.0.0.1:8084/a6b931a3-a669-4672-8cad-6bd9dc996945", + "blob:http://127.0.0.1:8084/dab8a335-10b2-4bdb-9154-8a5066ab2c6e", + "blob:http://127.0.0.1:8084/458a089d-af12-4dc2-baca-d3db664fdff6" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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/eba4e23c-2267-472e-83c2-3a8f984004c2", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/eba4e23c-2267-472e-83c2-3a8f984004c2", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/eba4e23c-2267-472e-83c2-3a8f984004c2" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/665b798b-7172-47bc-84fe-55ed5b9baa23", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c2b1b716-09ae-4bbc-9aed-230181dbaeca", + "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/665b798b-7172-47bc-84fe-55ed5b9baa23", + "blob:http://127.0.0.1:8084/6a3a5dc9-1122-4d38-a075-8e21a7f8d920", + "blob:http://127.0.0.1:8084/c2b1b716-09ae-4bbc-9aed-230181dbaeca" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/ffd11880-4a10-43d7-94ef-c849a897e169", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e2d3ea60-e2b4-4e02-84ad-14db7531ed31", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/dbcfc46b-f1b5-40c8-8a7e-2308024fb843", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/ffd11880-4a10-43d7-94ef-c849a897e169", + "blob:http://127.0.0.1:8084/401f995c-1fc5-4043-9913-b9db9a458b9c", + "blob:http://127.0.0.1:8084/e2d3ea60-e2b4-4e02-84ad-14db7531ed31", + "blob:http://127.0.0.1:8084/b9ba5d33-30fd-4666-84cc-6d9211068253", + "blob:http://127.0.0.1:8084/dbcfc46b-f1b5-40c8-8a7e-2308024fb843" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/5c878426-acd2-4df0-805a-ebd58e50f55c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/53b62cb4-6d71-41ed-b4b1-f0dab216b666", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/41676127-06da-49cf-b30b-7a23d7625be4", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/7249e83a-b38d-4ed2-83a5-a0da4af6ff82", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5c878426-acd2-4df0-805a-ebd58e50f55c", + "blob:http://127.0.0.1:8084/011156e4-22ea-4296-9749-de3a497be6f0", + "blob:http://127.0.0.1:8084/53b62cb4-6d71-41ed-b4b1-f0dab216b666", + "blob:http://127.0.0.1:8084/54db44f9-e0a0-4a17-b740-0a06e1478126", + "blob:http://127.0.0.1:8084/41676127-06da-49cf-b30b-7a23d7625be4", + "blob:http://127.0.0.1:8084/1ac2de39-0623-4818-b360-cb9569b45b0a", + "blob:http://127.0.0.1:8084/1b672714-5cb2-440e-b815-e88fb9d4ec9b", + "blob:http://127.0.0.1:8084/7249e83a-b38d-4ed2-83a5-a0da4af6ff82" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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/2b41fda1-67cd-4a97-972a-c834ca3708ee", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/2b41fda1-67cd-4a97-972a-c834ca3708ee", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/bad39f38-d5be-4e51-a8e4-086e5c52ace0", + "blob:http://127.0.0.1:8084/2b41fda1-67cd-4a97-972a-c834ca3708ee" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/225168a9-7a13-4968-89f3-52186191c1b5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3641592b-13da-4e21-ac41-f70ed3690572", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/225168a9-7a13-4968-89f3-52186191c1b5", + "blob:http://127.0.0.1:8084/24e0bc1e-c1f4-48bb-afd2-39014c5b30ee", + "blob:http://127.0.0.1:8084/1d52cbaa-73e9-41a7-a3a0-4bcdb4f9431d", + "blob:http://127.0.0.1:8084/3641592b-13da-4e21-ac41-f70ed3690572" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/5478ca1b-f3cc-4251-b94e-e03d3b4b9588", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/88905703-4f8b-464b-8d7f-0743e0bcfab7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e3eea52c-510a-4a6c-a1fd-4de60a50449b", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5478ca1b-f3cc-4251-b94e-e03d3b4b9588", + "blob:http://127.0.0.1:8084/ecb870da-545d-4806-95ba-b68b3e3fc906", + "blob:http://127.0.0.1:8084/88905703-4f8b-464b-8d7f-0743e0bcfab7", + "blob:http://127.0.0.1:8084/54db2a47-c0d6-4991-adce-68d8a4a8f714", + "blob:http://127.0.0.1:8084/ac41eb74-c5bc-44e8-b533-e39af6f70e00", + "blob:http://127.0.0.1:8084/e3eea52c-510a-4a6c-a1fd-4de60a50449b" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3872cff5-ee01-484b-a0a6-b47c6bc246bf", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/fc37fcae-9378-4cad-a37a-9ff3a4acdcad", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/27dc00ad-7d9f-4539-8010-21cb5b32176d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/041a3524-8739-42f8-b82f-1ddc95f6101e", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/3872cff5-ee01-484b-a0a6-b47c6bc246bf", + "blob:http://127.0.0.1:8084/d41862d3-fb28-4570-8851-568db4aeecb6", + "blob:http://127.0.0.1:8084/fc37fcae-9378-4cad-a37a-9ff3a4acdcad", + "blob:http://127.0.0.1:8084/e7c15448-c082-4ed0-a0f5-2be989e41c3a", + "blob:http://127.0.0.1:8084/27dc00ad-7d9f-4539-8010-21cb5b32176d", + "blob:http://127.0.0.1:8084/aabb82bd-e1f9-444f-adde-c3b3dd788297", + "blob:http://127.0.0.1:8084/041a3524-8739-42f8-b82f-1ddc95f6101e" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/88b5a163-687a-43b2-98cf-a15dc509c7a8", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/88b5a163-687a-43b2-98cf-a15dc509c7a8" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/d8a4c7a1-7400-4c72-8d11-ec4372ff8799", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e541b76e-1771-49e4-a10f-ab5d65734f91", + "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/d8a4c7a1-7400-4c72-8d11-ec4372ff8799", + "blob:http://127.0.0.1:8084/b48d1eb2-f04f-4ed9-abd3-80cb883d4a14", + "blob:http://127.0.0.1:8084/e541b76e-1771-49e4-a10f-ab5d65734f91" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/644f1cdb-e29e-4041-8205-3441089fe914", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/722f9f5b-fb33-431b-9d04-13f67dd9aad1", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/454037c5-50c4-4c77-a4bf-107caa16e9fe", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/644f1cdb-e29e-4041-8205-3441089fe914", + "blob:http://127.0.0.1:8084/01b003d4-4693-4084-8ffc-e0671d30d988", + "blob:http://127.0.0.1:8084/722f9f5b-fb33-431b-9d04-13f67dd9aad1", + "blob:http://127.0.0.1:8084/4c4f73db-8d30-49fc-b879-a2b67c41b3d7", + "blob:http://127.0.0.1:8084/454037c5-50c4-4c77-a4bf-107caa16e9fe" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/7e8bcf7a-668c-4eea-8502-8b8a5d670bf9", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/42ce8e36-1d7c-42ad-9439-e2ea1de8e182", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/325b2284-3ddb-4ea4-9503-e568011e203a", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a4dea84a-b37d-4338-b05a-1f3a3717b1d6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7e8bcf7a-668c-4eea-8502-8b8a5d670bf9", + "blob:http://127.0.0.1:8084/b2aa6c26-6907-4db3-b65d-ad29a166799f", + "blob:http://127.0.0.1:8084/42ce8e36-1d7c-42ad-9439-e2ea1de8e182", + "blob:http://127.0.0.1:8084/cdded587-1a15-47b6-8d2b-a9148aa88bcc", + "blob:http://127.0.0.1:8084/325b2284-3ddb-4ea4-9503-e568011e203a", + "blob:http://127.0.0.1:8084/963330ad-0762-40eb-84f6-367219b0d814", + "blob:http://127.0.0.1:8084/fc34655d-1506-4e74-b781-e95f20c1145c", + "blob:http://127.0.0.1:8084/a4dea84a-b37d-4338-b05a-1f3a3717b1d6" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/19c19ecd-aedd-4e50-9069-d0f9eecb7973", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a989e882-20c5-49ff-ae0d-fcbf7d0b0429", + "blob:http://127.0.0.1:8084/19c19ecd-aedd-4e50-9069-d0f9eecb7973" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/7f773a49-d338-4332-ae38-5472bc27e74e", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/8bcee027-7482-47b2-96c8-032117cc6ab6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7f773a49-d338-4332-ae38-5472bc27e74e", + "blob:http://127.0.0.1:8084/13fdba02-d7e8-4796-a96a-12f7a6e8a397", + "blob:http://127.0.0.1:8084/5e73abbd-7421-4c6e-9aff-e1ba75a7552d", + "blob:http://127.0.0.1:8084/8bcee027-7482-47b2-96c8-032117cc6ab6" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/25de130b-62fb-4baf-be89-9bb694661b1f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/133ee9d7-78d2-42f3-9106-40c5533ce881", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9e9f14f5-1811-4059-ad3a-4cb13c656d73", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/25de130b-62fb-4baf-be89-9bb694661b1f", + "blob:http://127.0.0.1:8084/33876436-ed82-4c02-b887-41bac9463237", + "blob:http://127.0.0.1:8084/133ee9d7-78d2-42f3-9106-40c5533ce881", + "blob:http://127.0.0.1:8084/b2b5b545-d509-4d47-a3a3-d3f3ffc8307e", + "blob:http://127.0.0.1:8084/dc9cf57e-e451-47d0-8bee-5fe39cf4aaef", + "blob:http://127.0.0.1:8084/9e9f14f5-1811-4059-ad3a-4cb13c656d73" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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/9ce6b296-9bc5-4a1d-af71-520d8d8b5ec7", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/a1cef3f8-84de-42f2-9873-2e0371c6c7f8", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9c201c54-79d3-434e-968d-a41335e6ddda", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/34a95126-d1b9-4f55-a6bd-8bc205ab2b4d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ec1ba7c2-a0a0-43b0-9f7e-5aee2857af31", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/a1cef3f8-84de-42f2-9873-2e0371c6c7f8", + "blob:http://127.0.0.1:8084/07c968f2-377e-43f5-87f6-9bbf45d614c0", + "blob:http://127.0.0.1:8084/9c201c54-79d3-434e-968d-a41335e6ddda", + "blob:http://127.0.0.1:8084/8407aa25-b00b-43cb-8a77-3d82bc7d403f", + "blob:http://127.0.0.1:8084/34a95126-d1b9-4f55-a6bd-8bc205ab2b4d", + "blob:http://127.0.0.1:8084/9ce6b296-9bc5-4a1d-af71-520d8d8b5ec7", + "blob:http://127.0.0.1:8084/ec1ba7c2-a0a0-43b0-9f7e-5aee2857af31" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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/73c99008-8f3c-430a-9984-14148ce15787", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/73c99008-8f3c-430a-9984-14148ce15787", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/73c99008-8f3c-430a-9984-14148ce15787" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/fd59b2bf-001a-42e5-a995-b85f03498ef5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/8d925fd2-1ba4-4e3b-b70f-b85dca093422", + "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/fd59b2bf-001a-42e5-a995-b85f03498ef5", + "blob:http://127.0.0.1:8084/49e24f06-e1c6-4a8a-94a5-36ab139d4aa0", + "blob:http://127.0.0.1:8084/8d925fd2-1ba4-4e3b-b70f-b85dca093422" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/417c71da-7b8c-468d-8c92-49b0b68da4c1", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/eb51ecbc-4f52-47eb-9d6e-fcc401d61a70", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/7a253a9e-c18f-44bd-aa21-4a17b6c0ea5b", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/417c71da-7b8c-468d-8c92-49b0b68da4c1", + "blob:http://127.0.0.1:8084/2930afc5-6ac6-491b-8685-7ff8662baa20", + "blob:http://127.0.0.1:8084/eb51ecbc-4f52-47eb-9d6e-fcc401d61a70", + "blob:http://127.0.0.1:8084/78b291a5-809e-4f71-ac7c-bd421db94dde", + "blob:http://127.0.0.1:8084/7a253a9e-c18f-44bd-aa21-4a17b6c0ea5b" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/0e46417c-6a0e-4a49-a277-82aef341ff9a", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a79a060f-2d50-4744-9139-bc967623477a", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/d9127500-1302-4586-b162-26086ca7b334", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3324bbac-d313-47d3-a05a-2b7de2e793ab", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/0e46417c-6a0e-4a49-a277-82aef341ff9a", + "blob:http://127.0.0.1:8084/e12afcb6-eefd-40d6-9caf-2522685d4750", + "blob:http://127.0.0.1:8084/a79a060f-2d50-4744-9139-bc967623477a", + "blob:http://127.0.0.1:8084/089427ff-4139-4348-9625-acf9926865af", + "blob:http://127.0.0.1:8084/d9127500-1302-4586-b162-26086ca7b334", + "blob:http://127.0.0.1:8084/142e34e6-66a3-4173-bae4-b80bd7d31439", + "blob:http://127.0.0.1:8084/9e9377ed-803e-4c06-8e07-faed39bf7f0c", + "blob:http://127.0.0.1:8084/3324bbac-d313-47d3-a05a-2b7de2e793ab" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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/9495e452-3d53-410e-8220-e9a5aa7566a6", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/9495e452-3d53-410e-8220-e9a5aa7566a6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/fbafd078-4f49-4daa-869f-c078e0252eb4", + "blob:http://127.0.0.1:8084/9495e452-3d53-410e-8220-e9a5aa7566a6" + ], + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/ab234222-eecc-484f-8e75-f4323cbf8def", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/206c9a44-bbaf-44a4-a38e-e3e99d5666b6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ab234222-eecc-484f-8e75-f4323cbf8def", + "blob:http://127.0.0.1:8084/f44be2b9-cb65-4ff7-be37-6695a9856c90", + "blob:http://127.0.0.1:8084/b5e8dc85-0844-4dfa-8eb6-8fad92cdc76e", + "blob:http://127.0.0.1:8084/206c9a44-bbaf-44a4-a38e-e3e99d5666b6" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "a3c88975ded7d90b9f5a29ef9f5bcb9da9d81f14b47e3df8afbe1d27c0642d5e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/307544d2-ce59-4b94-a381-3c5a4473633f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/82abec14-1b36-45d7-825f-c6aeecbc7c47", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/28f54f7f-c60e-464c-9e21-8a3f83ce8395", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/307544d2-ce59-4b94-a381-3c5a4473633f", + "blob:http://127.0.0.1:8084/703ae175-f8e6-4a0c-8d7e-990e019194f2", + "blob:http://127.0.0.1:8084/82abec14-1b36-45d7-825f-c6aeecbc7c47", + "blob:http://127.0.0.1:8084/f4eaae8f-4643-4511-b46d-20216953975b", + "blob:http://127.0.0.1:8084/7bed3da5-f098-410e-b287-9ccdf9052c7d", + "blob:http://127.0.0.1:8084/28f54f7f-c60e-464c-9e21-8a3f83ce8395" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.vtt" + ] + } + } + } + ] + }, + { + "command": "yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js", + "candidate": "main", + "file": "refactor/.cache/multiple-subtitles07-browser-main.json", + "sha256": "0fa53006848473c8ac53d966d85956c99fbe152a527a839e37966437a5de7c6c", + "resultsDirectory": "refactor/.cache/multiple-subtitles07-browser-main-results", + "stats": { + "startTime": "2026-09-13T03:41:43.565Z", + "duration": 21464.524999999998, + "expected": 36, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/934e342f-41ce-4a9e-b80f-5ae7d225689e", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/0c81859c-6e0f-480d-8c77-139e1f30560c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/4aa8a355-9e33-48d3-a047-ef227c542f13", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/92918cae-7f0a-4c4a-9436-7979bb5209a9", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/934e342f-41ce-4a9e-b80f-5ae7d225689e", + "blob:http://127.0.0.1:8084/3443385b-ebab-43a4-be99-e4067bd27b9e", + "blob:http://127.0.0.1:8084/0c81859c-6e0f-480d-8c77-139e1f30560c", + "blob:http://127.0.0.1:8084/bd12f760-fe06-48ca-aea5-6ba8673290e2", + "blob:http://127.0.0.1:8084/4aa8a355-9e33-48d3-a047-ef227c542f13", + "blob:http://127.0.0.1:8084/e8cda4ff-dbc7-4be1-8152-a14560dbaef9", + "blob:http://127.0.0.1:8084/92918cae-7f0a-4c4a-9436-7979bb5209a9" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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/e8f6fff7-f5f1-488b-ae0b-91699bdeb1d1", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/e8f6fff7-f5f1-488b-ae0b-91699bdeb1d1", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/e8f6fff7-f5f1-488b-ae0b-91699bdeb1d1" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/4529bc0f-2b27-4891-b67a-46fc09acc5f6", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/5de8d568-ca4c-428b-972e-6918fa097a9f", + "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/4529bc0f-2b27-4891-b67a-46fc09acc5f6", + "blob:http://127.0.0.1:8084/1c66c3af-8d86-48fd-a866-a28749a1f636", + "blob:http://127.0.0.1:8084/5de8d568-ca4c-428b-972e-6918fa097a9f" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/7e8d7c41-a768-4eab-bd95-1105a072c3c3", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/98ff9b38-8eb4-470c-b2bf-1ed2389f3845", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b88deb58-937a-4794-bc0b-715995ecce24", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/7e8d7c41-a768-4eab-bd95-1105a072c3c3", + "blob:http://127.0.0.1:8084/252f6a89-c562-4ffe-8b0e-1aff2fe1ac99", + "blob:http://127.0.0.1:8084/98ff9b38-8eb4-470c-b2bf-1ed2389f3845", + "blob:http://127.0.0.1:8084/54dcaa47-d64c-4640-a523-c7e16e3dcea3", + "blob:http://127.0.0.1:8084/b88deb58-937a-4794-bc0b-715995ecce24" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3ce3e8fe-1460-4210-90dd-9b10e10c6a3e", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e82c347a-dd6a-4e08-b26a-ce9d230fdb94", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/02c8753f-2e5b-421c-9266-1d429467cade", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/67a4ca51-4275-4947-82ff-3a86ad8cbbab", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3ce3e8fe-1460-4210-90dd-9b10e10c6a3e", + "blob:http://127.0.0.1:8084/febf2c61-db5d-422e-8f68-f0365bdfdbcc", + "blob:http://127.0.0.1:8084/e82c347a-dd6a-4e08-b26a-ce9d230fdb94", + "blob:http://127.0.0.1:8084/b4bd39e4-ed69-4f31-802c-13f805721413", + "blob:http://127.0.0.1:8084/02c8753f-2e5b-421c-9266-1d429467cade", + "blob:http://127.0.0.1:8084/322087bb-5355-4a47-8779-49d4accd0abb", + "blob:http://127.0.0.1:8084/5e978c01-6377-4487-b51e-8c06d84f2c11", + "blob:http://127.0.0.1:8084/67a4ca51-4275-4947-82ff-3a86ad8cbbab" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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/6736362f-78d7-4ec6-94b6-a310bdfca948", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/6736362f-78d7-4ec6-94b6-a310bdfca948", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c69cca7c-6c43-4554-9fed-0dddf94ac17e", + "blob:http://127.0.0.1:8084/6736362f-78d7-4ec6-94b6-a310bdfca948" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/7880a814-3ae8-41db-857d-92bad4830361", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3a11d8e4-6811-495e-8cc1-c1178b77fdc4", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7880a814-3ae8-41db-857d-92bad4830361", + "blob:http://127.0.0.1:8084/3a99a834-1f5c-449d-8871-6319284e7199", + "blob:http://127.0.0.1:8084/4519c0f3-a688-421c-aeed-9f766a378029", + "blob:http://127.0.0.1:8084/3a11d8e4-6811-495e-8cc1-c1178b77fdc4" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/6ff4f3b0-7937-43fd-99e2-ad8cf6dd7a4c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/f4161c46-6cd8-4ab4-959f-6d02672f2b22", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/36cd6229-2e01-4483-833b-fbf01501699b", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6ff4f3b0-7937-43fd-99e2-ad8cf6dd7a4c", + "blob:http://127.0.0.1:8084/a09e889b-a97f-40da-a3b5-7ac50bb518be", + "blob:http://127.0.0.1:8084/f4161c46-6cd8-4ab4-959f-6d02672f2b22", + "blob:http://127.0.0.1:8084/7a7180c4-758f-44d9-a4ec-5a82d2eb889d", + "blob:http://127.0.0.1:8084/3cef9bf0-9817-4f8b-a90f-bf7371698edc", + "blob:http://127.0.0.1:8084/36cd6229-2e01-4483-833b-fbf01501699b" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/f4b6eea5-b503-4a16-9e19-d88b85ac0fe5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a9622bdd-9173-4cae-bbcc-a7c5c59efba2", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/1ca13960-9af2-4204-a070-6cc7b78e4c2c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/116463b3-9e23-4fe8-9544-64555a0c67e5", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/f4b6eea5-b503-4a16-9e19-d88b85ac0fe5", + "blob:http://127.0.0.1:8084/bc6bed61-1e71-42a0-aae1-a5c7f6cb9bc2", + "blob:http://127.0.0.1:8084/a9622bdd-9173-4cae-bbcc-a7c5c59efba2", + "blob:http://127.0.0.1:8084/17182504-4b92-442c-9751-b1702f8dff95", + "blob:http://127.0.0.1:8084/1ca13960-9af2-4204-a070-6cc7b78e4c2c", + "blob:http://127.0.0.1:8084/3520ca49-1f8b-4586-8eeb-8a1cd8e6e23f", + "blob:http://127.0.0.1:8084/116463b3-9e23-4fe8-9544-64555a0c67e5" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/2d404bdb-ae0f-41b7-9d13-7679e049b6e5", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/2d404bdb-ae0f-41b7-9d13-7679e049b6e5" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/68e6b971-d327-4a22-9287-6a53a6159a9f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/1161af0b-4172-4f8c-bd62-f58b8de30dee", + "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/68e6b971-d327-4a22-9287-6a53a6159a9f", + "blob:http://127.0.0.1:8084/488eed69-c2d1-47fe-b0e4-204b4495558d", + "blob:http://127.0.0.1:8084/1161af0b-4172-4f8c-bd62-f58b8de30dee" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/c4179b67-b0ff-47aa-b97a-e2ac3bdfc2d4", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9aee31ed-f872-4d53-8130-3a07c2438daa", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ffa028b6-a096-4642-b515-37a36feaf31a", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/c4179b67-b0ff-47aa-b97a-e2ac3bdfc2d4", + "blob:http://127.0.0.1:8084/8b8c64fb-5454-421a-b3d5-3a7a9a36a2b1", + "blob:http://127.0.0.1:8084/9aee31ed-f872-4d53-8130-3a07c2438daa", + "blob:http://127.0.0.1:8084/b274bd47-1bd9-4c3f-b5f6-44b2643195d7", + "blob:http://127.0.0.1:8084/ffa028b6-a096-4642-b515-37a36feaf31a" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/94155a26-73bd-4925-bd44-a80d4364fa26", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/42dda5cd-4792-467a-9bec-ac17511fa3eb", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/435fec3f-3c88-47d9-a46a-aa6368262671", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/10d216a9-ffb8-43b0-b6b5-5e9d729e3307", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/94155a26-73bd-4925-bd44-a80d4364fa26", + "blob:http://127.0.0.1:8084/28e9017c-0f98-41a6-93da-1481036253ec", + "blob:http://127.0.0.1:8084/42dda5cd-4792-467a-9bec-ac17511fa3eb", + "blob:http://127.0.0.1:8084/9f983d5d-9d63-4427-ae9b-c0d6d398f8f1", + "blob:http://127.0.0.1:8084/435fec3f-3c88-47d9-a46a-aa6368262671", + "blob:http://127.0.0.1:8084/e3d1c505-d7e8-4576-bcf6-d2b962287f7f", + "blob:http://127.0.0.1:8084/432b9752-3076-403e-b16a-8ebf49fcdaaa", + "blob:http://127.0.0.1:8084/10d216a9-ffb8-43b0-b6b5-5e9d729e3307" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/0839a6cd-ec2f-4261-8ffe-a39a0c31cf1f", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/8a59a91d-cbc7-4546-a557-23c0c0e46a33", + "blob:http://127.0.0.1:8084/0839a6cd-ec2f-4261-8ffe-a39a0c31cf1f" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/77690376-139c-409c-8d1f-283729f38ea2", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/29a0b03a-9596-4e4c-b894-95db1d50bf6a", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/77690376-139c-409c-8d1f-283729f38ea2", + "blob:http://127.0.0.1:8084/9b3beead-7cda-4084-9e32-aa390f42fe76", + "blob:http://127.0.0.1:8084/e803f337-2d93-4229-b0f5-78a73f18070a", + "blob:http://127.0.0.1:8084/29a0b03a-9596-4e4c-b894-95db1d50bf6a" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/d1c106c6-e898-49cd-b6ad-f2b9c3261dae", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/21d1973b-de8c-4e8a-ab11-8b733b6bf2ce", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/1d91088f-e95a-461e-a1a7-248566437947", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/d1c106c6-e898-49cd-b6ad-f2b9c3261dae", + "blob:http://127.0.0.1:8084/cd2e54ac-d315-4fef-b77d-ca2a2fa57317", + "blob:http://127.0.0.1:8084/21d1973b-de8c-4e8a-ab11-8b733b6bf2ce", + "blob:http://127.0.0.1:8084/c724157b-613a-4961-82de-ac3acb95e9aa", + "blob:http://127.0.0.1:8084/031cf00e-46f5-45a6-aa42-ed75edf4d508", + "blob:http://127.0.0.1:8084/1d91088f-e95a-461e-a1a7-248566437947" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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/0a2c0eec-e82e-4d14-b544-5629c6f1d1fa", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/e6ff67f9-ec50-4683-a2e7-1e83cb264bcf", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/80397020-d0cd-4108-aa66-1dd3d794a52f", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/48374d11-9659-4eb0-9c9b-6abaa4f071eb", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/64299ae8-c9c6-40d0-8391-72db6e856bcc", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/e6ff67f9-ec50-4683-a2e7-1e83cb264bcf", + "blob:http://127.0.0.1:8084/121bab3c-819d-48ed-a143-50b7513bf294", + "blob:http://127.0.0.1:8084/80397020-d0cd-4108-aa66-1dd3d794a52f", + "blob:http://127.0.0.1:8084/fb84e274-3649-4e9a-ab31-b03cf7534fe0", + "blob:http://127.0.0.1:8084/48374d11-9659-4eb0-9c9b-6abaa4f071eb", + "blob:http://127.0.0.1:8084/0a2c0eec-e82e-4d14-b544-5629c6f1d1fa", + "blob:http://127.0.0.1:8084/64299ae8-c9c6-40d0-8391-72db6e856bcc" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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/729282df-6b7f-425a-a9cf-d8c6a9ae2494", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/729282df-6b7f-425a-a9cf-d8c6a9ae2494", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/729282df-6b7f-425a-a9cf-d8c6a9ae2494" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/efcf30d6-8cb5-4106-a44b-3b2cf83b2d28", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ee7a4081-8cb9-49d8-920c-f35a6ebe2aae", + "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/efcf30d6-8cb5-4106-a44b-3b2cf83b2d28", + "blob:http://127.0.0.1:8084/d5e7f9c7-5f05-4b65-b3be-928afc3c7cff", + "blob:http://127.0.0.1:8084/ee7a4081-8cb9-49d8-920c-f35a6ebe2aae" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/61d2024b-1e60-4c72-b0f5-5dd02d179ee5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/25374cd2-8e99-4e03-a077-77a163da58d5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/572bb4c5-1303-4ab1-80f8-c77de20fa4c9", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/61d2024b-1e60-4c72-b0f5-5dd02d179ee5", + "blob:http://127.0.0.1:8084/1914b30e-79ec-47a6-b077-2c2de16a8105", + "blob:http://127.0.0.1:8084/25374cd2-8e99-4e03-a077-77a163da58d5", + "blob:http://127.0.0.1:8084/08cc3945-1546-4147-ac40-ddcc6a797db2", + "blob:http://127.0.0.1:8084/572bb4c5-1303-4ab1-80f8-c77de20fa4c9" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/60aab8bd-fb04-4073-b29c-acc130ebb1f0", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/bde2fb58-d7f4-46d3-beae-7a71613f4e99", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/4e37c628-fcfd-4ce7-bb7a-ae2e61e5072a", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/00e73585-c265-4e9d-a704-7000cf175eba", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/60aab8bd-fb04-4073-b29c-acc130ebb1f0", + "blob:http://127.0.0.1:8084/105d2a30-1bc9-43c6-8958-6d2388804d17", + "blob:http://127.0.0.1:8084/bde2fb58-d7f4-46d3-beae-7a71613f4e99", + "blob:http://127.0.0.1:8084/6044985e-a6b9-4ca9-bd42-bd3a2244a2b0", + "blob:http://127.0.0.1:8084/4e37c628-fcfd-4ce7-bb7a-ae2e61e5072a", + "blob:http://127.0.0.1:8084/dc4b5aa2-d04e-4843-a73f-0232ad3fc40f", + "blob:http://127.0.0.1:8084/97701bf3-9611-45e4-ab55-5b9694199d99", + "blob:http://127.0.0.1:8084/00e73585-c265-4e9d-a704-7000cf175eba" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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/18fac890-77e6-4bb1-9ffb-7644a3976755", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/18fac890-77e6-4bb1-9ffb-7644a3976755", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c66c7de6-5d4a-4763-b2c5-07bf90a38452", + "blob:http://127.0.0.1:8084/18fac890-77e6-4bb1-9ffb-7644a3976755" + ], + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/1ff6a6fd-2807-403f-a5f9-f96233aaa11c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/33890273-7b48-46ce-9c18-9fe0f83ed6bd", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1ff6a6fd-2807-403f-a5f9-f96233aaa11c", + "blob:http://127.0.0.1:8084/4c945d4f-a4ff-48b1-8daa-63fe18d22371", + "blob:http://127.0.0.1:8084/c47ee976-d607-4243-9d8e-35b5606d5e3d", + "blob:http://127.0.0.1:8084/33890273-7b48-46ce-9c18-9fe0f83ed6bd" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/48fa9aab-b27e-4823-ac14-ff8d90e0db09", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ed7b024e-f893-4362-9e34-b418cfdd5fdb", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/be54de1b-5ff2-4159-829e-c3e94a6d673c", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/48fa9aab-b27e-4823-ac14-ff8d90e0db09", + "blob:http://127.0.0.1:8084/c369cc6c-f14c-435f-b231-bd8721f08077", + "blob:http://127.0.0.1:8084/ed7b024e-f893-4362-9e34-b418cfdd5fdb", + "blob:http://127.0.0.1:8084/f458483e-a319-433f-ac97-69bc66edb02a", + "blob:http://127.0.0.1:8084/e32e1b03-ec03-4375-94a5-7e5d84f2843d", + "blob:http://127.0.0.1:8084/be54de1b-5ff2-4159-829e-c3e94a6d673c" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.vtt" + ] + } + } + } + ] + }, + { + "command": "yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js", + "candidate": "legacy", + "file": "refactor/.cache/multiple-subtitles07-browser-legacy.json", + "sha256": "f3e33ad475dae46c022b81696ec601588e32475c900d2d33a58250aca9769a92", + "resultsDirectory": "refactor/.cache/multiple-subtitles07-browser-legacy-results", + "stats": { + "startTime": "2026-09-13T03:42:05.701Z", + "duration": 21299.914, + "expected": 36, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/0b3fb15c-9753-4fd2-aed2-03a72999e75e", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/33b58c4e-84c2-4e38-8cb9-5eb6ebdeca2c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/5c951b4d-347e-46c8-9ea6-3e8bb538dd21", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/23b60c06-5164-4c39-bb87-37c12c677dc0", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/0b3fb15c-9753-4fd2-aed2-03a72999e75e", + "blob:http://127.0.0.1:8084/1a4550e4-e2a0-4f05-a17a-c52dc828815d", + "blob:http://127.0.0.1:8084/33b58c4e-84c2-4e38-8cb9-5eb6ebdeca2c", + "blob:http://127.0.0.1:8084/c5f352e6-f751-4d6a-bc39-7f4b42c53770", + "blob:http://127.0.0.1:8084/5c951b4d-347e-46c8-9ea6-3e8bb538dd21", + "blob:http://127.0.0.1:8084/f8b5487f-7f85-4a3c-aeab-5bf8595bf941", + "blob:http://127.0.0.1:8084/23b60c06-5164-4c39-bb87-37c12c677dc0" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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/6bddfe64-46ee-4460-8f47-879b3fd2c8bc", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/6bddfe64-46ee-4460-8f47-879b3fd2c8bc", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/6bddfe64-46ee-4460-8f47-879b3fd2c8bc" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/1cc9a0e8-0df5-4f2b-bbe6-9f75a706c195", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/64cc7851-4339-44dd-bd77-8350925911cd", + "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/1cc9a0e8-0df5-4f2b-bbe6-9f75a706c195", + "blob:http://127.0.0.1:8084/a0b3b622-b64b-4a5c-a01e-8cffe382cd5f", + "blob:http://127.0.0.1:8084/64cc7851-4339-44dd-bd77-8350925911cd" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/ac712f40-cf7c-4f0a-a243-47201bf269ca", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b28343f0-3b2a-42ca-9d96-d0103eaa9268", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/de0003ce-0a49-4162-8828-3e646886c7af", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/ac712f40-cf7c-4f0a-a243-47201bf269ca", + "blob:http://127.0.0.1:8084/d2e6c204-c499-4a42-9b4d-40aea237ec26", + "blob:http://127.0.0.1:8084/b28343f0-3b2a-42ca-9d96-d0103eaa9268", + "blob:http://127.0.0.1:8084/bb51c1a3-e9fa-48d4-9d1b-7bd02738c1ef", + "blob:http://127.0.0.1:8084/de0003ce-0a49-4162-8828-3e646886c7af" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/b27668d6-0572-4a69-b079-0d8bd81670b5", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ad18a6c5-ae97-413a-91fc-a154be796d8c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/65103c45-8190-4a7d-908a-840780a7b9c8", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/161fce9d-56eb-419f-8989-f16797dc4055", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/b27668d6-0572-4a69-b079-0d8bd81670b5", + "blob:http://127.0.0.1:8084/d0f871a1-364a-4494-8777-9d9c5929bbed", + "blob:http://127.0.0.1:8084/ad18a6c5-ae97-413a-91fc-a154be796d8c", + "blob:http://127.0.0.1:8084/fcda7168-2220-4d25-8252-8fc54d934d1f", + "blob:http://127.0.0.1:8084/65103c45-8190-4a7d-908a-840780a7b9c8", + "blob:http://127.0.0.1:8084/3e727ea9-324e-4766-b4d7-19c8dd76a291", + "blob:http://127.0.0.1:8084/4dad1f67-52b2-4520-9805-6dd0a395eb1a", + "blob:http://127.0.0.1:8084/161fce9d-56eb-419f-8989-f16797dc4055" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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/86d5da1f-6844-45cf-ac00-8aaae7f8841f", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_FILE_NOT_FOUND" + } + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/86d5da1f-6844-45cf-ac00-8aaae7f8841f", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/096159a3-99aa-4f74-8206-37f141141a62", + "blob:http://127.0.0.1:8084/86d5da1f-6844-45cf-ac00-8aaae7f8841f" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/5112179d-aed3-4b12-aed5-953c8270466e", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/cd7bd161-5305-4b9c-b62b-f08fbe3923c6", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5112179d-aed3-4b12-aed5-953c8270466e", + "blob:http://127.0.0.1:8084/b1c3eafb-2116-4c99-99ab-6601017bb571", + "blob:http://127.0.0.1:8084/65bd5e76-2d2f-44b8-9377-c97ecd56ca17", + "blob:http://127.0.0.1:8084/cd7bd161-5305-4b9c-b62b-f08fbe3923c6" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/4cef068c-bd66-4c69-8b6f-4f792a65f595", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e5c3c92e-56ca-4e5d-b530-cea7e2b19962", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c363e624-35e0-4145-94f4-de10c4492198", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/4cef068c-bd66-4c69-8b6f-4f792a65f595", + "blob:http://127.0.0.1:8084/6349ddc5-6083-48e5-90c6-55b298d5f484", + "blob:http://127.0.0.1:8084/e5c3c92e-56ca-4e5d-b530-cea7e2b19962", + "blob:http://127.0.0.1:8084/040313a4-95b3-4de4-9143-1c7248739f46", + "blob:http://127.0.0.1:8084/6493afdf-fbbd-4fe9-9fa4-e3b641ac94ec", + "blob:http://127.0.0.1:8084/c363e624-35e0-4145-94f4-de10c4492198" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/3b9b2a64-e746-435f-886e-8e038a12131d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/7b7fdd08-cee5-4f0c-a3b2-a4c20c6d3433", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/650dafbf-b391-4854-8a36-57b0baab1005", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/a9c480f3-a0a4-4fb3-82c2-274225314226", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/3b9b2a64-e746-435f-886e-8e038a12131d", + "blob:http://127.0.0.1:8084/9cafa11e-35d1-4cc7-a7da-b9411a3cfbc1", + "blob:http://127.0.0.1:8084/7b7fdd08-cee5-4f0c-a3b2-a4c20c6d3433", + "blob:http://127.0.0.1:8084/3d377de9-1808-4ae1-8125-a50e6528903b", + "blob:http://127.0.0.1:8084/650dafbf-b391-4854-8a36-57b0baab1005", + "blob:http://127.0.0.1:8084/ba7a9e7d-6ff0-47e3-a295-00b1ecd9c60e", + "blob:http://127.0.0.1:8084/a9c480f3-a0a4-4fb3-82c2-274225314226" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/56431d85-5185-42d1-a884-2e95d22bb554", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/56431d85-5185-42d1-a884-2e95d22bb554" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/c03d3099-4318-462e-81cb-0fb324a4a6e7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/adff6ae0-6e05-4cd3-88c6-fb6a5daa0ef5", + "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/c03d3099-4318-462e-81cb-0fb324a4a6e7", + "blob:http://127.0.0.1:8084/a61a91da-339e-430b-bcb5-191202fc53dc", + "blob:http://127.0.0.1:8084/adff6ae0-6e05-4cd3-88c6-fb6a5daa0ef5" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/9c26a9fe-6e0c-4136-a63b-b923d56670ca", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/e06ef665-5b3c-40dc-af89-e60cc35437c0", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/6bf5d025-0bed-42ea-b1e6-00097d12770d", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/9c26a9fe-6e0c-4136-a63b-b923d56670ca", + "blob:http://127.0.0.1:8084/65f24909-9536-45a3-8fc4-e765e05a6877", + "blob:http://127.0.0.1:8084/e06ef665-5b3c-40dc-af89-e60cc35437c0", + "blob:http://127.0.0.1:8084/f3152295-e530-4485-8924-0904522071c8", + "blob:http://127.0.0.1:8084/6bf5d025-0bed-42ea-b1e6-00097d12770d" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/dd000664-fe28-44e2-8dde-fd8b4e0726b2", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/887863db-52ab-44b7-870e-049877d2121c", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/41706841-49c7-418f-9b27-cb5caf5408b7", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/682bed09-e342-4ccf-92db-02887f09578f", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/dd000664-fe28-44e2-8dde-fd8b4e0726b2", + "blob:http://127.0.0.1:8084/b5cabe8b-755e-426d-a250-ee00d2916048", + "blob:http://127.0.0.1:8084/887863db-52ab-44b7-870e-049877d2121c", + "blob:http://127.0.0.1:8084/7fa2bce6-a5ba-431d-ab47-01659559a4d9", + "blob:http://127.0.0.1:8084/41706841-49c7-418f-9b27-cb5caf5408b7", + "blob:http://127.0.0.1:8084/b96f043b-b670-4d89-88d5-26bd8d026541", + "blob:http://127.0.0.1:8084/e94bfdac-b361-4375-9d12-a6664b8e20b1", + "blob:http://127.0.0.1:8084/682bed09-e342-4ccf-92db-02887f09578f" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/919f7e7e-8384-44a0-9806-f38f801d6c27", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6d824de6-7bcf-48f4-bd85-50cfa8421890", + "blob:http://127.0.0.1:8084/919f7e7e-8384-44a0-9806-f38f801d6c27" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/6b2c77e3-3b34-42d2-a723-79cb7d602081", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/f345af55-0223-4275-8afa-352a8a09d9e7", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6b2c77e3-3b34-42d2-a723-79cb7d602081", + "blob:http://127.0.0.1:8084/793f3a6d-84e6-4598-9572-556806905c8c", + "blob:http://127.0.0.1:8084/cbc988d7-7f9e-441a-8aba-83ee8972419a", + "blob:http://127.0.0.1:8084/f345af55-0223-4275-8afa-352a8a09d9e7" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/f9a11637-f794-47ce-853c-47eb71b872b0", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/95229cf9-1c00-4497-9edd-a01398b0e6ff", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c5a1cbbe-a6ac-4adf-a706-16fb9da84606", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/f9a11637-f794-47ce-853c-47eb71b872b0", + "blob:http://127.0.0.1:8084/b94e9b09-d216-4872-9c42-cda2d427658b", + "blob:http://127.0.0.1:8084/95229cf9-1c00-4497-9edd-a01398b0e6ff", + "blob:http://127.0.0.1:8084/8f82d5f4-5e59-4e24-936c-f9f29af5eb8d", + "blob:http://127.0.0.1:8084/0d7b5591-083a-43df-984a-0343e706e807", + "blob:http://127.0.0.1:8084/c5a1cbbe-a6ac-4adf-a706-16fb9da84606" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/cce7df51-8882-4736-be5b-0db758f332da", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3f2abf4c-ac7b-4740-a6ce-025edaf0163d", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/c3668762-5f33-4704-abd9-ce18aaa137ba", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/25e4c6d9-1a6a-4678-a43f-0cea84df6a22", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/cce7df51-8882-4736-be5b-0db758f332da", + "blob:http://127.0.0.1:8084/e78ce827-0606-4b7d-9320-ba072f2f3c28", + "blob:http://127.0.0.1:8084/3f2abf4c-ac7b-4740-a6ce-025edaf0163d", + "blob:http://127.0.0.1:8084/28d8c937-830b-4937-8b41-6f881e7ad77e", + "blob:http://127.0.0.1:8084/c3668762-5f33-4704-abd9-ce18aaa137ba", + "blob:http://127.0.0.1:8084/462d9aab-2516-4cde-9f3d-31409e2616fe", + "blob:http://127.0.0.1:8084/25e4c6d9-1a6a-4678-a43f-0cea84df6a22" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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/b0bad2ce-d005-40ba-ba93-8d5348351cbd", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/b0bad2ce-d005-40ba-ba93-8d5348351cbd", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/b0bad2ce-d005-40ba-ba93-8d5348351cbd" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/498075a5-bed3-4b49-8cec-04a59ee72363", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/6c9e401b-54f2-4e0f-9cea-79141981cd0f", + "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/498075a5-bed3-4b49-8cec-04a59ee72363", + "blob:http://127.0.0.1:8084/0f86a49d-8d76-48ab-99dd-317247c364db", + "blob:http://127.0.0.1:8084/6c9e401b-54f2-4e0f-9cea-79141981cd0f" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "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/fb99c902-a332-44db-be4d-15baea5308f6", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/b30a66b8-4051-4370-a9c9-144acd91f3a6", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/9a84a8e4-c90a-4a5d-9cf2-88479ce67a9a", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/bb9be288-49f1-4ee4-8776-d774805b9792", + "destroyed": false + } + ], + "revoked": [ + "http://127.0.0.1:8084/test/player.html?core=published", + "blob:http://127.0.0.1:8084/b30a66b8-4051-4370-a9c9-144acd91f3a6", + "blob:http://127.0.0.1:8084/a69d92b5-22d9-45c2-9b70-424a2a5e1e3d", + "blob:http://127.0.0.1:8084/9a84a8e4-c90a-4a5d-9cf2-88479ce67a9a", + "blob:http://127.0.0.1:8084/fb99c902-a332-44db-be4d-15baea5308f6", + "blob:http://127.0.0.1:8084/bb9be288-49f1-4ee4-8776-d774805b9792" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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/89d1ccd0-6461-4861-9376-0b14661709b2", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/8657d093-b9a9-4db9-b64c-c70a9dd97702", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/92941306-dd9a-46a9-ae4c-cf53f42a86ff", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/ace14501-88bf-432a-90e3-030c9a4bee84", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/699546b9-8c5f-40a3-bfbf-9e7faed05a59", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/8657d093-b9a9-4db9-b64c-c70a9dd97702", + "blob:http://127.0.0.1:8084/9c6ec8dc-f377-4463-aa99-29f414a872a4", + "blob:http://127.0.0.1:8084/92941306-dd9a-46a9-ae4c-cf53f42a86ff", + "blob:http://127.0.0.1:8084/377dbdb6-93a9-477b-9c2f-739acd9de321", + "blob:http://127.0.0.1:8084/ace14501-88bf-432a-90e3-030c9a4bee84", + "blob:http://127.0.0.1:8084/89d1ccd0-6461-4861-9376-0b14661709b2", + "blob:http://127.0.0.1:8084/a62fc13f-c811-495f-8014-f0970805dc5c", + "blob:http://127.0.0.1:8084/699546b9-8c5f-40a3-bfbf-9e7faed05a59" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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/57930a62-bbf3-400c-8a91-f20cef401789", + "resourceType": "fetch", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/57930a62-bbf3-400c-8a91-f20cef401789", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/09ea01c0-6385-4018-bcd4-1bb176e461d4", + "blob:http://127.0.0.1:8084/57930a62-bbf3-400c-8a91-f20cef401789" + ], + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "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": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/f7f2d3ea-6529-44ad-9c70-fd3ea29fe31a", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/6d9edd14-785a-4c63-82fa-260d4c54d299", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/f7f2d3ea-6529-44ad-9c70-fd3ea29fe31a", + "blob:http://127.0.0.1:8084/52d1599b-73fb-40cf-9c8f-cc2554c745b2", + "blob:http://127.0.0.1:8084/82643014-2213-48a2-9b0e-b124765c2661", + "blob:http://127.0.0.1:8084/6d9edd14-785a-4c63-82fa-260d4c54d299" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-restart.vtt" + ] + } + } + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "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/de2c14be-140a-47df-bdba-623cf909efd1", + "resourceType": "other", + "failure": null + } + ] + }, + "evidence": { + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e" + }, + "observation": { + "native": { + "text": "<00:01.250>
Before
<00:02.000>
after
<00:03.500>", + "timestamps": [ + "00:00:01.250", + "00:00:02.000", + "00:00:03.500" + ] + }, + "visible": "\n Before after\n " + }, + "probe": { + "initialized": [ + { + "url": "blob:http://127.0.0.1:8084/2ba19f65-c441-4bef-ad56-f547b213e6b8", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/3edebb6e-c492-4222-b4ec-484569155ff9", + "destroyed": false + }, + { + "url": "blob:http://127.0.0.1:8084/b65d3406-bf8b-4ceb-8901-a1a95e4d65ef", + "destroyed": false + } + ], + "revoked": [ + "blob:http://127.0.0.1:8084/2ba19f65-c441-4bef-ad56-f547b213e6b8", + "blob:http://127.0.0.1:8084/0cde4952-0f5a-42c5-b6c0-6142e03b1794", + "blob:http://127.0.0.1:8084/3edebb6e-c492-4222-b4ec-484569155ff9", + "blob:http://127.0.0.1:8084/de2c14be-140a-47df-bdba-623cf909efd1", + "blob:http://127.0.0.1:8084/fa2769da-c5cc-46e7-9b24-087888051f33", + "blob:http://127.0.0.1:8084/b65d3406-bf8b-4ceb-8901-a1a95e4d65ef" + ], + "signal": null, + "errors": [], + "requests": [ + "/test/multiple-timed.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" + }, + "intermediateFailures": [ + { + "file": "refactor/.cache/multiple-subtitles07-initial-browser.json", + "sha256": "36571069122618ccc400e0eb39ef6213509a595785ebdd295256d07c18e29742", + "stats": { + "startTime": "2026-09-13T03:25:57.422Z", + "duration": 4280.911, + "expected": 0, + "skipped": 0, + "unexpected": 2, + "flaky": 0 + }, + "observation": "Bare valid timestamps were visibly rendered in both core HTML layers.", + "resultsDirectory": "refactor/.cache/multiple-subtitles07-initial-browser-results", + "errors": [ + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "chromium", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mnot\u001b[2m.\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: not \u001b[32m\"<00:02.000>\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \u001b[7m<00:02.000>\u001b[27m after\u001b[39m\n\u001b[31m \"\u001b[39m\n\n 195 | expect(visible).toContain('Before')\n 196 | expect(visible).toContain('after')\n> 197 | expect(visible).not.toContain('<00:02.000>')\n | ^\n 198 | await page.evaluate(() => window.art.destroy(false))\n 199 | await evidence(page, testInfo, core, { native, visible })\n 200 | })\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:25" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "chromium", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mnot\u001b[2m.\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: not \u001b[32m\"<00:02.000>\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \u001b[7m<00:02.000>\u001b[27m after\u001b[39m\n\u001b[31m \"\u001b[39m\n\n 195 | expect(visible).toContain('Before')\n 196 | expect(visible).toContain('after')\n> 197 | expect(visible).not.toContain('<00:02.000>')\n | ^\n 198 | await page.evaluate(() => window.art.destroy(false))\n 199 | await evidence(page, testInfo, core, { native, visible })\n 200 | })\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:25" + ] + } + ], + "scope": "Archived intermediate uncommitted implementations, not current acceptance; logs/screenshots/traces preserve the observed failures." + }, + { + "file": "refactor/.cache/multiple-subtitles07-caption-browser.json", + "sha256": "1be73a8c140411ab0629a8a8d8eba9ca7976ad7b9156b56fbf378932af3f3300", + "stats": { + "startTime": "2026-09-13T03:29:24.006Z", + "duration": 24043.054, + "expected": 0, + "skipped": 0, + "unexpected": 6, + "flaky": 0 + }, + "observation": "Standard entity inputs exposed pre-existing vendor semicolon corruption.", + "resultsDirectory": "refactor/.cache/multiple-subtitles07-caption-browser-results", + "errors": [ + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "chromium", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoHaveText\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m failed\n\nLocator: locator('.art-subtitle-literal')\nExpected: \u001b[32m\"Literal <00:02.000> example\"\u001b[39m\nReceived: \u001b[31m\"Literal <\u001b[7m;\u001b[27m00:02.000>\u001b[7m;\u001b[27m example\"\u001b[39m\nTimeout: 7000ms\n\nCall log:\n\u001b[2m - Expect \"toHaveText\" locator('.art-subtitle-literal') with timeout 7000ms\u001b[22m\n\u001b[2m - waiting for locator('.art-subtitle-literal')\u001b[22m\n\u001b[2m 18 × locator resolved to
Literal <;00:02.000>; example
\u001b[22m\n\u001b[2m - unexpected value \"Literal <;00:02.000>; example\"\u001b[22m\n\n\n 198 | expect(visible).toContain('after')\n 199 | expect(visible).not.toContain('<00:02.000>')\n> 200 | await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example')\n | ^\n 201 | await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['literal']))\n 202 | await expect(page.locator('.art-subtitle-timed')).toHaveCount(0)\n 203 | await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:200:57" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "chromium", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoHaveText\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m failed\n\nLocator: locator('.art-subtitle-literal')\nExpected: \u001b[32m\"Literal <00:02.000> example\"\u001b[39m\nReceived: \u001b[31m\"Literal <\u001b[7m;\u001b[27m00:02.000>\u001b[7m;\u001b[27m example\"\u001b[39m\nTimeout: 7000ms\n\nCall log:\n\u001b[2m - Expect \"toHaveText\" locator('.art-subtitle-literal') with timeout 7000ms\u001b[22m\n\u001b[2m - waiting for locator('.art-subtitle-literal')\u001b[22m\n\u001b[2m 18 × locator resolved to
Literal <;00:02.000>; example
\u001b[22m\n\u001b[2m - unexpected value \"Literal <;00:02.000>; example\"\u001b[22m\n\n\n 198 | expect(visible).toContain('after')\n 199 | expect(visible).not.toContain('<00:02.000>')\n> 200 | await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example')\n | ^\n 201 | await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['literal']))\n 202 | await expect(page.locator('.art-subtitle-timed')).toHaveCount(0)\n 203 | await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:200:57" + ] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "firefox", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoHaveText\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m failed\n\nLocator: locator('.art-subtitle-literal')\nExpected: \u001b[32m\"Literal <00:02.000> example\"\u001b[39m\nReceived: \u001b[31m\"Literal <\u001b[7m;\u001b[27m00:02.000>\u001b[7m;\u001b[27m example\"\u001b[39m\nTimeout: 7000ms\n\nCall log:\n\u001b[2m - Expect \"toHaveText\" locator('.art-subtitle-literal') with timeout 7000ms\u001b[22m\n\u001b[2m - waiting for locator('.art-subtitle-literal')\u001b[22m\n\u001b[2m 17 × locator resolved to
Literal <;00:02.000>; example
\u001b[22m\n\u001b[2m - unexpected value \"Literal <;00:02.000>; example\"\u001b[22m\n\n\n 198 | expect(visible).toContain('after')\n 199 | expect(visible).not.toContain('<00:02.000>')\n> 200 | await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example')\n | ^\n 201 | await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['literal']))\n 202 | await expect(page.locator('.art-subtitle-timed')).toHaveCount(0)\n 203 | await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:200:57" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "firefox", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoHaveText\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m failed\n\nLocator: locator('.art-subtitle-literal')\nExpected: \u001b[32m\"Literal <00:02.000> example\"\u001b[39m\nReceived: \u001b[31m\"Literal <\u001b[7m;\u001b[27m00:02.000>\u001b[7m;\u001b[27m example\"\u001b[39m\nTimeout: 7000ms\n\nCall log:\n\u001b[2m - Expect \"toHaveText\" locator('.art-subtitle-literal') with timeout 7000ms\u001b[22m\n\u001b[2m - waiting for locator('.art-subtitle-literal')\u001b[22m\n\u001b[2m 18 × locator resolved to
Literal <;00:02.000>; example
\u001b[22m\n\u001b[2m - unexpected value \"Literal <;00:02.000>; example\"\u001b[22m\n\n\n 198 | expect(visible).toContain('after')\n 199 | expect(visible).not.toContain('<00:02.000>')\n> 200 | await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example')\n | ^\n 201 | await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['literal']))\n 202 | await expect(page.locator('.art-subtitle-timed')).toHaveCount(0)\n 203 | await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:200:57" + ] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "webkit", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoHaveText\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m failed\n\nLocator: locator('.art-subtitle-literal')\nExpected: \u001b[32m\"Literal <00:02.000> example\"\u001b[39m\nReceived: \u001b[31m\"Literal <\u001b[7m;\u001b[27m00:02.000>\u001b[7m;\u001b[27m example\"\u001b[39m\nTimeout: 7000ms\n\nCall log:\n\u001b[2m - Expect \"toHaveText\" locator('.art-subtitle-literal') with timeout 7000ms\u001b[22m\n\u001b[2m - waiting for locator('.art-subtitle-literal')\u001b[22m\n\u001b[2m 17 × locator resolved to
Literal <;00:02.000>; example
\u001b[22m\n\u001b[2m - unexpected value \"Literal <;00:02.000>; example\"\u001b[22m\n\n\n 198 | expect(visible).toContain('after')\n 199 | expect(visible).not.toContain('<00:02.000>')\n> 200 | await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example')\n | ^\n 201 | await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['literal']))\n 202 | await expect(page.locator('.art-subtitle-timed')).toHaveCount(0)\n 203 | await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:200:57" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "webkit", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoHaveText\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m failed\n\nLocator: locator('.art-subtitle-literal')\nExpected: \u001b[32m\"Literal <00:02.000> example\"\u001b[39m\nReceived: \u001b[31m\"Literal <\u001b[7m;\u001b[27m00:02.000>\u001b[7m;\u001b[27m example\"\u001b[39m\nTimeout: 7000ms\n\nCall log:\n\u001b[2m - Expect \"toHaveText\" locator('.art-subtitle-literal') with timeout 7000ms\u001b[22m\n\u001b[2m - waiting for locator('.art-subtitle-literal')\u001b[22m\n\u001b[2m 17 × locator resolved to
Literal <;00:02.000>; example
\u001b[22m\n\u001b[2m - unexpected value \"Literal <;00:02.000>; example\"\u001b[22m\n\n\n 198 | expect(visible).toContain('after')\n 199 | expect(visible).not.toContain('<00:02.000>')\n> 200 | await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example')\n | ^\n 201 | await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['literal']))\n 202 | await expect(page.locator('.art-subtitle-timed')).toHaveCount(0)\n 203 | await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:200:57" + ] + } + ], + "scope": "Archived intermediate uncommitted implementations, not current acceptance; logs/screenshots/traces preserve the observed failures." + }, + { + "file": "refactor/.cache/multiple-subtitles07-marker-browser.json", + "sha256": "6895d3d772f982539ba1891782a149e7505d06c38b5a918d208f0167d249900a", + "stats": { + "startTime": "2026-09-13T03:35:39.585Z", + "duration": 9820.661, + "expected": 0, + "skipped": 0, + "unexpected": 6, + "flaky": 0 + }, + "observation": "Removing a VTT class element also removed following HTML caption nodes because HTML closing-tag matching differs.", + "resultsDirectory": "refactor/.cache/multiple-subtitles07-marker-browser-results", + "errors": [ + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "chromium", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"after\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \"\u001b[39m\n\n 195 | const visible = await timedLine.textContent()\n 196 | expect(visible).toContain('Before')\n> 197 | expect(visible).toContain('after')\n | ^\n 198 | expect(visible).not.toContain('<00:02.000>')\n 199 | // Public cue edits exercise literal display without relying on the vendor's entity decoder.\n 200 | await page.evaluate(() => {\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:21" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "chromium", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"after\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \"\u001b[39m\n\n 195 | const visible = await timedLine.textContent()\n 196 | expect(visible).toContain('Before')\n> 197 | expect(visible).toContain('after')\n | ^\n 198 | expect(visible).not.toContain('<00:02.000>')\n 199 | // Public cue edits exercise literal display without relying on the vendor's entity decoder.\n 200 | await page.evaluate(() => {\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:21" + ] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "firefox", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"after\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \"\u001b[39m\n\n 195 | const visible = await timedLine.textContent()\n 196 | expect(visible).toContain('Before')\n> 197 | expect(visible).toContain('after')\n | ^\n 198 | expect(visible).not.toContain('<00:02.000>')\n 199 | // Public cue edits exercise literal display without relying on the vendor's entity decoder.\n 200 | await page.evaluate(() => {\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:21" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "firefox", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"after\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \"\u001b[39m\n\n 195 | const visible = await timedLine.textContent()\n 196 | expect(visible).toContain('Before')\n> 197 | expect(visible).toContain('after')\n | ^\n 198 | expect(visible).not.toContain('<00:02.000>')\n 199 | // Public cue edits exercise literal display without relying on the vendor's entity decoder.\n 200 | await page.evaluate(() => {\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:21" + ] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "project": "webkit", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"after\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \"\u001b[39m\n\n 195 | const visible = await timedLine.textContent()\n 196 | expect(visible).toContain('Before')\n> 197 | expect(visible).toContain('after')\n | ^\n 198 | expect(visible).not.toContain('<00:02.000>')\n 199 | // Public cue edits exercise literal display without relying on the vendor's entity decoder.\n 200 | await page.evaluate(() => {\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:21" + ] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "project": "webkit", + "status": "failed", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"after\"\u001b[39m\nReceived string: \u001b[31m\"\u001b[39m\n\u001b[31m Before \"\u001b[39m\n\n 195 | const visible = await timedLine.textContent()\n 196 | expect(visible).toContain('Before')\n> 197 | expect(visible).toContain('after')\n | ^\n 198 | expect(visible).not.toContain('<00:02.000>')\n 199 | // Public cue edits exercise literal display without relying on the vendor's entity decoder.\n 200 | await page.evaluate(() => {\n at D:\\github\\ArtPlayer\\test\\browser\\multiple-subtitles-lifecycle.spec.js:197:21" + ] + } + ], + "scope": "Archived intermediate uncommitted implementations, not current acceptance; logs/screenshots/traces preserve the observed failures." + } + ], + "artifacts": [ + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js", + "sha256": "7abb6571fcdc371195c56004d6fc0f0980d9651777c2089baf87be27739ad618", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.js" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js", + "sha256": "339836d8a56d1c8168857b579cd241940e4c4b5d77d52dbc470fd49e51fbe92e", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs", + "sha256": "ebf6e73aa421d6715e07babe1c3835b575c400e907a4cc760c15c3e9d70a28b6", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.mjs" + } + ], + "remaining": "Public synchronous declarations and installed consumer compatibility remain task 04. ENTITY-01 is a separately reproduced vendor defect owned by task 05. Full supported cores, physical Safari/mobile, editor and release acceptance remain 05/06 and release reviews. Literal display uses public cue.text edits, not proof that vendor entity decoding was fixed." +} diff --git a/refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md b/refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md new file mode 100644 index 000000000..6bb0ade98 --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md @@ -0,0 +1,49 @@ +# PKG-MULTI-SUB-07 内嵌时间戳与显示层修复 + +九份历史实现已复现 `<00:02.000>` 被包装成字符串并输出 ``。 +本次保留数字 TimestampNode,处理顶层与嵌套节点,在有效的 VTT class 节点中承载 +时间戳。解析树仍不可变,重复选择/reset 不积累包装;文本、标签和轨序的原回归保留。 +私有 WrappedNode 现在与 CueNode 一样要求数字时间戳,编译负例从 8 项增为 9 项。 + +## 原生与自定义字幕层 + +直接输出有效时间戳后,Chromium 中的发布/候选核心都把 `<00:02.000>` 显示成文字。 +这不是测试超时或资源加载失败,最初两个失败断言及截图/trace 已保留。 +因此新增 caption.ts,在既有 subtitleAfterUpdate 事件后处理插件生成的专用标记。 + +- 原生 WebVTT 中的 `` 是合法 class 节点, + 其中的数字时间戳仍解析为 processing instruction,cue.text 不被显示层修改。 +- HTML 不会把 `` 与 `` 配对,后面的字幕可能成为该元素的子节点。 + 直接删除标记曾导致后文丢失;已通过真实浏览器复现并修正为移除前导时间指令、 + 将其余子节点移回父层。对应失败报告保留,最终断言检查前后文字。 +- 不对整段文字做时间戳正则删除。仅处理专用标记中的前导指令,普通用户文字和 + 原有 `.art-subtitle-` CSS 钩子保留。public cue.text 修改产生的字面量标签 + 有真实浏览器回归,清空/reset 后正常恢复原字幕。 +- 注册的监听器由当前 lifetime 管理,失败/销毁时移除;已有资源测试改为检查所有 + 监听器集合为空。没有增加新公开选项、返回值或事件,不改变逐 cue 显示策略。 + +## 验证范围 + +纯测试覆盖开始/中间/末尾、多个毫秒时间戳、Unicode、嵌套标签、冻结树、重复选择 +和重新解析有效 VTT;历史时间戳失败仍保持,不将候选修复覆盖进旧预期。 +原生场景检查开头/中间/末尾三个 processing instruction、前后文字、红色样式、 +字面量标签保留、清空/reset,以及原有 SRT、销毁/HTTP/Blob/视频切源场景。 +通过源码/main/legacy 与发布/候选核心,在 Chromium/Firefox/Windows WebKit 重跑。 +具体结果、编译器和输入指纹见[验证](../baselines/multiple-subtitles-timestamps.json)。 + +最终三入口各 37 项回归通过,本包 267 项通过;原生各 36 项,共 108 项通过, +原生 processing instruction 精确为 00:00:01.250、00:00:02.000、00:00:03.500。 +完整 CI 2337 项通过(1987 单元、14 工程、336 基线),严格生产 TS 353 文件。 +状态更新后 9 项元数据测试通过。显式 lint 无错误/警告,全量 lint 保留既有生成声明警告。 + +## 独立发现与后续边界 + +给字面量场景提供正常 `<...>` 输入时,vendor 生成了 `<;...>;`。九份历史实现 +又复现 `<`、`>`、`&` 的多余分号,新增 MULTI-SUB-ENTITY-01,由 05 处置。 +本任务不改 vendor;最终字面量显示测试通过公开 cue.text 修改隔离显示层,明确不代表 +该实体解码缺陷已修复。额外分号的初始六个浏览器失败记录保留。 + +04 的公开类型、完整旧核心/实体设备、实体解码、全部样式/编辑器和安装消费者仍待 +后续任务。正常构建更新三个产物及 docs 副本;无新依赖、公开声明和版本变更。 +本任务独立本地提交,不推送、不发布。回退恢复原 NaN 输出并移除显示监听器; +已发布失败基线及新发现记录仍可追溯。 diff --git a/refactor/plan.md b/refactor/plan.md index 73eed1e74..04be92ffa 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 223 项,范围 22 个包及工作区/示例。 -状态:todo 89 / doing 14 / blocked 0 / done 120 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 88 / doing 14 / blocked 0 / done 121 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -220,7 +220,7 @@ | 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 | doing | -| PKG-MULTI-SUB-07 | artplayer-plugin-multiple-subtitles
修复字幕内嵌时间戳序列化 | PKG-MULTI-SUB-03 | 保留数字时间戳节点、修正无效 NaN 输出、明确旧/新字幕输出差异 | 旧九份实现可复现,候选有效内嵌时间戳及文字/CSS/原生字幕通过,公开工厂与选择接口不变 | H | todo | +| PKG-MULTI-SUB-07 | artplayer-plugin-multiple-subtitles
修复字幕内嵌时间戳序列化 | PKG-MULTI-SUB-03 | 保留数字时间戳节点、修正无效 NaN 输出、明确旧/新字幕输出差异 | 旧九份实现可复现,候选有效内嵌时间戳及文字/CSS/原生字幕通过,公开工厂与选择接口不变 | H | done | | 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 | @@ -516,6 +516,7 @@ - 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-MULTI-SUB-07: [记录](baselines/multiple-subtitles-timestamps.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.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 507491644..e14fb5246 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,21 @@ # 进度与证据 +## PKG-MULTI-SUB-07 内嵌时间戳与显示修复完成 + +修复数字时间戳被文本包装成 NaN;合法 VTT class 标记保留原生 processing instruction, +caption.ts 在既有事件后只展开专用标记,保留后续节点及用户文字,销毁移除监听。 +覆盖顶层/嵌套、开始/中间/末尾、多个毫秒时间戳、冻结树和重复选择;私有类型不再 +允许字符串时间戳。源码/main/legacy 各 37 项通过,本包 267 项通过,双 TS 编译器各 +9 个负例准确拒绝。三入口原生各 36 项共 108 项通过,原生时间精确为 1.250/2.000/3.500 秒。 +最初裸标签可见、删除标记丢后文及 vendor 实体分号问题均保留了实际失败报告。 +时间戳修复后完整 CI 2337 项通过(1987 单元、14 工程、336 基线),严格生产 TS 353 文件。 +正常构建/ESM 入口通过,dist/docs 副本一致;公开声明、依赖、版本和 vendor 源码未改。 +TIMESTAMP 风险关闭;九份历史实体分号问题新增 ENTITY 风险,归 05 独立修复。 +见[变更](changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md)和[验证](baselines/multiple-subtitles-timestamps.json)。 +223 项:121 done、14 doing、88 todo;198 项风险。独立本地提交,不推送、不发布。 +下一步继续 MULTI-SUB-04 公开声明/异步及旧模块消费者兼容;实体解码和完整设备组合仍待 05。 + + ## PKG-MULTI-SUB-04 严格运行时 TS 检查点(doing) 五个自有模块迁为 TS,新增明确的 Option/Promise Result/Track/host/lifetime 类型; diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 805a5bf8b..8fd8aadb3 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -200,4 +200,5 @@ | 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 | +| MULTI-SUB-TIMESTAMP-01 | resolved / 已复现 | Text wrappers replace numeric inline cue timestamps and serialize NaN times | PKG-MULTI-SUB-07, PKG-MULTI-SUB-05 | +| MULTI-SUB-ENTITY-01 | open / 已复现 | Vendored cue entity decoding retains semicolons after lt, gt and amp entities | PKG-MULTI-SUB-05 | diff --git a/refactor/risks.json b/refactor/risks.json index fc1e49cfc..df8d29e2c 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -4403,7 +4403,7 @@ "id": "MULTI-SUB-TIMESTAMP-01", "title": "Text wrappers replace numeric inline cue timestamps and serialize NaN times", "confirmation": "reproduced", - "status": "open", + "status": "resolved", "owners": [ "PKG-MULTI-SUB-07", "PKG-MULTI-SUB-05" @@ -4411,10 +4411,38 @@ "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" + "test/multiple-subtitles-failures.test.js", + "refactor/baselines/multiple-subtitles-timestamps.json", + "refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md", + "test/multiple-subtitles-merge.test.js", + "test/browser/multiple-subtitles-lifecycle.spec.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." + "closureCriteria": "Valid inline timestamps and surrounding text survive serialization and native subtitle playback; frozen historical failure remains reproducible and ordinary markup regressions pass.", + "resolutionEvidence": [ + "refactor/baselines/multiple-subtitles-timestamps.json", + "refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md", + "test/multiple-subtitles-merge.test.js", + "test/browser/multiple-subtitles-lifecycle.spec.js", + "packages/artplayer-plugin-multiple-subtitles/src/caption.ts" + ], + "resolutionRationale": "Numeric timestamp nodes serialize inside valid VTT class markers, preserve native processing instructions, and are unwrapped only in the custom caption DOM without deleting subsequent text. Source/main/legacy tests, frozen-tree and timestamp cases, strict negative fixtures, and 108 native cases across both core fixtures pass. Public cue edits verify literal text preservation independently of the unresolved vendor entity bug." + }, + { + "id": "MULTI-SUB-ENTITY-01", + "title": "Vendored cue entity decoding retains semicolons after lt, gt and amp entities", + "confirmation": "reproduced", + "status": "open", + "owners": [ + "PKG-MULTI-SUB-05" + ], + "evidence": [ + "test/multiple-subtitles-failures.test.js", + "refactor/baselines/multiple-subtitles-timestamps.json", + "refactor/changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md" + ], + "compatibleResolution": "Independently fix valid named-entity decoding, documenting the vendor patch and preserving provenance checks and ordinary cue behavior.", + "closureCriteria": "Nine historical implementations reproduce the defect; candidate entity/markup regressions and real subtitle display pass, with vendor adaptation and provenance documentation updated." } ] } diff --git a/refactor/scripts/multiple-subtitles-runtime-types.test.mjs b/refactor/scripts/multiple-subtitles-runtime-types.test.mjs index 82eca7722..ef731d0b9 100644 --- a/refactor/scripts/multiple-subtitles-runtime-types.test.mjs +++ b/refactor/scripts/multiple-subtitles-runtime-types.test.mjs @@ -32,6 +32,6 @@ for (const compiler of [ts, runtimeCompat]) { } assert.deepEqual(diagnostics(false), []) const rejected = diagnostics(true) - assert.equal(rejected.length, 8, JSON.stringify(rejected)) + assert.equal(rejected.length, 9, JSON.stringify(rejected)) }) } diff --git a/refactor/tasks.json b/refactor/tasks.json index 85ee93230..f05b03e27 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2338,11 +2338,14 @@ "dependsOn": [ "PKG-MULTI-SUB-03" ], - "status": "todo", + "status": "done", "risk": "H", "deliverable": "保留数字时间戳节点、修正无效 NaN 输出、明确旧/新字幕输出差异", "acceptance": "旧九份实现可复现,候选有效内嵌时间戳及文字/CSS/原生字幕通过,公开工厂与选择接口不变", - "evidence": [] + "evidence": [ + "baselines/multiple-subtitles-timestamps.json", + "changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md" + ] }, { "id": "PKG-MULTI-SUB-05", diff --git a/test/browser/multiple-subtitles-lifecycle.spec.js b/test/browser/multiple-subtitles-lifecycle.spec.js index b5088f67e..4c3a84729 100644 --- a/test/browser/multiple-subtitles-lifecycle.spec.js +++ b/test/browser/multiple-subtitles-lifecycle.spec.js @@ -166,4 +166,49 @@ for (const core of ['published', 'candidate']) { await page.evaluate(() => window.art.destroy(false)) await evidence(page, testInfo, core, { requests, nativeSourceSwitch: true }) }) + + test(`Multiple subtitles candidate ${core} core: inline timestamps remain native instructions without visible time tags`, async ({ page }, testInfo) => { + const timed = 'WEBVTT\n\n00:00.000 --> 00:08.000\n<00:01.250>Before <00:02.000> after<00:03.500>\n' + await page.route('**/test/multiple-timed.vtt', route => route.fulfill({ body: timed, contentType: 'text/vtt' })) + await prepare(page, core) + await page.addStyleTag({ content: '.art-subtitle-timed { color: rgb(255, 0, 0); }' }) + await page.evaluate(async () => { + await window.art.plugins.add(window.artplayerPluginMultipleSubtitles({ subtitles: [{ url: '/test/multiple-timed.vtt', name: 'timed' }] })) + }) + await expect.poll(() => page.evaluate(() => window.art.subtitle.cues.length)).toBe(1) + const native = await page.evaluate(() => { + const cue = window.art.subtitle.cues[0] + const fragment = cue.getCueAsHTML() + const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_PROCESSING_INSTRUCTION) + const timestamps = [] + while (walker.nextNode()) timestamps.push(walker.currentNode.nodeValue) + return { text: cue.text, timestamps } + }) + expect(native.text).toContain('<00:02.000>') + expect(native.text).not.toContain('NaN') + expect(native.timestamps).toHaveLength(3) + await page.locator('#play').click() + await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1) + await page.locator('#pause').click() + await expect(page.locator('.art-subtitle-timed').first()).toHaveCSS('color', 'rgb(255, 0, 0)') + const timedLine = page.locator('.art-subtitle-line').filter({ has: page.locator('.art-subtitle-timed') }) + const visible = await timedLine.textContent() + expect(visible).toContain('Before') + expect(visible).toContain('after') + expect(visible).not.toMatch(/<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/) + // Public cue edits exercise literal display without relying on the vendor's entity decoder. + await page.evaluate(() => { + window.art.subtitle.cues[0].text += '
Literal <00:02.000> example
' + window.art.subtitle.update() + }) + await expect(page.locator('.art-subtitle-literal')).toHaveText('Literal <00:02.000> example') + await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks([])) + await expect(page.locator('.art-subtitle-timed')).toHaveCount(0) + await page.evaluate(() => window.art.plugins.multipleSubtitles.reset()) + await expect(timedLine).toContainText('Before') + await expect(timedLine).not.toContainText(/<(?:\d+:)?\d{2}:\d{2}\.\d{3}>/) + await expect(page.locator('.art-subtitle-literal')).toHaveCount(0) + await page.evaluate(() => window.art.destroy(false)) + await evidence(page, testInfo, core, { native, visible }) + }) } diff --git a/test/multiple-subtitles-failures.test.js b/test/multiple-subtitles-failures.test.js index 21100bc5a..e00afc215 100644 --- a/test/multiple-subtitles-failures.test.js +++ b/test/multiple-subtitles-failures.test.js @@ -263,4 +263,10 @@ for (const implementation of implementations) { await env.factory(options(subtitle()))(env.art) assert((await env.latestText()).includes('')) }) + + test(`Multiple-subtitles ${implementation.name} failure baseline: entity decoding retains stray semicolons`, async () => { + const env = create({ responses: { 'a.vtt': subtitleVtt('Literal <00:02.000> example &') } }) + await env.factory(options(subtitle()))(env.art) + assert(env.utils.unescape(await env.latestText()).includes('Literal <;00:02.000>; example &;')) + }) } diff --git a/test/multiple-subtitles-lifecycle.test.js b/test/multiple-subtitles-lifecycle.test.js index eb35a33cb..9f45e0b61 100644 --- a/test/multiple-subtitles-lifecycle.test.js +++ b/test/multiple-subtitles-lifecycle.test.js @@ -21,7 +21,7 @@ async function flush() { } function empty(env) { assert.equal(env.liveBlobs.size, 0) - assert.equal(env.listeners.get('destroy')?.size || 0, 0) + for (const listeners of env.listeners.values()) assert.equal(listeners.size, 0) } for (const abortController of [true, false]) { diff --git a/test/multiple-subtitles-merge.test.js b/test/multiple-subtitles-merge.test.js index 3b4b87351..30e87b1b2 100644 --- a/test/multiple-subtitles-merge.test.js +++ b/test/multiple-subtitles-merge.test.js @@ -74,3 +74,48 @@ test('Multiple subtitles request: real core SRT/ASS conversion and encoded text for (const value of ['SRT text', 'ASS text', 'café', '日本語']) assert(text.includes(value)) current.emit('destroy') }) + +for (const [text, timestamps] of [ + ['Before <00:02.000> after', ['<00:02.000>']], + ['<00:01.250> one <00:03.500> two', ['<00:01.250>', '<00:03.500>']], + ['end <00:07.000>', ['<00:07.000>']], + ['甲<00:02.000>乙', ['<00:02.000>']], + ['bold <00:02.000> after', ['<00:02.000>']], +]) { + test(`Multiple subtitles timestamps: valid nodes survive selection and reset (${text})`, async () => { + const input = `WEBVTT\n\n00:00.000 --> 00:08.000\n${text}\n` + const env = multipleSubtitlesEnvironment(candidate, { responses: { 'a.vtt': input, 'b.vtt': subtitleVtt('Other') } }) + const result = await env.factory({ subtitles: [{ url: 'a.vtt', name: 'a' }, { url: 'b.vtt', name: 'b' }] })(env.art) + result.tracks(['a']) + const selected = await env.latestText() + for (const timestamp of timestamps) assert(selected.includes(timestamp)) + assert(!selected.includes('NaN')) + const reparsed = parseTracks([selected], [{ name: 'reparsed' }]) + assert.deepEqual(reparsed[0].errors, []) + const numbers = [] + function collect(nodes) { + for (const node of nodes) { + if (node.type === 'timestamp') + numbers.push(node.value) + if (node.children) + collect(node.children) + } + } + collect(reparsed[0].cues[0].tree.children) + assert.equal(numbers.length, timestamps.length) + result.tracks(['b']) + assert(!(await env.latestText()).includes(timestamps[0])) + result.reset() + for (const timestamp of timestamps) assert((await env.latestText()).includes(timestamp)) + env.emit('destroy') + }) +} + +test('Multiple subtitles timestamps: frozen numeric nodes stay unchanged across duplicate selection', () => { + const trees = freeze(parseTracks(['WEBVTT\n\n00:00.000 --> 00:08.000\nBefore <00:02.000> after\n'], [{ name: 'a' }])) + const original = JSON.stringify(trees) + const output = serializeTracks([trees[0], trees[0]]) + assert.equal((output.match(/<00:02\.000>/g) || []).length, 2) + assert.equal((output.match(//g) || []).length, 2) + assert.equal(JSON.stringify(trees), original) +}) diff --git a/test/types/multiple-subtitles-runtime.ts b/test/types/multiple-subtitles-runtime.ts index 3e64bc3ad..21cf3cceb 100644 --- a/test/types/multiple-subtitles-runtime.ts +++ b/test/types/multiple-subtitles-runtime.ts @@ -1,5 +1,5 @@ import type Artplayer from 'artplayer' -import type { CueNode, TimestampNode } from '../../packages/artplayer-plugin-multiple-subtitles/src/parser' +import type { CueNode, TimestampNode, WrappedNode } 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' @@ -52,6 +52,9 @@ void timestamp // @ts-expect-error Parsed timestamps are numbers, not wrapped serialized values. const wrongTimestamp: TimestampNode = { type: 'timestamp', value: '2' } void wrongTimestamp +// @ts-expect-error Serialized timestamp nodes must also keep numeric values. +const wrongWrapped: WrappedNode = { type: 'timestamp', value: '
2
' } +void wrongWrapped const trees = parseTracks(['WEBVTT\n\n'], [{}]) const vtt: string = serializeTracks(trees) void vtt