libbitsub 1.10.2 → 1.11.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.
Files changed (89) hide show
  1. package/README.md +184 -34
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/integrations/hlsjs.d.ts +45 -0
  7. package/dist/integrations/hlsjs.d.ts.map +1 -0
  8. package/dist/integrations/hlsjs.js +121 -0
  9. package/dist/integrations/hlsjs.js.map +1 -0
  10. package/dist/integrations/index.d.ts +11 -0
  11. package/dist/integrations/index.d.ts.map +1 -0
  12. package/dist/integrations/index.js +11 -0
  13. package/dist/integrations/index.js.map +1 -0
  14. package/dist/integrations/react.d.ts +55 -0
  15. package/dist/integrations/react.d.ts.map +1 -0
  16. package/dist/integrations/react.js +127 -0
  17. package/dist/integrations/react.js.map +1 -0
  18. package/dist/integrations/shaka.d.ts +38 -0
  19. package/dist/integrations/shaka.d.ts.map +1 -0
  20. package/dist/integrations/shaka.js +71 -0
  21. package/dist/integrations/shaka.js.map +1 -0
  22. package/dist/integrations/shared.d.ts +41 -0
  23. package/dist/integrations/shared.d.ts.map +1 -0
  24. package/dist/integrations/shared.js +123 -0
  25. package/dist/integrations/shared.js.map +1 -0
  26. package/dist/integrations/videojs.d.ts +85 -0
  27. package/dist/integrations/videojs.d.ts.map +1 -0
  28. package/dist/integrations/videojs.js +111 -0
  29. package/dist/integrations/videojs.js.map +1 -0
  30. package/dist/ts/diagnostics.d.ts.map +1 -1
  31. package/dist/ts/diagnostics.js.map +1 -1
  32. package/dist/ts/frame-export.d.ts.map +1 -1
  33. package/dist/ts/frame-export.js +4 -4
  34. package/dist/ts/frame-export.js.map +1 -1
  35. package/dist/ts/parsers.d.ts +7 -6
  36. package/dist/ts/parsers.d.ts.map +1 -1
  37. package/dist/ts/parsers.js +76 -13
  38. package/dist/ts/parsers.js.map +1 -1
  39. package/dist/ts/range-loader.d.ts +31 -0
  40. package/dist/ts/range-loader.d.ts.map +1 -0
  41. package/dist/ts/range-loader.js +220 -0
  42. package/dist/ts/range-loader.js.map +1 -0
  43. package/dist/ts/renderers.d.ts +14 -0
  44. package/dist/ts/renderers.d.ts.map +1 -1
  45. package/dist/ts/renderers.js +445 -162
  46. package/dist/ts/renderers.js.map +1 -1
  47. package/dist/ts/types.d.ts +51 -1
  48. package/dist/ts/types.d.ts.map +1 -1
  49. package/dist/ts/utils.d.ts.map +1 -1
  50. package/dist/ts/utils.js.map +1 -1
  51. package/dist/ts/wasm.d.ts +2 -0
  52. package/dist/ts/wasm.d.ts.map +1 -1
  53. package/dist/ts/wasm.js +12 -0
  54. package/dist/ts/wasm.js.map +1 -1
  55. package/dist/ts/webgl2-renderer.d.ts +9 -0
  56. package/dist/ts/webgl2-renderer.d.ts.map +1 -1
  57. package/dist/ts/webgl2-renderer.js +34 -0
  58. package/dist/ts/webgl2-renderer.js.map +1 -1
  59. package/dist/ts/webgpu-renderer.d.ts +9 -0
  60. package/dist/ts/webgpu-renderer.d.ts.map +1 -1
  61. package/dist/ts/webgpu-renderer.js +70 -2
  62. package/dist/ts/webgpu-renderer.js.map +1 -1
  63. package/dist/ts/worker.d.ts +11 -0
  64. package/dist/ts/worker.d.ts.map +1 -1
  65. package/dist/ts/worker.js +229 -161
  66. package/dist/ts/worker.js.map +1 -1
  67. package/dist/wrapper.d.ts +3 -1
  68. package/dist/wrapper.d.ts.map +1 -1
  69. package/dist/wrapper.js +2 -0
  70. package/dist/wrapper.js.map +1 -1
  71. package/examples/README.md +38 -0
  72. package/examples/hlsjs/README.md +53 -0
  73. package/examples/react/README.md +60 -0
  74. package/examples/shaka/README.md +61 -0
  75. package/examples/videojs/README.md +55 -0
  76. package/package.json +64 -7
  77. package/pkg/README.md +184 -34
  78. package/pkg/libbitsub.d.ts +14 -0
  79. package/pkg/libbitsub.js +50 -0
  80. package/pkg/libbitsub_bg.wasm +0 -0
  81. package/pkg/libbitsub_bg.wasm.d.ts +7 -0
  82. package/pkg/package.json +1 -1
  83. package/src/integrations/hlsjs.ts +154 -0
  84. package/src/integrations/index.ts +36 -0
  85. package/src/integrations/react.tsx +170 -0
  86. package/src/integrations/shaka.ts +92 -0
  87. package/src/integrations/shared.ts +170 -0
  88. package/src/integrations/videojs.ts +200 -0
  89. package/src/wrapper.ts +12 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Shared player-integration helpers for libbitsub.
