@streamplace/components 0.7.18 → 0.7.21

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 (122) hide show
  1. package/dist/assets/emoji-data.json +19371 -0
  2. package/dist/components/chat/chat-box.js +319 -0
  3. package/dist/components/chat/chat-message.js +87 -0
  4. package/dist/components/chat/chat.js +150 -0
  5. package/dist/components/chat/emoji-suggestions.js +35 -0
  6. package/dist/components/chat/mention-suggestions.js +42 -0
  7. package/dist/components/chat/mod-view.js +112 -0
  8. package/dist/components/chat/system-message.js +19 -0
  9. package/dist/components/dashboard/chat-panel.js +38 -0
  10. package/dist/components/dashboard/header.js +80 -0
  11. package/dist/components/dashboard/index.js +14 -0
  12. package/dist/components/dashboard/information-widget.js +234 -0
  13. package/dist/components/dashboard/mod-actions.js +71 -0
  14. package/dist/components/dashboard/problems.js +74 -0
  15. package/dist/components/icons/bluesky-icon.js +9 -0
  16. package/dist/components/keep-awake.js +7 -0
  17. package/dist/components/keep-awake.native.js +16 -0
  18. package/dist/components/mobile-player/fullscreen.js +76 -0
  19. package/dist/components/mobile-player/fullscreen.native.js +141 -0
  20. package/dist/components/mobile-player/player.js +94 -0
  21. package/dist/components/mobile-player/props.js +2 -0
  22. package/dist/components/mobile-player/shared.js +54 -0
  23. package/dist/components/mobile-player/ui/autoplay-button.js +68 -0
  24. package/dist/components/mobile-player/ui/countdown.js +83 -0
  25. package/dist/components/mobile-player/ui/index.js +12 -0
  26. package/dist/components/mobile-player/ui/input.js +42 -0
  27. package/dist/components/mobile-player/ui/metrics.js +44 -0
  28. package/dist/components/mobile-player/ui/report-modal.js +90 -0
  29. package/dist/components/mobile-player/ui/streamer-context-menu.js +7 -0
  30. package/dist/components/mobile-player/ui/streamer-loading-overlay.js +104 -0
  31. package/dist/components/mobile-player/ui/viewer-context-menu.js +51 -0
  32. package/dist/components/mobile-player/ui/viewer-loading-overlay.js +49 -0
  33. package/dist/components/mobile-player/ui/viewers.js +23 -0
  34. package/dist/components/mobile-player/use-webrtc.js +243 -0
  35. package/dist/components/mobile-player/video-async.native.js +276 -0
  36. package/dist/components/mobile-player/video-retry.js +29 -0
  37. package/dist/components/mobile-player/video.js +475 -0
  38. package/dist/components/mobile-player/video.native.js +56 -0
  39. package/dist/components/mobile-player/webrtc-diagnostics.js +110 -0
  40. package/dist/components/mobile-player/webrtc-primitives.js +27 -0
  41. package/dist/components/mobile-player/webrtc-primitives.native.js +8 -0
  42. package/dist/components/share/sharesheet.js +91 -0
  43. package/dist/components/ui/button.js +223 -0
  44. package/dist/components/ui/dialog.js +206 -0
  45. package/dist/components/ui/dropdown.js +172 -0
  46. package/dist/components/ui/icons.js +25 -0
  47. package/dist/components/ui/index.js +34 -0
  48. package/dist/components/ui/info-box.js +31 -0
  49. package/dist/components/ui/info-row.js +23 -0
  50. package/dist/components/ui/input.js +205 -0
  51. package/dist/components/ui/loader.js +10 -0
  52. package/dist/components/ui/primitives/button.js +125 -0
  53. package/dist/components/ui/primitives/input.js +206 -0
  54. package/dist/components/ui/primitives/modal.js +206 -0
  55. package/dist/components/ui/primitives/text.js +292 -0
  56. package/dist/components/ui/resizeable.js +121 -0
  57. package/dist/components/ui/slider.js +5 -0
  58. package/dist/components/ui/text.js +177 -0
  59. package/dist/components/ui/textarea.js +19 -0
  60. package/dist/components/ui/toast.js +175 -0
  61. package/dist/components/ui/view.js +252 -0
  62. package/dist/hooks/index.js +14 -0
  63. package/dist/hooks/useAvatars.js +35 -0
  64. package/dist/hooks/useCameraToggle.js +12 -0
  65. package/dist/hooks/useKeyboard.js +36 -0
  66. package/dist/hooks/useKeyboardSlide.js +14 -0
  67. package/dist/hooks/useLivestreamInfo.js +69 -0
  68. package/dist/hooks/useOuterAndInnerDimensions.js +30 -0
  69. package/dist/hooks/usePlayerDimensions.js +22 -0
  70. package/dist/hooks/usePointerDevice.js +71 -0
  71. package/dist/hooks/useSegmentDimensions.js +17 -0
  72. package/dist/hooks/useSegmentTiming.js +65 -0
  73. package/dist/index.js +34 -0
  74. package/dist/lib/browser.js +35 -0
  75. package/dist/lib/facet.js +92 -0
  76. package/dist/lib/system-messages.js +101 -0
  77. package/dist/lib/theme/atoms.js +646 -0
  78. package/dist/lib/theme/atoms.types.js +6 -0
  79. package/dist/lib/theme/index.js +35 -0
  80. package/dist/lib/theme/theme.js +256 -0
  81. package/dist/lib/theme/tokens.js +659 -0
  82. package/dist/lib/utils.js +105 -0
  83. package/dist/livestream-provider/index.js +30 -0
  84. package/dist/livestream-provider/websocket.js +45 -0
  85. package/dist/livestream-store/chat.js +308 -0
  86. package/dist/livestream-store/context.js +5 -0
  87. package/dist/livestream-store/index.js +7 -0
  88. package/dist/livestream-store/livestream-state.js +2 -0
  89. package/dist/livestream-store/livestream-store.js +58 -0
  90. package/dist/livestream-store/problems.js +76 -0
  91. package/dist/livestream-store/stream-key.js +88 -0
  92. package/dist/livestream-store/websocket-consumer.js +94 -0
  93. package/dist/player-store/context.js +5 -0
  94. package/dist/player-store/index.js +9 -0
  95. package/dist/player-store/player-provider.js +58 -0
  96. package/dist/player-store/player-state.js +25 -0
  97. package/dist/player-store/player-store.js +201 -0
  98. package/dist/player-store/single-player-provider.js +121 -0
  99. package/dist/streamplace-provider/context.js +5 -0
  100. package/dist/streamplace-provider/index.js +20 -0
  101. package/dist/streamplace-provider/poller.js +49 -0
  102. package/dist/streamplace-provider/xrpc.js +0 -0
  103. package/dist/streamplace-store/block.js +65 -0
  104. package/dist/streamplace-store/index.js +6 -0
  105. package/dist/streamplace-store/stream.js +247 -0
  106. package/dist/streamplace-store/streamplace-store.js +47 -0
  107. package/dist/streamplace-store/user.js +52 -0
  108. package/dist/streamplace-store/xrpc.js +15 -0
  109. package/dist/ui/index.js +79 -0
  110. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  111. package/package.json +5 -4
  112. package/src/components/chat/chat-box.tsx +3 -0
  113. package/src/components/chat/mod-view.tsx +39 -5
  114. package/src/components/mobile-player/fullscreen.tsx +2 -0
  115. package/src/components/mobile-player/ui/autoplay-button.tsx +86 -0
  116. package/src/components/mobile-player/ui/index.ts +1 -0
  117. package/src/components/mobile-player/video.tsx +11 -1
  118. package/src/livestream-store/chat.tsx +22 -0
  119. package/src/player-store/player-provider.tsx +2 -1
  120. package/src/player-store/player-state.tsx +6 -0
  121. package/src/player-store/player-store.tsx +4 -0
  122. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStreamKey = void 0;
