mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
fix(core): [CORE-24] preserve source playback intent and explicit pauses
This commit is contained in:
1 parent
b5e96664ee
commit
0218196df9
22 files changed
+12895
-36
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
|
||||
@@ -2096,6 +2096,22 @@ function playAndPause(option) {
|
||||
const current = /* @__PURE__ */ new WeakMap();
|
||||
const assignments = /* @__PURE__ */ new WeakMap();
|
||||
const sources = /* @__PURE__ */ new WeakMap();
|
||||
function sourcePlaybackIntent(owner) {
|
||||
const operation = current.get(owner);
|
||||
return operation?.active() ? operation.resumePlaying : void 0;
|
||||
}
|
||||
function requestSourcePlayback(owner, playing) {
|
||||
const operation = current.get(owner);
|
||||
if (!operation?.active() || operation.resumePlaying === void 0)
|
||||
return;
|
||||
if (!playing && operation.internalPause) {
|
||||
operation.internalPause = false;
|
||||
} else {
|
||||
operation.resumePlaying = playing;
|
||||
if (!playing)
|
||||
operation.pauseRevision++;
|
||||
}
|
||||
}
|
||||
function getSourceScope(owner) {
|
||||
return sources.get(owner) || getScope(owner);
|
||||
}
|
||||
@@ -2104,6 +2120,12 @@ function captureSource(owner) {
|
||||
const source = sources.get(owner);
|
||||
return () => current.get(owner) === operation && (!operation || operation.active()) && sources.get(owner) === source && (!source || !source.closed);
|
||||
}
|
||||
function captureSourcePlayback(owner) {
|
||||
const active2 = captureSource(owner);
|
||||
const operation = current.get(owner);
|
||||
const revision = operation?.pauseRevision;
|
||||
return () => active2() && operation?.pauseRevision === revision;
|
||||
}
|
||||
function beginSource(owner) {
|
||||
const previous = sources.get(owner);
|
||||
const source = getScope(owner).child();
|
||||
@@ -2117,6 +2139,7 @@ function beginSource(owner) {
|
||||
scope,
|
||||
assigned: false,
|
||||
acceptingEvents: false,
|
||||
pauseRevision: 0,
|
||||
active: () => !isClosing(owner) && !scope.closed && current.get(owner) === operation
|
||||
};
|
||||
current.set(owner, operation);
|
||||
@@ -5713,6 +5736,7 @@ function pauseMix(art) {
|
||||
} = art;
|
||||
def(art, "pause", {
|
||||
value() {
|
||||
requestSourcePlayback(art, false);
|
||||
const result = $video.pause();
|
||||
notice.show = i18n.get("Pause");
|
||||
art.emit("pause");
|
||||
@@ -5960,7 +5984,8 @@ function playMix(art) {
|
||||
} = art;
|
||||
def(art, "play", {
|
||||
async value() {
|
||||
const active2 = captureSource(art);
|
||||
requestSourcePlayback(art, true);
|
||||
const active2 = captureSourcePlayback(art);
|
||||
const result = await $video.play();
|
||||
if (!active2())
|
||||
return result;
|
||||
@@ -6203,13 +6228,16 @@ function switchSource(art, url, currentTime) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const playing = sourcePlaybackIntent(art) ?? art.playing;
|
||||
const operation = beginSource(art);
|
||||
operation.resumePlaying = playing;
|
||||
const scope = operation.scope.child();
|
||||
let settled = false;
|
||||
const settle = (failed = false, error2) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
operation.resumePlaying = void 0;
|
||||
operation.onError = void 0;
|
||||
operation.onAssigned = void 0;
|
||||
scope.dispose();
|
||||
@@ -6227,8 +6255,13 @@ function switchSource(art, url, currentTime) {
|
||||
const fail = (error2) => settle(true, error2);
|
||||
operation.onError = fail;
|
||||
try {
|
||||
const { playing, aspectRatio: aspectRatio2, playbackRate: playbackRate2 } = art;
|
||||
art.pause();
|
||||
const { aspectRatio: aspectRatio2, playbackRate: playbackRate2 } = art;
|
||||
operation.internalPause = true;
|
||||
try {
|
||||
art.pause();
|
||||
} finally {
|
||||
operation.internalPause = false;
|
||||
}
|
||||
if (!active2())
|
||||
return;
|
||||
const readiness = scope.child();
|
||||
@@ -6237,10 +6270,11 @@ function switchSource(art, url, currentTime) {
|
||||
let rateRestored = false;
|
||||
const position = positionRestoration(art, currentTime, active2);
|
||||
const resume = async () => {
|
||||
const pauseRevision = operation.pauseRevision;
|
||||
art.aspectRatio = aspectRatio2;
|
||||
if (!active2())
|
||||
return;
|
||||
if (playing) {
|
||||
if (operation.resumePlaying) {
|
||||
try {
|
||||
await art.play();
|
||||
} catch {
|
||||
@@ -6248,7 +6282,8 @@ function switchSource(art, url, currentTime) {
|
||||
}
|
||||
if (!active2())
|
||||
return;
|
||||
art.notice.show = "";
|
||||
if (operation.pauseRevision === pauseRevision)
|
||||
art.notice.show = "";
|
||||
settle();
|
||||
};
|
||||
const resumeWhenReady = () => {
|
||||
|
||||
@@ -260,6 +260,17 @@ twice. media/position-revision.ts also tracks legal public currentTime writes;
|
||||
a consumer assigning currentTime directly during loading supersedes the automatic
|
||||
restoration, including writes before metadata. Invalid NaN assignments remain no-ops.
|
||||
Source cancellation still settles pending work and removes all listeners.
|
||||
CORE-24 keeps the desired playback state on the pending source operation. A new switch inherits
|
||||
that intent before disposing its predecessor; it must not infer the user's intent from the
|
||||
temporary native pause performed by the previous switch. playMix/pauseMix update intent at
|
||||
command entry, before native calls or public callbacks can reenter. The switch's internal pause
|
||||
consumes one marker; subsequent explicit pauses remain effective, including inside callbacks.
|
||||
The pause revision invalidates late play notifications and prevents an old resume from clearing
|
||||
a newer pause notice. It does not change the public play promise's original value or rejection.
|
||||
Completed, failed or cancelled operations cannot lend intent to a later independent source.
|
||||
Do not replace these command hooks with late event listeners: an earlier user pause listener
|
||||
may synchronously start another switch. See test/source.test.js, test/browser/source-intent.spec.js
|
||||
and refactor/changes/2026-09-12-CORE-24-source-intent.md for the regressions and validation scope.
|
||||
Use test/source.test.js, test/progress.test.js and the browser source and
|
||||
progress-quality specs. Keep native media events in evidence when changing this
|
||||
sequence; the original rate-interruption hypothesis was not sufficient to explain
|
||||
|
||||
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
|
||||
+40
-5
@@ -2096,6 +2096,22 @@ function playAndPause(option) {
|
||||
const current = /* @__PURE__ */ new WeakMap();
|
||||
const assignments = /* @__PURE__ */ new WeakMap();
|
||||
const sources = /* @__PURE__ */ new WeakMap();
|
||||
function sourcePlaybackIntent(owner) {
|
||||
const operation = current.get(owner);
|
||||
return operation?.active() ? operation.resumePlaying : void 0;
|
||||
}
|
||||
function requestSourcePlayback(owner, playing) {
|
||||
const operation = current.get(owner);
|
||||
if (!operation?.active() || operation.resumePlaying === void 0)
|
||||
return;
|
||||
if (!playing && operation.internalPause) {
|
||||
operation.internalPause = false;
|
||||
} else {
|
||||
operation.resumePlaying = playing;
|
||||
if (!playing)
|
||||
operation.pauseRevision++;
|
||||
}
|
||||
}
|
||||
function getSourceScope(owner) {
|
||||
return sources.get(owner) || getScope(owner);
|
||||
}
|
||||
@@ -2104,6 +2120,12 @@ function captureSource(owner) {
|
||||
const source = sources.get(owner);
|
||||
return () => current.get(owner) === operation && (!operation || operation.active()) && sources.get(owner) === source && (!source || !source.closed);
|
||||
}
|
||||
function captureSourcePlayback(owner) {
|
||||
const active2 = captureSource(owner);
|
||||
const operation = current.get(owner);
|
||||
const revision = operation?.pauseRevision;
|
||||
return () => active2() && operation?.pauseRevision === revision;
|
||||
}
|
||||
function beginSource(owner) {
|
||||
const previous = sources.get(owner);
|
||||
const source = getScope(owner).child();
|
||||
@@ -2117,6 +2139,7 @@ function beginSource(owner) {
|
||||
scope,
|
||||
assigned: false,
|
||||
acceptingEvents: false,
|
||||
pauseRevision: 0,
|
||||
active: () => !isClosing(owner) && !scope.closed && current.get(owner) === operation
|
||||
};
|
||||
current.set(owner, operation);
|
||||
@@ -5713,6 +5736,7 @@ function pauseMix(art) {
|
||||
} = art;
|
||||
def(art, "pause", {
|
||||
value() {
|
||||
requestSourcePlayback(art, false);
|
||||
const result = $video.pause();
|
||||
notice.show = i18n.get("Pause");
|
||||
art.emit("pause");
|
||||
@@ -5960,7 +5984,8 @@ function playMix(art) {
|
||||
} = art;
|
||||
def(art, "play", {
|
||||
async value() {
|
||||
const active2 = captureSource(art);
|
||||
requestSourcePlayback(art, true);
|
||||
const active2 = captureSourcePlayback(art);
|
||||
const result = await $video.play();
|
||||
if (!active2())
|
||||
return result;
|
||||
@@ -6203,13 +6228,16 @@ function switchSource(art, url, currentTime) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const playing = sourcePlaybackIntent(art) ?? art.playing;
|
||||
const operation = beginSource(art);
|
||||
operation.resumePlaying = playing;
|
||||
const scope = operation.scope.child();
|
||||
let settled = false;
|
||||
const settle = (failed = false, error2) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
operation.resumePlaying = void 0;
|
||||
operation.onError = void 0;
|
||||
operation.onAssigned = void 0;
|
||||
scope.dispose();
|
||||
@@ -6227,8 +6255,13 @@ function switchSource(art, url, currentTime) {
|
||||
const fail = (error2) => settle(true, error2);
|
||||
operation.onError = fail;
|
||||
try {
|
||||
const { playing, aspectRatio: aspectRatio2, playbackRate: playbackRate2 } = art;
|
||||
art.pause();
|
||||
const { aspectRatio: aspectRatio2, playbackRate: playbackRate2 } = art;
|
||||
operation.internalPause = true;
|
||||
try {
|
||||
art.pause();
|
||||
} finally {
|
||||
operation.internalPause = false;
|
||||
}
|
||||
if (!active2())
|
||||
return;
|
||||
const readiness = scope.child();
|
||||
@@ -6237,10 +6270,11 @@ function switchSource(art, url, currentTime) {
|
||||
let rateRestored = false;
|
||||
const position = positionRestoration(art, currentTime, active2);
|
||||
const resume = async () => {
|
||||
const pauseRevision = operation.pauseRevision;
|
||||
art.aspectRatio = aspectRatio2;
|
||||
if (!active2())
|
||||
return;
|
||||
if (playing) {
|
||||
if (operation.resumePlaying) {
|
||||
try {
|
||||
await art.play();
|
||||
} catch {
|
||||
@@ -6248,7 +6282,8 @@ function switchSource(art, url, currentTime) {
|
||||
}
|
||||
if (!active2())
|
||||
return;
|
||||
art.notice.show = "";
|
||||
if (operation.pauseRevision === pauseRevision)
|
||||
art.notice.show = "";
|
||||
settle();
|
||||
};
|
||||
const resumeWhenReady = () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PauseHost } from '../media/hosts'
|
||||
import type { PlaybackMethods } from '../media/types'
|
||||
import { requestSourcePlayback } from '../source/operation'
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function pauseMix<Media extends Pick<PlaybackMethods, 'pause'>>(art: PauseHost<Media>): asserts art is PauseHost<Media> & { pause: () => ReturnType<Media['pause']> } {
|
||||
@@ -11,6 +12,7 @@ export default function pauseMix<Media extends Pick<PlaybackMethods, 'pause'>>(a
|
||||
|
||||
def(art, 'pause', {
|
||||
value() {
|
||||
requestSourcePlayback(art, false)
|
||||
const result = $video.pause()
|
||||
notice.show = i18n.get('Pause')
|
||||
art.emit('pause')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PlayHost } from '../media/hosts'
|
||||
import type { PlaybackMethods } from '../media/types'
|
||||
import { captureSource } from '../source/operation'
|
||||
import { captureSourcePlayback, requestSourcePlayback } from '../source/operation'
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function playMix<Media extends Pick<PlaybackMethods, 'play'>>(art: PlayHost<Media>): asserts art is PlayHost<Media> & { play: () => Promise<Awaited<ReturnType<Media['play']>>> } {
|
||||
@@ -14,7 +14,8 @@ export default function playMix<Media extends Pick<PlaybackMethods, 'play'>>(art
|
||||
|
||||
def(art, 'play', {
|
||||
async value() {
|
||||
const active = captureSource(art)
|
||||
requestSourcePlayback(art, true)
|
||||
const active = captureSourcePlayback(art)
|
||||
const result = await $video.play()
|
||||
if (!active())
|
||||
return result
|
||||
|
||||
@@ -6,6 +6,9 @@ export interface SourceOperation {
|
||||
scope: ResourceScope
|
||||
assigned: boolean
|
||||
acceptingEvents: boolean
|
||||
pauseRevision: number
|
||||
resumePlaying?: boolean
|
||||
internalPause?: boolean
|
||||
onAssigned?: () => void
|
||||
onError?: (error: unknown) => void
|
||||
active: () => boolean
|
||||
@@ -15,6 +18,25 @@ const current = new WeakMap<object, SourceOperation>()
|
||||
const assignments = new WeakMap<object, SourceOperation>()
|
||||
const sources = new WeakMap<object, ResourceScope>()
|
||||
|
||||
export function sourcePlaybackIntent(owner: object): boolean | undefined {
|
||||
const operation = current.get(owner)
|
||||
return operation?.active() ? operation.resumePlaying : undefined
|
||||
}
|
||||
|
||||
export function requestSourcePlayback(owner: object, playing: boolean): void {
|
||||
const operation = current.get(owner)
|
||||
if (!operation?.active() || operation.resumePlaying === undefined)
|
||||
return
|
||||
if (!playing && operation.internalPause) {
|
||||
operation.internalPause = false
|
||||
}
|
||||
else {
|
||||
operation.resumePlaying = playing
|
||||
if (!playing)
|
||||
operation.pauseRevision++
|
||||
}
|
||||
}
|
||||
|
||||
export function getSourceScope(owner: object): ResourceScope {
|
||||
return sources.get(owner) || getScope(owner)
|
||||
}
|
||||
@@ -26,6 +48,13 @@ export function captureSource(owner: object): () => boolean {
|
||||
&& sources.get(owner) === source && (!source || !source.closed)
|
||||
}
|
||||
|
||||
export function captureSourcePlayback(owner: object): () => boolean {
|
||||
const active = captureSource(owner)
|
||||
const operation = current.get(owner)
|
||||
const revision = operation?.pauseRevision
|
||||
return () => active() && operation?.pauseRevision === revision
|
||||
}
|
||||
|
||||
export function beginSource(owner: object): SourceOperation {
|
||||
const previous = sources.get(owner)
|
||||
const source = getScope(owner).child()
|
||||
@@ -39,6 +68,7 @@ export function beginSource(owner: object): SourceOperation {
|
||||
scope,
|
||||
assigned: false,
|
||||
acceptingEvents: false,
|
||||
pauseRevision: 0,
|
||||
active: () => !isClosing(owner) && !scope.closed && current.get(owner) === operation,
|
||||
}
|
||||
current.set(owner, operation)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SourceEvent, SourceListener, SwitchHost } from './types'
|
||||
import type { SourceListener, SwitchHost } from './types'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { listenSource } from './listen'
|
||||
import { assignUrl, beginSource } from './operation'
|
||||
import { assignUrl, beginSource, sourcePlaybackIntent } from './operation'
|
||||
import { positionRestoration } from './restore-position'
|
||||
|
||||
export function switchSource(art: SwitchHost, url: string, currentTime: number): Promise<void> {
|
||||
@@ -10,13 +10,16 @@ export function switchSource(art: SwitchHost, url: string, currentTime: number):
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
const playing = sourcePlaybackIntent(art) ?? art.playing
|
||||
const operation = beginSource(art)
|
||||
operation.resumePlaying = playing
|
||||
const scope = operation.scope.child()
|
||||
let settled = false
|
||||
const settle = (failed = false, error?: unknown) => {
|
||||
if (settled)
|
||||
return
|
||||
settled = true
|
||||
operation.resumePlaying = undefined
|
||||
operation.onError = undefined
|
||||
operation.onAssigned = undefined
|
||||
scope.dispose()
|
||||
@@ -34,8 +37,14 @@ export function switchSource(art: SwitchHost, url: string, currentTime: number):
|
||||
const fail = (error: unknown) => settle(true, error)
|
||||
operation.onError = fail
|
||||
try {
|
||||
const { playing, aspectRatio, playbackRate } = art
|
||||
art.pause()
|
||||
const { aspectRatio, playbackRate } = art
|
||||
operation.internalPause = true
|
||||
try {
|
||||
art.pause()
|
||||
}
|
||||
finally {
|
||||
operation.internalPause = false
|
||||
}
|
||||
if (!active())
|
||||
return
|
||||
|
||||
@@ -45,10 +54,11 @@ export function switchSource(art: SwitchHost, url: string, currentTime: number):
|
||||
let rateRestored = false
|
||||
const position = positionRestoration(art, currentTime, active)
|
||||
const resume = async () => {
|
||||
const pauseRevision = operation.pauseRevision
|
||||
art.aspectRatio = aspectRatio
|
||||
if (!active())
|
||||
return
|
||||
if (playing) {
|
||||
if (operation.resumePlaying) {
|
||||
// The public play promise still rejects; this internal resume is best effort.
|
||||
try {
|
||||
await art.play()
|
||||
@@ -57,7 +67,8 @@ export function switchSource(art: SwitchHost, url: string, currentTime: number):
|
||||
}
|
||||
if (!active())
|
||||
return
|
||||
art.notice.show = ''
|
||||
if (operation.pauseRevision === pauseRevision)
|
||||
art.notice.show = ''
|
||||
settle()
|
||||
}
|
||||
const resumeWhenReady = () => {
|
||||
@@ -103,8 +114,8 @@ export function switchSource(art: SwitchHost, url: string, currentTime: number):
|
||||
'seek': position.manual,
|
||||
}
|
||||
const capture = scope.child()
|
||||
const queued: [SourceEvent, unknown][] = []
|
||||
const names = Object.keys(handlers) as SourceEvent[]
|
||||
const queued: [keyof typeof handlers, unknown][] = []
|
||||
const names = Object.keys(handlers) as (keyof typeof handlers)[]
|
||||
for (const name of names) {
|
||||
listenSource(capture, art, name, (event) => {
|
||||
if (operation.acceptingEvents)
|
||||
@@ -123,7 +134,7 @@ export function switchSource(art: SwitchHost, url: string, currentTime: number):
|
||||
settle()
|
||||
return
|
||||
}
|
||||
const dispatch = {} as Record<SourceEvent, SourceListener>
|
||||
const dispatch = {} as Record<keyof typeof handlers, SourceListener>
|
||||
for (const name of names)
|
||||
dispatch[name] = listenSource(readiness, art, name, handlers[name], name !== 'video:seeked')
|
||||
for (const [name, event] of queued) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,63 @@
|
||||
# CORE-24:连续切源的播放意图
|
||||
|
||||
状态:完成验收,独立本地提交;起点 b5e96664。Audio-05 的首轮三引擎证据确认六项候选核心回归。
|
||||
|
||||
原切源每次读取 art.playing 并立即 art.pause()。第二次同步切源读取到第一次内部
|
||||
暂停后的 false;旧操作被正确取消后,原先负责恢复播放的旧监听器不再执行,新
|
||||
操作也没有恢复意图。修复必须传递最新切源的播放意图,同时保留过期操作无副作用。
|
||||
|
||||
将意图归属当前 source operation,在仍待恢复的操作间转移;用户明确 pause 可以
|
||||
取消恢复。内部 pause 仍走既有公开事件,不能把它误当作用户放弃播放。补真实暂停
|
||||
状态的受控宿主、重入/取消/失败/销毁和真实浏览器测试。完成后独立提交并恢复 Audio-05。
|
||||
|
||||
## 实现与兼容
|
||||
|
||||
source/operation.ts 保存待恢复意图、内部暂停标记及暂停修订号;switch.ts 在取消旧
|
||||
操作前取得意图,结算时清除,避免完整结束/失败操作的状态泄漏到之后的来源。
|
||||
playMix/pauseMix 在原生命令之前更新意图。内部暂停标记由进入 pauseMix 的第一次调用
|
||||
消费,嵌套用户暂停仍然生效,不改变现有 pause 事件、同步返回或 play Promise 值/拒绝。
|
||||
play 的副作用还核对暂停修订号:晚到的 native play 不能在用户暂停之后更新提示、
|
||||
发出 play 或执行互斥,switch 的旧恢复也不能清空新暂停提示。
|
||||
|
||||
最初尝试监听 play/pause 事件,已有用例虽通过,但无法保证比用户预注册的回调先执行;
|
||||
审查发现用户 pause 回调同步切源时可能先继承旧意图。因此采用命令入口方案(ADR-024),
|
||||
没有增加额外监听器、定时器、依赖或公开字段。新用例使用真实 pauseMix/playMix,
|
||||
不再让受控宿主的 pause 只计数却不改变播放状态。
|
||||
|
||||
一项既有晚到恢复测试原来只写 art.playing=false,未表达暂停命令;它现在同时调用
|
||||
真实 art.pause,保留其旧操作不能覆盖新提示的断言。早期事件方案因此悬挂的运行已
|
||||
主动终止并保留日志,没有当作通过。新增“恢复未完成时用户暂停”也先复现了错误 play
|
||||
事件,再以修订号修复,失败日志均保留。
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 源切换 34 项单元通过;新增 8 项覆盖连续操作、显式暂停、嵌套暂停、用户监听器重入、
|
||||
未兑现的公开 play 意图、晚到恢复,以及直接 URL/失败/完成/销毁边界。
|
||||
- 完整 `yarn ci:check` 594 项通过(548 单元、11 工程、35 基线),严格 TS 与声明检查通过。
|
||||
后续新增一个浏览器命令顺序用例,最终单独 lint 和下面两套矩阵验证。
|
||||
- 正常 `yarn build artplayer`、`yarn build:i18n` 通过;后者恢复核心构建清理的语言产物,
|
||||
i18n 与上次提交无差异。三种核心 JS 与 docs/compiled 一致,公开声明/版本未改变。
|
||||
- 正式 main/legacy 产物的 source-intent/source/media-events/playback-properties
|
||||
三引擎最终各 81 项通过,无重试或跳过。早期事件方案的 39 项及新增用例前的 78 项
|
||||
保留为过程记录,不充当最终实现证据。
|
||||
- 原先失败的六项“候选核心 + 旧/新 Audio”连续切源用例全部通过,测试断言未改。
|
||||
该组合重跑使用核心源码构建与明确指定的 Audio main 产物;正式核心产物另由上述矩阵验证。
|
||||
- `yarn test:package` 在仓库外实际安装 tarball 并冻结重装:34 个运行时检查,5 组兼容类型
|
||||
与 8 组精确类型通过。打包核心 JS 与浏览器所测的正式文件逐字节比对。
|
||||
- `yarn test:coverage` 通过,224 个运行时文件进入报告、26 个明确排除,0 项门槛违例。
|
||||
总体 Node 行覆盖 61.04%、分支 86.70%、函数 66.75%;不能将浏览器数量当作覆盖率。
|
||||
变动文件的独立计数及产物体积差异见 [冻结证据](../baselines/core-source-intent-validation.json)。
|
||||
|
||||
main gzip 从 58,555 增至 58,748 字节(+193),legacy +181、ESM +248;这是兼容行为
|
||||
修复的实际体积成本,不声称有性能/体积优化收益。source/operation.ts 的行/分支/函数
|
||||
覆盖均 100%,source/switch.ts 行 98.08%、分支 94.20%,其他浏览器路径仍有 Node 覆盖缺口。
|
||||
|
||||
## 后续与回退
|
||||
|
||||
CORE-SOURCE-01 按上述公开命令与连续切源范围关闭;AUDIO-RESUME-01(旧核心 + 新音频
|
||||
在 WebKit 上恢复)、原生偏移边界对照和真实网络缓冲仍由 Audio-05 接续,未一并关闭。
|
||||
核心完整安装包全量矩阵、性能/体积风险和物理设备/发布复盘门槛仍需后续最终候选验证;
|
||||
本任务的 81 项矩阵不等于重复了 CORE-22 的所有场景,也不代表 npm 发布放行。
|
||||
|
||||
回退此独立提交会恢复上次核心运行实现及三种构建文件,同时重新打开 CORE-SOURCE-01;
|
||||
不应只回退命令入口或操作状态的一半。无 push/tag/publish/merge。
|
||||
@@ -5,8 +5,9 @@ CORE-22 汇总核心自有源码、公开声明、生成产物和当前固定消
|
||||
[交付记录](changes/2026-09-11-CORE-22-core-acceptance.md);任务状态以 tasks.json 为准。
|
||||
|
||||
后续 Audio-05 的新组合测试复现了候选核心连续切源丢失播放意图,见
|
||||
[首轮证据](baselines/audio-combinations-first.json)。CORE-SOURCE-01/CORE-24 作为新发现
|
||||
继续修复,当前不能据本文件历史阶段结果放行;原有通过记录保留其当时的测试范围。
|
||||
[首轮证据](baselines/audio-combinations-first.json)。CORE-24 已修复并通过针对性正式
|
||||
产物/隔离安装验收,见 [修复记录](changes/2026-09-12-CORE-24-source-intent.md)。
|
||||
原有通过记录保留其当时的测试范围,不能据历史阶段结果替代最终候选的全量发布验收。
|
||||
|
||||
## 契约与回归入口
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
| ADR-021 | 采用方向 | 用户指定 Yarn,固定 Classic 1.22.22 和唯一 yarn.lock | 与原 v1 锁格式一致;保留 Node 24.21.0 和既有依赖版本,ENG-PM-01 验证;替代 ENG-01 的 npm 默认工具选择 |
|
||||
| ADR-022 | 采用方向 | Chrome 不可用时使用 Codex 内置浏览器 | 用户明确授权;记录实际环境和能力范围,必要设备测试仍独立验收 |
|
||||
| ADR-023 | 已验证于 Audio Track | 参数类型纠正会破坏旧推断时,默认入口保留兼容类型,/runtime 复用相同运行文件提供精确类型 | PKG-AUDIO-04 的 Parameters 与无注解 update 实现证明 Partial/重载仍可能破坏旧用户;沿用核心的类型分层方向,具体包需独立编译与入口身份验证,不增加第二套实现或用 any 绕过 |
|
||||
| ADR-024 | 已验证于 CORE-24 | 切源播放意图归属当前操作,在公开 play/pause 命令入口更新,内部暂停不覆盖用户意图 | Audio-05 三引擎复现连续切源最终停在 0;新操作继承未完成操作的意图,显式暂停及其修订号终止旧恢复副作用,过期 Promise 仍保留原结算语义;不依赖用户回调之后才执行的事件监听器 |
|
||||
|
||||
## 开始实现前需补齐的决定
|
||||
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 217 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 147 / doing 1 / blocked 0 / done 69 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 146 / doing 1 / blocked 0 / done 70 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
| CORE-21 | artplayer<br>生成核心声明并校验包内容 | CORE-20, ENG-07 | 由 TS 生成的既有入口声明与三种发布产物 | 内部类型不泄漏,旧 JS/TS 消费者与 i18n 通过;逐项完成 CORE-07 留下的 BASE-TYPE-04/05/07 声明方案,旧合法消费者与实际返回分别留证;不能以虚假的交叉返回类型或改变运行时来掩盖冲突,未决项阻止核心声明出口;处理 CORE-10 / BASE-TYPE-08 的 setter-only 属性虚构 getter 声明,并复核 PlaybackControls 精确视图与旧 toggle 声明共存策略 | H | done |
|
||||
| CORE-23 | artplayer<br>检查并补齐键盘、焦点与可访问名称 | CORE-13, CORE-14, CORE-17, ENG-05, BASE-04 | 主要控件/设置/模式退出的键盘与焦点回归、名称和字幕可用性检查及必要兼容修正 | 保持旧快捷键和 DOM/CSS 钩子;真实浏览器验证,不以静态属性检查代替交互;处理 BASE-DOM-01 的主要控件 Tab 不可达,保留既有名称与快捷键 | H | done |
|
||||
| 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 | todo |
|
||||
| CORE-24 | artplayer<br>修复连续切源的播放意图继承 | CORE-22, PKG-AUDIO-04 | 连续切源保留原播放意图,同时尊重显式暂停与最新来源,回归旧/新 audio | 真实暂停状态的受控测试、重入/取消/失败/销毁、三引擎和 main/legacy 产物验证通过;不恢复过期来源的公开副作用 | H | done |
|
||||
|
||||
## 5 包迁移:artplayer-plugin-chapter
|
||||
|
||||
@@ -463,7 +463,7 @@
|
||||
- CORE-21: [记录](changes/2026-09-11-CORE-21-public-declarations.md) [记录](baselines/core-declarations-validation.json) [记录](core-public-types.md) [记录](typechecking.md)
|
||||
- CORE-23: [记录](changes/2026-09-11-CORE-23-keyboard-focus.md) [记录](baselines/keyboard-validation.json)
|
||||
- CORE-22: [记录](changes/2026-09-11-CORE-22-core-acceptance.md) [记录](baselines/core-setting-navigation-partial.json) [记录](core-acceptance.md) [记录](baselines/core-acceptance-validation.json)
|
||||
- CORE-24: [记录](baselines/audio-combinations-first.json)
|
||||
- CORE-24: [记录](baselines/audio-combinations-first.json) [记录](changes/2026-09-12-CORE-24-source-intent.md) [记录](baselines/core-source-intent-validation.json)
|
||||
- PKG-CHAPTER-01: [记录](changes/2026-09-10-PKG-CHAPTER-01-contract.md) [记录](baselines/chapter-contract.md)
|
||||
- PKG-CHAPTER-02: [记录](changes/2026-09-10-PKG-CHAPTER-02-tests.md) [记录](baselines/chapter-validation.json)
|
||||
- PKG-CHAPTER-03: [记录](changes/2026-09-10-PKG-CHAPTER-03-typescript-modules.md) [记录](baselines/chapter-migration-validation.json)
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# 进度与证据
|
||||
|
||||
## 最新完成:CORE-24 连续切源播放意图修复
|
||||
|
||||
Audio-05 检查点已提交 b5e96664,原六项候选核心回归保持原断言重跑全部通过。
|
||||
切源现在继承尚未完成操作的播放意图,公开 play/pause 在用户回调前更新它;内部
|
||||
暂停不覆盖意图,显式暂停和暂停修订号阻止晚到恢复的事件/提示副作用。
|
||||
|
||||
源切换单元 34 项、完整 CI 594 项通过;正式 main/legacy 三引擎各 81 项通过;
|
||||
隔离 tarball 的 34 项运行时、5 组兼容类型、8 组精确类型通过,运行字节与正式文件一致。
|
||||
覆盖率 0 门槛违例,Node 总行覆盖 61.04%,不声称全部源码均已覆盖。
|
||||
CORE-SOURCE-01 关闭,AUDIO-RESUME-01、偏移原生对照、真实缓冲及 HLS 风险仍开放。
|
||||
|
||||
当前 217 项、70 done、1 doing、146 todo。本任务独立本地提交核实后恢复 Audio-05。
|
||||
详见 [修复记录](changes/2026-09-12-CORE-24-source-intent.md) 和
|
||||
[冻结证据](baselines/core-source-intent-validation.json)。未推送或发布,全局目标保持不变。
|
||||
|
||||
## 当前实施:Audio-05 发现连续切源回归,拆分 CORE-24
|
||||
|
||||
Audio-04 已独立提交 13c898c1。新组合测试覆盖旧/新核心、旧/新 audio 的连续切源、
|
||||
|
||||
@@ -130,5 +130,5 @@
|
||||
| AUDIO-SYNC-01 | open / 已复现 | Audio Track 未同步原生暂停/结束,偏移边界与切源/缓冲仍待完整验证 | PKG-AUDIO-02, PKG-AUDIO-03, PKG-AUDIO-05 |
|
||||
| AUDIO-TYPE-01 | accepted-with-scope / 源码/产物事实 | Audio Track update 部分配置与公开/编辑器声明不一致 | PKG-AUDIO-04 |
|
||||
| AUDIO-DEMO-01 | open / 源码/产物事实 | Audio Track README 示例链接名称不匹配且缺维护说明 | PKG-AUDIO-06 |
|
||||
| CORE-SOURCE-01 | open / 已复现 | 候选核心连续切源丢失原播放意图 | CORE-24 |
|
||||
| CORE-SOURCE-01 | resolved / 已复现 | 候选核心连续切源丢失原播放意图 | CORE-24 |
|
||||
| AUDIO-RESUME-01 | open / 已复现 | WebKit 旧核心切源后候选音频保持暂停 | PKG-AUDIO-05 |
|
||||
+9
-2
@@ -2731,7 +2731,7 @@
|
||||
"id": "CORE-SOURCE-01",
|
||||
"title": "候选核心连续切源丢失原播放意图",
|
||||
"confirmation": "reproduced",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"owners": [
|
||||
"CORE-24"
|
||||
],
|
||||
@@ -2740,7 +2740,14 @@
|
||||
"packages/artplayer/src/source/switch.ts"
|
||||
],
|
||||
"compatibleResolution": "继承被替代切源操作的播放意图;显式暂停可以取消恢复,过期操作继续只结算不发布副作用。",
|
||||
"closureCriteria": "新旧对照的连续切源、手动暂停、重入/取消/错误/销毁和正式产物三引擎验证通过。"
|
||||
"closureCriteria": "新旧对照的连续切源、手动暂停、重入/取消/错误/销毁和正式产物三引擎验证通过。",
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-12-CORE-24-source-intent.md",
|
||||
"refactor/baselines/core-source-intent-validation.json",
|
||||
"test/source.test.js",
|
||||
"test/browser/source-intent.spec.js"
|
||||
],
|
||||
"resolutionRationale": "待完成切源操作继承播放意图,公开 play/pause 在原生调用及用户监听器之前更新;内部暂停标记仅消费一次,暂停修订号阻止晚到恢复副作用。34 项源测试、594 项 CI、正式 main/legacy 各 81 项三引擎和原六项 Audio 失败重跑通过,隔离 tarball 字节/运行时/类型验证通过。范围是公开命令及切源操作,不关闭独立 AUDIO-RESUME-01、真机或完整发布门槛。"
|
||||
},
|
||||
{
|
||||
"id": "AUDIO-RESUME-01",
|
||||
|
||||
+4
-2
@@ -1249,12 +1249,14 @@
|
||||
"CORE-22",
|
||||
"PKG-AUDIO-04"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "H",
|
||||
"deliverable": "连续切源保留原播放意图,同时尊重显式暂停与最新来源,回归旧/新 audio",
|
||||
"acceptance": "真实暂停状态的受控测试、重入/取消/失败/销毁、三引擎和 main/legacy 产物验证通过;不恢复过期来源的公开副作用",
|
||||
"evidence": [
|
||||
"baselines/audio-combinations-first.json"
|
||||
"baselines/audio-combinations-first.json",
|
||||
"changes/2026-09-12-CORE-24-source-intent.md",
|
||||
"baselines/core-source-intent-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
async function start(page, core) {
|
||||
await page.goto(`/test/player.html?core=${core}`)
|
||||
await page.evaluate(() => window.createPlayer('/test/pattern.mp4?intent=initial'))
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
}
|
||||
|
||||
for (const core of ['published', 'candidate']) {
|
||||
test(`${core}: rapid source switches preserve playing on the latest real video without plugins`, async ({ page }, testInfo) => {
|
||||
await start(page, core)
|
||||
await page.evaluate(async () => {
|
||||
window.restarted = []
|
||||
window.art.on('restart', url => window.restarted.push(url))
|
||||
await Promise.all(['one', 'two', 'last'].map(name => window.art.switchUrl(`/test/pattern.mp4?intent=${name}`)))
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
expect(await page.evaluate(() => window.art.video.paused)).toBe(false)
|
||||
expect(await page.evaluate(() => window.art.video.currentSrc)).toContain('intent=last')
|
||||
expect(await page.evaluate(() => window.restarted)).toEqual(core === 'candidate' ? ['/test/pattern.mp4?intent=last'] : ['one', 'two', 'last'].map(name => `/test/pattern.mp4?intent=${name}`))
|
||||
await testInfo.attach('source-intent', { contentType: 'application/json', body: JSON.stringify(await page.evaluate(() => ({ time: window.art.currentTime, paused: window.art.video.paused, restarts: window.restarted }))) })
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
})
|
||||
}
|
||||
|
||||
for (const command of ['pause-before-next', 'pause-after-next', 'reentrant-pause', 'pause-listener-switch']) {
|
||||
test(`candidate: ${command} cancels automatic resume until the next user play`, async ({ page }, testInfo) => {
|
||||
await start(page, 'candidate')
|
||||
await page.evaluate(async (command) => {
|
||||
const { art } = window
|
||||
if (command === 'reentrant-pause')
|
||||
art.once('pause', () => art.pause())
|
||||
const first = art.switchUrl('/test/pattern.mp4?intent=first')
|
||||
if (command === 'pause-before-next')
|
||||
art.pause()
|
||||
let last
|
||||
if (command === 'pause-listener-switch') {
|
||||
art.once('pause', () => {
|
||||
last = art.switchUrl('/test/pattern.mp4?intent=last')
|
||||
})
|
||||
art.pause()
|
||||
}
|
||||
else {
|
||||
last = art.switchUrl('/test/pattern.mp4?intent=last')
|
||||
}
|
||||
if (command === 'pause-after-next')
|
||||
art.pause()
|
||||
await Promise.all([first, last])
|
||||
}, command)
|
||||
expect(await page.evaluate(() => window.art.video.paused)).toBe(true)
|
||||
expect(await page.evaluate(() => window.art.video.currentSrc)).toContain('intent=last')
|
||||
await testInfo.attach('source-paused', { contentType: 'application/json', body: JSON.stringify(await page.evaluate(() => ({ paused: window.art.video.paused, readyState: window.art.video.readyState }))) })
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
expect(await page.evaluate(() => window.art.video.paused)).toBe(false)
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
})
|
||||
}
|
||||
+161
-1
@@ -4,11 +4,12 @@ import { mock, test } from 'node:test'
|
||||
import { setImmediate } from 'node:timers/promises'
|
||||
import { loadModules } from './helpers/load.js'
|
||||
|
||||
const { Emitter, urlMix, switchMix, playMix, beginLifecycle, getScope, positionRestoration, currentTimeMix } = await loadModules({
|
||||
const { Emitter, urlMix, switchMix, playMix, pauseMix, beginLifecycle, getScope, positionRestoration, currentTimeMix } = await loadModules({
|
||||
Emitter: 'packages/artplayer/src/utils/emitter',
|
||||
urlMix: 'packages/artplayer/src/player/urlMix',
|
||||
switchMix: 'packages/artplayer/src/player/switchMix',
|
||||
playMix: 'packages/artplayer/src/player/playMix',
|
||||
pauseMix: 'packages/artplayer/src/player/pauseMix',
|
||||
beginLifecycle: { file: 'packages/artplayer/src/lifecycle/instance', name: 'beginLifecycle' },
|
||||
getScope: { file: 'packages/artplayer/src/lifecycle/instance', name: 'getScope' },
|
||||
positionRestoration: { file: 'packages/artplayer/src/source/restore-position', name: 'positionRestoration' },
|
||||
@@ -26,15 +27,172 @@ function createArt(customType) {
|
||||
aspectRatio: '16:9',
|
||||
loading: { show: false },
|
||||
notice: { show: '' },
|
||||
i18n: { get: key => key },
|
||||
pause: mock.fn(),
|
||||
play: mock.fn(() => Promise.resolve()),
|
||||
})
|
||||
beginLifecycle(art)
|
||||
art.template.$video.pause = () => {
|
||||
art.playing = false
|
||||
}
|
||||
pauseMix(art)
|
||||
art.pause = mock.fn(art.pause)
|
||||
urlMix(art)
|
||||
switchMix(art)
|
||||
return art
|
||||
}
|
||||
|
||||
function createPlayingArt() {
|
||||
const art = createArt()
|
||||
const video = art.template.$video
|
||||
video.paused = false
|
||||
Object.defineProperty(art, 'playing', { get: () => !video.paused })
|
||||
video.pause = () => {
|
||||
video.paused = true
|
||||
}
|
||||
video.play = async () => {
|
||||
video.paused = false
|
||||
}
|
||||
art.constructor = { instances: [] }
|
||||
playMix(art)
|
||||
art.play = mock.fn(art.play)
|
||||
return art
|
||||
}
|
||||
|
||||
test('rapid switches inherit playback intent despite their own native pauses', async () => {
|
||||
const art = createPlayingArt()
|
||||
const first = art.switchUrl('first.mp4')
|
||||
assert.equal(art.playing, false)
|
||||
const second = art.switchUrl('second.mp4')
|
||||
const third = art.switchUrl('last.mp4')
|
||||
await Promise.all([first, second])
|
||||
assert.equal(art.play.mock.callCount(), 0)
|
||||
art.emit('video:canplay')
|
||||
await third
|
||||
assert.equal(art.url, 'last.mp4')
|
||||
assert.equal(art.playing, true)
|
||||
assert.equal(art.play.mock.callCount(), 1)
|
||||
assert.deepEqual(Object.keys(art.e), [])
|
||||
})
|
||||
|
||||
test('explicit pause during a switch cancels inherited playback for subsequent switches', async () => {
|
||||
const art = createPlayingArt()
|
||||
const first = art.switchUrl('first.mp4')
|
||||
art.pause()
|
||||
const second = art.switchUrl('last.mp4')
|
||||
art.emit('video:canplay')
|
||||
await Promise.all([first, second])
|
||||
assert.equal(art.play.mock.callCount(), 0)
|
||||
assert.equal(art.playing, false)
|
||||
assert.deepEqual(Object.keys(art.e), [])
|
||||
})
|
||||
|
||||
test('explicit pause before readiness prevents the current switch from resuming', async () => {
|
||||
const art = createPlayingArt()
|
||||
const pending = art.switchUrl('pending.mp4')
|
||||
art.pause()
|
||||
art.emit('video:canplay')
|
||||
await pending
|
||||
assert.equal(art.playing, false)
|
||||
assert.equal(art.play.mock.callCount(), 0)
|
||||
})
|
||||
|
||||
test('a reentrant explicit pause is not swallowed by the internal switch pause', async () => {
|
||||
const art = createPlayingArt()
|
||||
art.once('pause', () => art.pause())
|
||||
const pending = art.switchUrl('pending.mp4')
|
||||
art.emit('video:canplay')
|
||||
await pending
|
||||
assert.equal(art.playing, false)
|
||||
assert.equal(art.play.mock.callCount(), 0)
|
||||
assert.deepEqual(Object.keys(art.e), [])
|
||||
})
|
||||
|
||||
test('a user pause updates intent before an existing pause listener starts another switch', async () => {
|
||||
const art = createPlayingArt()
|
||||
const first = art.switchUrl('first.mp4')
|
||||
let second
|
||||
art.once('pause', () => {
|
||||
second = art.switchUrl('last.mp4')
|
||||
})
|
||||
art.pause()
|
||||
art.emit('video:canplay')
|
||||
await Promise.all([first, second])
|
||||
assert.equal(art.url, 'last.mp4')
|
||||
assert.equal(art.playing, false)
|
||||
assert.equal(art.play.mock.callCount(), 0)
|
||||
assert.deepEqual(Object.keys(art.e), [])
|
||||
})
|
||||
|
||||
test('a pending explicit play command survives replacement even before native play fulfills', async () => {
|
||||
const art = createPlayingArt()
|
||||
const first = art.switchUrl('first.mp4')
|
||||
art.pause()
|
||||
let finish
|
||||
let calls = 0
|
||||
art.template.$video.play = () => {
|
||||
if (++calls === 1)
|
||||
return new Promise(resolve => finish = resolve)
|
||||
art.template.$video.paused = false
|
||||
return Promise.resolve()
|
||||
}
|
||||
const playing = art.play()
|
||||
const second = art.switchUrl('last.mp4')
|
||||
await first
|
||||
art.emit('video:canplay')
|
||||
await second
|
||||
finish(42)
|
||||
assert.equal(await playing, 42)
|
||||
assert.equal(calls, 2)
|
||||
assert.equal(art.playing, true)
|
||||
})
|
||||
|
||||
test('a late native resume cannot publish play or clear notice after an explicit pause', async () => {
|
||||
const art = createPlayingArt()
|
||||
let finish
|
||||
const events = []
|
||||
art.on('play', () => events.push('play'))
|
||||
art.template.$video.play = () => new Promise(resolve => finish = resolve)
|
||||
const pending = art.switchUrl('pending.mp4')
|
||||
art.emit('video:canplay')
|
||||
art.pause()
|
||||
finish(42)
|
||||
await pending
|
||||
assert.deepEqual(events, [])
|
||||
assert.equal(art.notice.show, 'Pause')
|
||||
assert.equal(art.playing, false)
|
||||
})
|
||||
|
||||
test('cancelled, failed and completed switches never leak playback intent to a new source', async () => {
|
||||
for (const boundary of ['direct-url', 'failure', 'completed', 'destroy']) {
|
||||
const art = createPlayingArt()
|
||||
const pending = art.switchUrl('first.mp4')
|
||||
if (boundary === 'failure') {
|
||||
const error = new Error('Controlled media failure')
|
||||
const rejected = assert.rejects(pending, actual => actual === error)
|
||||
art.emit('video:error', error)
|
||||
await rejected
|
||||
}
|
||||
else {
|
||||
if (boundary === 'direct-url')
|
||||
art.url = 'direct.mp4'
|
||||
if (boundary === 'completed')
|
||||
art.emit('video:canplay')
|
||||
if (boundary === 'destroy')
|
||||
getScope(art).dispose()
|
||||
await pending
|
||||
}
|
||||
art.pause()
|
||||
const played = art.play.mock.callCount()
|
||||
const next = art.switchUrl('next.mp4')
|
||||
art.emit('video:canplay')
|
||||
await next
|
||||
assert.equal(art.play.mock.callCount(), played, boundary)
|
||||
assert.equal(art.playing, false, boundary)
|
||||
assert.deepEqual(Object.keys(art.e), [])
|
||||
}
|
||||
})
|
||||
|
||||
test('source assignment does not revoke a caller-owned Blob URL still used elsewhere', async (t) => {
|
||||
const art = createArt()
|
||||
const blob = new Blob(['shared media'], { type: 'video/mp4' })
|
||||
@@ -349,6 +507,8 @@ test('late resume fulfillment or rejection cannot clear a newer operation notice
|
||||
const first = art.switchUrl('first.mp4')
|
||||
art.emit('video:canplay')
|
||||
art.playing = false
|
||||
// Model the explicit pause command, not only a read-only playback-state snapshot.
|
||||
art.pause()
|
||||
const second = art.switchUrl('second.mp4')
|
||||
await first
|
||||
art.notice.show = 'new source notice'
|
||||
|
||||
Reference in new issue
Block a user