mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 03:46:16 -08:00
142 lines
4.9 KiB
JavaScript
142 lines
4.9 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'
|
|
|
|
export async function hlsImplementations() {
|
|
const { release } = JSON.parse(fs.readFileSync(new URL('../../refactor/baselines/hls-control-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])
|
|
let factory
|
|
if (format === 'module') {
|
|
factory = (await import(`data:text/javascript;base64,${Buffer.from(bytes).toString('base64')}`)).default
|
|
}
|
|
else {
|
|
const context = { module: { exports: {} } }
|
|
context.exports = context.module.exports
|
|
vm.runInNewContext(bytes.toString(), context, { timeout: 5000 })
|
|
factory = context.module.exports
|
|
}
|
|
assert.equal(typeof factory, 'function')
|
|
implementations.push({ name: `published-${format}`, factory })
|
|
}
|
|
for (const filename of (process.env.ARTPLAYER_TEST_HLS || '').split(path.delimiter).filter(Boolean)) {
|
|
const bytes = fs.readFileSync(filename)
|
|
let factory
|
|
if (filename.endsWith('.mjs')) {
|
|
factory = (await import(`data:text/javascript;base64,${bytes.toString('base64')}`)).default
|
|
}
|
|
else {
|
|
const context = { module: { exports: {} } }
|
|
context.exports = context.module.exports
|
|
vm.runInNewContext(bytes.toString(), context, { timeout: 5000 })
|
|
factory = context.module.exports
|
|
}
|
|
assert.equal(typeof factory, 'function')
|
|
implementations.push({ name: `artifact-${path.basename(filename)}`, factory })
|
|
}
|
|
return implementations
|
|
}
|
|
|
|
// Controlled SDK and registry host: no DOM rendering, decoding or ABR simulation.
|
|
export function hlsHost() {
|
|
const listeners = new Map()
|
|
const calls = []
|
|
const controls = new Map()
|
|
const settings = new Map()
|
|
const video = {}
|
|
const art = {
|
|
template: { $video: video },
|
|
constructor: { utils: { errorHandle: (condition, message) => {
|
|
if (!condition)
|
|
throw new Error(message)
|
|
} } },
|
|
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
|
|
},
|
|
notice: {},
|
|
}
|
|
let notice = ''
|
|
Object.defineProperty(art.notice, 'show', {
|
|
get: () => notice,
|
|
set: (value) => {
|
|
calls.push(['notice', value])
|
|
notice = value
|
|
},
|
|
})
|
|
for (const [name, entries] of [['controls', controls], ['setting', settings]]) {
|
|
art[name] = {
|
|
update(item) {
|
|
calls.push([`${name}.update`, item.name])
|
|
entries.set(item.name, item)
|
|
},
|
|
remove(nameToRemove) {
|
|
calls.push([`${name}.remove`, nameToRemove])
|
|
entries.delete(nameToRemove)
|
|
},
|
|
check(item) { calls.push([`${name}.check`, item]) },
|
|
}
|
|
}
|
|
const hls = {
|
|
media: video,
|
|
levels: [{ height: 360 }, { height: 720 }, { height: 1080 }],
|
|
audioTracks: [{ id: 0, name: 'English' }, { id: 1, lang: 'fr' }],
|
|
}
|
|
for (const [key, initial] of [['currentLevel', -1], ['audioTrack', 0]]) {
|
|
let value = initial
|
|
Object.defineProperty(hls, key, {
|
|
configurable: true,
|
|
get: () => value,
|
|
set: (next) => {
|
|
calls.push([key, next])
|
|
value = next
|
|
},
|
|
})
|
|
}
|
|
art.hls = hls
|
|
return { art, hls, calls, controls, settings, listeners }
|
|
}
|
|
|
|
export const bothMenus = () => ({ quality: { control: true, setting: true }, audio: { control: true, setting: true } })
|
|
export const selections = menu => Array.from(menu.selector, item => [item.html, item.value, item.default])
|
|
|
|
export function attachHlsEvents(hls) {
|
|
const listeners = new Map()
|
|
const Events = Object.fromEntries(['MANIFEST_PARSED', 'LEVELS_UPDATED', 'LEVEL_SWITCHED', 'AUDIO_TRACKS_UPDATED', 'AUDIO_TRACK_SWITCHED', 'DESTROYING'].map(key => [key, key]))
|
|
hls.constructor = { Events }
|
|
hls.on = (name, callback) => {
|
|
if (!listeners.has(name))
|
|
listeners.set(name, new Set())
|
|
listeners.get(name).add(callback)
|
|
}
|
|
hls.off = (name, callback) => listeners.get(name)?.delete(callback)
|
|
return {
|
|
listeners,
|
|
emit(name) {
|
|
for (const callback of [...(listeners.get(name) || [])])
|
|
callback(name, {})
|
|
},
|
|
count: () => [...listeners.values()].reduce((total, entries) => total + entries.size, 0),
|
|
}
|
|
}
|