@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.
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 -125
  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,10 +0,0 @@
1
- import { UUID } from "../types/uuid.js";
2
- interface PlayerUiActionTargets {
3
- container?: HTMLDivElement | null;
4
- video?: HTMLVideoElement | null;
5
- }
6
- export declare function showPlayerControls(playerId: UUID): void;
7
- export declare function hidePlayerControls(playerId: UUID): void;
8
- export declare function togglePlayerFullscreen(playerId: UUID, overrides?: PlayerUiActionTargets): Promise<void>;
9
- export declare function togglePlayerPictureInPicture(playerId: UUID, overrides?: PlayerUiActionTargets): Promise<void>;
10
- export {};
@@ -1,58 +0,0 @@
1
- import { isDocumentFullscreen } from "./fullscreen-utils.js";
2
- import { togglePictureInPicture } from "./pip-utils.js";
3
- import { controlsStore } from "../store/controls-store.js";
4
- import { playerContainerElementStore } from "../store/player-container-element-store.js";
5
- import { playerMediaElementStore } from "../store/player-media-element-store.js";
6
- import { playerStateStore } from "../store/player-store.js";
7
-
8
- // src/lib/player-ui-actions.ts
9
- function getPlayerUiTargets(playerId, overrides) {
10
- return {
11
- container: overrides?.container ?? playerContainerElementStore.get(playerId),
12
- video: overrides?.video ?? playerMediaElementStore.get(playerId)
13
- };
14
- }
15
- function shouldExitFullscreen(playerId) {
16
- return playerStateStore.get(playerId).get().isFullscreen || isDocumentFullscreen();
17
- }
18
- async function exitPlayerFullscreen(playerId, video) {
19
- try {
20
- if (isDocumentFullscreen() && document.exitFullscreen) await document.exitFullscreen();
21
- else if (video && typeof video.webkitExitFullscreen === "function") video.webkitExitFullscreen();
22
- else return;
23
- playerStateStore.setFullscreen(playerId, false);
24
- } catch {}
25
- }
26
- async function enterPlayerFullscreen(playerId, container, video) {
27
- try {
28
- if (container?.requestFullscreen) await container.requestFullscreen();
29
- else if (video?.requestFullscreen) await video.requestFullscreen();
30
- else if (video && typeof video.webkitEnterFullscreen === "function") video.webkitEnterFullscreen();
31
- else return;
32
- playerStateStore.setFullscreen(playerId, true);
33
- } catch {}
34
- }
35
- function showPlayerControls(playerId) {
36
- controlsStore.setVisible(playerId, true);
37
- }
38
- function hidePlayerControls(playerId) {
39
- controlsStore.hideControls(playerId);
40
- }
41
- async function togglePlayerFullscreen(playerId, overrides) {
42
- const { container, video } = getPlayerUiTargets(playerId, overrides);
43
- if (!container && !video) return;
44
- if (shouldExitFullscreen(playerId)) {
45
- await exitPlayerFullscreen(playerId, video);
46
- return;
47
- }
48
- await enterPlayerFullscreen(playerId, container, video);
49
- }
50
- async function togglePlayerPictureInPicture(playerId, overrides) {
51
- const { video } = getPlayerUiTargets(playerId, overrides);
52
- if (!video) return;
53
- const nextPictureInPictureState = !playerStateStore.get(playerId).get().isPictureInPicture;
54
- await togglePictureInPicture(video);
55
- playerStateStore.setPictureInPicture(playerId, nextPictureInPictureState);
56
- }
57
-
58
- export { hidePlayerControls, showPlayerControls, togglePlayerFullscreen, togglePlayerPictureInPicture };
@@ -1,11 +0,0 @@
1
- /**
2
- * Pure progress-fraction helper extracted from useProgressBar.
3
- *
4
- * Returns a value in [0, 1] representing how far through `duration`
5
- * `currentTime` is. Defensive against the common edge cases where
6
- * duration is zero, NaN, or Infinity (e.g. before metadata loads),
7
- * and against non-finite currentTime values. The previous inline
8
- * `video.currentTime / video.duration` formula produced NaN in
9
- * those cases, which leaked into React state.
10
- */
11
- export declare function computeProgressFraction(currentTime: number, duration: number): number;
@@ -1,20 +0,0 @@
1
- // src/lib/progress-fraction.ts
2
- /**
3
- * Pure progress-fraction helper extracted from useProgressBar.
4
- *
5
- * Returns a value in [0, 1] representing how far through `duration`
6
- * `currentTime` is. Defensive against the common edge cases where
7
- * duration is zero, NaN, or Infinity (e.g. before metadata loads),
8
- * and against non-finite currentTime values. The previous inline
9
- * `video.currentTime / video.duration` formula produced NaN in
10
- * those cases, which leaked into React state.
11
- */
12
- function computeProgressFraction(currentTime, duration) {
13
- if (!Number.isFinite(duration) || duration <= 0) return 0;
14
- if (!Number.isFinite(currentTime)) return 0;
15
- if (currentTime <= 0) return 0;
16
- if (currentTime >= duration) return 1;
17
- return currentTime / duration;
18
- }
19
-
20
- export { computeProgressFraction };
@@ -1,28 +0,0 @@
1
- /**
2
- * Minimum position in seconds to resume from (skip if too close to start)
3
- */
4
- export declare const RESUME_MIN_POSITION_SECONDS = 5;
5
- /**
6
- * Minimum remaining duration in seconds to resume (skip if too close to end)
7
- */
8
- export declare const RESUME_MIN_REMAINING_SECONDS = 5;
9
- /**
10
- * Interval in milliseconds between progress persistence operations
11
- */
12
- export declare const PERSIST_INTERVAL_MS = 2e3;
13
- export declare const getPersistedProgress: (videoId: string) => number;
14
- export declare const persistProgress: (videoId: string, positionMs: number) => void;
15
- /**
16
- * Determine if a saved progress position should be used to resume playback
17
- * @param savedPositionSeconds - The saved position in seconds
18
- * @param durationSeconds - The total video duration in seconds
19
- * @returns true if position is valid for resuming (not too close to start or end)
20
- */
21
- export declare function shouldResumeFromPosition(savedPositionSeconds: number, durationSeconds: number): boolean;
22
- /**
23
- * Determine if enough time has elapsed to persist progress again
24
- * @param lastPersistTime - Timestamp of last persist operation (from performance.now())
25
- * @param currentTime - Current timestamp (from performance.now())
26
- * @returns true if interval has elapsed
27
- */
28
- export declare function shouldPersistProgress(lastPersistTime: number, currentTime: number): boolean;
@@ -1,54 +0,0 @@
1
- // src/lib/progress-persist.ts
2
- const KEY = "videosdk_progress_map";
3
- /**
4
- * Minimum position in seconds to resume from (skip if too close to start)
5
- */
6
- const RESUME_MIN_POSITION_SECONDS = 5;
7
- /**
8
- * Minimum remaining duration in seconds to resume (skip if too close to end)
9
- */
10
- const RESUME_MIN_REMAINING_SECONDS = 5;
11
- /**
12
- * Interval in milliseconds between progress persistence operations
13
- */
14
- const PERSIST_INTERVAL_MS = 2e3;
15
- const getPersistedProgress = (videoId) => {
16
- if (typeof window === "undefined") return 0;
17
- try {
18
- const raw = window.localStorage.getItem(KEY);
19
- if (!raw) return 0;
20
- const map = JSON.parse(raw);
21
- return Math.max(0, Math.floor(map[videoId] ?? 0));
22
- } catch {
23
- return 0;
24
- }
25
- };
26
- const persistProgress = (videoId, positionMs) => {
27
- if (typeof window === "undefined") return;
28
- try {
29
- const raw = window.localStorage.getItem(KEY);
30
- const map = raw ? JSON.parse(raw) : {};
31
- map[videoId] = Math.max(0, Math.floor(positionMs));
32
- window.localStorage.setItem(KEY, JSON.stringify(map));
33
- } catch {}
34
- };
35
- /**
36
- * Determine if a saved progress position should be used to resume playback
37
- * @param savedPositionSeconds - The saved position in seconds
38
- * @param durationSeconds - The total video duration in seconds
39
- * @returns true if position is valid for resuming (not too close to start or end)
40
- */
41
- function shouldResumeFromPosition(savedPositionSeconds, durationSeconds) {
42
- return savedPositionSeconds > 5 && savedPositionSeconds < durationSeconds - 5;
43
- }
44
- /**
45
- * Determine if enough time has elapsed to persist progress again
46
- * @param lastPersistTime - Timestamp of last persist operation (from performance.now())
47
- * @param currentTime - Current timestamp (from performance.now())
48
- * @returns true if interval has elapsed
49
- */
50
- function shouldPersistProgress(lastPersistTime, currentTime) {
51
- return currentTime - lastPersistTime > PERSIST_INTERVAL_MS;
52
- }
53
-
54
- export { PERSIST_INTERVAL_MS, RESUME_MIN_POSITION_SECONDS, RESUME_MIN_REMAINING_SECONDS, getPersistedProgress, persistProgress, shouldPersistProgress, shouldResumeFromPosition };
@@ -1,20 +0,0 @@
1
- import { UUID } from "../types/uuid.js";
2
- export interface SegmentAnalyticsParams {
3
- playerId: UUID;
4
- currentTimeMs: number;
5
- playbackRate: number;
6
- isPaused: boolean;
7
- }
8
- /**
9
- * Start tracking a new analytics segment
10
- */
11
- export declare function startSegment(params: SegmentAnalyticsParams): void;
12
- /**
13
- * End the current analytics segment
14
- */
15
- export declare function endSegment(params: Pick<SegmentAnalyticsParams, "playerId" | "currentTimeMs">): void;
16
- /**
17
- * Restart segment if video is currently playing
18
- * This is useful when playback rate changes or other segment-breaking events occur
19
- */
20
- export declare function restartSegmentIfPlaying(params: SegmentAnalyticsParams): void;
@@ -1,29 +0,0 @@
1
- import { analyticsStoreActions } from "../store/analytics-store.js";
2
-
3
- // src/lib/segment-analytics.ts
4
- /**
5
- * Start tracking a new analytics segment
6
- */
7
- function startSegment(params) {
8
- const { playerId, currentTimeMs, playbackRate } = params;
9
- analyticsStoreActions.startSegment(playerId, currentTimeMs, playbackRate);
10
- }
11
- /**
12
- * End the current analytics segment
13
- */
14
- function endSegment(params) {
15
- const { playerId, currentTimeMs } = params;
16
- analyticsStoreActions.endSegment(playerId, currentTimeMs);
17
- }
18
- /**
19
- * Restart segment if video is currently playing
20
- * This is useful when playback rate changes or other segment-breaking events occur
21
- */
22
- function restartSegmentIfPlaying(params) {
23
- const { playerId, currentTimeMs, playbackRate, isPaused } = params;
24
- if (isPaused) return;
25
- analyticsStoreActions.endSegment(playerId, currentTimeMs);
26
- analyticsStoreActions.startSegment(playerId, currentTimeMs, playbackRate);
27
- }
28
-
29
- export { endSegment, restartSegmentIfPlaying, startSegment };
@@ -1,31 +0,0 @@
1
- export interface SubtitleTrack {
2
- index: number;
3
- label: string;
4
- language: string;
5
- kind: string;
6
- track: TextTrack;
7
- }
8
- /**
9
- * Calculates the efficient end time for a cue to prevent flickering and ensure readability.
10
- */
11
- export declare const calculateSmoothedEndTime: (cue: TextTrackCue | VTTCue, nextCue?: TextTrackCue | VTTCue) => number;
12
- /**
13
- * Helper to find the next cue in a TextTrackCueList by reference.
14
- */
15
- export declare const findNextCue: (activeCue: TextTrackCue, allCues: TextTrackCueList | null) => TextTrackCue | undefined;
16
- export declare function stripHtmlTags(input: string): string;
17
- /**
18
- * Extracts and cleans text from a cue object.
19
- * Handles VTTCue vs TextTrackCue differences.
20
- */
21
- export declare const getCleanCueText: (cue: TextTrackCue | VTTCue) => string;
22
- /**
23
- * Applies smoothing logic to a list of cues (typically activeCues).
24
- * Modifies the cues in-place and caches them to avoid re-processing.
25
- */
26
- export declare const processCues: (cues: TextTrackCueList | TextTrackCue[], allCues: TextTrackCueList | null) => void;
27
- /**
28
- * Parses the video element's text tracks and extracts relevant subtitle/caption tracks.
29
- * Also normalizes track state.
30
- */
31
- export declare const extractSubtitleTracks: (tracks: TextTrackList) => SubtitleTrack[];
@@ -1,75 +0,0 @@
1
- import { clamp } from "./numbers.js";
2
-
3
- // src/lib/subtitle-logic.ts
4
- const SMOOTHING_GAP_THRESHOLD = .5;
5
- const MIN_SUBTITLE_DURATION = 1;
6
- const OVERLAP_BUFFER = .1;
7
- /**
8
- * Calculates the efficient end time for a cue to prevent flickering and ensure readability.
9
- */
10
- const calculateSmoothedEndTime = (cue, nextCue) => {
11
- let effectiveEndTime = cue.endTime;
12
- if (nextCue) {
13
- if (nextCue.startTime - cue.endTime <= SMOOTHING_GAP_THRESHOLD) return nextCue.startTime + OVERLAP_BUFFER;
14
- const minEndTime = cue.startTime + MIN_SUBTITLE_DURATION;
15
- return clamp(minEndTime, effectiveEndTime, nextCue.startTime);
16
- }
17
- return Math.max(effectiveEndTime, cue.startTime + MIN_SUBTITLE_DURATION);
18
- };
19
- /**
20
- * Helper to find the next cue in a TextTrackCueList by reference.
21
- */
22
- const findNextCue = (activeCue, allCues) => {
23
- if (!allCues || allCues.length === 0) return void 0;
24
- for (let index = 0; index < allCues.length - 1; index++) if (allCues[index] === activeCue) return allCues[index + 1];
25
- };
26
- function stripHtmlTags(input) {
27
- return input.replace(/<[^>]*>/g, "");
28
- }
29
- /**
30
- * Extracts and cleans text from a cue object.
31
- * Handles VTTCue vs TextTrackCue differences.
32
- */
33
- const getCleanCueText = (cue) => {
34
- return stripHtmlTags(cue instanceof VTTCue ? cue.text : cue.text ?? "");
35
- };
36
- const processedCues = /* @__PURE__ */ new WeakSet();
37
- /**
38
- * Applies smoothing logic to a list of cues (typically activeCues).
39
- * Modifies the cues in-place and caches them to avoid re-processing.
40
- */
41
- const processCues = (cues, allCues) => {
42
- for (let i = 0; i < cues.length; i++) {
43
- const cue = cues[i];
44
- if (!processedCues.has(cue)) {
45
- const nextCue = findNextCue(cue, allCues);
46
- const newEndTime = calculateSmoothedEndTime(cue, nextCue);
47
- if (Math.abs(cue.endTime - newEndTime) > .01) cue.endTime = newEndTime;
48
- processedCues.add(cue);
49
- }
50
- }
51
- };
52
- /**
53
- * Parses the video element's text tracks and extracts relevant subtitle/caption tracks.
54
- * Also normalizes track state.
55
- */
56
- const extractSubtitleTracks = (tracks) => {
57
- const subtitleTracks = [];
58
- for (let i = 0; i < tracks.length; i++) {
59
- const track = tracks[i];
60
- if (track.kind === "subtitles" || track.kind === "captions") {
61
- if (!track.label && !track.language && !track.id) continue;
62
- subtitleTracks.push({
63
- index: i,
64
- label: track.label || `${track.kind} ${i + 1}`,
65
- language: track.language || "unknown",
66
- kind: track.kind,
67
- track
68
- });
69
- track.mode = "disabled";
70
- }
71
- }
72
- return subtitleTracks;
73
- };
74
-
75
- export { calculateSmoothedEndTime, extractSubtitleTracks, findNextCue, getCleanCueText, processCues, stripHtmlTags };
@@ -1,16 +0,0 @@
1
- import { UUID } from "../types/uuid.js";
2
- /**
3
- * Structural shape of a video element ref. Compatible with React.RefObject
4
- * but does not depend on React, keeping this module framework-agnostic.
5
- */
6
- interface VideoElementRef {
7
- current: HTMLVideoElement | null;
8
- }
9
- export declare const handleLoadedMetadata: (videoElement: HTMLVideoElement, playerId: UUID) => void;
10
- export declare const clearSubtitles: (playerId: UUID) => void;
11
- /**
12
- * Handles the 'cuechange' event to update subtitles.
13
- * Uses event-driven logic + lookahead smoothing to prevent flickering.
14
- */
15
- export declare const handleCueChange: (playerId: UUID, videoRef: VideoElementRef) => void;
16
- export {};
@@ -1,37 +0,0 @@
1
- import { playerStateStore } from "../store/player-store.js";
2
- import { extractSubtitleTracks, getCleanCueText, processCues } from "./subtitle-logic.js";
3
-
4
- // src/lib/subtitle-utils.ts
5
- const handleLoadedMetadata = (videoElement, playerId) => {
6
- const subtitleTracks = extractSubtitleTracks(videoElement.textTracks);
7
- playerStateStore.setAvailableSubtitleTracks(playerId, subtitleTracks);
8
- };
9
- const clearSubtitles = (playerId) => {
10
- playerStateStore.setCurrentSubtitleText(playerId, "");
11
- };
12
- /**
13
- * Handles the 'cuechange' event to update subtitles.
14
- * Uses event-driven logic + lookahead smoothing to prevent flickering.
15
- */
16
- const handleCueChange = (playerId, videoRef) => {
17
- if (!videoRef.current) return;
18
- const player = playerStateStore.get(playerId).get();
19
- const index = player.subtitleTrack;
20
- if (index < 0 || index >= player.availableSubtitleTracks.length) {
21
- if (player.currentSubtitleText !== "") clearSubtitles(playerId);
22
- return;
23
- }
24
- const trackObj = player.availableSubtitleTracks[index].track;
25
- if (trackObj.mode !== "showing") return;
26
- const activeCues = trackObj.activeCues;
27
- if (!activeCues || activeCues.length === 0) {
28
- if (player.currentSubtitleText !== "") playerStateStore.setCurrentSubtitleText(playerId, "");
29
- return;
30
- }
31
- processCues(activeCues, trackObj.cues);
32
- const activeCue = activeCues[activeCues.length - 1];
33
- const newText = getCleanCueText(activeCue);
34
- if (newText !== player.currentSubtitleText) playerStateStore.setCurrentSubtitleText(playerId, newText);
35
- };
36
-
37
- export { clearSubtitles, handleCueChange, handleLoadedMetadata };
@@ -1 +0,0 @@
1
- export declare const formatTime: (seconds: number) => string;
package/dist/lib/time.js DELETED
@@ -1,17 +0,0 @@
1
- // src/lib/time.ts
2
- function leadingZero(num) {
3
- let numStr = num.toString();
4
- if (num < 10) numStr = "0" + numStr;
5
- return numStr;
6
- }
7
- const formatTime = (seconds) => {
8
- if (isNaN(seconds) || seconds < 0) return "00:00";
9
- const hours = Math.floor(seconds / 3600);
10
- const minutes = Math.floor(seconds % 3600 / 60);
11
- const remainingSeconds = Math.floor(seconds % 60);
12
- const output = `${leadingZero(minutes)}:${leadingZero(remainingSeconds)}`;
13
- if (hours > 0) return `${leadingZero(hours)}:${output}`;
14
- return output;
15
- };
16
-
17
- export { formatTime };
@@ -1,4 +0,0 @@
1
- export declare const getPersistedSubtitleTrack: () => number;
2
- export declare const persistSubtitleTrack: (trackIndex: number) => void;
3
- export declare const getPersistedAudioTrack: () => number;
4
- export declare const persistAudioTrack: (trackIndex: number) => void;
@@ -1,35 +0,0 @@
1
- // src/lib/track-persistence.ts
2
- const SUBTITLE_TRACK_KEY = "video-player-subtitle-track";
3
- const AUDIO_TRACK_KEY = "audioTrack";
4
- const getPersistedSubtitleTrack = () => {
5
- try {
6
- const stored = localStorage.getItem(SUBTITLE_TRACK_KEY);
7
- return stored !== null ? parseInt(stored, 10) : -1;
8
- } catch {
9
- return -1;
10
- }
11
- };
12
- const persistSubtitleTrack = (trackIndex) => {
13
- try {
14
- localStorage.setItem(SUBTITLE_TRACK_KEY, trackIndex.toString());
15
- } catch {
16
- return;
17
- }
18
- };
19
- const getPersistedAudioTrack = () => {
20
- try {
21
- const stored = localStorage.getItem(AUDIO_TRACK_KEY);
22
- return stored ? parseInt(stored, 10) : 0;
23
- } catch {
24
- return 0;
25
- }
26
- };
27
- const persistAudioTrack = (trackIndex) => {
28
- try {
29
- localStorage.setItem(AUDIO_TRACK_KEY, trackIndex.toString());
30
- } catch {
31
- return;
32
- }
33
- };
34
-
35
- export { getPersistedAudioTrack, getPersistedSubtitleTrack, persistAudioTrack, persistSubtitleTrack };
@@ -1,34 +0,0 @@
1
- /**
2
- * PRELOAD_RANGE determines how many videos around the current one should be loaded
3
- *
4
- * Example with BEFORE: 1, AFTER: 2, current index is 5:
5
- *
6
- * Legend:
7
- * PRELOADING (buffering in background)
8
- * PLAYING (currently visible)
9
- * IDLE (exists but not loaded)
10
- * UNLOADED (too far, save memory)
11
- */
12
- export declare const PRELOAD_RANGE: {
13
- BEFORE: number;
14
- AFTER: number;
15
- };
16
- /**
17
- * UNLOAD_DISTANCE determines when to unload videos to free memory
18
- * If a video is more than this distance from current, unload it
19
- */
20
- export declare const UNLOAD_DISTANCE = 5;
21
- /**
22
- * Video loading states
23
- */
24
- export declare const VideoLoadState: {
25
- readonly UNLOADED: "unloaded";
26
- readonly IDLE: "idle";
27
- readonly PRELOADING: "preloading";
28
- readonly PLAYING: "playing";
29
- };
30
- export type VideoLoadState = (typeof VideoLoadState)[keyof typeof VideoLoadState];
31
- /**
32
- * Calculate what state a video should be in based on its distance from current
33
- */
34
- export declare function getVideoLoadState(videoIndex: number, currentIndex: number, isCurrent: boolean): VideoLoadState;
@@ -1,41 +0,0 @@
1
- // src/lib/video-preload-config.ts
2
- /**
3
- * PRELOAD_RANGE determines how many videos around the current one should be loaded
4
- *
5
- * Example with BEFORE: 1, AFTER: 2, current index is 5:
6
- *
7
- * Legend:
8
- * PRELOADING (buffering in background)
9
- * PLAYING (currently visible)
10
- * IDLE (exists but not loaded)
11
- * UNLOADED (too far, save memory)
12
- */
13
- const PRELOAD_RANGE = {
14
- BEFORE: 0,
15
- AFTER: 1
16
- };
17
- /**
18
- * UNLOAD_DISTANCE determines when to unload videos to free memory
19
- * If a video is more than this distance from current, unload it
20
- */
21
- const UNLOAD_DISTANCE = 5;
22
- /**
23
- * Video loading states
24
- */
25
- const VideoLoadState = {
26
- UNLOADED: "unloaded",
27
- IDLE: "idle",
28
- PRELOADING: "preloading",
29
- PLAYING: "playing"
30
- };
31
- /**
32
- * Calculate what state a video should be in based on its distance from current
33
- */
34
- function getVideoLoadState(videoIndex, currentIndex, isCurrent) {
35
- if (isCurrent) return VideoLoadState.PLAYING;
36
- const distance = videoIndex - currentIndex;
37
- if (Math.abs(distance) > 5) return VideoLoadState.UNLOADED;
38
- return distance < 0 && distance >= -PRELOAD_RANGE.BEFORE || distance > 0 && distance <= PRELOAD_RANGE.AFTER ? VideoLoadState.PRELOADING : VideoLoadState.IDLE;
39
- }
40
-
41
- export { PRELOAD_RANGE, UNLOAD_DISTANCE, VideoLoadState, getVideoLoadState };
@@ -1,25 +0,0 @@
1
- /**
2
- * Live "is the player buffering right now?" predicate.
3
- *
4
- * Different from `video-preload-config.ts` despite the similar name:
5
- * - `video-preload-config.ts` decides the `<video preload>` attribute for
6
- * a slot in the feed virtualization (one-time decision per slot).
7
- * - `shouldShowSpinner` is called continuously during playback to decide
8
- * whether the buffering spinner should currently be visible.
9
- *
10
- * Inputs come from a live `<HTMLVideoElement>`. The function is otherwise
11
- * pure and framework-agnostic.
12
- */
13
- /**
14
- * Decide whether the buffering spinner should be visible for the given
15
- * video element.
16
- *
17
- * Logic, in order:
18
- * 1. No video → never show.
19
- * 2. Playing past the start → if the current time falls inside any buffered
20
- * range, the player has the bytes it needs; never show.
21
- * 3. Otherwise fall back to `readyState`/`seeking`: show when the browser
22
- * reports `HAVE_CURRENT_DATA` (2) or is actively seeking with
23
- * `readyState < 3` (HAVE_FUTURE_DATA).
24
- */
25
- export declare function shouldShowSpinner(video: HTMLVideoElement | null): boolean;
@@ -1,35 +0,0 @@
1
- // src/lib/video-spinner.ts
2
- /**
3
- * Live "is the player buffering right now?" predicate.
4
- *
5
- * Different from `video-preload-config.ts` despite the similar name:
6
- * - `video-preload-config.ts` decides the `<video preload>` attribute for
7
- * a slot in the feed virtualization (one-time decision per slot).
8
- * - `shouldShowSpinner` is called continuously during playback to decide
9
- * whether the buffering spinner should currently be visible.
10
- *
11
- * Inputs come from a live `<HTMLVideoElement>`. The function is otherwise
12
- * pure and framework-agnostic.
13
- */
14
- /**
15
- * Decide whether the buffering spinner should be visible for the given
16
- * video element.
17
- *
18
- * Logic, in order:
19
- * 1. No video → never show.
20
- * 2. Playing past the start → if the current time falls inside any buffered
21
- * range, the player has the bytes it needs; never show.
22
- * 3. Otherwise fall back to `readyState`/`seeking`: show when the browser
23
- * reports `HAVE_CURRENT_DATA` (2) or is actively seeking with
24
- * `readyState < 3` (HAVE_FUTURE_DATA).
25
- */
26
- function shouldShowSpinner(video) {
27
- if (!video) return false;
28
- if (!video.paused && video.currentTime > 0) {
29
- const buffered = video.buffered;
30
- for (let i = 0; i < buffered.length; i++) if (video.currentTime >= buffered.start(i) && video.currentTime < buffered.end(i)) return false;
31
- }
32
- return video.readyState < 3 && (video.seeking || video.readyState === 2);
33
- }
34
-
35
- export { shouldShowSpinner };
@@ -1,28 +0,0 @@
1
- export interface VideoStats {
2
- droppedFrames: number;
3
- volume: number;
4
- networkSpeed: number;
5
- bufferHealth: number;
6
- currentRes: {
7
- width: number;
8
- height: number;
9
- } | null;
10
- optimalRes: {
11
- width: number;
12
- height: number;
13
- } | null;
14
- }
15
- export interface CollectStatsInput {
16
- video: HTMLVideoElement | null;
17
- hlsBandwidthEstimate: number | null;
18
- hlsLevels: ReadonlyArray<{
19
- width: number;
20
- height: number;
21
- }>;
22
- hlsCurrentLevel: number;
23
- hlsAutoLevelEnabled: boolean;
24
- hlsAutoLevelCapping: number;
25
- }
26
- export declare function getDroppedFrames(video: HTMLVideoElement): number;
27
- export declare function getBufferHealth(video: HTMLVideoElement): number;
28
- export declare function collectVideoStats(input: CollectStatsInput): VideoStats;