From b37019de257538c4e75d9d15fff5ccfdce62a066 Mon Sep 17 00:00:00 2001 From: ryanontheinstide Date: Fri, 21 Feb 2025 19:15:53 -0500 Subject: [PATCH 1/3] feat: accept query params for stream settings --- ui/src/app/page.tsx | 20 ++++++++++++++- ui/src/components/room.tsx | 44 +++++++++++++++++++++----------- ui/src/components/settings.tsx | 31 +++++++++++----------- ui/src/hooks/use-query-params.ts | 30 ++++++++++++++++++++++ 4 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 ui/src/hooks/use-query-params.ts diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx index 6d7e24fe..aa371147 100644 --- a/ui/src/app/page.tsx +++ b/ui/src/app/page.tsx @@ -4,9 +4,27 @@ import { Room } from "@/components/room"; import { PromptContext } from "@/components/settings"; import { useState, useEffect } from "react"; +// Read query params once at page load +function getQueryParams() { + if (typeof window === 'undefined') return null; + + const searchParams = new URLSearchParams(window.location.search); + const frameRateParam = searchParams.get('frameRate'); + + return { + streamUrl: searchParams.get('streamUrl'), + frameRate: frameRateParam ? parseInt(frameRateParam) : undefined, + videoDevice: searchParams.get('videoDevice'), + audioDevice: searchParams.get('audioDevice'), + workflowUrl: searchParams.get('workflowUrl'), + skipDialog: searchParams.get('skipDialog') === 'true' + }; +} + export default function Page() { const [originalPrompts, setOriginalPrompts] = useState(null); const [currentPrompts, setCurrentPrompts] = useState(null); + const [queryParams] = useState(getQueryParams); // Read once on mount // Update currentPrompt whenever originalPrompt changes useEffect(() => { @@ -24,7 +42,7 @@ export default function Page() { }}>
- +
diff --git a/ui/src/components/room.tsx b/ui/src/components/room.tsx index a3de16c3..5da9f9b8 100644 --- a/ui/src/components/room.tsx +++ b/ui/src/components/room.tsx @@ -1,10 +1,10 @@ "use client"; import { PeerConnector } from "@/components/peer"; -import { StreamConfig, StreamSettings } from "@/components/settings"; +import { StreamConfig, StreamSettings, DEFAULT_CONFIG } from "@/components/settings"; import { Webcam } from "@/components/webcam"; import { usePeerContext } from "@/context/peer-context"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState, useMemo } from "react"; import { toast } from "sonner"; import { Tooltip, @@ -13,6 +13,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { ControlPanelsContainer } from "@/components/control-panels-container"; + interface MediaStreamPlayerProps { stream: MediaStream; } @@ -154,26 +155,38 @@ function Stage({ connected, onStreamReady }: StageProps) { ); } +interface RoomProps { + initialParams?: { + streamUrl?: string; + frameRate?: number; + videoDevice?: string; + audioDevice?: string; + }; +} + /** * Creates a room component for the user to stream their webcam to ComfyStream and * see the output stream. */ -export const Room = () => { +export const Room = ({ initialParams }: RoomProps) => { const [connect, setConnect] = useState(false); const [isConnected, setIsConnected] = useState(false); - const [isStreamSettingsOpen, setIsStreamSettingsOpen] = - useState(true); + const [isStreamSettingsOpen, setIsStreamSettingsOpen] = useState(true); const [localStream, setLocalStream] = useState(null); - const [loadingToastId, setLoadingToastId] = useState< - string | number | undefined - >(undefined); - - const [config, setConfig] = useState({ - streamUrl: "", - frameRate: 0, - selectedVideoDeviceId: "", - selectedAudioDeviceId: "", // New property for audio device - prompts: null, + const [loadingToastId, setLoadingToastId] = useState(undefined); + + const [config, setConfig] = useState(() => { + // Initialize config with initial values, merging with defaults + const config = { + ...DEFAULT_CONFIG, + ...(initialParams?.streamUrl ? { streamUrl: initialParams.streamUrl } : {}), + ...(initialParams?.frameRate ? { frameRate: initialParams.frameRate } : {}), + ...(initialParams?.videoDevice ? { selectedVideoDeviceId: initialParams.videoDevice } : {}), + ...(initialParams?.audioDevice ? { selectedAudioDeviceId: initialParams.audioDevice } : {}), + }; + console.log('Room: initializing with params:', initialParams); + console.log('Room: initial config:', config); + return config; }); const connectingRef = useRef(false); @@ -268,6 +281,7 @@ export const Room = () => { open={isStreamSettingsOpen} onOpenChange={setIsStreamSettingsOpen} onSave={onStreamConfigSave} + currentConfig={config} /> diff --git a/ui/src/components/settings.tsx b/ui/src/components/settings.tsx index ce162d36..1afd186e 100644 --- a/ui/src/components/settings.tsx +++ b/ui/src/components/settings.tsx @@ -63,23 +63,17 @@ interface StreamSettingsProps { open: boolean; onOpenChange: (open: boolean) => void; onSave: (config: StreamConfig) => void; + currentConfig?: StreamConfig; } export function StreamSettings({ open, onOpenChange, onSave, + currentConfig, }: StreamSettingsProps) { const isDesktop = useMediaQuery("(min-width: 768px)"); - const [config, setConfig] = useState(DEFAULT_CONFIG); - - const handleSubmit = (config: StreamConfig) => { - setConfig(config); - onSave(config); - onOpenChange(false); - }; - if (isDesktop) { return ( @@ -89,7 +83,7 @@ export function StreamSettings({
Stream Settings
- +
); @@ -102,7 +96,7 @@ export function StreamSettings({ Stream Settings
- +
@@ -115,8 +109,8 @@ const formSchema = z.object({ }); interface ConfigFormProps { - config: StreamConfig; onSubmit: (config: StreamConfig) => void; + currentConfig?: StreamConfig; } interface PromptContextType { @@ -135,17 +129,24 @@ export const PromptContext = createContext({ export const usePrompt = () => useContext(PromptContext); -function ConfigForm({ config, onSubmit }: ConfigFormProps) { +function ConfigForm({ onSubmit, currentConfig }: ConfigFormProps) { const [prompts, setPrompts] = useState([]); const { setOriginalPrompts } = usePrompt(); const [videoDevices, setVideoDevices] = useState([]); const [audioDevices, setAudioDevices] = useState([]); - const [selectedVideoDevice, setSelectedVideoDevice] = useState(config.selectedVideoDeviceId); - const [selectedAudioDevice, setSelectedAudioDevice] = useState(config.selectedAudioDeviceId); + const [selectedVideoDevice, setSelectedVideoDevice] = useState( + currentConfig?.selectedVideoDeviceId || DEFAULT_CONFIG.selectedVideoDeviceId + ); + const [selectedAudioDevice, setSelectedAudioDevice] = useState( + currentConfig?.selectedAudioDeviceId || DEFAULT_CONFIG.selectedAudioDeviceId + ); const form = useForm>({ resolver: zodResolver(formSchema), - defaultValues: config, + defaultValues: { + streamUrl: currentConfig?.streamUrl || DEFAULT_CONFIG.streamUrl, + frameRate: currentConfig?.frameRate || DEFAULT_CONFIG.frameRate, + }, }); /** diff --git a/ui/src/hooks/use-query-params.ts b/ui/src/hooks/use-query-params.ts new file mode 100644 index 00000000..90073a47 --- /dev/null +++ b/ui/src/hooks/use-query-params.ts @@ -0,0 +1,30 @@ +// Read params directly from window.location.search +const cachedParams = typeof window !== 'undefined' ? (() => { + console.log('Reading query params from:', window.location.search); + const searchParams = new URLSearchParams(window.location.search); + const frameRateParam = searchParams.get('frameRate'); + + const params = { + streamUrl: searchParams.get('streamUrl'), + frameRate: frameRateParam ? parseInt(frameRateParam) : undefined, + videoDevice: searchParams.get('videoDevice'), + audioDevice: searchParams.get('audioDevice'), + workflowUrl: searchParams.get('workflowUrl'), + skipDialog: searchParams.get('skipDialog') === 'true' + }; + console.log('Parsed params:', params); + return params; +})() : { + streamUrl: null, + frameRate: undefined, + videoDevice: null, + audioDevice: null, + workflowUrl: null, + skipDialog: false +}; + +// Just return the cached params +export function useQueryParams() { + console.log('useQueryParams called, returning:', cachedParams); + return cachedParams; +} \ No newline at end of file From f666c1a2ea03b1d17c8250216ac353db89d986bb Mon Sep 17 00:00:00 2001 From: ryanontheinstide Date: Fri, 21 Feb 2025 19:19:47 -0500 Subject: [PATCH 2/3] feat: accept serialized json for workflow --- ui/src/app/page.tsx | 2 ++ ui/src/components/room.tsx | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx index aa371147..082b9706 100644 --- a/ui/src/app/page.tsx +++ b/ui/src/app/page.tsx @@ -10,6 +10,7 @@ function getQueryParams() { const searchParams = new URLSearchParams(window.location.search); const frameRateParam = searchParams.get('frameRate'); + const workflowParam = searchParams.get('workflow'); return { streamUrl: searchParams.get('streamUrl'), @@ -17,6 +18,7 @@ function getQueryParams() { videoDevice: searchParams.get('videoDevice'), audioDevice: searchParams.get('audioDevice'), workflowUrl: searchParams.get('workflowUrl'), + workflow: workflowParam ? JSON.parse(decodeURIComponent(workflowParam)) : undefined, skipDialog: searchParams.get('skipDialog') === 'true' }; } diff --git a/ui/src/components/room.tsx b/ui/src/components/room.tsx index 5da9f9b8..9401e3bc 100644 --- a/ui/src/components/room.tsx +++ b/ui/src/components/room.tsx @@ -161,6 +161,9 @@ interface RoomProps { frameRate?: number; videoDevice?: string; audioDevice?: string; + workflowUrl?: string; + workflow?: any; + skipDialog?: boolean; }; } @@ -183,6 +186,7 @@ export const Room = ({ initialParams }: RoomProps) => { ...(initialParams?.frameRate ? { frameRate: initialParams.frameRate } : {}), ...(initialParams?.videoDevice ? { selectedVideoDeviceId: initialParams.videoDevice } : {}), ...(initialParams?.audioDevice ? { selectedAudioDeviceId: initialParams.audioDevice } : {}), + ...(initialParams?.workflow ? { prompts: [initialParams.workflow] } : {}), }; console.log('Room: initializing with params:', initialParams); console.log('Room: initial config:', config); From d6d785a1150cda460feffb3bbb274beab2affb46 Mon Sep 17 00:00:00 2001 From: ryanontheinstide Date: Fri, 21 Feb 2025 19:22:55 -0500 Subject: [PATCH 3/3] fix: ts error --- ui/src/app/page.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx index 082b9706..dc495ebf 100644 --- a/ui/src/app/page.tsx +++ b/ui/src/app/page.tsx @@ -6,18 +6,18 @@ import { useState, useEffect } from "react"; // Read query params once at page load function getQueryParams() { - if (typeof window === 'undefined') return null; + if (typeof window === 'undefined') return undefined; const searchParams = new URLSearchParams(window.location.search); const frameRateParam = searchParams.get('frameRate'); const workflowParam = searchParams.get('workflow'); return { - streamUrl: searchParams.get('streamUrl'), + streamUrl: searchParams.get('streamUrl') || undefined, frameRate: frameRateParam ? parseInt(frameRateParam) : undefined, - videoDevice: searchParams.get('videoDevice'), - audioDevice: searchParams.get('audioDevice'), - workflowUrl: searchParams.get('workflowUrl'), + videoDevice: searchParams.get('videoDevice') || undefined, + audioDevice: searchParams.get('audioDevice') || undefined, + workflowUrl: searchParams.get('workflowUrl') || undefined, workflow: workflowParam ? JSON.parse(decodeURIComponent(workflowParam)) : undefined, skipDialog: searchParams.get('skipDialog') === 'true' };