4
+ const crypto_1 = require("@atproto/crypto");
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const accounts_1 = require("viem/accounts");
8
+ const browser_1 = require("../lib/browser");
9
+ const xrpc_1 = require("../streamplace-store/xrpc");
10
+ const livestream_store_1 = require("./livestream-store");
11
+ const useStreamKey = () => {
12
+ const pdsAgent = (0, xrpc_1.usePDSAgent)();
13
+ const streamKey = (0, livestream_store_1.useLivestreamStore)((state) => state.streamKey);
14
+ const setStreamKey = (0, livestream_store_1.useLivestreamStore)((state) => state.setStreamKey);
15
+ const [key, setKey] = (0, react_1.useState)(streamKey ? JSON.parse(streamKey) : null);
16
+ const [error, setError] = (0, react_1.useState)(null);
17
+ (0, react_1.useEffect)(() => {
18
+ if (key)
19
+ return; // already have key
20
+ const generateKey = async () => {
21
+ if (!pdsAgent) {
22
+ setError("PDS Agent is not available");
23
+ return;
24
+ }
25
+ let did = pdsAgent.did;
26
+ if (!did) {
27
+ setError("PDS Agent did is not available (not logged in?)");
28
+ return;
29
+ }
30
+ const keypair = await crypto_1.Secp256k1Keypair.create({ exportable: true });
31
+ const exportedKey = await keypair.export();
32
+ const didBytes = new TextEncoder().encode(did);
33
+ const combinedKey = new Uint8Array([...exportedKey, ...didBytes]);
34
+ const multibaseKey = (0, crypto_1.bytesToMultibase)(combinedKey, "base58btc");
35
+ const hexKey = Array.from(exportedKey)
36
+ .map((b) => b.toString(16).padStart(2, "0"))
37
+ .join("");
38
+ const account = (0, accounts_1.privateKeyToAccount)(`0x${hexKey}`);
39
+ const newKey = {
40
+ privateKey: multibaseKey,
41
+ did: keypair.did(),
42
+ address: account.address.toLowerCase(),
43
+ };
44
+ let platform = react_native_1.Platform.OS;
45
+ if (react_native_1.Platform.OS === "web" &&
46
+ typeof window !== "undefined" &&
47
+ window.navigator) {
48
+ if (window.navigator.userAgent.includes("streamplace-desktop")) {
49
+ platform = "Desktop";
50
+ }
51
+ else {
52
+ platform = (0, browser_1.getBrowserName)(window.navigator.userAgent);
53
+ if (platform !== "unknown") {
54
+ platform = platform;
55
+ }
56
+ }
57
+ }
58
+ else if (platform === "android") {
59
+ platform = "Android";
60
+ }
61
+ else if (platform === "ios") {
62
+ platform = "iOS";
63
+ }
64
+ else if (platform === "macos") {
65
+ platform = "macOS";
66
+ }
67
+ else if (platform === "windows") {
68
+ platform = "Windows";
69
+ }
70
+ const record = {
71
+ signingKey: keypair.did(),
72
+ createdAt: new Date().toISOString(),
73
+ createdBy: "Streamplace on " + platform,
74
+ };
75
+ await pdsAgent.com.atproto.repo.createRecord({
76
+ repo: did,
77
+ collection: "place.stream.key",
78
+ record,
79
+ });
80
+ setStreamKey(JSON.stringify(newKey));
81
+ setKey(newKey);
82
+ };
83
+ generateKey();
84
+ // eslint-disable-next-line react-hooks/exhaustive-deps
85
+ }, [key, setStreamKey]);
86
+ return { streamKey: key, error };
87
+ };
88
+ exports.useStreamKey = useStreamKey;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleWebSocketMessages = void 0;
4
+ const api_1 = require("@atproto/api");
5
+ const streamplace_1 = require("streamplace");
6
+ const system_messages_1 = require("../lib/system-messages");
7
+ const chat_1 = require("./chat");
8
+ const problems_1 = require("./problems");
9
+ const MAX_RECENT_SEGMENTS = 10;
10
+ const handleWebSocketMessages = (state, messages) => {
11
+ for (const message of messages) {
12
+ if (streamplace_1.PlaceStreamLivestream.isLivestreamView(message)) {
13
+ const newLivestream = message;
14
+ const oldLivestream = state.livestream;
15
+ // check if this is actually new
16
+ if (!oldLivestream || oldLivestream.uri !== newLivestream.uri) {
17
+ const streamTitle = newLivestream.record.title || "something cool!";
18
+ const systemMessage = system_messages_1.SystemMessages.streamStart(streamTitle);
19
+ // set proper times
20
+ systemMessage.indexedAt = newLivestream.indexedAt;
21
+ systemMessage.record.createdAt = newLivestream.record.createdAt;
22
+ state = (0, chat_1.reduceChat)(state, [systemMessage], []);
23
+ }
24
+ state = {
25
+ ...state,
26
+ livestream: newLivestream,
27
+ };
28
+ }
29
+ else if (streamplace_1.PlaceStreamLivestream.isViewerCount(message)) {
30
+ state = {
31
+ ...state,
32
+ viewers: message.count,
33
+ };
34
+ }
35
+ else if (streamplace_1.PlaceStreamChatDefs.isMessageView(message)) {
36
+ // Explicitly map MessageView to MessageViewHydrated
37
+ const hydrated = {
38
+ uri: message.uri,
39
+ cid: message.cid,
40
+ author: message.author,
41
+ record: message.record,
42
+ indexedAt: message.indexedAt,
43
+ chatProfile: message.chatProfile,
44
+ replyTo: message.replyTo,
45
+ deleted: message.deleted,
46
+ };
47
+ state = (0, chat_1.reduceChat)(state, [hydrated], [], []);
48
+ }
49
+ else if (streamplace_1.PlaceStreamSegment.isRecord(message)) {
50
+ const newRecentSegments = [...state.recentSegments];
51
+ newRecentSegments.unshift(message);
52
+ if (newRecentSegments.length > MAX_RECENT_SEGMENTS) {
53
+ newRecentSegments.pop();
54
+ }
55
+ state = {
56
+ ...state,
57
+ segment: message,
58
+ recentSegments: newRecentSegments,
59
+ problems: (0, problems_1.findProblems)(newRecentSegments),
60
+ };
61
+ }
62
+ else if (streamplace_1.PlaceStreamDefs.isBlockView(message)) {
63
+ const block = message;
64
+ state = (0, chat_1.reduceChat)(state, [], [block], []);
65
+ }
66
+ else if (streamplace_1.PlaceStreamDefs.isRenditions(message)) {
67
+ state = {
68
+ ...state,
69
+ renditions: message.renditions,
70
+ };
71
+ }
72
+ else if (api_1.AppBskyActorDefs.isProfileViewBasic(message)) {
73
+ state = {
74
+ ...state,
75
+ profile: message,
76
+ };
77
+ }
78
+ else if (streamplace_1.PlaceStreamChatGate.isRecord(message)) {
79
+ const hideRecord = message;
80
+ const hiddenMessageUri = hideRecord.hiddenMessage;
81
+ const newPendingHides = [...state.pendingHides];
82
+ if (!newPendingHides.includes(hiddenMessageUri)) {
83
+ newPendingHides.push(hiddenMessageUri);
84
+ }
85
+ state = {
86
+ ...state,
87
+ pendingHides: newPendingHides,
88
+ };
89
+ state = (0, chat_1.reduceChat)(state, [], [], [hiddenMessageUri]);
90
+ }
91
+ }
92
+ return (0, chat_1.reduceChat)(state, [], [], []);
93
+ };
94
+ exports.handleWebSocketMessages = handleWebSocketMessages;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlayerContext = void 0;
4
+ const react_1 = require("react");
5
+ exports.PlayerContext = (0, react_1.createContext)(null);
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ // barrel file :)
5
+ tslib_1.__exportStar(require("./context"), exports);
6
+ tslib_1.__exportStar(require("./player-provider"), exports);
7
+ tslib_1.__exportStar(require("./player-state"), exports);
8
+ tslib_1.__exportStar(require("./player-store"), exports);
9
+ tslib_1.__exportStar(require("./single-player-provider"), exports);
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlayerProvider = void 0;
4
+ exports.withPlayerProvider = withPlayerProvider;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const crypto_1 = require("crypto");
7
+ const react_1 = require("react");
8
+ const context_1 = require("./context");
9
+ const player_store_1 = require("./player-store");
10
+ const PlayerProvider = ({ children, initialPlayers = [], defaultId = Math.random().toString(36).slice(8), }) => {
11
+ const [players, setPlayers] = (0, react_1.useState)(() => {
12
+ // Initialize with any initial player IDs provided
13
+ const initialPlayerStores = {};
14
+ for (const playerId of initialPlayers) {
15
+ initialPlayerStores[playerId] = (0, player_store_1.makePlayerStore)(playerId);
16
+ }
17
+ // Always create at least one player by default
18
+ if (initialPlayers.length === 0) {
19
+ initialPlayerStores[defaultId] = (0, player_store_1.makePlayerStore)(defaultId);
20
+ }
21
+ return initialPlayerStores;
22
+ });
23
+ const createPlayer = (0, react_1.useCallback)((id) => {
24
+ const playerId = id || (0, crypto_1.randomUUID)();
25
+ const playerStore = (0, player_store_1.makePlayerStore)(playerId);
26
+ setPlayers((prev) => ({
27
+ ...prev,
28
+ [playerId]: playerStore,
29
+ }));
30
+ return playerId;
31
+ }, []);
32
+ const removePlayer = (0, react_1.useCallback)((id) => {
33
+ setPlayers((prev) => {
34
+ // Don't remove the last player
35
+ if (Object.keys(prev).length <= 1) {
36
+ console.warn("Cannot remove the last player");
37
+ return prev;
38
+ }
39
+ const newPlayers = { ...prev };
40
+ delete newPlayers[id];
41
+ return newPlayers;
42
+ });
43
+ }, []);
44
+ const contextValue = (0, react_1.useMemo)(() => ({
45
+ players,
46
+ createPlayer,
47
+ removePlayer,
48
+ }), [players, createPlayer, removePlayer]);
49
+ return ((0, jsx_runtime_1.jsx)(context_1.PlayerContext.Provider, { value: contextValue, children: children }));
50
+ };
51
+ exports.PlayerProvider = PlayerProvider;
52
+ // HOC to wrap components that need player context
53
+ function withPlayerProvider(Component) {
54
+ return function WithPlayerProvider(props) {
55
+ const { initialPlayers, ...componentProps } = props;
56
+ return ((0, jsx_runtime_1.jsx)(exports.PlayerProvider, { initialPlayers: initialPlayers, children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps }) }));
57
+ };
58
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IngestMediaSource = exports.PlayerStatus = exports.PlayerProtocol = void 0;
4
+ var PlayerProtocol;
5
+ (function (PlayerProtocol) {
6
+ PlayerProtocol["WEBRTC"] = "webrtc";
7
+ PlayerProtocol["HLS"] = "hls";
8
+ PlayerProtocol["PROGRESSIVE_MP4"] = "progressive-mp4";
9
+ PlayerProtocol["PROGRESSIVE_WEBM"] = "progressive-webm";
10
+ })(PlayerProtocol || (exports.PlayerProtocol = PlayerProtocol = {}));
11
+ var PlayerStatus;
12
+ (function (PlayerStatus) {
13
+ PlayerStatus["START"] = "start";
14
+ PlayerStatus["PLAYING"] = "playing";
15
+ PlayerStatus["STALLED"] = "stalled";
16
+ PlayerStatus["SUSPEND"] = "suspend";
17
+ PlayerStatus["WAITING"] = "waiting";
18
+ PlayerStatus["PAUSE"] = "pause";
19
+ PlayerStatus["MUTE"] = "mute";
20
+ })(PlayerStatus || (exports.PlayerStatus = PlayerStatus = {}));
21
+ var IngestMediaSource;
22
+ (function (IngestMediaSource) {
23
+ IngestMediaSource["USER"] = "user";
24
+ IngestMediaSource["DISPLAY"] = "display";
25
+ })(IngestMediaSource || (exports.IngestMediaSource = IngestMediaSource = {}));
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useOffline = exports.intoPlayerProtocol = exports.usePlayerProtocol = exports.makePlayerStore = void 0;
4
+ exports.usePlayerContext = usePlayerContext;
5
+ exports.getPlayerStoreById = getPlayerStoreById;
6
+ exports.getFirstPlayerID = getFirstPlayerID;
7
+ exports.getPlayerStoreFromContext = getPlayerStoreFromContext;
8
+ exports.usePlayerStore = usePlayerStore;
9
+ const react_1 = require("react");
10
+ const zustand_1 = require("zustand");
11
+ const livestream_store_1 = require("../livestream-store");
12
+ const context_1 = require("./context");
13
+ const player_state_1 = require("./player-state");
14
+ const makePlayerStore = (id) => {
15
+ return (0, zustand_1.createStore)()((set) => ({
16
+ id: id || Math.random().toString(36).slice(8),
17
+ selectedRendition: "source",
18
+ setSelectedRendition: (rendition) => set((state) => ({ ...state, selectedRendition: rendition })),
19
+ protocol: player_state_1.PlayerProtocol.WEBRTC,
20
+ setProtocol: (protocol) => set((state) => ({ ...state, protocol: protocol })),
21
+ src: "",
22
+ setSrc: (src) => set(() => ({ src })),
23
+ ingestStarting: false,
24
+ setIngestStarting: (ingestStarting) => set(() => ({ ingestStarting })),
25
+ ingestMediaSource: undefined,
26
+ setIngestMediaSource: (ingestMediaSource) => set(() => ({ ingestMediaSource })),
27
+ ingestCamera: "user",
28
+ setIngestCamera: (ingestCamera) => set(() => ({ ingestCamera })),
29
+ ingestConnectionState: null,
30
+ setIngestConnectionState: (ingestConnectionState) => set(() => ({ ingestConnectionState })),
31
+ ingestAutoStart: false,
32
+ setIngestAutoStart: (ingestAutoStart) => set(() => ({ ingestAutoStart })),
33
+ ingestStarted: null,
34
+ setIngestStarted: (timestamp) => set(() => ({ ingestStarted: timestamp })),
35
+ muted: false,
36
+ setMuted: (isMuted) => set(() => ({ muted: isMuted, muteWasForced: false })),
37
+ volume: 1.0,
38
+ setVolume: (volume) => set(() => ({ volume, muteWasForced: false })),
39
+ fullscreen: false,
40
+ setFullscreen: (isFullscreen) => set(() => ({ fullscreen: isFullscreen })),
41
+ status: player_state_1.PlayerStatus.START,
42
+ setStatus: (status) => set(() => ({ status })),
43
+ playTime: 0,
44
+ setPlayTime: (playTime) => set(() => ({ playTime })),
45
+ videoRef: undefined,
46
+ setVideoRef: (videoRef) => set(() => ({ videoRef })),
47
+ pipMode: false,
48
+ setPipMode: (pipMode) => set(() => ({ pipMode })),
49
+ // Picture-in-Picture action function (set by player component)
50
+ pipAction: undefined,
51
+ setPipAction: (action) => set(() => ({ pipAction: action })),
52
+ // Player element width/height setters for global sync
53
+ playerWidth: undefined,
54
+ setPlayerWidth: (playerWidth) => set(() => ({ playerWidth })),
55
+ playerHeight: undefined,
56
+ setPlayerHeight: (playerHeight) => set(() => ({ playerHeight })),
57
+ // * Whether mute was forced by the browser or not for autoplay
58
+ // * Will get set to 'false' if the user has interacted with the volume
59
+ muteWasForced: false,
60
+ setMuteWasForced: (muteWasForced) => set(() => ({ muteWasForced })),
61
+ autoplayFailed: false,
62
+ setAutoplayFailed: (autoplayFailed) => set(() => ({ autoplayFailed })),
63
+ embedded: false,
64
+ setEmbedded: (embedded) => set(() => ({ embedded })),
65
+ showControls: true,
66
+ controlsTimeout: undefined,
67
+ setShowControls: (showControls) => set({ showControls, controlsTimeout: undefined }),
68
+ telemetry: true,
69
+ setTelemetry: (telemetry) => set(() => ({ telemetry })),
70
+ ingestLive: false,
71
+ setIngestLive: (ingestLive) => set(() => ({ ingestLive })),
72
+ reportingURL: null,
73
+ setReportingURL: (reportingURL) => set(() => ({ reportingURL })),
74
+ playerEvent: async (url, time, eventType, meta) => set((x) => {
75
+ const data = {
76
+ time: time,
77
+ playerId: x.id,
78
+ eventType: eventType,
79
+ meta: {
80
+ ...meta,
81
+ },
82
+ };
83
+ try {
84
+ // fetch url from sp provider
85
+ const reportingURL = x.reportingURL ?? `${url}/api/player-event`;
86
+ fetch(reportingURL, {
87
+ method: "POST",
88
+ body: JSON.stringify(data),
89
+ });
90
+ }
91
+ catch (e) {
92
+ console.error("error sending player telemetry", e);
93
+ }
94
+ return {};
95
+ }),
96
+ // Clear the controls timeout, if it exists.
97
+ // Should be called on player unmount.
98
+ clearControlsTimeout: () => set((state) => {
99
+ if (state.controlsTimeout) {
100
+ clearTimeout(state.controlsTimeout);
101
+ }
102
+ return { controlsTimeout: undefined };
103
+ }),
104
+ setUserInteraction: () => set((p) => {
105
+ // controls timeout
106
+ if (p.controlsTimeout) {
107
+ clearTimeout(p.controlsTimeout);
108
+ }
109
+ let controlsTimeout = setTimeout(() => p.setShowControls(false), 1000);
110
+ return { showControls: true, controlsTimeout };
111
+ }),
112
+ showDebugInfo: false,
113
+ setShowDebugInfo: (showDebugInfo) => set(() => ({ showDebugInfo })),
114
+ modMessage: null,
115
+ setModMessage: (modMessage) => set(() => ({ modMessage })),
116
+ reportModalOpen: false,
117
+ setReportModalOpen: (reportModalOpen) => set(() => ({ reportModalOpen })),
118
+ reportSubject: null,
119
+ setReportSubject: (subject) => set(() => ({ reportSubject: subject })),
120
+ }));
121
+ };
122
+ exports.makePlayerStore = makePlayerStore;
123
+ function usePlayerContext() {
124
+ const context = (0, react_1.useContext)(context_1.PlayerContext);
125
+ if (!context) {
126
+ throw new Error("usePlayerContext must be used within a PlayerProvider");
127
+ }
128
+ return context;
129
+ }
130
+ // Get a specific player store by ID
131
+ function getPlayerStoreById(id) {
132
+ const { players } = usePlayerContext();
133
+ const playerStore = players[id];
134
+ if (!playerStore) {
135
+ throw new Error(`No player found with ID: ${id}`);
136
+ }
137
+ return playerStore;
138
+ }
139
+ // Will get the first player ID in the context
140
+ function getFirstPlayerID() {
141
+ const { players } = usePlayerContext();
142
+ const playerIds = Object.keys(players);
143
+ if (playerIds.length === 0) {
144
+ throw new Error("No players found in context");
145
+ }
146
+ return playerIds[0];
147
+ }
148
+ function getPlayerStoreFromContext() {
149
+ console.warn("getPlayerStoreFromContext is deprecated. Use getPlayerStoreById instead.");
150
+ const { players } = usePlayerContext();
151
+ const playerIds = Object.keys(players);
152
+ if (playerIds.length === 0) {
153
+ throw new Error("No players found in context");
154
+ }
155
+ return players[playerIds[0]];
156
+ }
157
+ // Use a specific player store by ID
158
+ // If no ID is provided, it will use the first player in the context
159
+ function usePlayerStore(selector, playerId) {
160
+ if (!playerId) {
161
+ playerId = Object.keys(usePlayerContext().players)[0];
162
+ }
163
+ const store = getPlayerStoreById(playerId);
164
+ return (0, zustand_1.useStore)(store, selector);
165
+ }
166
+ /* Convenience selectors/hooks */
167
+ const usePlayerProtocol = (playerId) => usePlayerStore((x) => [x.protocol, x.setProtocol], playerId);
168
+ exports.usePlayerProtocol = usePlayerProtocol;
169
+ const intoPlayerProtocol = (protocol) => {
170
+ switch (protocol) {
171
+ case "hls":
172
+ return player_state_1.PlayerProtocol.HLS;
173
+ case "progressive-mp4":
174
+ return player_state_1.PlayerProtocol.PROGRESSIVE_MP4;
175
+ case "progressive-webm":
176
+ return player_state_1.PlayerProtocol.PROGRESSIVE_WEBM;
177
+ default:
178
+ return player_state_1.PlayerProtocol.WEBRTC;
179
+ }
180
+ };
181
+ exports.intoPlayerProtocol = intoPlayerProtocol;
182
+ // returns true if the livestream has been offline for more than 10 seconds and we're not playing
183
+ const useOffline = () => {
184
+ const status = usePlayerStore((x) => x.status);
185
+ const segment = (0, livestream_store_1.useLivestreamStore)((x) => x.segment);
186
+ const [now, setNow] = (0, react_1.useState)(Date.now());
187
+ (0, react_1.useEffect)(() => {
188
+ const interval = setInterval(() => {
189
+ setNow(Date.now());
190
+ }, 500);
191
+ return () => clearInterval(interval);
192
+ }, []);
193
+ if (status === player_state_1.PlayerStatus.PLAYING) {
194
+ return false;
195
+ }
196
+ if (!segment?.startTime) {
197
+ return false;
198
+ }
199
+ return now - Date.parse(segment.startTime) > 10000;
200
+ };
201
+ exports.useOffline = useOffline;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SinglePlayerProvider = void 0;
4
+ exports.useSinglePlayerContext = useSinglePlayerContext;
5
+ exports.useCurrentPlayerId = useCurrentPlayerId;
6
+ exports.useCurrentPlayerStore = useCurrentPlayerStore;
7
+ exports.useCurrentPlayerProtocol = useCurrentPlayerProtocol;
8
+ exports.useCurrentPlayerRendition = useCurrentPlayerRendition;
9
+ exports.useCurrentPlayerIngest = useCurrentPlayerIngest;
10
+ exports.withSinglePlayer = withSinglePlayer;
11
+ const tslib_1 = require("tslib");
12
+ const jsx_runtime_1 = require("react/jsx-runtime");
13
+ const react_1 = tslib_1.__importStar(require("react"));
14
+ const zustand_1 = require("zustand");
15
+ const player_store_1 = require("../player-store");
16
+ const player_state_1 = require("./player-state");
17
+ const SinglePlayerContext = (0, react_1.createContext)(null);
18
+ /**
19
+ * Provider component for a single player that creates a scoped context
20
+ * This allows components to access a specific player's state without passing IDs around
21
+ */
22
+ const SinglePlayerProvider = ({ children, playerId: providedPlayerId, protocol = player_state_1.PlayerProtocol.WEBRTC, rendition = "auto", }) => {
23
+ const { players, createPlayer } = (0, player_store_1.usePlayerContext)();
24
+ // Create or get a player ID
25
+ const playerId = (0, react_1.useMemo)(() => {
26
+ // If a player ID is provided and exists, use it
27
+ if (providedPlayerId && players[providedPlayerId]) {
28
+ return providedPlayerId;
29
+ }
30
+ // If a player ID is provided but doesn't exist, create it
31
+ if (providedPlayerId) {
32
+ return createPlayer(providedPlayerId);
33
+ }
34
+ // Otherwise create a new player
35
+ return createPlayer();
36
+ }, [providedPlayerId, players, createPlayer]);
37
+ // Get the player store
38
+ const playerStore = (0, react_1.useMemo)(() => {
39
+ return players[playerId];
40
+ }, [players, playerId]);
41
+ // Set initial protocol and rendition if provided
42
+ react_1.default.useEffect(() => {
43
+ if (protocol) {
44
+ playerStore.setState((state) => ({
45
+ ...state,
46
+ protocol,
47
+ }));
48
+ }
49
+ if (rendition) {
50
+ playerStore.setState((state) => ({
51
+ ...state,
52
+ selectedRendition: rendition,
53
+ }));
54
+ }
55
+ }, [playerStore, protocol, rendition]);
56
+ // Create context value
57
+ const contextValue = (0, react_1.useMemo)(() => ({
58
+ playerId,
59
+ playerStore,
60
+ }), [playerId, playerStore]);
61
+ return ((0, jsx_runtime_1.jsx)(SinglePlayerContext.Provider, { value: contextValue, children: children }));
62
+ };
63
+ exports.SinglePlayerProvider = SinglePlayerProvider;
64
+ /**
65
+ * Hook to access the current single player context
66
+ */
67
+ function useSinglePlayerContext() {
68
+ const context = (0, react_1.useContext)(SinglePlayerContext);
69
+ if (!context) {
70
+ throw new Error("useSinglePlayerContext must be used within a SinglePlayerProvider");
71
+ }
72
+ return context;
73
+ }
74
+ /**
75
+ * Hook to access the current player ID from the single player context
76
+ */
77
+ function useCurrentPlayerId() {
78
+ const { playerId } = useSinglePlayerContext();
79
+ return playerId;
80
+ }
81
+ /**
82
+ * Hook to access state from the current player without needing to specify the ID
83
+ */
84
+ function useCurrentPlayerStore(selector) {
85
+ const { playerStore } = useSinglePlayerContext();
86
+ return (0, zustand_1.useStore)(playerStore, selector);
87
+ }
88
+ /**
89
+ * Hook to get the protocol of the current player
90
+ */
91
+ function useCurrentPlayerProtocol() {
92
+ return useCurrentPlayerStore((state) => [state.protocol, state.setProtocol]);
93
+ }
94
+ /**
95
+ * Hook to get the selected rendition of the current player
96
+ */
97
+ function useCurrentPlayerRendition() {
98
+ return useCurrentPlayerStore((state) => [state.selectedRendition, state.setSelectedRendition]);
99
+ }
100
+ /**
101
+ * Hook to get the ingest state of the current player
102
+ */
103
+ function useCurrentPlayerIngest() {
104
+ return useCurrentPlayerStore((state) => ({
105
+ starting: state.ingestStarting,
106
+ setStarting: state.setIngestStarting,
107
+ connectionState: state.ingestConnectionState,
108
+ setConnectionState: state.setIngestConnectionState,
109
+ startedTimestamp: state.ingestStarted,
110
+ setStartedTimestamp: state.setIngestStarted,
111
+ }));
112
+ }
113
+ /**
114
+ * HOC to wrap components with a SinglePlayerProvider
115
+ */
116
+ function withSinglePlayer(Component) {
117
+ return function WithSinglePlayer(props) {
118
+ const { playerId, protocol, rendition, ...componentProps } = props;
119
+ return ((0, jsx_runtime_1.jsx)(exports.SinglePlayerProvider, { playerId: playerId, protocol: protocol, rendition: rendition, children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps }) }));
120
+ };
121
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamplaceContext = void 0;
4
+ const react_1 = require("react");
5
+ exports.StreamplaceContext = (0, react_1.createContext)(null);
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamplaceProvider = StreamplaceProvider;
4
+ const tslib_1 = require("tslib");
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const streamplace_store_1 = require("../streamplace-store/streamplace-store");
8
+ const context_1 = require("./context");
9
+ const poller_1 = tslib_1.__importDefault(require("./poller"));
10
+ function StreamplaceProvider({ children, url, oauthSession, }) {
11
+ // todo: handle url changes?
12
+ const store = (0, react_1.useRef)((0, streamplace_store_1.makeStreamplaceStore)({ url })).current;
13
+ (0, react_1.useEffect)(() => {
14
+ store.setState({ url });
15
+ }, [url]);
16
+ (0, react_1.useEffect)(() => {
17
+ store.setState({ oauthSession });
18
+ }, [oauthSession]);
19
+ return ((0, jsx_runtime_1.jsx)(context_1.StreamplaceContext.Provider, { value: { store: store }, children: (0, jsx_runtime_1.jsx)(poller_1.default, { children: children }) }));
20
+ }