@livepeer-frameworks/player-react 0.1.1 → 0.1.2

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 (158) hide show
  1. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +359 -0
  2. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  3. package/dist/cjs/assets/logomark.svg.js +8 -0
  4. package/dist/cjs/assets/logomark.svg.js.map +1 -0
  5. package/dist/cjs/components/DevModePanel.js +826 -0
  6. package/dist/cjs/components/DevModePanel.js.map +1 -0
  7. package/dist/cjs/components/DvdLogo.js +200 -0
  8. package/dist/cjs/components/DvdLogo.js.map +1 -0
  9. package/dist/cjs/components/Icons.js +439 -0
  10. package/dist/cjs/components/Icons.js.map +1 -0
  11. package/dist/cjs/components/IdleScreen.js +587 -0
  12. package/dist/cjs/components/IdleScreen.js.map +1 -0
  13. package/dist/cjs/components/LoadingScreen.js +523 -0
  14. package/dist/cjs/components/LoadingScreen.js.map +1 -0
  15. package/dist/cjs/components/Player.js +420 -0
  16. package/dist/cjs/components/Player.js.map +1 -0
  17. package/dist/cjs/components/PlayerControls.js +798 -0
  18. package/dist/cjs/components/PlayerControls.js.map +1 -0
  19. package/dist/cjs/components/PlayerErrorBoundary.js +80 -0
  20. package/dist/cjs/components/PlayerErrorBoundary.js.map +1 -0
  21. package/dist/cjs/components/SeekBar.js +253 -0
  22. package/dist/cjs/components/SeekBar.js.map +1 -0
  23. package/dist/cjs/components/SkipIndicator.js +92 -0
  24. package/dist/cjs/components/SkipIndicator.js.map +1 -0
  25. package/dist/cjs/components/SpeedIndicator.js +43 -0
  26. package/dist/cjs/components/SpeedIndicator.js.map +1 -0
  27. package/dist/cjs/components/StatsPanel.js +202 -0
  28. package/dist/cjs/components/StatsPanel.js.map +1 -0
  29. package/dist/cjs/components/StreamStateOverlay.js +229 -0
  30. package/dist/cjs/components/StreamStateOverlay.js.map +1 -0
  31. package/dist/cjs/components/ThumbnailOverlay.js +86 -0
  32. package/dist/cjs/components/ThumbnailOverlay.js.map +1 -0
  33. package/dist/cjs/components/TitleOverlay.js +32 -0
  34. package/dist/cjs/components/TitleOverlay.js.map +1 -0
  35. package/dist/cjs/context/PlayerContext.js +46 -0
  36. package/dist/cjs/context/PlayerContext.js.map +1 -0
  37. package/dist/cjs/hooks/useMetaTrack.js +165 -0
  38. package/dist/cjs/hooks/useMetaTrack.js.map +1 -0
  39. package/dist/cjs/hooks/usePlaybackQuality.js +131 -0
  40. package/dist/cjs/hooks/usePlaybackQuality.js.map +1 -0
  41. package/dist/cjs/hooks/usePlayerController.js +518 -0
  42. package/dist/cjs/hooks/usePlayerController.js.map +1 -0
  43. package/dist/cjs/hooks/usePlayerSelection.js +90 -0
  44. package/dist/cjs/hooks/usePlayerSelection.js.map +1 -0
  45. package/dist/cjs/hooks/useStreamState.js +360 -0
  46. package/dist/cjs/hooks/useStreamState.js.map +1 -0
  47. package/dist/cjs/hooks/useTelemetry.js +120 -0
  48. package/dist/cjs/hooks/useTelemetry.js.map +1 -0
  49. package/dist/cjs/hooks/useViewerEndpoints.js +222 -0
  50. package/dist/cjs/hooks/useViewerEndpoints.js.map +1 -0
  51. package/dist/cjs/index.js +97 -1
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/cjs/ui/badge.js +34 -0
  54. package/dist/cjs/ui/badge.js.map +1 -0
  55. package/dist/cjs/ui/button.js +74 -0
  56. package/dist/cjs/ui/button.js.map +1 -0
  57. package/dist/cjs/ui/context-menu.js +163 -0
  58. package/dist/cjs/ui/context-menu.js.map +1 -0
  59. package/dist/cjs/ui/slider.js +60 -0
  60. package/dist/cjs/ui/slider.js.map +1 -0
  61. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
  62. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  63. package/dist/esm/assets/logomark.svg.js +4 -0
  64. package/dist/esm/assets/logomark.svg.js.map +1 -0
  65. package/dist/esm/components/DevModePanel.js +822 -0
  66. package/dist/esm/components/DevModePanel.js.map +1 -0
  67. package/dist/esm/components/DvdLogo.js +196 -0
  68. package/dist/esm/components/DvdLogo.js.map +1 -0
  69. package/dist/esm/components/Icons.js +421 -0
  70. package/dist/esm/components/Icons.js.map +1 -0
  71. package/dist/esm/components/IdleScreen.js +582 -0
  72. package/dist/esm/components/IdleScreen.js.map +1 -0
  73. package/dist/esm/components/LoadingScreen.js +519 -0
  74. package/dist/esm/components/LoadingScreen.js.map +1 -0
  75. package/dist/esm/components/Player.js +416 -0
  76. package/dist/esm/components/Player.js.map +1 -0
  77. package/dist/esm/components/PlayerControls.js +794 -0
  78. package/dist/esm/components/PlayerControls.js.map +1 -0
  79. package/dist/esm/components/PlayerErrorBoundary.js +76 -0
  80. package/dist/esm/components/PlayerErrorBoundary.js.map +1 -0
  81. package/dist/esm/components/SeekBar.js +249 -0
  82. package/dist/esm/components/SeekBar.js.map +1 -0
  83. package/dist/esm/components/SkipIndicator.js +88 -0
  84. package/dist/esm/components/SkipIndicator.js.map +1 -0
  85. package/dist/esm/components/SpeedIndicator.js +39 -0
  86. package/dist/esm/components/SpeedIndicator.js.map +1 -0
  87. package/dist/esm/components/StatsPanel.js +198 -0
  88. package/dist/esm/components/StatsPanel.js.map +1 -0
  89. package/dist/esm/components/StreamStateOverlay.js +224 -0
  90. package/dist/esm/components/StreamStateOverlay.js.map +1 -0
  91. package/dist/esm/components/ThumbnailOverlay.js +82 -0
  92. package/dist/esm/components/ThumbnailOverlay.js.map +1 -0
  93. package/dist/esm/components/TitleOverlay.js +28 -0
  94. package/dist/esm/components/TitleOverlay.js.map +1 -0
  95. package/dist/esm/context/PlayerContext.js +41 -0
  96. package/dist/esm/context/PlayerContext.js.map +1 -0
  97. package/dist/esm/hooks/useMetaTrack.js +163 -0
  98. package/dist/esm/hooks/useMetaTrack.js.map +1 -0
  99. package/dist/esm/hooks/usePlaybackQuality.js +129 -0
  100. package/dist/esm/hooks/usePlaybackQuality.js.map +1 -0
  101. package/dist/esm/hooks/usePlayerController.js +516 -0
  102. package/dist/esm/hooks/usePlayerController.js.map +1 -0
  103. package/dist/esm/hooks/usePlayerSelection.js +88 -0
  104. package/dist/esm/hooks/usePlayerSelection.js.map +1 -0
  105. package/dist/esm/hooks/useStreamState.js +358 -0
  106. package/dist/esm/hooks/useStreamState.js.map +1 -0
  107. package/dist/esm/hooks/useTelemetry.js +118 -0
  108. package/dist/esm/hooks/useTelemetry.js.map +1 -0
  109. package/dist/esm/hooks/useViewerEndpoints.js +220 -0
  110. package/dist/esm/hooks/useViewerEndpoints.js.map +1 -0
  111. package/dist/esm/index.js +23 -1
  112. package/dist/esm/index.js.map +1 -1
  113. package/dist/esm/ui/badge.js +31 -0
  114. package/dist/esm/ui/badge.js.map +1 -0
  115. package/dist/esm/ui/button.js +52 -0
  116. package/dist/esm/ui/button.js.map +1 -0
  117. package/dist/esm/ui/context-menu.js +132 -0
  118. package/dist/esm/ui/context-menu.js.map +1 -0
  119. package/dist/esm/ui/slider.js +38 -0
  120. package/dist/esm/ui/slider.js.map +1 -0
  121. package/dist/types/components/DvdLogo.d.ts +1 -1
  122. package/dist/types/components/Icons.d.ts +1 -1
  123. package/dist/types/components/Player.d.ts +1 -1
  124. package/dist/types/components/PlayerErrorBoundary.d.ts +2 -1
  125. package/dist/types/components/StreamStateOverlay.d.ts +2 -2
  126. package/dist/types/components/SubtitleRenderer.d.ts +2 -2
  127. package/dist/types/context/PlayerContext.d.ts +2 -2
  128. package/dist/types/context/index.d.ts +2 -2
  129. package/dist/types/hooks/useMetaTrack.d.ts +3 -3
  130. package/dist/types/hooks/usePlaybackQuality.d.ts +2 -2
  131. package/dist/types/hooks/usePlayerController.d.ts +26 -3
  132. package/dist/types/hooks/usePlayerSelection.d.ts +1 -1
  133. package/dist/types/hooks/useStreamState.d.ts +1 -1
  134. package/dist/types/hooks/useTelemetry.d.ts +1 -1
  135. package/dist/types/hooks/useViewerEndpoints.d.ts +3 -3
  136. package/dist/types/index.d.ts +28 -28
  137. package/dist/types/types.d.ts +3 -3
  138. package/dist/types/ui/select.d.ts +1 -1
  139. package/package.json +22 -14
  140. package/src/components/DvdLogo.tsx +1 -1
  141. package/src/components/Player.tsx +42 -3
  142. package/src/components/PlayerControls.tsx +36 -17
  143. package/src/components/PlayerErrorBoundary.tsx +2 -1
  144. package/src/hooks/usePlayerController.ts +61 -1
  145. package/dist/types/components/players/DashJsPlayer.d.ts +0 -18
  146. package/dist/types/components/players/HlsJsPlayer.d.ts +0 -18
  147. package/dist/types/components/players/MewsWsPlayer/index.d.ts +0 -18
  148. package/dist/types/components/players/MistPlayer.d.ts +0 -20
  149. package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +0 -20
  150. package/dist/types/components/players/NativePlayer.d.ts +0 -19
  151. package/dist/types/components/players/VideoJsPlayer.d.ts +0 -18
  152. package/src/components/players/DashJsPlayer.tsx +0 -58
  153. package/src/components/players/HlsJsPlayer.tsx +0 -58
  154. package/src/components/players/MewsWsPlayer/index.tsx +0 -58
  155. package/src/components/players/MistPlayer.tsx +0 -62
  156. package/src/components/players/MistWebRTCPlayer/index.tsx +0 -68
  157. package/src/components/players/NativePlayer.tsx +0 -56
  158. package/src/components/players/VideoJsPlayer.tsx +0 -58
