diff --git a/js/components/src/components/mobile-player/use-webrtc.tsx b/js/components/src/components/mobile-player/use-webrtc.tsx index e81227e9..18261a4a 100644 --- a/js/components/src/components/mobile-player/use-webrtc.tsx +++ b/js/components/src/components/mobile-player/use-webrtc.tsx @@ -1,10 +1,10 @@ -import { SessionManager } from "@atproto/api/dist/session-manager"; import { useEffect, useRef, useState } from "react"; import * as sdpTransform from "sdp-transform"; import { StreamplaceAgent } from "streamplace"; import { - createAgentForServer, PlayerStatus, + useDID, + useHandle, usePlayerStore, usePossiblyUnauthedPDSAgent, useStreamKey, @@ -19,10 +19,15 @@ export default function useWebRTC( const [stuck, setStuck] = useState(false); const setStatus = usePlayerStore((x) => x.setStatus); let agent = usePossiblyUnauthedPDSAgent(); - const oauthSession = useStreamplaceStore((state) => state.oauthSession); + const myDID = useDID(); + const myHandle = useHandle(); const playbackWorkerUrl = useStreamplaceStore( (state) => state.playbackWorkerUrl, ); + const isOwnStream = !!( + myDID && + (streamer === myDID || streamer === myHandle) + ); const lastChange = useRef(0); @@ -66,7 +71,7 @@ export default function useWebRTC( streamer, undefined, agent, - oauthSession, + isOwnStream, playbackWorkerUrl, ); }); @@ -106,7 +111,7 @@ export default function useWebRTC( clearInterval(handle); peerConnection.close(); }; - }, [streamer, agent, oauthSession, playbackWorkerUrl]); + }, [streamer, agent, isOwnStream, playbackWorkerUrl]); return [mediaStream, stuck]; } @@ -127,7 +132,7 @@ export async function negotiateConnectionWithClientOffer( streamer: string, bearerToken?: string, agent?: StreamplaceAgent, - oauthSession?: SessionManager | null, + isOwnStream?: boolean, playbackWorkerUrl?: string | null, ) { /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer */ @@ -166,7 +171,7 @@ export async function negotiateConnectionWithClientOffer( ofr.sdp, bearerToken, agent, - oauthSession, + isOwnStream, playbackWorkerUrl, ); let text = new TextDecoder().decode(response.data); @@ -253,7 +258,6 @@ export async function negotiateIngestConnectionWithClientOffer( async function getPlaybackServerAgent( agent: StreamplaceAgent, - oauthSession: SessionManager | null | undefined, streamer: string, playbackWorkerUrl?: string | null, ): Promise { @@ -269,7 +273,7 @@ async function getPlaybackServerAgent( if (res.data.servers.length > 0) { const serverUrl = res.data.servers[0]; console.log(`Using playback server: ${serverUrl}`); - return createAgentForServer(oauthSession, serverUrl); + return new StreamplaceAgent(serverUrl); } } catch (e) { console.error("getPlaybackServer failed, using default agent:", e); @@ -282,18 +286,18 @@ async function postSDPOffer( data: string, bearerToken?: string, agent?: StreamplaceAgent, - oauthSession?: SessionManager | null, + isOwnStream?: boolean, playbackWorkerUrl?: string | null, ) { if (!agent) { throw new Error("No agent found"); } - const playbackAgent = await getPlaybackServerAgent( - agent, - oauthSession, - streamer, - playbackWorkerUrl, - ); + // Own stream: use the authenticated PDS agent directly (needed for + // unpublished stream preview). Otherwise, look up a playback server + // and use an anonymous agent for it. + const playbackAgent = isOwnStream + ? agent + : await getPlaybackServerAgent(agent, streamer, playbackWorkerUrl); return await playbackAgent.place.stream.playback.whep(data, { qp: { rendition: "source", diff --git a/js/components/src/streamplace-store/xrpc.tsx b/js/components/src/streamplace-store/xrpc.tsx index 78240ffa..89145011 100644 --- a/js/components/src/streamplace-store/xrpc.tsx +++ b/js/components/src/streamplace-store/xrpc.tsx @@ -1,4 +1,3 @@ -import { SessionManager } from "@atproto/api/dist/session-manager"; import { useMemo } from "react"; import { StreamplaceAgent } from "streamplace"; import { useStreamplaceStore, useUrl } from "./streamplace-store"; @@ -39,27 +38,6 @@ export function usePossiblyUnauthedPDSAgent(): StreamplaceAgent | null { }, [oauthSession]); } -// Creates an agent that targets a specific server URL. When authenticated, -// the OAuth session's DPoP fetch is reused but requests are redirected to the -// target server by passing an absolute URL to the session's fetchHandler -// (absolute URLs in `new URL(abs, base)` ignore the base). -export function createAgentForServer( - oauthSession: SessionManager | null | undefined, - serverUrl: string, -): StreamplaceAgent { - if (!oauthSession) { - return new StreamplaceAgent(serverUrl); - } - const wrappedSession: SessionManager = { - did: oauthSession.did, - fetchHandler(pathname: string, init: RequestInit): Promise { - const fullUrl = new URL(pathname, serverUrl).toString(); - return oauthSession.fetchHandler.call(oauthSession, fullUrl, init); - }, - }; - return new StreamplaceAgent(wrappedSession); -} - // always returns an unauthenticated agent pointed at the public bluesky API // probably should not be used in most places, but in case we have a bug it may be useful export function useUnauthenticatedBlueskyAppViewAgent(): StreamplaceAgent {