jassub 1.7.1 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jassub-worker.js +15 -15
- package/dist/jassub-worker.wasm.js +1 -1
- package/dist/jassub.es.js +526 -488
- package/dist/jassub.umd.js +1 -1
- package/dist/js/jassub-worker-modern.js +10 -0
- package/dist/js/jassub-worker-modern.wasm +0 -0
- package/dist/js/jassub-worker.js +10 -0
- package/dist/js/jassub-worker.wasm +0 -0
- package/dist/js/jassub-worker.wasm.js +84 -0
- package/dist/js/jassub.js +799 -0
- package/package.json +8 -6
- package/src/jassub.js +79 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jassub",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "libass Subtitle Renderer and Parser library for browsers",
|
|
5
5
|
"main": "src/jassub.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,14 +29,16 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/ThaUnknown/jassub",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"rvfc-polyfill": "^1.0.
|
|
32
|
+
"rvfc-polyfill": "^1.0.6"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"vite
|
|
37
|
-
"
|
|
35
|
+
"terser": "^5.19.4",
|
|
36
|
+
"vite": "^4.4.9",
|
|
37
|
+
"vite-plugin-static-copy": "^0.17.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
|
-
"build": "node vite.build.js"
|
|
40
|
+
"build": "node vite.build.js",
|
|
41
|
+
"docker:build": "docker build -t thaunknown/jassub-build .",
|
|
42
|
+
"docker:run": "docker run -it --rm -v ${PWD}:/code --name thaunknown_jassub-build thaunknown/jassub-build:latest"
|
|
41
43
|
}
|
|
42
44
|
}
|
package/src/jassub.js
CHANGED
|
@@ -58,17 +58,16 @@ export default class JASSUB extends EventTarget {
|
|
|
58
58
|
* @param {Number} [options.libassMemoryLimit] libass bitmap cache memory limit in MiB (approximate).
|
|
59
59
|
* @param {Number} [options.libassGlyphLimit] libass glyph cache memory limit in MiB (approximate).
|
|
60
60
|
*/
|
|
61
|
-
constructor (options
|
|
61
|
+
constructor (options) {
|
|
62
62
|
super()
|
|
63
|
-
if (!globalThis.Worker)
|
|
64
|
-
|
|
65
|
-
}
|
|
63
|
+
if (!globalThis.Worker) throw this.destroy('Worker not supported')
|
|
64
|
+
if (!options) throw this.destroy('No options provided')
|
|
66
65
|
|
|
67
|
-
this._loaded = new Promise(resolve => {
|
|
66
|
+
this._loaded = /** @type {Promise<void>} */(new Promise(resolve => {
|
|
68
67
|
this._init = resolve
|
|
69
|
-
})
|
|
68
|
+
}))
|
|
70
69
|
|
|
71
|
-
JASSUB._test()
|
|
70
|
+
const test = JASSUB._test()
|
|
72
71
|
this._onDemandRender = 'requestVideoFrameCallback' in HTMLVideoElement.prototype && (options.onDemandRender ?? true)
|
|
73
72
|
|
|
74
73
|
// don't support offscreen rendering on custom canvases, as we can't replace it if colorSpace doesn't match
|
|
@@ -85,7 +84,7 @@ export default class JASSUB extends EventTarget {
|
|
|
85
84
|
this._canvasParent.className = 'JASSUB'
|
|
86
85
|
this._canvasParent.style.position = 'relative'
|
|
87
86
|
|
|
88
|
-
this._createCanvas()
|
|
87
|
+
this._canvas = this._createCanvas()
|
|
89
88
|
|
|
90
89
|
if (this._video.nextSibling) {
|
|
91
90
|
this._video.parentNode.insertBefore(this._canvasParent, this._video.nextSibling)
|
|
@@ -93,11 +92,12 @@ export default class JASSUB extends EventTarget {
|
|
|
93
92
|
this._video.parentNode.appendChild(this._canvasParent)
|
|
94
93
|
}
|
|
95
94
|
} else if (!this._canvas) {
|
|
96
|
-
this.destroy('Don\'t know where to render: you should give video or canvas in options.')
|
|
95
|
+
throw this.destroy('Don\'t know where to render: you should give video or canvas in options.')
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
this._bufferCanvas = document.createElement('canvas')
|
|
100
99
|
this._bufferCtx = this._bufferCanvas.getContext('2d')
|
|
100
|
+
if (!this._bufferCtx) throw this.destroy('Canvas rendering not supported')
|
|
101
101
|
|
|
102
102
|
this._canvasctrl = this._offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas
|
|
103
103
|
this._ctx = !this._offscreenRender && this._canvasctrl.getContext('2d')
|
|
@@ -124,29 +124,33 @@ export default class JASSUB extends EventTarget {
|
|
|
124
124
|
this._worker.onmessage = e => this._onmessage(e)
|
|
125
125
|
this._worker.onerror = e => this._error(e)
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
127
|
+
test.then(() => {
|
|
128
|
+
this._worker.postMessage({
|
|
129
|
+
target: 'init',
|
|
130
|
+
wasmUrl: JASSUB._supportsSIMD && options.modernWasmUrl ? options.modernWasmUrl : options.wasmUrl || 'jassub-worker.wasm',
|
|
131
|
+
legacyWasmUrl: options.legacyWasmUrl || 'jassub-worker.wasm.js',
|
|
132
|
+
asyncRender: typeof createImageBitmap !== 'undefined' && (options.asyncRender ?? true),
|
|
133
|
+
onDemandRender: this._onDemandRender,
|
|
134
|
+
width: this._canvasctrl.width || 0,
|
|
135
|
+
height: this._canvasctrl.height || 0,
|
|
136
|
+
blendMode: options.blendMode || 'js',
|
|
137
|
+
subUrl: options.subUrl,
|
|
138
|
+
subContent: options.subContent || null,
|
|
139
|
+
fonts: options.fonts || [],
|
|
140
|
+
availableFonts: options.availableFonts || { 'liberation sans': './default.woff2' },
|
|
141
|
+
fallbackFont: options.fallbackFont || 'liberation sans',
|
|
142
|
+
debug: this.debug,
|
|
143
|
+
targetFps: options.targetFps || 24,
|
|
144
|
+
dropAllAnimations: options.dropAllAnimations,
|
|
145
|
+
dropAllBlur: options.dropAllBlur,
|
|
146
|
+
libassMemoryLimit: options.libassMemoryLimit || 0,
|
|
147
|
+
libassGlyphLimit: options.libassGlyphLimit || 0,
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
useLocalFonts: typeof queryLocalFonts !== 'undefined' && (options.useLocalFonts ?? true),
|
|
150
|
+
hasBitmapBug: JASSUB._hasBitmapBug
|
|
151
|
+
})
|
|
152
|
+
if (this._offscreenRender === true) this.sendMessage('offscreenCanvas', null, [this._canvasctrl])
|
|
148
153
|
})
|
|
149
|
-
if (this._offscreenRender === true) this.sendMessage('offscreenCanvas', null, [this._canvasctrl])
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
_createCanvas () {
|
|
@@ -155,15 +159,21 @@ export default class JASSUB extends EventTarget {
|
|
|
155
159
|
this._canvas.style.position = 'absolute'
|
|
156
160
|
this._canvas.style.pointerEvents = 'none'
|
|
157
161
|
this._canvasParent.appendChild(this._canvas)
|
|
162
|
+
return this._canvas
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
// test support for WASM, ImageData, alphaBug, but only once, on init so it doesn't run when first running the page
|
|
166
|
+
|
|
167
|
+
/** @type {boolean|null} */
|
|
161
168
|
static _supportsSIMD = null
|
|
169
|
+
/** @type {boolean|null} */
|
|
162
170
|
static _hasAlphaBug = null
|
|
171
|
+
/** @type {boolean|null} */
|
|
172
|
+
static _hasBitmapBug = null
|
|
163
173
|
|
|
164
|
-
static _test () {
|
|
174
|
+
static async _test () {
|
|
165
175
|
// check if ran previously
|
|
166
|
-
if (JASSUB.
|
|
176
|
+
if (JASSUB._hasBitmapBug !== null) return null
|
|
167
177
|
|
|
168
178
|
try {
|
|
169
179
|
JASSUB._supportsSIMD = WebAssembly.validate(Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11))
|
|
@@ -173,6 +183,7 @@ export default class JASSUB extends EventTarget {
|
|
|
173
183
|
|
|
174
184
|
const canvas1 = document.createElement('canvas')
|
|
175
185
|
const ctx1 = canvas1.getContext('2d', { willReadFrequently: true })
|
|
186
|
+
if (!ctx1) throw new Error('Canvas rendering not supported')
|
|
176
187
|
// test ImageData constructor
|
|
177
188
|
if (typeof ImageData.prototype.constructor === 'function') {
|
|
178
189
|
try {
|
|
@@ -183,6 +194,7 @@ export default class JASSUB extends EventTarget {
|
|
|
183
194
|
} catch (e) {
|
|
184
195
|
console.log('Detected that ImageData is not constructable despite browser saying so')
|
|
185
196
|
|
|
197
|
+
// @ts-ignore
|
|
186
198
|
self.ImageData = function (data, width, height) {
|
|
187
199
|
const imageData = ctx1.createImageData(width, height)
|
|
188
200
|
if (data) imageData.data.set(data)
|
|
@@ -195,6 +207,7 @@ export default class JASSUB extends EventTarget {
|
|
|
195
207
|
// (with alpha == 0) as non-black which then leads to visual artifacts.
|
|
196
208
|
const canvas2 = document.createElement('canvas')
|
|
197
209
|
const ctx2 = canvas2.getContext('2d', { willReadFrequently: true })
|
|
210
|
+
if (!ctx2) throw new Error('Canvas rendering not supported')
|
|
198
211
|
|
|
199
212
|
canvas1.width = canvas2.width = 1
|
|
200
213
|
canvas1.height = canvas2.height = 1
|
|
@@ -206,6 +219,24 @@ export default class JASSUB extends EventTarget {
|
|
|
206
219
|
const postPut = ctx2.getImageData(0, 0, 1, 1).data
|
|
207
220
|
JASSUB._hasAlphaBug = prePut[1] !== postPut[1]
|
|
208
221
|
if (JASSUB._hasAlphaBug) console.log('Detected a browser having issue with transparent pixels, applying workaround')
|
|
222
|
+
|
|
223
|
+
if (typeof createImageBitmap !== 'undefined') {
|
|
224
|
+
const subarray = new Uint8ClampedArray([255, 0, 255, 0, 255]).subarray(1, 5)
|
|
225
|
+
ctx2.drawImage(await createImageBitmap(new ImageData(subarray, 1)), 0, 0)
|
|
226
|
+
const { data } = ctx2.getImageData(0, 0, 1, 1)
|
|
227
|
+
JASSUB._hasBitmapBug = false
|
|
228
|
+
for (const [i, number] of data.entries()) {
|
|
229
|
+
// realistically at most this will be a diff of 4, but just to be safe
|
|
230
|
+
if (Math.abs(subarray[i] - number) > 15) {
|
|
231
|
+
JASSUB._hasBitmapBug = true
|
|
232
|
+
console.log('Detected a browser having issue with partial bitmaps, applying workaround')
|
|
233
|
+
break
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
JASSUB._hasBitmapBug = false
|
|
238
|
+
}
|
|
239
|
+
|
|
209
240
|
canvas1.remove()
|
|
210
241
|
canvas2.remove()
|
|
211
242
|
}
|
|
@@ -443,7 +474,7 @@ export default class JASSUB extends EventTarget {
|
|
|
443
474
|
|
|
444
475
|
/**
|
|
445
476
|
* Get all ASS events.
|
|
446
|
-
* @param {function(Error|null, ASS_Event)} callback Function to callback when worker returns the events.
|
|
477
|
+
* @param {function(Error|null, ASS_Event): void} callback Function to callback when worker returns the events.
|
|
447
478
|
*/
|
|
448
479
|
getEvents (callback) {
|
|
449
480
|
this._fetchFromWorker({
|
|
@@ -485,7 +516,7 @@ export default class JASSUB extends EventTarget {
|
|
|
485
516
|
|
|
486
517
|
/**
|
|
487
518
|
* Create a new ASS style directly.
|
|
488
|
-
* @param {ASS_Style}
|
|
519
|
+
* @param {ASS_Style} style
|
|
489
520
|
*/
|
|
490
521
|
createStyle (style) {
|
|
491
522
|
this.sendMessage('createStyle', { style })
|
|
@@ -510,7 +541,7 @@ export default class JASSUB extends EventTarget {
|
|
|
510
541
|
|
|
511
542
|
/**
|
|
512
543
|
* Get all ASS styles.
|
|
513
|
-
* @param {function(Error|null, ASS_Style)} callback Function to callback when worker returns the styles.
|
|
544
|
+
* @param {function(Error|null, ASS_Style): void} callback Function to callback when worker returns the styles.
|
|
514
545
|
*/
|
|
515
546
|
getStyles (callback) {
|
|
516
547
|
this._fetchFromWorker({
|
|
@@ -530,6 +561,7 @@ export default class JASSUB extends EventTarget {
|
|
|
530
561
|
|
|
531
562
|
_sendLocalFont (name) {
|
|
532
563
|
try {
|
|
564
|
+
// @ts-ignore
|
|
533
565
|
queryLocalFonts().then(fontData => {
|
|
534
566
|
const font = fontData?.find(obj => obj.fullName.toLowerCase() === name)
|
|
535
567
|
if (font) {
|
|
@@ -550,6 +582,7 @@ export default class JASSUB extends EventTarget {
|
|
|
550
582
|
// electron by default has all permissions enabled, and it doesn't have perm query
|
|
551
583
|
// if this happens, just send it
|
|
552
584
|
if (navigator?.permissions?.query) {
|
|
585
|
+
// @ts-ignore
|
|
553
586
|
navigator.permissions.query({ name: 'local-fonts' }).then(permission => {
|
|
554
587
|
if (permission.state === 'granted') {
|
|
555
588
|
this._sendLocalFont(font)
|
|
@@ -634,8 +667,9 @@ export default class JASSUB extends EventTarget {
|
|
|
634
667
|
|
|
635
668
|
/**
|
|
636
669
|
* Veryify the color spaces for subtitles and videos, then apply filters to correct the color of subtitles.
|
|
637
|
-
* @param {
|
|
638
|
-
* @param {String}
|
|
670
|
+
* @param {Object} options
|
|
671
|
+
* @param {String} options.subtitleColorSpace Subtitle color space. One of: BT601 BT709 SMPTE240M FCC
|
|
672
|
+
* @param {String=} options.videoColorSpace Video color space. One of: BT601 BT709
|
|
639
673
|
*/
|
|
640
674
|
_verifyColorSpace ({ subtitleColorSpace, videoColorSpace = this._videoColorSpace }) {
|
|
641
675
|
if (!subtitleColorSpace || !videoColorSpace) return
|
|
@@ -767,6 +801,8 @@ export default class JASSUB extends EventTarget {
|
|
|
767
801
|
this.dispatchEvent(event)
|
|
768
802
|
|
|
769
803
|
console.error(error)
|
|
804
|
+
|
|
805
|
+
return error
|
|
770
806
|
}
|
|
771
807
|
|
|
772
808
|
_removeListeners () {
|
|
@@ -786,14 +822,15 @@ export default class JASSUB extends EventTarget {
|
|
|
786
822
|
|
|
787
823
|
/**
|
|
788
824
|
* Destroy the object, worker, listeners and all data.
|
|
789
|
-
* @param {String} [err] Error to throw when destroying.
|
|
825
|
+
* @param {String|Error} [err] Error to throw when destroying.
|
|
790
826
|
*/
|
|
791
827
|
destroy (err) {
|
|
792
|
-
if (err) this._error(err)
|
|
793
|
-
if (this._video && this._canvasParent) this._video.parentNode
|
|
828
|
+
if (err) err = this._error(err)
|
|
829
|
+
if (this._video && this._canvasParent) this._video.parentNode?.removeChild(this._canvasParent)
|
|
794
830
|
this._destroyed = true
|
|
795
831
|
this._removeListeners()
|
|
796
832
|
this.sendMessage('destroy')
|
|
797
|
-
this._worker
|
|
833
|
+
this._worker?.terminate()
|
|
834
|
+
return err
|
|
798
835
|
}
|
|
799
836
|
}
|