mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
fix(core): [CORE-SUBTITLE-OFFSET-01] refresh paused native cue membership and order
This commit is contained in:
1 parent
b5dee252d4
commit
efed847cdf
23 files changed
+6789
-27
No files matched your search
@@ -67,4 +67,4 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";function t(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var e,r={exports:{}};var n=(e||(e=1,r.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),c=r(t);if("object"===s){if("object"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(c,"'"));Object.keys(e).forEach(function(r){var s=t[r],c=e[r],l=n.slice();l.push(r),i(s,c,l),a(s,c,l),o(s,c,l)})}if("array"===s){if("array"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(c,"'"));t.forEach(function(r,s){var c=t[s],l=e[s]||e[0],d=n.slice();d.push(s),i(c,l,d),a(c,l,d),o(c,l,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),r.exports);const o=t(n),i="5.4.1";function a(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.appendChild(r)}):(document.head||document.documentElement).appendChild(r)),r.textContent=e}const s=globalThis.CUSTOM_USER_AGENT??("undefined"===typeof navigator?"":navigator.userAgent),c=/^(?:(?!chrome|android).)*safari/i.test(s),l=/iPad|iPhone|iPod/i.test(s)&&("undefined"===typeof window||!window.MSStream),d=l||s.includes("Macintosh")&&"undefined"!==typeof navigator&&navigator.maxTouchPoints>=1,u=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(s)||d,p="undefined"!==typeof window&&"undefined"!==typeof document;const f={properties:["audioTracks","autoplay","buffered","controller","controlLine truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";function t(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var e,r={exports:{}};var n=(e||(e=1,r.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),c=r(t);if("object"===s){if("object"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(c,"'"));Object.keys(e).forEach(function(r){var s=t[r],c=e[r],l=n.slice();l.push(r),i(s,c,l),a(s,c,l),o(s,c,l)})}if("array"===s){if("array"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(c,"'"));t.forEach(function(r,s){var c=t[s],l=e[s]||e[0],d=n.slice();d.push(s),i(c,l,d),a(c,l,d),o(c,l,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),r.exports);const o=t(n),i="5.4.1";function a(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.appendChild(r)}):(document.head||document.documentElement).appendChild(r)),r.textContent=e}const s=globalThis.CUSTOM_USER_AGENT??("undefined"===typeof navigator?"":navigator.userAgent),c=/^(?:(?!chrome|android).)*safari/i.test(s),l=/iPad|iPhone|iPod/i.test(s)&&("undefined"===typeof window||!window.MSStream),d=l||s.includes("Macintosh")&&"undefined"!==typeof navigator&&navigator.maxTouchPoints>=1,u=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(s)||d,p="undefined"!==typeof window&&"undefined"!==typeof document;const f={properties:["audioTracks","autoplay","buffered","controller","controlLine truncated
|
||||
@@ -67,4 +67,4 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,r=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(e,r,n)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[r]=n,s=(t,e)=>{for(var r in e||(e={}))o.call(e,r)&&a(t,r,e[r]);if(n)for(var r of n(e))i.call(e,r)&&a(t,r,e[r]);return t},l=(t,n)=>e(t,r(n)),c=(t,e,r)=>new Promise((n,o)=>{var i=t=>{try{s(r.next(t))}catch(e){o(e)}},a=t=>{try{s(r.throw(t))}catch(e){o(e)}},s=t=>t.done?n(t.value):Promise.resolve(t.value).then(i,a);s((r=r.apply(t,e)).next())});function d(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var u,p={exports:{}};var f=(u||(u=1,p.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=n.slice();c.push(r),i(s,l,c),a(s,l,c),o(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=n.slice();d.push(s),i(l,c,d),a(l,c,d),o(l,c,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),p.exports);const h=d(f),m="5.4.1";function v(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.apLine truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,r=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(e,r,n)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[r]=n,s=(t,e)=>{for(var r in e||(e={}))o.call(e,r)&&a(t,r,e[r]);if(n)for(var r of n(e))i.call(e,r)&&a(t,r,e[r]);return t},l=(t,n)=>e(t,r(n)),c=(t,e,r)=>new Promise((n,o)=>{var i=t=>{try{s(r.next(t))}catch(e){o(e)}},a=t=>{try{s(r.throw(t))}catch(e){o(e)}},s=t=>t.done?n(t.value):Promise.resolve(t.value).then(i,a);s((r=r.apply(t,e)).next())});function d(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var u,p={exports:{}};var f=(u||(u=1,p.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=n.slice();c.push(r),i(s,l,c),a(s,l,c),o(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=n.slice();d.push(s),i(l,c,d),a(l,c,d),o(l,c,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),p.exports);const h=d(f),m="5.4.1";function v(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.apLine truncated
|
||||
@@ -6180,6 +6180,19 @@ function stateMix(art) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function refreshPausedCues(track, video, cues) {
|
||||
if (!track || track.mode === "disabled" || !video?.paused || !Number.isFinite(video.currentTime))
|
||||
return;
|
||||
const time2 = video.currentTime;
|
||||
const expected = cues.filter((cue) => cue.startTime <= time2 && time2 < cue.endTime);
|
||||
const active2 = Array.from(track.activeCues || []);
|
||||
if (expected.length === active2.length && expected.every((cue, index) => active2[index] === cue))
|
||||
return;
|
||||
for (const cue of cues)
|
||||
track.removeCue(cue);
|
||||
for (const cue of cues)
|
||||
track.addCue(cue);
|
||||
}
|
||||
function subtitleOffsetMix(art) {
|
||||
const { notice, i18n, template } = art;
|
||||
def(art, "subtitleOffset", {
|
||||
@@ -6199,6 +6212,7 @@ function subtitleOffsetMix(art) {
|
||||
cue.startTime = clamp(cue.originalStartTime + offset, 0, art.duration);
|
||||
cue.endTime = clamp(cue.originalEndTime + offset, 0, art.duration);
|
||||
}
|
||||
refreshPausedCues(template.$track.track, template.$video, cues);
|
||||
art.subtitle.update();
|
||||
notice.show = `${i18n.get("Subtitle Offset")}: ${value}s`;
|
||||
art.emit("subtitleOffset", value);
|
||||
|
||||
@@ -149,9 +149,11 @@ Read native `template.$track.track.cues` for old-core probes: older hosts do not
|
||||
provide the later `subtitle.cues` getter. Core5.1.2/5.1.7 only render their first
|
||||
active cue; the matrix explicitly records lost simultaneous languages for1.1.0+
|
||||
and candidate, while1.0.0's index-merged cue displays both. This remains an open
|
||||
compatibility issue, not an approved limitation. Firefox paused offset assertions
|
||||
currently fail on old and candidate cores; a WebKit5.3.0 source-switch failure is
|
||||
intermittent. See the task05 change and validation JSON for exact failed runs.
|
||||
compatibility issue, not an approved limitation. CORE-SUBTITLE-OFFSET-01 corrects
|
||||
paused offsets in the candidate core; published Firefox hosts retain their exact
|
||||
historical defect observations. WebKit5.3.0/5.4.0 source-switch failures remain
|
||||
under task05 investigation. See its change and validation JSON for original failures
|
||||
and the independent core offset change for the corrected candidate evidence.
|
||||
The matrix is runnable with `yarn test:browser test/browser/multiple-subtitles-combinations.spec.js`;
|
||||
do not replace these failures with skips or count boundary observations as acceptance.
|
||||
See `../../refactor/baselines/multiple-subtitles-contract.md` for exact historical differences.
|
||||
@@ -670,6 +670,18 @@ transport cancellation, conversion, ownership, native DOM events and cue renderi
|
||||
The offset mixin remains the player property entry, with original cue bounds retained
|
||||
on native cue objects. Validation evidence and remaining release gates live in refactor/.
|
||||
|
||||
timing.ts handles native active-cue invalidation after a paused offset edit. Some
|
||||
engines retain the old membership even after cue timestamps change. It compares
|
||||
the expected current interval/order to native activeCues and, only on mismatch,
|
||||
removes and re-adds the original cue objects in their captured order. The mixin
|
||||
then performs the existing update, notice and subtitleOffset emission. It does
|
||||
not seek media, replace cues, toggle track mode or allocate background work.
|
||||
Playing, disabled and capability-limited hosts retain their existing path. A
|
||||
mode-toggle workaround was rejected because Firefox reordered tied cues during
|
||||
successive offsets. Tests in subtitle-offset.spec.js verify synchronous native,
|
||||
public and rendered membership, identity, order, disabled state and resumed play;
|
||||
subtitle-offset-native.spec.js is an independent native video/track probe.
|
||||
|
||||
request.ts races work against scope closure. Superseded requests settle undefined even
|
||||
when the transport ignores AbortSignal; active failures reject. state.ts owns request
|
||||
and track scopes and only generated object URLs. A caller-owned URL is never revoked.
|
||||
|
||||
Vendored
+1
-1
@@ -67,4 +67,4 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";function t(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var e,r={exports:{}};var n=(e||(e=1,r.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),c=r(t);if("object"===s){if("object"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(c,"'"));Object.keys(e).forEach(function(r){var s=t[r],c=e[r],l=n.slice();l.push(r),i(s,c,l),a(s,c,l),o(s,c,l)})}if("array"===s){if("array"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(c,"'"));t.forEach(function(r,s){var c=t[s],l=e[s]||e[0],d=n.slice();d.push(s),i(c,l,d),a(c,l,d),o(c,l,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),r.exports);const o=t(n),i="5.4.1";function a(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.appendChild(r)}):(document.head||document.documentElement).appendChild(r)),r.textContent=e}const s=globalThis.CUSTOM_USER_AGENT??("undefined"===typeof navigator?"":navigator.userAgent),c=/^(?:(?!chrome|android).)*safari/i.test(s),l=/iPad|iPhone|iPod/i.test(s)&&("undefined"===typeof window||!window.MSStream),d=l||s.includes("Macintosh")&&"undefined"!==typeof navigator&&navigator.maxTouchPoints>=1,u=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(s)||d,p="undefined"!==typeof window&&"undefined"!==typeof document;const f={properties:["audioTracks","autoplay","buffered","controller","controlLine truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";function t(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var e,r={exports:{}};var n=(e||(e=1,r.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),c=r(t);if("object"===s){if("object"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(c,"'"));Object.keys(e).forEach(function(r){var s=t[r],c=e[r],l=n.slice();l.push(r),i(s,c,l),a(s,c,l),o(s,c,l)})}if("array"===s){if("array"!==c)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(c,"'"));t.forEach(function(r,s){var c=t[s],l=e[s]||e[0],d=n.slice();d.push(s),i(c,l,d),a(c,l,d),o(c,l,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),r.exports);const o=t(n),i="5.4.1";function a(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.appendChild(r)}):(document.head||document.documentElement).appendChild(r)),r.textContent=e}const s=globalThis.CUSTOM_USER_AGENT??("undefined"===typeof navigator?"":navigator.userAgent),c=/^(?:(?!chrome|android).)*safari/i.test(s),l=/iPad|iPhone|iPod/i.test(s)&&("undefined"===typeof window||!window.MSStream),d=l||s.includes("Macintosh")&&"undefined"!==typeof navigator&&navigator.maxTouchPoints>=1,u=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(s)||d,p="undefined"!==typeof window&&"undefined"!==typeof document;const f={properties:["audioTracks","autoplay","buffered","controller","controlLine truncated
|
||||
+1
-1
@@ -67,4 +67,4 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,r=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(e,r,n)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[r]=n,s=(t,e)=>{for(var r in e||(e={}))o.call(e,r)&&a(t,r,e[r]);if(n)for(var r of n(e))i.call(e,r)&&a(t,r,e[r]);return t},l=(t,n)=>e(t,r(n)),c=(t,e,r)=>new Promise((n,o)=>{var i=t=>{try{s(r.next(t))}catch(e){o(e)}},a=t=>{try{s(r.throw(t))}catch(e){o(e)}},s=t=>t.done?n(t.value):Promise.resolve(t.value).then(i,a);s((r=r.apply(t,e)).next())});function d(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var u,p={exports:{}};var f=(u||(u=1,p.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=n.slice();c.push(r),i(s,l,c),a(s,l,c),o(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=n.slice();d.push(s),i(l,c,d),a(l,c,d),o(l,c,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),p.exports);const h=d(f),m="5.4.1";function v(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.apLine truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.Artplayer=e(),define(function(){return t.Artplayer})):(t="undefined"!==typeof globalThis?globalThis:t||self).Artplayer=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,r=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(e,r,n)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[r]=n,s=(t,e)=>{for(var r in e||(e={}))o.call(e,r)&&a(t,r,e[r]);if(n)for(var r of n(e))i.call(e,r)&&a(t,r,e[r]);return t},l=(t,n)=>e(t,r(n)),c=(t,e,r)=>new Promise((n,o)=>{var i=t=>{try{s(r.next(t))}catch(e){o(e)}},a=t=>{try{s(r.throw(t))}catch(e){o(e)}},s=t=>t.done?n(t.value):Promise.resolve(t.value).then(i,a);s((r=r.apply(t,e)).next())});function d(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var u,p={exports:{}};var f=(u||(u=1,p.exports=function(){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e=Object.prototype.toString,r=function(r){if(void 0===r)return"undefined";if(null===r)return"null";var o=t(r);if("boolean"===o)return"boolean";if("string"===o)return"string";if("number"===o)return"number";if("symbol"===o)return"symbol";if("function"===o)return"GeneratorFunction"===n(r)?"generatorfunction":"function";if(function(t){return Array.isArray?Array.isArray(t):t instanceof Array}(r))return"array";if(function(t){return!(!t.constructor||"function"!=typeof t.constructor.isBuffer)&&t.constructor.isBuffer(t)}(r))return"buffer";if(function(t){try{if("number"==typeof t.length&&"function"==typeof t.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(r))return"arguments";if(function(t){return t instanceof Date||"function"==typeof t.toDateString&&"function"==typeof t.getDate&&"function"==typeof t.setDate}(r))return"date";if(function(t){return t instanceof Error||"string"==typeof t.message&&t.constructor&&"number"==typeof t.constructor.stackTraceLimit}(r))return"error";if(function(t){return t instanceof RegExp||"string"==typeof t.flags&&"boolean"==typeof t.ignoreCase&&"boolean"==typeof t.multiline&&"boolean"==typeof t.global}(r))return"regexp";switch(n(r)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(t){return"function"==typeof t.throw&&"function"==typeof t.return&&"function"==typeof t.next}(r))return"generator";switch(o=e.call(r)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return o.slice(8,-1).toLowerCase().replace(/\s/g,"")};function n(t){return t.constructor?t.constructor.name:null}function o(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return i(t,e,n),a(t,e,n),function(t,e,n){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=n.slice();c.push(r),i(s,l,c),a(s,l,c),o(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(n.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=n.slice();d.push(s),i(l,c,d),a(l,c,d),o(l,c,d)})}}(t,e,n),t}function i(t,e,n){if("string"===r(e)){var o=r(t);if("?"===e[0]&&(e=e.slice(1)+"|undefined"),!(-1<e.indexOf("|")?e.split("|").map(function(t){return t.toLowerCase().trim()}).filter(Boolean).some(function(t){return o===t}):e.toLowerCase().trim()===o))throw new Error("[Type Error]: '".concat(n.join("."),"' require '").concat(e,"' type, but got '").concat(o,"'"))}}function a(t,e,n){if("function"===r(e)){var o=e(t,r(t),n);if(!0!==o){var i=r(o);throw"string"===i?new Error(o):"error"===i?o:new Error("[Validator Error]: The scheme for '".concat(n.join("."),"' validator require return true, but got '").concat(o,"'"))}}}return o.kindOf=r,o}()),p.exports);const h=d(f),m="5.4.1";function v(t,e){let r=document.getElementById(t);r||(r=document.createElement("style"),r.id=t,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{document.head.apLine truncated
|
||||
+14
@@ -6180,6 +6180,19 @@ function stateMix(art) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function refreshPausedCues(track, video, cues) {
|
||||
if (!track || track.mode === "disabled" || !video?.paused || !Number.isFinite(video.currentTime))
|
||||
return;
|
||||
const time2 = video.currentTime;
|
||||
const expected = cues.filter((cue) => cue.startTime <= time2 && time2 < cue.endTime);
|
||||
const active2 = Array.from(track.activeCues || []);
|
||||
if (expected.length === active2.length && expected.every((cue, index) => active2[index] === cue))
|
||||
return;
|
||||
for (const cue of cues)
|
||||
track.removeCue(cue);
|
||||
for (const cue of cues)
|
||||
track.addCue(cue);
|
||||
}
|
||||
function subtitleOffsetMix(art) {
|
||||
const { notice, i18n, template } = art;
|
||||
def(art, "subtitleOffset", {
|
||||
@@ -6199,6 +6212,7 @@ function subtitleOffsetMix(art) {
|
||||
cue.startTime = clamp(cue.originalStartTime + offset, 0, art.duration);
|
||||
cue.endTime = clamp(cue.originalEndTime + offset, 0, art.duration);
|
||||
}
|
||||
refreshPausedCues(template.$track.track, template.$video, cues);
|
||||
art.subtitle.update();
|
||||
notice.show = `${i18n.get("Subtitle Offset")}: ${value}s`;
|
||||
art.emit("subtitleOffset", value);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { SubtitleOffsetHost } from '../subtitle/types'
|
||||
import { refreshPausedCues } from '../subtitle/timing'
|
||||
import { clamp, def } from '../utils'
|
||||
|
||||
export default function subtitleOffsetMix(art: SubtitleOffsetHost): void {
|
||||
@@ -21,6 +22,7 @@ export default function subtitleOffsetMix(art: SubtitleOffsetHost): void {
|
||||
cue.startTime = clamp(cue.originalStartTime + offset, 0, art.duration)
|
||||
cue.endTime = clamp(cue.originalEndTime + offset, 0, art.duration)
|
||||
}
|
||||
refreshPausedCues(template.$track.track, template.$video, cues)
|
||||
art.subtitle.update()
|
||||
notice.show = `${i18n.get('Subtitle Offset')}: ${value}s`
|
||||
art.emit('subtitleOffset', value)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { SubtitleCue } from './types'
|
||||
|
||||
export function refreshPausedCues(track: TextTrack | undefined, video: { paused?: boolean, currentTime?: number } | undefined, cues: readonly SubtitleCue[]): void {
|
||||
if (!track || track.mode === 'disabled' || !video?.paused || !Number.isFinite(video.currentTime))
|
||||
return
|
||||
const time = video.currentTime!
|
||||
const expected = cues.filter(cue => cue.startTime <= time && time < cue.endTime)
|
||||
const active = Array.from(track.activeCues || [])
|
||||
if (expected.length === active.length && expected.every((cue, index) => active[index] === cue))
|
||||
return
|
||||
// Some engines retain stale active flags after timing edits while paused.
|
||||
for (const cue of cues)
|
||||
track.removeCue(cue)
|
||||
for (const cue of cues)
|
||||
track.addCue(cue)
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export interface SubtitleView {
|
||||
}
|
||||
|
||||
export interface SubtitleOffsetHost {
|
||||
template: { $track?: SubtitleTrack | null }
|
||||
template: { $track?: SubtitleTrack | null, $video?: { paused?: boolean, currentTime?: number } }
|
||||
subtitle: { readonly cues: SubtitleCue[], update: () => void }
|
||||
readonly duration: number
|
||||
notice: NoticeSink
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,75 @@
|
||||
# CORE-SUBTITLE-OFFSET-01: paused subtitle timing invalidation
|
||||
|
||||
Baseline: b5dee252d461f91389c282a47401c5f8fd9a4483. Independent core correction
|
||||
discovered during PKG-MULTI-SUB-05; that package combination task remains doing.
|
||||
|
||||
## Reproduction and implementation
|
||||
|
||||
Firefox155 keeps cues in native activeCues after a paused offset changes their
|
||||
start/end outside currentTime. At paused time1.5, cues moved from1..3 to2..4
|
||||
still render. The old5.4.0 core and pre-change candidate both fail. A native
|
||||
HTMLVideoElement/TextTrack probe with no Artplayer instance reproduces the stale
|
||||
membership. Chromium153 and Windows WebKit26.6 have separate recorded observations.
|
||||
The first script-created-track probe did not become active on WebKit; the final
|
||||
probe uses a loaded HTML track matching the real integration, and records this
|
||||
fixture correction. Its initial overly precise seek assertion was corrected to
|
||||
require the actual1..2 interval; candidate offset tests separately compare each
|
||||
synchronous read against the pre-edit time.
|
||||
|
||||
`subtitle/timing.ts` owns a small synchronous native invalidation step. After
|
||||
changing cue bounds, the existing offset mixin passes the native track, optional
|
||||
media capabilities and captured cue array. If paused and native membership/order
|
||||
differs from cue intervals at currentTime, remove and re-add the original cue
|
||||
objects in that order. Disabled tracks, playing media, missing capabilities and
|
||||
already-correct native state do not enter this path. No polling, timer, observer,
|
||||
new public option, UA sniffing, media seek or replacement cue is introduced.
|
||||
|
||||
The first attempted mode-toggle correction fixed membership but reversed tied
|
||||
captions on a subsequent negative offset. That failing candidate report is retained;
|
||||
the final reinsertion implementation preserves order through1,-1,0,-3,0 offsets.
|
||||
Original cue bounds, identities, track mode and playback time remain. Update,
|
||||
notice and the public subtitleOffset event keep their existing synchronous order
|
||||
and values. Native cuechange/enter/exit notification can now reflect the corrected
|
||||
active membership, which is an intentional consequence of fixing stale activity.
|
||||
There is no new owned resource or disposal path.
|
||||
|
||||
## Verification and scope
|
||||
|
||||
Exact hashes, browser versions, logs, native state and artifact maps are recorded in
|
||||
[subtitle-offset-validation.json](../baselines/subtitle-offset-validation.json).
|
||||
|
||||
- Five new controlled regressions join the existing subtitle suite: stale/missing
|
||||
members, order drift, cue identity, update/event ordering, correct overlap,
|
||||
disabled/playing/capability-limited hosts. The six-file core group passes58 tests.
|
||||
- Strict core and root consumer TS checks pass. Public generated declarations remain
|
||||
unchanged:37 files checked,0 drift. Runtime helper types are internal only.
|
||||
- Expanded source browser run153:150 passing assertions,3 failures. Two failures
|
||||
are old WebKit core5.3.0/5.4.0 source switching and remain task05 risks; the third
|
||||
was the diagnostic seek precision assumption explained above. Candidate offset,
|
||||
lifecycle and combination checks pass. The run includes explicitly labelled old
|
||||
defects and must not be presented as153 successful compatibility acceptances.
|
||||
- Actual main build:15 passing cases comprise9 native probes,3 published behavior
|
||||
observations and3 candidate offset regressions. Actual legacy build:3 candidate
|
||||
offset cases pass. Candidate tests also cover disabled tracks and resumed play.
|
||||
- Three core formats and docs copies are rebuilt. The normal core build clears the
|
||||
dist directory, so build:i18n regenerates all11 languages; those bytes remain
|
||||
unchanged. Actual import/SSR/i18n distribution checks pass5 tests. CI regression
|
||||
passes50 tests. Lint retains only its existing docs declaration warning.
|
||||
|
||||
Published Firefox defects remain explicit in the offset and combination suites;
|
||||
only the candidate's assertion changes from failing to correct. The independent
|
||||
old-host overlap and WebKit source-switch issues are not waived or resolved here.
|
||||
No physical-device, full package, remote CI or publication acceptance is claimed.
|
||||
No dependency, lockfile, public declaration, version or package entrypoint change.
|
||||
|
||||
## Maintenance and rollback
|
||||
|
||||
Change timestamp invalidation in timing.ts and property bounds/notice behavior in
|
||||
player/subtitleOffsetMix.ts. Keep the native probe independent of player instances.
|
||||
Run node --test test/subtitle.test.js, strict core/root tsc, and the subtitle-offset,
|
||||
subtitle-lifecycle and multiple-subtitles-combinations browser suites. For actual
|
||||
artifact testing use the existing ARTPLAYER_BROWSER_ARTIFACTS map and include both
|
||||
core and chapter mappings, as required by the shared fixture server.
|
||||
|
||||
Revert the dedicated task commit, including build outputs, to remove this correction.
|
||||
This is a local commit only, with no push or npm publication authorization.
|
||||
+6
-4
@@ -2,9 +2,9 @@
|
||||
|
||||
> 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 247 项,范围 22 个包及工作区/示例。
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 248 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 55 / doing 17 / blocked 0 / done 175 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 55 / doing 17 / blocked 0 / done 176 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
| 包 | 基线版本 | 任务 |
|
||||
| --- | --- | --- |
|
||||
| artplayer | 5.4.1 | CORE-01, CORE-02, CORE-03, CORE-04, CORE-05, CORE-06, CORE-07, CORE-08, CORE-09, CORE-10, CORE-11, CORE-12, CORE-13, CORE-14, CORE-15, CORE-16, CORE-17, CORE-18, CORE-19, CORE-20, CORE-21, CORE-23, CORE-22, CORE-24, SITE-BUILD-01, CORE-25, ENG-12 |
|
||||
| artplayer | 5.4.1 | CORE-01, CORE-02, CORE-03, CORE-04, CORE-05, CORE-06, CORE-07, CORE-08, CORE-09, CORE-10, CORE-11, CORE-12, CORE-13, CORE-14, CORE-15, CORE-16, CORE-17, CORE-18, CORE-19, CORE-20, CORE-21, CORE-23, CORE-22, CORE-24, SITE-BUILD-01, CORE-25, ENG-12, CORE-SUBTITLE-OFFSET-01 |
|
||||
| artplayer-plugin-ads | 2.1.0 | PKG-ADS-01, PKG-ADS-02, PKG-ADS-03, PKG-ADS-04, PKG-ADS-05, PKG-ADS-06 |
|
||||
| artplayer-plugin-ambilight | 1.1.0 | PKG-AMBILIGHT-01, PKG-AMBILIGHT-02, PKG-AMBILIGHT-03, PKG-AMBILIGHT-04, PKG-AMBILIGHT-PROXY-01, PKG-AMBILIGHT-05, PKG-AMBILIGHT-06, PKG-FACTORY-01 |
|
||||
| artplayer-plugin-asr | 2.1.0 | PKG-ASR-01, PKG-ASR-02, PKG-ASR-03, PKG-ASR-04, PKG-ASR-05, PKG-ASR-06, PKG-ASR-07, PKG-ASR-08, PKG-ASR-09 |
|
||||
@@ -133,6 +133,7 @@
|
||||
| CORE-22 | artplayer<br>核心阶段完整验收 | CORE-21, CORE-23, PILOT-01, ENG-08, ENG-10, ENG-11 | 核心与旧插件的可自动化完整回归、资源/性能及明确外部验证缺口 | 核心自动化和公开差异处置通过;真实环境缺口链接包集成/REL 门槛,阶段完成不代表可公开发布 | H | done |
|
||||
| CORE-24 | artplayer<br>修复连续切源的播放意图继承 | CORE-22, PKG-AUDIO-04 | 连续切源保留原播放意图,同时尊重显式暂停与最新来源,回归旧/新 audio | 真实暂停状态的受控测试、重入/取消/失败/销毁、三引擎和 main/legacy 产物验证通过;不恢复过期来源的公开副作用 | H | done |
|
||||
| CORE-25 | artplayer<br>修复无 navigator 环境读取默认选项 | CORE-22, ENG-12 | 安全读取默认语言并保留浏览器已有默认值;实际 Node 与发布产物正反例 | 记录旧发布版 ReferenceError;候选在无 navigator 时可读默认选项,浏览器语言和公开类型不回归;不以补全局对象隐藏错误 | M | done |
|
||||
| CORE-SUBTITLE-OFFSET-01 | artplayer<br>修复暂停偏移后的原生活动字幕刷新 | CORE-15, CORE-22 | 暂停字幕偏移的原生轨道一致性修复、兼容回归和维护说明 | 保持cue对象、顺序、媒体时间、模式和公开事件;真实浏览器正负偏移及生命周期通过,历史缺陷单列 | H | done |
|
||||
|
||||
## 5 包迁移:artplayer-plugin-chapter
|
||||
|
||||
@@ -228,7 +229,7 @@
|
||||
| PKG-MULTI-SUB-07 | artplayer-plugin-multiple-subtitles<br>修复字幕内嵌时间戳序列化 | PKG-MULTI-SUB-03 | 保留数字时间戳节点、修正无效 NaN 输出、明确旧/新字幕输出差异 | 旧九份实现可复现,候选有效内嵌时间戳及文字/CSS/原生字幕通过,公开工厂与选择接口不变 | H | done |
|
||||
| PKG-MULTI-SUB-08 | artplayer-plugin-multiple-subtitles<br>修复字幕实体解码与嵌套标签包装 | PKG-MULTI-SUB-04, PKG-MULTI-SUB-07 | 使用解析器既有实体表入口修复多余分号;保护字面文本并以兄弟节点包装标签,保留CSS钩子和时间戳 | 冻结旧实现复现、实体/双重编码/标签/选择重置回归、新旧核心真实字幕显示通过;vendor原文与来源校验不变,类型/分发兼容 | M | done |
|
||||
| PKG-MULTI-SUB-05 | artplayer-plugin-multiple-subtitles<br>验证新旧核心和组合 | PKG-MULTI-SUB-04, CORE-22, PKG-MULTI-SUB-07, PKG-MULTI-SUB-08 | 与核心字幕/偏移/全屏组合,旧名称调用不变 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
|
||||
| PKG-MULTI-SUB-06 | artplayer-plugin-multiple-subtitles<br>验证分发并同步文档 | PKG-MULTI-SUB-05, ENG-07 | multiple.subtitles.js、parser 来源许可及打包边界 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
|
||||
| PKG-MULTI-SUB-06 | artplayer-plugin-multiple-subtitles<br>验证分发并同步文档 | PKG-MULTI-SUB-05, ENG-07, CORE-SUBTITLE-OFFSET-01 | multiple.subtitles.js、parser 来源许可及打包边界 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
|
||||
|
||||
## 5 包迁移:artplayer-plugin-jassub
|
||||
|
||||
@@ -645,3 +646,4 @@
|
||||
- PKG-AUTO-THUMB-09: [记录](changes/2026-09-14-PKG-AUTO-THUMB-09-alias.md) [记录](baselines/auto-thumbnail-alias-validation.json)
|
||||
- PKG-AUTO-THUMB-10: [记录](changes/2026-09-14-PKG-AUTO-THUMB-10-encoding.md) [记录](baselines/auto-thumbnail-encoding-validation.json)
|
||||
- ENG-LINT-01: [记录](changes/2026-09-14-ENG-LINT-01-manifest-order.md) [记录](baselines/manifest-lint-validation.json)
|
||||
- CORE-SUBTITLE-OFFSET-01: [记录](changes/2026-09-14-CORE-SUBTITLE-OFFSET-01-paused.md) [记录](baselines/subtitle-offset-validation.json)
|
||||
@@ -1,5 +1,21 @@
|
||||
# 进度与证据
|
||||
|
||||
## CORE-SUBTITLE-OFFSET-01 暂停字幕偏移修复完成
|
||||
|
||||
原生video/track探针确认Firefox155暂停时cue时间更新后活动标记陈旧;候选核心
|
||||
增加独立timing模块,只在暂停且原生cue成员/顺序与当前时间不一致时,将原cue
|
||||
按原顺序移除并重新加入。保持对象、模式、媒体时间与update/notice/公开偏移事件。
|
||||
先前mode切换方案会逆转同时间字幕顺序,已弃用并保留失败证据。新增5项风险回归,
|
||||
核心定向58项、严格核心/消费者TS、37声明零漂移、导入5项及CI50项通过。
|
||||
扩展源码浏览器153项中150断言通过,旧WebKit两项切源失败保持open,另一个原生
|
||||
探针时间精度假设已修正。正式main15项(含原生和历史观察)、legacy3项全部通过,
|
||||
候选覆盖正负/恢复偏移、禁用轨道和恢复播放。lint仅1既有warning。三格式/docs
|
||||
重建,i18n经正常脚本重建且无差异;main gzip9增加109字节,无依赖、版本或公开
|
||||
声明改动。见[变更](changes/2026-09-14-CORE-SUBTITLE-OFFSET-01-paused.md)与
|
||||
[证据](baselines/subtitle-offset-validation.json)。248项:176 done、17 doing、55 todo。
|
||||
关闭候选偏移缺陷,旧核心的对应失败保留明确观察;这不完成PKG-MULTI-SUB-05。
|
||||
下一步继续旧核心双语显示适配和WebKit切源定位,再完成剩余包组合/分发门槛。
|
||||
|
||||
## PKG-MULTI-SUB-05 新旧核心组合检查点(未完成)
|
||||
|
||||
任务08已独立提交675b095b2,提交审计175 done/171专属提交/4初始例外通过。
|
||||
|
||||
@@ -265,5 +265,5 @@
|
||||
| DEV-SERVOR-PORT-01 | resolved / 已复现 | Servor exits the entire dev process with status zero when the requested port is occupied | MOD-DEV-01 |
|
||||
| AUTO-THUMB-ENCODE-TIMEOUT-01 | resolved / 源码/产物事实 | Pending JPEG encoding has no bounded lifetime after frame readiness finishes | PKG-AUTO-THUMB-10 |
|
||||
| MULTI-SUB-MARKUP-01 | resolved / 已复现 | Semantic cue nodes receive HTML wrappers inside their annotation fields | PKG-MULTI-SUB-08 |
|
||||
| MULTI-SUB-OFFSET-01 | open / 已复现 | Paused Firefox subtitle offset leaves out-of-time native cues active and captions visible | PKG-MULTI-SUB-05 |
|
||||
| MULTI-SUB-OFFSET-01 | resolved / 已复现 | Paused Firefox subtitle offset leaves out-of-time native cues active and captions visible | PKG-MULTI-SUB-05, CORE-SUBTITLE-OFFSET-01 |
|
||||
| MULTI-SUB-SWITCH-01 | open / 待取证 | Intermittent WebKit core5.3.0 caption loss after selected-track source switch | PKG-MULTI-SUB-05 |
|
||||
+20
-7
@@ -6001,24 +6001,35 @@
|
||||
"id": "MULTI-SUB-OFFSET-01",
|
||||
"title": "Paused Firefox subtitle offset leaves out-of-time native cues active and captions visible",
|
||||
"confirmation": "reproduced",
|
||||
"workspaceState": "Two runs on Firefox155 with cores5.3.0/5.4.0/candidate: paused at1.5, subtitleOffset=1 changes start/end to2/4 but native activeCues and HTML still contain both captions after the assertion timeout. Candidate assertion remains failing. No production correction yet.",
|
||||
"workspaceState": "Native Firefox155 probe confirms stale paused membership. Candidate reinserts original cue objects only when paused membership/order is inconsistent; mode toggling was rejected after tied-caption reversal. Real main and legacy pass current paused positive/negative/reset offsets, disabled tracks and resumed playback in all3 engines. Published cores remain immutable defect observations, and package task05 still owns old-host combination acceptance.",
|
||||
"compatibleResolution": "Reduce native timing invalidation and ensure offset updates render the correct active captions without seeking media or changing public timing and event contracts.",
|
||||
"closureCriteria": "Candidate paused positive/negative/reset offsets refresh correctly, transitions and lifecycle regressions pass; historical behavior and actual old-core compatibility are separately documented.",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"owners": [
|
||||
"PKG-MULTI-SUB-05"
|
||||
"PKG-MULTI-SUB-05",
|
||||
"CORE-SUBTITLE-OFFSET-01"
|
||||
],
|
||||
"evidence": [
|
||||
"refactor/changes/2026-09-14-PKG-MULTI-SUB-05-combinations.md",
|
||||
"refactor/baselines/multiple-subtitles-combinations-validation.json",
|
||||
"test/browser/multiple-subtitles-combinations.spec.js"
|
||||
]
|
||||
"test/browser/multiple-subtitles-combinations.spec.js",
|
||||
"refactor/changes/2026-09-14-CORE-SUBTITLE-OFFSET-01-paused.md",
|
||||
"refactor/baselines/subtitle-offset-validation.json",
|
||||
"test/browser/subtitle-offset-native.spec.js",
|
||||
"test/browser/subtitle-offset.spec.js",
|
||||
"test/subtitle.test.js"
|
||||
],
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-14-CORE-SUBTITLE-OFFSET-01-paused.md",
|
||||
"refactor/baselines/subtitle-offset-validation.json"
|
||||
],
|
||||
"resolutionRationale": "Candidate core fix passes focused native/public/rendered membership, identity/order/time/mode/event and lifecycle checks, with actual main/legacy evidence. Historical Firefox failures are separately asserted and documented; closing this candidate defect does not approve all old-core combinations."
|
||||
},
|
||||
{
|
||||
"id": "MULTI-SUB-SWITCH-01",
|
||||
"title": "Intermittent WebKit core5.3.0 caption loss after selected-track source switch",
|
||||
"confirmation": "unverified",
|
||||
"workspaceState": "One Windows WebKit26.6 run lost Translation after switchUrl and seek1.5; diagnostic rerun passed without a production change. Root cause and reproducibility are unresolved; do not mark accepted or fixed.",
|
||||
"workspaceState": "One Windows WebKit26.6 run lost Translation after switchUrl and seek1.5; diagnostic rerun passed without a production change. Root cause and reproducibility are unresolved; do not mark accepted or fixed. Expanded153 offset/lifecycle run again lost the selected caption after source switch on published5.3.0 and5.4.0 in WebKit, while candidate core passed. Root cause remains unclassified; the offset fix does not close this issue.",
|
||||
"compatibleResolution": "Inspect native track loading and source-switch ordering with deterministic state evidence; distinguish historical host failure from fixture ordering and candidate regression.",
|
||||
"closureCriteria": "Root cause classified with targeted evidence, any required fix validated, and changed candidate/old-core combination rerun without hiding the original failure.",
|
||||
"status": "open",
|
||||
@@ -6028,7 +6039,9 @@
|
||||
"evidence": [
|
||||
"refactor/changes/2026-09-14-PKG-MULTI-SUB-05-combinations.md",
|
||||
"refactor/baselines/multiple-subtitles-combinations-validation.json",
|
||||
"test/browser/multiple-subtitles-combinations.spec.js"
|
||||
"test/browser/multiple-subtitles-combinations.spec.js",
|
||||
"refactor/changes/2026-09-14-CORE-SUBTITLE-OFFSET-01-paused.md",
|
||||
"refactor/baselines/subtitle-offset-validation.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
+22
-1
@@ -2429,7 +2429,8 @@
|
||||
],
|
||||
"dependsOn": [
|
||||
"PKG-MULTI-SUB-05",
|
||||
"ENG-07"
|
||||
"ENG-07",
|
||||
"CORE-SUBTITLE-OFFSET-01"
|
||||
],
|
||||
"status": "todo",
|
||||
"risk": "H",
|
||||
@@ -5136,6 +5137,26 @@
|
||||
"changes/2026-09-14-ENG-LINT-01-manifest-order.md",
|
||||
"baselines/manifest-lint-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "CORE-SUBTITLE-OFFSET-01",
|
||||
"phase": "3-4 核心迁移",
|
||||
"title": "修复暂停偏移后的原生活动字幕刷新",
|
||||
"scope": [
|
||||
"artplayer"
|
||||
],
|
||||
"dependsOn": [
|
||||
"CORE-15",
|
||||
"CORE-22"
|
||||
],
|
||||
"status": "done",
|
||||
"risk": "H",
|
||||
"deliverable": "暂停字幕偏移的原生轨道一致性修复、兼容回归和维护说明",
|
||||
"acceptance": "保持cue对象、顺序、媒体时间、模式和公开事件;真实浏览器正负偏移及生命周期通过,历史缺陷单列",
|
||||
"evidence": [
|
||||
"changes/2026-09-14-CORE-SUBTITLE-OFFSET-01-paused.md",
|
||||
"baselines/subtitle-offset-validation.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+13
-2
@@ -219,8 +219,19 @@ VTT/SRT selection, offsets/fullscreen/source changes and multi-instance URL
|
||||
ownership. Old1.0.0/1.1.0/1.2.0 factories run unchanged on the candidate core.
|
||||
Explicit single-active-cue probes record historical and candidate overlap defects;
|
||||
their passing assertions do not mean simultaneous captions are compatible.
|
||||
Paused Firefox offset checks remain failing pending a production fix, and the
|
||||
original WebKit source-switch failure is retained despite a passing rerun.
|
||||
Candidate paused Firefox offsets are corrected by CORE-SUBTITLE-OFFSET-01. Old
|
||||
Firefox hosts retain explicit defect observations; their behavior is not silently
|
||||
counted as correct. WebKit source-switch failures remain under investigation.
|
||||
See `refactor/changes/2026-09-14-PKG-MULTI-SUB-05-combinations.md` before changing
|
||||
expectations. Reports include native active cues and rendered text. The new frozen
|
||||
5.1.2 core route verifies archive integrity and member hashes before serving.
|
||||
|
||||
`subtitle-offset.spec.js` checks current/actual published core offset behavior,
|
||||
including synchronous native/public/rendered captions, identical cue objects,
|
||||
unchanged media time, disabled mode and resumed playback. The published Firefox
|
||||
branch asserts the original incorrect captions; the candidate must have the correct
|
||||
membership and order. `subtitle-offset-native.spec.js` loads a real HTML track
|
||||
without creating an Artplayer instance to compare native timing edits, cue
|
||||
reinsertion and mode toggling. It records actual time within the required interval,
|
||||
not a fabricated millisecond-exact seek landing. These diagnostic observations
|
||||
do not claim every native engine strategy is correct.
|
||||
@@ -38,7 +38,7 @@ async function seek(page, time) {
|
||||
}
|
||||
|
||||
for (const core of cores.filter(core => !['published-5.1.2', 'published-5.1.7'].includes(core))) {
|
||||
test(`Multiple subtitles candidate / ${core}: timed VTT and SRT, offsets, fullscreen and source change`, async ({ page, browser }, testInfo) => {
|
||||
test(`Multiple subtitles candidate / ${core}: timed VTT and SRT, offsets, fullscreen and source change`, async ({ page, browser, browserName }, testInfo) => {
|
||||
await prepare(page, core, candidate, browser, testInfo)
|
||||
await page.evaluate(async () => {
|
||||
await window.art.plugins.add(window.multipleFactory({ subtitles: [{ name: 'a', url: '/test/combination.vtt' }, { name: 'b', url: '/test/combination.srt' }] }))
|
||||
@@ -54,14 +54,17 @@ for (const core of cores.filter(core => !['published-5.1.2', 'published-5.1.7'].
|
||||
window.art.subtitleOffset = 1
|
||||
})
|
||||
expect(await page.evaluate(() => Array.from(window.art.template.$track.track.cues, cue => [cue.startTime, cue.endTime]))).toEqual([[2, 4], [2, 4], [5, 7]])
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(0)
|
||||
const staleHistoricalOffset = core !== 'candidate' && browserName === 'firefox'
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(staleHistoricalOffset ? 1 : 0)
|
||||
await seek(page, 2.5)
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveText('First')
|
||||
await page.evaluate(() => {
|
||||
window.art.subtitleOffset = -1
|
||||
})
|
||||
expect(await page.evaluate(() => Array.from(window.art.template.$track.track.cues, cue => [cue.startTime, cue.endTime]))).toEqual([[0, 2], [0, 2], [3, 5]])
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(0)
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(staleHistoricalOffset ? 1 : 0)
|
||||
if (staleHistoricalOffset)
|
||||
await testInfo.attach('historical-paused-offset-defect', { contentType: 'application/json', body: JSON.stringify({ core, browser: browser.version(), acceptedAsCorrect: false, observation: 'Published core keeps a stale active caption after paused offsets; candidate must clear it. Dedicated native/offset probes retain exact state.' }) })
|
||||
await page.evaluate(() => {
|
||||
window.art.subtitleOffset = 0
|
||||
})
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
for (const invalidation of ['time-only', 'remove-add', 'mode-toggle']) {
|
||||
test(`Native paused cue timing probe: ${invalidation}`, async ({ page, browser }, testInfo) => {
|
||||
await page.route('**/test/native-offset.vtt', route => route.fulfill({ contentType: 'text/vtt', body: 'WEBVTT\n\n00:01.000 --> 00:03.000\ncaption\n' }))
|
||||
await page.goto('/test/player.html')
|
||||
await page.evaluate(async () => {
|
||||
const video = document.createElement('video')
|
||||
video.muted = true
|
||||
video.src = '/test/pattern.mp4'
|
||||
document.body.append(video)
|
||||
window.probeVideo = video
|
||||
await new Promise(resolve => video.addEventListener('loadedmetadata', resolve, { once: true }))
|
||||
const element = document.createElement('track')
|
||||
element.kind = 'metadata'
|
||||
element.src = '/test/native-offset.vtt'
|
||||
const loaded = new Promise(resolve => element.addEventListener('load', resolve, { once: true }))
|
||||
video.append(element)
|
||||
const track = element.track
|
||||
track.mode = 'hidden'
|
||||
await loaded
|
||||
window.probeTrack = track
|
||||
window.probeCue = track.cues[0]
|
||||
await video.play()
|
||||
video.pause()
|
||||
const sought = new Promise(resolve => video.addEventListener('seeked', resolve, { once: true }))
|
||||
video.currentTime = 1.5
|
||||
await sought
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.probeTrack.activeCues.length)).toBe(1)
|
||||
const state = await page.evaluate(async (invalidation) => {
|
||||
const track = window.probeTrack
|
||||
const cue = window.probeCue
|
||||
const read = () => ({ time: window.probeVideo.currentTime, paused: window.probeVideo.paused, active: Array.from(track.activeCues || [], cue => [cue.startTime, cue.endTime]), sameCue: track.cues[0] === cue })
|
||||
const before = read()
|
||||
cue.startTime = 2
|
||||
cue.endTime = 4
|
||||
const changed = read()
|
||||
if (invalidation === 'remove-add') {
|
||||
track.removeCue(cue)
|
||||
track.addCue(cue)
|
||||
}
|
||||
if (invalidation === 'mode-toggle') {
|
||||
track.mode = 'disabled'
|
||||
track.mode = 'hidden'
|
||||
}
|
||||
const immediate = read()
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
const painted = read()
|
||||
window.probeVideo.remove()
|
||||
return { before, changed, immediate, painted }
|
||||
}, invalidation)
|
||||
await testInfo.attach('native-offset-probe', { contentType: 'application/json', body: JSON.stringify({ invalidation, browser: browser.version(), state, scope: 'Native video/TextTrack only, no Artplayer instance; two paint callbacks observe invalidation without moving playback time' }) })
|
||||
expect(state.before.time).toBeGreaterThanOrEqual(1)
|
||||
expect(state.before.time).toBeLessThan(2)
|
||||
expect(state.before).toMatchObject({ paused: true, active: [[1, 3]], sameCue: true })
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
const vtt = 'WEBVTT\n\n00:01.000 --> 00:03.000\nFirst\n\n00:01.000 --> 00:03.000\nOverlap\n\n00:04.000 --> 00:06.000\nLater\n'
|
||||
|
||||
for (const core of ['candidate', 'published']) {
|
||||
test(`${core}: ${core === 'candidate' ? 'paused subtitle offsets refresh native membership without moving media' : 'historical paused subtitle offset behavior'}`, async ({ page, browserName }, testInfo) => {
|
||||
await page.route('**/test/offset.vtt', route => route.fulfill({ contentType: 'text/vtt', body: vtt }))
|
||||
await page.goto(`/test/player.html?core=${core}`)
|
||||
await page.evaluate(() => window.createPlayer('/test/pattern.mp4'))
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
await page.evaluate(() => window.art.subtitle.switch('/test/offset.vtt'))
|
||||
await expect.poll(() => page.evaluate(() => window.art.subtitle.cues.length)).toBe(3)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
|
||||
await page.locator('#pause').click()
|
||||
await page.evaluate(() => new Promise((resolve) => {
|
||||
window.art.once('video:seeked', resolve)
|
||||
window.art.seek = 1.5
|
||||
}))
|
||||
await expect(page.locator('.art-subtitle-line')).toHaveCount(2)
|
||||
const records = await page.evaluate(() => {
|
||||
const art = window.art
|
||||
const track = art.template.$track.track
|
||||
const cues = Array.from(track.cues)
|
||||
const events = []
|
||||
art.on('subtitleOffset', value => events.push(value))
|
||||
const states = []
|
||||
const time = art.currentTime
|
||||
for (const offset of [1, -1, 0, -3, 0]) {
|
||||
art.subtitleOffset = offset
|
||||
states.push({ offset, time: art.currentTime, paused: art.video.paused, native: Array.from(track.activeCues, cue => cue.text), public: art.subtitle.activeCues.map(cue => cue.text), rendered: Array.from(art.template.$subtitle.querySelectorAll('.art-subtitle-line'), line => line.textContent.trim()), sameCues: cues.every(cue => Array.from(track.cues).includes(cue)), mode: track.mode })
|
||||
}
|
||||
return { states, events, time }
|
||||
})
|
||||
await testInfo.attach('paused-offset', { contentType: 'application/json', body: JSON.stringify({ core, records }) })
|
||||
expect(records.events).toEqual([1, -1, 0, -3, 0])
|
||||
const texts = core === 'published' && browserName === 'firefox'
|
||||
? [['First', 'Overlap'], ['First', 'Overlap'], ['First', 'Overlap'], ['First', 'Overlap', 'Later'], ['First', 'Overlap', 'Later']]
|
||||
: [[], ['First', 'Overlap'], ['First', 'Overlap'], ['Later'], ['First', 'Overlap']]
|
||||
expect(records.time).toBeCloseTo(1.5, 2)
|
||||
for (const [index, state] of records.states.entries()) {
|
||||
expect(state).toEqual({ offset: [1, -1, 0, -3, 0][index], time: records.time, paused: true, native: texts[index], public: texts[index], rendered: texts[index], sameCues: true, mode: 'hidden' })
|
||||
}
|
||||
if (core === 'candidate') {
|
||||
const disabled = await page.evaluate(() => {
|
||||
const track = window.art.template.$track.track
|
||||
track.mode = 'disabled'
|
||||
window.art.subtitleOffset = 2
|
||||
return { mode: track.mode, active: window.art.subtitle.activeCues.length, offset: window.art.subtitleOffset }
|
||||
})
|
||||
expect(disabled).toEqual({ mode: 'disabled', active: 0, offset: 0 })
|
||||
await page.evaluate(() => {
|
||||
window.art.template.$track.track.mode = 'hidden'
|
||||
})
|
||||
await page.locator('#play').click()
|
||||
await page.evaluate(() => {
|
||||
window.art.subtitleOffset = 1
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(2.25)
|
||||
await expect(page.locator('.art-subtitle-line')).toHaveCount(2)
|
||||
await page.locator('#pause').click()
|
||||
}
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
})
|
||||
}
|
||||
+82
-1
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict'
|
||||
import { test as it } from 'node:test'
|
||||
import { loadModules } from './helpers/load.js'
|
||||
|
||||
const { ResourceScope, SubtitleRequest, parseSubtitle, subtitleOffsetMix, beginLifecycle, initSubtitleState, replaceSubtitleTrack } = await loadModules({
|
||||
const { ResourceScope, SubtitleRequest, parseSubtitle, subtitleOffsetMix, beginLifecycle, initSubtitleState, replaceSubtitleTrack, refreshPausedCues } = await loadModules({
|
||||
ResourceScope: 'packages/artplayer/src/lifecycle/scope',
|
||||
SubtitleRequest: 'packages/artplayer/src/subtitle/request',
|
||||
parseSubtitle: { file: 'packages/artplayer/src/subtitle/parse', name: 'parseSubtitle' },
|
||||
@@ -11,6 +11,7 @@ const { ResourceScope, SubtitleRequest, parseSubtitle, subtitleOffsetMix, beginL
|
||||
beginLifecycle: { file: 'packages/artplayer/src/lifecycle/instance', name: 'beginLifecycle' },
|
||||
initSubtitleState: { file: 'packages/artplayer/src/subtitle/state', name: 'initSubtitleState' },
|
||||
replaceSubtitleTrack: { file: 'packages/artplayer/src/subtitle/track', name: 'replaceSubtitleTrack' },
|
||||
refreshPausedCues: { file: 'packages/artplayer/src/subtitle/timing', name: 'refreshPausedCues' },
|
||||
})
|
||||
|
||||
it('subtitle track attribute failure releases its new scope and preserves the original error', () => {
|
||||
@@ -170,3 +171,83 @@ it('subtitle offsets with no cues retain the default state and do not notify', (
|
||||
assert.equal(art.subtitleOffset, 0)
|
||||
assert.equal(Object.hasOwn(art.notice, 'show'), false)
|
||||
})
|
||||
|
||||
it('paused offsets refresh stale native membership before rendering and notifying, preserving cue identity', () => {
|
||||
const cues = [{ startTime: 1, endTime: 3, text: 'First' }, { startTime: 4, endTime: 6, text: 'Later' }]
|
||||
const video = { paused: true, currentTime: 1.5 }
|
||||
const events = []
|
||||
let active = [cues[0]]
|
||||
const indexed = [...cues]
|
||||
const track = {
|
||||
mode: 'hidden',
|
||||
removeCue(cue) {
|
||||
events.push(['remove', cue.text])
|
||||
indexed.splice(indexed.indexOf(cue), 1)
|
||||
active = active.filter(item => item !== cue)
|
||||
},
|
||||
addCue(cue) {
|
||||
events.push(['add', cue.text])
|
||||
indexed.push(cue)
|
||||
active = indexed.filter(cue => cue.startTime <= video.currentTime && video.currentTime < cue.endTime)
|
||||
},
|
||||
get activeCues() { return active },
|
||||
}
|
||||
const art = { template: { $track: { track }, $video: video }, duration: 8, subtitle: { cues, update() {
|
||||
events.push(active.map(cue => cue.text))
|
||||
} }, notice: {}, i18n: { get: key => key }, emit: (...args) => events.push(args) }
|
||||
subtitleOffsetMix(art)
|
||||
art.subtitleOffset = 1
|
||||
const reindex = [['remove', 'First'], ['remove', 'Later'], ['add', 'First'], ['add', 'Later']]
|
||||
assert.deepEqual(events, [...reindex, [], ['subtitleOffset', 1]])
|
||||
events.length = 0
|
||||
art.subtitleOffset = -3
|
||||
assert.deepEqual(events, [...reindex, ['Later'], ['subtitleOffset', -3]])
|
||||
assert.equal(active[0], cues[1])
|
||||
assert.equal(video.currentTime, 1.5)
|
||||
assert.equal(video.paused, true)
|
||||
assert.equal(track.mode, 'hidden')
|
||||
assert.deepEqual(indexed, cues)
|
||||
})
|
||||
|
||||
it('paused refresh detects wrong members even when active counts match and preserves showing mode', () => {
|
||||
const expired = { startTime: 0, endTime: 1 }
|
||||
const current = { startTime: 1, endTime: 2 }
|
||||
const writes = []
|
||||
const track = { mode: 'showing', activeCues: [expired], removeCue: cue => writes.push(['remove', cue]), addCue: cue => writes.push(['add', cue]) }
|
||||
Object.defineProperty(track, 'mode', { get: () => 'showing', set: () => assert.fail('must retain mode') })
|
||||
refreshPausedCues(track, { paused: true, currentTime: 1 }, [expired, current])
|
||||
assert.deepEqual(writes, [['remove', expired], ['remove', current], ['add', expired], ['add', current]])
|
||||
})
|
||||
|
||||
it('paused refresh preserves correct overlap membership without toggling a native track', () => {
|
||||
const cues = [{ startTime: 0, endTime: 2 }, { startTime: 1, endTime: 3 }]
|
||||
const track = { mode: 'hidden', removeCue() {
|
||||
assert.fail('unnecessary native invalidation')
|
||||
}, activeCues: cues }
|
||||
refreshPausedCues(track, { paused: true, currentTime: 1.5 }, cues)
|
||||
})
|
||||
|
||||
it('paused refresh reindexes tied cues when native membership is correct but order drifted', () => {
|
||||
const first = { startTime: 0, endTime: 2 }
|
||||
const second = { startTime: 0, endTime: 2 }
|
||||
const added = []
|
||||
const track = { mode: 'hidden', activeCues: [second, first], removeCue() {}, addCue: cue => added.push(cue) }
|
||||
refreshPausedCues(track, { paused: true, currentTime: 1 }, [first, second])
|
||||
assert.deepEqual(added, [first, second])
|
||||
})
|
||||
|
||||
it('paused refresh does not enable disabled tracks or intervene in playing and limited media hosts', () => {
|
||||
for (const [mode, video] of [['disabled', { paused: true, currentTime: 1.5 }], ['hidden', { paused: false, currentTime: 1.5 }], ['hidden', undefined], ['hidden', { paused: true }], ['hidden', { paused: true, currentTime: Number.NaN }]]) {
|
||||
const track = {
|
||||
get mode() { return mode },
|
||||
set mode(_value) {
|
||||
assert.fail('unexpected track change')
|
||||
},
|
||||
get activeCues() {
|
||||
assert.fail('unexpected cue inspection')
|
||||
},
|
||||
}
|
||||
refreshPausedCues(track, video, [])
|
||||
}
|
||||
refreshPausedCues(undefined, { paused: true, currentTime: 1 }, [])
|
||||
})
|
||||
Reference in new issue
Block a user