@rixl/media-core 1.0.1 → 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.
Files changed (173) 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/BZFoe94X.js +1 -0
  5. package/dist/chunks/DMkwQjgA.js +1 -0
  6. package/dist/index.d.ts +2005 -82
  7. package/dist/index.js +6 -73
  8. package/package.json +13 -6
  9. package/dist/api/analytics.d.ts +0 -16
  10. package/dist/api/analytics.js +0 -227
  11. package/dist/api/feed.js +0 -52
  12. package/dist/api/heatmap.d.ts +0 -6
  13. package/dist/api/heatmap.js +0 -28
  14. package/dist/api/hot-segments.d.ts +0 -7
  15. package/dist/api/hot-segments.js +0 -20
  16. package/dist/api/image.js +0 -11
  17. package/dist/api/rixl-client.d.ts +0 -3
  18. package/dist/api/rixl-client.js +0 -14
  19. package/dist/api/upload.d.ts +0 -76
  20. package/dist/api/upload.js +0 -150
  21. package/dist/api/video.js +0 -11
  22. package/dist/lib/analytics-builders.d.ts +0 -59
  23. package/dist/lib/analytics-builders.js +0 -88
  24. package/dist/lib/analytics-initialize.d.ts +0 -1
  25. package/dist/lib/analytics-initialize.js +0 -59
  26. package/dist/lib/analytics-utils.d.ts +0 -43
  27. package/dist/lib/analytics-utils.js +0 -85
  28. package/dist/lib/analytics-wire.js +0 -153
  29. package/dist/lib/buffer-progress.d.ts +0 -20
  30. package/dist/lib/buffer-progress.js +0 -37
  31. package/dist/lib/caption-styles.d.ts +0 -20
  32. package/dist/lib/caption-styles.js +0 -56
  33. package/dist/lib/caption-utils.d.ts +0 -80
  34. package/dist/lib/caption-utils.js +0 -111
  35. package/dist/lib/chapter-utils.d.ts +0 -102
  36. package/dist/lib/chapter-utils.js +0 -147
  37. package/dist/lib/click-disambiguation.d.ts +0 -44
  38. package/dist/lib/click-disambiguation.js +0 -80
  39. package/dist/lib/click-outside.d.ts +0 -68
  40. package/dist/lib/click-outside.js +0 -64
  41. package/dist/lib/colors.d.ts +0 -6
  42. package/dist/lib/colors.js +0 -16
  43. package/dist/lib/content-analytics.d.ts +0 -27
  44. package/dist/lib/content-analytics.js +0 -38
  45. package/dist/lib/environment-effects.d.ts +0 -89
  46. package/dist/lib/environment-effects.js +0 -139
  47. package/dist/lib/feed-autoscroll.d.ts +0 -10
  48. package/dist/lib/feed-autoscroll.js +0 -13
  49. package/dist/lib/feed-container-height.d.ts +0 -7
  50. package/dist/lib/feed-container-height.js +0 -11
  51. package/dist/lib/feed-description-overflow.d.ts +0 -1
  52. package/dist/lib/feed-description-overflow.js +0 -9
  53. package/dist/lib/feed-dwell.d.ts +0 -2
  54. package/dist/lib/feed-dwell.js +0 -8
  55. package/dist/lib/feed-loop.d.ts +0 -10
  56. package/dist/lib/feed-loop.js +0 -21
  57. package/dist/lib/feed-scroll.d.ts +0 -11
  58. package/dist/lib/feed-scroll.js +0 -19
  59. package/dist/lib/feed-transforms.d.ts +0 -5
  60. package/dist/lib/feed-transforms.js +0 -30
  61. package/dist/lib/filter-undefined.js +0 -11
  62. package/dist/lib/fullscreen-utils.d.ts +0 -1
  63. package/dist/lib/fullscreen-utils.js +0 -7
  64. package/dist/lib/hls-buffer-flush.js +0 -46
  65. package/dist/lib/hls-error-recovery.d.ts +0 -35
  66. package/dist/lib/hls-error-recovery.js +0 -75
  67. package/dist/lib/hls-event-handlers.js +0 -61
  68. package/dist/lib/hls-loader.js +0 -23
  69. package/dist/lib/hls-setup.d.ts +0 -72
  70. package/dist/lib/hls-setup.js +0 -144
  71. package/dist/lib/hls-utils.d.ts +0 -31
  72. package/dist/lib/hls-utils.js +0 -34
  73. package/dist/lib/image-render.d.ts +0 -29
  74. package/dist/lib/image-render.js +0 -63
  75. package/dist/lib/keyboard-config.d.ts +0 -66
  76. package/dist/lib/keyboard-config.js +0 -127
  77. package/dist/lib/keyboard-utils.d.ts +0 -4
  78. package/dist/lib/keyboard-utils.js +0 -11
  79. package/dist/lib/lifecycle-effects.d.ts +0 -67
  80. package/dist/lib/lifecycle-effects.js +0 -78
  81. package/dist/lib/loading-debounce.d.ts +0 -26
  82. package/dist/lib/loading-debounce.js +0 -38
  83. package/dist/lib/numbers.d.ts +0 -6
  84. package/dist/lib/numbers.js +0 -36
  85. package/dist/lib/pip-utils.d.ts +0 -16
  86. package/dist/lib/pip-utils.js +0 -32
  87. package/dist/lib/playback-handlers.d.ts +0 -33
  88. package/dist/lib/playback-handlers.js +0 -51
  89. package/dist/lib/playback-slider.d.ts +0 -23
  90. package/dist/lib/playback-slider.js +0 -24
  91. package/dist/lib/player-labels.d.ts +0 -23
  92. package/dist/lib/player-labels.js +0 -132
  93. package/dist/lib/player-menu-labels.d.ts +0 -80
  94. package/dist/lib/player-menu-labels.js +0 -829
  95. package/dist/lib/player-ui-actions.d.ts +0 -10
  96. package/dist/lib/player-ui-actions.js +0 -58
  97. package/dist/lib/progress-fraction.d.ts +0 -11
  98. package/dist/lib/progress-fraction.js +0 -20
  99. package/dist/lib/progress-persist.d.ts +0 -28
  100. package/dist/lib/progress-persist.js +0 -54
  101. package/dist/lib/segment-analytics.d.ts +0 -20
  102. package/dist/lib/segment-analytics.js +0 -29
  103. package/dist/lib/subtitle-logic.d.ts +0 -31
  104. package/dist/lib/subtitle-logic.js +0 -75
  105. package/dist/lib/subtitle-utils.d.ts +0 -16
  106. package/dist/lib/subtitle-utils.js +0 -37
  107. package/dist/lib/time.d.ts +0 -1
  108. package/dist/lib/time.js +0 -17
  109. package/dist/lib/track-persistence.d.ts +0 -4
  110. package/dist/lib/track-persistence.js +0 -35
  111. package/dist/lib/video-preload-config.d.ts +0 -34
  112. package/dist/lib/video-preload-config.js +0 -41
  113. package/dist/lib/video-spinner.d.ts +0 -25
  114. package/dist/lib/video-spinner.js +0 -35
  115. package/dist/lib/video-stats.d.ts +0 -28
  116. package/dist/lib/video-stats.js +0 -44
  117. package/dist/lib/volume-utils.d.ts +0 -59
  118. package/dist/lib/volume-utils.js +0 -55
  119. package/dist/progressbar.d.ts +0 -10
  120. package/dist/progressbar.js +0 -123
  121. package/dist/store/analytics-context-store.d.ts +0 -4
  122. package/dist/store/analytics-context-store.js +0 -13
  123. package/dist/store/analytics-device-store.d.ts +0 -8
  124. package/dist/store/analytics-device-store.js +0 -44
  125. package/dist/store/analytics-session-store.d.ts +0 -8
  126. package/dist/store/analytics-session-store.js +0 -40
  127. package/dist/store/analytics-store.d.ts +0 -30
  128. package/dist/store/analytics-store.js +0 -88
  129. package/dist/store/controls-store.d.ts +0 -16
  130. package/dist/store/controls-store.js +0 -53
  131. package/dist/store/feed-cache-store.d.ts +0 -24
  132. package/dist/store/feed-cache-store.js +0 -165
  133. package/dist/store/feed-interaction-store.d.ts +0 -17
  134. package/dist/store/feed-interaction-store.js +0 -32
  135. package/dist/store/feed-store.d.ts +0 -16
  136. package/dist/store/feed-store.js +0 -38
  137. package/dist/store/generic-store.js +0 -24
  138. package/dist/store/image-store.d.ts +0 -2
  139. package/dist/store/image-store.js +0 -8
  140. package/dist/store/player-container-element-store.d.ts +0 -10
  141. package/dist/store/player-container-element-store.js +0 -38
  142. package/dist/store/player-global-actions.js +0 -53
  143. package/dist/store/player-media-element-store.d.ts +0 -10
  144. package/dist/store/player-media-element-store.js +0 -38
  145. package/dist/store/player-store-actions.js +0 -197
  146. package/dist/store/player-store.d.ts +0 -94
  147. package/dist/store/player-store.js +0 -127
  148. package/dist/store/player-track-actions.js +0 -44
  149. package/dist/store/postStore.d.ts +0 -20
  150. package/dist/store/postStore.js +0 -112
  151. package/dist/store/settings/constants.d.ts +0 -15
  152. package/dist/store/settings/constants.js +0 -47
  153. package/dist/store/settings/defaults.d.ts +0 -2
  154. package/dist/store/settings/defaults.js +0 -24
  155. package/dist/store/settings/storage.d.ts +0 -3
  156. package/dist/store/settings/storage.js +0 -28
  157. package/dist/store/settings/store.d.ts +0 -32
  158. package/dist/store/settings/store.js +0 -59
  159. package/dist/store/settings/types.d.ts +0 -27
  160. package/dist/store/settings/validation.d.ts +0 -7
  161. package/dist/store/settings/validation.js +0 -55
  162. package/dist/store/settings-menu-store.d.ts +0 -19
  163. package/dist/store/settings-menu-store.js +0 -30
  164. package/dist/store/video-store.d.ts +0 -2
  165. package/dist/store/video-store.js +0 -8
  166. package/dist/types/analytics.d.ts +0 -113
  167. package/dist/types/feed.d.ts +0 -21
  168. package/dist/types/file.d.ts +0 -12
  169. package/dist/types/heatmap.d.ts +0 -5
  170. package/dist/types/hot-segments.d.ts +0 -9
  171. package/dist/types/image.d.ts +0 -9
  172. package/dist/types/uuid.d.ts +0 -1
  173. package/dist/types/video.d.ts +0 -22
