@rixl/media-core 1.0.2 → 1.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.
- package/README.md +74 -84
- package/dist/chunks/B-QvSqjj.js +1 -0
- package/dist/chunks/B2DDE32u.js +1 -0
- package/dist/chunks/BZFoe94X.js +1 -0
- package/dist/chunks/DMkwQjgA.js +1 -0
- package/dist/index.d.ts +2005 -82
- package/dist/index.js +6 -73
- package/package.json +13 -6
- package/dist/api/analytics.d.ts +0 -16
- package/dist/api/analytics.js +0 -227
- package/dist/api/feed.js +0 -52
- package/dist/api/heatmap.d.ts +0 -6
- package/dist/api/heatmap.js +0 -28
- package/dist/api/hot-segments.d.ts +0 -7
- package/dist/api/hot-segments.js +0 -20
- package/dist/api/image.js +0 -11
- package/dist/api/rixl-client.d.ts +0 -3
- package/dist/api/rixl-client.js +0 -14
- package/dist/api/upload.d.ts +0 -76
- package/dist/api/upload.js +0 -150
- package/dist/api/video.js +0 -11
- package/dist/lib/analytics-builders.d.ts +0 -59
- package/dist/lib/analytics-builders.js +0 -88
- package/dist/lib/analytics-initialize.d.ts +0 -1
- package/dist/lib/analytics-initialize.js +0 -59
- package/dist/lib/analytics-utils.d.ts +0 -43
- package/dist/lib/analytics-utils.js +0 -85
- package/dist/lib/analytics-wire.js +0 -153
- package/dist/lib/buffer-progress.d.ts +0 -20
- package/dist/lib/buffer-progress.js +0 -37
- package/dist/lib/caption-styles.d.ts +0 -20
- package/dist/lib/caption-styles.js +0 -56
- package/dist/lib/caption-utils.d.ts +0 -80
- package/dist/lib/caption-utils.js +0 -111
- package/dist/lib/chapter-utils.d.ts +0 -102
- package/dist/lib/chapter-utils.js +0 -147
- package/dist/lib/click-disambiguation.d.ts +0 -44
- package/dist/lib/click-disambiguation.js +0 -80
- package/dist/lib/click-outside.d.ts +0 -68
- package/dist/lib/click-outside.js +0 -64
- package/dist/lib/colors.d.ts +0 -6
- package/dist/lib/colors.js +0 -16
- package/dist/lib/content-analytics.d.ts +0 -27
- package/dist/lib/content-analytics.js +0 -38
- package/dist/lib/environment-effects.d.ts +0 -89
- package/dist/lib/environment-effects.js +0 -139
- package/dist/lib/feed-autoscroll.d.ts +0 -10
- package/dist/lib/feed-autoscroll.js +0 -13
- package/dist/lib/feed-container-height.d.ts +0 -7
- package/dist/lib/feed-container-height.js +0 -11
- package/dist/lib/feed-description-overflow.d.ts +0 -1
- package/dist/lib/feed-description-overflow.js +0 -9
- package/dist/lib/feed-dwell.d.ts +0 -2
- package/dist/lib/feed-dwell.js +0 -8
- package/dist/lib/feed-loop.d.ts +0 -10
- package/dist/lib/feed-loop.js +0 -21
- package/dist/lib/feed-scroll.d.ts +0 -11
- package/dist/lib/feed-scroll.js +0 -19
- package/dist/lib/feed-transforms.d.ts +0 -5
- package/dist/lib/feed-transforms.js +0 -30
- package/dist/lib/filter-undefined.js +0 -11
- package/dist/lib/fullscreen-utils.d.ts +0 -1
- package/dist/lib/fullscreen-utils.js +0 -7
- package/dist/lib/hls-buffer-flush.js +0 -46
- package/dist/lib/hls-error-recovery.d.ts +0 -35
- package/dist/lib/hls-error-recovery.js +0 -75
- package/dist/lib/hls-event-handlers.js +0 -61
- package/dist/lib/hls-loader.js +0 -23
- package/dist/lib/hls-setup.d.ts +0 -72
- package/dist/lib/hls-setup.js +0 -144
- package/dist/lib/hls-utils.d.ts +0 -31
- package/dist/lib/hls-utils.js +0 -34
- package/dist/lib/image-render.d.ts +0 -29
- package/dist/lib/image-render.js +0 -63
- package/dist/lib/keyboard-config.d.ts +0 -66
- package/dist/lib/keyboard-config.js +0 -127
- package/dist/lib/keyboard-utils.d.ts +0 -4
- package/dist/lib/keyboard-utils.js +0 -11
- package/dist/lib/lifecycle-effects.d.ts +0 -67
- package/dist/lib/lifecycle-effects.js +0 -78
- package/dist/lib/loading-debounce.d.ts +0 -26
- package/dist/lib/loading-debounce.js +0 -38
- package/dist/lib/numbers.d.ts +0 -6
- package/dist/lib/numbers.js +0 -36
- package/dist/lib/pip-utils.d.ts +0 -16
- package/dist/lib/pip-utils.js +0 -32
- package/dist/lib/playback-handlers.d.ts +0 -33
- package/dist/lib/playback-handlers.js +0 -51
- package/dist/lib/playback-slider.d.ts +0 -23
- package/dist/lib/playback-slider.js +0 -24
- package/dist/lib/player-labels.d.ts +0 -23
- package/dist/lib/player-labels.js +0 -132
- package/dist/lib/player-menu-labels.d.ts +0 -80
- package/dist/lib/player-menu-labels.js +0 -829
- package/dist/lib/player-ui-actions.d.ts +0 -10
- package/dist/lib/player-ui-actions.js +0 -58
- package/dist/lib/progress-fraction.d.ts +0 -11
- package/dist/lib/progress-fraction.js +0 -20
- package/dist/lib/progress-persist.d.ts +0 -28
- package/dist/lib/progress-persist.js +0 -54
- package/dist/lib/segment-analytics.d.ts +0 -20
- package/dist/lib/segment-analytics.js +0 -29
- package/dist/lib/subtitle-logic.d.ts +0 -31
- package/dist/lib/subtitle-logic.js +0 -75
- package/dist/lib/subtitle-utils.d.ts +0 -16
- package/dist/lib/subtitle-utils.js +0 -37
- package/dist/lib/time.d.ts +0 -1
- package/dist/lib/time.js +0 -17
- package/dist/lib/track-persistence.d.ts +0 -4
- package/dist/lib/track-persistence.js +0 -35
- package/dist/lib/video-preload-config.d.ts +0 -34
- package/dist/lib/video-preload-config.js +0 -41
- package/dist/lib/video-spinner.d.ts +0 -25
- package/dist/lib/video-spinner.js +0 -35
- package/dist/lib/video-stats.d.ts +0 -28
- package/dist/lib/video-stats.js +0 -44
- package/dist/lib/volume-utils.d.ts +0 -59
- package/dist/lib/volume-utils.js +0 -55
- package/dist/progressbar.d.ts +0 -10
- package/dist/progressbar.js +0 -125
- package/dist/store/analytics-context-store.d.ts +0 -4
- package/dist/store/analytics-context-store.js +0 -13
- package/dist/store/analytics-device-store.d.ts +0 -8
- package/dist/store/analytics-device-store.js +0 -44
- package/dist/store/analytics-session-store.d.ts +0 -8
- package/dist/store/analytics-session-store.js +0 -40
- package/dist/store/analytics-store.d.ts +0 -30
- package/dist/store/analytics-store.js +0 -88
- package/dist/store/controls-store.d.ts +0 -16
- package/dist/store/controls-store.js +0 -53
- package/dist/store/feed-cache-store.d.ts +0 -24
- package/dist/store/feed-cache-store.js +0 -165
- package/dist/store/feed-interaction-store.d.ts +0 -17
- package/dist/store/feed-interaction-store.js +0 -32
- package/dist/store/feed-store.d.ts +0 -16
- package/dist/store/feed-store.js +0 -38
- package/dist/store/generic-store.js +0 -24
- package/dist/store/image-store.d.ts +0 -2
- package/dist/store/image-store.js +0 -8
- package/dist/store/player-container-element-store.d.ts +0 -10
- package/dist/store/player-container-element-store.js +0 -38
- package/dist/store/player-global-actions.js +0 -53
- package/dist/store/player-media-element-store.d.ts +0 -10
- package/dist/store/player-media-element-store.js +0 -38
- package/dist/store/player-store-actions.js +0 -197
- package/dist/store/player-store.d.ts +0 -94
- package/dist/store/player-store.js +0 -127
- package/dist/store/player-track-actions.js +0 -44
- package/dist/store/postStore.d.ts +0 -20
- package/dist/store/postStore.js +0 -112
- package/dist/store/settings/constants.d.ts +0 -15
- package/dist/store/settings/constants.js +0 -47
- package/dist/store/settings/defaults.d.ts +0 -2
- package/dist/store/settings/defaults.js +0 -24
- package/dist/store/settings/storage.d.ts +0 -3
- package/dist/store/settings/storage.js +0 -28
- package/dist/store/settings/store.d.ts +0 -32
- package/dist/store/settings/store.js +0 -59
- package/dist/store/settings/types.d.ts +0 -27
- package/dist/store/settings/validation.d.ts +0 -7
- package/dist/store/settings/validation.js +0 -55
- package/dist/store/settings-menu-store.d.ts +0 -19
- package/dist/store/settings-menu-store.js +0 -30
- package/dist/store/video-store.d.ts +0 -2
- package/dist/store/video-store.js +0 -8
- package/dist/types/analytics.d.ts +0 -113
- package/dist/types/feed.d.ts +0 -21
- package/dist/types/file.d.ts +0 -12
- package/dist/types/heatmap.d.ts +0 -5
- package/dist/types/hot-segments.d.ts +0 -9
- package/dist/types/image.d.ts +0 -9
- package/dist/types/uuid.d.ts +0 -1
- package/dist/types/video.d.ts +0 -22
package/dist/index.d.ts
CHANGED
|
@@ -1,82 +1,2005 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
import { AuthConfig, ConnectConfig, client } from "@rixl/sdk";
|
|
2
|
+
import { MapStore, ReadableAtom, WritableAtom } from "nanostores";
|
|
3
|
+
import Hls, { MediaPlaylist } from "hls.js";
|
|
4
|
+
interface ProgressBarProps {
|
|
5
|
+
video: HTMLVideoElement;
|
|
6
|
+
onDraggingChange?: (isDragging: boolean) => void;
|
|
7
|
+
onProgressChange?: (progress: number) => void;
|
|
8
|
+
}
|
|
9
|
+
type ProgressBarCoordinates = {
|
|
10
|
+
clientX: number;
|
|
11
|
+
} | {
|
|
12
|
+
touches: ArrayLike<{
|
|
13
|
+
clientX: number;
|
|
14
|
+
}>;
|
|
15
|
+
changedTouches?: ArrayLike<{
|
|
16
|
+
clientX: number;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
type ProgressBarStartEvent = ProgressBarCoordinates & {
|
|
20
|
+
currentTarget: EventTarget | null;
|
|
21
|
+
};
|
|
22
|
+
interface ProgressBarControls {
|
|
23
|
+
handleDragStart: (event: ProgressBarStartEvent) => void;
|
|
24
|
+
destroy: () => void;
|
|
25
|
+
}
|
|
26
|
+
export declare const initProgressBar: ({ video, onDraggingChange, onProgressChange }: ProgressBarProps) => ProgressBarControls;
|
|
27
|
+
type FileStatus = "uploading" | "uploaded" | "processing" | "ready" | "error";
|
|
28
|
+
interface FileData {
|
|
29
|
+
id: string;
|
|
30
|
+
project_id: string;
|
|
31
|
+
format: string;
|
|
32
|
+
url: string;
|
|
33
|
+
name: string;
|
|
34
|
+
size: number;
|
|
35
|
+
status: FileStatus;
|
|
36
|
+
created_at: Date;
|
|
37
|
+
updated_at: Date;
|
|
38
|
+
}
|
|
39
|
+
interface ImageData {
|
|
40
|
+
id: string;
|
|
41
|
+
thumbhash: string;
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
attached_to_video: boolean;
|
|
45
|
+
file: FileData;
|
|
46
|
+
}
|
|
47
|
+
interface Chapter {
|
|
48
|
+
title: string;
|
|
49
|
+
start_time_sec: number;
|
|
50
|
+
end_time_sec: number;
|
|
51
|
+
duration_label: string;
|
|
52
|
+
}
|
|
53
|
+
interface VideoData {
|
|
54
|
+
id: string;
|
|
55
|
+
file: FileData;
|
|
56
|
+
poster: ImageData;
|
|
57
|
+
duration: number;
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
codec: string;
|
|
61
|
+
bitrate: number;
|
|
62
|
+
framerate: string;
|
|
63
|
+
hdr: boolean;
|
|
64
|
+
chapters?: Chapter[];
|
|
65
|
+
plan_type?: "free" | "pro" | "pay-as-you-go" | "custom";
|
|
66
|
+
}
|
|
67
|
+
interface FeedPost {
|
|
68
|
+
id: string;
|
|
69
|
+
feedId: string;
|
|
70
|
+
creatorId: string;
|
|
71
|
+
type: "image" | "video";
|
|
72
|
+
description: string;
|
|
73
|
+
createdAt: string;
|
|
74
|
+
image?: ImageData;
|
|
75
|
+
video?: VideoData;
|
|
76
|
+
projectId?: string;
|
|
77
|
+
planType?: string;
|
|
78
|
+
updatedAt?: string;
|
|
79
|
+
}
|
|
80
|
+
interface FeedPostsResponse {
|
|
81
|
+
posts: FeedPost[];
|
|
82
|
+
total: number;
|
|
83
|
+
offset: number;
|
|
84
|
+
limit: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get a feed post by ID, using cache if available
|
|
88
|
+
* @param feedId ID of the feed
|
|
89
|
+
* @param postId ID of the post to fetch
|
|
90
|
+
* @returns Promise with the feed post
|
|
91
|
+
*/
|
|
92
|
+
export declare const getFeedPost: (feedId: string, postId: string) => Promise<FeedPost>;
|
|
93
|
+
/**
|
|
94
|
+
* Get posts from a specific feed, with pagination
|
|
95
|
+
* @param feedId ID of the feed
|
|
96
|
+
* @param offset Pagination offset
|
|
97
|
+
* @param limit Maximum number of items to return
|
|
98
|
+
* @returns Promise with feed posts response
|
|
99
|
+
*/
|
|
100
|
+
export declare const getFeedPosts: (feedId: string, offset?: number, limit?: number) => Promise<FeedPostsResponse>;
|
|
101
|
+
export declare const getImage: (imageId: string) => Promise<ImageData>;
|
|
102
|
+
export declare const getVideo: (videoId: string) => Promise<VideoData>;
|
|
103
|
+
type UUID = `${string}-${string}-${string}-${string}-${string}`;
|
|
104
|
+
export declare const formatTime: (seconds: number) => string;
|
|
105
|
+
/**
|
|
106
|
+
* Returns a copy of `obj` with all `undefined` values removed.
|
|
107
|
+
* A type assertion is required because `Object.fromEntries` cannot infer
|
|
108
|
+
* `Partial<T>` from filtered entries.
|
|
109
|
+
*/
|
|
110
|
+
export declare function filterUndefined<T extends Record<string, unknown>>(obj: T): Partial<T>;
|
|
111
|
+
/**
|
|
112
|
+
* Clamp a value between min and max bounds
|
|
113
|
+
*/
|
|
114
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
115
|
+
export declare function formatNumber(number: number): string;
|
|
116
|
+
export declare function formatNumberSm(number: number): string;
|
|
117
|
+
type CSSProperties = Record<string, string | number | null | undefined>;
|
|
118
|
+
interface ImageRenderState {
|
|
119
|
+
isLoading: boolean;
|
|
120
|
+
isVisible: boolean;
|
|
121
|
+
aspectRatio: number;
|
|
122
|
+
}
|
|
123
|
+
type ImageRenderAction = {
|
|
124
|
+
type: "reset";
|
|
125
|
+
aspectRatio?: number;
|
|
126
|
+
} | {
|
|
127
|
+
type: "loaded";
|
|
128
|
+
} | {
|
|
129
|
+
type: "visible";
|
|
130
|
+
} | {
|
|
131
|
+
type: "set_aspect_ratio";
|
|
132
|
+
aspectRatio: number;
|
|
133
|
+
};
|
|
134
|
+
export declare const DEFAULT_IMAGE_ASPECT_RATIO = .5;
|
|
135
|
+
export declare const IMAGE_ASPECT_RATIO_THRESHOLD = .7;
|
|
136
|
+
export declare const initialImageRenderState: ImageRenderState;
|
|
137
|
+
interface ImageContainerStyleParams {
|
|
138
|
+
aspectRatio: number;
|
|
139
|
+
isVisible: boolean;
|
|
140
|
+
thumbhashUrl?: string;
|
|
141
|
+
}
|
|
142
|
+
export declare function imageRenderReducer(state: ImageRenderState, action: ImageRenderAction): ImageRenderState;
|
|
143
|
+
export declare function getImageAspectRatio(width?: number, height?: number): number | undefined;
|
|
144
|
+
export declare function getImageContainerStyle({ aspectRatio, isVisible, thumbhashUrl }: ImageContainerStyleParams): CSSProperties;
|
|
145
|
+
export declare function getImageStyle(aspectRatio: number, isLoading: boolean): CSSProperties;
|
|
146
|
+
export declare function hexToRGB(hex: string): {
|
|
147
|
+
r: number;
|
|
148
|
+
g: number;
|
|
149
|
+
b: number;
|
|
150
|
+
};
|
|
151
|
+
export declare function hexToRGBAString(hex: string, alpha?: number): string;
|
|
152
|
+
/**
|
|
153
|
+
* Check if keyboard event should be ignored (input fields, textareas, etc.)
|
|
154
|
+
*/
|
|
155
|
+
export declare function shouldIgnoreKeyboardEvent(target: EventTarget | null): boolean;
|
|
156
|
+
export declare const PLAYER_LANGUAGES: readonly ["en", "de", "es", "fr", "it", "pl", "ru", "tr", "uk"];
|
|
157
|
+
type PlayerLanguage = (typeof PLAYER_LANGUAGES)[number];
|
|
158
|
+
/**
|
|
159
|
+
* Control-bar strings for the player UI. Values mirror the `controls.*` and
|
|
160
|
+
* `captions.toggle` keys of packages/react/src/locales/<lang>.json — one
|
|
161
|
+
* label per button regardless of state, keyboard hint included, same as the
|
|
162
|
+
* React tooltips.
|
|
163
|
+
*/
|
|
164
|
+
interface PlayerControlLabels {
|
|
165
|
+
play: string;
|
|
166
|
+
pause: string;
|
|
167
|
+
replay: string;
|
|
168
|
+
mute: string;
|
|
169
|
+
unmute: string;
|
|
170
|
+
volume: string;
|
|
171
|
+
settings: string;
|
|
172
|
+
pip: string;
|
|
173
|
+
fullScreen: string;
|
|
174
|
+
captionsToggle: string;
|
|
175
|
+
}
|
|
176
|
+
/** Same semantics as the React resolveSupportedLanguage: lowercase exact match, fallback en. */
|
|
177
|
+
export declare function resolvePlayerLanguage(code: string | null | undefined): PlayerLanguage;
|
|
178
|
+
export declare function getPlayerControlLabels(code: string | null | undefined): PlayerControlLabels;
|
|
179
|
+
/**
|
|
180
|
+
* Media SDK video keyboard shortcuts configuration
|
|
181
|
+
*/
|
|
182
|
+
type VideoSdkKey = "Space" | "KeyK" | "KeyJ" | "KeyL" | "KeyI" | "KeyF" | "KeyM" | "KeyN" | "KeyP" | "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight" | "MediaPlay" | "MediaPause" | "MediaPlayPause" | "F1" | "F12" | "Digit0" | "Digit1" | "Digit2" | "Digit3" | "Digit4" | "Digit5" | "Digit6" | "Digit7" | "Digit8" | "Digit9";
|
|
183
|
+
export declare const ShortcutAction: {
|
|
184
|
+
readonly PLAY_PAUSE: "play_pause";
|
|
185
|
+
readonly SEEK_10: "seek_10";
|
|
186
|
+
readonly SEEK_5: "seek_5";
|
|
187
|
+
readonly FULLSCREEN: "fullscreen";
|
|
188
|
+
readonly MINIPLAYER: "miniplayer";
|
|
189
|
+
readonly MUTE: "mute";
|
|
190
|
+
readonly VOLUME: "volume";
|
|
191
|
+
};
|
|
192
|
+
export type ShortcutAction = (typeof ShortcutAction)[keyof typeof ShortcutAction];
|
|
193
|
+
interface ShortcutDefinition {
|
|
194
|
+
action: ShortcutAction;
|
|
195
|
+
keys: VideoSdkKey[];
|
|
196
|
+
labelKey: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Default keyboard shortcuts configuration
|
|
200
|
+
*/
|
|
201
|
+
export declare const SHORTCUTS_CONFIG: ShortcutDefinition[];
|
|
202
|
+
/**
|
|
203
|
+
* Seek amounts in seconds
|
|
204
|
+
*/
|
|
205
|
+
export declare const SEEK_SMALL_SECONDS = 5;
|
|
206
|
+
export declare const SEEK_LARGE_SECONDS = 10;
|
|
207
|
+
/**
|
|
208
|
+
* Volume change delta (0-1 range)
|
|
209
|
+
*/
|
|
210
|
+
export declare const VOLUME_DELTA = .1;
|
|
211
|
+
interface VideoKeyboardHandlers {
|
|
212
|
+
onPlayPause: () => void;
|
|
213
|
+
onSeek: (seconds: number) => void;
|
|
214
|
+
onFullscreen: () => void;
|
|
215
|
+
onMiniplayer: () => void;
|
|
216
|
+
onMute: () => void;
|
|
217
|
+
onVolume: (delta: number) => void;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get seek amount for a keyboard event
|
|
221
|
+
* @returns number of seconds to seek (positive = forward, negative = backward)
|
|
222
|
+
*/
|
|
223
|
+
export declare function getSeekAmount(keyCode: string, action: ShortcutAction): number;
|
|
224
|
+
/**
|
|
225
|
+
* Get volume delta for a keyboard event
|
|
226
|
+
* @returns volume change amount (positive = increase, negative = decrease)
|
|
227
|
+
*/
|
|
228
|
+
export declare function getVolumeDelta(keyCode: string): number;
|
|
229
|
+
/**
|
|
230
|
+
* Execute keyboard shortcut action
|
|
231
|
+
*/
|
|
232
|
+
export declare function executeShortcutAction(action: ShortcutAction, keyCode: string, handlers: VideoKeyboardHandlers): void;
|
|
233
|
+
/**
|
|
234
|
+
* Full play/pause key set including media keys — mirrors React's
|
|
235
|
+
* component-level PlayPauseShortcuts, which extends SHORTCUTS_CONFIG.
|
|
236
|
+
*/
|
|
237
|
+
export declare const PLAY_PAUSE_KEYS: VideoSdkKey[];
|
|
238
|
+
/** Resolve a KeyboardEvent.code to its shortcut action, or null when unmapped. */
|
|
239
|
+
export declare function resolveShortcutAction(keyCode: string): ShortcutAction | null;
|
|
240
|
+
/**
|
|
241
|
+
* Digit0–Digit9 → 0–90 percent seek targets — mirrors React's
|
|
242
|
+
* PercentageShortcuts table.
|
|
243
|
+
*/
|
|
244
|
+
export declare function getSeekPercent(keyCode: string): number | null;
|
|
245
|
+
/**
|
|
246
|
+
* Volume and mute utility functions
|
|
247
|
+
* Pure functions for volume calculations and mute state logic
|
|
248
|
+
*/
|
|
249
|
+
/**
|
|
250
|
+
* Calculate the volume to set, ensuring it's never 0 when unmuting
|
|
251
|
+
* @param currentVolume - The current volume from settings (0-1)
|
|
252
|
+
* @param defaultVolume - The default volume to use if current is 0 (default: 1.0)
|
|
253
|
+
* @returns The volume to set (0-1)
|
|
254
|
+
*/
|
|
255
|
+
export declare function calculateVolumeToSet(currentVolume: number, defaultVolume?: number): number;
|
|
256
|
+
/**
|
|
257
|
+
* Determine if video should be muted based on player state and interaction state
|
|
258
|
+
* @param playerMuted - Whether the current player is muted
|
|
259
|
+
* @param isFeed - Whether this is a feed video
|
|
260
|
+
* @param feedInteracted - Whether user has interacted with the feed
|
|
261
|
+
* @param playerInteracted - Whether user has interacted with the player
|
|
262
|
+
* @returns Whether the video should be muted
|
|
263
|
+
*/
|
|
264
|
+
interface MuteCheckParams {
|
|
265
|
+
playerMuted: boolean;
|
|
266
|
+
playerInteracted: boolean;
|
|
267
|
+
}
|
|
268
|
+
export declare function shouldBeMuted({ playerMuted, playerInteracted }: MuteCheckParams): boolean;
|
|
269
|
+
/**
|
|
270
|
+
* Get the volume to restore when unmuting, with a minimum threshold
|
|
271
|
+
* @param currentVolume - The current volume (0-1)
|
|
272
|
+
* @param previousVolume - The previously saved volume before muting (0-1)
|
|
273
|
+
* @param minVolume - The minimum volume to restore to (default: 0.1)
|
|
274
|
+
* @returns The volume to restore (0-1)
|
|
275
|
+
*/
|
|
276
|
+
export declare function getRestoredVolume(currentVolume: number, previousVolume: number, minVolume?: number): number;
|
|
277
|
+
/**
|
|
278
|
+
* Clamp volume to valid range (0-1)
|
|
279
|
+
* @param volume - The volume to clamp
|
|
280
|
+
* @returns Volume clamped to 0-1 range
|
|
281
|
+
*/
|
|
282
|
+
export declare function clampVolume(volume: number): number;
|
|
283
|
+
/**
|
|
284
|
+
* Check if volume is effectively muted (0 or very close to 0)
|
|
285
|
+
* @param volume - The volume to check
|
|
286
|
+
* @param threshold - The threshold below which volume is considered muted (default: 0.01)
|
|
287
|
+
* @returns Whether volume is effectively muted
|
|
288
|
+
*/
|
|
289
|
+
export declare function isEffectivelyMuted(volume: number, threshold?: number): boolean;
|
|
290
|
+
interface MousePointerLike {
|
|
291
|
+
clientX: number;
|
|
292
|
+
}
|
|
293
|
+
interface TouchPointerLike {
|
|
294
|
+
touches: ArrayLike<MousePointerLike>;
|
|
295
|
+
}
|
|
296
|
+
/** clientX of a mouse or touch event (first touch) — shared by the volume sliders. */
|
|
297
|
+
export declare function getPointerClientX(event: MousePointerLike | TouchPointerLike): number;
|
|
298
|
+
/** Map a pointer position over a slider rect to a clamped 0-1 volume. */
|
|
299
|
+
export declare function getVolumeFromPointer(rect: {
|
|
300
|
+
left: number;
|
|
301
|
+
width: number;
|
|
302
|
+
}, clientX: number): number;
|
|
303
|
+
type Viewport = {
|
|
304
|
+
width: number;
|
|
305
|
+
height: number;
|
|
306
|
+
};
|
|
307
|
+
interface Position {
|
|
308
|
+
x: number;
|
|
309
|
+
y: number;
|
|
310
|
+
}
|
|
311
|
+
export declare const DEFAULT_FONT_SIZE = 16;
|
|
312
|
+
export declare const MIN_FONT_SIZE = 12;
|
|
313
|
+
export declare const MAX_FONT_SIZE = 120;
|
|
314
|
+
export declare const FONT_SIZE_RATIO = .03;
|
|
315
|
+
export declare const FONT_SIZE_VIEWPORT_RATIO = .07;
|
|
316
|
+
export declare const DEFAULT_CAPTION_FONT_SIZE = 100;
|
|
317
|
+
export declare const SAFE_ZONE_MARGIN = 8;
|
|
318
|
+
export declare const CAPTION_WIDTH_RATIO = .6;
|
|
319
|
+
/**
|
|
320
|
+
* Smart caption positioning constants
|
|
321
|
+
*/
|
|
322
|
+
export declare const BOTTOM_SNAP_THRESHOLD = 85;
|
|
323
|
+
export declare const BOTTOM_OFFSET_CONTROLS_VISIBLE = 80;
|
|
324
|
+
export declare const BOTTOM_OFFSET_CONTROLS_HIDDEN = 24;
|
|
325
|
+
/**
|
|
326
|
+
* Calculates the font size based on the viewport width and user preference.
|
|
327
|
+
* Uses a logarithmic scale
|
|
328
|
+
* 100% -> 1.0x
|
|
329
|
+
* 200% -> 1.5x
|
|
330
|
+
* 400% -> 2.0x
|
|
331
|
+
*/
|
|
332
|
+
export declare const calculateFontSize: (viewport: Viewport, captionFontSize: number) => number;
|
|
333
|
+
/**
|
|
334
|
+
* Constrains a percentage-based position to be within safe bounds (0-100).
|
|
335
|
+
* Assumes the input position is already in percentages.
|
|
336
|
+
*/
|
|
337
|
+
export declare const constrainPercentagePosition: (position: Position, elementWidthRatio?: number) => Position;
|
|
338
|
+
/**
|
|
339
|
+
* Converts pixel coordinates to percentage coordinates relative to the viewport.
|
|
340
|
+
*/
|
|
341
|
+
export declare const pixelsToPercentage: (x: number, y: number, viewport: Viewport) => Position;
|
|
342
|
+
/**
|
|
343
|
+
* Calculate pointer offsets for caption dragging
|
|
344
|
+
*/
|
|
345
|
+
interface PointerOffsets {
|
|
346
|
+
clickXRelative: number;
|
|
347
|
+
clickYRelative: number;
|
|
348
|
+
currentVisualX: number;
|
|
349
|
+
currentVisualY: number;
|
|
350
|
+
}
|
|
351
|
+
interface GetPointerOffsetsOptions {
|
|
352
|
+
containerRect: DOMRect;
|
|
353
|
+
elementRect: DOMRect;
|
|
354
|
+
clientX: number;
|
|
355
|
+
clientY: number;
|
|
356
|
+
}
|
|
357
|
+
export declare function getPointerOffsets(options: GetPointerOffsetsOptions): PointerOffsets;
|
|
358
|
+
/**
|
|
359
|
+
* Calculate new drag position from delta
|
|
360
|
+
*/
|
|
361
|
+
interface CalculateDragPositionOptions {
|
|
362
|
+
startPositionPixels: Position;
|
|
363
|
+
startX: number;
|
|
364
|
+
startY: number;
|
|
365
|
+
currentXRelative: number;
|
|
366
|
+
currentYRelative: number;
|
|
367
|
+
viewport: Viewport;
|
|
368
|
+
}
|
|
369
|
+
export declare function calculateDragPosition(options: CalculateDragPositionOptions): Position;
|
|
370
|
+
/**
|
|
371
|
+
* Calculate safe zone viewport dimensions
|
|
372
|
+
*/
|
|
373
|
+
export declare function calculateSafeViewport(viewport: Viewport): Viewport;
|
|
374
|
+
/**
|
|
375
|
+
* Determine if caption should be pinned to bottom
|
|
376
|
+
*/
|
|
377
|
+
export declare function shouldPinToBottom(positionY: number, isDragging: boolean): boolean;
|
|
378
|
+
/**
|
|
379
|
+
* CSS text-shadow value for a caption character edge style.
|
|
380
|
+
* Shadow size scales with font size so edges stay proportional.
|
|
381
|
+
*/
|
|
382
|
+
export declare function getTextShadowValue(edgeStyle: string, fontSize: number): string;
|
|
383
|
+
type VideoTheme = "default" | "minimal" | "feed" | "hover" | "hideUI";
|
|
384
|
+
type CaptionLangType = "off" | "eng" | "auto";
|
|
385
|
+
interface SettingsState {
|
|
386
|
+
volume: number;
|
|
387
|
+
playbackSpeed: number;
|
|
388
|
+
customPlayback: number;
|
|
389
|
+
allowCaptions: boolean;
|
|
390
|
+
captionPosition: {
|
|
391
|
+
x: number;
|
|
392
|
+
y: number;
|
|
393
|
+
};
|
|
394
|
+
captionLang: CaptionLangType;
|
|
395
|
+
captionFontFamily: string;
|
|
396
|
+
captionFontSize: number;
|
|
397
|
+
captionFontColor: string;
|
|
398
|
+
captionFontOpacity: number;
|
|
399
|
+
captionBGColor: string;
|
|
400
|
+
captionBGOpacity: number;
|
|
401
|
+
captionWindowColor: string;
|
|
402
|
+
captionWindowOpacity: number;
|
|
403
|
+
captionCharacterEdgeStyle: string;
|
|
404
|
+
selectedLanguageCode?: string;
|
|
405
|
+
globalMutedPreference: boolean;
|
|
406
|
+
preferredQuality?: number;
|
|
407
|
+
preferredAudioLanguage?: string;
|
|
408
|
+
preferredSubtitleLanguage?: string;
|
|
409
|
+
}
|
|
410
|
+
interface SmartCaptionPositionParams {
|
|
411
|
+
position: Position;
|
|
412
|
+
isDragging: boolean;
|
|
413
|
+
controlsVisible: boolean;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Caption window placement: percent-based within the safe zone, snapping to
|
|
417
|
+
* the bottom (above the controls bar when visible) once dragged past the
|
|
418
|
+
* snap threshold. Single source for the React hook and the Lit overlay.
|
|
419
|
+
*/
|
|
420
|
+
export declare function getSmartCaptionPosition({ position, isDragging, controlsVisible }: SmartCaptionPositionParams): CSSProperties;
|
|
421
|
+
type CaptionWindowSettings = Pick<SettingsState, "captionWindowColor" | "captionWindowOpacity">;
|
|
422
|
+
/** Caption window box: width from the safe viewport, settings-driven background, drag cursor. */
|
|
423
|
+
export declare function getCaptionWindowStyle(settings: CaptionWindowSettings, safeViewportWidth: number, isDragging: boolean): CSSProperties;
|
|
424
|
+
type CaptionTextSettings = Pick<SettingsState, "captionFontFamily" | "captionFontColor" | "captionFontOpacity" | "captionBGColor" | "captionBGOpacity" | "captionCharacterEdgeStyle">;
|
|
425
|
+
/** Caption text styling from the user's caption settings (font, colors, edge-style shadow). */
|
|
426
|
+
export declare function getCaptionTextStyle(settings: CaptionTextSettings, fontSize: number): CSSProperties;
|
|
427
|
+
export declare function isDocumentFullscreen(): boolean;
|
|
428
|
+
/**
|
|
429
|
+
* Utility functions for Picture-in-Picture logic
|
|
430
|
+
*/
|
|
431
|
+
/**
|
|
432
|
+
* Check if a video element is currently in Picture-in-Picture mode.
|
|
433
|
+
*
|
|
434
|
+
* Browsers disagree for a video inside shadow DOM: by the spec,
|
|
435
|
+
* `document.pictureInPictureElement` is retargeted to the shadow host and the
|
|
436
|
+
* owning ShadowRoot reports the video, but Chromium reports the video on the
|
|
437
|
+
* document and nothing on the ShadowRoot. Either match counts.
|
|
438
|
+
*/
|
|
439
|
+
export declare function isVideoPictureInPicture(video: HTMLVideoElement | null): boolean;
|
|
440
|
+
/**
|
|
441
|
+
* Helper to toggle PIP mode safely
|
|
442
|
+
*/
|
|
443
|
+
export declare function togglePictureInPicture(video: HTMLVideoElement | null): Promise<void>;
|
|
444
|
+
/**
|
|
445
|
+
* Minimum position in seconds to resume from (skip if too close to start)
|
|
446
|
+
*/
|
|
447
|
+
export declare const RESUME_MIN_POSITION_SECONDS = 5;
|
|
448
|
+
/**
|
|
449
|
+
* Minimum remaining duration in seconds to resume (skip if too close to end)
|
|
450
|
+
*/
|
|
451
|
+
export declare const RESUME_MIN_REMAINING_SECONDS = 5;
|
|
452
|
+
/**
|
|
453
|
+
* Interval in milliseconds between progress persistence operations
|
|
454
|
+
*/
|
|
455
|
+
export declare const PERSIST_INTERVAL_MS = 2e3;
|
|
456
|
+
export declare const getPersistedProgress: (videoId: string) => number;
|
|
457
|
+
export declare const persistProgress: (videoId: string, positionMs: number) => void;
|
|
458
|
+
/**
|
|
459
|
+
* Determine if a saved progress position should be used to resume playback
|
|
460
|
+
* @param savedPositionSeconds - The saved position in seconds
|
|
461
|
+
* @param durationSeconds - The total video duration in seconds
|
|
462
|
+
* @returns true if position is valid for resuming (not too close to start or end)
|
|
463
|
+
*/
|
|
464
|
+
export declare function shouldResumeFromPosition(savedPositionSeconds: number, durationSeconds: number): boolean;
|
|
465
|
+
/**
|
|
466
|
+
* Determine if enough time has elapsed to persist progress again
|
|
467
|
+
* @param lastPersistTime - Timestamp of last persist operation (from performance.now())
|
|
468
|
+
* @param currentTime - Current timestamp (from performance.now())
|
|
469
|
+
* @returns true if interval has elapsed
|
|
470
|
+
*/
|
|
471
|
+
export declare function shouldPersistProgress(lastPersistTime: number, currentTime: number): boolean;
|
|
472
|
+
/**
|
|
473
|
+
* PRELOAD_RANGE determines how many videos around the current one should be loaded
|
|
474
|
+
*
|
|
475
|
+
* Example with BEFORE: 1, AFTER: 2, current index is 5:
|
|
476
|
+
*
|
|
477
|
+
* Legend:
|
|
478
|
+
* PRELOADING (buffering in background)
|
|
479
|
+
* PLAYING (currently visible)
|
|
480
|
+
* IDLE (exists but not loaded)
|
|
481
|
+
* UNLOADED (too far, save memory)
|
|
482
|
+
*/
|
|
483
|
+
export declare const PRELOAD_RANGE: {
|
|
484
|
+
BEFORE: number;
|
|
485
|
+
AFTER: number;
|
|
486
|
+
};
|
|
487
|
+
/**
|
|
488
|
+
* UNLOAD_DISTANCE determines when to unload videos to free memory
|
|
489
|
+
* If a video is more than this distance from current, unload it
|
|
490
|
+
*/
|
|
491
|
+
export declare const UNLOAD_DISTANCE = 5;
|
|
492
|
+
/**
|
|
493
|
+
* Video loading states
|
|
494
|
+
*/
|
|
495
|
+
export declare const VideoLoadState: {
|
|
496
|
+
readonly UNLOADED: "unloaded";
|
|
497
|
+
readonly IDLE: "idle";
|
|
498
|
+
readonly PRELOADING: "preloading";
|
|
499
|
+
readonly PLAYING: "playing";
|
|
500
|
+
};
|
|
501
|
+
export type VideoLoadState = (typeof VideoLoadState)[keyof typeof VideoLoadState];
|
|
502
|
+
/**
|
|
503
|
+
* Calculate what state a video should be in based on its distance from current
|
|
504
|
+
*/
|
|
505
|
+
export declare function getVideoLoadState(videoIndex: number, currentIndex: number, isCurrent: boolean): VideoLoadState;
|
|
506
|
+
/**
|
|
507
|
+
* Delay in milliseconds before showing the loading spinner
|
|
508
|
+
* This prevents flash-of-spinner for brief buffering moments
|
|
509
|
+
*/
|
|
510
|
+
export declare const LOADING_SPINNER_DELAY_MS = 300;
|
|
511
|
+
interface LoadingDebounceState {
|
|
512
|
+
timerId: ReturnType<typeof setTimeout> | null;
|
|
513
|
+
}
|
|
514
|
+
interface UpdateLoadingStateOptions {
|
|
515
|
+
state: LoadingDebounceState;
|
|
516
|
+
shouldShow: boolean;
|
|
517
|
+
onShow: () => void;
|
|
518
|
+
onHide: () => void;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Create initial loading debounce state
|
|
522
|
+
*/
|
|
523
|
+
export declare function createLoadingDebounceState(): LoadingDebounceState;
|
|
524
|
+
/**
|
|
525
|
+
* Update loading state with debounce
|
|
526
|
+
*/
|
|
527
|
+
export declare function updateLoadingState(options: UpdateLoadingStateOptions): void;
|
|
528
|
+
/**
|
|
529
|
+
* Clean up loading debounce state (clear any pending timers)
|
|
530
|
+
*/
|
|
531
|
+
export declare function cleanupLoadingDebounce(state: LoadingDebounceState): void;
|
|
532
|
+
/**
|
|
533
|
+
* Calculate aspect ratio from video element dimensions
|
|
534
|
+
* @returns aspect ratio (width/height) or undefined if dimensions not available
|
|
535
|
+
*/
|
|
536
|
+
export declare function calculateAspectRatio(videoElement: HTMLVideoElement | null, videoData: VideoData | undefined): number | undefined;
|
|
537
|
+
interface PlaybackHandlerCallbacks {
|
|
538
|
+
onSegmentStart: () => void;
|
|
539
|
+
onSegmentEnd: () => void;
|
|
540
|
+
onRestartSegment: (isPaused?: boolean) => void;
|
|
541
|
+
onShowSpinner: (show: boolean) => void;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Handle video play event
|
|
545
|
+
*/
|
|
546
|
+
export declare function handleVideoPlay(playerId: UUID, callbacks: Pick<PlaybackHandlerCallbacks, "onSegmentStart">): void;
|
|
547
|
+
/**
|
|
548
|
+
* Handle video pause event
|
|
549
|
+
*/
|
|
550
|
+
export declare function handleVideoPause(playerId: UUID, callbacks: Pick<PlaybackHandlerCallbacks, "onSegmentEnd">): void;
|
|
551
|
+
/**
|
|
552
|
+
* Handle video rate change event
|
|
553
|
+
*/
|
|
554
|
+
export declare function handleVideoRateChange(playerId: UUID, videoElement: HTMLVideoElement | null, callbacks: Pick<PlaybackHandlerCallbacks, "onRestartSegment">): void;
|
|
555
|
+
/**
|
|
556
|
+
* Handle video error event
|
|
557
|
+
*/
|
|
558
|
+
export declare function handleVideoError(playerId: UUID, callbacks: Pick<PlaybackHandlerCallbacks, "onShowSpinner">): void;
|
|
559
|
+
/**
|
|
560
|
+
* Update player loading state in store
|
|
561
|
+
*/
|
|
562
|
+
export declare function updatePlayerLoadingState(playerId: UUID, isLoading: boolean): void;
|
|
563
|
+
/**
|
|
564
|
+
* Delay in milliseconds for single-click detection
|
|
565
|
+
* Used to distinguish between single click (toggle play) and double click (fullscreen)
|
|
566
|
+
*/
|
|
567
|
+
export declare const SINGLE_CLICK_DELAY_MS = 250;
|
|
568
|
+
/**
|
|
569
|
+
* Maximum time between clicks to be considered a double-click
|
|
570
|
+
*/
|
|
571
|
+
export declare const DOUBLE_CLICK_THRESHOLD_MS = 400;
|
|
572
|
+
interface ClickDisambiguationState {
|
|
573
|
+
singleClickTimerId: ReturnType<typeof setTimeout> | null;
|
|
574
|
+
lastClickTime: number;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Create initial click disambiguation state
|
|
578
|
+
*/
|
|
579
|
+
export declare function createClickDisambiguationState(): ClickDisambiguationState;
|
|
580
|
+
interface HandleClickOptions {
|
|
581
|
+
state: ClickDisambiguationState;
|
|
582
|
+
hideUI: boolean;
|
|
583
|
+
allowFullscreen: boolean;
|
|
584
|
+
isExcludedTarget: boolean;
|
|
585
|
+
onTogglePlay: () => void;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Handle a click event with disambiguation logic
|
|
589
|
+
* Returns true if the click was handled, false if ignored
|
|
590
|
+
*/
|
|
591
|
+
export declare function handleClick(options: HandleClickOptions): boolean;
|
|
592
|
+
interface HandleDoubleClickOptions {
|
|
593
|
+
state: ClickDisambiguationState;
|
|
594
|
+
allowFullscreen: boolean;
|
|
595
|
+
isExcludedTarget: boolean;
|
|
596
|
+
onToggleFullscreen: () => void;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Handle a double-click event
|
|
600
|
+
* Returns true if the double-click was handled, false if ignored
|
|
601
|
+
*/
|
|
602
|
+
export declare function handleDoubleClick(options: HandleDoubleClickOptions): boolean;
|
|
603
|
+
/**
|
|
604
|
+
* Clean up click disambiguation state (clear any pending timers)
|
|
605
|
+
*/
|
|
606
|
+
export declare function cleanupClickDisambiguation(state: ClickDisambiguationState): void;
|
|
607
|
+
/**
|
|
608
|
+
* Pure helpers for video-element environment effects.
|
|
609
|
+
*
|
|
610
|
+
* "Environment effects" = things the player has to keep in sync with the
|
|
611
|
+
* surrounding document/window: fullscreen state, picture-in-picture state,
|
|
612
|
+
* tab visibility, viewport size, and the volume/mute state derived from
|
|
613
|
+
* player + user-interaction inputs.
|
|
614
|
+
*
|
|
615
|
+
* Everything in this module is framework-agnostic. React/Lit/Vue wrappers
|
|
616
|
+
* supply the DOM listeners and the store-update callbacks; this module
|
|
617
|
+
* supplies the predicates, the state derivations, and the listener factories.
|
|
618
|
+
*/
|
|
619
|
+
/**
|
|
620
|
+
* Bind the `document` `fullscreenchange` event to a callback that receives the
|
|
621
|
+
* current fullscreen state.
|
|
622
|
+
*
|
|
623
|
+
* Returns a teardown function that removes the listener.
|
|
624
|
+
*/
|
|
625
|
+
export declare function bindFullscreenChangeListener(onChange: (isFullscreen: boolean) => void): () => void;
|
|
626
|
+
/**
|
|
627
|
+
* Bind `enterpictureinpicture` and `leavepictureinpicture` events on a
|
|
628
|
+
* video element to a callback that receives the current PiP state.
|
|
629
|
+
*
|
|
630
|
+
* Returns a teardown function.
|
|
631
|
+
*/
|
|
632
|
+
export declare function bindPictureInPictureChangeListener(videoElement: HTMLVideoElement | null, onChange: (isPip: boolean) => void): () => void;
|
|
633
|
+
interface PipReentryOptions {
|
|
634
|
+
videoElement: HTMLVideoElement | null;
|
|
635
|
+
isCurrentlyInPip: boolean;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Decide whether to re-request PiP when the document becomes hidden.
|
|
639
|
+
*
|
|
640
|
+
* Returns `true` when the caller should re-enter PiP on the supplied
|
|
641
|
+
* video element. The actual `requestPictureInPicture()` call is left to the
|
|
642
|
+
* caller (since it returns a promise and the caller chooses how to handle
|
|
643
|
+
* errors).
|
|
644
|
+
*/
|
|
645
|
+
export declare function shouldReenterPictureInPicture({ videoElement, isCurrentlyInPip }: PipReentryOptions): boolean;
|
|
646
|
+
/**
|
|
647
|
+
* Bind a `visibilitychange` handler that re-enters PiP whenever the document
|
|
648
|
+
* becomes hidden and the player believes itself to be in PiP but the browser
|
|
649
|
+
* has dropped out.
|
|
650
|
+
*
|
|
651
|
+
* Returns a teardown function.
|
|
652
|
+
*/
|
|
653
|
+
export declare function bindPipVisibilityReentry(getOptions: () => PipReentryOptions, onError?: (error: unknown) => void): () => void;
|
|
654
|
+
interface AppliedVolumeSync {
|
|
655
|
+
/** The `muted` value that should be assigned to the video element. */
|
|
656
|
+
muted: boolean;
|
|
657
|
+
/** The `volume` value that should be assigned, or `null` if not changing. */
|
|
658
|
+
volume: number | null;
|
|
659
|
+
/** When non-null, the store's current `volume` should be updated to this value. */
|
|
660
|
+
storeVolumeUpdate: number | null;
|
|
661
|
+
}
|
|
662
|
+
interface DeriveVolumeSyncOptions {
|
|
663
|
+
/** Hard mute applied by the consumer (e.g. theme override). */
|
|
664
|
+
forcedMuted: boolean;
|
|
665
|
+
/** The player's persisted `muted` state. */
|
|
666
|
+
playerMuted: boolean;
|
|
667
|
+
/** Whether the user has interacted with the player yet. */
|
|
668
|
+
playerInteracted: boolean;
|
|
669
|
+
/** The player's persisted `volume`. */
|
|
670
|
+
playerVolume: number;
|
|
671
|
+
/** The player's persisted `previousVolume` (used to restore on unmute). */
|
|
672
|
+
playerPreviousVolume: number;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Pure derivation of the values that should be written to the video element
|
|
676
|
+
* (and back to the store) given the current player state.
|
|
677
|
+
*
|
|
678
|
+
* Mirrors the behaviour previously implemented inside `useVolumeEffect`:
|
|
679
|
+
*
|
|
680
|
+
* 1. When the caller forces mute, nothing else is touched.
|
|
681
|
+
* 2. Otherwise, `shouldBeMuted` decides the effective mute state.
|
|
682
|
+
* 3. When unmuted, `getRestoredVolume` resolves the volume to assign, and the
|
|
683
|
+
* store is updated only when the current persisted volume is `0`.
|
|
684
|
+
*/
|
|
685
|
+
export declare function deriveVolumeSync({ forcedMuted, playerMuted, playerInteracted, playerVolume, playerPreviousVolume }: DeriveVolumeSyncOptions): AppliedVolumeSync | null;
|
|
686
|
+
/**
|
|
687
|
+
* Observe a container element's viewport size, invoking the callback with the
|
|
688
|
+
* current `{width, height}` whenever it changes (via `ResizeObserver`) or
|
|
689
|
+
* the window resizes.
|
|
690
|
+
*
|
|
691
|
+
* Returns a teardown function.
|
|
692
|
+
*/
|
|
693
|
+
export declare function observeViewport(container: HTMLElement | null, onChange: (viewport: Viewport) => void): () => void;
|
|
694
|
+
/**
|
|
695
|
+
* Pure helpers and DOM-listener factories for player lifecycle effects:
|
|
696
|
+
* playback rate sync, ended handling, preload state machine, reset-on-data
|
|
697
|
+
* derivations, and feed playback gating.
|
|
698
|
+
*
|
|
699
|
+
* Framework-agnostic. React/Lit/Vue wrappers supply the store-update callbacks;
|
|
700
|
+
* this module supplies the predicates, derivations, and listener factories.
|
|
701
|
+
*/
|
|
702
|
+
/** Valid values for `<video preload>`. */
|
|
703
|
+
type VideoPreloadAttribute = "auto" | "metadata" | "none";
|
|
704
|
+
/**
|
|
705
|
+
* Map a feed virtualization `VideoLoadState` to the appropriate
|
|
706
|
+
* `<video preload>` attribute value.
|
|
707
|
+
*
|
|
708
|
+
* - UNLOADED → "none" (slot is far from viewport; don't fetch anything)
|
|
709
|
+
* - IDLE → "metadata" (slot is reachable but not active; fetch headers only)
|
|
710
|
+
* - PRELOADING / PLAYING → "auto" (slot is active or about to be; fetch segments)
|
|
711
|
+
*/
|
|
712
|
+
export declare function getPreloadAttribute(loadState: VideoLoadState): VideoPreloadAttribute;
|
|
713
|
+
/**
|
|
714
|
+
* Bind a `loadeddata` listener that re-applies the playback rate after the
|
|
715
|
+
* browser resets it (most browsers reset `playbackRate` to `1.0` on each
|
|
716
|
+
* `<source>` change).
|
|
717
|
+
*
|
|
718
|
+
* Also writes the rate immediately so that callers don't have to do so
|
|
719
|
+
* separately. Returns a teardown function.
|
|
720
|
+
*/
|
|
721
|
+
export declare function bindPlaybackRateSync(videoElement: HTMLVideoElement | null, playbackRate: number): () => void;
|
|
722
|
+
/**
|
|
723
|
+
* Bind an `ended` listener that invokes `onEnded()` exactly once per native
|
|
724
|
+
* `ended` event. Returns a teardown function.
|
|
725
|
+
*/
|
|
726
|
+
export declare function bindEndedListener(videoElement: HTMLVideoElement | null, onEnded: () => void): () => void;
|
|
727
|
+
interface PosterDimensions {
|
|
728
|
+
width?: number;
|
|
729
|
+
height?: number;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Compute the aspect ratio (width / height) from poster dimensions, returning
|
|
733
|
+
* `null` when either dimension is missing or zero.
|
|
734
|
+
*
|
|
735
|
+
* Mirrors the conditional in `useResetOnDataChangeEffect`:
|
|
736
|
+
*
|
|
737
|
+
* if (videoData?.poster?.width && videoData?.poster?.height) {
|
|
738
|
+
* setAspectRatio(videoData.poster.width / videoData.poster.height);
|
|
739
|
+
* }
|
|
740
|
+
*/
|
|
741
|
+
export declare function getAspectRatioFromPoster(poster: PosterDimensions | undefined): number | null;
|
|
742
|
+
interface FeedPlaybackDecisionInput {
|
|
743
|
+
isFeed: boolean;
|
|
744
|
+
isCurrent: boolean;
|
|
745
|
+
loadState: VideoLoadState;
|
|
746
|
+
showSpinner: boolean;
|
|
747
|
+
videoReadyState: number;
|
|
748
|
+
}
|
|
749
|
+
type FeedPlaybackAction = "play" | "pause" | "none";
|
|
750
|
+
/**
|
|
751
|
+
* Decide what a feed-virtualized player should do given the current slot state.
|
|
752
|
+
*
|
|
753
|
+
* - In a non-feed context, returns "none" (caller handles its own playback).
|
|
754
|
+
* - If the slot is not current, returns "pause" so the off-screen player stops.
|
|
755
|
+
* - If the slot is current, in PLAYING load state, not spinning, and the video
|
|
756
|
+
* has enough data (readyState >= 2), returns "play".
|
|
757
|
+
* - Otherwise returns "none" (still loading / preloading; wait).
|
|
758
|
+
*/
|
|
759
|
+
export declare function getFeedPlaybackAction({ isFeed, isCurrent, loadState, showSpinner, videoReadyState }: FeedPlaybackDecisionInput): FeedPlaybackAction;
|
|
760
|
+
/**
|
|
761
|
+
* Generic "click outside" detection with an optional exclusion attribute.
|
|
762
|
+
*
|
|
763
|
+
* Use case: a settings menu that should close when the user clicks outside it,
|
|
764
|
+
* but NOT when the user clicks the button that opens it (otherwise the button
|
|
765
|
+
* both opens and immediately closes the menu in the same pointer cycle).
|
|
766
|
+
*
|
|
767
|
+
* Pure, DOM-only, framework-agnostic. React/Lit/Vue wrappers call
|
|
768
|
+
* `bindClickOutside` from their lifecycle hooks.
|
|
769
|
+
*/
|
|
770
|
+
interface ClickOutsideOptions {
|
|
771
|
+
/**
|
|
772
|
+
* Function that returns the "inside" element. Called on every pointer event,
|
|
773
|
+
* so the caller can pass a ref-like getter that may be null until the
|
|
774
|
+
* referenced element is mounted.
|
|
775
|
+
*/
|
|
776
|
+
getContainer: () => HTMLElement | null;
|
|
777
|
+
/**
|
|
778
|
+
* Optional attribute name (e.g. `"data-settings-button"`). Any pointer event
|
|
779
|
+
* whose target is inside an element carrying this attribute is treated as
|
|
780
|
+
* "inside" and does NOT trigger `onOutsideClick`.
|
|
781
|
+
*
|
|
782
|
+
* This is what lets the button that toggles the menu coexist with a
|
|
783
|
+
* close-on-outside-click listener: the button's own clicks are ignored here,
|
|
784
|
+
* letting the button's own onClick decide whether to toggle.
|
|
785
|
+
*/
|
|
786
|
+
excludeAttribute?: string;
|
|
787
|
+
/**
|
|
788
|
+
* Invoked when a pointer event lands outside both the container and (if
|
|
789
|
+
* configured) any element carrying `excludeAttribute`.
|
|
790
|
+
*/
|
|
791
|
+
onOutsideClick: (event: MouseEvent) => void;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Returns `true` when the given event target is inside an element carrying
|
|
795
|
+
* the supplied attribute. When `attribute` is undefined, always returns false.
|
|
796
|
+
*/
|
|
797
|
+
export declare function isInsideExcludedElement(target: EventTarget | null, attribute: string | undefined): boolean;
|
|
798
|
+
/**
|
|
799
|
+
* Returns `true` when the event lands outside both the container and any
|
|
800
|
+
* excluded element.
|
|
801
|
+
*
|
|
802
|
+
* Checks the composed path as well as the plain target: document-level
|
|
803
|
+
* listeners see shadow-DOM events retargeted to the host, and
|
|
804
|
+
* `document.querySelector` cannot see into shadow roots — the composed path
|
|
805
|
+
* is the only reliable record of where the click really landed.
|
|
806
|
+
*/
|
|
807
|
+
export declare function isClickOutside(event: MouseEvent, container: HTMLElement | null, excludeAttribute: string | undefined): boolean;
|
|
808
|
+
/**
|
|
809
|
+
* Bind a `mousedown` listener on the document that invokes `onOutsideClick`
|
|
810
|
+
* whenever the event lands outside the container and outside any element
|
|
811
|
+
* carrying `excludeAttribute`.
|
|
812
|
+
*
|
|
813
|
+
* Returns a teardown function that removes the listener.
|
|
814
|
+
*
|
|
815
|
+
* The container is looked up via `getContainer()` on every event so the caller
|
|
816
|
+
* can wire it to a ref that may be null at bind time.
|
|
817
|
+
*/
|
|
818
|
+
export declare function bindClickOutside({ getContainer, excludeAttribute, onOutsideClick }: ClickOutsideOptions): () => void;
|
|
819
|
+
/**
|
|
820
|
+
* Bind a `visibilitychange` listener that fires `onHidden` when the document
|
|
821
|
+
* is hidden. Returns a teardown function.
|
|
822
|
+
*
|
|
823
|
+
* Settings menus use this to close themselves when the user tabs away.
|
|
824
|
+
* Lives next to `bindClickOutside` because the two are almost always wired
|
|
825
|
+
* together for "dismissible overlay" behaviour.
|
|
826
|
+
*/
|
|
827
|
+
export declare function bindDocumentHidden(onHidden: () => void): () => void;
|
|
828
|
+
/**
|
|
829
|
+
* Live "is the player buffering right now?" predicate.
|
|
830
|
+
*
|
|
831
|
+
* Different from `video-preload-config.ts` despite the similar name:
|
|
832
|
+
* - `video-preload-config.ts` decides the `<video preload>` attribute for
|
|
833
|
+
* a slot in the feed virtualization (one-time decision per slot).
|
|
834
|
+
* - `shouldShowSpinner` is called continuously during playback to decide
|
|
835
|
+
* whether the buffering spinner should currently be visible.
|
|
836
|
+
*
|
|
837
|
+
* Inputs come from a live `<HTMLVideoElement>`. The function is otherwise
|
|
838
|
+
* pure and framework-agnostic.
|
|
839
|
+
*/
|
|
840
|
+
/**
|
|
841
|
+
* Decide whether the buffering spinner should be visible for the given
|
|
842
|
+
* video element.
|
|
843
|
+
*
|
|
844
|
+
* Logic, in order:
|
|
845
|
+
* 1. No video → never show.
|
|
846
|
+
* 2. Playing past the start → if the current time falls inside any buffered
|
|
847
|
+
* range, the player has the bytes it needs; never show.
|
|
848
|
+
* 3. Otherwise fall back to `readyState`/`seeking`: show when the browser
|
|
849
|
+
* reports `HAVE_CURRENT_DATA` (2) or is actively seeking with
|
|
850
|
+
* `readyState < 3` (HAVE_FUTURE_DATA).
|
|
851
|
+
*/
|
|
852
|
+
export declare function shouldShowSpinner(video: HTMLVideoElement | null): boolean;
|
|
853
|
+
interface QualityLevel {
|
|
854
|
+
height: number;
|
|
855
|
+
width: number;
|
|
856
|
+
bitrate: number;
|
|
857
|
+
name: string;
|
|
858
|
+
index: number;
|
|
859
|
+
}
|
|
860
|
+
interface AudioTrack {
|
|
861
|
+
index: number;
|
|
862
|
+
label: string;
|
|
863
|
+
language: string;
|
|
864
|
+
kind: string;
|
|
865
|
+
/** The raw Hls.js MediaPlaylist object for this audio track. */
|
|
866
|
+
track: MediaPlaylist;
|
|
867
|
+
}
|
|
868
|
+
export declare const sortLevelsByQuality: (levels: QualityLevel[]) => QualityLevel[];
|
|
869
|
+
export declare const isHlsStream: (url: string) => boolean;
|
|
870
|
+
export declare const generateLevelName: (level: {
|
|
871
|
+
height: number;
|
|
872
|
+
width: number;
|
|
873
|
+
bitrate: number;
|
|
874
|
+
}) => string;
|
|
875
|
+
interface QualityLabelParams {
|
|
876
|
+
resolution: number;
|
|
877
|
+
levels: QualityLevel[];
|
|
878
|
+
currentLevel: number;
|
|
879
|
+
autoText: string;
|
|
880
|
+
}
|
|
881
|
+
export declare const getQualityLabel: ({ resolution, levels, currentLevel, autoText }: QualityLabelParams) => string;
|
|
882
|
+
export declare const MAX_NETWORK_RETRIES = 3;
|
|
883
|
+
export declare const MAX_MEDIA_RECOVERIES = 1;
|
|
884
|
+
interface HlsErrorPayload {
|
|
885
|
+
/** The hls.js error type, e.g. `"networkError"`, `"mediaError"`. */
|
|
886
|
+
error: string;
|
|
887
|
+
/** Specific hls.js error details (e.g. `"manifestLoadError"`) or undefined. */
|
|
888
|
+
code?: string;
|
|
889
|
+
}
|
|
890
|
+
interface HlsErrorRecoveryState {
|
|
891
|
+
networkRetries: number;
|
|
892
|
+
mediaRecoveries: number;
|
|
893
|
+
}
|
|
894
|
+
export declare function createHlsErrorRecoveryState(): HlsErrorRecoveryState;
|
|
895
|
+
type HlsErrorAction = {
|
|
896
|
+
kind: "retry-network";
|
|
897
|
+
attempt: number;
|
|
898
|
+
} | {
|
|
899
|
+
kind: "recover-media";
|
|
900
|
+
attempt: number;
|
|
901
|
+
} | {
|
|
902
|
+
kind: "give-up";
|
|
903
|
+
payload: HlsErrorPayload;
|
|
904
|
+
};
|
|
905
|
+
interface HlsErrorInput {
|
|
906
|
+
type: string;
|
|
907
|
+
details?: string;
|
|
908
|
+
fatal: boolean;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Decide what to do given an HLS error and the current recovery counters.
|
|
912
|
+
*
|
|
913
|
+
* Mutates `state` to advance the appropriate counter when retry/recover is
|
|
914
|
+
* selected. Non-fatal errors return `null` (the caller should ignore them).
|
|
915
|
+
*/
|
|
916
|
+
export declare function decideHlsErrorAction(error: HlsErrorInput, state: HlsErrorRecoveryState): HlsErrorAction | null;
|
|
917
|
+
/**
|
|
918
|
+
* Framework-agnostic HLS setup.
|
|
919
|
+
*
|
|
920
|
+
* Owns:
|
|
921
|
+
* - HLS.js configuration tuned for the SDK's playback profile.
|
|
922
|
+
* - Media-attached / manifest-parsed / level-switched / audio-track-switched
|
|
923
|
+
* event handlers.
|
|
924
|
+
* - Buffer-flush-on-far-seek heuristic (see `createSeekingHandler`).
|
|
925
|
+
* - Error recovery policy (see Error recovery section below).
|
|
926
|
+
* - Safari native HLS fallback.
|
|
927
|
+
*
|
|
928
|
+
* hls.js itself is loaded on first use (see `loadHls`), so `setupHls` is
|
|
929
|
+
* async and pages that never attach an HLS stream never download it.
|
|
930
|
+
*
|
|
931
|
+
* Resolves to a `HlsController` whose lifetime the caller manages via `destroy()`.
|
|
932
|
+
* Caller responsibilities:
|
|
933
|
+
* - Provide the `<video>` element and a `playerId`.
|
|
934
|
+
* - Subscribe to quality/level/audio changes via the controller's getters or
|
|
935
|
+
* by reading `playerStateStore`.
|
|
936
|
+
* - Surface errors to UI via the `onError` callback.
|
|
937
|
+
*/
|
|
938
|
+
type HlsInstance = Hls;
|
|
939
|
+
interface HlsController {
|
|
940
|
+
hls: Hls | null;
|
|
941
|
+
levels: QualityLevel[];
|
|
942
|
+
currentLevel: number;
|
|
943
|
+
isAuto: boolean;
|
|
944
|
+
destroy: () => void;
|
|
945
|
+
setQuality: (index: number) => void;
|
|
946
|
+
}
|
|
947
|
+
interface SetupHlsOptions {
|
|
948
|
+
videoElement: HTMLVideoElement;
|
|
949
|
+
videoUrl: string;
|
|
950
|
+
playerId: UUID;
|
|
951
|
+
isPreloading?: boolean;
|
|
952
|
+
/**
|
|
953
|
+
* Invoked when an HLS error is given up on (after retries are exhausted, or
|
|
954
|
+
* for non-recoverable error types). Callers typically dispatch a custom event
|
|
955
|
+
* and/or show an error overlay.
|
|
956
|
+
*/
|
|
957
|
+
onError?: (payload: HlsErrorPayload) => void;
|
|
958
|
+
/**
|
|
959
|
+
* Invoked whenever HLS reports updated quality levels (after manifest parse
|
|
960
|
+
* or level update). Useful for framework wrappers that mirror controller
|
|
961
|
+
* state into reactive store/state (e.g. React `useState`).
|
|
962
|
+
*/
|
|
963
|
+
onLevelsChange?: (levels: QualityLevel[]) => void;
|
|
964
|
+
/**
|
|
965
|
+
* Invoked whenever HLS switches to a different quality level.
|
|
966
|
+
*/
|
|
967
|
+
onLevelSwitch?: (level: number) => void;
|
|
968
|
+
}
|
|
969
|
+
export declare function createHlsConfig(isPreloading?: boolean): Partial<Hls["config"]>;
|
|
970
|
+
/**
|
|
971
|
+
* Set up HLS streaming on a video element, resolving to a controller for
|
|
972
|
+
* quality/audio switching and teardown.
|
|
973
|
+
*
|
|
974
|
+
* - For non-HLS URLs: sets `videoElement.src` directly and resolves to `null`
|
|
975
|
+
* without loading hls.js.
|
|
976
|
+
* - For HLS URLs on Safari (no `Hls.isSupported()` but native canPlayType):
|
|
977
|
+
* sets `videoElement.src` and returns `null` (UI features that depend on
|
|
978
|
+
* the HLS controller — quality switching, audio tracks — won't be available
|
|
979
|
+
* in native mode).
|
|
980
|
+
* - Otherwise: instantiates HLS.js, attaches the media, wires events
|
|
981
|
+
* (including error recovery), and returns the controller.
|
|
982
|
+
*/
|
|
983
|
+
export declare function setupHls(opts: SetupHlsOptions): Promise<HlsController | null>;
|
|
984
|
+
export declare const getPersistedSubtitleTrack: () => number;
|
|
985
|
+
export declare const persistSubtitleTrack: (trackIndex: number) => void;
|
|
986
|
+
export declare const getPersistedAudioTrack: () => number;
|
|
987
|
+
export declare const persistAudioTrack: (trackIndex: number) => void;
|
|
988
|
+
type ContentType = "video" | "image";
|
|
989
|
+
type ViewType = "start" | "watch" | "end";
|
|
990
|
+
type Page = "feed" | "standalone" | "profile";
|
|
991
|
+
interface Segment {
|
|
992
|
+
start_ms: number;
|
|
993
|
+
end_ms: number;
|
|
994
|
+
speed: number;
|
|
995
|
+
}
|
|
996
|
+
interface ContentViewEvent {
|
|
997
|
+
_type: "content_views";
|
|
998
|
+
timestamp?: number;
|
|
999
|
+
content_id: string;
|
|
1000
|
+
content_type: ContentType;
|
|
1001
|
+
view_type: ViewType;
|
|
1002
|
+
watch_duration_ms: number;
|
|
1003
|
+
page: Page;
|
|
1004
|
+
feed_id?: string;
|
|
1005
|
+
post_id?: string;
|
|
1006
|
+
video_position_ms?: number;
|
|
1007
|
+
video_total_duration_ms?: number;
|
|
1008
|
+
segments?: Segment[];
|
|
1009
|
+
device_id?: string;
|
|
1010
|
+
country?: string;
|
|
1011
|
+
}
|
|
1012
|
+
interface EngagementEvent {
|
|
1013
|
+
_type: "engagement";
|
|
1014
|
+
timestamp?: number;
|
|
1015
|
+
engagement_type: string;
|
|
1016
|
+
resource_type?: string;
|
|
1017
|
+
resource_id?: string;
|
|
1018
|
+
page?: string;
|
|
1019
|
+
comment_text?: string;
|
|
1020
|
+
share_platform?: string;
|
|
1021
|
+
country?: string;
|
|
1022
|
+
device_type?: string;
|
|
1023
|
+
session_id?: string;
|
|
1024
|
+
}
|
|
1025
|
+
interface InteractionEvent {
|
|
1026
|
+
_type: "interaction";
|
|
1027
|
+
timestamp?: number;
|
|
1028
|
+
interaction_type: string;
|
|
1029
|
+
element_type?: string;
|
|
1030
|
+
element_id?: string;
|
|
1031
|
+
session_id?: string;
|
|
1032
|
+
page?: string;
|
|
1033
|
+
page_url?: string;
|
|
1034
|
+
scroll_depth?: number;
|
|
1035
|
+
search_query?: string;
|
|
1036
|
+
click_x?: number;
|
|
1037
|
+
click_y?: number;
|
|
1038
|
+
device_type?: string;
|
|
1039
|
+
browser?: string;
|
|
1040
|
+
resource_id?: string;
|
|
1041
|
+
resource_type?: string;
|
|
1042
|
+
feed_id?: string;
|
|
1043
|
+
post_id?: string;
|
|
1044
|
+
content_id?: string;
|
|
1045
|
+
}
|
|
1046
|
+
interface ErrorEvent {
|
|
1047
|
+
_type: "error";
|
|
1048
|
+
timestamp?: number;
|
|
1049
|
+
error_type: string;
|
|
1050
|
+
error_code?: string;
|
|
1051
|
+
error_message?: string;
|
|
1052
|
+
stack_trace?: string;
|
|
1053
|
+
resource_type?: string;
|
|
1054
|
+
resource_id?: string;
|
|
1055
|
+
endpoint?: string;
|
|
1056
|
+
session_id?: string;
|
|
1057
|
+
device_type?: string;
|
|
1058
|
+
browser?: string;
|
|
1059
|
+
os?: string;
|
|
1060
|
+
}
|
|
1061
|
+
interface SessionStartEvent {
|
|
1062
|
+
_type: "session_start";
|
|
1063
|
+
timestamp?: number;
|
|
1064
|
+
utm_source?: string;
|
|
1065
|
+
utm_campaign?: string;
|
|
1066
|
+
browser?: string;
|
|
1067
|
+
language?: string;
|
|
1068
|
+
screen_resolution?: string;
|
|
1069
|
+
platform?: string;
|
|
1070
|
+
tg_platform?: string;
|
|
1071
|
+
tg_version?: string;
|
|
1072
|
+
timezone?: string;
|
|
1073
|
+
timezone_offset?: number;
|
|
1074
|
+
color_depth?: number;
|
|
1075
|
+
touch_support?: boolean;
|
|
1076
|
+
country?: string;
|
|
1077
|
+
user_id?: string;
|
|
1078
|
+
}
|
|
1079
|
+
interface AnalyticsContext {
|
|
1080
|
+
user_id?: string;
|
|
1081
|
+
country?: string;
|
|
1082
|
+
city?: string;
|
|
1083
|
+
region?: string;
|
|
1084
|
+
device?: string;
|
|
1085
|
+
os?: string;
|
|
1086
|
+
os_version?: string;
|
|
1087
|
+
language?: string;
|
|
1088
|
+
browser?: string;
|
|
1089
|
+
screen_resolution?: string;
|
|
1090
|
+
platform?: string;
|
|
1091
|
+
tg_platform?: string;
|
|
1092
|
+
tg_version?: string;
|
|
1093
|
+
timezone?: string;
|
|
1094
|
+
timezone_offset?: number;
|
|
1095
|
+
color_depth?: number;
|
|
1096
|
+
touch_support?: boolean;
|
|
1097
|
+
utm_source?: string;
|
|
1098
|
+
utm_campaign?: string;
|
|
1099
|
+
}
|
|
1100
|
+
type AnalyticsEvent = ContentViewEvent | EngagementEvent | InteractionEvent | ErrorEvent | SessionStartEvent;
|
|
1101
|
+
interface CurrentSegment {
|
|
1102
|
+
start_ms: number;
|
|
1103
|
+
speed: number;
|
|
1104
|
+
}
|
|
1105
|
+
interface AnalyticsState {
|
|
1106
|
+
segments: Map<string, Segment[]>;
|
|
1107
|
+
currentSegment: Map<string, CurrentSegment | null>;
|
|
1108
|
+
}
|
|
1109
|
+
export declare const analyticsStore: WritableAtom<AnalyticsState>;
|
|
1110
|
+
export declare const analyticsStoreActions: {
|
|
1111
|
+
/**
|
|
1112
|
+
* Start tracking a new segment
|
|
1113
|
+
*/
|
|
1114
|
+
startSegment(playerId: string, positionMs: number, speed: number): void;
|
|
1115
|
+
/**
|
|
1116
|
+
* End the current segment being tracked
|
|
1117
|
+
*/
|
|
1118
|
+
endSegment(playerId: string, positionMs: number): void;
|
|
1119
|
+
/**
|
|
1120
|
+
* Get segments including the current in-progress segment
|
|
1121
|
+
*/
|
|
1122
|
+
getSegmentsWithCurrent(playerId: string, currentPositionMs: number): Segment[];
|
|
1123
|
+
/**
|
|
1124
|
+
* Clear all segments for a player
|
|
1125
|
+
*/
|
|
1126
|
+
clearSegments(playerId: string): void;
|
|
1127
|
+
};
|
|
1128
|
+
interface SegmentAnalyticsParams {
|
|
1129
|
+
playerId: UUID;
|
|
1130
|
+
currentTimeMs: number;
|
|
1131
|
+
playbackRate: number;
|
|
1132
|
+
isPaused: boolean;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Start tracking a new analytics segment
|
|
1136
|
+
*/
|
|
1137
|
+
export declare function startSegment(params: SegmentAnalyticsParams): void;
|
|
1138
|
+
/**
|
|
1139
|
+
* End the current analytics segment
|
|
1140
|
+
*/
|
|
1141
|
+
export declare function endSegment(params: Pick<SegmentAnalyticsParams, "playerId" | "currentTimeMs">): void;
|
|
1142
|
+
/**
|
|
1143
|
+
* Restart segment if video is currently playing
|
|
1144
|
+
* This is useful when playback rate changes or other segment-breaking events occur
|
|
1145
|
+
*/
|
|
1146
|
+
export declare function restartSegmentIfPlaying(params: SegmentAnalyticsParams): void;
|
|
1147
|
+
interface ControlsState {
|
|
1148
|
+
visible: boolean;
|
|
1149
|
+
hideTimeout: ReturnType<typeof setTimeout> | null;
|
|
1150
|
+
}
|
|
1151
|
+
interface ControlsStore {
|
|
1152
|
+
state: Map<UUID, MapStore<ControlsState>>;
|
|
1153
|
+
get: (id: UUID) => MapStore<ControlsState>;
|
|
1154
|
+
deleteControls: (id: UUID) => void;
|
|
1155
|
+
setVisible: (id: UUID, visible: boolean) => void;
|
|
1156
|
+
showControls: (id: UUID, autoHideMs?: number) => void;
|
|
1157
|
+
hideControls: (id: UUID) => void;
|
|
1158
|
+
}
|
|
1159
|
+
export declare const controlsStore: ControlsStore;
|
|
1160
|
+
interface FeedCacheState {
|
|
1161
|
+
cachedPosts: FeedPost[];
|
|
1162
|
+
isLoadingFromCache: boolean;
|
|
1163
|
+
}
|
|
1164
|
+
export declare const feedCacheAtom: WritableAtom<FeedCacheState>;
|
|
1165
|
+
interface FeedCacheStore {
|
|
1166
|
+
currentFeedId: string | undefined;
|
|
1167
|
+
loadCache: (feedId?: string) => FeedPost[];
|
|
1168
|
+
/**
|
|
1169
|
+
* Append posts to the cache. Pass the feed the posts came from and the batch
|
|
1170
|
+
* is refused if the cache has since moved to another feed.
|
|
1171
|
+
*/
|
|
1172
|
+
saveNewPosts: (newPosts: FeedPost[], feedId?: string) => void;
|
|
1173
|
+
removePost: (postId: string) => void;
|
|
1174
|
+
clearCache: () => void;
|
|
1175
|
+
getCacheStats: () => {
|
|
1176
|
+
size: number;
|
|
1177
|
+
timestamp: number | null;
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
export declare const feedCacheStore: FeedCacheStore;
|
|
1181
|
+
interface PostStoreState {
|
|
1182
|
+
initialized: boolean;
|
|
1183
|
+
feedId: string;
|
|
1184
|
+
posts: FeedPost[];
|
|
1185
|
+
isLoadedInitial: boolean;
|
|
1186
|
+
isFetchingVideos: boolean;
|
|
1187
|
+
currentIndex: number;
|
|
1188
|
+
total: number;
|
|
1189
|
+
}
|
|
1190
|
+
export declare const postStore: WritableAtom<PostStoreState>;
|
|
1191
|
+
interface PostStoreActions {
|
|
1192
|
+
init: (feedId: string) => void;
|
|
1193
|
+
loadInitialPosts: () => Promise<void>;
|
|
1194
|
+
fetchNewVideos: () => Promise<void>;
|
|
1195
|
+
setCurrentIndex: (index: number) => void;
|
|
1196
|
+
}
|
|
1197
|
+
export declare const postStoreActions: PostStoreActions;
|
|
1198
|
+
export declare const FEED_COMPONENTS_COUNT = 5;
|
|
1199
|
+
export declare const FEED_TRANSITION_THRESHOLD: number;
|
|
1200
|
+
interface FeedInteractionStore {
|
|
1201
|
+
hasInteracted: WritableAtom<boolean>;
|
|
1202
|
+
whenInteracted: WritableAtom<Date>;
|
|
1203
|
+
hasInitialized: WritableAtom<boolean>;
|
|
1204
|
+
videosInitialized: WritableAtom<number>;
|
|
1205
|
+
components: WritableAtom<number[]>;
|
|
1206
|
+
originalPositions: WritableAtom<number[]>;
|
|
1207
|
+
transforms: WritableAtom<number[]>;
|
|
1208
|
+
setTransforms: (transforms: number[]) => void;
|
|
1209
|
+
}
|
|
1210
|
+
export declare const feedStore: FeedInteractionStore;
|
|
1211
|
+
export declare const setHasInteracted: () => boolean;
|
|
1212
|
+
export declare const initializeVideo: () => void;
|
|
1213
|
+
interface PlayerContainerElementStore {
|
|
1214
|
+
register: (playerId: UUID, element: HTMLDivElement) => void;
|
|
1215
|
+
unregister: (playerId: UUID) => void;
|
|
1216
|
+
get: (playerId: UUID) => HTMLDivElement | null;
|
|
1217
|
+
subscribe: (playerId: UUID, listener: () => void) => () => void;
|
|
1218
|
+
clear: () => void;
|
|
1219
|
+
}
|
|
1220
|
+
export declare const playerContainerElementStore: PlayerContainerElementStore;
|
|
1221
|
+
interface PlayerMediaElementStore {
|
|
1222
|
+
register: (playerId: UUID, element: HTMLVideoElement) => void;
|
|
1223
|
+
unregister: (playerId: UUID) => void;
|
|
1224
|
+
get: (playerId: UUID) => HTMLVideoElement | null;
|
|
1225
|
+
subscribe: (playerId: UUID, listener: () => void) => () => void;
|
|
1226
|
+
clear: () => void;
|
|
1227
|
+
}
|
|
1228
|
+
export declare const playerMediaElementStore: PlayerMediaElementStore;
|
|
1229
|
+
export declare const defaultSettings: SettingsState;
|
|
1230
|
+
export declare const FONT_FAMILIES: {
|
|
1231
|
+
readonly monospace_serif: "\"Courier New\", Courier, \"Nimbus Mono L\", \"Cutive Mono\", monospace";
|
|
1232
|
+
readonly proportional_serif: "\"Times New Roman\", Times, Georgia, Cambria, \"PT Serif Caption\", serif";
|
|
1233
|
+
readonly monospace_sans: "\"Deja Vu Sans Mono\", \"Lucida Console\", Monaco, Consolas, \"PT Mono\", monospace";
|
|
1234
|
+
readonly proportional_sans: "Roboto, Arial, Helvetica, Verdana, \"PT Sans Caption\", sans-serif";
|
|
1235
|
+
readonly casual: "\"Comic Sans MS\", Impact, Handlee, fantasy";
|
|
1236
|
+
readonly cursive: "\"Monotype Corsiva\", \"URW Chancery L\", \"Apple Chancery\", \"Dancing Script\", cursive";
|
|
1237
|
+
readonly small_caps: "Arial, Helvetica, Verdana, \"Marcellus SC\", sans-serif";
|
|
1238
|
+
};
|
|
1239
|
+
type FontFamilyKey = keyof typeof FONT_FAMILIES;
|
|
1240
|
+
export declare const FONT_SIZES: number[];
|
|
1241
|
+
export declare const COLORS: Record<string, string>;
|
|
1242
|
+
export declare const EDGE_STYLES: string[];
|
|
1243
|
+
export declare const PLAYBACK_SPEEDS: number[];
|
|
1244
|
+
export declare const LANG_TYPES: string[];
|
|
1245
|
+
/**
|
|
1246
|
+
* Validate and parse settings, returning defaults for invalid values
|
|
1247
|
+
* @param parsed - Raw settings object (e.g., from localStorage)
|
|
1248
|
+
* @returns Validated settings with defaults applied for invalid values
|
|
1249
|
+
*/
|
|
1250
|
+
export declare const validateSettings: (parsed: unknown) => SettingsState;
|
|
1251
|
+
export declare const loadSettings: () => SettingsState;
|
|
1252
|
+
export declare const saveSettings: (settings: SettingsState) => void;
|
|
1253
|
+
interface SettingsStore {
|
|
1254
|
+
state: MapStore<SettingsState>;
|
|
1255
|
+
getSettings: () => SettingsState;
|
|
1256
|
+
setVolume: (volume: number) => number;
|
|
1257
|
+
setPlaybackSpeed: (playbackSpeed: number) => number;
|
|
1258
|
+
setCustomPlayback: (customPlayback: number) => number;
|
|
1259
|
+
setAllowCaptions: (allowCaptions: boolean) => boolean;
|
|
1260
|
+
setCaptionPosition: (x: number, y: number) => {
|
|
1261
|
+
x: number;
|
|
1262
|
+
y: number;
|
|
1263
|
+
};
|
|
1264
|
+
setSubtitleLang: (captionLang: CaptionLangType) => CaptionLangType;
|
|
1265
|
+
setCaptionFontFamily: (captionFontFamily: string) => string;
|
|
1266
|
+
setCaptionFontSize: (captionFontSize: number) => number;
|
|
1267
|
+
setCaptionFontColor: (captionFontColor: string) => string;
|
|
1268
|
+
setCaptionBGColor: (captionBGColor: string) => string;
|
|
1269
|
+
setCaptionBGOpacity: (captionOpacity: number) => number;
|
|
1270
|
+
setCaptionWindowColor: (captionWindowColor: string) => string;
|
|
1271
|
+
setCaptionWindowOpacity: (captionWindowOpacity: number) => number;
|
|
1272
|
+
setCaptionFontOpacity: (captionFontOpacity: number) => number;
|
|
1273
|
+
setCaptionCharacterEdgeStyle: (captionCharacterEdgeStyle: string) => string;
|
|
1274
|
+
resetCaptionSettings: () => void;
|
|
1275
|
+
setSelectedLanguageCode: (languageCode: string) => string | undefined;
|
|
1276
|
+
setMuted: (muted: boolean) => boolean;
|
|
1277
|
+
setPreferredQuality: (quality: number | undefined) => number | undefined;
|
|
1278
|
+
setPreferredAudioLanguage: (language: string | undefined) => string | undefined;
|
|
1279
|
+
setPreferredSubtitleLanguage: (language: string | undefined) => string | undefined;
|
|
1280
|
+
}
|
|
1281
|
+
export declare const settingsStore: SettingsStore;
|
|
1282
|
+
/**
|
|
1283
|
+
* Settings-menu keys follow a hierarchical naming convention:
|
|
1284
|
+
* - Root menus: simple names (main, quality, audio, captions, playback)
|
|
1285
|
+
* - Nested menus: parent-child pattern (captions.style, captions.font, etc.)
|
|
1286
|
+
*/
|
|
1287
|
+
type MenuKey = "main" | "quality" | "audio" | "captions" | "playback" | "captions.style" | "captions.font" | "captions.background" | "captions.window" | "captions.font-family" | "captions.edge-style";
|
|
1288
|
+
/** Stack of visited menus for navigation history */
|
|
1289
|
+
export declare const menuStack: WritableAtom<MenuKey[]>;
|
|
1290
|
+
/** Direction of the last navigation (for animation) */
|
|
1291
|
+
export declare const navigationDirection: WritableAtom<"forward" | "back">;
|
|
1292
|
+
/** Current active menu is the top of the stack */
|
|
1293
|
+
export declare const activeMenu: ReadableAtom<MenuKey>;
|
|
1294
|
+
/** Navigate to a new menu (pushes to stack) */
|
|
1295
|
+
export declare const pushMenu: (menuId: MenuKey) => void;
|
|
1296
|
+
/** Go back to previous menu (pops from stack) */
|
|
1297
|
+
export declare const popMenu: () => void;
|
|
1298
|
+
/** Reset to main menu */
|
|
1299
|
+
export declare const resetMenu: () => void;
|
|
1300
|
+
/**
|
|
1301
|
+
* Fill percentage for the custom playback-speed slider. Piecewise on purpose:
|
|
1302
|
+
* normal speed (1.0x) sits at the visual middle of the track, with min–1.0
|
|
1303
|
+
* mapped to the lower half and 1.0–max to the upper half. Note the pointer
|
|
1304
|
+
* mapping below is linear — the same asymmetry the React slider ships with.
|
|
1305
|
+
*/
|
|
1306
|
+
export declare function computePlaybackFillPercentage(value: number, min: number, max: number): number;
|
|
1307
|
+
interface PlaybackPointerOptions {
|
|
1308
|
+
rect: {
|
|
1309
|
+
left: number;
|
|
1310
|
+
width: number;
|
|
1311
|
+
};
|
|
1312
|
+
clientX: number;
|
|
1313
|
+
min: number;
|
|
1314
|
+
max: number;
|
|
1315
|
+
step: number;
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Linear pointer-position → playback value: clamped to the track, snapped to
|
|
1319
|
+
* the step grid, formatted to two decimals (the value React persists via
|
|
1320
|
+
* settingsStore.setCustomPlayback).
|
|
1321
|
+
*/
|
|
1322
|
+
export declare function computePlaybackValueFromPointer({ rect, clientX, min, max, step }: PlaybackPointerOptions): number;
|
|
1323
|
+
/**
|
|
1324
|
+
* Settings-menu strings, mirroring the corresponding keys of
|
|
1325
|
+
* packages/react/src/locales/<lang>.json verbatim (quality.*, audio.*,
|
|
1326
|
+
* playback.*, captions.* and common.*). Edge-style options are keyed by the
|
|
1327
|
+
* EDGE_STYLES runtime values and font families by FontFamilyKey, so menus can
|
|
1328
|
+
* look labels up directly from the constants they render.
|
|
1329
|
+
*
|
|
1330
|
+
* Import this module from the lazy settings chunk only — it must not be
|
|
1331
|
+
* pulled into the initial player bundle.
|
|
1332
|
+
*/
|
|
1333
|
+
interface PlayerMenuLabels {
|
|
1334
|
+
quality: {
|
|
1335
|
+
title: string;
|
|
1336
|
+
auto: string;
|
|
1337
|
+
};
|
|
1338
|
+
audio: {
|
|
1339
|
+
title: string;
|
|
1340
|
+
noTracks: string;
|
|
1341
|
+
};
|
|
1342
|
+
playback: {
|
|
1343
|
+
speed: string;
|
|
1344
|
+
custom: string;
|
|
1345
|
+
normal: string;
|
|
1346
|
+
};
|
|
1347
|
+
captions: {
|
|
1348
|
+
title: string;
|
|
1349
|
+
off: string;
|
|
1350
|
+
style: string;
|
|
1351
|
+
font: string;
|
|
1352
|
+
background: string;
|
|
1353
|
+
window: string;
|
|
1354
|
+
resetAll: string;
|
|
1355
|
+
edgeStyle: {
|
|
1356
|
+
title: string;
|
|
1357
|
+
options: Record<string, string>;
|
|
1358
|
+
};
|
|
1359
|
+
fontFamilies: Record<FontFamilyKey, string>;
|
|
1360
|
+
};
|
|
1361
|
+
stats: {
|
|
1362
|
+
title: string;
|
|
1363
|
+
copyDebug: string;
|
|
1364
|
+
videoId: string;
|
|
1365
|
+
playerId: string;
|
|
1366
|
+
viewport: string;
|
|
1367
|
+
dropped: string;
|
|
1368
|
+
resolution: string;
|
|
1369
|
+
volume: string;
|
|
1370
|
+
codecs: string;
|
|
1371
|
+
color: string;
|
|
1372
|
+
connectionSpeed: string;
|
|
1373
|
+
bufferHealth: string;
|
|
1374
|
+
};
|
|
1375
|
+
shortcuts: {
|
|
1376
|
+
title: string;
|
|
1377
|
+
playPause: string;
|
|
1378
|
+
seek10s: string;
|
|
1379
|
+
seek5s: string;
|
|
1380
|
+
pip: string;
|
|
1381
|
+
fullscreen: string;
|
|
1382
|
+
mute: string;
|
|
1383
|
+
volume: string;
|
|
1384
|
+
};
|
|
1385
|
+
colors: Record<string, string>;
|
|
1386
|
+
common: {
|
|
1387
|
+
back: string;
|
|
1388
|
+
family: string;
|
|
1389
|
+
more: string;
|
|
1390
|
+
less: string;
|
|
1391
|
+
settings: string;
|
|
1392
|
+
size: string;
|
|
1393
|
+
opacity: string;
|
|
1394
|
+
color: string;
|
|
1395
|
+
reset: string;
|
|
1396
|
+
custom: string;
|
|
1397
|
+
options: string;
|
|
1398
|
+
off: string;
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
export declare function getPlayerMenuLabels(code: string | null | undefined): PlayerMenuLabels;
|
|
1402
|
+
interface SubtitleTrack {
|
|
1403
|
+
index: number;
|
|
1404
|
+
label: string;
|
|
1405
|
+
language: string;
|
|
1406
|
+
kind: string;
|
|
1407
|
+
track: TextTrack;
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Calculates the efficient end time for a cue to prevent flickering and ensure readability.
|
|
1411
|
+
*/
|
|
1412
|
+
export declare const calculateSmoothedEndTime: (cue: TextTrackCue | VTTCue, nextCue?: TextTrackCue | VTTCue) => number;
|
|
1413
|
+
/**
|
|
1414
|
+
* Helper to find the next cue in a TextTrackCueList by reference.
|
|
1415
|
+
*/
|
|
1416
|
+
export declare const findNextCue: (activeCue: TextTrackCue, allCues: TextTrackCueList | null) => TextTrackCue | undefined;
|
|
1417
|
+
export declare function stripHtmlTags(input: string): string;
|
|
1418
|
+
/**
|
|
1419
|
+
* Extracts and cleans text from a cue object.
|
|
1420
|
+
* Handles VTTCue vs TextTrackCue differences.
|
|
1421
|
+
*/
|
|
1422
|
+
export declare const getCleanCueText: (cue: TextTrackCue | VTTCue) => string;
|
|
1423
|
+
/**
|
|
1424
|
+
* Applies smoothing logic to a list of cues (typically activeCues).
|
|
1425
|
+
* Modifies the cues in-place and caches them to avoid re-processing.
|
|
1426
|
+
*/
|
|
1427
|
+
export declare const processCues: (cues: TextTrackCueList | TextTrackCue[], allCues: TextTrackCueList | null) => void;
|
|
1428
|
+
/**
|
|
1429
|
+
* Parses the video element's text tracks and extracts relevant subtitle/caption tracks.
|
|
1430
|
+
* Also normalizes track state.
|
|
1431
|
+
*/
|
|
1432
|
+
export declare const extractSubtitleTracks: (tracks: TextTrackList) => SubtitleTrack[];
|
|
1433
|
+
interface PlayerState {
|
|
1434
|
+
paused: boolean;
|
|
1435
|
+
loading: boolean;
|
|
1436
|
+
ended: boolean;
|
|
1437
|
+
mounted: boolean;
|
|
1438
|
+
hasInteracted: boolean;
|
|
1439
|
+
whenInteracted: Date;
|
|
1440
|
+
isFullscreen: boolean;
|
|
1441
|
+
isPictureInPicture: boolean;
|
|
1442
|
+
muted: boolean;
|
|
1443
|
+
playbackRate: number;
|
|
1444
|
+
volume: number;
|
|
1445
|
+
previousVolume: number;
|
|
1446
|
+
viewport: {
|
|
1447
|
+
width: number;
|
|
1448
|
+
height: number;
|
|
1449
|
+
};
|
|
1450
|
+
resolution: number;
|
|
1451
|
+
subtitleTrack: number;
|
|
1452
|
+
availableSubtitleTracks: SubtitleTrack[];
|
|
1453
|
+
currentSubtitleText: string;
|
|
1454
|
+
audioTrack: number;
|
|
1455
|
+
availableAudioTracks: AudioTrack[];
|
|
1456
|
+
hlsInstance: Hls | null;
|
|
1457
|
+
}
|
|
1458
|
+
interface PlayerProgressSnapshot {
|
|
1459
|
+
currentTime: number;
|
|
1460
|
+
duration: number;
|
|
1461
|
+
progress: number;
|
|
1462
|
+
buffered: number;
|
|
1463
|
+
}
|
|
1464
|
+
interface PlayerStateStore {
|
|
1465
|
+
state: Map<UUID, MapStore<PlayerState>>;
|
|
1466
|
+
get: (id: UUID) => MapStore<PlayerState>;
|
|
1467
|
+
deletePlayer: (id: UUID) => void;
|
|
1468
|
+
getEffectiveMediaMuted: (player: Pick<PlayerState, "muted" | "volume" | "hasInteracted">) => boolean;
|
|
1469
|
+
muteMedia: (id: UUID) => void;
|
|
1470
|
+
unmuteMedia: (id: UUID) => void;
|
|
1471
|
+
toggleMediaMute: (id: UUID) => void;
|
|
1472
|
+
setMediaVolume: (id: UUID, volume: number) => void;
|
|
1473
|
+
playMedia: (id: UUID) => Promise<void>;
|
|
1474
|
+
pauseMedia: (id: UUID) => void;
|
|
1475
|
+
toggleMediaPlayback: (id: UUID) => Promise<void>;
|
|
1476
|
+
pauseOtherMedia: (except: UUID) => void;
|
|
1477
|
+
setMediaPlaybackRate: (id: UUID, playbackRate: number) => void;
|
|
1478
|
+
readProgressSnapshot: (id: UUID) => PlayerProgressSnapshot;
|
|
1479
|
+
subscribeToProgress: (id: UUID, onStoreChange: () => void) => () => void;
|
|
1480
|
+
seekProgressTo: (id: UUID, seconds: number) => void;
|
|
1481
|
+
seekProgressBy: (id: UUID, deltaSeconds: number) => void;
|
|
1482
|
+
getTrackOptions: (tracks: {
|
|
1483
|
+
label: string;
|
|
1484
|
+
language: string;
|
|
1485
|
+
kind: string;
|
|
1486
|
+
}[]) => {
|
|
1487
|
+
index: number;
|
|
1488
|
+
label: string;
|
|
1489
|
+
language: string;
|
|
1490
|
+
kind: string;
|
|
1491
|
+
}[];
|
|
1492
|
+
selectAudioTrack: (id: UUID, trackIndex: number) => void;
|
|
1493
|
+
selectSubtitleTrack: (id: UUID, trackIndex: number) => void;
|
|
1494
|
+
setAudioTrack: (id: UUID, audioTrack: number) => void;
|
|
1495
|
+
setAvailableAudioTracks: (id: UUID, tracks: AudioTrack[]) => void;
|
|
1496
|
+
setGlobalMuted: (muted: boolean) => void;
|
|
1497
|
+
setGlobalVolume: (volume: number) => void;
|
|
1498
|
+
setGlobalPlaybackRate: (rate: number) => void;
|
|
1499
|
+
setGlobalPreferredQuality: (quality: number | undefined) => void;
|
|
1500
|
+
setGlobalPreferredAudioLanguage: (language: string | undefined) => void;
|
|
1501
|
+
setGlobalPreferredSubtitleLanguage: (language: string | undefined) => void;
|
|
1502
|
+
pauseAllPlayers: () => void;
|
|
1503
|
+
setMounted: (id: UUID) => void;
|
|
1504
|
+
setHasInteracted: (id: UUID) => void;
|
|
1505
|
+
setPaused: (id: UUID, paused: boolean) => void;
|
|
1506
|
+
setLoading: (id: UUID, loading: boolean) => void;
|
|
1507
|
+
setEnded: (id: UUID, ended: boolean) => void;
|
|
1508
|
+
setFullscreen: (id: UUID, isFullscreen: boolean) => void;
|
|
1509
|
+
setPictureInPicture: (id: UUID, isPictureInPicture: boolean) => void;
|
|
1510
|
+
setMuted: (id: UUID, muted: boolean) => void;
|
|
1511
|
+
setPlaybackRate: (id: UUID, playbackRate: number) => void;
|
|
1512
|
+
setPreviousVolume: (id: UUID, previousVolume: number) => void;
|
|
1513
|
+
setVolume: (id: UUID, volume: number) => void;
|
|
1514
|
+
setViewport: (id: UUID, width: number, height: number) => void;
|
|
1515
|
+
setResolution: (id: UUID, resolution: number) => void;
|
|
1516
|
+
setSubtitleTrack: (id: UUID, subtitleTrack: number) => void;
|
|
1517
|
+
setAvailableSubtitleTracks: (id: UUID, tracks: SubtitleTrack[]) => void;
|
|
1518
|
+
setCurrentSubtitleText: (id: UUID, text: string) => void;
|
|
1519
|
+
setHlsInstance: (id: UUID, hlsInstance: Hls | null) => void;
|
|
1520
|
+
}
|
|
1521
|
+
export declare const playerStateStore: PlayerStateStore;
|
|
1522
|
+
interface PlayerUiActionTargets {
|
|
1523
|
+
container?: HTMLDivElement | null;
|
|
1524
|
+
video?: HTMLVideoElement | null;
|
|
1525
|
+
}
|
|
1526
|
+
export declare function showPlayerControls(playerId: UUID): void;
|
|
1527
|
+
export declare function hidePlayerControls(playerId: UUID): void;
|
|
1528
|
+
export declare function togglePlayerFullscreen(playerId: UUID, overrides?: PlayerUiActionTargets): Promise<void>;
|
|
1529
|
+
export declare function togglePlayerPictureInPicture(playerId: UUID, overrides?: PlayerUiActionTargets): Promise<void>;
|
|
1530
|
+
/**
|
|
1531
|
+
* Structural shape of a video element ref. Compatible with React.RefObject
|
|
1532
|
+
* but does not depend on React, keeping this module framework-agnostic.
|
|
1533
|
+
*/
|
|
1534
|
+
interface VideoElementRef {
|
|
1535
|
+
current: HTMLVideoElement | null;
|
|
1536
|
+
}
|
|
1537
|
+
export declare const handleLoadedMetadata: (videoElement: HTMLVideoElement, playerId: UUID) => void;
|
|
1538
|
+
export declare const clearSubtitles: (playerId: UUID) => void;
|
|
1539
|
+
/**
|
|
1540
|
+
* Handles the 'cuechange' event to update subtitles.
|
|
1541
|
+
* Uses event-driven logic + lookahead smoothing to prevent flickering.
|
|
1542
|
+
*/
|
|
1543
|
+
export declare const handleCueChange: (playerId: UUID, videoRef: VideoElementRef) => void;
|
|
1544
|
+
/**
|
|
1545
|
+
* Pure buffer-progress computations extracted from useBufferProgress.
|
|
1546
|
+
* Operate on plain TimeRanges + numbers, not HTMLVideoElement, so they
|
|
1547
|
+
* are framework-agnostic and trivially testable.
|
|
1548
|
+
*/
|
|
1549
|
+
interface BufferRange {
|
|
1550
|
+
start: number;
|
|
1551
|
+
end: number;
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Convert a TimeRanges into normalized 0-100 percentage ranges.
|
|
1555
|
+
* Returns [] when duration is zero/invalid or timeRanges is missing.
|
|
1556
|
+
*/
|
|
1557
|
+
export declare const computeBufferRanges: (timeRanges: TimeRanges | null | undefined, duration: number) => BufferRange[];
|
|
1558
|
+
/**
|
|
1559
|
+
* How much buffer (as a 0-100 percentage) is loaded ahead of the
|
|
1560
|
+
* current playback position. Considers only ranges that cover the
|
|
1561
|
+
* current position; takes the maximum contiguous lookahead.
|
|
1562
|
+
*/
|
|
1563
|
+
export declare const computeBufferedAhead: (ranges: BufferRange[], currentTime: number, duration: number) => number;
|
|
1564
|
+
/**
|
|
1565
|
+
* Pure progress-fraction helper extracted from useProgressBar.
|
|
1566
|
+
*
|
|
1567
|
+
* Returns a value in [0, 1] representing how far through `duration`
|
|
1568
|
+
* `currentTime` is. Defensive against the common edge cases where
|
|
1569
|
+
* duration is zero, NaN, or Infinity (e.g. before metadata loads),
|
|
1570
|
+
* and against non-finite currentTime values. The previous inline
|
|
1571
|
+
* `video.currentTime / video.duration` formula produced NaN in
|
|
1572
|
+
* those cases, which leaked into React state.
|
|
1573
|
+
*/
|
|
1574
|
+
export declare function computeProgressFraction(currentTime: number, duration: number): number;
|
|
1575
|
+
interface BuildEventParams {
|
|
1576
|
+
contentId: string;
|
|
1577
|
+
contentType: ContentType;
|
|
1578
|
+
viewType: ViewType;
|
|
1579
|
+
watchDurationMs: number;
|
|
1580
|
+
page: Page;
|
|
1581
|
+
feedId?: string;
|
|
1582
|
+
postId?: string;
|
|
1583
|
+
videoPositionMs?: number;
|
|
1584
|
+
videoTotalDurationMs?: number;
|
|
1585
|
+
segments?: Segment[];
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Build a content view event for analytics
|
|
1589
|
+
* Pure function that constructs the event payload
|
|
1590
|
+
*/
|
|
1591
|
+
export declare function buildContentViewEvent(params: BuildEventParams): ContentViewEvent;
|
|
1592
|
+
/**
|
|
1593
|
+
* Calculate watch duration from start time to now
|
|
1594
|
+
*/
|
|
1595
|
+
export declare function calculateWatchDuration(startTimeMs: number | null): number;
|
|
1596
|
+
/**
|
|
1597
|
+
* Determine the effective page based on feedId presence
|
|
1598
|
+
*/
|
|
1599
|
+
export declare function getEffectivePage(page: Page, feedId?: string): Page;
|
|
1600
|
+
/**
|
|
1601
|
+
* Check if segments should be included in the event
|
|
1602
|
+
* Only include for "watch" and "end" view types
|
|
1603
|
+
*/
|
|
1604
|
+
export declare function shouldIncludeSegments(viewType: ViewType): boolean;
|
|
1605
|
+
/**
|
|
1606
|
+
* Round milliseconds for video position/duration
|
|
1607
|
+
*/
|
|
1608
|
+
export declare function roundMs(ms: number): number;
|
|
1609
|
+
/**
|
|
1610
|
+
* Convert seconds to milliseconds
|
|
1611
|
+
*/
|
|
1612
|
+
export declare function secondsToMs(seconds: number): number;
|
|
1613
|
+
/**
|
|
1614
|
+
* Convert milliseconds to seconds (for timestamp)
|
|
1615
|
+
*/
|
|
1616
|
+
export declare function msToSeconds(ms: number): number;
|
|
1617
|
+
interface VideoAnalyticsPayload {
|
|
1618
|
+
videoPositionMs?: number;
|
|
1619
|
+
videoTotalDurationMs?: number;
|
|
1620
|
+
segments?: Segment[];
|
|
1621
|
+
}
|
|
1622
|
+
interface VideoSnapshot {
|
|
1623
|
+
currentTime: number;
|
|
1624
|
+
duration: number;
|
|
1625
|
+
}
|
|
1626
|
+
interface BuildVideoAnalyticsPayloadParams {
|
|
1627
|
+
contentType: ContentType;
|
|
1628
|
+
viewType: ViewType;
|
|
1629
|
+
video?: VideoSnapshot | null;
|
|
1630
|
+
playerId?: string;
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Build and POST a single content-view event.
|
|
1634
|
+
*/
|
|
1635
|
+
export declare function postContentEvent(params: BuildEventParams): void;
|
|
1636
|
+
/**
|
|
1637
|
+
* Compute the video position / duration / segments to attach to a
|
|
1638
|
+
* content-view event. Returns an empty payload for non-video content
|
|
1639
|
+
* or when the video snapshot is unavailable.
|
|
1640
|
+
*/
|
|
1641
|
+
export declare function buildVideoAnalyticsPayload({ contentType, viewType, video, playerId }: BuildVideoAnalyticsPayloadParams): VideoAnalyticsPayload;
|
|
1642
|
+
export declare const analyticsContextStore: WritableAtom<AnalyticsContext>;
|
|
1643
|
+
export declare function setAnalyticsContext(context: Partial<AnalyticsContext>): void;
|
|
1644
|
+
interface DeviceState {
|
|
1645
|
+
deviceId: string | undefined;
|
|
1646
|
+
}
|
|
1647
|
+
export declare const analyticsDeviceStore: WritableAtom<DeviceState>;
|
|
1648
|
+
export declare function getOrCreateDeviceId(): string;
|
|
1649
|
+
export declare function setDeviceId(deviceId: string): void;
|
|
1650
|
+
interface SessionState {
|
|
1651
|
+
sessionId: string | undefined;
|
|
1652
|
+
started: boolean;
|
|
1653
|
+
}
|
|
1654
|
+
export declare const analyticsSessionStore: WritableAtom<SessionState>;
|
|
1655
|
+
export declare function getOrCreateSessionId(): string;
|
|
1656
|
+
export declare function initializeAnalyticsContext(): void;
|
|
1657
|
+
/**
|
|
1658
|
+
* Chapter with calculated end time and duration
|
|
1659
|
+
*/
|
|
1660
|
+
interface ChapterWithEnd extends Chapter {
|
|
1661
|
+
endTime: number;
|
|
1662
|
+
durationSec: number;
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Check if a single chapter is within the video duration
|
|
1666
|
+
* @param chapter - The chapter to validate
|
|
1667
|
+
* @param videoDuration - The total video duration in seconds
|
|
1668
|
+
* @param tolerance - Tolerance in seconds for rounding errors (default: 1)
|
|
1669
|
+
* @returns Whether the chapter is within valid bounds
|
|
1670
|
+
*/
|
|
1671
|
+
export declare function isChapterWithinDuration(chapter: Chapter, videoDuration: number, tolerance?: number): boolean;
|
|
1672
|
+
/**
|
|
1673
|
+
* Validate chapters for use with chaptered progress bar
|
|
1674
|
+
* Returns false if:
|
|
1675
|
+
* - chapters is undefined or empty
|
|
1676
|
+
* - less than 2 chapters (need multiple for chaptered bar)
|
|
1677
|
+
* - any chapter exceeds video duration
|
|
1678
|
+
*
|
|
1679
|
+
* @param chapters - Array of chapters to validate
|
|
1680
|
+
* @param videoDuration - The total video duration in seconds
|
|
1681
|
+
* @param tolerance - Tolerance in seconds for rounding errors (default: 1)
|
|
1682
|
+
* @returns Whether chapters are valid for chaptered progress bar
|
|
1683
|
+
*/
|
|
1684
|
+
export declare function validateChapters(chapters: Chapter[] | undefined, videoDuration: number, tolerance?: number): boolean;
|
|
1685
|
+
/**
|
|
1686
|
+
* Calculate chapter end times based on next chapter start or video duration
|
|
1687
|
+
*/
|
|
1688
|
+
export declare function calculateChaptersWithEnd(chapters: Chapter[], duration: number): ChapterWithEnd[];
|
|
1689
|
+
/**
|
|
1690
|
+
* Slice heatmap data for a specific chapter based on its time range
|
|
1691
|
+
*/
|
|
1692
|
+
interface HeatmapSliceParams {
|
|
1693
|
+
heatmapData: number[];
|
|
1694
|
+
chapterStartTime: number;
|
|
1695
|
+
chapterEndTime: number;
|
|
1696
|
+
videoDuration: number;
|
|
1697
|
+
}
|
|
1698
|
+
export declare function sliceHeatmapData({ heatmapData, chapterStartTime, chapterEndTime, videoDuration }: HeatmapSliceParams): number[];
|
|
1699
|
+
/**
|
|
1700
|
+
* Calculate all heatmap slices for chapters
|
|
1701
|
+
*/
|
|
1702
|
+
export declare function calculateHeatmapSlices(heatmapData: number[] | null, chapters: ChapterWithEnd[], duration: number): number[][];
|
|
1703
|
+
/**
|
|
1704
|
+
* Calculate global max value from heatmap data for consistent normalization
|
|
1705
|
+
*/
|
|
1706
|
+
export declare function calculateGlobalMax(heatmapData: number[] | null): number;
|
|
1707
|
+
/**
|
|
1708
|
+
* Calculate progress percentage within a chapter segment
|
|
1709
|
+
* @returns 0-100 percentage
|
|
1710
|
+
*/
|
|
1711
|
+
export declare function calculateSegmentProgress(currentPosition: number, chapterStart: number, chapterEnd: number): number;
|
|
1712
|
+
/**
|
|
1713
|
+
* Calculate buffer ranges within a chapter segment
|
|
1714
|
+
* Converts global buffer ranges to segment-local 0-100 scale
|
|
1715
|
+
*/
|
|
1716
|
+
type SegmentBufferParams = {
|
|
1717
|
+
globalBufferRanges: BufferRange[];
|
|
1718
|
+
chapterStart: number;
|
|
1719
|
+
chapterEnd: number;
|
|
1720
|
+
videoDuration: number;
|
|
1721
|
+
};
|
|
1722
|
+
export declare function calculateSegmentBufferRanges({ globalBufferRanges, chapterStart, chapterEnd, videoDuration }: SegmentBufferParams): BufferRange[];
|
|
1723
|
+
/**
|
|
1724
|
+
* Calculate flex grow ratio for a chapter based on its duration
|
|
1725
|
+
*/
|
|
1726
|
+
export declare function calculateFlexGrow(chapterDuration: number, videoDuration: number): number;
|
|
1727
|
+
/**
|
|
1728
|
+
* Determine current playback position
|
|
1729
|
+
* Uses progress during drag for real-time feedback, currentTime otherwise
|
|
1730
|
+
*/
|
|
1731
|
+
interface CurrentPositionParams {
|
|
1732
|
+
isDragging: boolean;
|
|
1733
|
+
progress: number;
|
|
1734
|
+
duration: number;
|
|
1735
|
+
currentTime: number;
|
|
1736
|
+
}
|
|
1737
|
+
export declare function getCurrentPosition({ isDragging, progress, duration, currentTime }: CurrentPositionParams): number;
|
|
1738
|
+
/**
|
|
1739
|
+
* Find which segment is hovered based on mouse X position
|
|
1740
|
+
* @param segments - Array of segment DOM elements
|
|
1741
|
+
* @param mouseX - Mouse X coordinate (clientX)
|
|
1742
|
+
* @returns Index of hovered segment, or null if none
|
|
1743
|
+
*/
|
|
1744
|
+
export declare function findHoveredSegmentIndex(segments: Array<HTMLDivElement | null>, mouseX: number): number | null;
|
|
1745
|
+
/**
|
|
1746
|
+
* Start time of the first chapter after the current position, or null when
|
|
1747
|
+
* already in the last chapter. Mirrors React useChapterNavigation.
|
|
1748
|
+
*/
|
|
1749
|
+
export declare function getNextChapterStart(chapters: Chapter[] | undefined, currentTime: number): number | null;
|
|
1750
|
+
/**
|
|
1751
|
+
* Target for "previous chapter": restart the current chapter when more than
|
|
1752
|
+
* 3s in (or when it is the first), otherwise jump to the previous chapter.
|
|
1753
|
+
* Mirrors React useChapterNavigation.
|
|
1754
|
+
*/
|
|
1755
|
+
export declare function getPrevChapterTarget(chapters: Chapter[] | undefined, currentTime: number): number | null;
|
|
1756
|
+
interface BuildEngagementParams {
|
|
1757
|
+
engagementType: string;
|
|
1758
|
+
resourceType?: string;
|
|
1759
|
+
resourceId?: string;
|
|
1760
|
+
page?: string;
|
|
1761
|
+
commentText?: string;
|
|
1762
|
+
sharePlatform?: string;
|
|
1763
|
+
country?: string;
|
|
1764
|
+
deviceType?: string;
|
|
1765
|
+
sessionId?: string;
|
|
1766
|
+
}
|
|
1767
|
+
interface BuildInteractionParams {
|
|
1768
|
+
interactionType: string;
|
|
1769
|
+
elementType?: string;
|
|
1770
|
+
elementId?: string;
|
|
1771
|
+
sessionId?: string;
|
|
1772
|
+
page?: string;
|
|
1773
|
+
pageUrl?: string;
|
|
1774
|
+
scrollDepth?: number;
|
|
1775
|
+
searchQuery?: string;
|
|
1776
|
+
clickX?: number;
|
|
1777
|
+
clickY?: number;
|
|
1778
|
+
deviceType?: string;
|
|
1779
|
+
browser?: string;
|
|
1780
|
+
resourceId?: string;
|
|
1781
|
+
resourceType?: string;
|
|
1782
|
+
feedId?: string;
|
|
1783
|
+
postId?: string;
|
|
1784
|
+
contentId?: string;
|
|
1785
|
+
}
|
|
1786
|
+
interface BuildErrorParams {
|
|
1787
|
+
errorType: string;
|
|
1788
|
+
errorCode?: string;
|
|
1789
|
+
errorMessage?: string;
|
|
1790
|
+
stackTrace?: string;
|
|
1791
|
+
resourceType?: string;
|
|
1792
|
+
resourceId?: string;
|
|
1793
|
+
endpoint?: string;
|
|
1794
|
+
sessionId?: string;
|
|
1795
|
+
deviceType?: string;
|
|
1796
|
+
browser?: string;
|
|
1797
|
+
os?: string;
|
|
1798
|
+
}
|
|
1799
|
+
interface BuildSessionStartParams {
|
|
1800
|
+
browser?: string;
|
|
1801
|
+
language?: string;
|
|
1802
|
+
screenResolution?: string;
|
|
1803
|
+
platform?: string;
|
|
1804
|
+
tgPlatform?: string;
|
|
1805
|
+
tgVersion?: string;
|
|
1806
|
+
timezone?: string;
|
|
1807
|
+
timezoneOffset?: number;
|
|
1808
|
+
colorDepth?: number;
|
|
1809
|
+
touchSupport?: boolean;
|
|
1810
|
+
country?: string;
|
|
1811
|
+
userId?: string;
|
|
1812
|
+
utmSource?: string;
|
|
1813
|
+
utmCampaign?: string;
|
|
1814
|
+
}
|
|
1815
|
+
export declare function postAnalytics(events: AnalyticsEvent[]): Promise<void>;
|
|
1816
|
+
export declare function postEngagement(params: BuildEngagementParams): void;
|
|
1817
|
+
export declare function postInteraction(params: BuildInteractionParams): void;
|
|
1818
|
+
export declare function postError(params: BuildErrorParams): void;
|
|
1819
|
+
/**
|
|
1820
|
+
* Resolves once the events outstanding at call time have been sent, rather than
|
|
1821
|
+
* once a flush has merely been started. Two paths used to return early: a call
|
|
1822
|
+
* during an active flush reported a drain that had not happened, and a call
|
|
1823
|
+
* while the only outstanding batch was already in flight (so the queue looked
|
|
1824
|
+
* empty) returned before that request finished. Delegating to flushQueue makes
|
|
1825
|
+
* it wait for whichever drain covers the outstanding work — starting one now,
|
|
1826
|
+
* bypassing the timer, when nothing is running yet.
|
|
1827
|
+
*/
|
|
1828
|
+
export declare function flushAnalytics(): Promise<void>;
|
|
1829
|
+
interface VideoHeatmap {
|
|
1830
|
+
video_id: string;
|
|
1831
|
+
total_duration_ms: number;
|
|
1832
|
+
data: number[];
|
|
1833
|
+
}
|
|
1834
|
+
interface GetVideoHeatmapOptions {
|
|
1835
|
+
videoId: string;
|
|
1836
|
+
buckets?: number;
|
|
1837
|
+
}
|
|
1838
|
+
export declare function getVideoHeatmap({ videoId, buckets }: GetVideoHeatmapOptions): Promise<VideoHeatmap | null>;
|
|
1839
|
+
/**
|
|
1840
|
+
* Hot segment data for a video
|
|
1841
|
+
* Represents a segment with higher-than-average viewership
|
|
1842
|
+
*/
|
|
1843
|
+
interface HotSegment {
|
|
1844
|
+
start_second: number;
|
|
1845
|
+
end_second: number;
|
|
1846
|
+
multiplier: number;
|
|
1847
|
+
}
|
|
1848
|
+
interface GetHotSegmentsOptions {
|
|
1849
|
+
videoId: string;
|
|
1850
|
+
startDate?: string;
|
|
1851
|
+
endDate?: string;
|
|
1852
|
+
}
|
|
1853
|
+
export declare function getHotSegments({ videoId, startDate, endDate }: GetHotSegmentsOptions): Promise<HotSegment[]>;
|
|
1854
|
+
interface VideoStats {
|
|
1855
|
+
droppedFrames: number;
|
|
1856
|
+
volume: number;
|
|
1857
|
+
networkSpeed: number;
|
|
1858
|
+
bufferHealth: number;
|
|
1859
|
+
currentRes: {
|
|
1860
|
+
width: number;
|
|
1861
|
+
height: number;
|
|
1862
|
+
} | null;
|
|
1863
|
+
optimalRes: {
|
|
1864
|
+
width: number;
|
|
1865
|
+
height: number;
|
|
1866
|
+
} | null;
|
|
1867
|
+
}
|
|
1868
|
+
interface CollectStatsInput {
|
|
1869
|
+
video: HTMLVideoElement | null;
|
|
1870
|
+
hlsBandwidthEstimate: number | null;
|
|
1871
|
+
hlsLevels: ReadonlyArray<{
|
|
1872
|
+
width: number;
|
|
1873
|
+
height: number;
|
|
1874
|
+
}>;
|
|
1875
|
+
hlsCurrentLevel: number;
|
|
1876
|
+
hlsAutoLevelEnabled: boolean;
|
|
1877
|
+
hlsAutoLevelCapping: number;
|
|
1878
|
+
}
|
|
1879
|
+
export declare function getDroppedFrames(video: HTMLVideoElement): number;
|
|
1880
|
+
export declare function getBufferHealth(video: HTMLVideoElement): number;
|
|
1881
|
+
export declare function collectVideoStats(input: CollectStatsInput): VideoStats;
|
|
1882
|
+
export declare const LOOP_REBASE_BASE_MULTIPLIER = 2;
|
|
1883
|
+
export declare const LOOP_REBASE_THRESHOLD_MULTIPLIER = 50;
|
|
1884
|
+
export declare function shiftArray(arr: number[], shiftBy: number): number[];
|
|
1885
|
+
export declare function calcShiftCountArray(index: number, arraySize: number): number[];
|
|
1886
|
+
export declare function buildTransforms(currentIndex: number, originalPositions: number[]): number[];
|
|
1887
|
+
export declare function computeScrollIndex(scrollTop: number, containerHeight: number, gap?: number): number;
|
|
1888
|
+
export declare function isAtEndOfFeed(currentIndex: number, postsLength: number): boolean;
|
|
1889
|
+
export declare function isFeedExhausted(postsLength: number, total: number): boolean;
|
|
1890
|
+
interface ShouldFetchMoreInput {
|
|
1891
|
+
currentIndex: number;
|
|
1892
|
+
postsLength: number;
|
|
1893
|
+
total: number;
|
|
1894
|
+
isFetching: boolean;
|
|
1895
|
+
isLoaded: boolean;
|
|
1896
|
+
}
|
|
1897
|
+
export declare function shouldFetchMore({ currentIndex, postsLength, total, isFetching, isLoaded }: ShouldFetchMoreInput): boolean;
|
|
1898
|
+
export declare function resolvePostIndex(transform: number, postsLength: number, loop: boolean): number;
|
|
1899
|
+
export declare function getLoopRebaseThreshold(postsLength: number): number;
|
|
1900
|
+
interface LoopRebaseInput {
|
|
1901
|
+
currentIndex: number;
|
|
1902
|
+
postsLength: number;
|
|
1903
|
+
loop: boolean;
|
|
1904
|
+
total: number;
|
|
1905
|
+
}
|
|
1906
|
+
export declare function shouldRebaseLoopIndex({ currentIndex, postsLength, loop, total }: LoopRebaseInput): boolean;
|
|
1907
|
+
export declare function getRebasedLoopIndex(currentIndex: number, postsLength: number): number;
|
|
1908
|
+
export declare const IMAGE_DWELL_MS = 5e3;
|
|
1909
|
+
export declare function shouldDwellAdvance(autoscroll: boolean, isCurrent: boolean, isImage: boolean): boolean;
|
|
1910
|
+
export declare function gateVideoEnded(isCurrent: boolean, autoscroll: boolean): boolean;
|
|
1911
|
+
interface AutoAdvanceInput {
|
|
1912
|
+
isCurrent: boolean;
|
|
1913
|
+
autoscroll: boolean;
|
|
1914
|
+
loop: boolean;
|
|
1915
|
+
currentIndex: number;
|
|
1916
|
+
postsLength: number;
|
|
1917
|
+
total: number;
|
|
1918
|
+
}
|
|
1919
|
+
export declare function shouldAutoAdvance({ isCurrent, autoscroll, loop, currentIndex, postsLength, total }: AutoAdvanceInput): boolean;
|
|
1920
|
+
export declare function getViewportFallbackHeight(safeAreaTabBar: number): number;
|
|
1921
|
+
interface ContainerHeightInput {
|
|
1922
|
+
measuredHeight: number;
|
|
1923
|
+
scrollContainerHeight: number;
|
|
1924
|
+
safeAreaTabBar: number;
|
|
1925
|
+
}
|
|
1926
|
+
export declare function resolveContainerHeight({ measuredHeight, scrollContainerHeight, safeAreaTabBar }: ContainerHeightInput): number;
|
|
1927
|
+
export declare function checkIsCut(el: HTMLElement | null): boolean;
|
|
1928
|
+
export declare function connect(config: ConnectConfig): Promise<string | undefined>;
|
|
1929
|
+
interface CreateImageUploadOptions {
|
|
1930
|
+
projectId: string;
|
|
1931
|
+
orgId?: string;
|
|
1932
|
+
name: string;
|
|
1933
|
+
}
|
|
1934
|
+
interface ImageUploadResult {
|
|
1935
|
+
imageId: string;
|
|
1936
|
+
uploadUrl: string;
|
|
1937
|
+
}
|
|
1938
|
+
export declare function createImageUpload(options: CreateImageUploadOptions): Promise<ImageUploadResult>;
|
|
1939
|
+
export declare function uploadImage(file: Blob | Uint8Array, options: CreateImageUploadOptions): Promise<ImageUploadResult>;
|
|
1940
|
+
interface CreateVideoUploadOptions {
|
|
1941
|
+
projectId: string;
|
|
1942
|
+
orgId?: string;
|
|
1943
|
+
name: string;
|
|
1944
|
+
videoQuality?: string;
|
|
1945
|
+
}
|
|
1946
|
+
interface VideoUploadResult {
|
|
1947
|
+
videoId: string;
|
|
1948
|
+
posterId: string;
|
|
1949
|
+
videoUploadUrl: string;
|
|
1950
|
+
posterUploadUrl: string;
|
|
1951
|
+
}
|
|
1952
|
+
export declare function createVideoUpload(options: CreateVideoUploadOptions): Promise<VideoUploadResult>;
|
|
1953
|
+
export declare function uploadVideo(videoFile: Blob | Uint8Array, posterFile: Blob | Uint8Array, options: CreateVideoUploadOptions): Promise<VideoUploadResult>;
|
|
1954
|
+
interface AudioTrackUploadItem {
|
|
1955
|
+
languageCode: string;
|
|
1956
|
+
label?: string;
|
|
1957
|
+
fileName: string;
|
|
1958
|
+
format: string;
|
|
1959
|
+
size?: number;
|
|
1960
|
+
}
|
|
1961
|
+
interface AudioTrackUploadTarget {
|
|
1962
|
+
id: string;
|
|
1963
|
+
languageCode?: string;
|
|
1964
|
+
uploadUrl?: string;
|
|
1965
|
+
}
|
|
1966
|
+
interface AudioTrackUploadResult {
|
|
1967
|
+
targets: AudioTrackUploadTarget[];
|
|
1968
|
+
}
|
|
1969
|
+
export declare function createAudioTrackUpload(projectId: string, videoId: string, items: AudioTrackUploadItem[]): Promise<AudioTrackUploadResult>;
|
|
1970
|
+
interface SubtitleUploadItem {
|
|
1971
|
+
languageCode: string;
|
|
1972
|
+
label?: string;
|
|
1973
|
+
fileName: string;
|
|
1974
|
+
format: string;
|
|
1975
|
+
size?: number;
|
|
1976
|
+
}
|
|
1977
|
+
interface SubtitleUploadTarget {
|
|
1978
|
+
id: string;
|
|
1979
|
+
languageCode?: string;
|
|
1980
|
+
uploadUrl?: string;
|
|
1981
|
+
}
|
|
1982
|
+
interface SubtitleUploadResult {
|
|
1983
|
+
targets: SubtitleUploadTarget[];
|
|
1984
|
+
}
|
|
1985
|
+
export declare function createSubtitleUpload(projectId: string, videoId: string, items: SubtitleUploadItem[]): Promise<SubtitleUploadResult>;
|
|
1986
|
+
interface CreatePostUploadOptions {
|
|
1987
|
+
projectId: string;
|
|
1988
|
+
feedId: string;
|
|
1989
|
+
orgId?: string;
|
|
1990
|
+
creatorId: string;
|
|
1991
|
+
description?: string;
|
|
1992
|
+
contentType: string;
|
|
1993
|
+
fileName: string;
|
|
1994
|
+
videoQuality?: string;
|
|
1995
|
+
}
|
|
1996
|
+
interface PostUploadResult {
|
|
1997
|
+
postId: string;
|
|
1998
|
+
contentId: string;
|
|
1999
|
+
posterId: string;
|
|
2000
|
+
contentUploadUrl: string;
|
|
2001
|
+
posterUploadUrl: string;
|
|
2002
|
+
}
|
|
2003
|
+
export declare function uploadPost(contentFile: Blob | Uint8Array, posterFile: Blob | Uint8Array, options: CreatePostUploadOptions): Promise<PostUploadResult>;
|
|
2004
|
+
export declare function createPostUpload(options: CreatePostUploadOptions): Promise<PostUploadResult>;
|
|
2005
|
+
export type { AnalyticsContext, AnalyticsEvent, AppliedVolumeSync, AudioTrack, AudioTrackUploadItem, AudioTrackUploadResult, AuthConfig, AutoAdvanceInput, BufferRange, BuildEngagementParams, BuildErrorParams, BuildEventParams, BuildInteractionParams, BuildSessionStartParams, BuildVideoAnalyticsPayloadParams, CSSProperties, CalculateDragPositionOptions, CaptionLangType, CaptionTextSettings, CaptionWindowSettings, Chapter, ChapterWithEnd, ClickDisambiguationState, ClickOutsideOptions, CollectStatsInput, ConnectConfig, ContainerHeightInput, ContentType, ContentViewEvent, ControlsState, CreateImageUploadOptions, CreatePostUploadOptions, CreateVideoUploadOptions, DeriveVolumeSyncOptions, EngagementEvent, ErrorEvent, FeedPlaybackAction, FeedPlaybackDecisionInput, FeedPost, FeedPostsResponse, FileData, FileStatus, FontFamilyKey, GetHotSegmentsOptions, GetPointerOffsetsOptions, GetVideoHeatmapOptions, HandleClickOptions, HandleDoubleClickOptions, HlsController, HlsErrorAction, HlsErrorInput, HlsErrorPayload, HlsErrorRecoveryState, HlsInstance, HotSegment, ImageContainerStyleParams, ImageData, ImageRenderAction, ImageRenderState, ImageUploadResult, InteractionEvent, LoadingDebounceState, LoopRebaseInput, MenuKey, Page, PipReentryOptions, PlaybackHandlerCallbacks, PlaybackPointerOptions, PlayerControlLabels, PlayerLanguage, PlayerMenuLabels, PlayerState, PlayerStateStore, PointerOffsets, Position, PostStoreState, PostUploadResult, PosterDimensions, ProgressBarControls, ProgressBarProps, ProgressBarStartEvent, QualityLevel, Segment, SegmentAnalyticsParams, SessionStartEvent, SettingsState, SetupHlsOptions, ShortcutAction as ShortcutActionType, ShortcutDefinition, ShouldFetchMoreInput, SmartCaptionPositionParams, SubtitleTrack, SubtitleUploadItem, SubtitleUploadResult, UUID, UpdateLoadingStateOptions, VideoAnalyticsPayload, VideoData, VideoHeatmap, VideoKeyboardHandlers, VideoPreloadAttribute, VideoSdkKey, VideoSnapshot, VideoStats, VideoTheme, VideoUploadResult, ViewType, Viewport };
|