Live video on the AT Protocol
1import { bytesToMultibase, Secp256k1Keypair } from "@atproto/crypto";
2import { useEffect, useState } from "react";
3import { Platform } from "react-native";
4import { PlaceStreamKey } from "streamplace";
5import { privateKeyToAccount } from "viem/accounts";
6import { getBrowserName } from "../lib/browser";
7import { usePDSAgent } from "../streamplace-store/xrpc";
8import { useLivestreamStore } from "./livestream-store";
9
10export const useStreamKey = (): {
11 streamKey: {
12 privateKey: string;
13 did: string;
14 address: string;
15 } | null;
16 error: string | null;
17} => {
18 const pdsAgent = usePDSAgent();
19 const streamKey = useLivestreamStore((state) => state.streamKey);
20 const setStreamKey = useLivestreamStore((state) => state.setStreamKey);
21 const [key, setKey] = useState<any>(streamKey ? JSON.parse(streamKey) : null);
22 const [error, setError] = useState<string | null>(null);
23
24 useEffect(() => {
25 if (key) return; // already have key
26
27 const generateKey = async () => {
28 if (!pdsAgent) {
29 setError("PDS Agent is not available");
30 return;
31 }
32 let did = pdsAgent.did;
33 if (!did) {
34 setError("PDS Agent did is not available (not logged in?)");
35 return;
36 }
37
38 const keypair = await Secp256k1Keypair.create({ exportable: true });
39 const exportedKey = await keypair.export();
40 const didBytes = new TextEncoder().encode(did);
41 const combinedKey = new Uint8Array([...exportedKey, ...didBytes]);
42 const multibaseKey = bytesToMultibase(combinedKey, "base58btc");
43 const hexKey = Array.from(exportedKey)
44 .map((b) => b.toString(16).padStart(2, "0"))
45 .join("");
46 const account = privateKeyToAccount(`0x${hexKey}`);
47 const newKey = {
48 privateKey: multibaseKey,
49 did: keypair.did(),
50 address: account.address.toLowerCase(),
51 };
52
53 let platform: string = Platform.OS;
54 if (
55 Platform.OS === "web" &&
56 typeof window !== "undefined" &&
57 window.navigator
58 ) {
59 if (window.navigator.userAgent.includes("streamplace-desktop")) {
60 platform = "Desktop";
61 } else {
62 platform = getBrowserName(window.navigator.userAgent);
63 if (platform !== "unknown") {
64 platform = platform;
65 }
66 }
67 } else if (platform === "android") {
68 platform = "Android";
69 } else if (platform === "ios") {
70 platform = "iOS";
71 } else if (platform === "macos") {
72 platform = "macOS";
73 } else if (platform === "windows") {
74 platform = "Windows";
75 }
76
77 const record: PlaceStreamKey.Record = {
78 $type: "place.stream.key",
79 signingKey: keypair.did(),
80 createdAt: new Date().toISOString(),
81 createdBy: "Streamplace on " + platform,
82 };
83 await pdsAgent.com.atproto.repo.createRecord({
84 repo: did,
85 collection: "place.stream.key",
86 record,
87 });
88
89 setStreamKey(JSON.stringify(newKey));
90 setKey(newKey);
91 };
92
93 generateKey();
94 // eslint-disable-next-line react-hooks/exhaustive-deps
95 }, [key, setStreamKey]);
96
97 return { streamKey: key, error };
98};