@lightbird/core 0.1.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.
@@ -0,0 +1,252 @@
1
+ import * as react from 'react';
2
+ import { RefObject } from 'react';
3
+
4
+ declare function useVideoPlayback(videoRef: RefObject<HTMLVideoElement | null>): {
5
+ isPlaying: boolean;
6
+ progress: number;
7
+ duration: number;
8
+ volume: number;
9
+ isMuted: boolean;
10
+ playbackRate: number;
11
+ loop: boolean;
12
+ togglePlay: () => void;
13
+ seek: (t: number) => void;
14
+ setVolume: (v: number) => void;
15
+ toggleMute: () => void;
16
+ setPlaybackRate: (r: number) => void;
17
+ frameStep: (direction: "forward" | "backward") => void;
18
+ toggleLoop: () => void;
19
+ };
20
+
21
+ interface Chapter {
22
+ index: number;
23
+ title: string;
24
+ startTime: number;
25
+ endTime: number;
26
+ }
27
+ interface PlaylistItem {
28
+ id: string;
29
+ name: string;
30
+ url: string;
31
+ type: 'video' | 'stream';
32
+ file?: File;
33
+ duration?: number;
34
+ }
35
+ interface SubtitleCue {
36
+ startTime: number;
37
+ endTime: number;
38
+ text: string;
39
+ }
40
+ interface Subtitle {
41
+ id: string;
42
+ name: string;
43
+ lang: string;
44
+ url?: string;
45
+ type: 'embedded' | 'external';
46
+ /** Detected source format (vtt, srt, ass, ssa). Defaults to 'vtt'. */
47
+ format?: 'vtt' | 'srt' | 'ass' | 'ssa';
48
+ }
49
+ interface AudioTrack {
50
+ id: string;
51
+ name: string;
52
+ lang: string;
53
+ }
54
+ interface VideoFilters {
55
+ brightness: number;
56
+ contrast: number;
57
+ saturate: number;
58
+ hue: number;
59
+ }
60
+ interface VideoMetadata {
61
+ filename: string;
62
+ fileSize: number | null;
63
+ duration: number;
64
+ container: string;
65
+ width: number;
66
+ height: number;
67
+ frameRate: number | null;
68
+ videoBitrate: number | null;
69
+ videoCodec: string | null;
70
+ colorSpace: string | null;
71
+ audioTracks: AudioTrackMeta[];
72
+ subtitleTracks: SubtitleTrackMeta[];
73
+ }
74
+ interface AudioTrackMeta {
75
+ index: number;
76
+ codec: string | null;
77
+ channels: number | null;
78
+ sampleRate: number | null;
79
+ language: string | null;
80
+ bitrate: number | null;
81
+ }
82
+ interface SubtitleTrackMeta {
83
+ index: number;
84
+ format: string | null;
85
+ language: string | null;
86
+ }
87
+
88
+ declare function useVideoFilters(videoRef: RefObject<HTMLVideoElement | null>): {
89
+ filters: VideoFilters;
90
+ zoom: number;
91
+ setFilters: react.Dispatch<react.SetStateAction<VideoFilters>>;
92
+ setZoom: react.Dispatch<react.SetStateAction<number>>;
93
+ resetFilters: () => void;
94
+ };
95
+
96
+ declare class UniversalSubtitleManager {
97
+ private records;
98
+ private videoElement;
99
+ private nextId;
100
+ constructor(videoElement?: HTMLVideoElement);
101
+ setVideoElement(videoElement: HTMLVideoElement): void;
102
+ addSubtitleFiles(files: File[]): Promise<Subtitle[]>;
103
+ removeSubtitle(id: string): boolean;
104
+ switchSubtitle(id: string): void;
105
+ /**
106
+ * Sets a time offset (in seconds) for a VTT/SRT subtitle.
107
+ * Regenerates the blob URL with shifted timestamps and updates the track element.
108
+ */
109
+ setOffset(id: string, offsetSeconds: number): Promise<void>;
110
+ /** Returns parsed cue index for a subtitle (empty array for ASS/SSA). */
111
+ getCues(id: string): SubtitleCue[];
112
+ /** Returns cues for all loaded VTT/SRT subtitles merged, for global search. */
113
+ getAllCues(): SubtitleCue[];
114
+ getSubtitles(): Subtitle[];
115
+ clearSubtitles(): void;
116
+ destroy(): void;
117
+ importSubtitles(subtitles: Subtitle[]): void;
118
+ }
119
+
120
+ interface UseSubtitlesOptions {
121
+ onError?: (message: string) => void;
122
+ onSuccess?: (message: string) => void;
123
+ }
124
+ declare function useSubtitles(options?: UseSubtitlesOptions): {
125
+ subtitles: Subtitle[];
126
+ activeSubtitle: string;
127
+ managerRef: react.MutableRefObject<UniversalSubtitleManager | null>;
128
+ initManager: (videoEl: HTMLVideoElement) => void;
129
+ importSubtitles: (subs: Subtitle[]) => void;
130
+ reset: () => void;
131
+ addSubtitleFiles: (files: File[]) => Promise<void>;
132
+ removeSubtitle: (id: string) => void;
133
+ switchSubtitle: (id: string) => void;
134
+ };
135
+
136
+ declare function usePlaylist(): {
137
+ playlist: PlaylistItem[];
138
+ currentIndex: number | null;
139
+ currentItem: PlaylistItem | null;
140
+ selectItem: (index: number) => void;
141
+ appendItem: (item: PlaylistItem) => void;
142
+ removeItem: (index: number) => void;
143
+ reorderItems: (newPlaylist: PlaylistItem[]) => void;
144
+ addFiles: (files: File[]) => Promise<{
145
+ id: `${string}-${string}-${string}-${string}-${string}`;
146
+ name: string;
147
+ url: string;
148
+ type: "video";
149
+ file: File;
150
+ duration: number;
151
+ }[]>;
152
+ replaceWithFile: (file: File) => void;
153
+ nextItem: () => void;
154
+ prevItem: () => void;
155
+ parseFiles: (files: FileList) => {
156
+ videoFiles: File[];
157
+ subtitleFiles: File[];
158
+ };
159
+ setPlaylist: react.Dispatch<react.SetStateAction<PlaylistItem[]>>;
160
+ setCurrentIndex: react.Dispatch<react.SetStateAction<number | null>>;
161
+ };
162
+
163
+ type ShortcutAction = 'play-pause' | 'seek-forward-5' | 'seek-backward-5' | 'seek-forward-30' | 'seek-backward-30' | 'volume-up' | 'volume-down' | 'mute' | 'fullscreen' | 'next-item' | 'prev-item' | 'screenshot' | 'show-shortcuts' | 'next-chapter' | 'prev-chapter';
164
+ interface ShortcutBinding {
165
+ action: ShortcutAction;
166
+ label: string;
167
+ defaultKey: string;
168
+ key: string;
169
+ modifiers?: {
170
+ ctrl?: boolean;
171
+ shift?: boolean;
172
+ alt?: boolean;
173
+ };
174
+ }
175
+
176
+ type ShortcutHandlers = Partial<Record<ShortcutAction, () => void>>;
177
+ declare function useKeyboardShortcuts(shortcuts: ShortcutBinding[], handlers: ShortcutHandlers): void;
178
+
179
+ declare function useFullscreen(containerRef: RefObject<HTMLDivElement | null>): {
180
+ isFullscreen: boolean;
181
+ toggle: () => void;
182
+ };
183
+
184
+ declare function usePictureInPicture(videoRef: RefObject<HTMLVideoElement | null>): {
185
+ isPiP: boolean;
186
+ isSupported: boolean;
187
+ enter: () => Promise<void>;
188
+ exit: () => Promise<void>;
189
+ toggle: () => Promise<void>;
190
+ };
191
+
192
+ declare function useProgressPersistence(videoRef: RefObject<HTMLVideoElement | null>, currentVideoName: string | null): void;
193
+
194
+ declare function useVideoInfo(videoRef: RefObject<HTMLVideoElement | null>, currentFile: File | null): {
195
+ metadata: VideoMetadata | null;
196
+ enrichMetadata: (extra: Partial<VideoMetadata>) => void;
197
+ };
198
+
199
+ interface UseMediaSessionOptions {
200
+ title: string | null;
201
+ artwork?: string | null;
202
+ onPlay: () => void;
203
+ onPause: () => void;
204
+ onNext: () => void;
205
+ onPrev: () => void;
206
+ onSeekForward: () => void;
207
+ onSeekBackward: () => void;
208
+ }
209
+ declare function useMediaSession(options: UseMediaSessionOptions): void;
210
+
211
+ interface SimplePlayerFile {
212
+ name: string;
213
+ file: File;
214
+ url: string;
215
+ externalSubtitles?: Subtitle[];
216
+ }
217
+
218
+ interface MKVPlayerFile {
219
+ name: string;
220
+ file: File;
221
+ videoUrl?: string;
222
+ audioTracks: AudioTrack[];
223
+ subtitleTracks: Subtitle[];
224
+ activeAudioTrack: string;
225
+ activeSubtitleTrack: string;
226
+ }
227
+
228
+ type ProcessedFile = SimplePlayerFile | MKVPlayerFile;
229
+ interface VideoPlayer {
230
+ initialize(videoElement: HTMLVideoElement): Promise<ProcessedFile>;
231
+ getAudioTracks(): AudioTrack[];
232
+ getSubtitles(): Subtitle[];
233
+ getChapters?(): Chapter[];
234
+ switchAudioTrack(trackId: string): Promise<void>;
235
+ switchSubtitle(trackId: string): Promise<void>;
236
+ destroy(): void;
237
+ cancel?(): void;
238
+ /**
239
+ * Resolves when track metadata is fully populated. Only meaningful for
240
+ * MKVPlayer on the native path (where the probe runs after initialize()).
241
+ * For all other players/paths this is already resolved when initialize() returns.
242
+ */
243
+ tracksReady?: Promise<void>;
244
+ }
245
+
246
+ declare function useChapters(videoRef: RefObject<HTMLVideoElement>, playerRef: RefObject<VideoPlayer | null>): {
247
+ chapters: Chapter[];
248
+ currentChapter: Chapter | null;
249
+ goToChapter: (index: number) => void;
250
+ };
251
+
252
+ export { type ShortcutHandlers, type UseMediaSessionOptions, type UseSubtitlesOptions, useChapters, useFullscreen, useKeyboardShortcuts, useMediaSession, usePictureInPicture, usePlaylist, useProgressPersistence, useSubtitles, useVideoFilters, useVideoInfo, useVideoPlayback };
@@ -0,0 +1,252 @@
1
+ import * as react from 'react';
2
+ import { RefObject } from 'react';
3
+
4
+ declare function useVideoPlayback(videoRef: RefObject<HTMLVideoElement | null>): {
5
+ isPlaying: boolean;
6
+ progress: number;
7
+ duration: number;
8
+ volume: number;
9
+ isMuted: boolean;
10
+ playbackRate: number;
11
+ loop: boolean;
12
+ togglePlay: () => void;
13
+ seek: (t: number) => void;
14
+ setVolume: (v: number) => void;
15
+ toggleMute: () => void;
16
+ setPlaybackRate: (r: number) => void;
17
+ frameStep: (direction: "forward" | "backward") => void;
18
+ toggleLoop: () => void;
19
+ };
20
+
21
+ interface Chapter {
22
+ index: number;
23
+ title: string;
24
+ startTime: number;
25
+ endTime: number;
26
+ }
27
+ interface PlaylistItem {
28
+ id: string;
29
+ name: string;
30
+ url: string;
31
+ type: 'video' | 'stream';
32
+ file?: File;
33
+ duration?: number;
34
+ }
35
+ interface SubtitleCue {
36
+ startTime: number;
37
+ endTime: number;
38
+ text: string;
39
+ }
40
+ interface Subtitle {
41
+ id: string;
42
+ name: string;
43
+ lang: string;
44
+ url?: string;
45
+ type: 'embedded' | 'external';
46
+ /** Detected source format (vtt, srt, ass, ssa). Defaults to 'vtt'. */
47
+ format?: 'vtt' | 'srt' | 'ass' | 'ssa';
48
+ }
49
+ interface AudioTrack {
50
+ id: string;
51
+ name: string;
52
+ lang: string;
53
+ }
54
+ interface VideoFilters {
55
+ brightness: number;
56
+ contrast: number;
57
+ saturate: number;
58
+ hue: number;
59
+ }
60
+ interface VideoMetadata {
61
+ filename: string;
62
+ fileSize: number | null;
63
+ duration: number;
64
+ container: string;
65
+ width: number;
66
+ height: number;
67
+ frameRate: number | null;
68
+ videoBitrate: number | null;
69
+ videoCodec: string | null;
70
+ colorSpace: string | null;
71
+ audioTracks: AudioTrackMeta[];
72
+ subtitleTracks: SubtitleTrackMeta[];
73
+ }
74
+ interface AudioTrackMeta {
75
+ index: number;
76
+ codec: string | null;
77
+ channels: number | null;
78
+ sampleRate: number | null;
79
+ language: string | null;
80
+ bitrate: number | null;
81
+ }
82
+ interface SubtitleTrackMeta {
83
+ index: number;
84
+ format: string | null;
85
+ language: string | null;
86
+ }
87
+
88
+ declare function useVideoFilters(videoRef: RefObject<HTMLVideoElement | null>): {
89
+ filters: VideoFilters;
90
+ zoom: number;
91
+ setFilters: react.Dispatch<react.SetStateAction<VideoFilters>>;
92
+ setZoom: react.Dispatch<react.SetStateAction<number>>;
93
+ resetFilters: () => void;
94
+ };
95
+
96
+ declare class UniversalSubtitleManager {
97
+ private records;
98
+ private videoElement;
99
+ private nextId;
100
+ constructor(videoElement?: HTMLVideoElement);
101
+ setVideoElement(videoElement: HTMLVideoElement): void;
102
+ addSubtitleFiles(files: File[]): Promise<Subtitle[]>;
103
+ removeSubtitle(id: string): boolean;
104
+ switchSubtitle(id: string): void;
105
+ /**
106
+ * Sets a time offset (in seconds) for a VTT/SRT subtitle.
107
+ * Regenerates the blob URL with shifted timestamps and updates the track element.
108
+ */
109
+ setOffset(id: string, offsetSeconds: number): Promise<void>;
110
+ /** Returns parsed cue index for a subtitle (empty array for ASS/SSA). */
111
+ getCues(id: string): SubtitleCue[];
112
+ /** Returns cues for all loaded VTT/SRT subtitles merged, for global search. */
113
+ getAllCues(): SubtitleCue[];
114
+ getSubtitles(): Subtitle[];
115
+ clearSubtitles(): void;
116
+ destroy(): void;
117
+ importSubtitles(subtitles: Subtitle[]): void;
118
+ }
119
+
120
+ interface UseSubtitlesOptions {
121
+ onError?: (message: string) => void;
122
+ onSuccess?: (message: string) => void;
123
+ }
124
+ declare function useSubtitles(options?: UseSubtitlesOptions): {
125
+ subtitles: Subtitle[];
126
+ activeSubtitle: string;
127
+ managerRef: react.MutableRefObject<UniversalSubtitleManager | null>;
128
+ initManager: (videoEl: HTMLVideoElement) => void;
129
+ importSubtitles: (subs: Subtitle[]) => void;
130
+ reset: () => void;
131
+ addSubtitleFiles: (files: File[]) => Promise<void>;
132
+ removeSubtitle: (id: string) => void;
133
+ switchSubtitle: (id: string) => void;
134
+ };
135
+
136
+ declare function usePlaylist(): {
137
+ playlist: PlaylistItem[];
138
+ currentIndex: number | null;
139
+ currentItem: PlaylistItem | null;
140
+ selectItem: (index: number) => void;
141
+ appendItem: (item: PlaylistItem) => void;
142
+ removeItem: (index: number) => void;
143
+ reorderItems: (newPlaylist: PlaylistItem[]) => void;
144
+ addFiles: (files: File[]) => Promise<{
145
+ id: `${string}-${string}-${string}-${string}-${string}`;
146
+ name: string;
147
+ url: string;
148
+ type: "video";
149
+ file: File;
150
+ duration: number;
151
+ }[]>;
152
+ replaceWithFile: (file: File) => void;
153
+ nextItem: () => void;
154
+ prevItem: () => void;
155
+ parseFiles: (files: FileList) => {
156
+ videoFiles: File[];
157
+ subtitleFiles: File[];
158
+ };
159
+ setPlaylist: react.Dispatch<react.SetStateAction<PlaylistItem[]>>;
160
+ setCurrentIndex: react.Dispatch<react.SetStateAction<number | null>>;
161
+ };
162
+
163
+ type ShortcutAction = 'play-pause' | 'seek-forward-5' | 'seek-backward-5' | 'seek-forward-30' | 'seek-backward-30' | 'volume-up' | 'volume-down' | 'mute' | 'fullscreen' | 'next-item' | 'prev-item' | 'screenshot' | 'show-shortcuts' | 'next-chapter' | 'prev-chapter';
164
+ interface ShortcutBinding {
165
+ action: ShortcutAction;
166
+ label: string;
167
+ defaultKey: string;
168
+ key: string;
169
+ modifiers?: {
170
+ ctrl?: boolean;
171
+ shift?: boolean;
172
+ alt?: boolean;
173
+ };
174
+ }
175
+
176
+ type ShortcutHandlers = Partial<Record<ShortcutAction, () => void>>;
177
+ declare function useKeyboardShortcuts(shortcuts: ShortcutBinding[], handlers: ShortcutHandlers): void;
178
+
179
+ declare function useFullscreen(containerRef: RefObject<HTMLDivElement | null>): {
180
+ isFullscreen: boolean;
181
+ toggle: () => void;
182
+ };
183
+
184
+ declare function usePictureInPicture(videoRef: RefObject<HTMLVideoElement | null>): {
185
+ isPiP: boolean;
186
+ isSupported: boolean;
187
+ enter: () => Promise<void>;
188
+ exit: () => Promise<void>;
189
+ toggle: () => Promise<void>;
190
+ };
191
+
192
+ declare function useProgressPersistence(videoRef: RefObject<HTMLVideoElement | null>, currentVideoName: string | null): void;
193
+
194
+ declare function useVideoInfo(videoRef: RefObject<HTMLVideoElement | null>, currentFile: File | null): {
195
+ metadata: VideoMetadata | null;
196
+ enrichMetadata: (extra: Partial<VideoMetadata>) => void;
197
+ };
198
+
199
+ interface UseMediaSessionOptions {
200
+ title: string | null;
201
+ artwork?: string | null;
202
+ onPlay: () => void;
203
+ onPause: () => void;
204
+ onNext: () => void;
205
+ onPrev: () => void;
206
+ onSeekForward: () => void;
207
+ onSeekBackward: () => void;
208
+ }
209
+ declare function useMediaSession(options: UseMediaSessionOptions): void;
210
+
211
+ interface SimplePlayerFile {
212
+ name: string;
213
+ file: File;
214
+ url: string;
215
+ externalSubtitles?: Subtitle[];
216
+ }
217
+
218
+ interface MKVPlayerFile {
219
+ name: string;
220
+ file: File;
221
+ videoUrl?: string;
222
+ audioTracks: AudioTrack[];
223
+ subtitleTracks: Subtitle[];
224
+ activeAudioTrack: string;
225
+ activeSubtitleTrack: string;
226
+ }
227
+
228
+ type ProcessedFile = SimplePlayerFile | MKVPlayerFile;
229
+ interface VideoPlayer {
230
+ initialize(videoElement: HTMLVideoElement): Promise<ProcessedFile>;
231
+ getAudioTracks(): AudioTrack[];
232
+ getSubtitles(): Subtitle[];
233
+ getChapters?(): Chapter[];
234
+ switchAudioTrack(trackId: string): Promise<void>;
235
+ switchSubtitle(trackId: string): Promise<void>;
236
+ destroy(): void;
237
+ cancel?(): void;
238
+ /**
239
+ * Resolves when track metadata is fully populated. Only meaningful for
240
+ * MKVPlayer on the native path (where the probe runs after initialize()).
241
+ * For all other players/paths this is already resolved when initialize() returns.
242
+ */
243
+ tracksReady?: Promise<void>;
244
+ }
245
+
246
+ declare function useChapters(videoRef: RefObject<HTMLVideoElement>, playerRef: RefObject<VideoPlayer | null>): {
247
+ chapters: Chapter[];
248
+ currentChapter: Chapter | null;
249
+ goToChapter: (index: number) => void;
250
+ };
251
+
252
+ export { type ShortcutHandlers, type UseMediaSessionOptions, type UseSubtitlesOptions, useChapters, useFullscreen, useKeyboardShortcuts, useMediaSession, usePictureInPicture, usePlaylist, useProgressPersistence, useSubtitles, useVideoFilters, useVideoInfo, useVideoPlayback };