Files
ArtPlayer/test/helpers/thumbnail.js

114 lines
4.0 KiB
JavaScript

import fs from 'node:fs'
import process from 'node:process'
import vm from 'node:vm'
import { verifyThumbnailContract } from '../../refactor/scripts/thumbnail-contract.mjs'
import { compilePackage } from './load.js'
export async function thumbnailCandidate() {
if (process.env.ARTPLAYER_THUMBNAIL_BASELINE === '1')
return thumbnailHistorical().find(item => item.name === 'workspace.js')
return { name: process.env.ARTPLAYER_THUMBNAIL_ARTIFACT ? 'candidate-artifact' : 'candidate-source', code: process.env.ARTPLAYER_THUMBNAIL_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_THUMBNAIL_ARTIFACT, 'utf8') : await compilePackage('artplayer-tool-thumbnail', 'umd') }
}
export function thumbnailHistorical() {
const { source, historical } = verifyThumbnailContract()
return [
{ name: 'cdn-3.5.31', code: historical.get('packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js'), legacy: true },
...['.js', '.legacy.js'].map(suffix => ({ name: `workspace${suffix}`, code: source.get(`packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail${suffix}`), legacy: false })),
]
}
export function thumbnailEnvironment(implementation, script = false) {
const operations = []
const timers = new Map()
const blobs = []
const controls = { deferBlobs: false }
let nextTimer = 0
let nextUrl = 0
const context2D = Object.fromEntries(['fillRect', 'fillText', 'drawImage'].map(name => [name, (...args) => operations.push({ name, args })]))
class Element {
constructor(tagName = 'div') {
this.tagName = tagName.toUpperCase()
this.type = this.tagName === 'INPUT' ? 'file' : ''
this.style = {}
this.children = []
this.handlers = new Map()
this.files = []
if (this.tagName === 'VIDEO') {
this.duration = 0
this.videoWidth = 160
this.videoHeight = 90
this.currentTime = 0
this.canPlayType = () => 'probably'
}
}
appendChild(child) {
child.parentNode = this
this.children.push(child)
operations.push({ name: 'append', parent: this.tagName, child: child.tagName })
return child
}
removeChild(child) {
const index = this.children.indexOf(child)
if (index === -1)
throw new Error('NotFoundError')
this.children.splice(index, 1)
child.parentNode = null
return child
}
addEventListener(name, callback) {
operations.push({ name: 'listen', type: name, callback })
if (!callback)
return
const list = this.handlers.get(name) || new Set()
list.add(callback)
this.handlers.set(name, list)
}
removeEventListener(name, callback) { this.handlers.get(name)?.delete(callback) }
getContext() { return context2D }
click() { operations.push({ name: 'click', download: this.download, href: this.href }) }
toBlob(callback) {
if (controls.deferBlobs)
blobs.push(callback)
else
callback({ kind: 'image/png' })
}
}
const body = new Element('body')
const module = { exports: {} }
const box = {
Element,
document: { body, createElement: tag => new Element(tag) },
URL: {
createObjectURL(value) {
if (value == null)
throw new TypeError('Blob required')
const url = `blob:thumbnail-${++nextUrl}`
operations.push({ name: 'createURL', value, url })
return url
},
revokeObjectURL(url) { operations.push({ name: 'revokeURL', url }) },
},
setTimeout(callback, delay) {
const id = ++nextTimer
timers.set(id, { callback, delay })
return id
},
clearTimeout(id) { timers.delete(id) },
}
box.window = box
box.self = box
if (!script) {
box.module = module
box.exports = module.exports
}
vm.runInNewContext(implementation.code, box)
const exported = script ? box.ArtplayerToolThumbnail : module.exports
const Factory = exported.default || exported
return { box, Element, body, Factory, exported, operations, context2D, timers, blobs, controls }
}