akarisub 0.2.2 → 1.0.0
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/README.md +198 -167
- package/THIRD_PARTY_NOTICES.md +55 -163
- package/dist/COPYRIGHT +56 -164
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -147
- package/dist/index.js.map +1 -0
- package/dist/ts/{ts/akarisub.d.ts → akarisub.d.ts} +80 -99
- package/dist/ts/akarisub.d.ts.map +1 -0
- package/dist/ts/akarisub.js +2086 -0
- package/dist/ts/akarisub.js.map +1 -0
- package/dist/ts/timing.d.ts +63 -0
- package/dist/ts/timing.d.ts.map +1 -0
- package/dist/ts/timing.js +219 -0
- package/dist/ts/timing.js.map +1 -0
- package/dist/ts/{ts/types.d.ts → types.d.ts} +103 -78
- package/dist/ts/types.d.ts.map +1 -0
- package/dist/ts/types.js +2 -0
- package/dist/ts/{ts/types.js.map → types.js.map} +1 -1
- package/dist/ts/{ts/utils.d.ts → utils.d.ts} +0 -31
- package/dist/ts/utils.d.ts.map +1 -0
- package/dist/ts/{ts/utils.js → utils.js} +0 -56
- package/dist/ts/utils.js.map +1 -0
- package/dist/ts/wasm.d.ts +12 -0
- package/dist/ts/wasm.d.ts.map +1 -0
- package/dist/ts/wasm.js +46 -0
- package/dist/ts/wasm.js.map +1 -0
- package/dist/ts/{ts/webgl2-renderer.d.ts → webgl2-renderer.d.ts} +0 -12
- package/dist/ts/webgl2-renderer.d.ts.map +1 -0
- package/dist/ts/{ts/webgl2-renderer.js → webgl2-renderer.js} +0 -27
- package/dist/ts/webgl2-renderer.js.map +1 -0
- package/dist/ts/{ts/webgpu-renderer.d.ts → webgpu-renderer.d.ts} +17 -16
- package/dist/ts/webgpu-renderer.d.ts.map +1 -0
- package/dist/ts/{ts/webgpu-renderer.js → webgpu-renderer.js} +148 -71
- package/dist/ts/webgpu-renderer.js.map +1 -0
- package/dist/ts/worker.d.ts +2 -0
- package/dist/ts/{ts/worker.d.ts.map → worker.d.ts.map} +1 -1
- package/dist/ts/{ts/worker.js → worker.js} +1064 -264
- package/dist/ts/worker.js.map +1 -0
- package/dist/wrapper.d.ts +8 -0
- package/dist/wrapper.d.ts.map +1 -0
- package/dist/{ts/index.js → wrapper.js} +4 -14
- package/dist/wrapper.js.map +1 -0
- package/package.json +24 -22
- package/pkg/akarisub.js +53 -0
- package/{dist/akarisub-worker.wasm → pkg/akarisub.wasm} +0 -0
- package/src/wrapper.ts +46 -0
- package/dist/akarisub-worker.js +0 -39
- package/dist/akarisub.umd.js +0 -159
- package/dist/ts/index.d.ts +0 -14
- package/dist/ts/index.d.ts.map +0 -1
- package/dist/ts/index.js.map +0 -1
- package/dist/ts/ts/akarisub.d.ts.map +0 -1
- package/dist/ts/ts/akarisub.js +0 -1073
- package/dist/ts/ts/akarisub.js.map +0 -1
- package/dist/ts/ts/types.d.ts.map +0 -1
- package/dist/ts/ts/types.js +0 -5
- package/dist/ts/ts/utils.d.ts.map +0 -1
- package/dist/ts/ts/utils.js.map +0 -1
- package/dist/ts/ts/webgl2-renderer.d.ts.map +0 -1
- package/dist/ts/ts/webgl2-renderer.js.map +0 -1
- package/dist/ts/ts/webgpu-renderer.d.ts.map +0 -1
- package/dist/ts/ts/webgpu-renderer.js.map +0 -1
- package/dist/ts/ts/worker.d.ts +0 -6
- package/dist/ts/ts/worker.js.map +0 -1
- package/dist/ts/wrapper.d.ts +0 -8
- package/dist/ts/wrapper.d.ts.map +0 -1
- package/dist/ts/wrapper.js +0 -9
- package/dist/ts/wrapper.js.map +0 -1
- package/src/ts/akarisub.ts +0 -1272
- package/src/ts/types.ts +0 -397
- package/src/ts/utils.ts +0 -512
- package/src/ts/webgl2-renderer.ts +0 -426
- package/src/ts/webgpu-renderer.ts +0 -691
- package/src/ts/worker.ts +0 -2052
- /package/{dist → assets}/default.woff2 +0 -0
package/README.md
CHANGED
|
@@ -49,10 +49,10 @@ deno add jsr:@altq/akarisub
|
|
|
49
49
|
|
|
50
50
|
## Usage
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
In most bundler-based projects, no manual worker setup is required. AkariSub resolves the WASM glue and binary relative to the package module URL, so bundlers such as Vite, webpack, and Rollup can emit the assets automatically.
|
|
53
53
|
|
|
54
54
|
```js
|
|
55
|
-
import AkariSub from '
|
|
55
|
+
import AkariSub from 'akarisub'
|
|
56
56
|
|
|
57
57
|
const renderer = new AkariSub({
|
|
58
58
|
video: document.querySelector('video'),
|
|
@@ -60,29 +60,21 @@ const renderer = new AkariSub({
|
|
|
60
60
|
})
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
If you use a bundler like Vite, you can instead do:
|
|
63
|
+
If your app serves package files in a way that does not expose those emitted assets to the browser, you can still provide the public fallback by copying the WASM file and its JS glue to `/akarisub/`:
|
|
66
64
|
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
import wasmUrl from 'akarisub/worker.wasm?url'
|
|
71
|
-
|
|
72
|
-
const renderer = new AkariSub({
|
|
73
|
-
video: document.querySelector('video'),
|
|
74
|
-
subContent: subtitleString,
|
|
75
|
-
workerUrl,
|
|
76
|
-
wasmUrl
|
|
77
|
-
})
|
|
65
|
+
```bash
|
|
66
|
+
mkdir -p public/akarisub
|
|
67
|
+
cp node_modules/akarisub/pkg/akarisub.wasm node_modules/akarisub/pkg/akarisub.js public/akarisub/
|
|
78
68
|
```
|
|
79
69
|
|
|
70
|
+
The worker is created from the package module URL. `workerUrl`, `wasmUrl`, and `glueUrl` remain in the option type for overrides and do not need to be set in bundler-based apps.
|
|
71
|
+
|
|
80
72
|
## Using only with canvas
|
|
81
73
|
|
|
82
74
|
You're also able to use it without any video. However, that requires you to set the time the subtitles should render at yourself. Disable `onDemandRender` (it relies on video frame callbacks) and drive the clock manually:
|
|
83
75
|
|
|
84
76
|
```js
|
|
85
|
-
import AkariSub from '
|
|
77
|
+
import AkariSub from 'akarisub'
|
|
86
78
|
|
|
87
79
|
const renderer = new AkariSub({
|
|
88
80
|
canvas: document.querySelector('canvas'),
|
|
@@ -94,6 +86,36 @@ const renderer = new AkariSub({
|
|
|
94
86
|
renderer.setCurrentTime(true, 15)
|
|
95
87
|
```
|
|
96
88
|
|
|
89
|
+
Custom canvases stay on the main thread by default. Set both `offscreenRender: true` and `rawAssImageGpu: true` to opt into worker WebGL2 raw-mask composition when dense overlays benefit from it.
|
|
90
|
+
|
|
91
|
+
### Timing semantics
|
|
92
|
+
|
|
93
|
+
AkariSub sends libass an integer millisecond timestamp. Fractional media times are floored to the current millisecond, matching libass's `Start <= now < Start + Duration` event boundaries. `timeOffset` and `renderAhead` are measured in seconds.
|
|
94
|
+
|
|
95
|
+
By default, `adaptiveTiming` predicts the media time at which a completed canvas paint will become visible, compensating bounded rendering and delivery latency. Pause and seek frames always use the exact media timestamp. Set `adaptiveTiming: false` when deterministic frame sampling is more important than compensating live presentation latency.
|
|
96
|
+
|
|
97
|
+
For frame-locked VOD playback, provide the encoded video's presentation timestamps in seconds. AkariSub prepares a small window of full subtitle bitmaps and commits the matching bitmap inside `requestVideoFrameCallback`, which keeps fast `\t` and `\move` animation sampling aligned with libass:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
const frameTimeline = new Float64Array([0, 0.041708, 0.083417])
|
|
101
|
+
const renderer = new AkariSub({
|
|
102
|
+
video,
|
|
103
|
+
subContent,
|
|
104
|
+
frameTimeline,
|
|
105
|
+
framePrefetch: 2
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// Timelines can also arrive after renderer construction.
|
|
109
|
+
renderer.setFrameTimeline(frameTimeline)
|
|
110
|
+
renderer.setFrameTimeline(null) // return to adaptive continuous-time rendering
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use timestamps from the encoded rendition the browser plays, normalized to the browser media clock. Passing a timeline remains optional; cache misses and unsupported paths automatically use normal adaptive rendering.
|
|
114
|
+
|
|
115
|
+
If the browser timeline is normalized from decode time rather than the first display PTS, preserve that initial B-frame reorder gap in the array and set `frameTimeline.subtitleTimeOffset` to the first frame timestamp. AkariSub uses the unmodified array to locate the visible video frame, then removes the offset only when sampling libass.
|
|
116
|
+
|
|
117
|
+
`subtitleTimeOffset` is signed. When the source video starts after the container subtitle clock, subtract that source lead from the reorder offset. For example, an encoded reorder gap of `0.083422` seconds and a source video start of `0.007` seconds use `subtitleTimeOffset = 0.076422`, so the first displayed frame is sampled by libass at `0.007` just like mpv.
|
|
118
|
+
|
|
97
119
|
## Changing subtitles
|
|
98
120
|
|
|
99
121
|
You're not limited to only display the subtitle file you referenced in your options. You're able to dynamically change subtitles on the fly. There's four methods that you can use for this specifically:
|
|
@@ -133,6 +155,7 @@ console.log(stats)
|
|
|
133
155
|
// maxRenderTime: 8.32,
|
|
134
156
|
// minRenderTime: 0.12,
|
|
135
157
|
// lastRenderTime: 1.23,
|
|
158
|
+
// timingCompensationMs: 4.8,
|
|
136
159
|
// renderFps: 60,
|
|
137
160
|
// usingWorker: true,
|
|
138
161
|
// offscreenRender: true,
|
|
@@ -155,22 +178,23 @@ console.log(`Events: ${eventCount}, Styles: ${styleCount}`)
|
|
|
155
178
|
|
|
156
179
|
**Stats Reference:**
|
|
157
180
|
|
|
158
|
-
| Property
|
|
159
|
-
|
|
160
|
-
| `framesRendered`
|
|
161
|
-
| `framesDropped`
|
|
162
|
-
| `avgRenderTime`
|
|
163
|
-
| `maxRenderTime`
|
|
164
|
-
| `minRenderTime`
|
|
165
|
-
| `lastRenderTime`
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
181
|
+
| Property | Type | Description |
|
|
182
|
+
| ---------------------- | ------- | ----------------------------------------------------------------------- |
|
|
183
|
+
| `framesRendered` | number | Total frames rendered since reset |
|
|
184
|
+
| `framesDropped` | number | Frames dropped due to slow rendering |
|
|
185
|
+
| `avgRenderTime` | number | Average render time in milliseconds |
|
|
186
|
+
| `maxRenderTime` | number | Maximum render time in milliseconds |
|
|
187
|
+
| `minRenderTime` | number | Minimum render time in milliseconds |
|
|
188
|
+
| `lastRenderTime` | number | Most recent render time in milliseconds |
|
|
189
|
+
| `timingCompensationMs` | number | Automatically learned presentation-latency compensation in milliseconds |
|
|
190
|
+
| `renderFps` | number | Estimated render FPS based on timing |
|
|
191
|
+
| `usingWorker` | boolean | Whether using Web Worker |
|
|
192
|
+
| `offscreenRender` | boolean | Whether offscreen rendering is enabled |
|
|
193
|
+
| `onDemandRender` | boolean | Whether on-demand rendering is enabled |
|
|
194
|
+
| `pendingRenders` | number | Number of pending render operations |
|
|
195
|
+
| `totalEvents` | number | Total subtitle events in current track |
|
|
196
|
+
| `cacheHits` | number | Number of cache hits (unchanged frames) |
|
|
197
|
+
| `cacheMisses` | number | Number of cache misses (rendered frames) |
|
|
174
198
|
|
|
175
199
|
## GPU Rendering
|
|
176
200
|
|
|
@@ -198,173 +222,180 @@ if (renderer.isUsingGPURenderer) {
|
|
|
198
222
|
|
|
199
223
|
The default options are best, and automatically fallback to the next fastest options in line, when the API's they use are unsupported. You can however forcefully change this behavior by specifying options.
|
|
200
224
|
|
|
201
|
-
| Option
|
|
202
|
-
|
|
203
|
-
| `video`
|
|
204
|
-
| `canvas`
|
|
205
|
-
| `blendMode`
|
|
206
|
-
| `asyncRender`
|
|
207
|
-
| `offscreenRender`
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
218
|
-
| `
|
|
219
|
-
| `
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
225
|
+
| Option | Type | Default | Description |
|
|
226
|
+
| --------------------- | ------------------------------------ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
227
|
+
| `video` | HTMLVideoElement | - | Video to use as target for rendering and event listeners |
|
|
228
|
+
| `canvas` | HTMLCanvasElement | - | Canvas to use for manual handling (optional if video is provided) |
|
|
229
|
+
| `blendMode` | `'js'` \| `'wasm'` | `'wasm'` | Image blending mode. WASM is better for low-end devices, JS for hardware acceleration |
|
|
230
|
+
| `asyncRender` | boolean | auto | Render via ImageBitmap. Defaults to `true` on Canvas2D paths and `false` when a GPU renderer is active (raw buffers upload with fewer copies) or on WebKit |
|
|
231
|
+
| `offscreenRender` | boolean | automatic | Render fully on the worker; enabled for video-managed canvases and disabled for custom canvases |
|
|
232
|
+
| `rawAssImageGpu` | boolean | `false` | Compose raw libass masks with worker WebGL2 when offscreen rendering is active |
|
|
233
|
+
| `onDemandRender` | boolean | `true` | Render subtitles as the video player renders frames |
|
|
234
|
+
| `adaptiveTiming` | boolean | `true` | Compensate measured queue, worker, bitmap, IPC, and paint latency while video is playing; pause and seek renders remain frame-exact |
|
|
235
|
+
| `frameTimeline` | FrameTimeline | - | Encoded browser-frame timestamps plus optional media/subtitle clock offsets; enables frame-locked libass sampling |
|
|
236
|
+
| `framePrefetch` | number | `2` | Number of exact subtitle-frame bitmaps to prepare ahead (`0` disables preparation, maximum `24`) |
|
|
237
|
+
| `targetFps` | number | `24` | Target FPS when not using onDemandRender |
|
|
238
|
+
| `timeOffset` | number | `0` | Subtitle time offset in seconds |
|
|
239
|
+
| `debug` | boolean | `false` | Enable debug logging |
|
|
240
|
+
| `prescaleFactor` | number | `1.0` | Scale factor for subtitles canvas |
|
|
241
|
+
| `prescaleHeightLimit` | number | `1080` | Height limit for prescaling in pixels |
|
|
242
|
+
| `maxRenderHeight` | number | `0` | Maximum render height (0 = no limit) |
|
|
243
|
+
| `dropAllAnimations` | boolean | `false` | Discard all animated tags for performance |
|
|
244
|
+
| `dropAllBlur` | boolean | `false` | Drop all blur effects (~10x performance gain) |
|
|
245
|
+
| `clampPos` | boolean | `false` | Clamp `\pos` values to script resolution |
|
|
246
|
+
| `renderAhead` | number | `0` | Optional extra seconds to render ahead, in addition to adaptive timing; also applies when `onDemandRender` is disabled |
|
|
247
|
+
| `workerUrl` | string | package worker URL | Optional worker script URL. Defaults to the package worker module URL |
|
|
248
|
+
| `wasmUrl` | string | package WASM URL | Optional WASM binary URL. Defaults to the URL resolved from `import.meta.url` |
|
|
249
|
+
| `glueUrl` | string | package glue URL | Optional WASM glue script URL. Defaults to the URL resolved from `import.meta.url` |
|
|
250
|
+
| `subUrl` | string | - | URL of the subtitle file to play |
|
|
251
|
+
| `subContent` | string \| Uint8Array \| ArrayBuffer | - | Content of the subtitle file to play |
|
|
252
|
+
| `encryptedSubContent` | EncryptedSubtitleContent | - | AES-GCM encrypted subtitle payload, decrypted inside the worker |
|
|
253
|
+
| `fonts` | (string \| Uint8Array)[] | - | Array of font URLs or Uint8Arrays to force load |
|
|
254
|
+
| `availableFonts` | Record<string, string \| Uint8Array> | liberation sans from package assets | Available fonts map (lowercase name → URL/data) |
|
|
255
|
+
| `fallbackFonts` | string[] | `['liberation sans']` | Fallback font families in order, used for the fontconfig cascade |
|
|
256
|
+
| `useLocalFonts` | boolean | `true` | Use Local Font Access API if available |
|
|
257
|
+
| `libassMemoryLimit` | number | `128` | libass bitmap cache memory limit in MiB |
|
|
258
|
+
| `libassGlyphLimit` | number | `2048` | libass glyph cache limit |
|
|
259
|
+
| `fullTrackWarmup` | boolean | `false` | Pre-render early track windows after load to warm libass caches |
|
|
260
|
+
| `onCanvasFallback` | function | - | Callback when no GPU renderer is available (Canvas2D fallback) |
|
|
232
261
|
|
|
233
262
|
## Methods
|
|
234
263
|
|
|
235
264
|
### Track Management
|
|
236
265
|
|
|
237
|
-
| Method
|
|
238
|
-
|
|
239
|
-
| `setTrackByUrl(url)`
|
|
240
|
-
| `setTrack(content)`
|
|
241
|
-
| `setEncryptedTrack(content)` | `content: EncryptedSubtitleContent`
|
|
242
|
-
| `freeTrack()`
|
|
266
|
+
| Method | Parameters | Description |
|
|
267
|
+
| ---------------------------- | ---------------------------------------------- | ---------------------------------------------------------------- |
|
|
268
|
+
| `setTrackByUrl(url)` | `url: string` | Load subtitle from URL |
|
|
269
|
+
| `setTrack(content)` | `content: string \| Uint8Array \| ArrayBuffer` | Set subtitle from content |
|
|
270
|
+
| `setEncryptedTrack(content)` | `content: EncryptedSubtitleContent` | Set subtitle from an encrypted payload (decrypted in the worker) |
|
|
271
|
+
| `freeTrack()` | - | Remove current subtitles |
|
|
243
272
|
|
|
244
273
|
### Playback Control
|
|
245
274
|
|
|
246
|
-
| Method
|
|
247
|
-
|
|
248
|
-
| `setIsPaused(isPaused)`
|
|
249
|
-
| `setRate(rate)`
|
|
250
|
-
| `setCurrentTime(isPaused?, currentTime?, rate?)` | `isPaused?: boolean, currentTime?: number, rate?: number` | Set current time, playback state and rate
|
|
275
|
+
| Method | Parameters | Description |
|
|
276
|
+
| ------------------------------------------------ | --------------------------------------------------------- | --------------------------------------------- |
|
|
277
|
+
| `setIsPaused(isPaused)` | `isPaused: boolean` | Set playback pause state |
|
|
278
|
+
| `setRate(rate)` | `rate: number` | Set playback rate (speed multiplier) |
|
|
279
|
+
| `setCurrentTime(isPaused?, currentTime?, rate?)` | `isPaused?: boolean, currentTime?: number, rate?: number` | Set current time, playback state and rate |
|
|
280
|
+
| `setFrameTimeline(frameTimes)` | `ArrayLike<number> \| null` | Replace or disable the encoded-frame timeline |
|
|
251
281
|
|
|
252
282
|
### Video & Canvas
|
|
253
283
|
|
|
254
|
-
| Method
|
|
255
|
-
|
|
256
|
-
| `setVideo(video)`
|
|
257
|
-
| `resize(width?, height?, top?, left?, force?)` | `width?: number, height?: number, top?: number, left?: number, force?: boolean` | Resize the canvas
|
|
284
|
+
| Method | Parameters | Description |
|
|
285
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------- |
|
|
286
|
+
| `setVideo(video)` | `video: HTMLVideoElement` | Change target video element |
|
|
287
|
+
| `resize(width?, height?, top?, left?, force?)` | `width?: number, height?: number, top?: number, left?: number, force?: boolean` | Resize the canvas |
|
|
258
288
|
|
|
259
289
|
### Event Management
|
|
260
290
|
|
|
261
|
-
| Method
|
|
262
|
-
|
|
263
|
-
| `createEvent(event)`
|
|
264
|
-
| `setEvent(event, index)` | `event: Partial<ASSEvent>, index: number` | `void`
|
|
265
|
-
| `removeEvent(index)`
|
|
266
|
-
| `getEvents()`
|
|
267
|
-
| `getEventCount()`
|
|
291
|
+
| Method | Parameters | Returns | Description |
|
|
292
|
+
| ------------------------ | ----------------------------------------- | --------------------- | ----------------------------- |
|
|
293
|
+
| `createEvent(event)` | `event: Partial<ASSEvent>` | `void` | Create a new ASS event |
|
|
294
|
+
| `setEvent(event, index)` | `event: Partial<ASSEvent>, index: number` | `void` | Overwrite event at index |
|
|
295
|
+
| `removeEvent(index)` | `index: number` | `void` | Remove event at index |
|
|
296
|
+
| `getEvents()` | - | `Promise<ASSEvent[]>` | Get all ASS events |
|
|
297
|
+
| `getEventCount()` | - | `Promise<number>` | Get event count (lightweight) |
|
|
268
298
|
|
|
269
299
|
### Style Management
|
|
270
300
|
|
|
271
|
-
| Method
|
|
272
|
-
|
|
273
|
-
| `createStyle(style)`
|
|
274
|
-
| `setStyle(style, index)` | `style: Partial<ASSStyle>, index: number` | `void`
|
|
275
|
-
| `removeStyle(index)`
|
|
276
|
-
| `getStyles()`
|
|
277
|
-
| `getStyleCount()`
|
|
278
|
-
| `styleOverride(style)`
|
|
279
|
-
| `disableStyleOverride()` | -
|
|
301
|
+
| Method | Parameters | Returns | Description |
|
|
302
|
+
| ------------------------ | ----------------------------------------- | --------------------- | ----------------------------- |
|
|
303
|
+
| `createStyle(style)` | `style: Partial<ASSStyle>` | `void` | Create a new ASS style |
|
|
304
|
+
| `setStyle(style, index)` | `style: Partial<ASSStyle>, index: number` | `void` | Overwrite style at index |
|
|
305
|
+
| `removeStyle(index)` | `index: number` | `void` | Remove style at index |
|
|
306
|
+
| `getStyles()` | - | `Promise<ASSStyle[]>` | Get all ASS styles |
|
|
307
|
+
| `getStyleCount()` | - | `Promise<number>` | Get style count (lightweight) |
|
|
308
|
+
| `styleOverride(style)` | `style: Partial<ASSStyle>` | `void` | Set a style override |
|
|
309
|
+
| `disableStyleOverride()` | - | `void` | Disable style override |
|
|
280
310
|
|
|
281
311
|
### Font Management
|
|
282
312
|
|
|
283
|
-
| Method
|
|
284
|
-
|
|
285
|
-
| `addFont(font)`
|
|
286
|
-
| `setDefaultFont(font)` | `font: string`
|
|
313
|
+
| Method | Parameters | Description |
|
|
314
|
+
| ---------------------- | ---------------------------- | ------------------------------ |
|
|
315
|
+
| `addFont(font)` | `font: string \| Uint8Array` | Add a font to the renderer |
|
|
316
|
+
| `setDefaultFont(font)` | `font: string` | Change the default font family |
|
|
287
317
|
|
|
288
318
|
### Statistics & Debugging
|
|
289
319
|
|
|
290
|
-
| Method
|
|
291
|
-
|
|
292
|
-
| `getStats()`
|
|
293
|
-
| `resetStats()`
|
|
294
|
-
| `getEventCount()` | -
|
|
295
|
-
| `getStyleCount()` | -
|
|
320
|
+
| Method | Parameters | Returns | Description |
|
|
321
|
+
| ----------------- | ---------- | --------------------------- | ----------------------------- |
|
|
322
|
+
| `getStats()` | - | `Promise<PerformanceStats>` | Get performance statistics |
|
|
323
|
+
| `resetStats()` | - | `Promise<void>` | Reset statistics counters |
|
|
324
|
+
| `getEventCount()` | - | `Promise<number>` | Get event count (lightweight) |
|
|
325
|
+
| `getStyleCount()` | - | `Promise<number>` | Get style count (lightweight) |
|
|
296
326
|
|
|
297
327
|
### Lifecycle
|
|
298
328
|
|
|
299
|
-
| Method
|
|
300
|
-
|
|
301
|
-
| `destroy(err?)`
|
|
302
|
-
| `sendMessage(target, data?, transferable?)` | `target: string, data?: Record<string, any>, transferable?: Transferable[]` | Send data to worker
|
|
329
|
+
| Method | Parameters | Description |
|
|
330
|
+
| ------------------------------------------- | --------------------------------------------------------------------------- | -------------------------------- |
|
|
331
|
+
| `destroy(err?)` | `err?: Error \| string` | Destroy the renderer and cleanup |
|
|
332
|
+
| `sendMessage(target, data?, transferable?)` | `target: string, data?: Record<string, any>, transferable?: Transferable[]` | Send data to worker |
|
|
303
333
|
|
|
304
334
|
## Properties
|
|
305
335
|
|
|
306
|
-
| Property
|
|
307
|
-
|
|
308
|
-
| `debug`
|
|
309
|
-
| `prescaleFactor`
|
|
310
|
-
| `prescaleHeightLimit` | number
|
|
311
|
-
| `maxRenderHeight`
|
|
312
|
-
| `timeOffset`
|
|
313
|
-
| `renderAhead`
|
|
314
|
-
| `
|
|
315
|
-
| `
|
|
316
|
-
| `
|
|
317
|
-
| `
|
|
336
|
+
| Property | Type | Description |
|
|
337
|
+
| --------------------- | ---------------------------------------- | ------------------------------------------------------------- |
|
|
338
|
+
| `debug` | boolean | Enable/disable debug logging |
|
|
339
|
+
| `prescaleFactor` | number | Scale factor for subtitles |
|
|
340
|
+
| `prescaleHeightLimit` | number | Height limit for prescaling |
|
|
341
|
+
| `maxRenderHeight` | number | Maximum render height |
|
|
342
|
+
| `timeOffset` | number | Subtitle time offset in seconds |
|
|
343
|
+
| `renderAhead` | number | Optional extra seconds to render ahead of the video clock |
|
|
344
|
+
| `framePrefetch` | number | Number of exact subtitle frames prepared ahead |
|
|
345
|
+
| `busy` | boolean | Whether the renderer is currently busy |
|
|
346
|
+
| `rendererType` | `'webgpu'` \| `'webgl2'` \| `'canvas2d'` | Active renderer backend (read-only) |
|
|
347
|
+
| `isUsingGPURenderer` | boolean | Whether a hardware-accelerated renderer is active (read-only) |
|
|
348
|
+
| `isUsingWebGPU` | boolean | _Deprecated_ - use `rendererType === 'webgpu'` |
|
|
318
349
|
|
|
319
350
|
## Type Definitions
|
|
320
351
|
|
|
321
352
|
### ASSEvent
|
|
322
353
|
|
|
323
|
-
| Property
|
|
324
|
-
|
|
325
|
-
| `Start`
|
|
326
|
-
| `Duration`
|
|
327
|
-
| `Style`
|
|
328
|
-
| `Name`
|
|
329
|
-
| `MarginL`
|
|
330
|
-
| `MarginR`
|
|
331
|
-
| `MarginV`
|
|
332
|
-
| `Effect`
|
|
333
|
-
| `Text`
|
|
334
|
-
| `ReadOrder` | number | Read order number
|
|
335
|
-
| `Layer`
|
|
336
|
-
| `_index`
|
|
354
|
+
| Property | Type | Description |
|
|
355
|
+
| ----------- | ------ | -------------------------------- |
|
|
356
|
+
| `Start` | number | Start time in milliseconds |
|
|
357
|
+
| `Duration` | number | Duration in milliseconds |
|
|
358
|
+
| `Style` | string | Style name |
|
|
359
|
+
| `Name` | string | Character name (informational) |
|
|
360
|
+
| `MarginL` | number | Left margin override in pixels |
|
|
361
|
+
| `MarginR` | number | Right margin override in pixels |
|
|
362
|
+
| `MarginV` | number | Bottom margin override in pixels |
|
|
363
|
+
| `Effect` | string | Transition effect |
|
|
364
|
+
| `Text` | string | Subtitle text content |
|
|
365
|
+
| `ReadOrder` | number | Read order number |
|
|
366
|
+
| `Layer` | number | Z-index layer |
|
|
367
|
+
| `_index` | number | Internal index (optional) |
|
|
337
368
|
|
|
338
369
|
### ASSStyle
|
|
339
370
|
|
|
340
|
-
| Property
|
|
341
|
-
|
|
342
|
-
| `Name`
|
|
343
|
-
| `FontName`
|
|
344
|
-
| `FontSize`
|
|
345
|
-
| `PrimaryColour`
|
|
346
|
-
| `SecondaryColour`
|
|
347
|
-
| `OutlineColour`
|
|
348
|
-
| `BackColour`
|
|
349
|
-
| `Bold`
|
|
350
|
-
| `Italic`
|
|
351
|
-
| `Underline`
|
|
352
|
-
| `StrikeOut`
|
|
353
|
-
| `ScaleX`
|
|
354
|
-
| `ScaleY`
|
|
355
|
-
| `Spacing`
|
|
356
|
-
| `Angle`
|
|
357
|
-
| `BorderStyle`
|
|
358
|
-
| `Outline`
|
|
359
|
-
| `Shadow`
|
|
360
|
-
| `Alignment`
|
|
361
|
-
| `MarginL`
|
|
362
|
-
| `MarginR`
|
|
363
|
-
| `MarginV`
|
|
364
|
-
| `Encoding`
|
|
365
|
-
| `treat_fontname_as_pattern` | number | Treat font name as pattern
|
|
366
|
-
| `Blur`
|
|
367
|
-
| `Justify`
|
|
371
|
+
| Property | Type | Description |
|
|
372
|
+
| --------------------------- | ------ | --------------------------------------------------- |
|
|
373
|
+
| `Name` | string | Style name (case sensitive) |
|
|
374
|
+
| `FontName` | string | Font family name |
|
|
375
|
+
| `FontSize` | number | Font size |
|
|
376
|
+
| `PrimaryColour` | number | Primary color (RGBA as uint32) |
|
|
377
|
+
| `SecondaryColour` | number | Secondary color (RGBA as uint32) |
|
|
378
|
+
| `OutlineColour` | number | Outline color (RGBA as uint32) |
|
|
379
|
+
| `BackColour` | number | Background/shadow color (RGBA as uint32) |
|
|
380
|
+
| `Bold` | number | Bold (-1 = true, 0 = false) |
|
|
381
|
+
| `Italic` | number | Italic (-1 = true, 0 = false) |
|
|
382
|
+
| `Underline` | number | Underline (-1 = true, 0 = false) |
|
|
383
|
+
| `StrikeOut` | number | StrikeOut (-1 = true, 0 = false) |
|
|
384
|
+
| `ScaleX` | number | Width scale (percent) |
|
|
385
|
+
| `ScaleY` | number | Height scale (percent) |
|
|
386
|
+
| `Spacing` | number | Extra spacing between characters (pixels) |
|
|
387
|
+
| `Angle` | number | Rotation angle (degrees) |
|
|
388
|
+
| `BorderStyle` | number | Border style (1 = outline + shadow, 3 = opaque box) |
|
|
389
|
+
| `Outline` | number | Outline width (0-4 pixels) |
|
|
390
|
+
| `Shadow` | number | Shadow depth (0-4 pixels) |
|
|
391
|
+
| `Alignment` | number | Alignment (1-9, numpad style) |
|
|
392
|
+
| `MarginL` | number | Left margin (pixels) |
|
|
393
|
+
| `MarginR` | number | Right margin (pixels) |
|
|
394
|
+
| `MarginV` | number | Vertical margin (pixels) |
|
|
395
|
+
| `Encoding` | number | Font encoding |
|
|
396
|
+
| `treat_fontname_as_pattern` | number | Treat font name as pattern |
|
|
397
|
+
| `Blur` | number | Blur amount |
|
|
398
|
+
| `Justify` | number | Text justification |
|
|
368
399
|
|
|
369
400
|
# How to build?
|
|
370
401
|
|
|
@@ -395,10 +426,10 @@ git clone --recursive https://github.com/altqx/akarisub.git
|
|
|
395
426
|
```bash
|
|
396
427
|
mise install # installs emsdk, bun, cmake
|
|
397
428
|
bun install # JS dependencies
|
|
398
|
-
make # builds the static libs (fribidi, freetype, harfbuzz, fontconfig, libass, ...) and the WASM
|
|
399
|
-
bun run build # builds the WASM
|
|
429
|
+
make # builds the static libs (fribidi, freetype, harfbuzz, fontconfig, libass, ...) and the WASM glue
|
|
430
|
+
bun run build # builds the WASM glue and TypeScript
|
|
400
431
|
```
|
|
401
432
|
|
|
402
433
|
- If on macOS with libtool from brew, `LIBTOOLIZE=glibtoolize make`
|
|
403
|
-
- Incremental rebuilds of the
|
|
404
|
-
- Artifacts are in `dist/`
|
|
434
|
+
- Incremental rebuilds of the WASM glue only: `bun run build:wasm` (or `make worker`)
|
|
435
|
+
- Artifacts are in `pkg/` (WASM glue and binary) and `dist/` (TypeScript)
|