mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
153 lines
4.5 KiB
JavaScript
153 lines
4.5 KiB
JavaScript
import vm from 'node:vm'
|
|
import { transform } from 'esbuild'
|
|
import { verifyCanvasContract } from '../../refactor/scripts/canvas-contract.mjs'
|
|
import { readMember } from '../../refactor/scripts/releases.mjs'
|
|
|
|
export async function canvasHistorical() {
|
|
const contract = await verifyCanvasContract()
|
|
return [
|
|
...[contract.baseline.release, ...contract.baseline.previous].map(release => ({ name: `published-${release.version}`, source: readMember(contract.archives.get(release.version), `package/${release.manifest.main.replace(/^\.\//, '')}`).toString(), format: 'artifact' })),
|
|
{ name: 'frozen-workspace', source: contract.sources.get('packages/artplayer-proxy-canvas/src/index.js'), format: 'source' },
|
|
]
|
|
}
|
|
|
|
export async function canvasEnvironment(implementation, settings = {}) {
|
|
const calls = []
|
|
const draws = []
|
|
const listeners = new Map()
|
|
const proxies = []
|
|
const emitted = []
|
|
const timers = new Map()
|
|
const frames = new Map()
|
|
const cancelled = []
|
|
let nextTimer = 0
|
|
let nextFrame = settings.firstFrame ?? 0
|
|
const context = {
|
|
drawImage(...args) {
|
|
draws.push(args)
|
|
settings.onDraw?.(...args)
|
|
},
|
|
}
|
|
const canvas = {
|
|
nodeName: 'CANVAS',
|
|
width: 300,
|
|
height: 150,
|
|
title: 'canvas-title',
|
|
style: {},
|
|
getContext(kind) { return kind === '2d' && !settings.noContext ? context : null },
|
|
toDataURL(...args) {
|
|
calls.push({ name: 'toDataURL', receiver: this, args })
|
|
return 'data:canvas'
|
|
},
|
|
addEventListener(...args) { calls.push({ name: 'canvas-listener', receiver: this, args }) },
|
|
}
|
|
const video = {
|
|
nodeName: 'VIDEO',
|
|
videoWidth: 320,
|
|
videoHeight: 180,
|
|
width: 320,
|
|
height: 180,
|
|
title: 'video-title',
|
|
src: '',
|
|
currentTime: 0,
|
|
paused: true,
|
|
muted: false,
|
|
style: {},
|
|
play(...args) {
|
|
calls.push({ name: 'play', receiver: this, args })
|
|
this.paused = false
|
|
return Promise.resolve()
|
|
},
|
|
pause(...args) {
|
|
calls.push({ name: 'pause', receiver: this, args })
|
|
this.paused = true
|
|
},
|
|
addEventListener(...args) { calls.push({ name: 'video-listener', receiver: this, args }) },
|
|
}
|
|
const art = {
|
|
constructor: { config: { events: ['loadedmetadata', 'play', 'pause', 'seeked', 'error'] }, utils: { createElement: tag => tag === 'canvas' ? canvas : video } },
|
|
option: { autoSize: false },
|
|
template: { $player: { clientWidth: 640, clientHeight: 480 } },
|
|
isDestroy: false,
|
|
on(name, callback) {
|
|
if (!listeners.has(name))
|
|
listeners.set(name, new Set())
|
|
listeners.get(name).add(callback)
|
|
return art
|
|
},
|
|
off(name, callback) {
|
|
listeners.get(name)?.delete(callback)
|
|
return art
|
|
},
|
|
emit(name, ...args) {
|
|
emitted.push({ name, args })
|
|
for (const callback of [...(listeners.get(name) || [])]) callback(...args)
|
|
return art
|
|
},
|
|
proxy(target, name, callback) {
|
|
const entry = { target, name, callback }
|
|
proxies.push(entry)
|
|
return () => proxies.splice(proxies.indexOf(entry), 1)
|
|
},
|
|
destroy() {
|
|
art.isDestroy = true
|
|
art.emit('destroy')
|
|
},
|
|
}
|
|
const source = implementation.format === 'source' ? (await transform(implementation.source, { format: 'cjs', target: 'es2020' })).code : implementation.source
|
|
const module = { exports: {} }
|
|
vm.runInNewContext(source, {
|
|
module,
|
|
exports: module.exports,
|
|
window: {},
|
|
createImageBitmap: settings.createImageBitmap,
|
|
setTimeout(callback) {
|
|
const id = nextTimer++
|
|
timers.set(id, callback)
|
|
return id
|
|
},
|
|
clearTimeout(id) { timers.delete(id) },
|
|
requestAnimationFrame(callback) {
|
|
const id = nextFrame++
|
|
frames.set(id, callback)
|
|
return id
|
|
},
|
|
cancelAnimationFrame(id) {
|
|
cancelled.push(id)
|
|
frames.delete(id)
|
|
},
|
|
}, { timeout: 5000 })
|
|
return {
|
|
factory: module.exports.default || module.exports,
|
|
art,
|
|
canvas,
|
|
video,
|
|
context,
|
|
calls,
|
|
draws,
|
|
listeners,
|
|
proxies,
|
|
emitted,
|
|
timers,
|
|
frames,
|
|
cancelled,
|
|
flushTimers() {
|
|
const pending = [...timers.values()]
|
|
timers.clear()
|
|
for (const callback of pending) callback()
|
|
},
|
|
emitVideo(event) {
|
|
for (const entry of [...proxies]) {
|
|
if (entry.target === video && entry.name === event.type)
|
|
entry.callback(event)
|
|
}
|
|
},
|
|
async flush() {
|
|
// Resolve only the controlled draw/animation promise chain, without wall-clock sleeps.
|
|
await Promise.resolve()
|
|
await Promise.resolve()
|
|
await Promise.resolve()
|
|
},
|
|
}
|
|
}
|