@rixl/media-core 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +74 -84
  2. package/dist/chunks/B-QvSqjj.js +1 -0
  3. package/dist/chunks/B2DDE32u.js +1 -0
  4. package/dist/chunks/DMkwQjgA.js +1 -0
  5. package/dist/index.d.ts +1906 -82
  6. package/dist/index.js +6 -73
  7. package/package.json +13 -6
  8. package/dist/api/analytics.d.ts +0 -16
  9. package/dist/api/analytics.js +0 -227
  10. package/dist/api/feed.js +0 -52
  11. package/dist/api/heatmap.d.ts +0 -6
  12. package/dist/api/heatmap.js +0 -28
  13. package/dist/api/hot-segments.d.ts +0 -7
  14. package/dist/api/hot-segments.js +0 -20
  15. package/dist/api/image.js +0 -11
  16. package/dist/api/rixl-client.d.ts +0 -3
  17. package/dist/api/rixl-client.js +0 -14
  18. package/dist/api/upload.d.ts +0 -76
  19. package/dist/api/upload.js +0 -150
  20. package/dist/api/video.js +0 -11
  21. package/dist/lib/analytics-builders.d.ts +0 -59
  22. package/dist/lib/analytics-builders.js +0 -88
  23. package/dist/lib/analytics-initialize.d.ts +0 -1
  24. package/dist/lib/analytics-initialize.js +0 -59
  25. package/dist/lib/analytics-utils.d.ts +0 -43
  26. package/dist/lib/analytics-utils.js +0 -85
  27. package/dist/lib/analytics-wire.js +0 -153
  28. package/dist/lib/buffer-progress.d.ts +0 -20
  29. package/dist/lib/buffer-progress.js +0 -37
  30. package/dist/lib/caption-styles.d.ts +0 -20
  31. package/dist/lib/caption-styles.js +0 -56
  32. package/dist/lib/caption-utils.d.ts +0 -80
  33. package/dist/lib/caption-utils.js +0 -111
  34. package/dist/lib/chapter-utils.d.ts +0 -102
  35. package/dist/lib/chapter-utils.js +0 -147
  36. package/dist/lib/click-disambiguation.d.ts +0 -44
  37. package/dist/lib/click-disambiguation.js +0 -80
  38. package/dist/lib/click-outside.d.ts +0 -68
  39. package/dist/lib/click-outside.js +0 -64
  40. package/dist/lib/colors.d.ts +0 -6
  41. package/dist/lib/colors.js +0 -16
  42. package/dist/lib/content-analytics.d.ts +0 -27
  43. package/dist/lib/content-analytics.js +0 -38
  44. package/dist/lib/environment-effects.d.ts +0 -89
  45. package/dist/lib/environment-effects.js +0 -139
  46. package/dist/lib/feed-autoscroll.d.ts +0 -10
  47. package/dist/lib/feed-autoscroll.js +0 -13
  48. package/dist/lib/feed-container-height.d.ts +0 -7
  49. package/dist/lib/feed-container-height.js +0 -11
  50. package/dist/lib/feed-description-overflow.d.ts +0 -1
  51. package/dist/lib/feed-description-overflow.js +0 -9
  52. package/dist/lib/feed-dwell.d.ts +0 -2
  53. package/dist/lib/feed-dwell.js +0 -8
  54. package/dist/lib/feed-loop.d.ts +0 -10
  55. package/dist/lib/feed-loop.js +0 -21
  56. package/dist/lib/feed-scroll.d.ts +0 -11
  57. package/dist/lib/feed-scroll.js +0 -19
  58. package/dist/lib/feed-transforms.d.ts +0 -5
  59. package/dist/lib/feed-transforms.js +0 -30
  60. package/dist/lib/filter-undefined.js +0 -11
  61. package/dist/lib/fullscreen-utils.d.ts +0 -1
  62. package/dist/lib/fullscreen-utils.js +0 -7
  63. package/dist/lib/hls-buffer-flush.js +0 -46
  64. package/dist/lib/hls-error-recovery.d.ts +0 -35
  65. package/dist/lib/hls-error-recovery.js +0 -75
  66. package/dist/lib/hls-event-handlers.js +0 -61
  67. package/dist/lib/hls-loader.js +0 -23
  68. package/dist/lib/hls-setup.d.ts +0 -72
  69. package/dist/lib/hls-setup.js +0 -144
  70. package/dist/lib/hls-utils.d.ts +0 -31
  71. package/dist/lib/hls-utils.js +0 -34
  72. package/dist/lib/image-render.d.ts +0 -29
  73. package/dist/lib/image-render.js +0 -63
  74. package/dist/lib/keyboard-config.d.ts +0 -66
  75. package/dist/lib/keyboard-config.js +0 -127
  76. package/dist/lib/keyboard-utils.d.ts +0 -4
  77. package/dist/lib/keyboard-utils.js +0 -11
  78. package/dist/lib/lifecycle-effects.d.ts +0 -67
  79. package/dist/lib/lifecycle-effects.js +0 -78
  80. package/dist/lib/loading-debounce.d.ts +0 -26
  81. package/dist/lib/loading-debounce.js +0 -38
  82. package/dist/lib/numbers.d.ts +0 -6
  83. package/dist/lib/numbers.js +0 -36
  84. package/dist/lib/pip-utils.d.ts +0 -16
  85. package/dist/lib/pip-utils.js +0 -32
  86. package/dist/lib/playback-handlers.d.ts +0 -33
  87. package/dist/lib/playback-handlers.js +0 -51
  88. package/dist/lib/playback-slider.d.ts +0 -23
  89. package/dist/lib/playback-slider.js +0 -24
  90. package/dist/lib/player-labels.d.ts +0 -23
  91. package/dist/lib/player-labels.js +0 -132
  92. package/dist/lib/player-menu-labels.d.ts +0 -80
  93. package/dist/lib/player-menu-labels.js +0 -829
  94. package/dist/lib/player-ui-actions.d.ts +0 -10
  95. package/dist/lib/player-ui-actions.js +0 -58
  96. package/dist/lib/progress-fraction.d.ts +0 -11
  97. package/dist/lib/progress-fraction.js +0 -20
  98. package/dist/lib/progress-persist.d.ts +0 -28
  99. package/dist/lib/progress-persist.js +0 -54
  100. package/dist/lib/segment-analytics.d.ts +0 -20
  101. package/dist/lib/segment-analytics.js +0 -29
  102. package/dist/lib/subtitle-logic.d.ts +0 -31
  103. package/dist/lib/subtitle-logic.js +0 -75
  104. package/dist/lib/subtitle-utils.d.ts +0 -16
  105. package/dist/lib/subtitle-utils.js +0 -37
  106. package/dist/lib/time.d.ts +0 -1
  107. package/dist/lib/time.js +0 -17
  108. package/dist/lib/track-persistence.d.ts +0 -4
  109. package/dist/lib/track-persistence.js +0 -35
  110. package/dist/lib/video-preload-config.d.ts +0 -34
  111. package/dist/lib/video-preload-config.js +0 -41
  112. package/dist/lib/video-spinner.d.ts +0 -25
  113. package/dist/lib/video-spinner.js +0 -35
  114. package/dist/lib/video-stats.d.ts +0 -28
  115. package/dist/lib/video-stats.js +0 -44
  116. package/dist/lib/volume-utils.d.ts +0 -59
  117. package/dist/lib/volume-utils.js +0 -55
  118. package/dist/progressbar.d.ts +0 -10
  119. package/dist/progressbar.js +0 -125
  120. package/dist/store/analytics-context-store.d.ts +0 -4
  121. package/dist/store/analytics-context-store.js +0 -13
  122. package/dist/store/analytics-device-store.d.ts +0 -8
  123. package/dist/store/analytics-device-store.js +0 -44
  124. package/dist/store/analytics-session-store.d.ts +0 -8
  125. package/dist/store/analytics-session-store.js +0 -40
  126. package/dist/store/analytics-store.d.ts +0 -30
  127. package/dist/store/analytics-store.js +0 -88
  128. package/dist/store/controls-store.d.ts +0 -16
  129. package/dist/store/controls-store.js +0 -53
  130. package/dist/store/feed-cache-store.d.ts +0 -24
  131. package/dist/store/feed-cache-store.js +0 -165
  132. package/dist/store/feed-interaction-store.d.ts +0 -17
  133. package/dist/store/feed-interaction-store.js +0 -32
  134. package/dist/store/feed-store.d.ts +0 -16
  135. package/dist/store/feed-store.js +0 -38
  136. package/dist/store/generic-store.js +0 -24
  137. package/dist/store/image-store.d.ts +0 -2
  138. package/dist/store/image-store.js +0 -8
  139. package/dist/store/player-container-element-store.d.ts +0 -10
  140. package/dist/store/player-container-element-store.js +0 -38
  141. package/dist/store/player-global-actions.js +0 -53
  142. package/dist/store/player-media-element-store.d.ts +0 -10
  143. package/dist/store/player-media-element-store.js +0 -38
  144. package/dist/store/player-store-actions.js +0 -197
  145. package/dist/store/player-store.d.ts +0 -94
  146. package/dist/store/player-store.js +0 -127
  147. package/dist/store/player-track-actions.js +0 -44
  148. package/dist/store/postStore.d.ts +0 -20
  149. package/dist/store/postStore.js +0 -112
  150. package/dist/store/settings/constants.d.ts +0 -15
  151. package/dist/store/settings/constants.js +0 -47
  152. package/dist/store/settings/defaults.d.ts +0 -2
  153. package/dist/store/settings/defaults.js +0 -24
  154. package/dist/store/settings/storage.d.ts +0 -3
  155. package/dist/store/settings/storage.js +0 -28
  156. package/dist/store/settings/store.d.ts +0 -32
  157. package/dist/store/settings/store.js +0 -59
  158. package/dist/store/settings/types.d.ts +0 -27
  159. package/dist/store/settings/validation.d.ts +0 -7
  160. package/dist/store/settings/validation.js +0 -55
  161. package/dist/store/settings-menu-store.d.ts +0 -19
  162. package/dist/store/settings-menu-store.js +0 -30
  163. package/dist/store/video-store.d.ts +0 -2
  164. package/dist/store/video-store.js +0 -8
  165. package/dist/types/analytics.d.ts +0 -113
  166. package/dist/types/feed.d.ts +0 -21
  167. package/dist/types/file.d.ts +0 -12
  168. package/dist/types/heatmap.d.ts +0 -5
  169. package/dist/types/hot-segments.d.ts +0 -9
  170. package/dist/types/image.d.ts +0 -9
  171. package/dist/types/uuid.d.ts +0 -1
  172. package/dist/types/video.d.ts +0 -22
