mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
111 lines
3.8 KiB
JavaScript
111 lines
3.8 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { Buffer } from 'node:buffer'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import process from 'node:process'
|
|
import vm from 'node:vm'
|
|
import { ensureArchive, hash, readMember } from '../../refactor/scripts/releases.mjs'
|
|
import { loadPackage } from './load.js'
|
|
|
|
async function readFactory(bytes, esm) {
|
|
if (esm)
|
|
return (await import(`data:text/javascript;base64,${Buffer.from(bytes).toString('base64')}`)).default
|
|
const context = {
|
|
module: { exports: {} },
|
|
Audio: function Audio() {
|
|
return new globalThis.Audio()
|
|
},
|
|
console,
|
|
}
|
|
context.exports = context.module.exports
|
|
vm.runInNewContext(bytes.toString(), context, { timeout: 5000 })
|
|
return context.module.exports
|
|
}
|
|
|
|
export async function audioImplementations() {
|
|
const { release } = JSON.parse(fs.readFileSync(new URL('../../refactor/baselines/audio-track-release.json', import.meta.url)))
|
|
const archive = await ensureArchive(release)
|
|
const implementations = [{ name: 'source', factory: (await loadPackage(release.name)).default }]
|
|
for (const format of ['main', 'legacy', 'module']) {
|
|
const member = `package/${release.manifest[format].replace(/^\.\//, '')}`
|
|
const bytes = readMember(archive, member)
|
|
assert.equal(hash(bytes), release.files[member])
|
|
const factory = await readFactory(bytes, format === 'module')
|
|
implementations.push({ name: `published-${format}`, factory })
|
|
}
|
|
for (const file of (process.env.ARTPLAYER_TEST_AUDIO || '').split(path.delimiter).filter(Boolean))
|
|
implementations.push({ name: `artifact-${path.basename(file)}`, factory: await readFactory(fs.readFileSync(file), file.endsWith('.mjs')) })
|
|
return implementations
|
|
}
|
|
|
|
// Records plugin intent only. It does not decode media or emulate browser timing.
|
|
export function audioHost(t, initialize = () => {}) {
|
|
const instances = []
|
|
const warnings = []
|
|
let play = () => Promise.resolve()
|
|
class Audio {
|
|
constructor() {
|
|
instances.push(this)
|
|
this.calls = []
|
|
this.values = new Map()
|
|
this.paused = true
|
|
for (const [name, initial] of Object.entries({ src: '', preload: '', currentTime: 0, playbackRate: 1, volume: 1, muted: false })) {
|
|
this.values.set(name, initial)
|
|
Object.defineProperty(this, name, {
|
|
configurable: true,
|
|
get: () => this.values.get(name),
|
|
set: (next) => {
|
|
this.calls.push([name, next])
|
|
this.values.set(name, next)
|
|
},
|
|
})
|
|
}
|
|
initialize(this)
|
|
}
|
|
|
|
play() {
|
|
this.calls.push(['play'])
|
|
return play(this)
|
|
}
|
|
|
|
pause() { this.calls.push(['pause']) }
|
|
load() { this.calls.push(['load']) }
|
|
removeAttribute(name) {
|
|
this.calls.push(['removeAttribute', name])
|
|
this.values.set(name, '')
|
|
}
|
|
}
|
|
const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'Audio')
|
|
Object.defineProperty(globalThis, 'Audio', { configurable: true, value: Audio })
|
|
t.after(() => descriptor ? Object.defineProperty(globalThis, 'Audio', descriptor) : Reflect.deleteProperty(globalThis, 'Audio'))
|
|
t.mock.method(console, 'warn', error => warnings.push(error))
|
|
return { ...createAudioArt(), instances, warnings, setPlay: callback => play = callback }
|
|
}
|
|
|
|
export function createAudioArt() {
|
|
const listeners = new Map()
|
|
const art = {
|
|
video: { playbackRate: 1.25 },
|
|
currentTime: 0,
|
|
playing: false,
|
|
volume: 0.6,
|
|
muted: false,
|
|
on(event, callback) {
|
|
if (!listeners.has(event))
|
|
listeners.set(event, new Set())
|
|
listeners.get(event).add(callback)
|
|
return art
|
|
},
|
|
off(event, callback) {
|
|
listeners.get(event)?.delete(callback)
|
|
return art
|
|
},
|
|
emit(event, ...args) {
|
|
for (const callback of [...(listeners.get(event) || [])])
|
|
callback(...args)
|
|
return art
|
|
},
|
|
}
|
|
return { art, listeners }
|
|
}
|