mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
refactor(core): CORE-06 type media surfaces and playback hosts
This commit is contained in:
1 parent
97d320bd74
commit
ee859e4804
27 files changed
+2152
-79
No files matched your search
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!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 o=(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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),r.exports);const a=t(o),n="5.4.1",i={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","mediaGroup","muted","networkState","paused","playbackRate","played","preload","readyState","seekable","seeking","src","startDate","textTracks","videoTracks","volume"],methods:["addTextTrack","canPlayType","load","play","pause"],events:["abort","canplay","canplaythrough","durationchange","emptied","ended","error","loadeddata","loadedmetadata","loadstart","pause","play","playing","progress","ratechange","seeked","seeking","stalled","suspend","timeupdate","volumechange","waiting"],prototypes:["width","height","videoWidth","videoHeight","poster","webkitDecodedFrameCount","webkitDroppedFrameCount","playsInline","webLine 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 o=(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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),r.exports);const a=t(o),n="5.4.1",i={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","mediaGroup","muted","networkState","paused","playbackRate","played","preload","readyState","seekable","seeking","src","startDate","textTracks","videoTracks","volume"],methods:["addTextTrack","canPlayType","load","play","pause"],events:["abort","canplay","canplaythrough","durationchange","emptied","ended","error","loadeddata","loadedmetadata","loadstart","pause","play","playing","progress","ratechange","seeked","seeking","stalled","suspend","timeupdate","volumechange","waiting"],prototypes:["width","height","videoWidth","videoHeight","poster","webkitDecodedFrameCount","webkitDroppedFrameCount","playsInline","webLine truncated
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!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,e=Object.defineProperty,r=Object.defineProperties,o=Object.getOwnPropertyDescriptors,a=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,s=(t,r,o)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[r]=o,l=(t,e)=>{for(var r in e||(e={}))n.call(e,r)&&s(t,r,e[r]);if(a)for(var r of a(e))i.call(e,r)&&s(t,r,e[r]);return t},c=(t,e)=>r(t,o(e)),d=(t,e,r)=>new Promise((o,a)=>{var n=t=>{try{s(r.next(t))}catch(e){a(e)}},i=t=>{try{s(r.throw(t))}catch(e){a(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(n,i);s((r=r.apply(t,e)).next())});function p(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var h,u={exports:{}};var m=(h||(h=1,u.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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),u.exports);const f=p(m),g="5.4.1",v={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","meLine 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,e=Object.defineProperty,r=Object.defineProperties,o=Object.getOwnPropertyDescriptors,a=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,s=(t,r,o)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[r]=o,l=(t,e)=>{for(var r in e||(e={}))n.call(e,r)&&s(t,r,e[r]);if(a)for(var r of a(e))i.call(e,r)&&s(t,r,e[r]);return t},c=(t,e)=>r(t,o(e)),d=(t,e,r)=>new Promise((o,a)=>{var n=t=>{try{s(r.next(t))}catch(e){a(e)}},i=t=>{try{s(r.throw(t))}catch(e){a(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(n,i);s((r=r.apply(t,e)).next())});function p(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var h,u={exports:{}};var m=(h||(h=1,u.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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),u.exports);const f=p(m),g="5.4.1",v={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","meLine truncated
|
||||
@@ -3482,6 +3482,7 @@ function qualityMix(art) {
|
||||
});
|
||||
}
|
||||
function rectMix(art) {
|
||||
const host = art;
|
||||
def(art, "rect", {
|
||||
get: () => {
|
||||
return getRect(art.template.$player);
|
||||
@@ -3492,18 +3493,18 @@ function rectMix(art) {
|
||||
const key = keys[index];
|
||||
def(art, key, {
|
||||
get: () => {
|
||||
return art.rect[key];
|
||||
return host.rect[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
def(art, "x", {
|
||||
get: () => {
|
||||
return art.left + window.pageXOffset;
|
||||
return host.left + window.pageXOffset;
|
||||
}
|
||||
});
|
||||
def(art, "y", {
|
||||
get: () => {
|
||||
return art.top + window.pageYOffset;
|
||||
return host.top + window.pageYOffset;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@
|
||||
"ci:build": "yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
"test:unit": "node --test test/playback.test.js test/dash-control.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js",
|
||||
"test:unit": "node --test test/playback.test.js test/dash-control.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js",
|
||||
"test": "yarn test:node && yarn test:baseline",
|
||||
"test:browser": "playwright test",
|
||||
"test:browser:install": "playwright install chromium firefox webkit",
|
||||
|
||||
@@ -212,6 +212,45 @@ boundary; source-only tests cannot establish packaging or real media compatibili
|
||||
|
||||
## Types and remaining work
|
||||
|
||||
### Media and host boundaries
|
||||
|
||||
`src/media/types.ts` separates native video from canvas media shims. MediaState is
|
||||
the common playback state; PlaybackMethods preserves the actual play/pause return
|
||||
types. CanvasMedia requires a canvas with media operations, permits nullable source
|
||||
values used by MediaBunny, and does not claim the full HTMLVideoElement interface.
|
||||
Text tracks, picture-in-picture, frame callbacks and WebKit extensions are optional
|
||||
capabilities, including on NativeMedia where browser support can vary. Check them
|
||||
before use. These are internal types, not new public exports or runtime wrappers.
|
||||
|
||||
`src/media/hosts.ts` describes the dependencies of individual consumers instead of
|
||||
requiring the whole Artplayer class. PlayHost needs play, notice, events, mutex and
|
||||
the instance registry; PauseHost only needs pause, notice and events. LayoutHost
|
||||
only needs the player element's bounding rectangle. Notice's read and write types
|
||||
differ because its getter is visibility state and its setter accepts a message.
|
||||
|
||||
playMix, pauseMix, playingMix, durationMix and rectMix now consume these contracts.
|
||||
Their assertion signatures describe the properties installed with def; rectMix has
|
||||
one local cast for the getter properties it installs. They do not assert a shim to
|
||||
be native video. Play still awaits the media result before notice/event/mutex work;
|
||||
pause remains synchronous and preserves the media return value. Existing getter
|
||||
descriptors, boolean shim playing precedence, duration normalization and live layout
|
||||
reads are unchanged. play/pause/playing capture the original media object; duration
|
||||
reads template.$video on each access, as before.
|
||||
|
||||
The JS construction facade still assembles these hosts dynamically. Its complete
|
||||
static integration belongs to CORE-20; capability consumers migrate in their own
|
||||
CORE tasks. Do not assume these types already validate every third-party proxy.
|
||||
Public art.video remains the original object and retains its existing declaration
|
||||
for consumer compatibility. A public proxy typing extension requires separate
|
||||
consumer checks; internal code must not use that old declaration to hide a canvas.
|
||||
|
||||
`test/media-hosts.test.js` covers structural media methods, receiver/return values,
|
||||
event and mutex order, property descriptors and live getters. `test/types/media-hosts.ts`
|
||||
checks minimal hosts, native/canvas assignability and missing capabilities. The
|
||||
browser media-hosts suite combines published/candidate core with the existing
|
||||
workspace canvas proxy artifact (explicit hash attachment), real playback/seek and
|
||||
layout changes. It does not certify MediaBunny codecs or the proxy's full lifecycle.
|
||||
|
||||
Migrated sources compile with strict/noUncheckedIndexedAccess and browser-only
|
||||
ambient types. The merge accumulator is a dynamic string-key boundary with one
|
||||
documented cast to the pre-existing generic return contract. No file-wide any or
|
||||
|
||||
Vendored
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!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 o=(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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),r.exports);const a=t(o),n="5.4.1",i={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","mediaGroup","muted","networkState","paused","playbackRate","played","preload","readyState","seekable","seeking","src","startDate","textTracks","videoTracks","volume"],methods:["addTextTrack","canPlayType","load","play","pause"],events:["abort","canplay","canplaythrough","durationchange","emptied","ended","error","loadeddata","loadedmetadata","loadstart","pause","play","playing","progress","ratechange","seeked","seeking","stalled","suspend","timeupdate","volumechange","waiting"],prototypes:["width","height","videoWidth","videoHeight","poster","webkitDecodedFrameCount","webkitDroppedFrameCount","playsInline","webLine 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 o=(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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),r.exports);const a=t(o),n="5.4.1",i={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","mediaGroup","muted","networkState","paused","playbackRate","played","preload","readyState","seekable","seeking","src","startDate","textTracks","videoTracks","volume"],methods:["addTextTrack","canPlayType","load","play","pause"],events:["abort","canplay","canplaythrough","durationchange","emptied","ended","error","loadeddata","loadedmetadata","loadstart","pause","play","playing","progress","ratechange","seeked","seeking","stalled","suspend","timeupdate","volumechange","waiting"],prototypes:["width","height","videoWidth","videoHeight","poster","webkitDecodedFrameCount","webkitDroppedFrameCount","playsInline","webLine truncated
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!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,e=Object.defineProperty,r=Object.defineProperties,o=Object.getOwnPropertyDescriptors,a=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,s=(t,r,o)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[r]=o,l=(t,e)=>{for(var r in e||(e={}))n.call(e,r)&&s(t,r,e[r]);if(a)for(var r of a(e))i.call(e,r)&&s(t,r,e[r]);return t},c=(t,e)=>r(t,o(e)),d=(t,e,r)=>new Promise((o,a)=>{var n=t=>{try{s(r.next(t))}catch(e){a(e)}},i=t=>{try{s(r.throw(t))}catch(e){a(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(n,i);s((r=r.apply(t,e)).next())});function p(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var h,u={exports:{}};var m=(h||(h=1,u.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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),u.exports);const f=p(m),g="5.4.1",v={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","meLine 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,e=Object.defineProperty,r=Object.defineProperties,o=Object.getOwnPropertyDescriptors,a=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,s=(t,r,o)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[r]=o,l=(t,e)=>{for(var r in e||(e={}))n.call(e,r)&&s(t,r,e[r]);if(a)for(var r of a(e))i.call(e,r)&&s(t,r,e[r]);return t},c=(t,e)=>r(t,o(e)),d=(t,e,r)=>new Promise((o,a)=>{var n=t=>{try{s(r.next(t))}catch(e){a(e)}},i=t=>{try{s(r.throw(t))}catch(e){a(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(n,i);s((r=r.apply(t,e)).next())});function p(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var h,u={exports:{}};var m=(h||(h=1,u.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 a=t(r);if("boolean"===a)return"boolean";if("string"===a)return"string";if("number"===a)return"number";if("symbol"===a)return"symbol";if("function"===a)return"GeneratorFunction"===o(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(o(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(a=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 a.slice(8,-1).toLowerCase().replace(/\s/g,"")};function o(t){return t.constructor?t.constructor.name:null}function a(t,e){var o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:["option"];return n(t,e,o),i(t,e,o),function(t,e,o){var s=r(e),l=r(t);if("object"===s){if("object"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'object' type, but got '").concat(l,"'"));Object.keys(e).forEach(function(r){var s=t[r],l=e[r],c=o.slice();c.push(r),n(s,l,c),i(s,l,c),a(s,l,c)})}if("array"===s){if("array"!==l)throw new Error("[Type Error]: '".concat(o.join("."),"' require 'array' type, but got '").concat(l,"'"));t.forEach(function(r,s){var l=t[s],c=e[s]||e[0],d=o.slice();d.push(s),n(l,c,d),i(l,c,d),a(l,c,d)})}}(t,e,o),t}function n(t,e,o){if("string"===r(e)){var a=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 a===t}):e.toLowerCase().trim()===a))throw new Error("[Type Error]: '".concat(o.join("."),"' require '").concat(e,"' type, but got '").concat(a,"'"))}}function i(t,e,o){if("function"===r(e)){var a=e(t,r(t),o);if(!0!==a){var n=r(a);throw"string"===n?new Error(a):"error"===n?a:new Error("[Validator Error]: The scheme for '".concat(o.join("."),"' validator require return true, but got '").concat(a,"'"))}}}return a.kindOf=r,a}()),u.exports);const f=p(m),g="5.4.1",v={properties:["audioTracks","autoplay","buffered","controller","controls","crossOrigin","currentSrc","currentTime","defaultMuted","defaultPlaybackRate","duration","ended","error","loop","meLine truncated
|
||||
+4
-3
@@ -3482,6 +3482,7 @@ function qualityMix(art) {
|
||||
});
|
||||
}
|
||||
function rectMix(art) {
|
||||
const host = art;
|
||||
def(art, "rect", {
|
||||
get: () => {
|
||||
return getRect(art.template.$player);
|
||||
@@ -3492,18 +3493,18 @@ function rectMix(art) {
|
||||
const key = keys[index];
|
||||
def(art, key, {
|
||||
get: () => {
|
||||
return art.rect[key];
|
||||
return host.rect[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
def(art, "x", {
|
||||
get: () => {
|
||||
return art.left + window.pageXOffset;
|
||||
return host.left + window.pageXOffset;
|
||||
}
|
||||
});
|
||||
def(art, "y", {
|
||||
get: () => {
|
||||
return art.top + window.pageYOffset;
|
||||
return host.top + window.pageYOffset;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { ResolvedOption } from '../option/types'
|
||||
import type { PlaybackMethods } from './types'
|
||||
|
||||
export interface MediaHost<Media> {
|
||||
template: { $video: Media }
|
||||
}
|
||||
|
||||
interface NoticeHost {
|
||||
i18n: { get: (key: string) => string }
|
||||
notice: { get show(): unknown, set show(value: string | Error) }
|
||||
emit: (name: 'play' | 'pause') => unknown
|
||||
}
|
||||
|
||||
export type PauseHost<Media extends Pick<PlaybackMethods, 'pause'>> = MediaHost<Media> & NoticeHost
|
||||
|
||||
interface PauseTarget {
|
||||
pause: () => unknown
|
||||
}
|
||||
|
||||
export type PlayHost<Media extends Pick<PlaybackMethods, 'play'>> = MediaHost<Media> & NoticeHost & Partial<PauseTarget> & {
|
||||
option: Pick<ResolvedOption, 'mutex'>
|
||||
constructor: { instances: PauseTarget[] }
|
||||
}
|
||||
|
||||
export interface LayoutHost {
|
||||
template: { $player: Pick<Element, 'getBoundingClientRect'> }
|
||||
}
|
||||
|
||||
export type RectState = Readonly<Pick<DOMRect, 'bottom' | 'height' | 'left' | 'right' | 'top' | 'width'>> & {
|
||||
readonly rect: DOMRect
|
||||
readonly x: number
|
||||
readonly y: number
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
export type MediaState = Pick<HTMLMediaElement, 'currentTime' | 'duration' | 'paused' | 'ended' | 'readyState'> & {
|
||||
readonly playing?: boolean
|
||||
}
|
||||
|
||||
export interface PlaybackMethods<PlayResult = unknown, PauseResult = unknown> {
|
||||
play: () => PlayResult
|
||||
pause: () => PauseResult
|
||||
}
|
||||
|
||||
export interface OptionalMediaCapabilities {
|
||||
readonly textTracks?: ArrayLike<TextTrack>
|
||||
readonly error?: unknown
|
||||
requestPictureInPicture?: () => Promise<PictureInPictureWindow>
|
||||
requestVideoFrameCallback?: (callback: VideoFrameRequestCallback) => number
|
||||
cancelVideoFrameCallback?: (handle: number) => void
|
||||
webkitEnterFullscreen?: () => void
|
||||
webkitExitFullscreen?: () => void
|
||||
readonly webkitSupportsFullscreen?: boolean
|
||||
webkitShowPlaybackTargetPicker?: () => void
|
||||
webkitSupportsPresentationMode?: (mode: string) => boolean
|
||||
webkitSetPresentationMode?: (mode: string) => void
|
||||
readonly webkitPresentationMode?: string
|
||||
readonly webkitDisplayingFullscreen?: boolean
|
||||
}
|
||||
|
||||
export type NativeMedia = Omit<HTMLVideoElement, keyof OptionalMediaCapabilities> & OptionalMediaCapabilities
|
||||
|
||||
export type CanvasMedia = HTMLCanvasElement & MediaState & PlaybackMethods & OptionalMediaCapabilities & {
|
||||
src: string | null
|
||||
readonly currentSrc: string | null
|
||||
readonly videoWidth: number
|
||||
readonly videoHeight: number
|
||||
readonly buffered: TimeRanges
|
||||
volume: number
|
||||
muted: boolean
|
||||
playbackRate: number
|
||||
load: () => unknown
|
||||
}
|
||||
|
||||
export type MediaSurface = NativeMedia | CanvasMedia
|
||||
@@ -1,12 +0,0 @@
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function durationMix(art) {
|
||||
def(art, 'duration', {
|
||||
get: () => {
|
||||
const { duration } = art.template.$video
|
||||
if (duration === Infinity)
|
||||
return 0
|
||||
return duration || 0
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { MediaHost } from '../media/hosts'
|
||||
import type { MediaState } from '../media/types'
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function durationMix(art: MediaHost<Pick<MediaState, 'duration'>>): asserts art is MediaHost<Pick<MediaState, 'duration'>> & { readonly duration: number } {
|
||||
def(art, 'duration', {
|
||||
get: () => {
|
||||
const { duration } = art.template.$video
|
||||
if (duration === Infinity)
|
||||
return 0
|
||||
return duration || 0
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function pauseMix(art) {
|
||||
const {
|
||||
template: { $video },
|
||||
i18n,
|
||||
notice,
|
||||
} = art
|
||||
|
||||
def(art, 'pause', {
|
||||
value() {
|
||||
const result = $video.pause()
|
||||
notice.show = i18n.get('Pause')
|
||||
art.emit('pause')
|
||||
return result
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { PauseHost } from '../media/hosts'
|
||||
import type { PlaybackMethods } from '../media/types'
|
||||
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']> } {
|
||||
const {
|
||||
template: { $video },
|
||||
i18n,
|
||||
notice,
|
||||
} = art
|
||||
|
||||
def(art, 'pause', {
|
||||
value() {
|
||||
const result = $video.pause()
|
||||
notice.show = i18n.get('Pause')
|
||||
art.emit('pause')
|
||||
return result
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { PlayHost } from '../media/hosts'
|
||||
import type { PlaybackMethods } from '../media/types'
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function playMix(art) {
|
||||
export default function playMix<Media extends Pick<PlaybackMethods, 'play'>>(art: PlayHost<Media>): asserts art is PlayHost<Media> & { play: () => Promise<Awaited<ReturnType<Media['play']>>> } {
|
||||
const {
|
||||
i18n,
|
||||
notice,
|
||||
@@ -17,7 +19,7 @@ export default function playMix(art) {
|
||||
|
||||
if (option.mutex) {
|
||||
for (let index = 0; index < instances.length; index++) {
|
||||
const instance = instances[index]
|
||||
const instance = instances[index]!
|
||||
if (instance !== art) {
|
||||
instance.pause()
|
||||
}
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
import type { MediaHost } from '../media/hosts'
|
||||
import type { MediaState } from '../media/types'
|
||||
import { def } from '../utils'
|
||||
|
||||
export default function playingMix(art) {
|
||||
export default function playingMix(art: MediaHost<Omit<MediaState, 'duration'>>): asserts art is MediaHost<Omit<MediaState, 'duration'>> & { readonly playing: boolean } {
|
||||
const { $video } = art.template
|
||||
def(art, 'playing', {
|
||||
get: () => {
|
||||
@@ -1,31 +0,0 @@
|
||||
import { def, getRect } from '../utils'
|
||||
|
||||
export default function rectMix(art) {
|
||||
def(art, 'rect', {
|
||||
get: () => {
|
||||
return getRect(art.template.$player)
|
||||
},
|
||||
})
|
||||
|
||||
const keys = ['bottom', 'height', 'left', 'right', 'top', 'width']
|
||||
for (let index = 0; index < keys.length; index++) {
|
||||
const key = keys[index]
|
||||
def(art, key, {
|
||||
get: () => {
|
||||
return art.rect[key]
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
def(art, 'x', {
|
||||
get: () => {
|
||||
return art.left + window.pageXOffset
|
||||
},
|
||||
})
|
||||
|
||||
def(art, 'y', {
|
||||
get: () => {
|
||||
return art.top + window.pageYOffset
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { LayoutHost, RectState } from '../media/hosts'
|
||||
import { def, getRect } from '../utils'
|
||||
|
||||
export default function rectMix(art: LayoutHost): asserts art is LayoutHost & RectState {
|
||||
// These properties are installed below; no native media element is asserted.
|
||||
const host = art as LayoutHost & RectState
|
||||
def(art, 'rect', {
|
||||
get: () => {
|
||||
return getRect(art.template.$player)
|
||||
},
|
||||
})
|
||||
|
||||
const keys = ['bottom', 'height', 'left', 'right', 'top', 'width'] as const
|
||||
for (let index = 0; index < keys.length; index++) {
|
||||
const key = keys[index]!
|
||||
def(art, key, {
|
||||
get: () => {
|
||||
return host.rect[key]
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
def(art, 'x', {
|
||||
get: () => {
|
||||
return host.left + window.pageXOffset
|
||||
},
|
||||
})
|
||||
|
||||
def(art, 'y', {
|
||||
get: () => {
|
||||
return host.top + window.pageYOffset
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -36,3 +36,14 @@ current rejection; replace those two negative cases with positive consumer cover
|
||||
when expanding the public declarations. Do not change runtime validation to match a
|
||||
narrower historical declaration. Other optional fields and callback signatures remain
|
||||
subject to the existing public compatibility contract.
|
||||
|
||||
## Internal media migration
|
||||
|
||||
CORE-06 uses src/media/types.ts and src/media/hosts.ts for native media, canvas
|
||||
shims and minimal playback/layout dependencies. These types are not exported by
|
||||
the package. Public art.video keeps its historical declaration and runtime identity;
|
||||
do not cast an internal canvas to HTMLVideoElement merely to satisfy that public
|
||||
declaration. Optional capability checks belong to their consumers. Full constructor
|
||||
type integration is tracked by CORE-20; proxy package migration validates each real
|
||||
adapter separately. Source type fixtures and real canvas integration live in
|
||||
test/types/media-hosts.ts and test/browser/media-hosts.spec.js.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,52 @@
|
||||
# CORE-06:内部媒体与宿主类型
|
||||
|
||||
## 实现与边界
|
||||
|
||||
新增 src/media/types.ts 描述 NativeMedia、CanvasMedia、MediaState 和 PlaybackMethods;
|
||||
src/media/hosts.ts 描述最小播放、暂停和布局宿主。五个 player 模块 playMix、pauseMix、
|
||||
playingMix、durationMix、rectMix 从 JS 迁移 TS,依赖各自实际需要的字段,避免全部接收整个
|
||||
Artplayer 或将所有代理都视为 HTMLVideoElement。入口仍按原顺序调用这些模块。
|
||||
|
||||
CanvasMedia 包含 canvas 自身能力及媒体字段,source 允许 MediaBunny 使用的 null;
|
||||
textTracks、PiP、视频帧回调及 WebKit 扩展为可选能力,NativeMedia 也不能假定所有浏览器
|
||||
都有这些功能。PlaybackMethods 保留实际方法返回值,不将所有代理的 pause 改成 Promise。
|
||||
Notice 的 getter 与 setter 分开描述,因为读取是显示状态,写入是消息。
|
||||
|
||||
模块 assertion 签名表达 def 安装后的属性,rectMix 保留一处说明过的内部 getter 安装断言;
|
||||
没有把代理断言成 HTMLVideoElement,没有新增全局 any。主入口目前仍是 JS,其整体静态类型
|
||||
集成属于 CORE-20;这些内部类型不是运行时校验器,也不表示所有第三方 proxy 已经验证。
|
||||
|
||||
## 保留契约
|
||||
|
||||
API-02/03/04/07/09/10/11:公开 art.video 仍返回原始媒体对象,声明和所有分发入口未改变。
|
||||
play 仍 await 底层返回值,再设置 notice、emit play、处理 mutex;pause 同步返回底层结果。
|
||||
方法可抽取调用,底层方法 receiver 仍是媒体对象。playing 优先读取 shim 的 boolean,
|
||||
否则使用原生状态组合;duration 只按原规则处理 Infinity/假值。播放、暂停、playing 捕获
|
||||
原媒体引用,duration 动态读取 template.$video;布局 getter 动态读取 DOM 和页面滚动偏移。
|
||||
属性安装顺序和 enumerable/configurable/writable 保持原样。
|
||||
|
||||
不新增公开媒体包装层,不要求旧用户修改 art.video 调用。旧公开 video 声明不能用来掩盖
|
||||
内部 canvas 类型;公开类型扩展需另有消费者兼容检查。没有在本任务宣称修复 proxy 自身的
|
||||
异步绘制销毁问题或验证 MediaBunny codec,代理完整迁移及其资源所有权由各包任务继续处理。
|
||||
|
||||
## 验证与交接
|
||||
|
||||
- 四项 Node 测试覆盖结构化代理返回值、this、事件/互斥顺序、属性描述符、shim playing、
|
||||
时长规范化和实时布局。已有播放拒绝、切源、销毁回归同步执行。
|
||||
- 类型正例覆盖原生/canvas、最小宿主、同步返回及 readonly getter;五项负例拒绝裸 canvas、
|
||||
将 shim 当完整 video、无检查调用可选能力、错误暂停返回和写入只读布局。
|
||||
- yarn ci:check:83 单元、4 工程、24 冻结基线,共 111 项通过;28 个生产 TS 文件严格检查,
|
||||
其中核心 23 个、chapter 5 个。未迁移 JS 不计入。
|
||||
- yarn build artplayer 和 yarn build:i18n 生成产物;yarn test:package:release 在仓库外安装
|
||||
tarball,27 项运行时及五组类型模式零诊断。包内文档与源码指纹已与最终构建快照核对。
|
||||
- 安装 UMD 候选完整三浏览器 132 项通过;安装 legacy 的播放、生命周期和媒体宿主 48 项
|
||||
通过,无重试或跳过。新六项 canvas 测试验证真实播放、暂停、seek、对象身份和布局变化。
|
||||
canvas 来自已有 docs/compiled 工件,附件记录其 SHA;它不是独立安装的 npm 发布 proxy。
|
||||
|
||||
详见 [验证报告](../baselines/media-hosts-validation.json)、
|
||||
[核心维护地图](../../packages/artplayer/ARCHITECTURE.md) 和
|
||||
[类型说明](../../packages/artplayer/types/README.md)。无新增依赖或锁文件修改;test:unit
|
||||
加入新宿主测试。未推送、发布或执行远端 CI/真机验收。
|
||||
|
||||
本任务单独提交,回退该提交可一并恢复五个 JS 模块、生成产物、文档及任务状态。
|
||||
下一项 CORE-07 协调公开声明差异,保留历史合法类型消费并以运行时证据补充类型。
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 214 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 173 / doing 0 / blocked 0 / done 41 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 172 / doing 0 / blocked 0 / done 42 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
| CORE-03 | artplayer<br>建立内部资源作用域 | CORE-02 | 监听/订阅/定时器/RAF/请求/URL 的资源登记 | 实例与操作作用域分离,单项清理失败不阻断其他资源 | H | done |
|
||||
| CORE-04 | artplayer<br>修复初始化与销毁生命周期 | CORE-03, ENG-05, BASE-07 | 构造失败回收、重复/重入 destroy、instances 修复 | 事件顺序、removeHtml、多实例和销毁中异步有真实回归;处理 baselines/lifecycle-coverage.md 的 BASE-LIFE-04/05 并增加候选正向回归;协调 BASE-PERF-01 销毁后定时器与回调归属 | H | done |
|
||||
| CORE-05 | artplayer<br>区分输入配置和内部配置 | CORE-01, ENG-04 | Option/ResolvedOption、默认值与校验整理 | 配置合并/错误时机不变,JS 输入仍运行时校验 | M | done |
|
||||
| CORE-06 | artplayer<br>建立内部媒体与宿主类型 | CORE-05 | 原生 video、canvas shim、UI host 类型 | 不更改公开 art.video 用法,不以 HTMLVideoElement 断言掩盖 shim | M | todo |
|
||||
| CORE-06 | artplayer<br>建立内部媒体与宿主类型 | CORE-05 | 原生 video、canvas shim、UI host 类型 | 不更改公开 art.video 用法,不以 HTMLVideoElement 断言掩盖 shim | M | done |
|
||||
| CORE-07 | artplayer<br>逐项协调公开声明差异 | CORE-02, CORE-06, BASE-07 | plugins/toggle/cue/setting/static 的旧新类型对照 | 每项有兼容重载/扩展或待决结论,不能删除旧类型样例;处理 BASE-TYPE-01/04,分别保留旧合法声明消费并验证真实返回;协调 BASE-TYPE-05 的 Utils 导出、descriptor 和 timer 旧声明差异 | H | todo |
|
||||
| CORE-08 | artplayer<br>整理插件管理器与扩展类型 | CORE-03, CORE-07 | 注册流程、同步/异步结果、插件/事件扩展接口 | 旧插件同步可见性、重名与命名、销毁中异步插件保持明确语义;处理 baselines/lifecycle-coverage.md 的 BASE-LIFE-03 并增加候选正向回归 | H | todo |
|
||||
| CORE-09 | artplayer<br>整理 URL 与切源操作 | CORE-04, CORE-06, CORE-08 | url/switch/customType 的操作身份及取消方案 | 并发切源、错误、同 URL、销毁和 Promise 结算有契约;处理 baselines/lifecycle-coverage.md 的 BASE-LIFE-01/02/06 并增加候选正向回归 | H | todo |
|
||||
@@ -440,6 +440,7 @@
|
||||
- CORE-03: [记录](changes/2026-09-11-CORE-03-resource-scope.md) [记录](baselines/resource-scope-validation.json)
|
||||
- CORE-04: [记录](changes/2026-09-11-CORE-04-instance-lifecycle.md) [记录](baselines/instance-lifecycle-validation.json)
|
||||
- CORE-05: [记录](changes/2026-09-11-CORE-05-resolved-options.md) [记录](baselines/options-validation.json)
|
||||
- CORE-06: [记录](changes/2026-09-11-CORE-06-media-hosts.md) [记录](baselines/media-hosts-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,9 @@
|
||||
# 进度与证据
|
||||
|
||||
## 当前实施:CORE-06 媒体与宿主类型完成
|
||||
|
||||
建立原生 video/canvas 媒体及最小宿主类型,五个播放/布局 mixin 迁移 TS,保持公开 art.video 身份、描述符和返回时序。111 项 Node/基线、安装 UMD 三浏览器 132 项及 legacy 播放/生命周期/代理 48 项通过;严格打包 27 项运行时、五组类型零诊断。见 [交付](changes/2026-09-11-CORE-06-media-hosts.md)。当前 214 项,42 完成、172 待办;核心二十三个 TS 文件、chapter 五个。代理完整迁移和主入口静态集成尚未完成;下一项 CORE-07 逐项协调公开声明差异。
|
||||
|
||||
## 当前实施:CORE-05 配置与默认值 TS 拆分完成
|
||||
|
||||
核心入口接入独立默认值/解析模块,scheme 迁移 TS,区分 Option 与 ResolvedOption 并保留应用扩展类型。107 项 Node/基线、安装 UMD 三浏览器 126 项及 legacy 配置/生命周期 48 项通过;严格打包 27 项运行时、五组类型零诊断。见 [交付](changes/2026-09-11-CORE-05-resolved-options.md)。新登记 BASE-TYPE-06(省略 URL/数字控件 HTML 的声明缺口)交 CORE-07。当前 214 项,41 完成、173 待办;核心十六个 TS 文件、chapter 五个。下一项 CORE-06 内部媒体与宿主类型。
|
||||
|
||||
+5
-2
@@ -849,11 +849,14 @@
|
||||
"dependsOn": [
|
||||
"CORE-05"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "原生 video、canvas shim、UI host 类型",
|
||||
"acceptance": "不更改公开 art.video 用法,不以 HTMLVideoElement 断言掩盖 shim",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"changes/2026-09-11-CORE-06-media-hosts.md",
|
||||
"baselines/media-hosts-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "CORE-07",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
const proxyFile = new URL('../../docs/compiled/artplayer-proxy-canvas.js', import.meta.url)
|
||||
const proxyCode = fs.readFileSync(proxyFile, 'utf8')
|
||||
|
||||
for (const core of ['published', 'candidate']) {
|
||||
test(`${core}: existing canvas proxy keeps media identity, playback and layout`, async ({ page }, testInfo) => {
|
||||
await page.goto(`/test/player.html?core=${core}&chapter=published`)
|
||||
await page.addScriptTag({ content: proxyCode })
|
||||
await testInfo.attach('canvas-proxy-artifact', { contentType: 'application/json', body: JSON.stringify({
|
||||
kind: 'existing-workspace-artifact',
|
||||
file: 'docs/compiled/artplayer-proxy-canvas.js',
|
||||
sha256: createHash('sha256').update(proxyCode).digest('hex'),
|
||||
}) })
|
||||
await page.evaluate(() => {
|
||||
const container = document.createElement('div')
|
||||
Object.assign(container.style, { width: '640px', height: '360px' })
|
||||
document.body.append(container)
|
||||
window.draws = 0
|
||||
window.art = new window.Artplayer({
|
||||
container,
|
||||
url: '/test/pattern.mp4',
|
||||
muted: true,
|
||||
proxy: window.artplayerProxyCanvas(() => window.draws++),
|
||||
})
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
const initial = await page.evaluate(() => {
|
||||
const art = window.art
|
||||
return { canvas: art.video instanceof HTMLCanvasElement, native: art.video instanceof HTMLVideoElement, same: art.video === art.template.$video, duration: art.duration, width: art.width, rectWidth: art.rect.width }
|
||||
})
|
||||
expect(initial).toEqual({ canvas: true, native: false, same: true, duration: 8, width: 640, rectWidth: 640 })
|
||||
await page.evaluate(() => window.art.play())
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
|
||||
await expect.poll(() => page.evaluate(() => window.draws)).toBeGreaterThan(0)
|
||||
expect(await page.evaluate(() => window.art.playing)).toBe(true)
|
||||
await page.evaluate(() => window.art.pause())
|
||||
expect(await page.evaluate(() => window.art.playing)).toBe(false)
|
||||
await page.evaluate(() => {
|
||||
window.art.seek = 2
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeCloseTo(2, 1)
|
||||
const layout = await page.evaluate(() => {
|
||||
const art = window.art
|
||||
art.template.$container.style.width = '480px'
|
||||
return { width: art.width, x: art.x, expectedX: art.rect.left + window.pageXOffset }
|
||||
})
|
||||
expect(layout.width).toBe(480)
|
||||
expect(layout.x).toBe(layout.expectedX)
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Use the repository Node runner.
|
||||
import { test } from 'node:test'
|
||||
import { loadModules } from './helpers/load.js'
|
||||
|
||||
const { playMix, pauseMix, playingMix, durationMix, rectMix } = await loadModules(Object.fromEntries(['playMix', 'pauseMix', 'playingMix', 'durationMix', 'rectMix'].map(name => [name, `packages/artplayer/src/player/${name}`])))
|
||||
|
||||
test('structural playback hosts retain synchronous proxy values, async play order and receivers', async () => {
|
||||
const calls = []
|
||||
const media = {
|
||||
play() {
|
||||
assert.equal(this, media)
|
||||
calls.push('media:play')
|
||||
return 'proxy-result'
|
||||
},
|
||||
pause() {
|
||||
assert.equal(this, media)
|
||||
calls.push('media:pause')
|
||||
return 42
|
||||
},
|
||||
}
|
||||
const art = {
|
||||
template: { $video: media },
|
||||
option: { mutex: true },
|
||||
i18n: { get: key => key },
|
||||
notice: {
|
||||
get show() {
|
||||
return false
|
||||
},
|
||||
set show(message) {
|
||||
calls.push(`notice:${message}`)
|
||||
},
|
||||
},
|
||||
emit: name => calls.push(`event:${name}`),
|
||||
constructor: { instances: [] },
|
||||
}
|
||||
playMix(art)
|
||||
pauseMix(art)
|
||||
art.constructor.instances.push(art, {
|
||||
pause: () => calls.push('mutex'),
|
||||
})
|
||||
const play = art.play
|
||||
const pause = art.pause
|
||||
const result = play()
|
||||
assert(result instanceof Promise)
|
||||
assert.deepEqual(calls, ['media:play'])
|
||||
assert.equal(await result, 'proxy-result')
|
||||
assert.deepEqual(calls, ['media:play', 'notice:Play', 'event:play', 'mutex'])
|
||||
assert.equal(pause(), 42)
|
||||
assert.deepEqual(calls.slice(-3), ['media:pause', 'notice:Pause', 'event:pause'])
|
||||
for (const name of ['play', 'pause']) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(art, name)
|
||||
assert.equal(descriptor.enumerable, false)
|
||||
assert.equal(descriptor.configurable, false)
|
||||
assert.equal(descriptor.writable, false)
|
||||
}
|
||||
})
|
||||
|
||||
test('playing uses a shim boolean when present and otherwise keeps native fallback', () => {
|
||||
const video = { currentTime: 1, paused: false, ended: false, readyState: 3 }
|
||||
const art = { template: { $video: video } }
|
||||
playingMix(art)
|
||||
assert.equal(art.playing, true)
|
||||
video.playing = false
|
||||
assert.equal(art.playing, false)
|
||||
video.currentTime = 0
|
||||
video.playing = true
|
||||
assert.equal(art.playing, true)
|
||||
delete video.playing
|
||||
assert.equal(art.playing, false)
|
||||
video.currentTime = 1
|
||||
video.readyState = 2
|
||||
assert.equal(art.playing, false)
|
||||
video.readyState = 3
|
||||
video.ended = true
|
||||
assert.equal(art.playing, false)
|
||||
})
|
||||
|
||||
test('duration normalization reads the current media reference and keeps legacy infinity rules', () => {
|
||||
const art = { template: { $video: { duration: Infinity } } }
|
||||
durationMix(art)
|
||||
assert.equal(art.duration, 0)
|
||||
for (const [value, expected] of [[Number.NaN, 0], [0, 0], [-Infinity, -Infinity], [12.5, 12.5]]) {
|
||||
art.template.$video = { duration: value }
|
||||
assert.equal(art.duration, expected)
|
||||
}
|
||||
})
|
||||
|
||||
test('layout host getters stay live, preserve descriptor order and read scroll offsets', (context) => {
|
||||
const previous = Object.getOwnPropertyDescriptor(globalThis, 'window')
|
||||
Object.defineProperty(globalThis, 'window', { configurable: true, value: { pageXOffset: 4, pageYOffset: 8 } })
|
||||
context.after(() => {
|
||||
if (previous)
|
||||
Object.defineProperty(globalThis, 'window', previous)
|
||||
else
|
||||
delete globalThis.window
|
||||
})
|
||||
let rectangle = { bottom: 40, height: 30, left: 10, right: 50, top: 10, width: 40 }
|
||||
const player = { getBoundingClientRect() {
|
||||
assert.equal(this, player)
|
||||
return rectangle
|
||||
} }
|
||||
const art = { template: { $player: player } }
|
||||
rectMix(art)
|
||||
assert.equal(art.rect, rectangle)
|
||||
assert.equal(art.x, 14)
|
||||
assert.equal(art.y, 18)
|
||||
rectangle = { ...rectangle, width: 80, left: 20 }
|
||||
assert.equal(art.width, 80)
|
||||
assert.equal(art.x, 24)
|
||||
assert.deepEqual(Object.getOwnPropertyNames(art), ['template', 'rect', 'bottom', 'height', 'left', 'right', 'top', 'width', 'x', 'y'])
|
||||
const descriptor = Object.getOwnPropertyDescriptor(art, 'width')
|
||||
assert.equal(descriptor.enumerable, false)
|
||||
assert.equal(descriptor.configurable, false)
|
||||
assert.equal(descriptor.set, undefined)
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { LayoutHost, PauseHost, PlayHost } from '../../packages/artplayer/src/media/hosts'
|
||||
import type { CanvasMedia, MediaSurface, NativeMedia } from '../../packages/artplayer/src/media/types'
|
||||
import durationMix from '../../packages/artplayer/src/player/durationMix'
|
||||
import pauseMix from '../../packages/artplayer/src/player/pauseMix'
|
||||
import playingMix from '../../packages/artplayer/src/player/playingMix'
|
||||
import playMix from '../../packages/artplayer/src/player/playMix'
|
||||
import rectMix from '../../packages/artplayer/src/player/rectMix'
|
||||
|
||||
const native: NativeMedia = document.createElement('video')
|
||||
const canvas: CanvasMedia = Object.assign(document.createElement('canvas'), {
|
||||
src: null,
|
||||
currentSrc: null,
|
||||
currentTime: 0,
|
||||
duration: 10,
|
||||
paused: true,
|
||||
ended: false,
|
||||
readyState: 0,
|
||||
videoWidth: 320,
|
||||
videoHeight: 180,
|
||||
buffered: { length: 0, start: (_index: number) => 0, end: (_index: number) => 0 },
|
||||
volume: 1,
|
||||
muted: false,
|
||||
playbackRate: 1,
|
||||
play: () => 'sync-result',
|
||||
pause: () => 42,
|
||||
load: () => {},
|
||||
})
|
||||
function useSurface(media: MediaSurface) {
|
||||
// @ts-expect-error Proxy capability is optional, including on older native browsers.
|
||||
media.requestPictureInPicture()
|
||||
media.requestPictureInPicture?.()
|
||||
void media.textTracks?.[0]?.activeCues
|
||||
// @ts-expect-error A media surface cannot be passed off as a complete native video.
|
||||
const video: HTMLVideoElement = media
|
||||
void video
|
||||
}
|
||||
useSurface(native)
|
||||
useSurface(canvas)
|
||||
// @ts-expect-error A raw canvas lacks the proxy's media implementation.
|
||||
const incomplete: MediaSurface = document.createElement('canvas')
|
||||
void incomplete
|
||||
|
||||
const shared = { i18n: { get: (key: string) => key }, notice: { show: '' }, emit: (_name: 'play' | 'pause') => {} }
|
||||
const playHost: PlayHost<{ play: () => string }> = { ...shared, template: { $video: { play: () => 'sync-result' } }, option: { mutex: false }, constructor: { instances: [] } }
|
||||
playMix(playHost)
|
||||
const result: Promise<string> = playHost.play()
|
||||
const pauseHost: PauseHost<{ pause: () => number }> = { ...shared, template: { $video: { pause: () => 42 } } }
|
||||
pauseMix(pauseHost)
|
||||
const paused: number = pauseHost.pause()
|
||||
// @ts-expect-error Pausing is not forced to return a Promise.
|
||||
const wrong: Promise<void> = pauseHost.pause()
|
||||
void [result, paused, wrong]
|
||||
|
||||
const mediaHost = { template: { $video: canvas } }
|
||||
playingMix(mediaHost)
|
||||
durationMix(mediaHost)
|
||||
const state: boolean = mediaHost.playing
|
||||
const duration: number = mediaHost.duration
|
||||
const layout: LayoutHost = { template: { $player: document.createElement('div') } }
|
||||
rectMix(layout)
|
||||
const width: number = layout.width
|
||||
// @ts-expect-error Read-only getter properties cannot be assigned.
|
||||
layout.width = 2
|
||||
void [state, duration, width]
|
||||
Reference in new issue
Block a user