@@ -1,153 +0,0 @@
1
- import { filterUndefined } from "./filter-undefined.js";
2
-
3
- // src/lib/analytics-wire.ts
4
- function msToProtoDuration(ms) {
5
- const seconds = ms / 1e3;
6
- return `${Number(seconds.toFixed(3))}s`;
7
- }
8
- function toWireMediaType(type) {
9
- return type === "video" ? "MEDIA_TYPE_VIDEO" : "MEDIA_TYPE_IMAGE";
10
- }
11
- function toWireSegment(segment) {
12
- return {
13
- start_ms: msToProtoDuration(segment.start_ms),
14
- end_ms: msToProtoDuration(segment.end_ms),
15
- speed: segment.speed
16
- };
17
- }
18
- function toWireContentView(event, { context, deviceId }) {
19
- const wire = {
20
- content_id: event.content_id,
21
- content_type: toWireMediaType(event.content_type),
22
- view_type: event.view_type,
23
- watch_duration_ms: msToProtoDuration(event.watch_duration_ms),
24
- page: event.page,
25
- device_id: event.device_id ?? deviceId,
26
- ...filterUndefined({
27
- feed_id: event.feed_id,
28
- post_id: event.post_id,
29
- video_position_ms: event.video_position_ms !== void 0 ? msToProtoDuration(event.video_position_ms) : void 0,
30
- video_total_duration_ms: event.video_total_duration_ms !== void 0 ? msToProtoDuration(event.video_total_duration_ms) : void 0,
31
- segments: event.segments !== void 0 && event.segments.length > 0 ? event.segments.map(toWireSegment) : void 0
32
- })
33
- };
34
- const country = event.country ?? context.country;
35
- if (country !== void 0) wire.country = country;
36
- return wire;
37
- }
38
- function toWireEngagement(event, { context, sessionId }) {
39
- const wire = {
40
- engagement_type: event.engagement_type,
41
- session_id: event.session_id ?? sessionId,
42
- ...filterUndefined({
43
- resource_type: event.resource_type,
44
- resource_id: event.resource_id,
45
- page: event.page,
46
- comment_text: event.comment_text,
47
- share_platform: event.share_platform
48
- })
49
- };
50
- const country = event.country ?? context.country;
51
- if (country !== void 0) wire.country = country;
52
- const deviceType = event.device_type ?? context.device;
53
- if (deviceType !== void 0) wire.device_type = deviceType;
54
- return wire;
55
- }
56
- function toWireInteraction(event, { context, sessionId }) {
57
- const wire = {
58
- interaction_type: event.interaction_type,
59
- session_id: event.session_id ?? sessionId,
60
- ...filterUndefined({
61
- element_type: event.element_type,
62
- element_id: event.element_id,
63
- page: event.page,
64
- page_url: event.page_url,
65
- scroll_depth: event.scroll_depth,
66
- search_query: event.search_query,
67
- click_x: event.click_x,
68
- click_y: event.click_y,
69
- resource_id: event.resource_id,
70
- resource_type: event.resource_type,
71
- feed_id: event.feed_id,
72
- post_id: event.post_id,
73
- content_id: event.content_id,
74
- device_type: event.device_type,
75
- browser: event.browser
76
- })
77
- };
78
- const deviceType = wire.device_type ?? context.device;
79
- if (deviceType !== void 0) wire.device_type = deviceType;
80
- const browser = wire.browser ?? context.browser;
81
- if (browser !== void 0) wire.browser = browser;
82
- return wire;
83
- }
84
- function toWireError(event, { context, sessionId }) {
85
- const wire = {
86
- error_type: event.error_type,
87
- session_id: event.session_id ?? sessionId,
88
- ...filterUndefined({
89
- error_code: event.error_code,
90
- error_message: event.error_message,
91
- stack_trace: event.stack_trace,
92
- resource_type: event.resource_type,
93
- resource_id: event.resource_id,
94
- endpoint: event.endpoint,
95
- device_type: event.device_type,
96
- browser: event.browser,
97
- os: event.os
98
- })
99
- };
100
- const deviceType = wire.device_type ?? context.device;
101
- if (deviceType !== void 0) wire.device_type = deviceType;
102
- const browser = wire.browser ?? context.browser;
103
- if (browser !== void 0) wire.browser = browser;
104
- const os = wire.os ?? context.os;
105
- if (os !== void 0) wire.os = os;
106
- return wire;
107
- }
108
- function toWireSessionStart(event) {
109
- return { ...filterUndefined({
110
- utm_source: event.utm_source,
111
- utm_campaign: event.utm_campaign,
112
- browser: event.browser,
113
- language: event.language,
114
- screen_resolution: event.screen_resolution,
115
- platform: event.platform,
116
- tg_platform: event.tg_platform,
117
- tg_version: event.tg_version,
118
- timezone: event.timezone,
119
- timezone_offset: event.timezone_offset,
120
- color_depth: event.color_depth,
121
- touch_support: event.touch_support,
122
- country: event.country,
123
- user_id: event.user_id
124
- }) };
125
- }
126
- function applyTimestamp(wire, timestamp) {
127
- if (timestamp !== void 0) wire.timestamp = (/* @__PURE__ */ new Date(timestamp * 1e3)).toISOString();
128
- }
129
- function toWireEvent(event, options) {
130
- let wire;
131
- switch (event._type) {
132
- case "content_views":
133
- wire = { content_view: toWireContentView(event, options) };
134
- break;
135
- case "engagement":
136
- wire = { engagement: toWireEngagement(event, options) };
137
- break;
138
- case "interaction":
139
- wire = { interaction: toWireInteraction(event, options) };
140
- break;
141
- case "error":
142
- wire = { error: toWireError(event, options) };
143
- break;
144
- case "session_start":
145
- wire = { session_start: toWireSessionStart(event) };
146
- break;
147
- default: throw new Error(`Unsupported analytics event type: ${event._type}`);
148
- }
149
- applyTimestamp(wire, event.timestamp);
150
- return wire;
151
- }
152
-
153
- export { msToProtoDuration, toWireEvent, toWireSegment };
@@ -1,20 +0,0 @@
1
- /**
2
- * Pure buffer-progress computations extracted from useBufferProgress.
3
- * Operate on plain TimeRanges + numbers, not HTMLVideoElement, so they
4
- * are framework-agnostic and trivially testable.
5
- */
6
- export interface BufferRange {
7
- start: number;
8
- end: number;
9
- }
10
- /**
11
- * Convert a TimeRanges into normalized 0-100 percentage ranges.
12
- * Returns [] when duration is zero/invalid or timeRanges is missing.
13
- */
14
- export declare const computeBufferRanges: (timeRanges: TimeRanges | null | undefined, duration: number) => BufferRange[];
15
- /**
16
- * How much buffer (as a 0-100 percentage) is loaded ahead of the
17
- * current playback position. Considers only ranges that cover the
18
- * current position; takes the maximum contiguous lookahead.
19
- */
20
- export declare const computeBufferedAhead: (ranges: BufferRange[], currentTime: number, duration: number) => number;
@@ -1,37 +0,0 @@
1
- // src/lib/buffer-progress.ts
2
- /**
3
- * Convert a TimeRanges into normalized 0-100 percentage ranges.
4
- * Returns [] when duration is zero/invalid or timeRanges is missing.
5
- */
6
- const computeBufferRanges = (timeRanges, duration) => {
7
- if (!timeRanges || !duration || !Number.isFinite(duration) || duration <= 0) return [];
8
- const ranges = [];
9
- try {
10
- for (let i = 0; i < timeRanges.length; i++) {
11
- const end = timeRanges.end(i) / duration * 100;
12
- ranges.push({
13
- start: 0,
14
- end: Math.min(100, end)
15
- });
16
- }
17
- } catch {
18
- return [];
19
- }
20
- return ranges;
21
- };
22
- /**
23
- * How much buffer (as a 0-100 percentage) is loaded ahead of the
24
- * current playback position. Considers only ranges that cover the
25
- * current position; takes the maximum contiguous lookahead.
26
- */
27
- const computeBufferedAhead = (ranges, currentTime, duration) => {
28
- if (!ranges.length || !duration || !Number.isFinite(duration) || duration <= 0) return 0;
29
- const currentPercent = currentTime / duration * 100;
30
- let bufferAhead = 0;
31
- ranges.forEach((range) => {
32
- if (range.end > currentPercent && range.start <= currentPercent + .1) bufferAhead = Math.max(bufferAhead, range.end - currentPercent);
33
- });
34
- return bufferAhead;
35
- };
36
-
37
- export { computeBufferRanges, computeBufferedAhead };
@@ -1,20 +0,0 @@
1
- import { Position } from "./caption-utils.js";
2
- import { CSSProperties } from "./image-render.js";
3
- import { SettingsState } from "../store/settings/types.js";
4
- export interface SmartCaptionPositionParams {
5
- position: Position;
6
- isDragging: boolean;
7
- controlsVisible: boolean;
8
- }
9
- /**
10
- * Caption window placement: percent-based within the safe zone, snapping to
11
- * the bottom (above the controls bar when visible) once dragged past the
12
- * snap threshold. Single source for the React hook and the Lit overlay.
13
- */
14
- export declare function getSmartCaptionPosition({ position, isDragging, controlsVisible }: SmartCaptionPositionParams): CSSProperties;
15
- export type CaptionWindowSettings = Pick<SettingsState, "captionWindowColor" | "captionWindowOpacity">;
16
- /** Caption window box: width from the safe viewport, settings-driven background, drag cursor. */
17
- export declare function getCaptionWindowStyle(settings: CaptionWindowSettings, safeViewportWidth: number, isDragging: boolean): CSSProperties;
18
- export type CaptionTextSettings = Pick<SettingsState, "captionFontFamily" | "captionFontColor" | "captionFontOpacity" | "captionBGColor" | "captionBGOpacity" | "captionCharacterEdgeStyle">;
19
- /** Caption text styling from the user's caption settings (font, colors, edge-style shadow). */
20
- export declare function getCaptionTextStyle(settings: CaptionTextSettings, fontSize: number): CSSProperties;
@@ -1,56 +0,0 @@
1
- import { BOTTOM_OFFSET_CONTROLS_HIDDEN, BOTTOM_OFFSET_CONTROLS_VISIBLE, CAPTION_WIDTH_RATIO, getTextShadowValue, shouldPinToBottom } from "./caption-utils.js";
2
- import { hexToRGBAString } from "./colors.js";
3
- import { FONT_FAMILIES } from "../store/settings/constants.js";
4
-
5
- // src/lib/caption-styles.ts
6
- /**
7
- * Caption window placement: percent-based within the safe zone, snapping to
8
- * the bottom (above the controls bar when visible) once dragged past the
9
- * snap threshold. Single source for the React hook and the Lit overlay.
10
- */
11
- function getSmartCaptionPosition({ position, isDragging, controlsVisible }) {
12
- const transition = isDragging ? "none" : "all 300ms ease";
13
- if (shouldPinToBottom(position.y, isDragging)) {
14
- const bottomOffset = controlsVisible ? 80 : 24;
15
- return {
16
- left: `${position.x}%`,
17
- top: `calc(100% - ${bottomOffset}px)`,
18
- bottom: "auto",
19
- transform: "translateY(-100%)",
20
- transition
21
- };
22
- }
23
- return {
24
- left: `${position.x}%`,
25
- top: `${position.y}%`,
26
- bottom: "auto",
27
- transform: "translateY(0)",
28
- transition
29
- };
30
- }
31
- /** Caption window box: width from the safe viewport, settings-driven background, drag cursor. */
32
- function getCaptionWindowStyle(settings, safeViewportWidth, isDragging) {
33
- const width = (safeViewportWidth || 300) * CAPTION_WIDTH_RATIO;
34
- const backgroundColor = settings.captionWindowOpacity > 0 ? hexToRGBAString(settings.captionWindowColor, settings.captionWindowOpacity) : "transparent";
35
- return {
36
- width: `${width}px`,
37
- backgroundColor,
38
- cursor: isDragging ? "grabbing" : "grab",
39
- touchAction: "none"
40
- };
41
- }
42
- /** Caption text styling from the user's caption settings (font, colors, edge-style shadow). */
43
- function getCaptionTextStyle(settings, fontSize) {
44
- const fontFamily = FONT_FAMILIES[settings.captionFontFamily] || FONT_FAMILIES.proportional_sans;
45
- return {
46
- fontSize: `${fontSize}px`,
47
- fontFamily,
48
- color: hexToRGBAString(settings.captionFontColor, settings.captionFontOpacity),
49
- backgroundColor: settings.captionBGOpacity > 0 ? hexToRGBAString(settings.captionBGColor, settings.captionBGOpacity) : "transparent",
50
- textShadow: getTextShadowValue(settings.captionCharacterEdgeStyle, fontSize),
51
- userSelect: "none",
52
- touchAction: "none"
53
- };
54
- }
55
-
56
- export { getCaptionTextStyle, getCaptionWindowStyle, getSmartCaptionPosition };
@@ -1,80 +0,0 @@
1
- export type Viewport = {
2
- width: number;
3
- height: number;
4
- };
5
- export interface Position {
6
- x: number;
7
- y: number;
8
- }
9
- export declare const DEFAULT_FONT_SIZE = 16;
10
- export declare const MIN_FONT_SIZE = 12;
11
- export declare const MAX_FONT_SIZE = 120;
12
- export declare const FONT_SIZE_RATIO = .03;
13
- export declare const FONT_SIZE_VIEWPORT_RATIO = .07;
14
- export declare const DEFAULT_CAPTION_FONT_SIZE = 100;
15
- export declare const SAFE_ZONE_MARGIN = 8;
16
- export declare const CAPTION_WIDTH_RATIO = .6;
17
- /**
18
- * Smart caption positioning constants
19
- */
20
- export declare const BOTTOM_SNAP_THRESHOLD = 85;
21
- export declare const BOTTOM_OFFSET_CONTROLS_VISIBLE = 80;
22
- export declare const BOTTOM_OFFSET_CONTROLS_HIDDEN = 24;
23
- /**
24
- * Calculates the font size based on the viewport width and user preference.
25
- * Uses a logarithmic scale
26
- * 100% -> 1.0x
27
- * 200% -> 1.5x
28
- * 400% -> 2.0x
29
- */
30
- export declare const calculateFontSize: (viewport: Viewport, captionFontSize: number) => number;
31
- /**
32
- * Constrains a percentage-based position to be within safe bounds (0-100).
33
- * Assumes the input position is already in percentages.
34
- */
35
- export declare const constrainPercentagePosition: (position: Position, elementWidthRatio?: number) => Position;
36
- /**
37
- * Converts pixel coordinates to percentage coordinates relative to the viewport.
38
- */
39
- export declare const pixelsToPercentage: (x: number, y: number, viewport: Viewport) => Position;
40
- /**
41
- * Calculate pointer offsets for caption dragging
42
- */
43
- export interface PointerOffsets {
44
- clickXRelative: number;
45
- clickYRelative: number;
46
- currentVisualX: number;
47
- currentVisualY: number;
48
- }
49
- export interface GetPointerOffsetsOptions {
50
- containerRect: DOMRect;
51
- elementRect: DOMRect;
52
- clientX: number;
53
- clientY: number;
54
- }
55
- export declare function getPointerOffsets(options: GetPointerOffsetsOptions): PointerOffsets;
56
- /**
57
- * Calculate new drag position from delta
58
- */
59
- export interface CalculateDragPositionOptions {
60
- startPositionPixels: Position;
61
- startX: number;
62
- startY: number;
63
- currentXRelative: number;
64
- currentYRelative: number;
65
- viewport: Viewport;
66
- }
67
- export declare function calculateDragPosition(options: CalculateDragPositionOptions): Position;
68
- /**
69
- * Calculate safe zone viewport dimensions
70
- */
71
- export declare function calculateSafeViewport(viewport: Viewport): Viewport;
72
- /**
73
- * Determine if caption should be pinned to bottom
74
- */
75
- export declare function shouldPinToBottom(positionY: number, isDragging: boolean): boolean;
76
- /**
77
- * CSS text-shadow value for a caption character edge style.
78
- * Shadow size scales with font size so edges stay proportional.
79
- */
80
- export declare function getTextShadowValue(edgeStyle: string, fontSize: number): string;
@@ -1,111 +0,0 @@
1
- import { clamp } from "./numbers.js";
2
-
3
- // src/lib/caption-utils.ts
4
- const DEFAULT_FONT_SIZE = 16;
5
- const MIN_FONT_SIZE = 12;
6
- const MAX_FONT_SIZE = 120;
7
- const FONT_SIZE_RATIO = .03;
8
- const FONT_SIZE_VIEWPORT_RATIO = .07;
9
- const DEFAULT_CAPTION_FONT_SIZE = 100;
10
- const SAFE_ZONE_MARGIN = 8;
11
- const CAPTION_WIDTH_RATIO = .6;
12
- /**
13
- * Smart caption positioning constants
14
- */
15
- const BOTTOM_SNAP_THRESHOLD = 85;
16
- const BOTTOM_OFFSET_CONTROLS_VISIBLE = 80;
17
- const BOTTOM_OFFSET_CONTROLS_HIDDEN = 24;
18
- /**
19
- * Calculates the font size based on the viewport width and user preference.
20
- * Uses a logarithmic scale
21
- * 100% -> 1.0x
22
- * 200% -> 1.5x
23
- * 400% -> 2.0x
24
- */
25
- const calculateFontSize = (viewport, captionFontSize) => {
26
- if (!viewport.width) return 16;
27
- const baseSize = viewport.width * FONT_SIZE_RATIO;
28
- const ratio = Math.max(.1, captionFontSize / 100);
29
- const scaledSize = baseSize * (1 + .5 * Math.log2(ratio));
30
- const maxSize = Math.min(120, viewport.width * FONT_SIZE_VIEWPORT_RATIO);
31
- return clamp(scaledSize, 12, maxSize);
32
- };
33
- /**
34
- * Constrains a percentage-based position to be within safe bounds (0-100).
35
- * Assumes the input position is already in percentages.
36
- */
37
- const constrainPercentagePosition = (position, elementWidthRatio = .6) => {
38
- const widthPercentage = elementWidthRatio * 100;
39
- const maxX = Math.max(0, 100 - widthPercentage);
40
- return {
41
- x: clamp(position.x, 0, maxX),
42
- y: clamp(position.y, 0, 100)
43
- };
44
- };
45
- /**
46
- * Converts pixel coordinates to percentage coordinates relative to the viewport.
47
- */
48
- const pixelsToPercentage = (x, y, viewport) => {
49
- if (!viewport.width || !viewport.height) return {
50
- x: 0,
51
- y: 0
52
- };
53
- return {
54
- x: x / viewport.width * 100,
55
- y: y / viewport.height * 100
56
- };
57
- };
58
- function getPointerOffsets(options) {
59
- const { containerRect, elementRect, clientX, clientY } = options;
60
- return {
61
- clickXRelative: clientX - containerRect.left,
62
- clickYRelative: clientY - containerRect.top,
63
- currentVisualX: elementRect.left - containerRect.left,
64
- currentVisualY: elementRect.top - containerRect.top
65
- };
66
- }
67
- function calculateDragPosition(options) {
68
- const { startPositionPixels, startX, startY, currentXRelative, currentYRelative, viewport } = options;
69
- const deltaX = currentXRelative - startX;
70
- const deltaY = currentYRelative - startY;
71
- const newPixelX = startPositionPixels.x + deltaX;
72
- const newPixelY = startPositionPixels.y + deltaY;
73
- const raw = pixelsToPercentage(newPixelX, newPixelY, viewport);
74
- return constrainPercentagePosition(raw, CAPTION_WIDTH_RATIO);
75
- }
76
- /**
77
- * Calculate safe zone viewport dimensions
78
- */
79
- function calculateSafeViewport(viewport) {
80
- return {
81
- width: viewport.width - 16,
82
- height: viewport.height - 16
83
- };
84
- }
85
- /**
86
- * Determine if caption should be pinned to bottom
87
- */
88
- function shouldPinToBottom(positionY, isDragging) {
89
- return !isDragging && positionY > 85;
90
- }
91
- /**
92
- * CSS text-shadow value for a caption character edge style.
93
- * Shadow size scales with font size so edges stay proportional.
94
- */
95
- function getTextShadowValue(edgeStyle, fontSize) {
96
- const shadowSize = Math.max(1, fontSize * .06);
97
- switch (edgeStyle) {
98
- case "drop-shadow": return `${shadowSize}px ${shadowSize}px ${shadowSize}px rgba(0, 0, 0, 0.75)`;
99
- case "raised": return `${shadowSize}px ${shadowSize}px 0 rgba(0, 0, 0, 0.5), -${shadowSize * .5}px -${shadowSize * .5}px 0 rgba(255, 255, 255, 0.25)`;
100
- case "depressed": return `-${shadowSize}px -${shadowSize}px 0 rgba(0, 0, 0, 0.5), ${shadowSize * .5}px ${shadowSize * .5}px 0 rgba(255, 255, 255, 0.25)`;
101
- case "outline": return `
102
- -${shadowSize}px -${shadowSize}px 0 #000,
103
- ${shadowSize}px -${shadowSize}px 0 #000,
104
- -${shadowSize}px ${shadowSize}px 0 #000,
105
- ${shadowSize}px ${shadowSize}px 0 #000
106
- `;
107
- default: return "none";
108
- }
109
- }
110
-
111
- export { BOTTOM_OFFSET_CONTROLS_HIDDEN, BOTTOM_OFFSET_CONTROLS_VISIBLE, BOTTOM_SNAP_THRESHOLD, CAPTION_WIDTH_RATIO, DEFAULT_CAPTION_FONT_SIZE, DEFAULT_FONT_SIZE, FONT_SIZE_RATIO, FONT_SIZE_VIEWPORT_RATIO, MAX_FONT_SIZE, MIN_FONT_SIZE, SAFE_ZONE_MARGIN, calculateDragPosition, calculateFontSize, calculateSafeViewport, constrainPercentagePosition, getPointerOffsets, getTextShadowValue, pixelsToPercentage, shouldPinToBottom };
@@ -1,102 +0,0 @@
1
- import { Chapter } from "../types/video.js";
2
- import { BufferRange } from "./buffer-progress.js";
3
- /**
4
- * Chapter with calculated end time and duration
5
- */
6
- export interface ChapterWithEnd extends Chapter {
7
- endTime: number;
8
- durationSec: number;
9
- }
10
- /**
11
- * Check if a single chapter is within the video duration
12
- * @param chapter - The chapter to validate
13
- * @param videoDuration - The total video duration in seconds
14
- * @param tolerance - Tolerance in seconds for rounding errors (default: 1)
15
- * @returns Whether the chapter is within valid bounds
16
- */
17
- export declare function isChapterWithinDuration(chapter: Chapter, videoDuration: number, tolerance?: number): boolean;
18
- /**
19
- * Validate chapters for use with chaptered progress bar
20
- * Returns false if:
21
- * - chapters is undefined or empty
22
- * - less than 2 chapters (need multiple for chaptered bar)
23
- * - any chapter exceeds video duration
24
- *
25
- * @param chapters - Array of chapters to validate
26
- * @param videoDuration - The total video duration in seconds
27
- * @param tolerance - Tolerance in seconds for rounding errors (default: 1)
28
- * @returns Whether chapters are valid for chaptered progress bar
29
- */
30
- export declare function validateChapters(chapters: Chapter[] | undefined, videoDuration: number, tolerance?: number): boolean;
31
- /**
32
- * Calculate chapter end times based on next chapter start or video duration
33
- */
34
- export declare function calculateChaptersWithEnd(chapters: Chapter[], duration: number): ChapterWithEnd[];
35
- /**
36
- * Slice heatmap data for a specific chapter based on its time range
37
- */
38
- interface HeatmapSliceParams {
39
- heatmapData: number[];
40
- chapterStartTime: number;
41
- chapterEndTime: number;
42
- videoDuration: number;
43
- }
44
- export declare function sliceHeatmapData({ heatmapData, chapterStartTime, chapterEndTime, videoDuration }: HeatmapSliceParams): number[];
45
- /**
46
- * Calculate all heatmap slices for chapters
47
- */
48
- export declare function calculateHeatmapSlices(heatmapData: number[] | null, chapters: ChapterWithEnd[], duration: number): number[][];
49
- /**
50
- * Calculate global max value from heatmap data for consistent normalization
51
- */
52
- export declare function calculateGlobalMax(heatmapData: number[] | null): number;
53
- /**
54
- * Calculate progress percentage within a chapter segment
55
- * @returns 0-100 percentage
56
- */
57
- export declare function calculateSegmentProgress(currentPosition: number, chapterStart: number, chapterEnd: number): number;
58
- /**
59
- * Calculate buffer ranges within a chapter segment
60
- * Converts global buffer ranges to segment-local 0-100 scale
61
- */
62
- type SegmentBufferParams = {
63
- globalBufferRanges: BufferRange[];
64
- chapterStart: number;
65
- chapterEnd: number;
66
- videoDuration: number;
67
- };
68
- export declare function calculateSegmentBufferRanges({ globalBufferRanges, chapterStart, chapterEnd, videoDuration }: SegmentBufferParams): BufferRange[];
69
- /**
70
- * Calculate flex grow ratio for a chapter based on its duration
71
- */
72
- export declare function calculateFlexGrow(chapterDuration: number, videoDuration: number): number;
73
- /**
74
- * Determine current playback position
75
- * Uses progress during drag for real-time feedback, currentTime otherwise
76
- */
77
- interface CurrentPositionParams {
78
- isDragging: boolean;
79
- progress: number;
80
- duration: number;
81
- currentTime: number;
82
- }
83
- export declare function getCurrentPosition({ isDragging, progress, duration, currentTime }: CurrentPositionParams): number;
84
- /**
85
- * Find which segment is hovered based on mouse X position
86
- * @param segments - Array of segment DOM elements
87
- * @param mouseX - Mouse X coordinate (clientX)
88
- * @returns Index of hovered segment, or null if none
89
- */
90
- export declare function findHoveredSegmentIndex(segments: Array<HTMLDivElement | null>, mouseX: number): number | null;
91
- /**
92
- * Start time of the first chapter after the current position, or null when
93
- * already in the last chapter. Mirrors React useChapterNavigation.
94
- */
95
- export declare function getNextChapterStart(chapters: Chapter[] | undefined, currentTime: number): number | null;
96
- /**
97
- * Target for "previous chapter": restart the current chapter when more than
98
- * 3s in (or when it is the first), otherwise jump to the previous chapter.
99
- * Mirrors React useChapterNavigation.
100
- */
101
- export declare function getPrevChapterTarget(chapters: Chapter[] | undefined, currentTime: number): number | null;
102
- export {};