3
+ * Optional adapters build on this surface so the core package stays dependency-free.
4
+ */
5
+
6
+ import {
7
+ createAutoSubtitleRenderer,
8
+ type AutoVideoSubtitleOptions,
9
+ type PgsRenderer,
10
+ type SubtitleDisplaySettings,
11
+ type SubtitleRendererStats,
12
+ type VobSubRenderer
13
+ } from '../wrapper'
14
+
15
+ /** High-level renderer produced by integrations. */
16
+ export type BitSubRenderer = PgsRenderer | VobSubRenderer
17
+
18
+ /** Subtitle source + renderer options without a bound video element. */
19
+ export type BitSubSourceOptions = Omit<AutoVideoSubtitleOptions, 'video'>
20
+
21
+ export interface BitSubController {
22
+ /** Active renderer, or null when no track is loaded. */
23
+ readonly renderer: BitSubRenderer | null
24
+ /** Currently bound video element. */
25
+ readonly video: HTMLVideoElement | null
26
+ /** Load or replace the active subtitle track. */
27
+ load(source: BitSubSourceOptions): BitSubRenderer
28
+ /** Remove the active track without disposing the controller. */
29
+ clear(): void
30
+ /** Tear down the renderer and release the controller. */
31
+ dispose(): void
32
+ /** Whether {@link dispose} has already been called. */
33
+ readonly disposed: boolean
34
+ getDisplaySettings(): SubtitleDisplaySettings | null
35
+ setDisplaySettings(settings: Partial<SubtitleDisplaySettings>): void
36
+ resetDisplaySettings(): void
37
+ getStats(): SubtitleRendererStats | null
38
+ /** Rebind to another video element, keeping the last source when present. */
39
+ setVideo(video: HTMLVideoElement | null): void
40
+ }
41
+
42
+ export interface AttachBitSubOptions extends BitSubSourceOptions {
43
+ /** When false, skip the initial load even if source fields are present. Default true. */
44
+ autoLoad?: boolean
45
+ }
46
+
47
+ function hasSubtitleSource(source: BitSubSourceOptions | undefined): boolean {
48
+ if (!source) return false
49
+ return Boolean(source.subUrl || source.subContent || source.idxUrl || source.idxContent || source.fileName)
50
+ }
51
+
52
+ function assertVideo(video: HTMLVideoElement | null | undefined): HTMLVideoElement {
53
+ if (!video) {
54
+ throw new Error('libbitsub integration requires an HTMLVideoElement')
55
+ }
56
+ return video
57
+ }
58
+
59
+ /**
60
+ * Attach a disposable controller to a video element.
61
+ * Call {@link BitSubController.load} to start a track, or pass source fields to auto-load.
62
+ */
63
+ export function attachBitSub(video: HTMLVideoElement, options: AttachBitSubOptions = {}): BitSubController {
64
+ const { autoLoad = true, ...initialSource } = options
65
+ let disposed = false
66
+ let boundVideo: HTMLVideoElement | null = video
67
+ let renderer: BitSubRenderer | null = null
68
+ let lastSource: BitSubSourceOptions | null = hasSubtitleSource(initialSource) ? { ...initialSource } : null
69
+
70
+ const clearRenderer = (): void => {
71
+ if (!renderer) return
72
+ renderer.dispose()
73
+ renderer = null
74
+ }
75
+
76
+ const controller: BitSubController = {
77
+ get renderer() {
78
+ return renderer
79
+ },
80
+ get video() {
81
+ return boundVideo
82
+ },
83
+ get disposed() {
84
+ return disposed
85
+ },
86
+ load(source: BitSubSourceOptions): BitSubRenderer {
87
+ if (disposed) {
88
+ throw new Error('BitSubController has been disposed')
89
+ }
90
+ const activeVideo = assertVideo(boundVideo)
91
+ if (!hasSubtitleSource(source)) {
92
+ throw new Error('BitSubController.load requires subUrl, subContent, or a detectable fileName')
93
+ }
94
+
95
+ clearRenderer()
96
+ lastSource = { ...source }
97
+ renderer = createAutoSubtitleRenderer({
98
+ ...source,
99
+ video: activeVideo
100
+ })
101
+ return renderer
102
+ },
103
+ clear(): void {
104
+ if (disposed) return
105
+ clearRenderer()
106
+ lastSource = null
107
+ },
108
+ dispose(): void {
109
+ if (disposed) return
110
+ disposed = true
111
+ clearRenderer()
112
+ lastSource = null
113
+ boundVideo = null
114
+ },
115
+ getDisplaySettings(): SubtitleDisplaySettings | null {
116
+ return renderer?.getDisplaySettings() ?? null
117
+ },
118
+ setDisplaySettings(settings: Partial<SubtitleDisplaySettings>): void {
119
+ renderer?.setDisplaySettings(settings)
120
+ },
121
+ resetDisplaySettings(): void {
122
+ renderer?.resetDisplaySettings()
123
+ },
124
+ getStats(): SubtitleRendererStats | null {
125
+ return renderer?.getStats() ?? null
126
+ },
127
+ setVideo(nextVideo: HTMLVideoElement | null): void {
128
+ if (disposed) {
129
+ throw new Error('BitSubController has been disposed')
130
+ }
131
+
132
+ if (nextVideo === boundVideo) {
133
+ // Same element: still materialize a pending source (e.g. delayed autoLoad).
134
+ if (nextVideo && !renderer && lastSource) {
135
+ renderer = createAutoSubtitleRenderer({
136
+ ...lastSource,
137
+ video: nextVideo
138
+ })
139
+ }
140
+ return
141
+ }
142
+
143
+ const previousSource = lastSource
144
+ clearRenderer()
145
+ boundVideo = nextVideo
146
+
147
+ if (nextVideo && previousSource) {
148
+ lastSource = previousSource
149
+ renderer = createAutoSubtitleRenderer({
150
+ ...previousSource,
151
+ video: nextVideo
152
+ })
153
+ }
154
+ }
155
+ }
156
+
157
+ if (autoLoad && lastSource) {
158
+ controller.load(lastSource)
159
+ }
160
+
161
+ return controller
162
+ }
163
+
164
+ /** Convenience one-shot helper used by thin player wrappers. */
165
+ export function createBitSubRenderer(video: HTMLVideoElement, source: BitSubSourceOptions): BitSubRenderer {
166
+ return createAutoSubtitleRenderer({
167
+ ...source,
168
+ video: assertVideo(video)
169
+ })
170
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Optional Video.js plugin for libbitsub bitmap subtitles.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import videojs from 'video.js'
7
+ * import { registerBitSubPlugin } from 'libbitsub/videojs'
8
+ *
9
+ * registerBitSubPlugin(videojs)
10
+ * const player = videojs('my-video')
11
+ * player.bitsub({ subUrl: '/subs/movie.sup' })
12
+ * ```
13
+ */
14
+
15
+ import { attachBitSub, type AttachBitSubOptions, type BitSubController, type BitSubSourceOptions } from './shared'
16
+
17
+ /** Minimal Video.js surface used by the plugin (avoids a hard dependency). */
18
+ export interface VideoJsPlayerLike {
19
+ ready(callback: (this: this) => void): this
20
+ el(): HTMLElement | null
21
+ tech?: (asHtml?: boolean) => { el?: () => HTMLElement | null } | HTMLElement | null | undefined
22
+ on(type: string, handler: (...args: unknown[]) => void): this
23
+ off?(type: string, handler: (...args: unknown[]) => void): this
24
+ addClass?(className: string): void
25
+ removeClass?(className: string): void
26
+ trigger?(event: string, data?: unknown): void
27
+ }
28
+
29
+ export interface VideoJsPluginBase {
30
+ player: VideoJsPlayerLike
31
+ options_: unknown
32
+ dispose(): void
33
+ }
34
+
35
+ export interface VideoJsPluginConstructor {
36
+ new (player: VideoJsPlayerLike, options?: unknown): VideoJsPluginBase
37
+ prototype: VideoJsPluginBase
38
+ }
39
+
40
+ export interface VideoJsLike {
41
+ getPlugin(name: string): VideoJsPluginConstructor
42
+ registerPlugin(name: string, plugin: unknown): void
43
+ }
44
+
45
+ export type BitSubVideoJsOptions = AttachBitSubOptions
46
+
47
+ export interface BitSubVideoJsApi {
48
+ /** Active controller, if any. */
49
+ controller(): BitSubController | null
50
+ /** Load or replace the subtitle track. */
51
+ load(source: BitSubSourceOptions): void
52
+ /** Clear the current track. */
53
+ clear(): void
54
+ /** Dispose plugin-owned resources (also runs on player dispose). */
55
+ dispose(): void
56
+ setDisplaySettings(settings: Parameters<BitSubController['setDisplaySettings']>[0]): void
57
+ getDisplaySettings(): ReturnType<BitSubController['getDisplaySettings']>
58
+ getStats(): ReturnType<BitSubController['getStats']>
59
+ }
60
+
61
+ function resolveVideoElement(player: VideoJsPlayerLike): HTMLVideoElement | null {
62
+ const tech = typeof player.tech === 'function' ? player.tech(true) : undefined
63
+ const techEl =
64
+ tech && typeof (tech as { el?: () => HTMLElement | null }).el === 'function'
65
+ ? (tech as { el: () => HTMLElement | null }).el()
66
+ : (tech as HTMLElement | null | undefined)
67
+
68
+ if (techEl instanceof HTMLVideoElement) return techEl
69
+
70
+ const root = player.el()
71
+ if (!root) return null
72
+ return root.querySelector('video')
73
+ }
74
+
75
+ /**
76
+ * Register the `bitsub` Video.js plugin.
77
+ * Safe to call multiple times — subsequent calls are no-ops once registered.
78
+ */
79
+ export function registerBitSubPlugin(videojs: VideoJsLike, pluginName = 'bitsub'): void {
80
+ const existing = typeof videojs.getPlugin === 'function' ? videojs.getPlugin(pluginName) : undefined
81
+ // Only skip when a previous registerBitSubPlugin call marked the plugin.
82
+ if (existing && (existing as { __libbitsubPlugin?: boolean }).__libbitsubPlugin) {
83
+ return
84
+ }
85
+
86
+ const Plugin = videojs.getPlugin('plugin')
87
+
88
+ class BitSubPlugin extends Plugin implements BitSubVideoJsApi {
89
+ private controllerRef: BitSubController | null = null
90
+ private readonly onDispose: () => void
91
+ private readonly onLoadedData: () => void
92
+ private readonly initialOptions: BitSubVideoJsOptions
93
+
94
+ constructor(player: VideoJsPlayerLike, options: BitSubVideoJsOptions = {}) {
95
+ super(player, options)
96
+ this.initialOptions = options
97
+
98
+ this.onDispose = () => this.disposeController()
99
+ this.onLoadedData = () => this.ensureController(this.initialOptions, false)
100
+
101
+ player.ready(() => {
102
+ this.ensureController(options, true)
103
+ player.on('loadeddata', this.onLoadedData)
104
+ player.on('dispose', this.onDispose)
105
+ player.addClass?.('vjs-bitsub')
106
+ })
107
+ }
108
+
109
+ controller(): BitSubController | null {
110
+ return this.controllerRef
111
+ }
112
+
113
+ load(source: BitSubSourceOptions): void {
114
+ const controller = this.ensureController({}, false)
115
+ controller.load(source)
116
+ this.player.trigger?.('bitsubload', source)
117
+ }
118
+
119
+ clear(): void {
120
+ this.controllerRef?.clear()
121
+ this.player.trigger?.('bitsubclear')
122
+ }
123
+
124
+ setDisplaySettings(settings: Parameters<BitSubController['setDisplaySettings']>[0]): void {
125
+ this.controllerRef?.setDisplaySettings(settings)
126
+ }
127
+
128
+ getDisplaySettings(): ReturnType<BitSubController['getDisplaySettings']> {
129
+ return this.controllerRef?.getDisplaySettings() ?? null
130
+ }
131
+
132
+ getStats(): ReturnType<BitSubController['getStats']> {
133
+ return this.controllerRef?.getStats() ?? null
134
+ }
135
+
136
+ override dispose(): void {
137
+ this.player.off?.('loadeddata', this.onLoadedData)
138
+ this.player.off?.('dispose', this.onDispose)
139
+ this.player.removeClass?.('vjs-bitsub')
140
+ this.disposeController()
141
+ super.dispose()
142
+ }
143
+
144
+ private disposeController(): void {
145
+ this.controllerRef?.dispose()
146
+ this.controllerRef = null
147
+ }
148
+
149
+ private ensureController(options: BitSubVideoJsOptions, allowAutoload: boolean): BitSubController {
150
+ const video = resolveVideoElement(this.player)
151
+ if (!video) {
152
+ throw new Error('libbitsub Video.js plugin could not resolve an HTMLVideoElement')
153
+ }
154
+
155
+ if (this.controllerRef && !this.controllerRef.disposed) {
156
+ if (this.controllerRef.video !== video) {
157
+ this.controllerRef.setVideo(video)
158
+ }
159
+ return this.controllerRef
160
+ }
161
+
162
+ const { autoLoad = true, ...source } = options
163
+ this.controllerRef = attachBitSub(video, {
164
+ ...source,
165
+ autoLoad: allowAutoload ? autoLoad : false
166
+ })
167
+ return this.controllerRef
168
+ }
169
+ }
170
+
171
+ ;(BitSubPlugin as unknown as { __libbitsubPlugin?: boolean }).__libbitsubPlugin = true
172
+ videojs.registerPlugin(pluginName, BitSubPlugin)
173
+ }
174
+
175
+ /**
176
+ * Ambient augmentation helper for apps using the default plugin name `bitsub`.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * import 'libbitsub/videojs' // side-effect free; types only when referenced
181
+ * // or in a global.d.ts:
182
+ * // import type {} from 'libbitsub/videojs'
183
+ * ```
184
+ *
185
+ * For full player typing, extend your video.js module:
186
+ * ```ts
187
+ * import type { BitSubVideoJsApi, BitSubVideoJsOptions } from 'libbitsub/videojs'
188
+ * declare module 'video.js' {
189
+ * interface VideoJsPlayer {
190
+ * bitsub(options?: BitSubVideoJsOptions): BitSubVideoJsApi
191
+ * }
192
+ * }
193
+ * ```
194
+ */
195
+ export type VideoJsBitSubPlayer = VideoJsPlayerLike & {
196
+ bitsub(options?: BitSubVideoJsOptions): BitSubVideoJsApi
197
+ }
198
+
199
+ export type { AttachBitSubOptions, BitSubController, BitSubSourceOptions, BitSubRenderer } from './shared'
200
+ export { attachBitSub, createBitSubRenderer } from './shared'
package/src/wrapper.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  // Re-export all types
7
7
  export type {
8
+ AssetFetchStrategy,
8
9
  AutoSubtitleSource,
9
10
  AutoVideoSubtitleOptions,
10
11
  OpenedSubtitles,
@@ -45,6 +46,8 @@ export { SubtitleDiagnosticError, createSubtitleDiagnosticError, normalizeSubtit
45
46
  // Re-export WASM management
46
47
  export { initWasm, isWasmInitialized } from './ts/wasm'
47
48
 
49
+ export { warmup, ready, isWorkerAvailable, isWorkerReady } from './ts/worker'
50
+
48
51
  // Re-export WebGPU utilities
49
52
  export { isWebGPUSupported } from './ts/webgpu-renderer'
50
53
 
@@ -60,6 +63,15 @@ export { PgsRenderer, VobSubRenderer, createAutoSubtitleRenderer, type SubtitleR
60
63
  // Re-export format detection utilities
61
64
  export { detectSubtitleFormat } from './ts/utils'
62
65
 
66
+ export {
67
+ fetchSubtitleAsset,
68
+ fetchSubtitleText,
69
+ probeRangeSupport,
70
+ type AssetFetchOptions,
71
+ type AssetFetchProgress,
72
+ type RangeProbeResult
73
+ } from './ts/range-loader'
74
+
63
75
  // =============================================================================
64
76
  // Legacy Aliases (for backward compatibility)
65
77
  // =============================================================================