feat(subtitle): add artplayer-plugin-libass

This commit is contained in:
journey-ad committed 2023-11-14 12:36:35 +08:00
1 parent cf8cea8283
commit 08ec5716f9
17 files changed
+2450

No files matched your search

@@ -0,0 +1,222 @@
import SubtitlesOctopus from "libass-wasm"
const defaultAssSubtitle = `[Script Info]\nScriptType: v4.00+`
export default class LibassAdapter {
constructor(art, option) {
const { constructor, template } = art
this.art = art
this.$video = template.$video
this.$webvtt = template.$subtitle
this.utils = constructor.utils
this.libass = null
art.once("ready", this.init.bind(this, option))
}
async init(option) {
this.#checkWebAssemblySupport()
await this.#createLibass(option)
this.#addEventListeners()
this.art.emit("artplayerPluginLibass:init", this)
// set initial subtitle
const initialSubtitle = this.art?.option?.subtitle?.url
if (initialSubtitle && this.utils.getExt(initialSubtitle) === "ass") {
this.switch(initialSubtitle)
}
}
switch(url) {
this.art.emit("artplayerPluginLibass:switch", url)
if (url && this.utils.getExt(url) === "ass") {
this.currentType = "ass"
this.libass.freeTrack()
this.libass.setTrackByUrl(this.#toAbsoluteUrl(url))
this.visible = this.art.subtitle.show
} else {
this.currentType = "webvtt"
this.hide()
this.libass.freeTrack()
}
}
setVisibility(visible) {
this.visible = visible
}
setOffset(offset) {
this.timeOffset = offset
}
get active() {
return this.currentType === 'ass'
}
get visible() {
if (!this.libass)
return false
return this.libass.canvasParent.style.display !== "none"
}
set visible(visible) {
this.art.emit("artplayerPluginLibass:visible", visible)
this.#setVttVisible(!this.active)
if (this.libass.canvasParent) {
this.libass.canvasParent.style.display = visible ? "block" : "none"
if (visible) this.libass.resize()
}
}
get timeOffset() {
return this.libass.timeOffset
}
set timeOffset(offset) {
this.art.emit("artplayerPluginLibass:timeOffset", offset)
this.libass.timeOffset = offset
}
show() {
this.visible = true
}
hide() {
this.visible = false
}
destroy() {
this.art.emit("artplayerPluginLibass:destroy")
this.#removeEventListeners()
this.libass.dispose()
URL.revokeObjectURL(this.workerScriptUrl)
this.libass = null
}
async #createLibass(option = {}) {
if (!option.fallbackFont) {
return this.utils.errorHandle(option.fallbackFont, 'artplayerPluginLibass: fallbackFont is required')
}
if (!option.workerUrl) {
option.workerUrl = 'https://cdnjs.cloudflare.com/ajax/libs/libass-wasm/4.1.0/js/subtitles-octopus-worker.js'
}
if (option.availableFonts) {
option.availableFonts = Object
.entries(option.availableFonts)
.reduce((acc, [key, value]) => {
acc[key] = this.#toAbsoluteUrl(value)
return acc
}, {})
}
this.libass = new SubtitlesOctopus({
subContent: defaultAssSubtitle,
video: this.$video,
...option,
workerUrl: await this.#loadWorker(option),
fallbackFont: this.#toAbsoluteUrl(option.fallbackFont),
fonts: option.fonts?.map((font) => this.#toAbsoluteUrl(font)),
})
this.libass.canvasParent.className = "artplayer-plugin-libass"
this.libass.canvasParent.style.cssText = `
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
user-select: none;
pointer-events: none;
z-index: 20;`
}
#addEventListeners() {
this.switchHandler = this.switch.bind(this)
this.visibleHandler = this.setVisibility.bind(this)
this.offsetHandler = this.setOffset.bind(this)
this.art.on("subtitle", this.visibleHandler)
this.art.on('subtitleLoad', this.switchHandler)
this.art.on("subtitleOffset", this.offsetHandler)
this.art.once("destroy", this.destroy.bind(this))
}
#removeEventListeners() {
this.art.off("subtitle", this.visibleHandler)
this.art.off('subtitleLoad', this.switchHandler)
this.art.off("subtitleOffset", this.offsetHandler)
}
#setVttVisible(visible) {
this.$webvtt.style.visibility = visible ? 'visible' : 'hidden'
}
#toAbsoluteUrl(url) {
if (this.#isAbsoluteUrl(url))
return url
// handle absolute URL when the `Worker` of `BLOB` type loading network resources
return new URL(url, document.baseURI).toString()
}
#isAbsoluteUrl(url) {
return /^https?:\/\//.test(url)
}
#loadWorker({ workerUrl, wasmUrl }) {
return new Promise((resolve) => {
fetch(workerUrl)
.then(res => res.text())
.then(text => {
let workerScriptContent = text
workerScriptContent = workerScriptContent.replace(
/wasmBinaryFile\s*=\s*"(subtitles-octopus-worker\.wasm)"/g,
(_match, wasm) => {
if (!wasmUrl) {
wasmUrl = new URL(wasm, workerUrl).toString()
} else {
wasmUrl = this.#toAbsoluteUrl(wasmUrl)
}
return `wasmBinaryFile = "${wasmUrl}"`
}
)
const workerBlob = new Blob([workerScriptContent], { type: "text/javascript" })
resolve(URL.createObjectURL(workerBlob))
})
})
}
#checkWebAssemblySupport() {
let supportsWebAssembly = false
try {
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00))
if (module instanceof WebAssembly.Module)
supportsWebAssembly = (new WebAssembly.Instance(module) instanceof WebAssembly.Instance)
}
} catch (e) {
//
}
this.utils.errorHandle(supportsWebAssembly, "Browser does not support WebAssembly")
}
}
@@ -0,0 +1,42 @@
import LibassAdapter from './adapter'
function checkVersion(art) {
const {
version,
utils: { errorHandle },
} = art.constructor
const arr = version.split('.').map(Number)
const major = arr[0]
const minor = arr[1] / 100
errorHandle(
major + minor >= 5,
`Artplayer.js@${version} is not compatible the artplayerPluginLibass@${artplayerPluginLibass.version}. Please update it to version Artplayer.js@5.x.x`,
)
}
export default function artplayerPluginLibass(option) {
return (art) => {
checkVersion(art)
let adapter = new LibassAdapter(art, option)
return {
name: 'artplayerPluginLibass',
libass: adapter.libass,
visible: adapter.visible,
init: adapter.init.bind(adapter),
switch: adapter.switch.bind(adapter),
show: adapter.show.bind(adapter),
hide: adapter.hide.bind(adapter),
destroy: adapter.destroy.bind(adapter),
}
}
}
artplayerPluginLibass.env = process.env.NODE_ENV
artplayerPluginLibass.version = process.env.APP_VER
artplayerPluginLibass.build = process.env.BUILD_DATE
if (typeof window !== 'undefined') {
window['artplayerPluginLibass'] = artplayerPluginLibass
}