@@ -0,0 +1,88 @@
1
+ import { slicedToArray as _slicedToArray } from '../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { useState, useEffect, useCallback } from 'react';
3
+
4
+ /**
5
+ * Subscribe to player selection changes from a PlayerManager.
6
+ *
7
+ * This hook uses the event system in PlayerManager, which means:
8
+ * - Initial computation happens once when streamInfo is provided
9
+ * - Updates only fire when selection actually changes (different player+source)
10
+ * - No render spam from React strict mode or frequent re-renders
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { selection, combinations, ready } = usePlayerSelection(globalPlayerManager, {
15
+ * streamInfo,
16
+ * playbackMode: 'auto',
17
+ * });
18
+ *
19
+ * if (!ready) return <Loading />;
20
+ * if (!selection) return <NoPlayerAvailable />;
21
+ *
22
+ * return <div>Selected: {selection.player} + {selection.source.type}</div>;
23
+ * ```
24
+ */
25
+ function usePlayerSelection(manager, options) {
26
+ var streamInfo = options.streamInfo,
27
+ playbackMode = options.playbackMode,
28
+ debug = options.debug;
29
+ var _useState = useState(null),
30
+ _useState2 = _slicedToArray(_useState, 2),
31
+ selection = _useState2[0],
32
+ setSelection = _useState2[1];
33
+ var _useState3 = useState([]),
34
+ _useState4 = _slicedToArray(_useState3, 2),
35
+ combinations = _useState4[0],
36
+ setCombinations = _useState4[1];
37
+ var _useState5 = useState(false),
38
+ _useState6 = _slicedToArray(_useState5, 2),
39
+ ready = _useState6[0],
40
+ setReady = _useState6[1];
41
+ // Subscribe to events
42
+ useEffect(function () {
43
+ var unsubSelection = manager.on("selection-changed", function (sel) {
44
+ if (debug) {
45
+ var _sel$source;
46
+ console.log("[usePlayerSelection] Selection changed:", sel === null || sel === void 0 ? void 0 : sel.player, sel === null || sel === void 0 || (_sel$source = sel.source) === null || _sel$source === void 0 ? void 0 : _sel$source.type);
47
+ }
48
+ setSelection(sel);
49
+ });
50
+ var unsubCombos = manager.on("combinations-updated", function (combos) {
51
+ if (debug) {
52
+ console.log("[usePlayerSelection] Combinations updated:", combos.length);
53
+ }
54
+ setCombinations(combos);
55
+ setReady(true);
56
+ });
57
+ return function () {
58
+ unsubSelection();
59
+ unsubCombos();
60
+ };
61
+ }, [manager, debug]);
62
+ // Trigger initial computation when streamInfo changes
63
+ useEffect(function () {
64
+ if (!streamInfo) {
65
+ setSelection(null);
66
+ setCombinations([]);
67
+ setReady(false);
68
+ return;
69
+ }
70
+ // This will use cache if available, or compute + emit events if not
71
+ manager.getAllCombinations(streamInfo, playbackMode);
72
+ }, [manager, streamInfo, playbackMode]);
73
+ // Manual refresh function
74
+ var refresh = useCallback(function () {
75
+ if (!streamInfo) return;
76
+ manager.invalidateCache();
77
+ manager.getAllCombinations(streamInfo, playbackMode);
78
+ }, [manager, streamInfo, playbackMode]);
79
+ return {
80
+ selection: selection,
81
+ combinations: combinations,
82
+ ready: ready,
83
+ refresh: refresh
84
+ };
85
+ }
86
+
87
+ export { usePlayerSelection };
88
+ //# sourceMappingURL=usePlayerSelection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePlayerSelection.js","sources":["../../../../src/hooks/usePlayerSelection.ts"],"sourcesContent":["/**\n * usePlayerSelection\n *\n * React hook for subscribing to PlayerManager selection events.\n * Uses event-driven updates instead of polling - no render spam.\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport type {\n PlayerManager,\n PlayerSelection,\n PlayerCombination,\n StreamInfo,\n PlaybackMode,\n} from \"@livepeer-frameworks/player-core\";\n\nexport interface UsePlayerSelectionOptions {\n /** Stream info to compute selections for */\n streamInfo: StreamInfo | null;\n /** Playback mode override */\n playbackMode?: PlaybackMode;\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport interface UsePlayerSelectionReturn {\n /** Current best selection (null if no compatible player) */\n selection: PlayerSelection | null;\n /** All player+source combinations with scores */\n combinations: PlayerCombination[];\n /** Whether initial computation has completed */\n ready: boolean;\n /** Force recomputation (invalidates cache) */\n refresh: () => void;\n}\n\n/**\n * Subscribe to player selection changes from a PlayerManager.\n *\n * This hook uses the event system in PlayerManager, which means:\n * - Initial computation happens once when streamInfo is provided\n * - Updates only fire when selection actually changes (different player+source)\n * - No render spam from React strict mode or frequent re-renders\n *\n * @example\n * ```tsx\n * const { selection, combinations, ready } = usePlayerSelection(globalPlayerManager, {\n * streamInfo,\n * playbackMode: 'auto',\n * });\n *\n * if (!ready) return <Loading />;\n * if (!selection) return <NoPlayerAvailable />;\n *\n * return <div>Selected: {selection.player} + {selection.source.type}</div>;\n * ```\n */\nexport function usePlayerSelection(\n manager: PlayerManager,\n options: UsePlayerSelectionOptions\n): UsePlayerSelectionReturn {\n const { streamInfo, playbackMode, debug } = options;\n\n const [selection, setSelection] = useState<PlayerSelection | null>(null);\n const [combinations, setCombinations] = useState<PlayerCombination[]>([]);\n const [ready, setReady] = useState(false);\n\n // Subscribe to events\n useEffect(() => {\n const unsubSelection = manager.on(\"selection-changed\", (sel) => {\n if (debug) {\n console.log(\"[usePlayerSelection] Selection changed:\", sel?.player, sel?.source?.type);\n }\n setSelection(sel);\n });\n\n const unsubCombos = manager.on(\"combinations-updated\", (combos) => {\n if (debug) {\n console.log(\"[usePlayerSelection] Combinations updated:\", combos.length);\n }\n setCombinations(combos);\n setReady(true);\n });\n\n return () => {\n unsubSelection();\n unsubCombos();\n };\n }, [manager, debug]);\n\n // Trigger initial computation when streamInfo changes\n useEffect(() => {\n if (!streamInfo) {\n setSelection(null);\n setCombinations([]);\n setReady(false);\n return;\n }\n\n // This will use cache if available, or compute + emit events if not\n manager.getAllCombinations(streamInfo, playbackMode);\n }, [manager, streamInfo, playbackMode]);\n\n // Manual refresh function\n const refresh = useCallback(() => {\n if (!streamInfo) return;\n manager.invalidateCache();\n manager.getAllCombinations(streamInfo, playbackMode);\n }, [manager, streamInfo, playbackMode]);\n\n return {\n selection,\n combinations,\n ready,\n refresh,\n };\n}\n"],"names":["usePlayerSelection","manager","options","streamInfo","playbackMode","debug","_useState","useState","_useState2","_slicedToArray","selection","setSelection","_useState3","_useState4","combinations","setCombinations","_useState5","_useState6","ready","setReady","useEffect","unsubSelection","on","sel","_sel$source","console","log","player","source","type","unsubCombos","combos","length","getAllCombinations","refresh","useCallback","invalidateCache"],"mappings":"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"}
@@ -0,0 +1,358 @@
1
+ import { slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { useState, useRef, useCallback, useEffect } from 'react';
3
+
4
+ /**
5
+ * Parse MistServer error string into StreamStatus enum
6
+ */
7
+ function parseErrorToStatus(error) {
8
+ var lowerError = error.toLowerCase();
9
+ if (lowerError.includes("offline")) return "OFFLINE";
10
+ if (lowerError.includes("initializing")) return "INITIALIZING";
11
+ if (lowerError.includes("booting")) return "BOOTING";
12
+ if (lowerError.includes("waiting for data")) return "WAITING_FOR_DATA";
13
+ if (lowerError.includes("shutting down")) return "SHUTTING_DOWN";
14
+ if (lowerError.includes("invalid")) return "INVALID";
15
+ return "ERROR";
16
+ }
17
+ /**
18
+ * Get human-readable message for stream status
19
+ */
20
+ function getStatusMessage(status, percentage) {
21
+ switch (status) {
22
+ case "ONLINE":
23
+ return "Stream is online";
24
+ case "OFFLINE":
25
+ return "Stream is offline";
26
+ case "INITIALIZING":
27
+ return percentage !== undefined ? "Initializing... ".concat(Math.round(percentage * 10) / 10, "%") : "Stream is initializing";
28
+ case "BOOTING":
29
+ return "Stream is starting up";
30
+ case "WAITING_FOR_DATA":
31
+ return "Waiting for stream data";
32
+ case "SHUTTING_DOWN":
33
+ return "Stream is shutting down";
34
+ case "INVALID":
35
+ return "Stream status is invalid";
36
+ case "ERROR":
37
+ default:
38
+ return "Stream error";
39
+ }
40
+ }
41
+ /**
42
+ * Initial stream state
43
+ */
44
+ var initialState = {
45
+ status: "OFFLINE",
46
+ isOnline: false,
47
+ message: "Connecting...",
48
+ lastUpdate: 0
49
+ };
50
+ function useStreamState(options) {
51
+ var mistBaseUrl = options.mistBaseUrl,
52
+ streamName = options.streamName,
53
+ _options$pollInterval = options.pollInterval,
54
+ pollInterval = _options$pollInterval === void 0 ? 3000 : _options$pollInterval,
55
+ _options$enabled = options.enabled,
56
+ enabled = _options$enabled === void 0 ? true : _options$enabled,
57
+ _options$useWebSocket = options.useWebSocket,
58
+ useWebSocket = _options$useWebSocket === void 0 ? true : _options$useWebSocket,
59
+ _options$debug = options.debug,
60
+ debug = _options$debug === void 0 ? false : _options$debug;
61
+ var _useState = useState(initialState),
62
+ _useState2 = _slicedToArray(_useState, 2),
63
+ state = _useState2[0],
64
+ setState = _useState2[1];
65
+ var _useState3 = useState(false),
66
+ _useState4 = _slicedToArray(_useState3, 2),
67
+ socketReady = _useState4[0],
68
+ setSocketReady = _useState4[1];
69
+ var wsRef = useRef(null);
70
+ var pollTimeoutRef = useRef(null);
71
+ var wsTimeoutRef = useRef(null);
72
+ var mountedRef = useRef(true);
73
+ // MistPlayer-style WebSocket timeout (5 seconds)
74
+ var WS_TIMEOUT_MS = 5000;
75
+ /**
76
+ * Process MistServer response data
77
+ */
78
+ var processStreamInfo = useCallback(function (data) {
79
+ if (!mountedRef.current) return;
80
+ if (data.error) {
81
+ // Stream has an error state - preserve previous streamInfo (track data)
82
+ var status = parseErrorToStatus(data.error);
83
+ var message = data.on_error || getStatusMessage(status, data.perc);
84
+ setState(function (prev) {
85
+ return {
86
+ status: status,
87
+ isOnline: false,
88
+ message: message,
89
+ percentage: data.perc,
90
+ lastUpdate: Date.now(),
91
+ error: data.error,
92
+ streamInfo: prev.streamInfo // Preserve track data through error states
93
+ };
94
+ });
95
+ } else {
96
+ // Stream is online with valid metadata
97
+ // Merge new data with existing streamInfo to preserve source/tracks from initial fetch
98
+ // WebSocket updates may not include source array - only status updates
99
+ setState(function (prev) {
100
+ var _prev$streamInfo, _prev$streamInfo2, _data$meta, _prev$streamInfo3;
101
+ var mergedStreamInfo = _objectSpread2(_objectSpread2(_objectSpread2({}, prev.streamInfo), data), {}, {
102
+ // Override with new data
103
+ // Explicitly preserve source if not in new data
104
+ source: data.source || ((_prev$streamInfo = prev.streamInfo) === null || _prev$streamInfo === void 0 ? void 0 : _prev$streamInfo.source),
105
+ // Merge meta to preserve tracks
106
+ meta: _objectSpread2(_objectSpread2(_objectSpread2({}, (_prev$streamInfo2 = prev.streamInfo) === null || _prev$streamInfo2 === void 0 ? void 0 : _prev$streamInfo2.meta), data.meta), {}, {
107
+ // Preserve tracks if not in new data
108
+ tracks: ((_data$meta = data.meta) === null || _data$meta === void 0 ? void 0 : _data$meta.tracks) || ((_prev$streamInfo3 = prev.streamInfo) === null || _prev$streamInfo3 === void 0 || (_prev$streamInfo3 = _prev$streamInfo3.meta) === null || _prev$streamInfo3 === void 0 ? void 0 : _prev$streamInfo3.tracks)
109
+ })
110
+ });
111
+ return {
112
+ status: "ONLINE",
113
+ isOnline: true,
114
+ message: "Stream is online",
115
+ lastUpdate: Date.now(),
116
+ streamInfo: mergedStreamInfo
117
+ };
118
+ });
119
+ }
120
+ }, []);
121
+ /**
122
+ * HTTP polling fallback
123
+ * Adds metaeverywhere=1 and inclzero=1 like MistPlayer
124
+ */
125
+ var pollHttp = useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
126
+ var baseUrl, url, response, text, jsonpMatch, data, _t;
127
+ return _regenerator().w(function (_context) {
128
+ while (1) switch (_context.p = _context.n) {
129
+ case 0:
130
+ if (!(!mountedRef.current || !enabled)) {
131
+ _context.n = 1;
132
+ break;
133
+ }
134
+ return _context.a(2);
135
+ case 1:
136
+ _context.p = 1;
137
+ // Build URL with MistPlayer-style params
138
+ baseUrl = "".concat(mistBaseUrl.replace(/\/$/, ""), "/json_").concat(encodeURIComponent(streamName), ".js");
139
+ url = "".concat(baseUrl, "?metaeverywhere=1&inclzero=1");
140
+ _context.n = 2;
141
+ return fetch(url, {
142
+ method: "GET",
143
+ headers: {
144
+ Accept: "application/json"
145
+ }
146
+ });
147
+ case 2:
148
+ response = _context.v;
149
+ if (response.ok) {
150
+ _context.n = 3;
151
+ break;
152
+ }
153
+ throw new Error("HTTP ".concat(response.status));
154
+ case 3:
155
+ _context.n = 4;
156
+ return response.text();
157
+ case 4:
158
+ text = _context.v;
159
+ // Strip JSONP callback if present (use [\s\S]* instead of /s flag for ES5 compat)
160
+ jsonpMatch = text.match(/^[^(]+\(([\s\S]*)\);?$/);
161
+ if (jsonpMatch) {
162
+ text = jsonpMatch[1];
163
+ }
164
+ data = JSON.parse(text);
165
+ processStreamInfo(data);
166
+ _context.n = 7;
167
+ break;
168
+ case 5:
169
+ _context.p = 5;
170
+ _t = _context.v;
171
+ if (mountedRef.current) {
172
+ _context.n = 6;
173
+ break;
174
+ }
175
+ return _context.a(2);
176
+ case 6:
177
+ setState(function (prev) {
178
+ return _objectSpread2(_objectSpread2({}, prev), {}, {
179
+ status: "ERROR",
180
+ isOnline: false,
181
+ message: _t instanceof Error ? _t.message : "Connection failed",
182
+ lastUpdate: Date.now(),
183
+ error: _t instanceof Error ? _t.message : "Unknown error"
184
+ });
185
+ });
186
+ case 7:
187
+ // Schedule next poll
188
+ if (mountedRef.current && enabled && !useWebSocket) {
189
+ pollTimeoutRef.current = setTimeout(pollHttp, pollInterval);
190
+ }
191
+ case 8:
192
+ return _context.a(2);
193
+ }
194
+ }, _callee, null, [[1, 5]]);
195
+ })), [mistBaseUrl, streamName, enabled, useWebSocket, pollInterval, processStreamInfo]);
196
+ /**
197
+ * WebSocket connection with MistPlayer-style 5-second timeout
198
+ */
199
+ var connectWebSocket = useCallback(function () {
200
+ if (!mountedRef.current || !enabled || !useWebSocket) return;
201
+ // Clean up existing connection and timeout
202
+ if (wsTimeoutRef.current) {
203
+ clearTimeout(wsTimeoutRef.current);
204
+ wsTimeoutRef.current = null;
205
+ }
206
+ if (wsRef.current) {
207
+ wsRef.current.close();
208
+ wsRef.current = null;
209
+ }
210
+ try {
211
+ // Convert http(s) to ws(s)
212
+ var wsUrl = mistBaseUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:").replace(/\/$/, "");
213
+ // Build URL with MistPlayer-style params
214
+ var url = "".concat(wsUrl, "/json_").concat(encodeURIComponent(streamName), ".js?metaeverywhere=1&inclzero=1");
215
+ var ws = new WebSocket(url);
216
+ wsRef.current = ws;
217
+ // MistPlayer-style timeout: if no message within 5 seconds, fall back to HTTP
218
+ wsTimeoutRef.current = setTimeout(function () {
219
+ if (ws.readyState <= WebSocket.OPEN) {
220
+ if (debug) {
221
+ console.debug("[useStreamState] WebSocket timeout (5s), falling back to HTTP polling");
222
+ }
223
+ ws.close();
224
+ pollHttp();
225
+ }
226
+ }, WS_TIMEOUT_MS);
227
+ ws.onopen = function () {
228
+ if (debug) {
229
+ console.debug("[useStreamState] WebSocket connected");
230
+ }
231
+ setSocketReady(true);
232
+ };
233
+ ws.onmessage = function (event) {
234
+ // Clear timeout on first message
235
+ if (wsTimeoutRef.current) {
236
+ clearTimeout(wsTimeoutRef.current);
237
+ wsTimeoutRef.current = null;
238
+ }
239
+ try {
240
+ var data = JSON.parse(event.data);
241
+ processStreamInfo(data);
242
+ } catch (e) {
243
+ console.warn("[useStreamState] Failed to parse WebSocket message:", e);
244
+ }
245
+ };
246
+ ws.onerror = function (_event) {
247
+ console.warn("[useStreamState] WebSocket error, falling back to HTTP polling");
248
+ if (wsTimeoutRef.current) {
249
+ clearTimeout(wsTimeoutRef.current);
250
+ wsTimeoutRef.current = null;
251
+ }
252
+ ws.close();
253
+ };
254
+ ws.onclose = function () {
255
+ wsRef.current = null;
256
+ setSocketReady(false);
257
+ if (!mountedRef.current || !enabled) return;
258
+ // Fallback to HTTP polling or reconnect
259
+ if (debug) {
260
+ console.debug("[useStreamState] WebSocket closed, starting HTTP polling");
261
+ }
262
+ pollHttp();
263
+ };
264
+ } catch (error) {
265
+ console.warn("[useStreamState] WebSocket connection failed:", error);
266
+ // Fallback to HTTP polling
267
+ pollHttp();
268
+ }
269
+ }, [mistBaseUrl, streamName, enabled, useWebSocket, debug, processStreamInfo, pollHttp]);
270
+ /**
271
+ * Manual refetch function
272
+ */
273
+ var refetch = useCallback(function () {
274
+ var _wsRef$current;
275
+ if (useWebSocket && ((_wsRef$current = wsRef.current) === null || _wsRef$current === void 0 ? void 0 : _wsRef$current.readyState) === WebSocket.OPEN) {
276
+ // WebSocket will receive updates automatically
277
+ return;
278
+ }
279
+ pollHttp();
280
+ }, [useWebSocket, pollHttp]);
281
+ /**
282
+ * Setup connection on mount and when options change
283
+ * Always do initial HTTP poll to get full stream info (including sources),
284
+ * then connect WebSocket for real-time status updates.
285
+ * MistServer WebSocket updates may not include source array.
286
+ */
287
+ useEffect(function () {
288
+ mountedRef.current = true;
289
+ if (!enabled || !mistBaseUrl || !streamName) {
290
+ setState(initialState);
291
+ return;
292
+ }
293
+ // Reset state when stream changes
294
+ setState(_objectSpread2(_objectSpread2({}, initialState), {}, {
295
+ message: "Connecting...",
296
+ lastUpdate: Date.now()
297
+ }));
298
+ // Always do initial HTTP poll to get full data (including sources)
299
+ // Then connect WebSocket for real-time updates
300
+ var initializeConnection = /*#__PURE__*/function () {
301
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
302
+ return _regenerator().w(function (_context2) {
303
+ while (1) switch (_context2.n) {
304
+ case 0:
305
+ _context2.n = 1;
306
+ return pollHttp();
307
+ case 1:
308
+ // Then connect WebSocket for status updates (if enabled)
309
+ if (useWebSocket && mountedRef.current) {
310
+ connectWebSocket();
311
+ }
312
+ case 2:
313
+ return _context2.a(2);
314
+ }
315
+ }, _callee2);
316
+ }));
317
+ return function initializeConnection() {
318
+ return _ref2.apply(this, arguments);
319
+ };
320
+ }();
321
+ initializeConnection();
322
+ return function () {
323
+ // Set mounted=false FIRST before any other cleanup
324
+ mountedRef.current = false;
325
+ if (debug) {
326
+ console.debug("[useStreamState] cleanup starting, mountedRef set to false");
327
+ }
328
+ // Cleanup WebSocket timeout
329
+ if (wsTimeoutRef.current) {
330
+ clearTimeout(wsTimeoutRef.current);
331
+ wsTimeoutRef.current = null;
332
+ }
333
+ // Cleanup WebSocket - remove handlers BEFORE closing to prevent onclose callback
334
+ if (wsRef.current) {
335
+ // Detach handlers first to prevent onclose from triggering pollHttp
336
+ wsRef.current.onclose = null;
337
+ wsRef.current.onerror = null;
338
+ wsRef.current.onmessage = null;
339
+ wsRef.current.onopen = null;
340
+ wsRef.current.close();
341
+ wsRef.current = null;
342
+ }
343
+ // Cleanup polling timeout
344
+ if (pollTimeoutRef.current) {
345
+ clearTimeout(pollTimeoutRef.current);
346
+ pollTimeoutRef.current = null;
347
+ }
348
+ };
349
+ }, [enabled, mistBaseUrl, streamName, useWebSocket, debug, connectWebSocket, pollHttp]);
350
+ return _objectSpread2(_objectSpread2({}, state), {}, {
351
+ refetch: refetch,
352
+ socketRef: wsRef,
353
+ socketReady: socketReady
354
+ });
355
+ }
356
+
357
+ export { useStreamState };
358
+ //# sourceMappingURL=useStreamState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStreamState.js","sources":["../../../../src/hooks/useStreamState.ts"],"sourcesContent":["import { useEffect, useState, useRef, useCallback } from \"react\";\nimport type { UseStreamStateOptions, StreamState, StreamStatus, MistStreamInfo } from \"../types\";\n\n/**\n * Parse MistServer error string into StreamStatus enum\n */\nfunction parseErrorToStatus(error: string): StreamStatus {\n const lowerError = error.toLowerCase();\n\n if (lowerError.includes(\"offline\")) return \"OFFLINE\";\n if (lowerError.includes(\"initializing\")) return \"INITIALIZING\";\n if (lowerError.includes(\"booting\")) return \"BOOTING\";\n if (lowerError.includes(\"waiting for data\")) return \"WAITING_FOR_DATA\";\n if (lowerError.includes(\"shutting down\")) return \"SHUTTING_DOWN\";\n if (lowerError.includes(\"invalid\")) return \"INVALID\";\n\n return \"ERROR\";\n}\n\n/**\n * Get human-readable message for stream status\n */\nfunction getStatusMessage(status: StreamStatus, percentage?: number): string {\n switch (status) {\n case \"ONLINE\":\n return \"Stream is online\";\n case \"OFFLINE\":\n return \"Stream is offline\";\n case \"INITIALIZING\":\n return percentage !== undefined\n ? `Initializing... ${Math.round(percentage * 10) / 10}%`\n : \"Stream is initializing\";\n case \"BOOTING\":\n return \"Stream is starting up\";\n case \"WAITING_FOR_DATA\":\n return \"Waiting for stream data\";\n case \"SHUTTING_DOWN\":\n return \"Stream is shutting down\";\n case \"INVALID\":\n return \"Stream status is invalid\";\n case \"ERROR\":\n default:\n return \"Stream error\";\n }\n}\n\n/**\n * Initial stream state\n */\nconst initialState: StreamState = {\n status: \"OFFLINE\",\n isOnline: false,\n message: \"Connecting...\",\n lastUpdate: 0,\n};\n\n/**\n * Hook to poll MistServer for stream status via WebSocket or HTTP\n *\n * Uses native MistServer protocol:\n * - WebSocket: ws://{baseUrl}/json_{streamName}.js\n * - HTTP fallback: GET {baseUrl}/json_{streamName}.js\n *\n * @example\n * ```tsx\n * const { status, isOnline, message } = useStreamState({\n * mistBaseUrl: 'https://mist.example.com',\n * streamName: 'pk_...', // playbackId (view key)\n * pollInterval: 3000,\n * });\n * ```\n */\nexport interface UseStreamStateReturn extends StreamState {\n /** Manual refetch function */\n refetch: () => void;\n /** WebSocket reference for sharing with MistReporter */\n socketRef: React.RefObject<WebSocket | null>;\n /** True when WebSocket is connected and ready (triggers re-render) */\n socketReady: boolean;\n}\n\nexport function useStreamState(options: UseStreamStateOptions): UseStreamStateReturn {\n const {\n mistBaseUrl,\n streamName,\n pollInterval = 3000,\n enabled = true,\n useWebSocket = true,\n debug = false,\n } = options;\n\n const [state, setState] = useState<StreamState>(initialState);\n const [socketReady, setSocketReady] = useState(false);\n const wsRef = useRef<WebSocket | null>(null);\n const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const wsTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const mountedRef = useRef(true);\n\n // MistPlayer-style WebSocket timeout (5 seconds)\n const WS_TIMEOUT_MS = 5000;\n\n /**\n * Process MistServer response data\n */\n const processStreamInfo = useCallback((data: MistStreamInfo) => {\n if (!mountedRef.current) return;\n\n if (data.error) {\n // Stream has an error state - preserve previous streamInfo (track data)\n const status = parseErrorToStatus(data.error);\n const message = data.on_error || getStatusMessage(status, data.perc);\n\n setState((prev) => ({\n status,\n isOnline: false,\n message,\n percentage: data.perc,\n lastUpdate: Date.now(),\n error: data.error,\n streamInfo: prev.streamInfo, // Preserve track data through error states\n }));\n } else {\n // Stream is online with valid metadata\n // Merge new data with existing streamInfo to preserve source/tracks from initial fetch\n // WebSocket updates may not include source array - only status updates\n setState((prev) => {\n const mergedStreamInfo: MistStreamInfo = {\n ...prev.streamInfo, // Keep existing source/meta if present\n ...data, // Override with new data\n // Explicitly preserve source if not in new data\n source: data.source || prev.streamInfo?.source,\n // Merge meta to preserve tracks\n meta: {\n ...prev.streamInfo?.meta,\n ...data.meta,\n // Preserve tracks if not in new data\n tracks: data.meta?.tracks || prev.streamInfo?.meta?.tracks,\n },\n };\n\n return {\n status: \"ONLINE\",\n isOnline: true,\n message: \"Stream is online\",\n lastUpdate: Date.now(),\n streamInfo: mergedStreamInfo,\n };\n });\n }\n }, []);\n\n /**\n * HTTP polling fallback\n * Adds metaeverywhere=1 and inclzero=1 like MistPlayer\n */\n const pollHttp = useCallback(async () => {\n if (!mountedRef.current || !enabled) return;\n\n try {\n // Build URL with MistPlayer-style params\n const baseUrl = `${mistBaseUrl.replace(/\\/$/, \"\")}/json_${encodeURIComponent(streamName)}.js`;\n const url = `${baseUrl}?metaeverywhere=1&inclzero=1`;\n const response = await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n // MistServer returns JSON with potential JSONP wrapper\n let text = await response.text();\n // Strip JSONP callback if present (use [\\s\\S]* instead of /s flag for ES5 compat)\n const jsonpMatch = text.match(/^[^(]+\\(([\\s\\S]*)\\);?$/);\n if (jsonpMatch) {\n text = jsonpMatch[1];\n }\n\n const data = JSON.parse(text) as MistStreamInfo;\n processStreamInfo(data);\n } catch (error) {\n if (!mountedRef.current) return;\n\n setState((prev) => ({\n ...prev,\n status: \"ERROR\",\n isOnline: false,\n message: error instanceof Error ? error.message : \"Connection failed\",\n lastUpdate: Date.now(),\n error: error instanceof Error ? error.message : \"Unknown error\",\n }));\n }\n\n // Schedule next poll\n if (mountedRef.current && enabled && !useWebSocket) {\n pollTimeoutRef.current = setTimeout(pollHttp, pollInterval);\n }\n }, [mistBaseUrl, streamName, enabled, useWebSocket, pollInterval, processStreamInfo]);\n\n /**\n * WebSocket connection with MistPlayer-style 5-second timeout\n */\n const connectWebSocket = useCallback(() => {\n if (!mountedRef.current || !enabled || !useWebSocket) return;\n\n // Clean up existing connection and timeout\n if (wsTimeoutRef.current) {\n clearTimeout(wsTimeoutRef.current);\n wsTimeoutRef.current = null;\n }\n if (wsRef.current) {\n wsRef.current.close();\n wsRef.current = null;\n }\n\n try {\n // Convert http(s) to ws(s)\n const wsUrl = mistBaseUrl\n .replace(/^http:/, \"ws:\")\n .replace(/^https:/, \"wss:\")\n .replace(/\\/$/, \"\");\n\n // Build URL with MistPlayer-style params\n const url = `${wsUrl}/json_${encodeURIComponent(streamName)}.js?metaeverywhere=1&inclzero=1`;\n const ws = new WebSocket(url);\n wsRef.current = ws;\n\n // MistPlayer-style timeout: if no message within 5 seconds, fall back to HTTP\n wsTimeoutRef.current = setTimeout(() => {\n if (ws.readyState <= WebSocket.OPEN) {\n if (debug) {\n console.debug(\"[useStreamState] WebSocket timeout (5s), falling back to HTTP polling\");\n }\n ws.close();\n pollHttp();\n }\n }, WS_TIMEOUT_MS);\n\n ws.onopen = () => {\n if (debug) {\n console.debug(\"[useStreamState] WebSocket connected\");\n }\n setSocketReady(true);\n };\n\n ws.onmessage = (event) => {\n // Clear timeout on first message\n if (wsTimeoutRef.current) {\n clearTimeout(wsTimeoutRef.current);\n wsTimeoutRef.current = null;\n }\n\n try {\n const data = JSON.parse(event.data) as MistStreamInfo;\n processStreamInfo(data);\n } catch (e) {\n console.warn(\"[useStreamState] Failed to parse WebSocket message:\", e);\n }\n };\n\n ws.onerror = (_event) => {\n console.warn(\"[useStreamState] WebSocket error, falling back to HTTP polling\");\n if (wsTimeoutRef.current) {\n clearTimeout(wsTimeoutRef.current);\n wsTimeoutRef.current = null;\n }\n ws.close();\n };\n\n ws.onclose = () => {\n wsRef.current = null;\n setSocketReady(false);\n\n if (!mountedRef.current || !enabled) return;\n\n // Fallback to HTTP polling or reconnect\n if (debug) {\n console.debug(\"[useStreamState] WebSocket closed, starting HTTP polling\");\n }\n pollHttp();\n };\n } catch (error) {\n console.warn(\"[useStreamState] WebSocket connection failed:\", error);\n // Fallback to HTTP polling\n pollHttp();\n }\n }, [mistBaseUrl, streamName, enabled, useWebSocket, debug, processStreamInfo, pollHttp]);\n\n /**\n * Manual refetch function\n */\n const refetch = useCallback(() => {\n if (useWebSocket && wsRef.current?.readyState === WebSocket.OPEN) {\n // WebSocket will receive updates automatically\n return;\n }\n pollHttp();\n }, [useWebSocket, pollHttp]);\n\n /**\n * Setup connection on mount and when options change\n * Always do initial HTTP poll to get full stream info (including sources),\n * then connect WebSocket for real-time status updates.\n * MistServer WebSocket updates may not include source array.\n */\n useEffect(() => {\n mountedRef.current = true;\n\n if (!enabled || !mistBaseUrl || !streamName) {\n setState(initialState);\n return;\n }\n\n // Reset state when stream changes\n setState({\n ...initialState,\n message: \"Connecting...\",\n lastUpdate: Date.now(),\n });\n\n // Always do initial HTTP poll to get full data (including sources)\n // Then connect WebSocket for real-time updates\n const initializeConnection = async () => {\n // First HTTP poll to get complete stream info\n await pollHttp();\n\n // Then connect WebSocket for status updates (if enabled)\n if (useWebSocket && mountedRef.current) {\n connectWebSocket();\n }\n };\n\n initializeConnection();\n\n return () => {\n // Set mounted=false FIRST before any other cleanup\n mountedRef.current = false;\n if (debug) {\n console.debug(\"[useStreamState] cleanup starting, mountedRef set to false\");\n }\n\n // Cleanup WebSocket timeout\n if (wsTimeoutRef.current) {\n clearTimeout(wsTimeoutRef.current);\n wsTimeoutRef.current = null;\n }\n\n // Cleanup WebSocket - remove handlers BEFORE closing to prevent onclose callback\n if (wsRef.current) {\n // Detach handlers first to prevent onclose from triggering pollHttp\n wsRef.current.onclose = null;\n wsRef.current.onerror = null;\n wsRef.current.onmessage = null;\n wsRef.current.onopen = null;\n wsRef.current.close();\n wsRef.current = null;\n }\n\n // Cleanup polling timeout\n if (pollTimeoutRef.current) {\n clearTimeout(pollTimeoutRef.current);\n pollTimeoutRef.current = null;\n }\n };\n }, [enabled, mistBaseUrl, streamName, useWebSocket, debug, connectWebSocket, pollHttp]);\n\n return {\n ...state,\n refetch,\n socketRef: wsRef,\n socketReady,\n };\n}\n\nexport default useStreamState;\n"],"names":["parseErrorToStatus","error","lowerError","toLowerCase","includes","getStatusMessage","status","percentage","undefined","concat","Math","round","initialState","isOnline","message","lastUpdate","useStreamState","options","mistBaseUrl","streamName","_options$pollInterval","pollInterval","_options$enabled","enabled","_options$useWebSocket","useWebSocket","_options$debug","debug","_useState","useState","_useState2","_slicedToArray","state","setState","_useState3","_useState4","socketReady","setSocketReady","wsRef","useRef","pollTimeoutRef","wsTimeoutRef","mountedRef","WS_TIMEOUT_MS","processStreamInfo","useCallback","data","current","on_error","perc","prev","Date","now","streamInfo","_prev$streamInfo","_prev$streamInfo2","_data$meta","_prev$streamInfo3","mergedStreamInfo","_objectSpread","source","meta","tracks","pollHttp","_asyncToGenerator","_regenerator","m","_callee","baseUrl","url","response","text","jsonpMatch","_t","w","_context","p","n","a","replace","encodeURIComponent","fetch","method","headers","Accept","v","ok","Error","match","JSON","parse","setTimeout","connectWebSocket","clearTimeout","close","wsUrl","ws","WebSocket","readyState","OPEN","console","onopen","onmessage","event","e","warn","onerror","_event","onclose","refetch","_wsRef$current","useEffect","initializeConnection","_ref2","_callee2","_context2","apply","arguments","socketRef"],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import { useRef, useEffect, useCallback } from 'react';
2
+ import { TelemetryReporter } from '@livepeer-frameworks/player-core';
3
+
4
+ /**
5
+ * Hook to send telemetry data to a server
6
+ *
7
+ * Reports playback metrics at configurable intervals:
8
+ * - Current time and duration
9
+ * - Buffer health
10
+ * - Stall count and duration
11
+ * - Quality score and bitrate
12
+ * - Frame decode/drop stats
13
+ * - Errors encountered
14
+ *
15
+ * Uses navigator.sendBeacon() for reliable reporting on page unload.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const { sessionId, recordError } = useTelemetry({
20
+ * enabled: true,
21
+ * endpoint: '/api/telemetry',
22
+ * interval: 5000,
23
+ * videoElement,
24
+ * contentId: 'pk_...', // playbackId (view key)
25
+ * contentType: 'live',
26
+ * playerType: 'hlsjs',
27
+ * protocol: 'HLS',
28
+ * getQuality: () => qualityMonitor.getCurrentQuality(),
29
+ * });
30
+ *
31
+ * // Record custom error
32
+ * recordError('NETWORK_ERROR', 'Connection lost');
33
+ * ```
34
+ */
35
+ function useTelemetry(options) {
36
+ var _reporterRef$current$3, _reporterRef$current7;
37
+ var enabled = options.enabled,
38
+ endpoint = options.endpoint,
39
+ interval = options.interval,
40
+ authToken = options.authToken,
41
+ batchSize = options.batchSize,
42
+ videoElement = options.videoElement,
43
+ contentId = options.contentId,
44
+ contentType = options.contentType,
45
+ playerType = options.playerType,
46
+ protocol = options.protocol,
47
+ getQuality = options.getQuality;
48
+ var reporterRef = useRef(null);
49
+ // Create reporter instance
50
+ useEffect(function () {
51
+ if (!enabled || !endpoint || !contentId) {
52
+ var _reporterRef$current;
53
+ (_reporterRef$current = reporterRef.current) === null || _reporterRef$current === void 0 || _reporterRef$current.stop();
54
+ reporterRef.current = null;
55
+ return;
56
+ }
57
+ reporterRef.current = new TelemetryReporter({
58
+ endpoint: endpoint,
59
+ authToken: authToken,
60
+ interval: interval,
61
+ batchSize: batchSize,
62
+ contentId: contentId,
63
+ contentType: contentType,
64
+ playerType: playerType,
65
+ protocol: protocol
66
+ });
67
+ return function () {
68
+ var _reporterRef$current2;
69
+ (_reporterRef$current2 = reporterRef.current) === null || _reporterRef$current2 === void 0 || _reporterRef$current2.stop();
70
+ reporterRef.current = null;
71
+ };
72
+ }, [enabled, endpoint, authToken, interval, batchSize, contentId, contentType, playerType, protocol]);
73
+ // Start/stop reporting when video element changes
74
+ useEffect(function () {
75
+ if (!enabled || !videoElement || !reporterRef.current) {
76
+ return;
77
+ }
78
+ reporterRef.current.start(videoElement, getQuality);
79
+ return function () {
80
+ var _reporterRef$current3;
81
+ (_reporterRef$current3 = reporterRef.current) === null || _reporterRef$current3 === void 0 || _reporterRef$current3.stop();
82
+ };
83
+ }, [enabled, videoElement, getQuality]);
84
+ /**
85
+ * Record a custom error
86
+ */
87
+ var recordError = useCallback(function (code, message) {
88
+ var _reporterRef$current4;
89
+ (_reporterRef$current4 = reporterRef.current) === null || _reporterRef$current4 === void 0 || _reporterRef$current4.recordError(code, message);
90
+ }, []);
91
+ /**
92
+ * Get current session ID
93
+ */
94
+ var getSessionId = useCallback(function () {
95
+ var _reporterRef$current$, _reporterRef$current5;
96
+ return (_reporterRef$current$ = (_reporterRef$current5 = reporterRef.current) === null || _reporterRef$current5 === void 0 ? void 0 : _reporterRef$current5.getSessionId()) !== null && _reporterRef$current$ !== void 0 ? _reporterRef$current$ : null;
97
+ }, []);
98
+ /**
99
+ * Check if telemetry is active
100
+ */
101
+ var isActive = useCallback(function () {
102
+ var _reporterRef$current$2, _reporterRef$current6;
103
+ return (_reporterRef$current$2 = (_reporterRef$current6 = reporterRef.current) === null || _reporterRef$current6 === void 0 ? void 0 : _reporterRef$current6.isActive()) !== null && _reporterRef$current$2 !== void 0 ? _reporterRef$current$2 : false;
104
+ }, []);
105
+ return {
106
+ /** Session ID for this playback session */
107
+ sessionId: (_reporterRef$current$3 = (_reporterRef$current7 = reporterRef.current) === null || _reporterRef$current7 === void 0 ? void 0 : _reporterRef$current7.getSessionId()) !== null && _reporterRef$current$3 !== void 0 ? _reporterRef$current$3 : null,
108
+ /** Record a custom error */
109
+ recordError: recordError,
110
+ /** Get current session ID */
111
+ getSessionId: getSessionId,
112
+ /** Check if telemetry is active */
113
+ isActive: isActive
114
+ };
115
+ }
116
+
117
+ export { useTelemetry };
118
+ //# sourceMappingURL=useTelemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTelemetry.js","sources":["../../../../src/hooks/useTelemetry.ts"],"sourcesContent":["import { useEffect, useRef, useCallback } from \"react\";\nimport {\n TelemetryReporter,\n type TelemetryOptions,\n type PlaybackQuality,\n type ContentType,\n} from \"@livepeer-frameworks/player-core\";\n\nexport interface UseTelemetryOptions extends TelemetryOptions {\n /** Video element to monitor */\n videoElement: HTMLVideoElement | null;\n /** Content ID being played */\n contentId: string;\n /** Content type */\n contentType: ContentType;\n /** Player type name */\n playerType: string;\n /** Protocol being used */\n protocol: string;\n /** Optional quality getter function */\n getQuality?: () => PlaybackQuality | null;\n}\n\n/**\n * Hook to send telemetry data to a server\n *\n * Reports playback metrics at configurable intervals:\n * - Current time and duration\n * - Buffer health\n * - Stall count and duration\n * - Quality score and bitrate\n * - Frame decode/drop stats\n * - Errors encountered\n *\n * Uses navigator.sendBeacon() for reliable reporting on page unload.\n *\n * @example\n * ```tsx\n * const { sessionId, recordError } = useTelemetry({\n * enabled: true,\n * endpoint: '/api/telemetry',\n * interval: 5000,\n * videoElement,\n * contentId: 'pk_...', // playbackId (view key)\n * contentType: 'live',\n * playerType: 'hlsjs',\n * protocol: 'HLS',\n * getQuality: () => qualityMonitor.getCurrentQuality(),\n * });\n *\n * // Record custom error\n * recordError('NETWORK_ERROR', 'Connection lost');\n * ```\n */\nexport function useTelemetry(options: UseTelemetryOptions) {\n const {\n enabled,\n endpoint,\n interval,\n authToken,\n batchSize,\n videoElement,\n contentId,\n contentType,\n playerType,\n protocol,\n getQuality,\n } = options;\n\n const reporterRef = useRef<TelemetryReporter | null>(null);\n\n // Create reporter instance\n useEffect(() => {\n if (!enabled || !endpoint || !contentId) {\n reporterRef.current?.stop();\n reporterRef.current = null;\n return;\n }\n\n reporterRef.current = new TelemetryReporter({\n endpoint,\n authToken,\n interval,\n batchSize,\n contentId,\n contentType,\n playerType,\n protocol,\n });\n\n return () => {\n reporterRef.current?.stop();\n reporterRef.current = null;\n };\n }, [\n enabled,\n endpoint,\n authToken,\n interval,\n batchSize,\n contentId,\n contentType,\n playerType,\n protocol,\n ]);\n\n // Start/stop reporting when video element changes\n useEffect(() => {\n if (!enabled || !videoElement || !reporterRef.current) {\n return;\n }\n\n reporterRef.current.start(videoElement, getQuality);\n\n return () => {\n reporterRef.current?.stop();\n };\n }, [enabled, videoElement, getQuality]);\n\n /**\n * Record a custom error\n */\n const recordError = useCallback((code: string, message: string) => {\n reporterRef.current?.recordError(code, message);\n }, []);\n\n /**\n * Get current session ID\n */\n const getSessionId = useCallback((): string | null => {\n return reporterRef.current?.getSessionId() ?? null;\n }, []);\n\n /**\n * Check if telemetry is active\n */\n const isActive = useCallback((): boolean => {\n return reporterRef.current?.isActive() ?? false;\n }, []);\n\n return {\n /** Session ID for this playback session */\n sessionId: reporterRef.current?.getSessionId() ?? null,\n /** Record a custom error */\n recordError,\n /** Get current session ID */\n getSessionId,\n /** Check if telemetry is active */\n isActive,\n };\n}\n\nexport default useTelemetry;\n"],"names":["useTelemetry","options","_reporterRef$current$3","_reporterRef$current7","enabled","endpoint","interval","authToken","batchSize","videoElement","contentId","contentType","playerType","protocol","getQuality","reporterRef","useRef","useEffect","_reporterRef$current","current","stop","TelemetryReporter","_reporterRef$current2","start","_reporterRef$current3","recordError","useCallback","code","message","_reporterRef$current4","getSessionId","_reporterRef$current$","_reporterRef$current5","isActive","_reporterRef$current$2","_reporterRef$current6","sessionId"],"mappings":"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"}