@@ -1,144 +0,0 @@
1
- import { isHlsStream } from "./hls-utils.js";
2
- import { playerStateStore } from "../store/player-store.js";
3
- import { MAX_MEDIA_RECOVERIES, MAX_NETWORK_RETRIES, createHlsErrorRecoveryState, decideHlsErrorAction } from "./hls-error-recovery.js";
4
- import { loadHls } from "./hls-loader.js";
5
- import { setupHlsEventHandlers } from "./hls-event-handlers.js";
6
- import { createSeekingHandler } from "./hls-buffer-flush.js";
7
-
8
- // src/lib/hls-setup.ts
9
- function createHlsConfig(isPreloading) {
10
- return {
11
- debug: false,
12
- enableWorker: true,
13
- lowLatencyMode: false,
14
- backBufferLength: 10,
15
- maxBufferLength: isPreloading ? 10 : 30,
16
- maxMaxBufferLength: isPreloading ? 20 : 60,
17
- maxBufferSize: 6e7,
18
- maxBufferHole: .3,
19
- manifestLoadingTimeOut: 1e4,
20
- fragLoadingTimeOut: 2e4,
21
- startFragPrefetch: true,
22
- highBufferWatchdogPeriod: 2,
23
- nudgeOffset: .2,
24
- nudgeMaxRetry: 5,
25
- testBandwidth: true,
26
- progressive: false,
27
- abrEwmaDefaultEstimate: 5e6,
28
- abrMaxWithRealBitrate: true
29
- };
30
- }
31
- function tryAttachNativeSource(HlsClass, videoElement, videoUrl) {
32
- if (!HlsClass.isSupported() && videoElement.canPlayType("application/vnd.apple.mpegurl")) {
33
- videoElement.src = videoUrl;
34
- return true;
35
- }
36
- return false;
37
- }
38
- function bindErrorHandler(hlsInstance, events, onError) {
39
- const recoveryState = createHlsErrorRecoveryState();
40
- hlsInstance.on(events.ERROR, (_event, data) => {
41
- const action = decideHlsErrorAction(data, recoveryState);
42
- if (!action) return;
43
- switch (action.kind) {
44
- case "retry-network":
45
- hlsInstance.startLoad();
46
- return;
47
- case "recover-media":
48
- hlsInstance.recoverMediaError();
49
- return;
50
- case "give-up":
51
- onError?.(action.payload);
52
- return;
53
- }
54
- });
55
- }
56
- function buildController(opts) {
57
- const { hlsInstance, videoElement, playerId, handleSeeking, state } = opts;
58
- return {
59
- hls: hlsInstance,
60
- get levels() {
61
- return state.levels;
62
- },
63
- get currentLevel() {
64
- return state.currentLevel;
65
- },
66
- get isAuto() {
67
- return state.isAuto;
68
- },
69
- setQuality: (index) => {
70
- if (index === -1) {
71
- hlsInstance.loadLevel = -1;
72
- state.isAuto = true;
73
- } else {
74
- hlsInstance.loadLevel = index;
75
- state.isAuto = false;
76
- }
77
- },
78
- destroy: () => {
79
- videoElement.removeEventListener("seeking", handleSeeking);
80
- hlsInstance.destroy();
81
- playerStateStore.setHlsInstance(playerId, null);
82
- }
83
- };
84
- }
85
- /**
86
- * Set up HLS streaming on a video element, resolving to a controller for
87
- * quality/audio switching and teardown.
88
- *
89
- * - For non-HLS URLs: sets `videoElement.src` directly and resolves to `null`
90
- * without loading hls.js.
91
- * - For HLS URLs on Safari (no `Hls.isSupported()` but native canPlayType):
92
- * sets `videoElement.src` and returns `null` (UI features that depend on
93
- * the HLS controller — quality switching, audio tracks — won't be available
94
- * in native mode).
95
- * - Otherwise: instantiates HLS.js, attaches the media, wires events
96
- * (including error recovery), and returns the controller.
97
- */
98
- async function setupHls(opts) {
99
- const { videoElement, videoUrl, playerId, isPreloading, onError, onLevelsChange, onLevelSwitch } = opts;
100
- if (!isHlsStream(videoUrl)) {
101
- videoElement.src = videoUrl;
102
- return null;
103
- }
104
- const HlsClass = await loadHls();
105
- if (tryAttachNativeSource(HlsClass, videoElement, videoUrl)) return null;
106
- const hlsInstance = new HlsClass(createHlsConfig(isPreloading));
107
- const state = {
108
- levels: [],
109
- currentLevel: -1,
110
- isAuto: true
111
- };
112
- const handleSeeking = createSeekingHandler({
113
- hlsInstance,
114
- events: HlsClass.Events,
115
- videoElement
116
- });
117
- videoElement.addEventListener("seeking", handleSeeking);
118
- playerStateStore.setHlsInstance(playerId, hlsInstance);
119
- hlsInstance.attachMedia(videoElement);
120
- setupHlsEventHandlers({
121
- hlsInstance,
122
- events: HlsClass.Events,
123
- playerId,
124
- videoUrl,
125
- onLevelsChange: (levels) => {
126
- state.levels = levels;
127
- onLevelsChange?.(levels);
128
- },
129
- onLevelSwitch: (level) => {
130
- state.currentLevel = level;
131
- onLevelSwitch?.(level);
132
- }
133
- });
134
- bindErrorHandler(hlsInstance, HlsClass.Events, onError);
135
- return buildController({
136
- hlsInstance,
137
- videoElement,
138
- playerId,
139
- handleSeeking,
140
- state
141
- });
142
- }
143
-
144
- export { MAX_MEDIA_RECOVERIES, MAX_NETWORK_RETRIES, createHlsConfig, createHlsErrorRecoveryState, decideHlsErrorAction, setupHls };
@@ -1,31 +0,0 @@
1
- import { MediaPlaylist } from "hls.js";
2
- export interface QualityLevel {
3
- height: number;
4
- width: number;
5
- bitrate: number;
6
- name: string;
7
- index: number;
8
- }
9
- export interface AudioTrack {
10
- index: number;
11
- label: string;
12
- language: string;
13
- kind: string;
14
- /** The raw Hls.js MediaPlaylist object for this audio track. */
15
- track: MediaPlaylist;
16
- }
17
- export declare const sortLevelsByQuality: (levels: QualityLevel[]) => QualityLevel[];
18
- export declare const isHlsStream: (url: string) => boolean;
19
- export declare const generateLevelName: (level: {
20
- height: number;
21
- width: number;
22
- bitrate: number;
23
- }) => string;
24
- interface QualityLabelParams {
25
- resolution: number;
26
- levels: QualityLevel[];
27
- currentLevel: number;
28
- autoText: string;
29
- }
30
- export declare const getQualityLabel: ({ resolution, levels, currentLevel, autoText }: QualityLabelParams) => string;
31
- export {};
@@ -1,34 +0,0 @@
1
- // src/lib/hls-utils.ts
2
- const sortLevelsByQuality = (levels) => {
3
- return [...levels].sort((a, b) => {
4
- if (a.height > 0 && b.height > 0) return b.height - a.height;
5
- else if (a.height > 0) return -1;
6
- else if (b.height > 0) return 1;
7
- else return b.bitrate - a.bitrate;
8
- });
9
- };
10
- const isHlsStream = (url) => {
11
- return url.includes(".m3u8") || url.includes("application/vnd.apple.mpegurl") || url.includes("manifest(format=m3u8") || url.toLowerCase().includes("m3u8");
12
- };
13
- const generateLevelName = (level) => {
14
- if (level.height > 0 && level.width > 0) return `${Math.min(level.height, level.width)}p`;
15
- else if (level.height > 0) return `${level.height}p`;
16
- else if (level.width > 0) return `${level.width}p`;
17
- else {
18
- const bitrateKbps = Math.round(level.bitrate / 1e3);
19
- if (bitrateKbps >= 3e3) return "1080p";
20
- else if (bitrateKbps >= 2e3) return "720p";
21
- else if (bitrateKbps >= 1e3) return "480p";
22
- else if (bitrateKbps >= 500) return "360p";
23
- else if (bitrateKbps >= 250) return "240p";
24
- else return "144p";
25
- }
26
- };
27
- const getQualityLabel = ({ resolution, levels, currentLevel, autoText }) => {
28
- const isAutoSelected = resolution === -1;
29
- if (isAutoSelected && levels.length > 0 && currentLevel >= 0 && levels[currentLevel]) return `${autoText} (${levels[currentLevel].name})`;
30
- else if (!isAutoSelected && resolution >= 0 && levels[resolution]) return levels[resolution].name;
31
- return autoText;
32
- };
33
-
34
- export { generateLevelName, getQualityLabel, isHlsStream, sortLevelsByQuality };
@@ -1,29 +0,0 @@
1
- export type CSSProperties = Record<string, string | number | null | undefined>;
2
- export interface ImageRenderState {
3
- isLoading: boolean;
4
- isVisible: boolean;
5
- aspectRatio: number;
6
- }
7
- export type ImageRenderAction = {
8
- type: "reset";
9
- aspectRatio?: number;
10
- } | {
11
- type: "loaded";
12
- } | {
13
- type: "visible";
14
- } | {
15
- type: "set_aspect_ratio";
16
- aspectRatio: number;
17
- };
18
- export declare const DEFAULT_IMAGE_ASPECT_RATIO = .5;
19
- export declare const IMAGE_ASPECT_RATIO_THRESHOLD = .7;
20
- export declare const initialImageRenderState: ImageRenderState;
21
- export interface ImageContainerStyleParams {
22
- aspectRatio: number;
23
- isVisible: boolean;
24
- thumbhashUrl?: string;
25
- }
26
- export declare function imageRenderReducer(state: ImageRenderState, action: ImageRenderAction): ImageRenderState;
27
- export declare function getImageAspectRatio(width?: number, height?: number): number | undefined;
28
- export declare function getImageContainerStyle({ aspectRatio, isVisible, thumbhashUrl }: ImageContainerStyleParams): CSSProperties;
29
- export declare function getImageStyle(aspectRatio: number, isLoading: boolean): CSSProperties;
@@ -1,63 +0,0 @@
1
- // src/lib/image-render.ts
2
- const DEFAULT_IMAGE_ASPECT_RATIO = .5;
3
- const IMAGE_ASPECT_RATIO_THRESHOLD = .7;
4
- const initialImageRenderState = {
5
- isLoading: true,
6
- isVisible: false,
7
- aspectRatio: DEFAULT_IMAGE_ASPECT_RATIO
8
- };
9
- function imageRenderReducer(state, action) {
10
- switch (action.type) {
11
- case "reset": return {
12
- isLoading: true,
13
- isVisible: false,
14
- aspectRatio: action.aspectRatio ?? state.aspectRatio
15
- };
16
- case "loaded": return {
17
- ...state,
18
- isLoading: false
19
- };
20
- case "visible": return {
21
- ...state,
22
- isVisible: true
23
- };
24
- case "set_aspect_ratio": return {
25
- ...state,
26
- aspectRatio: action.aspectRatio
27
- };
28
- default: return state;
29
- }
30
- }
31
- function getImageAspectRatio(width, height) {
32
- if (!width || !height) return void 0;
33
- return width / height;
34
- }
35
- function getImageContainerStyle({ aspectRatio, isVisible, thumbhashUrl }) {
36
- const style = {
37
- display: "block",
38
- width: "100%",
39
- aspectRatio,
40
- position: "relative"
41
- };
42
- if (!isVisible) {
43
- style.backgroundImage = `url(${thumbhashUrl})`;
44
- style.backgroundSize = aspectRatio >= .7 ? "contain" : "cover";
45
- style.backgroundPosition = "center";
46
- style.backgroundRepeat = "no-repeat";
47
- }
48
- return style;
49
- }
50
- function getImageStyle(aspectRatio, isLoading) {
51
- return {
52
- position: "absolute",
53
- top: 0,
54
- left: 0,
55
- width: "100%",
56
- height: "100%",
57
- objectFit: aspectRatio >= .7 ? "contain" : "cover",
58
- opacity: isLoading ? 0 : 1,
59
- transition: "opacity 300ms ease"
60
- };
61
- }
62
-
63
- export { DEFAULT_IMAGE_ASPECT_RATIO, IMAGE_ASPECT_RATIO_THRESHOLD, getImageAspectRatio, getImageContainerStyle, getImageStyle, imageRenderReducer, initialImageRenderState };
@@ -1,66 +0,0 @@
1
- /**
2
- * Media SDK video keyboard shortcuts configuration
3
- */
4
- export 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";
5
- export declare const ShortcutAction: {
6
- readonly PLAY_PAUSE: "play_pause";
7
- readonly SEEK_10: "seek_10";
8
- readonly SEEK_5: "seek_5";
9
- readonly FULLSCREEN: "fullscreen";
10
- readonly MINIPLAYER: "miniplayer";
11
- readonly MUTE: "mute";
12
- readonly VOLUME: "volume";
13
- };
14
- export type ShortcutAction = (typeof ShortcutAction)[keyof typeof ShortcutAction];
15
- export interface ShortcutDefinition {
16
- action: ShortcutAction;
17
- keys: VideoSdkKey[];
18
- labelKey: string;
19
- }
20
- /**
21
- * Default keyboard shortcuts configuration
22
- */
23
- export declare const SHORTCUTS_CONFIG: ShortcutDefinition[];
24
- /**
25
- * Seek amounts in seconds
26
- */
27
- export declare const SEEK_SMALL_SECONDS = 5;
28
- export declare const SEEK_LARGE_SECONDS = 10;
29
- /**
30
- * Volume change delta (0-1 range)
31
- */
32
- export declare const VOLUME_DELTA = .1;
33
- export interface VideoKeyboardHandlers {
34
- onPlayPause: () => void;
35
- onSeek: (seconds: number) => void;
36
- onFullscreen: () => void;
37
- onMiniplayer: () => void;
38
- onMute: () => void;
39
- onVolume: (delta: number) => void;
40
- }
41
- /**
42
- * Get seek amount for a keyboard event
43
- * @returns number of seconds to seek (positive = forward, negative = backward)
44
- */
45
- export declare function getSeekAmount(keyCode: string, action: ShortcutAction): number;
46
- /**
47
- * Get volume delta for a keyboard event
48
- * @returns volume change amount (positive = increase, negative = decrease)
49
- */
50
- export declare function getVolumeDelta(keyCode: string): number;
51
- /**
52
- * Execute keyboard shortcut action
53
- */
54
- export declare function executeShortcutAction(action: ShortcutAction, keyCode: string, handlers: VideoKeyboardHandlers): void;
55
- /**
56
- * Full play/pause key set including media keys — mirrors React's
57
- * component-level PlayPauseShortcuts, which extends SHORTCUTS_CONFIG.
58
- */
59
- export declare const PLAY_PAUSE_KEYS: VideoSdkKey[];
60
- /** Resolve a KeyboardEvent.code to its shortcut action, or null when unmapped. */
61
- export declare function resolveShortcutAction(keyCode: string): ShortcutAction | null;
62
- /**
63
- * Digit0–Digit9 → 0–90 percent seek targets — mirrors React's
64
- * PercentageShortcuts table.
65
- */
66
- export declare function getSeekPercent(keyCode: string): number | null;
@@ -1,127 +0,0 @@
1
- // src/lib/keyboard-config.ts
2
- const ShortcutAction = {
3
- PLAY_PAUSE: "play_pause",
4
- SEEK_10: "seek_10",
5
- SEEK_5: "seek_5",
6
- FULLSCREEN: "fullscreen",
7
- MINIPLAYER: "miniplayer",
8
- MUTE: "mute",
9
- VOLUME: "volume"
10
- };
11
- /**
12
- * Default keyboard shortcuts configuration
13
- */
14
- const SHORTCUTS_CONFIG = [
15
- {
16
- action: ShortcutAction.PLAY_PAUSE,
17
- keys: ["Space", "KeyK"],
18
- labelKey: "shortcuts.play_pause"
19
- },
20
- {
21
- action: ShortcutAction.SEEK_10,
22
- keys: ["KeyJ", "KeyL"],
23
- labelKey: "shortcuts.seek_10s"
24
- },
25
- {
26
- action: ShortcutAction.SEEK_5,
27
- keys: ["ArrowLeft", "ArrowRight"],
28
- labelKey: "shortcuts.seek_5s"
29
- },
30
- {
31
- action: ShortcutAction.FULLSCREEN,
32
- keys: ["KeyF"],
33
- labelKey: "shortcuts.fullscreen"
34
- },
35
- {
36
- action: ShortcutAction.MINIPLAYER,
37
- keys: ["KeyI"],
38
- labelKey: "shortcuts.pip"
39
- },
40
- {
41
- action: ShortcutAction.MUTE,
42
- keys: ["KeyM"],
43
- labelKey: "shortcuts.mute"
44
- },
45
- {
46
- action: ShortcutAction.VOLUME,
47
- keys: ["ArrowUp", "ArrowDown"],
48
- labelKey: "shortcuts.volume"
49
- }
50
- ];
51
- /**
52
- * Seek amounts in seconds
53
- */
54
- const SEEK_SMALL_SECONDS = 5;
55
- const SEEK_LARGE_SECONDS = 10;
56
- /**
57
- * Volume change delta (0-1 range)
58
- */
59
- const VOLUME_DELTA = .1;
60
- /**
61
- * Get seek amount for a keyboard event
62
- * @returns number of seconds to seek (positive = forward, negative = backward)
63
- */
64
- function getSeekAmount(keyCode, action) {
65
- if (action === ShortcutAction.SEEK_10) return keyCode === "KeyL" ? 10 : -10;
66
- if (action === ShortcutAction.SEEK_5) return keyCode === "ArrowRight" ? 5 : -5;
67
- return 0;
68
- }
69
- /**
70
- * Get volume delta for a keyboard event
71
- * @returns volume change amount (positive = increase, negative = decrease)
72
- */
73
- function getVolumeDelta(keyCode) {
74
- return keyCode === "ArrowUp" ? VOLUME_DELTA : -.1;
75
- }
76
- /**
77
- * Execute keyboard shortcut action
78
- */
79
- function executeShortcutAction(action, keyCode, handlers) {
80
- switch (action) {
81
- case ShortcutAction.PLAY_PAUSE:
82
- handlers.onPlayPause();
83
- break;
84
- case ShortcutAction.SEEK_10:
85
- case ShortcutAction.SEEK_5:
86
- handlers.onSeek(getSeekAmount(keyCode, action));
87
- break;
88
- case ShortcutAction.FULLSCREEN:
89
- handlers.onFullscreen();
90
- break;
91
- case ShortcutAction.MINIPLAYER:
92
- handlers.onMiniplayer();
93
- break;
94
- case ShortcutAction.MUTE:
95
- handlers.onMute();
96
- break;
97
- case ShortcutAction.VOLUME: handlers.onVolume(getVolumeDelta(keyCode));
98
- }
99
- }
100
- /**
101
- * Full play/pause key set including media keys — mirrors React's
102
- * component-level PlayPauseShortcuts, which extends SHORTCUTS_CONFIG.
103
- */
104
- const PLAY_PAUSE_KEYS = [
105
- "Space",
106
- "KeyK",
107
- "F1",
108
- "F12",
109
- "MediaPlay",
110
- "MediaPause",
111
- "MediaPlayPause"
112
- ];
113
- const KEY_ACTION_MAP = new Map([...SHORTCUTS_CONFIG.flatMap((config) => config.keys.map((key) => [key, config.action])), ...PLAY_PAUSE_KEYS.map((key) => [key, ShortcutAction.PLAY_PAUSE])]);
114
- /** Resolve a KeyboardEvent.code to its shortcut action, or null when unmapped. */
115
- function resolveShortcutAction(keyCode) {
116
- return KEY_ACTION_MAP.get(keyCode) ?? null;
117
- }
118
- /**
119
- * Digit0–Digit9 → 0–90 percent seek targets — mirrors React's
120
- * PercentageShortcuts table.
121
- */
122
- function getSeekPercent(keyCode) {
123
- if (!/^Digit[0-9]$/.test(keyCode)) return null;
124
- return Number(keyCode.slice(5)) * 10;
125
- }
126
-
127
- export { PLAY_PAUSE_KEYS, SEEK_LARGE_SECONDS, SEEK_SMALL_SECONDS, SHORTCUTS_CONFIG, ShortcutAction, VOLUME_DELTA, executeShortcutAction, getSeekAmount, getSeekPercent, getVolumeDelta, resolveShortcutAction };
@@ -1,4 +0,0 @@
1
- /**
2
- * Check if keyboard event should be ignored (input fields, textareas, etc.)
3
- */
4
- export declare function shouldIgnoreKeyboardEvent(target: EventTarget | null): boolean;
@@ -1,11 +0,0 @@
1
- // src/lib/keyboard-utils.ts
2
- /**
3
- * Check if keyboard event should be ignored (input fields, textareas, etc.)
4
- */
5
- function shouldIgnoreKeyboardEvent(target) {
6
- if (!target || !(target instanceof HTMLElement)) return false;
7
- const tagName = target.tagName;
8
- return tagName === "INPUT" || tagName === "TEXTAREA" || target.contentEditable === "true";
9
- }
10
-
11
- export { shouldIgnoreKeyboardEvent };
@@ -1,67 +0,0 @@
1
- import { VideoLoadState } from "./video-preload-config.js";
2
- /**
3
- * Pure helpers and DOM-listener factories for player lifecycle effects:
4
- * playback rate sync, ended handling, preload state machine, reset-on-data
5
- * derivations, and feed playback gating.
6
- *
7
- * Framework-agnostic. React/Lit/Vue wrappers supply the store-update callbacks;
8
- * this module supplies the predicates, derivations, and listener factories.
9
- */
10
- /** Valid values for `<video preload>`. */
11
- export type VideoPreloadAttribute = "auto" | "metadata" | "none";
12
- /**
13
- * Map a feed virtualization `VideoLoadState` to the appropriate
14
- * `<video preload>` attribute value.
15
- *
16
- * - UNLOADED → "none" (slot is far from viewport; don't fetch anything)
17
- * - IDLE → "metadata" (slot is reachable but not active; fetch headers only)
18
- * - PRELOADING / PLAYING → "auto" (slot is active or about to be; fetch segments)
19
- */
20
- export declare function getPreloadAttribute(loadState: VideoLoadState): VideoPreloadAttribute;
21
- /**
22
- * Bind a `loadeddata` listener that re-applies the playback rate after the
23
- * browser resets it (most browsers reset `playbackRate` to `1.0` on each
24
- * `<source>` change).
25
- *
26
- * Also writes the rate immediately so that callers don't have to do so
27
- * separately. Returns a teardown function.
28
- */
29
- export declare function bindPlaybackRateSync(videoElement: HTMLVideoElement | null, playbackRate: number): () => void;
30
- /**
31
- * Bind an `ended` listener that invokes `onEnded()` exactly once per native
32
- * `ended` event. Returns a teardown function.
33
- */
34
- export declare function bindEndedListener(videoElement: HTMLVideoElement | null, onEnded: () => void): () => void;
35
- export interface PosterDimensions {
36
- width?: number;
37
- height?: number;
38
- }
39
- /**
40
- * Compute the aspect ratio (width / height) from poster dimensions, returning
41
- * `null` when either dimension is missing or zero.
42
- *
43
- * Mirrors the conditional in `useResetOnDataChangeEffect`:
44
- *
45
- * if (videoData?.poster?.width && videoData?.poster?.height) {
46
- * setAspectRatio(videoData.poster.width / videoData.poster.height);
47
- * }
48
- */
49
- export declare function getAspectRatioFromPoster(poster: PosterDimensions | undefined): number | null;
50
- export interface FeedPlaybackDecisionInput {
51
- isFeed: boolean;
52
- isCurrent: boolean;
53
- loadState: VideoLoadState;
54
- showSpinner: boolean;
55
- videoReadyState: number;
56
- }
57
- export type FeedPlaybackAction = "play" | "pause" | "none";
58
- /**
59
- * Decide what a feed-virtualized player should do given the current slot state.
60
- *
61
- * - In a non-feed context, returns "none" (caller handles its own playback).
62
- * - If the slot is not current, returns "pause" so the off-screen player stops.
63
- * - If the slot is current, in PLAYING load state, not spinning, and the video
64
- * has enough data (readyState >= 2), returns "play".
65
- * - Otherwise returns "none" (still loading / preloading; wait).
66
- */
67
- export declare function getFeedPlaybackAction({ isFeed, isCurrent, loadState, showSpinner, videoReadyState }: FeedPlaybackDecisionInput): FeedPlaybackAction;
@@ -1,78 +0,0 @@
1
- import { VideoLoadState } from "./video-preload-config.js";
2
-
3
- // src/lib/lifecycle-effects.ts
4
- /**
5
- * Map a feed virtualization `VideoLoadState` to the appropriate
6
- * `<video preload>` attribute value.
7
- *
8
- * - UNLOADED → "none" (slot is far from viewport; don't fetch anything)
9
- * - IDLE → "metadata" (slot is reachable but not active; fetch headers only)
10
- * - PRELOADING / PLAYING → "auto" (slot is active or about to be; fetch segments)
11
- */
12
- function getPreloadAttribute(loadState) {
13
- switch (loadState) {
14
- case VideoLoadState.UNLOADED: return "none";
15
- case VideoLoadState.IDLE: return "metadata";
16
- case VideoLoadState.PRELOADING:
17
- case VideoLoadState.PLAYING: return "auto";
18
- }
19
- }
20
- /**
21
- * Bind a `loadeddata` listener that re-applies the playback rate after the
22
- * browser resets it (most browsers reset `playbackRate` to `1.0` on each
23
- * `<source>` change).
24
- *
25
- * Also writes the rate immediately so that callers don't have to do so
26
- * separately. Returns a teardown function.
27
- */
28
- function bindPlaybackRateSync(videoElement, playbackRate) {
29
- if (!videoElement) return () => void 0;
30
- videoElement.playbackRate = playbackRate;
31
- const handler = () => {
32
- videoElement.playbackRate = playbackRate;
33
- };
34
- videoElement.addEventListener("loadeddata", handler);
35
- return () => videoElement.removeEventListener("loadeddata", handler);
36
- }
37
- /**
38
- * Bind an `ended` listener that invokes `onEnded()` exactly once per native
39
- * `ended` event. Returns a teardown function.
40
- */
41
- function bindEndedListener(videoElement, onEnded) {
42
- if (!videoElement) return () => void 0;
43
- videoElement.addEventListener("ended", onEnded);
44
- return () => videoElement.removeEventListener("ended", onEnded);
45
- }
46
- /**
47
- * Compute the aspect ratio (width / height) from poster dimensions, returning
48
- * `null` when either dimension is missing or zero.
49
- *
50
- * Mirrors the conditional in `useResetOnDataChangeEffect`:
51
- *
52
- * if (videoData?.poster?.width && videoData?.poster?.height) {
53
- * setAspectRatio(videoData.poster.width / videoData.poster.height);
54
- * }
55
- */
56
- function getAspectRatioFromPoster(poster) {
57
- if (!poster?.width || !poster?.height) return null;
58
- return poster.width / poster.height;
59
- }
60
- /**
61
- * Decide what a feed-virtualized player should do given the current slot state.
62
- *
63
- * - In a non-feed context, returns "none" (caller handles its own playback).
64
- * - If the slot is not current, returns "pause" so the off-screen player stops.
65
- * - If the slot is current, in PLAYING load state, not spinning, and the video
66
- * has enough data (readyState >= 2), returns "play".
67
- * - Otherwise returns "none" (still loading / preloading; wait).
68
- */
69
- function getFeedPlaybackAction({ isFeed, isCurrent, loadState, showSpinner, videoReadyState }) {
70
- if (!isFeed) return "none";
71
- if (!isCurrent) return "pause";
72
- if (loadState !== VideoLoadState.PLAYING) return "none";
73
- if (showSpinner) return "none";
74
- if (videoReadyState < 2) return "none";
75
- return "play";
76
- }
77
-
78
- export { bindEndedListener, bindPlaybackRateSync, getAspectRatioFromPoster, getFeedPlaybackAction, getPreloadAttribute };
@@ -1,26 +0,0 @@
1
- /**
2
- * Delay in milliseconds before showing the loading spinner
3
- * This prevents flash-of-spinner for brief buffering moments
4
- */
5
- export declare const LOADING_SPINNER_DELAY_MS = 300;
6
- export interface LoadingDebounceState {
7
- timerId: ReturnType<typeof setTimeout> | null;
8
- }
9
- export interface UpdateLoadingStateOptions {
10
- state: LoadingDebounceState;
11
- shouldShow: boolean;
12
- onShow: () => void;
13
- onHide: () => void;
14
- }
15
- /**
16
- * Create initial loading debounce state
17
- */
18
- export declare function createLoadingDebounceState(): LoadingDebounceState;
19
- /**
20
- * Update loading state with debounce
21
- */
22
- export declare function updateLoadingState(options: UpdateLoadingStateOptions): void;
23
- /**
24
- * Clean up loading debounce state (clear any pending timers)
25
- */
26
- export declare function cleanupLoadingDebounce(state: LoadingDebounceState): void;