From d44e81ce220d0d4475bf4f0f0c2e02ea5b09402a Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sun, 13 Sep 2026 12:14:41 +0800 Subject: [PATCH] refactor(multiple-subtitles): [PKG-MULTI-SUB-04] expose compatible runtime type entry --- .../artplayer-plugin-multiple-subtitles.d.ts | 45 +- .../artplayer-plugin-multiple-subtitles.js | 2 +- ...player-plugin-multiple-subtitles.legacy.js | 2 +- .../artplayer-plugin-multiple-subtitles.mjs | 9 +- package.json | 3 +- .../ARCHITECTURE.md | 26 +- .../README.md | 23 + .../artplayer-plugin-multiple-subtitles.js | 2 +- ...player-plugin-multiple-subtitles.legacy.js | 2 +- .../artplayer-plugin-multiple-subtitles.mjs | 9 +- .../package.json | 44 +- .../src/index.ts | 5 +- .../src/types.ts | 10 +- .../tsconfig.json | 2 + .../artplayer-plugin-multiple-subtitles.d.cts | 5 + .../artplayer-plugin-multiple-subtitles.d.mts | 5 + .../artplayer-plugin-multiple-subtitles.d.ts | 43 +- .../types/runtime.d.cts | 4 + .../types/runtime.d.mts | 5 + .../types/runtime.d.ts | 5 + .../multiple-subtitles-public-types.json | 2729 +++++++++++++++++ ...026-09-13-PKG-MULTI-SUB-04-public-types.md | 67 + .../consumers/multiple-subtitles-commonjs.ts | 11 + .../multiple-subtitles-export-alternatives.ts | 7 + .../consumers/multiple-subtitles-published.ts | 9 + refactor/plan.md | 2 +- refactor/progress.md | 19 + refactor/risks.json | 16 +- .../multiple-subtitles-package-types.mjs | 122 + .../scripts/multiple-subtitles-types.test.mjs | 89 + refactor/tasks.json | 4 +- scripts/build-ts.js | 2 +- test/multiple-subtitles-lifecycle.test.js | 23 + test/types/multiple-subtitles-public.ts | 55 + 34 files changed, 3346 insertions(+), 60 deletions(-) create mode 100644 packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.cts create mode 100644 packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.mts create mode 100644 packages/artplayer-plugin-multiple-subtitles/types/runtime.d.cts create mode 100644 packages/artplayer-plugin-multiple-subtitles/types/runtime.d.mts create mode 100644 packages/artplayer-plugin-multiple-subtitles/types/runtime.d.ts create mode 100644 refactor/baselines/multiple-subtitles-public-types.json create mode 100644 refactor/changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md create mode 100644 refactor/fixtures/consumers/multiple-subtitles-commonjs.ts create mode 100644 refactor/fixtures/consumers/multiple-subtitles-export-alternatives.ts create mode 100644 refactor/fixtures/consumers/multiple-subtitles-published.ts create mode 100644 refactor/scripts/multiple-subtitles-package-types.mjs create mode 100644 refactor/scripts/multiple-subtitles-types.test.mjs create mode 100644 test/types/multiple-subtitles-public.ts diff --git a/docs/assets/ts/artplayer-plugin-multiple-subtitles.d.ts b/docs/assets/ts/artplayer-plugin-multiple-subtitles.d.ts index a6f710bc1..6a9928bff 100644 --- a/docs/assets/ts/artplayer-plugin-multiple-subtitles.d.ts +++ b/docs/assets/ts/artplayer-plugin-multiple-subtitles.d.ts @@ -1,16 +1,45 @@ -declare const artplayerPluginMultipleSubtitles: (option: { - subtitles: { +// Generated from the package public declaration by yarn build:ts. Do not edit. +/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */ +declare namespace artplayerPluginMultipleSubtitlesDefinitions { + export interface TrackOption { url?: string name?: string type?: 'vtt' | 'srt' | 'ass' encoding?: string onParser?: (...args: object[]) => object - }[] -}) => (art: Artplayer) => { - name: 'multipleSubtitles' + } + export interface Option { + subtitles: TrackOption[] + } + export interface RuntimeOption { + subtitles?: TrackOption[] + } + export interface LegacyResult { + name: 'multipleSubtitles' + } + export interface Result extends LegacyResult { + tracks: (names?: string[]) => void + reset: () => void + } + /** Historical synchronous extraction; actual registration is asynchronous. */ + export type Factory = (option: Option) => (art: Artplayer) => LegacyResult + /** Accurate runtime view available through the /runtime entry. */ + export interface RuntimeFactory { + (option: RuntimeOption): (art: Artplayer) => Promise + default: RuntimeFactory + } + /** Preserve existing parameter extraction and replacement-function compatibility. */ + export function artplayerPluginMultipleSubtitles(option: Option): (art: Artplayer) => LegacyResult +} +declare const artplayerPluginMultipleSubtitles: typeof artplayerPluginMultipleSubtitlesDefinitions.artplayerPluginMultipleSubtitles +declare namespace artplayerPluginMultipleSubtitles { + export type TrackOption = artplayerPluginMultipleSubtitlesDefinitions.TrackOption + export type Option = artplayerPluginMultipleSubtitlesDefinitions.Option + export type RuntimeOption = artplayerPluginMultipleSubtitlesDefinitions.RuntimeOption + export type LegacyResult = artplayerPluginMultipleSubtitlesDefinitions.LegacyResult + export type Result = artplayerPluginMultipleSubtitlesDefinitions.Result + export type Factory = artplayerPluginMultipleSubtitlesDefinitions.Factory + export type RuntimeFactory = artplayerPluginMultipleSubtitlesDefinitions.RuntimeFactory } - -export default artplayerPluginMultipleSubtitles - export = artplayerPluginMultipleSubtitles export as namespace artplayerPluginMultipleSubtitles; diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.js b/docs/compiled/artplayer-plugin-multiple-subtitles.js index a42f27e79..706e4f14c 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],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}}}}); +!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()}}}function c({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}}}return Object.assign(c,{default:c})}); diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js b/docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js index 9910ed915..107edd0e3 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())}),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}})}}); +!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()}}}function y({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}})}return Object.assign(y,{default:y})}); diff --git a/docs/compiled/artplayer-plugin-multiple-subtitles.mjs b/docs/compiled/artplayer-plugin-multiple-subtitles.mjs index e043c9c70..66c7834f6 100644 --- a/docs/compiled/artplayer-plugin-multiple-subtitles.mjs +++ b/docs/compiled/artplayer-plugin-multiple-subtitles.mjs @@ -293,7 +293,7 @@ var WebVTTCueTimingsAndSettingsParser = function(line, errorHandler) { var settings = input.split(SPACE), seen = []; for (var i = 0; i < settings.length; i++) { if (settings[i] == "") continue; - var index = settings[i].indexOf(":"), setting = settings[i].slice(0, index), value = settings[i].slice(index + 1); + var index2 = settings[i].indexOf(":"), setting = settings[i].slice(0, index2), value = settings[i].slice(index2 + 1); if (seen.indexOf(setting) != -1) { err("Duplicate setting."); } @@ -812,9 +812,9 @@ function markNestedTimestamps(node) { } function parseTracks(vtts, subtitles) { const parser = new WebVTTParser(); - return vtts.map((vtt, index) => { + return vtts.map((vtt, index2) => { const tree = parser.parse(vtt, "metadata"); - return { ...tree, url: subtitles[index].url, name: subtitles[index].name }; + return { ...tree, url: subtitles[index2].url, name: subtitles[index2].name }; }); } function serializeTracks(trees) { @@ -1040,6 +1040,7 @@ function artplayerPluginMultipleSubtitles({ subtitles = [] }) { } }; } +const index = Object.assign(artplayerPluginMultipleSubtitles, { default: artplayerPluginMultipleSubtitles }); export { - artplayerPluginMultipleSubtitles as default + index as default }; diff --git a/package.json b/package.json index a53cf3ecc..b34ded187 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,8 @@ "test:auto-thumbnail": "node --test test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js refactor/scripts/auto-thumbnail-contract.test.mjs", "test:vtt-thumbnail": "node --test test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js refactor/scripts/vtt-thumbnail-contract.test.mjs refactor/scripts/vtt-thumbnail-types.test.mjs", "test:vtt-thumbnail-types-package": "node refactor/scripts/vtt-thumbnail-package-types.mjs", - "test:multiple-subtitles": "node --test test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js refactor/scripts/multiple-subtitles-contract.test.mjs refactor/scripts/multiple-subtitles-runtime-types.test.mjs" + "test:multiple-subtitles": "node --test test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js refactor/scripts/multiple-subtitles-contract.test.mjs refactor/scripts/multiple-subtitles-runtime-types.test.mjs refactor/scripts/multiple-subtitles-types.test.mjs", + "test:multiple-subtitles-types-package": "node refactor/scripts/multiple-subtitles-package-types.mjs" }, "browserslist": "last 1 Chrome version", "devDependencies": { diff --git a/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md b/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md index 4e1154653..1f97cf5ac 100644 --- a/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md +++ b/packages/artplayer-plugin-multiple-subtitles/ARCHITECTURE.md @@ -3,8 +3,11 @@ The public factory is `artplayerPluginMultipleSubtitles({ subtitles })`. Registration is asynchronous and resolves to `{ name: 'multipleSubtitles', tracks, reset }`. `tracks(names)` selects names in caller order, `tracks()` clears, and `reset()` restores the original order. -Both methods return undefined. Current declarations incorrectly omit the Promise and methods; -task PKG-MULTI-SUB-04 owns that compatibility work. Do not infer runtime behavior from them. +Both methods return undefined. Root and legacy declarations retain the historical synchronous +`LegacyResult` for existing extraction and replacement-function consumers. The additive +`artplayer-plugin-multiple-subtitles/runtime` entry describes the actual `Promise` +and selection methods. It shares the root implementation, not another plugin instance. +The factory has a writable `.default` self alias for historical CommonJS access. ## Current module boundaries @@ -19,8 +22,8 @@ task PKG-MULTI-SUB-04 owns that compatibility work. Do not infer runtime behavio 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/types.ts` describes selected tracks and narrow host/resource boundaries and reuses public + runtime option/result types. The private host/resource types are not a published API. - `src/parser.d.ts` describes the vendored parse result and discriminated cue nodes. Parsed 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 @@ -28,7 +31,11 @@ task PKG-MULTI-SUB-04 owns that compatibility work. Do not infer runtime behavio The pinned upstream reference and full adaptation check are in `../../refactor/baselines/multiple-subtitles-vendor/` and `../../refactor/scripts/multiple-subtitles-vendor.mjs`. -- `types/artplayer-plugin-multiple-subtitles.d.ts` is the historical public declaration. +- `types/artplayer-plugin-multiple-subtitles.d.ts` owns public data types and the historical + callable signature. Root/legacy `.d.mts` and `.d.cts` forward that signature; `runtime.d.*` + forwards the precise asynchronous factory. Classic resolution uses typesVersions. +- `scripts/build-ts.js` generates the editor's global factory and named type namespace from + this declaration through the semantic generator. Never edit the generated editor file. - `../../docs/assets/example/multiple.subtitles.js` demonstrates the real plugin name, `tracks/reset`, name order and `.art-subtitle-chinese/.art-subtitle-japanese` CSS hooks. @@ -77,6 +84,8 @@ Use the pinned Node and Yarn 1.22.22: ```sh yarn test:multiple-subtitles +yarn test:multiple-subtitles-types-package +yarn build:ts artplayer-plugin-multiple-subtitles yarn test:browser test/browser/multiple-subtitles-history.spec.js yarn test:browser test/browser/multiple-subtitles-lifecycle.spec.js yarn build artplayer-plugin-multiple-subtitles @@ -98,6 +107,11 @@ environment variable above before running it; source builds are used when it is The package tsconfig checks only authored TS and declarations, with strict/noUncheckedIndexedAccess and allowJs=false. TS 5.9.3/5.1.6 positive and negative fixtures cover internal runtime types. Do not remove cancellation/index boundary assertions by changing historical optional inputs. -The public synchronous declaration mismatch remains an explicit task 04 migration boundary. +Public declaration tests check exact old extraction/replacement signatures, accurate runtime +types and negative uses with TS 5.9.3 and 4.3.5. The package test packs core and plugin, installs +outside the workspace and checks Node10/NodeNext/Bundler modes plus real CJS/ESM identities. +Historical 1.0.0/1.1.0 export-assignment declarations and 1.2.0 default-module declarations +disagree on raw `import = require` extraction. Task 04 retains that unresolved boundary; +default-import compatibility must not be presented as proof of every historical module form. Tasks 05/06 still own complete core/device combinations and demo acceptance. See `../../refactor/baselines/multiple-subtitles-contract.md` for exact historical differences. diff --git a/packages/artplayer-plugin-multiple-subtitles/README.md b/packages/artplayer-plugin-multiple-subtitles/README.md index 65d4bc632..f46a0fcc4 100644 --- a/packages/artplayer-plugin-multiple-subtitles/README.md +++ b/packages/artplayer-plugin-multiple-subtitles/README.md @@ -6,6 +6,29 @@ Multiple subtitles plugin for ArtPlayer [https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-multiple-subtitles/index.js&example=multiple.subtitles) +## TypeScript + +The root and `/legacy` type entries preserve historical parameter and return-type extraction. +Registration has always been asynchronous. For accurate Promise and selection-method types, +use the additive runtime entry, which loads the same implementation: + +```ts +import multipleSubtitles from 'artplayer-plugin-multiple-subtitles/runtime' + +const register = multipleSubtitles({ + subtitles: [{ url: '/subtitles/en.vtt', name: 'en' }], +}) +// With an existing ArtPlayer instance: +const result = await register(art) +result.tracks(['en']) +result.reset() +``` + +The option object is required; `{}` means no tracks. `tracks()` clears the selection; +`reset()` restores all original tracks. Both methods return `void`. The existing `onParser` +field remains declared for compatibility but is not invoked by the implementation. +See [maintenance notes](ARCHITECTURE.md) for module responsibilities and validation commands. + ## License MIT © Harvey Zhao 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 a42f27e79..706e4f14c 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],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}}}}); +!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()}}}function c({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}}}return Object.assign(c,{default:c})}); 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 9910ed915..107edd0e3 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())}),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}})}}); +!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()}}}function y({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}})}return Object.assign(y,{default:y})}); 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 e043c9c70..66c7834f6 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 @@ -293,7 +293,7 @@ var WebVTTCueTimingsAndSettingsParser = function(line, errorHandler) { var settings = input.split(SPACE), seen = []; for (var i = 0; i < settings.length; i++) { if (settings[i] == "") continue; - var index = settings[i].indexOf(":"), setting = settings[i].slice(0, index), value = settings[i].slice(index + 1); + var index2 = settings[i].indexOf(":"), setting = settings[i].slice(0, index2), value = settings[i].slice(index2 + 1); if (seen.indexOf(setting) != -1) { err("Duplicate setting."); } @@ -812,9 +812,9 @@ function markNestedTimestamps(node) { } function parseTracks(vtts, subtitles) { const parser = new WebVTTParser(); - return vtts.map((vtt, index) => { + return vtts.map((vtt, index2) => { const tree = parser.parse(vtt, "metadata"); - return { ...tree, url: subtitles[index].url, name: subtitles[index].name }; + return { ...tree, url: subtitles[index2].url, name: subtitles[index2].name }; }); } function serializeTracks(trees) { @@ -1040,6 +1040,7 @@ function artplayerPluginMultipleSubtitles({ subtitles = [] }) { } }; } +const index = Object.assign(artplayerPluginMultipleSubtitles, { default: artplayerPluginMultipleSubtitles }); export { - artplayerPluginMultipleSubtitles as default + index as default }; diff --git a/packages/artplayer-plugin-multiple-subtitles/package.json b/packages/artplayer-plugin-multiple-subtitles/package.json index c008a8a68..b170583a1 100644 --- a/packages/artplayer-plugin-multiple-subtitles/package.json +++ b/packages/artplayer-plugin-multiple-subtitles/package.json @@ -19,19 +19,49 @@ ], "exports": { ".": { - "types": "./types/artplayer-plugin-multiple-subtitles.d.ts", - "import": "./dist/artplayer-plugin-multiple-subtitles.mjs", - "require": "./dist/artplayer-plugin-multiple-subtitles.js" + "import": { + "types": "./types/artplayer-plugin-multiple-subtitles.d.mts", + "default": "./dist/artplayer-plugin-multiple-subtitles.mjs" + }, + "require": { + "types": "./types/artplayer-plugin-multiple-subtitles.d.cts", + "default": "./dist/artplayer-plugin-multiple-subtitles.js" + } }, "./legacy": { - "types": "./types/artplayer-plugin-multiple-subtitles.d.ts", - "import": "./dist/artplayer-plugin-multiple-subtitles.legacy.js", - "require": "./dist/artplayer-plugin-multiple-subtitles.legacy.js" + "import": { + "types": "./types/artplayer-plugin-multiple-subtitles.d.mts", + "default": "./dist/artplayer-plugin-multiple-subtitles.legacy.js" + }, + "require": { + "types": "./types/artplayer-plugin-multiple-subtitles.d.cts", + "default": "./dist/artplayer-plugin-multiple-subtitles.legacy.js" + } + }, + "./runtime": { + "import": { + "types": "./types/runtime.d.mts", + "default": "./dist/artplayer-plugin-multiple-subtitles.mjs" + }, + "require": { + "types": "./types/runtime.d.cts", + "default": "./dist/artplayer-plugin-multiple-subtitles.js" + } } }, "main": "./dist/artplayer-plugin-multiple-subtitles.js", "module": "./dist/artplayer-plugin-multiple-subtitles.mjs", "types": "./types/artplayer-plugin-multiple-subtitles.d.ts", "legacy": "./dist/artplayer-plugin-multiple-subtitles.legacy.js", - "browserslist": "last 1 Chrome version" + "browserslist": "last 1 Chrome version", + "typesVersions": { + "*": { + "legacy": [ + "types/artplayer-plugin-multiple-subtitles.d.ts" + ], + "runtime": [ + "types/runtime.d.ts" + ] + } + } } diff --git a/packages/artplayer-plugin-multiple-subtitles/src/index.ts b/packages/artplayer-plugin-multiple-subtitles/src/index.ts index 3890b3c7e..02fd29afc 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/index.ts +++ b/packages/artplayer-plugin-multiple-subtitles/src/index.ts @@ -1,4 +1,5 @@ import type Artplayer from 'artplayer' +import type { RuntimeFactory } from '../types/artplayer-plugin-multiple-subtitles' import type { Option, Result, Track } from './types' import installCaptionView from './caption' import createLifetime from './lifetime' @@ -6,7 +7,7 @@ import { parseTracks, serializeTracks } from './merge' import createRenderer from './render' import loadVtt from './request' -export default function artplayerPluginMultipleSubtitles({ subtitles = [] }: Option) { +function artplayerPluginMultipleSubtitles({ subtitles = [] }: Option) { return async (art: Artplayer): Promise => { const { unescape, getExt, srtToVtt, assToVtt } = (art.constructor as typeof Artplayer).utils const lifetime = createLifetime(art) @@ -44,3 +45,5 @@ export default function artplayerPluginMultipleSubtitles({ subtitles = [] }: Opt } } } + +export default Object.assign(artplayerPluginMultipleSubtitles, { default: artplayerPluginMultipleSubtitles }) as RuntimeFactory diff --git a/packages/artplayer-plugin-multiple-subtitles/src/types.ts b/packages/artplayer-plugin-multiple-subtitles/src/types.ts index f73348463..0411295e5 100644 --- a/packages/artplayer-plugin-multiple-subtitles/src/types.ts +++ b/packages/artplayer-plugin-multiple-subtitles/src/types.ts @@ -4,15 +4,7 @@ import type legacyFactory from '../types/artplayer-plugin-multiple-subtitles' import type { ParseResult } from './parser' export type LegacyOption = Parameters[0] -export type TrackOption = LegacyOption['subtitles'][number] -export interface Option { - subtitles?: TrackOption[] -} -export interface Result { - name: 'multipleSubtitles' - tracks: (names?: string[]) => void - reset: () => void -} +export type { RuntimeOption as Option, Result, TrackOption } from '../types/artplayer-plugin-multiple-subtitles' export interface Track extends ParseResult { url: string | undefined name: string | undefined diff --git a/packages/artplayer-plugin-multiple-subtitles/tsconfig.json b/packages/artplayer-plugin-multiple-subtitles/tsconfig.json index d22a92187..14bed4891 100644 --- a/packages/artplayer-plugin-multiple-subtitles/tsconfig.json +++ b/packages/artplayer-plugin-multiple-subtitles/tsconfig.json @@ -6,6 +6,8 @@ "include": [ "src/**/*.ts", "types/**/*.d.ts", + "types/**/*.d.mts", + "types/**/*.d.cts", "../../types/assets.d.ts", "../../test/types/multiple-subtitles-runtime.ts" ] diff --git a/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.cts b/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.cts new file mode 100644 index 000000000..ad54c9e9f --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.cts @@ -0,0 +1,5 @@ +import type { Factory } from './artplayer-plugin-multiple-subtitles.js' + +declare const artplayerPluginMultipleSubtitles: Factory +export default artplayerPluginMultipleSubtitles +export type { Factory, LegacyResult, Option, Result, RuntimeFactory, RuntimeOption, TrackOption } from './artplayer-plugin-multiple-subtitles.js' diff --git a/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.mts b/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.mts new file mode 100644 index 000000000..ad54c9e9f --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.mts @@ -0,0 +1,5 @@ +import type { Factory } from './artplayer-plugin-multiple-subtitles.js' + +declare const artplayerPluginMultipleSubtitles: Factory +export default artplayerPluginMultipleSubtitles +export type { Factory, LegacyResult, Option, Result, RuntimeFactory, RuntimeOption, TrackOption } from './artplayer-plugin-multiple-subtitles.js' diff --git a/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts b/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts index 8db6c999d..ce1daa1ad 100644 --- a/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts +++ b/packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts @@ -1,15 +1,40 @@ import type Artplayer from 'artplayer' -declare const artplayerPluginMultipleSubtitles: (option: { - subtitles: { - url?: string - name?: string - type?: 'vtt' | 'srt' | 'ass' - encoding?: string - onParser?: (...args: object[]) => object - }[] -}) => (art: Artplayer) => { +export interface TrackOption { + url?: string + name?: string + type?: 'vtt' | 'srt' | 'ass' + encoding?: string + onParser?: (...args: object[]) => object +} + +export interface Option { + subtitles: TrackOption[] +} + +export interface RuntimeOption { + subtitles?: TrackOption[] +} + +export interface LegacyResult { name: 'multipleSubtitles' } +export interface Result extends LegacyResult { + tracks: (names?: string[]) => void + reset: () => void +} + +/** Historical synchronous extraction; actual registration is asynchronous. */ +export type Factory = (option: Option) => (art: Artplayer) => LegacyResult + +/** Accurate runtime view available through the /runtime entry. */ +export interface RuntimeFactory { + (option: RuntimeOption): (art: Artplayer) => Promise + default: RuntimeFactory +} + +/** Preserve existing parameter extraction and replacement-function compatibility. */ +declare function artplayerPluginMultipleSubtitles(option: Option): (art: Artplayer) => LegacyResult + export default artplayerPluginMultipleSubtitles diff --git a/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.cts b/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.cts new file mode 100644 index 000000000..7d8b8cbf9 --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.cts @@ -0,0 +1,4 @@ +import type { RuntimeFactory } from './artplayer-plugin-multiple-subtitles.js' + +declare const artplayerPluginMultipleSubtitles: RuntimeFactory +export = artplayerPluginMultipleSubtitles diff --git a/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.mts b/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.mts new file mode 100644 index 000000000..19cbc979a --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.mts @@ -0,0 +1,5 @@ +import type { RuntimeFactory } from './artplayer-plugin-multiple-subtitles.js' + +declare const artplayerPluginMultipleSubtitles: RuntimeFactory +export default artplayerPluginMultipleSubtitles +export type { Result, RuntimeFactory, RuntimeOption, TrackOption } from './artplayer-plugin-multiple-subtitles.js' diff --git a/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.ts b/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.ts new file mode 100644 index 000000000..19cbc979a --- /dev/null +++ b/packages/artplayer-plugin-multiple-subtitles/types/runtime.d.ts @@ -0,0 +1,5 @@ +import type { RuntimeFactory } from './artplayer-plugin-multiple-subtitles.js' + +declare const artplayerPluginMultipleSubtitles: RuntimeFactory +export default artplayerPluginMultipleSubtitles +export type { Result, RuntimeFactory, RuntimeOption, TrackOption } from './artplayer-plugin-multiple-subtitles.js' diff --git a/refactor/baselines/multiple-subtitles-public-types.json b/refactor/baselines/multiple-subtitles-public-types.json new file mode 100644 index 000000000..9601c9bbb --- /dev/null +++ b/refactor/baselines/multiple-subtitles-public-types.json @@ -0,0 +1,2729 @@ +{ + "schemaVersion": 1, + "task": "PKG-MULTI-SUB-04", + "status": "public-types-checkpoint-validated", + "parentCommit": "7f226ff8229e91d8cdc17053926cbbf1071474c4", + "checks": [ + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "source", + "tests": 39, + "file": "refactor/.cache/multiple-subtitles04-public-source.log", + "sha256": "54d9f3648f67f248c41a21c6de45b5a0a9020a2089033dafeeaa97a452e59a24", + "scope": "Pure frozen-tree tests use source; remaining behavior executes the selected entry." + }, + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "main", + "tests": 39, + "file": "refactor/.cache/multiple-subtitles04-public-main.log", + "sha256": "875f99a8bf58763ec08bb65d4351a7684971cff0dbe781ed8e5420fd81e70bf9", + "scope": "Pure frozen-tree tests use source; remaining behavior executes the selected entry." + }, + { + "command": "normal/lifecycle/merge selected-entry regressions", + "candidate": "legacy", + "tests": 39, + "file": "refactor/.cache/multiple-subtitles04-public-legacy.log", + "sha256": "d9693001f63dc46664780d26663506cadf4443a1655481d255820092d4a32c09", + "scope": "Pure frozen-tree tests use source; remaining behavior executes the selected entry." + }, + { + "command": "yarn ci:check", + "tests": 2344, + "unit": 1989, + "engineering": 14, + "baseline": 341, + "strictProductionTS": 353, + "file": "refactor/.cache/multiple-subtitles04-public-ci.log", + "sha256": "6ece0322eda63f5e5de92b8f1bf9bc5342dfcf4cc11b759df80c104bcfc5811d" + }, + { + "command": "build", + "file": "refactor/.cache/multiple-subtitles04-public-build.log", + "sha256": "b79432933936f313e0beaa9d204ac0838834756a28e28f90105c7a0831b90e31" + }, + { + "command": "editor", + "file": "refactor/.cache/multiple-subtitles04-public-editor.log", + "sha256": "3263ced7993ea0c0fe94c26dfcc2570ec1ee63755cca44afe45c7c66bc9aa2e8" + }, + { + "command": "lint", + "file": "refactor/.cache/multiple-subtitles04-public-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "command": "yarn test:multiple-subtitles", + "tests": 274, + "publicCompilerModes": 5, + "invalidUsesPerMode": 12, + "strictRuntimeCompilers": [ + "5.9.3", + "5.1.6" + ], + "runtimeInvalidUsesPerCompiler": 9, + "editorCompilers": [ + "5.9.3", + "4.3.5" + ], + "editorInvalidUsesPerCompiler": 2, + "file": "refactor/.cache/multiple-subtitles04-public-package-final.log", + "sha256": "8e7fec9f1b38cde93f2d5dd69f99bcf67ea9e9d58a0f53275e5e89627bc099d8" + } + ], + "installed": { + "file": "refactor/.cache/multiple-subtitles-package-types-QhZYAQ/report.json", + "sha256": "21d8692e89bceeeaaa7967b12a295319122347b1f4a16a1cd95d1f183cb361e1", + "command": "yarn test:multiple-subtitles-types-package", + "scope": "Three actual published packages in five compiler modes and packed candidate in seven modes, installed outside the workspace with packed core. Offline frozen reinstalls and byte identity verified. Expected old NodeNext ESM and CommonJS module extraction failures remain explicit; these do not count as candidate compatibility success. Raw historical export-assignment type compatibility remains unresolved. Full device and distribution acceptance remains separate.", + "packages": [ + { + "name": "artplayer", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\multiple-subtitles-package-types-QhZYAQ\\artplayer.tgz", + "sha256": "7be178936e4e12fc59d0af15402bf4b36adcd6dcf40f3ef90e73bbe6aaaf0d49", + "files": { + "package/ARCHITECTURE.md": "e19079b3d419d82c86e65d6e05c826020c06286b7edfb1cce185e62ce3c50d30", + "package/README.md": "1e185e65f67fc2081b4182bbc6c4558ea05b6e3fa3acc5dd0c3ac7df1d542b09", + "package/THIRD_PARTY_NOTICES": "b77b5b46b79fa0cda850b0781f78c3b1112c4ed9672fa291956fe8eff53098e7", + "package/dist/artplayer.cts": "0e91e907d021db02a68004603e6190505fdc0806d873ebb6af171aa089a81da7", + "package/dist/artplayer.js": "4b29968839ba816db09910a908d1d6b8ec6cf1edaad418c96e1f407545a19bf9", + "package/dist/artplayer.legacy.js": "2dc2eb7cb26012ef1d780680d622aba61a79b7ac3b37bb1ca7a6047834513dd9", + "package/dist/artplayer.mjs": "574749f090251b7d7bddb507a3ca3cc91a759f694e7f4865d19ef1162d330626", + "package/dist/artplayer.mts": "f6292596a9ccb1d43a6d6a12c35f859b1889ad15924c5ae0f9f280941aade313", + "package/dist/artplayer.ts": "26a42ac66811e70f68a8ebe62cbf304eb1993451b3e658961be98e7106162527", + "package/dist/component.ts": "aa1c43be67322781b4b355b143bc69d9f29d22e578e4974d6a13e957a7c0334f", + "package/dist/config.ts": "c4fd8681bf32b10c743e3e561c532520c8f32e43fd765524850b15e55cb91c72", + "package/dist/cssVar.ts": "e027d82fb4f01c9ce7559cf679b2d5ee1962d3f7e5b4dd64ca21f472b933efb6", + "package/dist/emitter.ts": "9c62551cdcc6dd3966bed8a7a85dd477435db977c91a294d2ac8f162f9592537", + "package/dist/events.ts": "af191f862af4ec8933b09113b8c074603ae254a8b70460d0f3006646cd74ae42", + "package/dist/i18n.ts": "34b0c684668044f7600c80b9d3979f8fb730138007b7cb4e7034326b340156fb", + "package/dist/i18n/ar.js": "bd0bef65f4f7dc3f8ed562904cc6fa9071ff4537fb64c1e0fc3d554141bcd3cb", + "package/dist/i18n/ar.mjs": "59310b7bbeea83bbb7d73aadc5ca82a6ec269c3530d19aa741c94d5c64fc5da8", + "package/dist/i18n/cs.js": "6e2c93daba24c2e48dfa206bcf4f2f2a8a02713b856e404d2e4e4ecd8309ca2f", + "package/dist/i18n/cs.mjs": "c73fba18797d62d7b2581e67fe7340c35fe84510b2250ff1ecaad253d9cabbac", + "package/dist/i18n/es.js": "f4133ac6ea06b69a474b33c4a1809a4066e2376bb9464a3f3aa8330f01f1944a", + "package/dist/i18n/es.mjs": "1390bca3def2be115e811de337df0a0c3f63998e51359f283023246245c552a7", + "package/dist/i18n/fa.js": "8d3ef2e0f0cc0ea480c2167b6fcb87cdf444e90759c3519e8daa139fec576da4", + "package/dist/i18n/fa.mjs": "ab441e8a2d79043b29ceca87c783bf06874bb9d3a149c1604bc167eb481be2a9", + "package/dist/i18n/fr.js": "89dc4194a6c0a962b9ce756b52c90401c1acf3b159f265f3bc0d7d6d7d987f03", + "package/dist/i18n/fr.mjs": "99491878430fb7b6f2b7bf93da0588a831704b412e01116f23ba1d9d45863816", + "package/dist/i18n/id.js": "4c6dc86db741bcc2857df55a9d037e82e91d375fa6fd22e0562809ff621b92db", + "package/dist/i18n/id.mjs": "383e5803a6b6686c6de4e867770241de75b6f517dcca2bdedc6f2004bf6e1fb3", + "package/dist/i18n/pl.js": "3a38828b1c3d719c0aca9906cf6a46c23555a8411f8d57645f0e0ce8147dc664", + "package/dist/i18n/pl.mjs": "079ed96732979035297af387c64a7b99519b5a43107fb86735a654ea4fe8e9b5", + "package/dist/i18n/ru.js": "62fac9dddb302eb16382e6e9301fc4b2c66f5f69d4a358a26b67026a5fa4e787", + "package/dist/i18n/ru.mjs": "a893aeaee1d79409db72e324fbff200e3864f33ed168f04d91d25f7cf2efa765", + "package/dist/i18n/tr.js": "1492c36b069a7d73db4f3913bd18085faff7afd8b3c158138fe264e31600e38c", + "package/dist/i18n/tr.mjs": "a90478cac078836b76c2a98261907610fee07f3e4b28671b8e7dbf533778f918", + "package/dist/i18n/vi.js": "c7216ef7e608a79b7e2c479a07bf310d4ab79577947ac4a62d676faaced0e9cf", + "package/dist/i18n/vi.mjs": "66270fdfa0b5da396322d1769169b7df37a030ba15ac18eb30bd87b3af8074ec", + "package/dist/i18n/zh-tw.js": "11c49857d6d40a5ffd224ffa59c9026e71621b345f63c5a086326db80a22371a", + "package/dist/i18n/zh-tw.mjs": "a38e630cb96de96c15d824686aec750e25cf6c8ada01285a292442a81566314f", + "package/dist/icons.ts": "0d8e8e52e52130dd4a29bab551dd7a2a954797e42e92a6e068c85d583e23e6a4", + "package/dist/language.cts": "2248040d86a8e34626755c4ecf53ea0174706c653073cf0e15884a030d173120", + "package/dist/language.mts": "c391e863012c77769e661afbf8fa949e9373aa88d17f58cbba7f5696edb8077f", + "package/dist/legacy/language.ts": "c452ffd764ff9b29b819b3475c42395d2ea7a33560d8c186ef5df3844b9524c3", + "package/dist/option.ts": "850e5264219b8b438c80694402225b690b131786677a5da0d22739ec2352d263", + "package/dist/playback.ts": "1ceaed3aa1cef511fe8b20f4874647f6065c723ad805b742a0a9247e16bb1bb5", + "package/dist/player.ts": "f1e50257df2ea592d0a0e1f36078dda77eaf2cf3b3d795307501396562d99f28", + "package/dist/plugin.ts": "2a57e148fda00ee687da5036b890b0f611cb94bb403a1ed52602179060259d35", + "package/dist/quality.ts": "7ed6ed78cea52991b2f6ddd2bd3b7bd8c5146fdfff7486fe8b814c3e632f1567", + "package/dist/runtime.cts": "52cfff1daeb6841a90212c8fa8d46ca46d6650bb7ed1b59b62115d854b8ad652", + "package/dist/runtime.mts": "1b09bc0ee0f0ee2f788689eaff441bdeda1fab0ce783e71b61b9792d81072b03", + "package/dist/runtime.ts": "ec4b1d56993eb482a77eb0f94b701047549d009e6ba6433f27e7d464fef30c2b", + "package/dist/runtime/component.ts": "840213299c942fe058eb5436977ceec066b9dccda24c55ae9f464b399c979e39", + "package/dist/runtime/construction.ts": "24cc5cda1f614314db382271a9b0d5583b781894cfdf6c548700b1b5270467ee", + "package/dist/runtime/events.ts": "52848f57d35eb1037cf74c8877521edf99a015bcd77f30ef6bbcba1807a99bfe", + "package/dist/runtime/media.ts": "8828f80953aec3e95328cefe691661e8897cb27cb309483cf806ec45a8549ae3", + "package/dist/runtime/option.ts": "bb672e49ffe74a6fc78543217f6de80d7d738890146df0857c709d4dc283e4ce", + "package/dist/runtime/player.ts": "a4a24794e26cfb9e4310bf0f459a7a407e85bf81b4f3a4feb6294331af8036fb", + "package/dist/runtime/plugin.ts": "9ad482f2a77914a91450419680b8af6b314b926f0831c2f7f6d7c466690f95c9", + "package/dist/runtime/services.ts": "8e4648d8c61bff47343f9e1c2b995af69fc00947264b826109ceadcbaf553254", + "package/dist/runtime/setting.ts": "c48d20957b57f79f985ef76a2950cc8a5cfa25dafa8ec5939e562661778e6218", + "package/dist/runtime/subtitle.ts": "13241c4f9a87517aa2cf7b3e0bb8cedaa12fa0b4d3f8934285797583b752557e", + "package/dist/runtime/template.ts": "da9975513a1938e7f386f4f65f718f0fc595164bfeac8f0cd99a3e97a54919a1", + "package/dist/runtime/utils.ts": "cab9916f5224a3b047812b9f759cc9cc62105662599744067a6955020ab36916", + "package/dist/setting.ts": "d12a75b656fff70cbb30be4bffec46d9c4273636e8722bce971a4d2b188712a1", + "package/dist/subtitle.ts": "d25b470e47d6a9bd696d9c1ad8adb58e56a208f05695186d513bc8c43c2e8acf", + "package/dist/template.ts": "68aed9e97d006d6be4fabb8504ca84dafab770b0925f4d6e7319e67c82f21b0d", + "package/dist/utils.ts": "494ec447ab467a07ddd7a2c2abf778b3fce69fd28a09aaa8ced9759f8de2e3f6", + "package/package.json": "82e92699e7c29847f32ac6767ece0401290e7d7a12854722bdbcfd09b90b7954", + "package/types/COMPATIBILITY.md": "a7da9e102a116104b513df4f7451247b7759bc502d3375f30bd5b14a541826e0", + "package/types/README.md": "1bd9868e773610e6312fd48a1c5aa20772d29df0af40c3db66e75b7ab4a1aee0", + "package/types/artplayer.d.cts": "3685065e4263abcfd9d953176e461d5b3eb990b0363b54689a7f55deba770fef", + "package/types/artplayer.d.mts": "b664fec51ee5c5381f2ceab25d99d52aee56feab04a2ee7b4ea5b2145ea5fa0e", + "package/types/artplayer.d.ts": "5d0162adf7ac6d0f2bb9b5cfe361f28657c4d9c540cadbf36eba3cc954ce50f8", + "package/types/component.d.ts": "ae5bc1ddeaa1ee1ebbc62573e32423993727079205c64751a5a314170e588a32", + "package/types/config.d.ts": "36f9a84cd436f17701a0d11d3417fc1fe0414e0c52c80083b94a85dfc329e37e", + "package/types/cssVar.d.ts": "93217860fff2ebf750f606d95ddd22220a58837119e63bdf2f5f78fd7488458b", + "package/types/emitter.d.ts": "7e1f9a2a8ce65feb95734f1ed52d1334e57aa388bbef2a43d8bc53a9b6f6623c", + "package/types/events.d.ts": "f83624c9268a284477e8bd793fe3562a2363b72f2a44d5c64fe8c6a6cede8830", + "package/types/i18n.d.ts": "f4f2f05bdfda73c74fef34767cb18ee8e11b743120b1c4598e9714d7846018e5", + "package/types/icons.d.ts": "326a67ba7bdcf52862c5b2a269e540140aff98d05c7b96196485a77f84fe5960", + "package/types/language.d.cts": "f7928e108cf7b7814c2d75cb0a525333e95873fca6cd6d89a740450656b9c1cd", + "package/types/language.d.mts": "6619a26d604b7cccbe2f596243d87ae37cfaae33b8b52874ec071d9787446f9e", + "package/types/legacy/language.d.ts": "c7abda4d506fd933c350e9030290bf9e64347a921d683a66115da1995ebe0a81", + "package/types/option.d.ts": "ccf2c8ddc537dc65c9682d39f044726e23bff18f476b622512d7e6a4b949b5f6", + "package/types/playback.d.ts": "58705c273df6e76a77a6575e769a9ec6c013785c25080f2551aa22415788d870", + "package/types/player.d.ts": "92b00211b70ac71336e495899df972a0519dd138111846044fe4e0f967966177", + "package/types/plugin.d.ts": "fc3594a41c51d8248713e33c486fb1f6a518ac5c7ce31be6988a6cd8d052c5f3", + "package/types/quality.d.ts": "1ac1a48b4f74ff9ba1ce60443e1463d50d1c6a2e5d87d63dc7d2a8eb7f4c2c34", + "package/types/runtime.d.cts": "7ee988dfea9a0ac71a34a656f96f7b74e78aa92547cfdfab7ff7e1139a77d4a3", + "package/types/runtime.d.mts": "dcb85f59a8b7714918ced0c1d18c2bb24e8b80b56d8c594e019dcbdb5ad88b08", + "package/types/runtime.d.ts": "9c6ccd39a1be293f17ae46dae8ea077053e12635ee79d73a5e3b3a559230fcff", + "package/types/runtime/component.d.ts": "1b4c0cee44568649e80140fe778febfcae88af0ac85515f1cbeff78ab0c6d14f", + "package/types/runtime/construction.d.ts": "7a87bedf6f7ffff0c46078aa5f1551e0077d35574e286516121f5f935e2d9ce4", + "package/types/runtime/events.d.ts": "a1e2f9d387475453eaf95c2a5ee79e228636c0bb859bda3fcc46da46574a0c31", + "package/types/runtime/media.d.ts": "8fc89401be7fe25542c169d07dd5675f10916ed20be8493e19695966cf3864a8", + "package/types/runtime/option.d.ts": "6e334e1a76fdcc40b4665c02523bd31c3994d96078fadeb40e23422aa54c1a1d", + "package/types/runtime/player.d.ts": "8ac6383502e4872e83659838844856903031a81a0adc8e8535feb78453aa042c", + "package/types/runtime/plugin.d.ts": "8528f2b17843149f42f9cc16d6fd70baa238c9dc2c329b67d2c38096799c69bd", + "package/types/runtime/services.d.ts": "604c9f8e4b6c0da818f96576bf9f4b0e96085a9003927b2f7ba30027b1ba237e", + "package/types/runtime/setting.d.ts": "349c1fcb205ad312916bd3ee55e6bf241ea8a569fb2ab6c80822e69fc518f7d7", + "package/types/runtime/subtitle.d.ts": "269ff84678d327b3a9dc2f5bc6cc0d731d1e081b85422e474621d5d792efb153", + "package/types/runtime/template.d.ts": "7a62f968a38b4bf9109a4439557c02074eb1d62ba1f6e21a8eb194e91f0badfe", + "package/types/runtime/utils.d.ts": "6bf731705d86853e066dbaf3b8d4003b6548423fe615b7412b722bd0d3a6e48b", + "package/types/setting.d.ts": "08305a75dff96f06490430e099338b463df471b550c1d0dac3abc8995b39eb09", + "package/types/subtitle.d.ts": "b18314e2d64efdb6045830b6e12f615ae2ed585416130e562329ad3ea337f6ce", + "package/types/template.d.ts": "4792bc0c2cbcb7966e4375118bd34b326166e0831862e94ace4cf6b1bbe6a8c8", + "package/types/utils.d.ts": "fca0d3d814df94a25f9eb067c86fa0cf272e2791bac793b91229d57680797712" + } + }, + { + "name": "artplayer-plugin-multiple-subtitles", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\multiple-subtitles-package-types-QhZYAQ\\artplayer-plugin-multiple-subtitles.tgz", + "sha256": "0d28f3fc3844d083723780cd5d0cc9b170bd0535214c3e3ec2a8b0af7f5ebc8d", + "files": { + "package/ARCHITECTURE.md": "287f18046e0965c7766b6aaf6f6e158f4b647a1c4e25a3a9e34912a482f2f1d5", + "package/README.md": "d2fce7b38cb6a5fad4cee7b5895793c664c1f1284e62a42084d8e2848e43166a", + "package/THIRD_PARTY_NOTICES": "50755f38458d41151c7f0b1675c744aa365f0510dc11f53832691ef5c14ce6b6", + "package/dist/artplayer-plugin-multiple-subtitles.js": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446", + "package/dist/artplayer-plugin-multiple-subtitles.legacy.js": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323", + "package/dist/artplayer-plugin-multiple-subtitles.mjs": "07e4f628b4038d7c250f6242c410ddb188148b4ed58bb592975532666412e3e2", + "package/package.json": "9a2050415658e3544fda3e9d35f14e277842b68f6493d0ac99f2920e7baf8cbd", + "package/types/artplayer-plugin-multiple-subtitles.d.cts": "f30630d6f5e69adfb6dd3bc7a262f66824a557a67a49257c41fb5e03e052181b", + "package/types/artplayer-plugin-multiple-subtitles.d.mts": "f30630d6f5e69adfb6dd3bc7a262f66824a557a67a49257c41fb5e03e052181b", + "package/types/artplayer-plugin-multiple-subtitles.d.ts": "30d4ced6135354e722e13cdf8890b58fa1f53f9840e211b98d3952f5c5ac5e82", + "package/types/runtime.d.cts": "c0b2c27d9562265fd82aae5b5259d4641b44be8ed723d1bed7d7be593d7900ff", + "package/types/runtime.d.mts": "49bb05b960e2fd41aa2a4eca2d2f39c4ea52845ac5b37e4ea66b265c995a06d4", + "package/types/runtime.d.ts": "49bb05b960e2fd41aa2a4eca2d2f39c4ea52845ac5b37e4ea66b265c995a06d4" + } + } + ], + "historicalPackages": [ + { + "label": "published-1.0.0", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-multiple-subtitles-1.0.0.tgz", + "sha256": "9b584f27287dbda1722315c07d05e1d7391453130b8fd9f952669e809e662ef5", + "missingEntrypoints": {} + }, + { + "label": "published-1.1.0", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-multiple-subtitles-1.1.0.tgz", + "sha256": "b5e8cfa9671496b08a6f1377bace4ce557c865335dc2f746156707c03c8d9d16", + "missingEntrypoints": {} + }, + { + "label": "published-1.2.0", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-multiple-subtitles-1.2.0.tgz", + "sha256": "a013fbe0772bf560d177449fc80e848c41f16ae2f72ace86f8fb0d5b1000a54e", + "missingEntrypoints": {} + } + ], + "matrix": [ + { + "plugin": "published-1.0.0", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": true, + "diagnostics": [] + }, + "default": { + "typeChecks": false, + "diagnostics": [ + 2339 + ] + } + } + }, + { + "plugin": "published-1.0.0", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": true, + "diagnostics": [] + }, + "default": { + "typeChecks": false, + "diagnostics": [ + 2339 + ] + } + } + }, + { + "plugin": "published-1.0.0", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": {} + }, + { + "plugin": "published-1.0.0", + "compiler": "5.9.3", + "mode": "bundler-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": {} + }, + { + "plugin": "published-1.0.0", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": true, + "diagnostics": [] + }, + "default": { + "typeChecks": false, + "diagnostics": [ + 2339 + ] + } + } + }, + { + "plugin": "published-1.1.0", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": true, + "diagnostics": [] + }, + "default": { + "typeChecks": false, + "diagnostics": [ + 2339 + ] + } + } + }, + { + "plugin": "published-1.1.0", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": true, + "diagnostics": [] + }, + "default": { + "typeChecks": false, + "diagnostics": [ + 2339 + ] + } + } + }, + { + "plugin": "published-1.1.0", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": {} + }, + { + "plugin": "published-1.1.0", + "compiler": "5.9.3", + "mode": "bundler-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": {} + }, + { + "plugin": "published-1.1.0", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": true, + "diagnostics": [] + }, + "default": { + "typeChecks": false, + "diagnostics": [ + 2339 + ] + } + } + }, + { + "plugin": "published-1.2.0", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": false, + "diagnostics": [ + 2322, + 2344, + 2344, + 2322, + 2344, + 2349 + ] + }, + "default": { + "typeChecks": true, + "diagnostics": [] + } + } + }, + { + "plugin": "published-1.2.0", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": false, + "diagnostics": [ + 2322, + 2344, + 2344, + 2322, + 2344, + 2349 + ] + }, + "default": { + "typeChecks": true, + "diagnostics": [] + } + } + }, + { + "plugin": "published-1.2.0", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "expectedHistoricalFailure": true, + "diagnostics": [ + 2322, + 2344, + 7019, + 2349, + 2344, + 2322, + 2344, + 2349 + ], + "rejectedInvalidUses": 0, + "commonjs": {} + }, + { + "plugin": "published-1.2.0", + "compiler": "5.9.3", + "mode": "bundler-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": {} + }, + { + "plugin": "published-1.2.0", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 0, + "commonjs": { + "direct": { + "typeChecks": false, + "diagnostics": [ + 2344, + 2322, + 2344, + 2741, + 2344, + 2349 + ] + }, + "default": { + "typeChecks": true, + "diagnostics": [] + } + } + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": { + "direct": { + "typeChecks": false, + "diagnostics": [ + 2322, + 2344, + 2344, + 2322, + 2344, + 2349 + ] + }, + "default": { + "typeChecks": true, + "diagnostics": [] + } + } + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": { + "direct": { + "typeChecks": false, + "diagnostics": [ + 2322, + 2344, + 2344, + 2322, + 2344, + 2349 + ] + }, + "default": { + "typeChecks": true, + "diagnostics": [] + } + } + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": {} + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "bundler-esm", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": {} + }, + { + "plugin": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": { + "direct": { + "typeChecks": false, + "diagnostics": [ + 2344, + 2322, + 2344, + 2741, + 2344, + 2349 + ] + }, + "default": { + "typeChecks": true, + "diagnostics": [] + } + } + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "node10-commonjs-no-interop", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": {} + }, + { + "plugin": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs-no-interop", + "expectedHistoricalFailure": false, + "diagnostics": [], + "rejectedInvalidUses": 12, + "commonjs": {} + } + ] + }, + "browser": [ + { + "candidate": "source", + "file": "refactor/.cache/multiple-subtitles04-public-browser-source.json", + "sha256": "2d61b7f567cf9d97f20460dc3033e5f14b6ff795742f5908c675f30d52b00226", + "results": "refactor/.cache/multiple-subtitles04-public-browser-source-results", + "stats": { + "startTime": "2026-09-13T04:05:53.916Z", + "duration": 24342.678, + "expected": 36, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "8ad5ddfa1ee1d79f7183b7a5fade748ca2093ceba7b467ec19b5b79ad129a249" + }, + "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 " + }, + "unhandledErrors": [] + } + ], + "scope": "Actual SRT display/selection, pending fetch destroy, Blob revocation, HTTP failure, video source switch and inline timestamps; three desktop engines, published/candidate cores. Expected HTTP/revoked-Blob console/network diagnostics remain in the full attachments. Not physical Safari/mobile or complete historical-core acceptance." + }, + { + "candidate": "main", + "file": "refactor/.cache/multiple-subtitles04-public-browser-main.json", + "sha256": "d228a6e4b78f068825486b2bd58825c270a626b9a23c920f736eded0579d2d05", + "results": "refactor/.cache/multiple-subtitles04-public-browser-main-results", + "stats": { + "startTime": "2026-09-13T04:06:19.013Z", + "duration": 21627.28, + "expected": 36, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446" + }, + "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 " + }, + "unhandledErrors": [] + } + ], + "scope": "Actual SRT display/selection, pending fetch destroy, Blob revocation, HTTP failure, video source switch and inline timestamps; three desktop engines, published/candidate cores. Expected HTTP/revoked-Blob console/network diagnostics remain in the full attachments. Not physical Safari/mobile or complete historical-core acceptance." + }, + { + "candidate": "legacy", + "file": "refactor/.cache/multiple-subtitles04-public-browser-legacy.json", + "sha256": "5197eeeb4cd68cff1d81a767dfeba2de98e123fb3cd8de479591dd261373d33d", + "results": "refactor/.cache/multiple-subtitles04-public-browser-legacy-results", + "stats": { + "startTime": "2026-09-13T04:06:41.353Z", + "duration": 21271.851, + "expected": 36, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "chromium", + "version": "153.0.8010.12", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "firefox", + "version": "155.0", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: SRT display and selectors survive the resource refactor", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy settles a pending native request before it responds", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: destroy revokes the Blob and retained reset stays inert", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: native HTTP error rejects without installing subtitles", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: video source switching retains cached subtitle selection", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate published core: inline timestamps remain native instructions without visible time tags", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "published", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "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 " + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: SRT display and selectors survive the resource refactor", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "nativeSrtDisplay": true, + "released": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy settles a pending native request before it responds", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "name": "multipleSubtitles", + "voidReset": true, + "initialized": 0, + "aborted": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: destroy revokes the Blob and retained reset stays inert", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "voidReset": true, + "inaccessible": true, + "initialized": 1, + "revoked": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: native HTTP error rejects without installing subtitles", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "message": "Failed to fetch multiple subtitles: HTTP 404", + "initialized": 0 + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: video source switching retains cached subtitle selection", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "observation": { + "requests": [ + "/test/multiple-restart.vtt" + ], + "nativeSourceSwitch": true + }, + "unhandledErrors": [] + }, + { + "title": "Multiple subtitles candidate candidate core: inline timestamps remain native instructions without visible time tags", + "browser": "webkit", + "version": "26.6", + "platform": "win32", + "core": "candidate", + "implementation": { + "name": "candidate", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323" + }, + "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 " + }, + "unhandledErrors": [] + } + ], + "scope": "Actual SRT display/selection, pending fetch destroy, Blob revocation, HTTP failure, video source switch and inline timestamps; three desktop engines, published/candidate cores. Expected HTTP/revoked-Blob console/network diagnostics remain in the full attachments. Not physical Safari/mobile or complete historical-core acceptance." + } + ], + "artifacts": [ + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js", + "sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.js" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js", + "sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.legacy.js" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs", + "sha256": "07e4f628b4038d7c250f6242c410ddb188148b4ed58bb592975532666412e3e2", + "copied": "docs/compiled/artplayer-plugin-multiple-subtitles.mjs" + } + ], + "inputs": [ + { + "file": "package.json", + "sha256": "5e196c3f79f1cfc13b57430b994a9a249d4e20935bdf8e6d8e87302677c5064a" + }, + { + "file": "scripts/build-ts.js", + "sha256": "119190f4a72639906d4bbbcdc9205db31b28560aa6be0bfe60f2e2e221a9efb0" + }, + { + "file": "test/multiple-subtitles-lifecycle.test.js", + "sha256": "445d274e8e31e992bd026ce9423d55c71267c27117720cf97de677ec901375e5" + }, + { + "file": "test/types/multiple-subtitles-public.ts", + "sha256": "e0b87c7833c88ac21901dde75048a2a974781c91d0244a4b1c216cb5ef5acc01" + }, + { + "file": "refactor/scripts/multiple-subtitles-types.test.mjs", + "sha256": "c0446f2826addd91c257bdf3da74295e1e8dc993dd204b27bde232fc3c5e139f" + }, + { + "file": "refactor/scripts/multiple-subtitles-package-types.mjs", + "sha256": "f578a874099cea74f3e64c092e40625dd9cf4560d96c72d4affe9d47ab5a616a" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/package.json", + "sha256": "9a2050415658e3544fda3e9d35f14e277842b68f6493d0ac99f2920e7baf8cbd" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.cts", + "sha256": "f30630d6f5e69adfb6dd3bc7a262f66824a557a67a49257c41fb5e03e052181b" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.mts", + "sha256": "f30630d6f5e69adfb6dd3bc7a262f66824a557a67a49257c41fb5e03e052181b" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts", + "sha256": "30d4ced6135354e722e13cdf8890b58fa1f53f9840e211b98d3952f5c5ac5e82" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/types/runtime.d.cts", + "sha256": "c0b2c27d9562265fd82aae5b5259d4641b44be8ed723d1bed7d7be593d7900ff" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/types/runtime.d.mts", + "sha256": "49bb05b960e2fd41aa2a4eca2d2f39c4ea52845ac5b37e4ea66b265c995a06d4" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/types/runtime.d.ts", + "sha256": "49bb05b960e2fd41aa2a4eca2d2f39c4ea52845ac5b37e4ea66b265c995a06d4" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/caption.ts", + "sha256": "cbc44cd8c9171685c0fcba059d6868799ef195a13379dbb7da791582ba5b3402" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/src/index.ts", + "sha256": "7231cbeca6404cc7c021b768089e3f11d59522f3479012ceec3234e25851ffb2" + }, + { + "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": "da6b3390ac671c887567b64e07ee006305e74517e0e06f86738d0e2ba46bc898" + } + ], + "remaining": "Raw 1.0.0/1.1.0 export-assignment module extraction conflicts with 1.2.0 default-module replacement types. TYPE/EXPORT risks and task 04 remain open pending explicit resolution. Vendor entity decoding and complete device/core/editor/distribution acceptance remain task 05/06. No dependency, lockfile or version changes; no push or publication." +} diff --git a/refactor/changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md b/refactor/changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md new file mode 100644 index 000000000..564f05c9a --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md @@ -0,0 +1,67 @@ +# PKG-MULTI-SUB-04 公开类型与实际安装检查点 + +本次处理 API-06/09/11。根入口和 legacy 保留旧的必填 subtitles 数组、可选轨道字段、 +同步 LegacyResult 推导以及可被普通函数替换的工厂类型;新增 `/runtime` 入口提供实际的 +Promise、tracks(names?) 和 reset()。Result/RuntimeOption 由公开定义统一维护, +内部实现复用,parser 与资源宿主边界仍为私有。没有把 Promise 伪装成同步结果。 + +## 最终入口与旧用法 + +- `import subtitles from 'artplayer-plugin-multiple-subtitles'` 和 `/legacy` 的旧默认导入、 + Parameters、ReturnType 和替换函数仍通过;旧返回声明只保留 name,运行时始终异步。 +- 新代码可 `import subtitles from 'artplayer-plugin-multiple-subtitles/runtime'`,通过 + `await subtitles({ subtitles: [...] })(art)` 无断言取得 tracks/reset;options 必传,{} 合法。 +- root/runtime 复用同一产物及函数。现代和 legacy 函数增加可写、可枚举、可配置的 + `.default` 自引用,兼容旧 CommonJS 对象的有效 default 调用;现代直接调用继续有效。 +- root/legacy 使用独立 .d.mts/.d.cts 转发默认声明;runtime 的 .d.cts 用 export= 提供 + 直接及 default 调用;typesVersions 支持 classic 解析。包内新增五个声明文件。 +- 编辑器通过正常 build:ts 语义生成旧全局工厂及命名类型空间。RuntimeFactory 是类型, + 没有新增同名全局函数。onParser 保留原字段类型及不调用的运行时行为。 + +## 实际兼容冲突仍待决 + +真实 1.0.0/1.1.0 包的 CommonJS 是对象.default,但声明 export= 将模块描述成直接函数; +1.2.0 运行时是直接函数,声明却是默认导出模块。安装消费者已分别复现: + +| 类型写法 | 1.0.0 / 1.1.0 | 1.2.0 | 本次候选 | +| --- | --- | --- | --- | +| default import,Parameters/ReturnType,普通函数替换 | 通过 classic | 通过 classic | 五种模式通过 | +| import=require 的原始模块直接提取/替换 | 类型通过;实际直接调用失败 | 类型失败 | 类型失败 | +| import=require 后取 .default 再提取/替换 | 类型失败;实际调用有效 | 类型通过 | 类型通过;实际也可调用 | +| NodeNext ESM 默认导入 | 通过 | 原声明 8 项诊断 | 候选通过 | + +不能将原始模块同时改成“可调用且必须有 default”的类型来静默结束任务:这样普通替换 +函数会缺少 default;若 default 可选,则已有无断言 default 调用失败。两个失败分别有 +2322 和 2722 的编译复现,TS 5.9.3/4.3.5 覆盖。当前候选保留 1.2 的模块声明,旧 JS 两种 +有效入口均可运行,但未声称兼容所有旧类型提取。需要明确接受这项类型迁移差异才能关闭 +对应边界;此前 Ads 的参数推导修正授权不自动覆盖此项。04 和 TYPE/EXPORT 风险保持 open。 + +## 已执行验证 + +- 本包 274 项通过;源码/main/legacy 正常、生命周期、合并各 39 项通过。新增两项验证 + script global/CJS 自别名、属性描述符、真实 async 注册、void 方法及销毁清理。 +- 公开类型五模式正例通过,移除 expect-error 各准确拒绝 12 个非法用法;实际旧三个 + tarball 声明与默认导入/替换消费者分别通过 TS 5.9.3/4.3.5 classic 检查。 +- strict 生产源码 TS 5.9.3/5.1.6 及每编译器 9 个负例通过;编辑器每编译器 2 个负例。 +- 新增 `yarn test:multiple-subtitles-types-package`:三个已发布 tarball × 五模式, + 候选实际 pack × 七模式,共 22 格;旧 1.2 NodeNext ESM 的失败单独登记,不计为兼容成功。 + 使用仓库外消费者、packed core、offline install/frozen reinstall、锁字节一致和全部 + 已安装成员字节核对,断言无 workspace 符号链接或类型逃逸。实际 Node 验证 root/runtime + CJS/ESM 的身份以及 legacy default/require 的身份,不只检查 manifest 路径存在。 +- 初次矩阵复现旧 1.2 NodeNext 无上下文 callback 的 7019 诊断;另一次核对发现旧 TS + 诊断顺序不同。核实具体旧包错误后固定各编译器预期,没有把候选失败列为允许项。 +- Chromium 153.0.8010.12 / Firefox 155.0 / Windows WebKit 26.6,发布/候选核心, + 三入口每个 36 项、共 108 项通过。覆盖 SRT 显示/选择、取消、Blob、HTTP、视频切源、 + 内嵌时间戳。无 skip/flaky;不是物理 Safari、移动设备或全部旧核心验收。 +- 完整 CI 2344 项通过(1989 单元、14 工程、341 基线),严格生产 TS 353 文件;显式 + lint 通过。正常 build 生成三个 dist 及 docs 副本,逐字一致;正常 build:ts 更新编辑器。 + +完整检查及内容指纹见[证据](../baselines/multiple-subtitles-public-types.json)。新增脚本已接入 +本包测试和主 baseline glob;实际安装脚本是独立较慢命令,最终分发集成仍归 06。 +没有新依赖、锁或版本变更。包内 README/ARCHITECTURE 同步入口和模块维护说明。 + +## 后续与回退 + +04 保持 doing,等待上述历史类型取舍结论;实体解码、完整核心/设备/编辑器验证仍归 05/06。 +本次独立本地检查点提交,不推送、不发布。回退恢复旧声明及 manifest,移除 runtime 入口和 +default 别名;此前请求、时间戳与资源修复保持。不能将本检查点视为整个包或项目已可发布。 diff --git a/refactor/fixtures/consumers/multiple-subtitles-commonjs.ts b/refactor/fixtures/consumers/multiple-subtitles-commonjs.ts new file mode 100644 index 000000000..048b38e4d --- /dev/null +++ b/refactor/fixtures/consumers/multiple-subtitles-commonjs.ts @@ -0,0 +1,11 @@ +import type Artplayer from 'artplayer' +// eslint-disable-next-line ts/no-require-imports -- Exercise historical export-assignment consumers. +import plugin = require('artplayer-plugin-multiple-subtitles') + +const vtt = plugin +declare const art: Artplayer +const options: Parameters[0] = { subtitles: [] } +const result: ReturnType> = { name: 'multipleSubtitles' } +const replacement: typeof vtt = (_option: Parameters[0]) => (_art: Artplayer) => result +void vtt(options)(art).name +void replacement diff --git a/refactor/fixtures/consumers/multiple-subtitles-export-alternatives.ts b/refactor/fixtures/consumers/multiple-subtitles-export-alternatives.ts new file mode 100644 index 000000000..58c020a42 --- /dev/null +++ b/refactor/fixtures/consumers/multiple-subtitles-export-alternatives.ts @@ -0,0 +1,7 @@ +import type { Factory } from 'artplayer-plugin-multiple-subtitles' + +declare const replacement: Factory +const required: Factory & { default: Factory } = replacement +const optional: Factory & { default?: Factory } = replacement +optional.default({ subtitles: [] }) +void required diff --git a/refactor/fixtures/consumers/multiple-subtitles-published.ts b/refactor/fixtures/consumers/multiple-subtitles-published.ts new file mode 100644 index 000000000..566b47247 --- /dev/null +++ b/refactor/fixtures/consumers/multiple-subtitles-published.ts @@ -0,0 +1,9 @@ +import Artplayer from 'artplayer' +import subtitles from 'artplayer-plugin-multiple-subtitles' + +const option: Parameters[0] = { subtitles: [{ url: '/en.vtt', name: 'en', type: 'vtt', encoding: 'utf-8', onParser: (...args) => args }, {}] } +const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [subtitles(option)] }) +const result: ReturnType> = { name: 'multipleSubtitles' } +const replacement: typeof subtitles = (_option: Parameters[0]) => (_art: Artplayer) => result +void subtitles(option)(art).name +void replacement diff --git a/refactor/plan.md b/refactor/plan.md index 04be92ffa..7255c5786 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -515,7 +515,7 @@ - PKG-MULTI-SUB-01: [记录](baselines/multiple-subtitles-release.json) [记录](baselines/multiple-subtitles-contract.md) [记录](baselines/multiple-subtitles-contract-validation.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-01-contract.md) - PKG-MULTI-SUB-02: [记录](baselines/multiple-subtitles-failures.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-02-failures.md) - PKG-MULTI-SUB-03: [记录](baselines/multiple-subtitles-resources.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-03-resources.md) -- PKG-MULTI-SUB-04: [记录](baselines/multiple-subtitles-runtime-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md) +- PKG-MULTI-SUB-04: [记录](baselines/multiple-subtitles-runtime-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md) [记录](baselines/multiple-subtitles-public-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-public-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) diff --git a/refactor/progress.md b/refactor/progress.md index e14fb5246..ecc76203e 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,24 @@ # 进度与证据 +## PKG-MULTI-SUB-04 公开类型与仓库外安装检查点(doing) + +保留 root/legacy 的旧必填 subtitles、同步 LegacyResult 提取与可替换工厂;新增 runtime +入口提供真实 Promise 和 tracks/reset,共享同一实现。新增可写 default 自别名、 +分开的 ESM/CJS 声明、classic typesVersions 和语义生成的编辑器类型,包内文档同步。 +实际旧三个 tarball 各五模式、候选 pack 七模式,共 22 格安装检查;offline/frozen 重装、 +全成员字节、类型未逃逸、真实 Node 入口身份通过。旧 1.2 NodeNext ESM 失败独立登记。 +公开声明每模式 12 个反例、编辑器各 2 个反例、严格运行时各 9 个反例通过;本包 274 项, +源码/main/legacy 各 39 项,三浏览器两核心三入口共 108 项通过。完整 CI 2344 项通过 +(1989 单元、14 工程、341 基线),严格生产 TS 353 文件;实际 build 与 docs 副本一致。 +见[变更与待决表](changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md)和 +[证据](baselines/multiple-subtitles-public-types.json)。没有新依赖、锁或版本修改。 +旧 1.0/1.1 原始 CommonJS 模块类型提取与 1.2 default 模块声明矛盾;必填/可选 default +两种替代均有编译失败证据。JS 调用已兼容,纯旧类型差异尚未获接受,04 与 TYPE/EXPORT +风险保持 open。VTT 的相同历史声明问题仍待决;不能扩用此前 Ads 的参数类型授权。 +223 项仍为 121 done、14 doing、88 todo;198 项风险。独立本地检查点,不推送、不发布。 +下一步处理类型取舍及其迁移说明;实体解码、完整设备/核心/编辑器仍待 05/06。 + + ## PKG-MULTI-SUB-07 内嵌时间戳与显示修复完成 修复数字时间戳被文本包装成 NaN;合法 VTT class 标记保留原生 processing instruction, diff --git a/refactor/risks.json b/refactor/risks.json index df8d29e2c..ece6b6525 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -4256,9 +4256,13 @@ "refactor/baselines/multiple-subtitles-release.json", "refactor/baselines/multiple-subtitles-contract.md", "test/multiple-subtitles.test.js", - "refactor/scripts/multiple-subtitles-contract.test.mjs" + "refactor/scripts/multiple-subtitles-contract.test.mjs", + "refactor/baselines/multiple-subtitles-public-types.json", + "refactor/changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md", + "refactor/scripts/multiple-subtitles-types.test.mjs", + "refactor/scripts/multiple-subtitles-package-types.mjs" ], - "compatibleResolution": "Preserve valid old consumers while exposing accurate methods and async types; review onParser variance separately.", + "compatibleResolution": "Root/legacy retain 1.2.0 default-module extraction and replaceable synchronous factory types; additive runtime entry exposes accurate Promise and methods; writable default self alias preserves historical valid JS forms. Actual installed old 1.0.0/1.1.0 raw export-assignment extraction conflicts with 1.2.0 declarations and remains awaiting explicit resolution. Do not close on default-import success alone.", "closureCriteria": "Strict old/new consumers and installed type views cover parameters, result methods and factory replacement." }, { @@ -4332,9 +4336,13 @@ "refactor/baselines/multiple-subtitles-release.json", "refactor/baselines/multiple-subtitles-contract.md", "test/multiple-subtitles.test.js", - "refactor/scripts/multiple-subtitles-contract.test.mjs" + "refactor/scripts/multiple-subtitles-contract.test.mjs", + "refactor/baselines/multiple-subtitles-public-types.json", + "refactor/changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md", + "refactor/scripts/multiple-subtitles-types.test.mjs", + "refactor/scripts/multiple-subtitles-package-types.mjs" ], - "compatibleResolution": "Preserve valid historical runtime import forms and explicitly assess type-only extraction conflicts.", + "compatibleResolution": "Root/legacy retain 1.2.0 default-module extraction and replaceable synchronous factory types; additive runtime entry exposes accurate Promise and methods; writable default self alias preserves historical valid JS forms. Actual installed old 1.0.0/1.1.0 raw export-assignment extraction conflicts with 1.2.0 declarations and remains awaiting explicit resolution. Do not close on default-import success alone.", "closureCriteria": "Actual installed CJS/ESM/global/legacy consumers and their declarations are verified." }, { diff --git a/refactor/scripts/multiple-subtitles-package-types.mjs b/refactor/scripts/multiple-subtitles-package-types.mjs new file mode 100644 index 000000000..5f87e9f30 --- /dev/null +++ b/refactor/scripts/multiple-subtitles-package-types.mjs @@ -0,0 +1,122 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import ts from 'typescript' +import compat from 'typescript-compat' +import { checkFiles, packedFiles } from '../../scripts/package-check.mjs' +import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs' +import { verifyMultipleSubtitlesContract } from './multiple-subtitles-contract.mjs' +import { hash, readMember } from './releases.mjs' + +async function main() { + assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:multiple-subtitles-types-package') + const yarn = process.env.npm_execpath + assert(yarn && fs.existsSync(yarn)) + const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/multiple-subtitles-package-types-')) + console.log(`Multiple subtitles isolated package evidence: ${output}`) + const contract = await verifyMultipleSubtitlesContract() + const packages = [] + for (const name of ['artplayer', 'artplayer-plugin-multiple-subtitles']) { + const archive = path.join(output, `${name}.tgz`) + fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name))) + const manifest = JSON.parse(readMember(archive, 'package/package.json')) + const files = packedFiles(archive) + checkFiles(manifest, files, name === contract.baseline.release.name ? Object.keys(contract.baseline.release.files) : []) + packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) }) + } + const matrix = [] + const core = packages[0] + const candidates = [ + ...[...contract.baseline.previous].reverse().concat(contract.baseline.release).map(release => ({ ...release, archive: contract.archives.get(release.version), label: `published-${release.version}` })), + { ...packages[1], label: 'candidate' }, + ] + for (const plugin of candidates) { + const consumer = consumerDirectory() + try { + writeJson(path.join(consumer, 'package.json'), { name: 'multiple-subtitles-isolated-consumer', private: true, dependencies: Object.fromEntries([core, plugin].map(pkg => [pkg.name, `file:${pkg.archive.replaceAll('\\', '/')}`])) }) + fs.copyFileSync(path.join(workspace, 'yarn.lock'), path.join(consumer, 'yarn.lock')) + fs.writeFileSync(path.join(output, `${plugin.label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer)) + const lock = fs.readFileSync(path.join(consumer, 'yarn.lock')) + fs.writeFileSync(path.join(output, `${plugin.label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer)) + assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock) + fs.writeFileSync(path.join(output, `${plugin.label}-yarn.lock`), lock) + for (const pkg of [core, plugin]) { + const root = path.join(consumer, 'node_modules', pkg.name) + assert.equal(fs.realpathSync(root), root, 'Installed package must not be a workspace link') + for (const [member, expected] of Object.entries(pkg.files)) + assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`) + } + const runtimeFile = path.join(consumer, 'exports.cjs') + fs.writeFileSync(runtimeFile, plugin.label === 'candidate' + ? `const assert = require('node:assert/strict'); +for (const name of ['artplayer-plugin-multiple-subtitles', 'artplayer-plugin-multiple-subtitles/legacy', 'artplayer-plugin-multiple-subtitles/runtime']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory({}), 'function'); } +assert.equal(require('artplayer-plugin-multiple-subtitles/runtime'), require('artplayer-plugin-multiple-subtitles')); +Promise.all(['artplayer-plugin-multiple-subtitles', 'artplayer-plugin-multiple-subtitles/runtime', 'artplayer-plugin-multiple-subtitles/legacy'].map(name => import(name))).then(([root, runtime, legacy]) => { assert.deepEqual(Object.keys(root), ['default']); assert.deepEqual(Object.keys(runtime), ['default']); assert.equal(runtime.default, root.default); assert.equal(root.default.default, root.default); assert.equal(legacy.default, require('artplayer-plugin-multiple-subtitles/legacy')); assert.equal(legacy.default.default, legacy.default); }).catch(error => { console.error(error); process.exitCode = 1; });` + : `const assert = require('node:assert/strict'); const value = require('artplayer-plugin-multiple-subtitles'); assert.equal(typeof value, '${plugin.version === '1.2.0' ? 'function' : 'object'}'); assert.equal(typeof (value.default || value)({}), 'function'); ${plugin.version === '1.2.0' ? '' : 'assert.throws(() => value({}), { name: \'TypeError\', message: \'value is not a function\' });'} assert.equal(typeof value.default, '${plugin.version === '1.2.0' ? 'undefined' : 'function'}');`) + fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer)) + const candidateModes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']] + if (plugin.label === 'candidate') + candidateModes.push([ts, 'node10-commonjs-no-interop'], [compat, 'node10-commonjs-no-interop']) + const historicalModes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']] + for (const [compiler, mode] of (plugin.label === 'candidate' ? candidateModes : historicalModes)) { + const next = mode.startsWith('nodenext') + const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts' + const source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/multiple-subtitles-public.ts' : 'refactor/fixtures/consumers/multiple-subtitles-published.ts'), 'utf8') + const filename = path.join(consumer, `consumer.${extension}`) + const historical = path.join(consumer, `historical.${extension}`) + fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/multiple-subtitles-published.ts'), historical) + const files = plugin.label === 'candidate' ? [filename, historical] : [filename] + if (mode === 'nodenext-cjs' && plugin.label === 'candidate') { + const commonjs = path.join(consumer, 'commonjs.cts') + fs.writeFileSync(commonjs, `import vtt = require('artplayer-plugin-multiple-subtitles'); import legacy = require('artplayer-plugin-multiple-subtitles/legacy'); import runtime = require('artplayer-plugin-multiple-subtitles/runtime'); vtt.default({subtitles: []}); legacy.default({subtitles: []}); runtime({}); runtime.default({});`) + files.push(commonjs) + } + const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs } + function compile(code) { + fs.writeFileSync(filename, code) + const program = compiler.createProgram(files, options) + for (const file of program.getSourceFiles()) { + const actual = fs.realpathSync(file.fileName) + assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`) + } + return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') })) + } + const diagnostics = compile(source) + const historicalFailure = plugin.version === '1.2.0' && mode === 'nodenext-esm' + const expected = historicalFailure ? [2322, 2344, 7019, 2349, 2344, 2322, 2344, 2349] : [] + writeJson(path.join(output, `${plugin.label}-${compiler.version}-${mode}-diagnostics.json`), { diagnostics, historicalFailure }) + assert.deepEqual(diagnostics.map(item => item.code), expected, `${plugin.label} ${compiler.version} ${mode}`) + const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : [] + if (plugin.label === 'candidate') + assert.equal(invalid.length, 12, 'Installed declarations must reject all invalid uses') + const commonjs = {} + if (mode === 'node10-commonjs' || mode === 'nodenext-cjs') { + const fixture = fs.readFileSync(path.join(workspace, 'refactor/fixtures/consumers/multiple-subtitles-commonjs.ts'), 'utf8') + const oldAssignment = plugin.label !== 'candidate' && plugin.version !== '1.2.0' + for (const form of ['direct', 'default']) { + const consumer = form === 'direct' ? fixture : fixture.replace('const vtt = plugin', 'const vtt = plugin.default') + const diagnostics = compile(consumer) + const expected = form === 'default' + ? oldAssignment ? [2339] : [] + : oldAssignment ? [] : compiler === compat ? [2344, 2322, 2344, 2741, 2344, 2349] : [2322, 2344, 2344, 2322, 2344, 2349] + writeJson(path.join(output, `${plugin.label}-${compiler.version}-${mode}-require-${form}.json`), { diagnostics, expected }) + assert.deepEqual(diagnostics.map(item => item.code), expected, `${plugin.label} ${compiler.version} ${mode} require ${form}`) + commonjs[form] = { diagnostics, typeChecks: diagnostics.length === 0 } + } + } + matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid, commonjs }) + } + } + finally { + removeConsumer(consumer) + } + } + writeJson(path.join(output, 'report.json'), { suite: 'multiple-subtitles-isolated-package-types', introducedBy: 'PKG-MULTI-SUB-04', scope: 'Three actual published packages in five compiler modes and packed candidate in seven modes, installed outside the workspace with packed core. Offline frozen reinstalls and byte identity verified. Expected old NodeNext ESM and CommonJS module extraction failures remain explicit; these do not count as candidate compatibility success. Raw historical export-assignment type compatibility remains unresolved. Full device and distribution acceptance remains separate.', packages, published: candidates.slice(0, 3).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: hash(fs.readFileSync(pkg.archive)), missingEntrypoints: pkg.missingEntrypoints })), matrix }) + console.log(`Multiple subtitles installed matrix verified: ${matrix.length} cases, including ${matrix.filter(item => item.historicalFailure).length} expected historical declaration failure; ${output}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/refactor/scripts/multiple-subtitles-types.test.mjs b/refactor/scripts/multiple-subtitles-types.test.mjs new file mode 100644 index 000000000..0a42296ca --- /dev/null +++ b/refactor/scripts/multiple-subtitles-types.test.mjs @@ -0,0 +1,89 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +// eslint-disable-next-line test/no-import-node-test -- Public declaration compatibility runner. +import test from 'node:test' +import { ESLint } from 'eslint' +import ts from 'typescript' +import compat from 'typescript-compat' +import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs' +import { checkConsumer } from '../../scripts/typecheck.mjs' +import { verifyMultipleSubtitlesContract } from './multiple-subtitles-contract.mjs' +import { readMember } from './releases.mjs' + +const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']] + +test('Multiple subtitles editor globals keep old extraction and expose explicit asynchronous types', async () => { + const source = fs.readFileSync('packages/artplayer-plugin-multiple-subtitles/types/artplayer-plugin-multiple-subtitles.d.ts', 'utf8') + const generated = generatePluginEditorDeclaration(source, 'artplayerPluginMultipleSubtitles') + const file = 'docs/assets/ts/artplayer-plugin-multiple-subtitles.d.ts' + const [formatted] = await new ESLint({ fix: true, fixTypes: ['layout'] }).lintText(generated, { filePath: file }) + assert.equal(formatted.errorCount, 0) + assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated) + const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8') + const consumer = `declare const art: Artplayer; +const option: artplayerPluginMultipleSubtitles.Option = { subtitles: [{name: 'en'}] }; +const old: artplayerPluginMultipleSubtitles.LegacyResult = artplayerPluginMultipleSubtitles(option)(art); +declare const runtime: artplayerPluginMultipleSubtitles.RuntimeFactory; +const pending: Promise = runtime({})(art); +pending.then(result => { result.tracks(['en']); result.reset(); }); +// @ts-expect-error Historical extraction remains synchronous. +const changed: Promise = old; +// @ts-expect-error URL is text. +artplayerPluginMultipleSubtitles({ subtitles: [{ url: 1 }] });` + for (const compiler of [ts, compat]) { + assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), []) + assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 2) + } +}) + +test('Multiple subtitles public extraction stays exact in five modes while runtime methods need no cast', () => { + const source = fs.readFileSync('test/types/multiple-subtitles-public.ts', 'utf8') + for (const [compiler, mode] of modes) { + assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`) + assert.equal(checkConsumer(compiler, mode, source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 12) + } +}) + +test('Multiple subtitles runtime CommonJS supports direct and default calls', () => { + assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import runtime = require('artplayer-plugin-multiple-subtitles/runtime'); +import legacy = require('artplayer-plugin-multiple-subtitles'); +import type Artplayer from 'artplayer'; declare const art: Artplayer; +const first = runtime({})(art); const second = runtime.default({})(art); +const promise: Promise<{name: 'multipleSubtitles', tracks(names?: string[]): void, reset(): void}> = first; +legacy.default({subtitles: []})(art).name; void [promise, second];`), []) +}) + +test('Multiple subtitles raw callable module alternatives retain the required-default versus replacement conflict', () => { + const source = fs.readFileSync('refactor/fixtures/consumers/multiple-subtitles-export-alternatives.ts', 'utf8') + for (const [compiler, mode] of [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [compat, 'node10-commonjs']]) { + assert.deepEqual(checkConsumer(compiler, mode, source).map(item => ({ line: item.line, code: item.code })), [{ line: 4, code: 2322 }, { line: 6, code: 2722 }]) + } +}) + +test('Multiple subtitles actual three historical declarations retain default-import and replacement consumers', async () => { + const contract = await verifyMultipleSubtitlesContract() + const consumer = fs.readFileSync('refactor/fixtures/consumers/multiple-subtitles-published.ts', 'utf8') + for (const release of [contract.baseline.release, ...contract.baseline.previous]) { + const old = readMember(contract.archives.get(release.version), `package/${release.manifest.types.replace(/^\.\//, '')}`).toString() + for (const compiler of [ts, compat]) { + const filename = path.resolve('test/types/multiple-subtitles-historical-consumer.ts') + const declaration = path.resolve('test/types/multiple-subtitles-historical.d.ts') + const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: true, module: compiler.ModuleKind.CommonJS, moduleResolution: compiler.ModuleResolutionKind.NodeJs, target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'] } + function historical(source) { + const host = compiler.createCompilerHost(options) + const read = host.getSourceFile.bind(host) + host.getSourceFile = (file, language, ...rest) => path.resolve(file) === filename + ? compiler.createSourceFile(file, source.replace('from \'artplayer-plugin-multiple-subtitles\'', 'from \'./multiple-subtitles-historical\''), language, true) + : path.resolve(file) === declaration ? compiler.createSourceFile(file, old, language, true) : read(file, language, ...rest) + const exists = host.fileExists.bind(host) + host.fileExists = file => path.resolve(file) === declaration || exists(file) + return compiler.getPreEmitDiagnostics(compiler.createProgram([filename, declaration], options, host)).map(error => error.code) + } + assert.deepEqual(historical(consumer), []) + assert.deepEqual(historical(`${consumer}\nsubtitles();`), [2554]) + assert.deepEqual(historical(`${consumer}\nconst absent: Parameters[0] = undefined;`), [2322]) + assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', consumer), []) + } + } +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index f05b03e27..4b646b7f3 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2325,7 +2325,9 @@ "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容", "evidence": [ "baselines/multiple-subtitles-runtime-types.json", - "changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md" + "changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md", + "baselines/multiple-subtitles-public-types.json", + "changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md" ] }, { diff --git a/scripts/build-ts.js b/scripts/build-ts.js index 6d51d20c8..6fafdf813 100644 --- a/scripts/build-ts.js +++ b/scripts/build-ts.js @@ -53,7 +53,7 @@ console.log(`✨ Built ${artplayerTSoutput}`); const type = pluginsTS[index] const { name, file } = parsePluginInfo(type) const source = String(fs.readFileSync(type)) - const semanticPlugin = ['artplayerPluginVttThumbnail', 'artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerProxyMediabunny', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe', 'ArtplayerToolThumbnail'].includes(name) + const semanticPlugin = ['artplayerPluginMultipleSubtitles', 'artplayerPluginVttThumbnail', 'artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerProxyMediabunny', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe', 'ArtplayerToolThumbnail'].includes(name) const localTypes = name === 'artplayerProxyMediabunny' ? { './media': fs.readFileSync(path.join(path.dirname(type), 'media.d.ts'), 'utf8') } : {} const code = semanticPlugin ? generatePluginEditorDeclaration(source, name, localTypes) diff --git a/test/multiple-subtitles-lifecycle.test.js b/test/multiple-subtitles-lifecycle.test.js index 9f45e0b61..71ba55f02 100644 --- a/test/multiple-subtitles-lifecycle.test.js +++ b/test/multiple-subtitles-lifecycle.test.js @@ -24,6 +24,29 @@ function empty(env) { for (const listeners of env.listeners.values()) assert.equal(listeners.size, 0) } +for (const script of [false, true]) { + test(`Multiple subtitles candidate: ${script ? 'script global' : 'CommonJS'} self alias preserves identity, registration and writable metadata`, async () => { + const env = create({ script }) + assert.equal(typeof env.exported, 'function') + assert.equal(env.exported.default, env.exported) + assert.equal(env.factory.default.default, env.factory) + const descriptor = Object.getOwnPropertyDescriptor(env.factory, 'default') + assert.equal(descriptor.writable, true) + assert.equal(descriptor.configurable, true) + assert.equal(descriptor.enumerable, true) + const pending = env.factory.default({})(env.art) + assert.equal(typeof pending.then, 'function') + const result = await pending + assert.equal(result.name, 'multipleSubtitles') + assert.equal(result.tracks(), undefined) + assert.equal(result.reset(), undefined) + env.factory.default = () => 'replacement' + assert.equal(env.factory.default(), 'replacement') + env.emit('destroy') + empty(env) + }) +} + for (const abortController of [true, false]) { for (const phase of ['fetch', 'body']) { test(`Multiple subtitles candidate: destroy cancels ${phase}, AbortController=${abortController}`, async () => { diff --git a/test/types/multiple-subtitles-public.ts b/test/types/multiple-subtitles-public.ts new file mode 100644 index 000000000..0ad2802c5 --- /dev/null +++ b/test/types/multiple-subtitles-public.ts @@ -0,0 +1,55 @@ +import type { Factory, LegacyResult, Option, Result, RuntimeFactory, TrackOption } from 'artplayer-plugin-multiple-subtitles' +import Artplayer from 'artplayer' +import legacy from 'artplayer-plugin-multiple-subtitles' +import legacyEntry from 'artplayer-plugin-multiple-subtitles/legacy' +import runtime from 'artplayer-plugin-multiple-subtitles/runtime' + +type Equal = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false +type Assert = T +type ExactOptions = Assert, [option: Option]>> +type ExactFactory = Assert> +type ExactResult = Assert>, LegacyResult>> +type ActualResult = Assert>, Promise>> +type OldTrack = Assert object }>> + +const option: Option = { subtitles: [{ url: '/en.vtt', name: 'en', onParser: (...args) => args }] } +const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [legacy(option), runtime(option)] }) +const old: LegacyResult = legacy(option)(art) +const replacement: typeof legacy = (_option: Option) => (_art: Artplayer) => ({ name: 'multipleSubtitles' }) +legacyEntry(option) +const factory: RuntimeFactory = runtime +const pending: Promise = factory({})(art) +const alias: Promise = factory.default.default(option)(art) +factory.default = runtime +pending.then((result) => { + const returned: void = result.tracks(['en']) + result.tracks() + result.reset() + // @ts-expect-error Selection names are strings. + result.tracks([1]) + void returned +}) +// @ts-expect-error Historical subtitles field remains required. +legacy({}) +// @ts-expect-error Published extraction keeps the option object required. +const absent: Parameters[0] = undefined +// @ts-expect-error Root declarations preserve historical synchronous extraction. +const changed: Promise = old +// @ts-expect-error Registration has no synchronous name. +void pending.name +// @ts-expect-error Factory options remain required. +runtime() +// @ts-expect-error Factory options cannot be undefined. +runtime(undefined) +// @ts-expect-error URL remains a string. +runtime({ subtitles: [{ url: 1 }] }) +// @ts-expect-error Invalid subtitle format remains rejected. +runtime({ subtitles: [{ type: 'xml' }] }) +// @ts-expect-error Unknown options remain rejected. +runtime({ unknown: true }) +// @ts-expect-error A host is required. +runtime({})() +// @ts-expect-error Accurate runtime types cannot accept a synchronous registrar. +const wrong: typeof runtime = replacement +void [alias, absent, changed, wrong] +export type { ActualResult, ExactFactory, ExactOptions, ExactResult, OldTrack }