@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.
- package/dist/assets/emoji-data.json +19371 -0
- package/dist/components/chat/chat-box.js +319 -0
- package/dist/components/chat/chat-message.js +87 -0
- package/dist/components/chat/chat.js +150 -0
- package/dist/components/chat/emoji-suggestions.js +35 -0
- package/dist/components/chat/mention-suggestions.js +42 -0
- package/dist/components/chat/mod-view.js +112 -0
- package/dist/components/chat/system-message.js +19 -0
- package/dist/components/dashboard/chat-panel.js +38 -0
- package/dist/components/dashboard/header.js +80 -0
- package/dist/components/dashboard/index.js +14 -0
- package/dist/components/dashboard/information-widget.js +234 -0
- package/dist/components/dashboard/mod-actions.js +71 -0
- package/dist/components/dashboard/problems.js +74 -0
- package/dist/components/icons/bluesky-icon.js +9 -0
- package/dist/components/keep-awake.js +7 -0
- package/dist/components/keep-awake.native.js +16 -0
- package/dist/components/mobile-player/fullscreen.js +76 -0
- package/dist/components/mobile-player/fullscreen.native.js +141 -0
- package/dist/components/mobile-player/player.js +94 -0
- package/dist/components/mobile-player/props.js +2 -0
- package/dist/components/mobile-player/shared.js +54 -0
- package/dist/components/mobile-player/ui/autoplay-button.js +68 -0
- package/dist/components/mobile-player/ui/countdown.js +83 -0
- package/dist/components/mobile-player/ui/index.js +12 -0
- package/dist/components/mobile-player/ui/input.js +42 -0
- package/dist/components/mobile-player/ui/metrics.js +44 -0
- package/dist/components/mobile-player/ui/report-modal.js +90 -0
- package/dist/components/mobile-player/ui/streamer-context-menu.js +7 -0
- package/dist/components/mobile-player/ui/streamer-loading-overlay.js +104 -0
- package/dist/components/mobile-player/ui/viewer-context-menu.js +51 -0
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js +49 -0
- package/dist/components/mobile-player/ui/viewers.js +23 -0
- package/dist/components/mobile-player/use-webrtc.js +243 -0
- package/dist/components/mobile-player/video-async.native.js +276 -0
- package/dist/components/mobile-player/video-retry.js +29 -0
- package/dist/components/mobile-player/video.js +475 -0
- package/dist/components/mobile-player/video.native.js +56 -0
- package/dist/components/mobile-player/webrtc-diagnostics.js +110 -0
- package/dist/components/mobile-player/webrtc-primitives.js +27 -0
- package/dist/components/mobile-player/webrtc-primitives.native.js +8 -0
- package/dist/components/share/sharesheet.js +91 -0
- package/dist/components/ui/button.js +223 -0
- package/dist/components/ui/dialog.js +206 -0
- package/dist/components/ui/dropdown.js +172 -0
- package/dist/components/ui/icons.js +25 -0
- package/dist/components/ui/index.js +34 -0
- package/dist/components/ui/info-box.js +31 -0
- package/dist/components/ui/info-row.js +23 -0
- package/dist/components/ui/input.js +205 -0
- package/dist/components/ui/loader.js +10 -0
- package/dist/components/ui/primitives/button.js +125 -0
- package/dist/components/ui/primitives/input.js +206 -0
- package/dist/components/ui/primitives/modal.js +206 -0
- package/dist/components/ui/primitives/text.js +292 -0
- package/dist/components/ui/resizeable.js +121 -0
- package/dist/components/ui/slider.js +5 -0
- package/dist/components/ui/text.js +177 -0
- package/dist/components/ui/textarea.js +19 -0
- package/dist/components/ui/toast.js +175 -0
- package/dist/components/ui/view.js +252 -0
- package/dist/hooks/index.js +14 -0
- package/dist/hooks/useAvatars.js +35 -0
- package/dist/hooks/useCameraToggle.js +12 -0
- package/dist/hooks/useKeyboard.js +36 -0
- package/dist/hooks/useKeyboardSlide.js +14 -0
- package/dist/hooks/useLivestreamInfo.js +69 -0
- package/dist/hooks/useOuterAndInnerDimensions.js +30 -0
- package/dist/hooks/usePlayerDimensions.js +22 -0
- package/dist/hooks/usePointerDevice.js +71 -0
- package/dist/hooks/useSegmentDimensions.js +17 -0
- package/dist/hooks/useSegmentTiming.js +65 -0
- package/dist/index.js +34 -0
- package/dist/lib/browser.js +35 -0
- package/dist/lib/facet.js +92 -0
- package/dist/lib/system-messages.js +101 -0
- package/dist/lib/theme/atoms.js +646 -0
- package/dist/lib/theme/atoms.types.js +6 -0
- package/dist/lib/theme/index.js +35 -0
- package/dist/lib/theme/theme.js +256 -0
- package/dist/lib/theme/tokens.js +659 -0
- package/dist/lib/utils.js +105 -0
- package/dist/livestream-provider/index.js +30 -0
- package/dist/livestream-provider/websocket.js +45 -0
- package/dist/livestream-store/chat.js +308 -0
- package/dist/livestream-store/context.js +5 -0
- package/dist/livestream-store/index.js +7 -0
- package/dist/livestream-store/livestream-state.js +2 -0
- package/dist/livestream-store/livestream-store.js +58 -0
- package/dist/livestream-store/problems.js +76 -0
- package/dist/livestream-store/stream-key.js +88 -0
- package/dist/livestream-store/websocket-consumer.js +94 -0
- package/dist/player-store/context.js +5 -0
- package/dist/player-store/index.js +9 -0
- package/dist/player-store/player-provider.js +58 -0
- package/dist/player-store/player-state.js +25 -0
- package/dist/player-store/player-store.js +201 -0
- package/dist/player-store/single-player-provider.js +121 -0
- package/dist/streamplace-provider/context.js +5 -0
- package/dist/streamplace-provider/index.js +20 -0
- package/dist/streamplace-provider/poller.js +49 -0
- package/dist/streamplace-provider/xrpc.js +0 -0
- package/dist/streamplace-store/block.js +65 -0
- package/dist/streamplace-store/index.js +6 -0
- package/dist/streamplace-store/stream.js +247 -0
- package/dist/streamplace-store/streamplace-store.js +47 -0
- package/dist/streamplace-store/user.js +52 -0
- package/dist/streamplace-store/xrpc.js +15 -0
- package/dist/ui/index.js +79 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +5 -4
- package/src/components/chat/chat-box.tsx +3 -0
- package/src/components/chat/mod-view.tsx +39 -5
- package/src/components/mobile-player/fullscreen.tsx +2 -0
- package/src/components/mobile-player/ui/autoplay-button.tsx +86 -0
- package/src/components/mobile-player/ui/index.ts +1 -0
- package/src/components/mobile-player/video.tsx +11 -1
- package/src/livestream-store/chat.tsx +22 -0
- package/src/player-store/player-provider.tsx +2 -1
- package/src/player-store/player-state.tsx +6 -0
- package/src/player-store/player-store.tsx +4 -0
- 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,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,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
|
+
}
|