Files
ArtPlayer/test/helpers/vtt-thumbnail.js

146 lines
6.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
import process from 'node:process'
import vm from 'node:vm'
import { transform } from 'esbuild'
import { readMember } from '../../refactor/scripts/releases.mjs'
import { verifyVttThumbnailContract } from '../../refactor/scripts/vtt-thumbnail-contract.mjs'
import { browserCandidate } from './browser-candidate.js'
export async function vttThumbnailCandidate() {
if (process.env.ARTPLAYER_VTT_THUMBNAIL_BASELINE === '1') {
assert(!process.env.ARTPLAYER_BROWSER_ARTIFACTS, 'Installed VTT thumbnail checks cannot use the frozen workspace')
return (await vttThumbnailHistorical()).find(item => item.name === 'frozen-workspace-js')
}
return { name: 'candidate', profile: 'current', ...await browserCandidate('artplayer-plugin-vtt-thumbnail', process.env.ARTPLAYER_VTT_THUMBNAIL_ARTIFACT) }
}
export async function vttThumbnailHistorical() {
const { baseline, archives, sources } = await verifyVttThumbnailContract()
const name = 'artplayer-plugin-vtt-thumbnail'
const implementations = []
for (const release of [baseline.release, ...baseline.previous]) {
if (release.version === '1.0.0')
continue
for (const field of ['main', 'legacy']) {
implementations.push({ name: `published-${release.version}-${field}`, version: release.version, profile: release.version === '1.0.1' ? 'old-control' : release.version === '1.0.2' ? 'no-class' : 'current', code: readMember(archives.get(release.version), `package/${release.manifest[field].replace(/^\.\//, '')}`).toString() })
}
}
for (const version of ['1.0.0', 'workspace']) {
const source = file => version === 'workspace' ? sources.get(`packages/${name}/src/${file}`) : readMember(archives.get(version), `package/src/${file}`).toString()
const code = `${source('getVttArray.js').replace('export default ', '')}\n${source('index.js').replace(/^import getVttArray from ['"].\/getVttArray['"];?\s*$/m, '')}`
implementations.push({ name: version === 'workspace' ? 'frozen-workspace-source' : 'published-1.0.0-source-only', version, profile: version === '1.0.0' ? 'mouse-source-only' : 'current', code: (await transform(code, { format: 'cjs', target: 'es2020' })).code })
}
for (const suffix of ['js', 'legacy.js'])
implementations.push({ name: `frozen-workspace-${suffix}`, version: 'workspace', profile: 'current', code: sources.get(`packages/${name}/dist/${name}.${suffix}`) })
return implementations
}
export const vttText = 'WEBVTT\n\n00:00:00.900 --> 00:00:05.900\nsheet.jpg#xywh=10,20,80,45\n\n00:00:05.100 --> 00:00:10.999\nsecond.jpg#xywh=90,65,80,45\n'
// Only the 1.0.1–1.0.3 compiled artifacts require compact cue arrows.
export function acceptedVttText(implementation, text = vttText) {
return /^published-1\.0\.[123]-(?:main|legacy)$/.test(implementation.name)
? text.replaceAll(' --> ', '-->')
: text
}
export function vttThumbnailEnvironment(implementation, { script = false, mobile = false, text = vttText, deferred = false } = {}) {
const requests = []
const controls = []
const listeners = new Map()
const proxies = new Map()
const timers = new Map()
const classes = new Set()
const styles = {}
const removed = []
const warnings = []
let nextTimer = 0
let resolveFetch
let rejectFetch
const response = source => ({ ok: true, status: 200, text: async () => source })
const pending = deferred
? new Promise((resolve, reject) => {
resolveFetch = resolve
rejectFetch = reject
})
: null
const progress = { clientWidth: 200, getBoundingClientRect: () => ({ left: 20 }) }
const control = { styles }
const art = {
constructor: { utils: {
isMobile: mobile,
clamp: (value, min, max) => Math.min(max, Math.max(min, value)),
setStyle(element, key, value) { element.styles[key] = value },
addClass(element, name) { classes.add(name) },
} },
duration: 10,
template: { $progress: progress },
events: { proxy(element, name, callback) {
if (!proxies.has(name))
proxies.set(name, [])
proxies.get(name).push(callback)
} },
controls: { add(option) {
controls.push(option)
this[option.name] = control
option.mounted(control)
return control
}, remove(name) {
removed.push(name)
delete this[name]
} },
on(name, callback) {
if (!listeners.has(name))
listeners.set(name, [])
listeners.get(name).push(callback)
return this
},
off(name, callback) { listeners.set(name, (listeners.get(name) || []).filter(item => item !== callback)) },
}
const box = {
window: {},
AbortController,
console: { warn(...args) { warnings.push(args) } },
fetch(...args) {
requests.push(args)
return pending || Promise.resolve(response(text))
},
setTimeout(callback, delay) {
const id = nextTimer++
timers.set(id, { callback, delay })
return id
},
clearTimeout(id) { timers.delete(id) },
}
const module = { exports: {} }
if (!script)
Object.assign(box, { module, exports: module.exports })
vm.runInNewContext(implementation.code, box)
const exported = script ? box.artplayerPluginVttThumbnail || box.window.artplayerPluginVttThumbnail : module.exports
const factory = typeof exported === 'function' ? exported : exported.default
return {
factory,
exported,
art,
box,
styles,
classes,
controls,
listeners,
proxies,
timers,
requests,
progress,
removed,
warnings,
resolve(source = text) { resolveFetch(response(source)) },
reject(error) { rejectFetch(error) },
async emit(name, ...args) { return Promise.all((listeners.get(name) || []).map(callback => callback(...args))) },
async hover(percentage, type = 'hover', event = {}) {
return Promise.all(implementation.profile === 'mouse-source-only'
? (proxies.get('mousemove') || []).map(callback => callback({ pageX: 20 + progress.clientWidth * percentage }))
: (listeners.get('setBar') || []).map(callback => callback(type, percentage, event)))
},
}
}