@@ -41,11 +41,11 @@ export interface StreamConfig {
41
41
streamUrl : string ;
42
42
frameRate : number ;
43
43
prompts ?: any ;
44
- selectedDeviceId : string | undefined ;
45
- selectedAudioDeviceId : string | undefined ;
44
+ selectedVideoDeviceId : string ;
45
+ selectedAudioDeviceId : string ;
46
46
}
47
47
48
- interface VideoDevice {
48
+ interface AVDevice {
49
49
deviceId : string ;
50
50
label : string ;
51
51
}
@@ -54,8 +54,8 @@ export const DEFAULT_CONFIG: StreamConfig = {
54
54
streamUrl :
55
55
process . env . NEXT_PUBLIC_DEFAULT_STREAM_URL || "http://127.0.0.1:8889" ,
56
56
frameRate : 30 ,
57
- selectedDeviceId : undefined ,
58
- selectedAudioDeviceId : undefined ,
57
+ selectedVideoDeviceId : "none" ,
58
+ selectedAudioDeviceId : "none" ,
59
59
} ;
60
60
61
61
interface StreamSettingsProps {
@@ -137,10 +137,10 @@ export const usePrompt = () => useContext(PromptContext);
137
137
function ConfigForm ( { config, onSubmit } : ConfigFormProps ) {
138
138
const [ prompts , setPrompts ] = useState < any [ ] > ( [ ] ) ;
139
139
const { setOriginalPrompts } = usePrompt ( ) ;
140
- const [ videoDevices , setVideoDevices ] = useState < VideoDevice [ ] > ( [ ] ) ;
141
- const [ audioDevices , setAudioDevices ] = useState < VideoDevice [ ] > ( [ ] ) ;
142
- const [ selectedDevice , setSelectedDevice ] = useState < string | undefined > ( config . selectedDeviceId ) ;
143
- const [ selectedAudioDevice , setSelectedAudioDevice ] = useState < string | undefined > ( config . selectedDeviceId ) ;
140
+ const [ videoDevices , setVideoDevices ] = useState < AVDevice [ ] > ( [ ] ) ;
141
+ const [ audioDevices , setAudioDevices ] = useState < AVDevice [ ] > ( [ ] ) ;
142
+ const [ selectedVideoDevice , setSelectedVideoDevice ] = useState < string | undefined > ( config . selectedVideoDeviceId ) ;
143
+ const [ selectedAudioDevice , setSelectedAudioDevice ] = useState < string | undefined > ( config . selectedAudioDeviceId ) ;
144
144
145
145
const form = useForm < z . infer < typeof formSchema > > ( {
146
146
resolver : zodResolver ( formSchema ) ,
@@ -152,35 +152,32 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
152
152
*/
153
153
const getVideoDevices = useCallback ( async ( ) => {
154
154
try {
155
- await navigator . mediaDevices . getUserMedia ( { video : true , audio : true } ) ;
155
+ await navigator . mediaDevices . getUserMedia ( { video : true } ) ;
156
156
157
157
const devices = await navigator . mediaDevices . enumerateDevices ( ) ;
158
158
const videoDevices = [
159
159
{ deviceId : "none" , label : "No Video" } ,
160
160
...devices
161
- . filter ( ( device ) => device . kind === "videoinput" )
162
- . map ( ( device ) => ( {
163
- deviceId : device . deviceId ,
164
- label : device . label || `Camera ${ device . deviceId . slice ( 0 , 5 ) } ...` ,
165
- } ) )
161
+ . filter ( ( device ) => device . kind === "videoinput" )
162
+ . map ( ( device ) => ( {
163
+ deviceId : device . deviceId ,
164
+ label : device . label || `Camera ${ device . deviceId . slice ( 0 , 5 ) } ...` ,
165
+ } ) )
166
166
] ;
167
167
168
168
setVideoDevices ( videoDevices ) ;
169
169
// Set default to first available camera if no selection yet
170
- if ( ! selectedDevice && videoDevices . length > 1 ) {
171
- setSelectedDevice ( videoDevices [ 1 ] . deviceId ) ; // Index 1 because 0 is "No Video"
170
+ if ( selectedVideoDevice == "none" && videoDevices . length > 1 ) {
171
+ setSelectedVideoDevice ( videoDevices [ 1 ] . deviceId ) ; // Index 1 because 0 is "No Video"
172
172
}
173
- } catch ( err ) {
174
- console . error ( "Failed to get video devices" ) ;
175
- // If we can't access video devices, still provide the None option
176
- const videoDevices = [ { deviceId : "none" , label : "No Video" } ] ;
177
- setVideoDevices ( videoDevices ) ;
178
- setSelectedDevice ( "none" ) ;
173
+ } catch ( err ) {
174
+ console . log ( `Failed to get video devices: ${ err } ` ) ;
179
175
}
180
- } , [ selectedDevice ] ) ;
176
+ } , [ ] ) ;
181
177
182
178
const getAudioDevices = useCallback ( async ( ) => {
183
179
try {
180
+ await navigator . mediaDevices . getUserMedia ( { audio : true } ) ;
184
181
const devices = await navigator . mediaDevices . enumerateDevices ( ) ;
185
182
const audioDevices = [
186
183
{ deviceId : "none" , label : "No Audio" } ,
@@ -194,17 +191,13 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
194
191
195
192
setAudioDevices ( audioDevices ) ;
196
193
// Set default to first available microphone if no selection yet
197
- if ( ! selectedAudioDevice && audioDevices . length > 1 ) {
198
- setSelectedAudioDevice ( audioDevices [ 0 ] . deviceId ) ; // Default to "No Audio" for now
194
+ if ( selectedAudioDevice == "none" && audioDevices . length > 1 ) {
195
+ setSelectedAudioDevice ( audioDevices [ 1 ] . deviceId ) ; // Index 1 because 0 is "No Audio"
199
196
}
200
197
} catch ( err ) {
201
- console . error ( "Failed to get audio devices" ) ;
202
- // If we can't access audio devices, still provide the None option
203
- const audioDevices = [ { deviceId : "none" , label : "No Audio" } ] ;
204
- setAudioDevices ( audioDevices ) ;
205
- setSelectedAudioDevice ( "none" ) ;
198
+ console . log ( `Failed to get audio devices: ${ err } ` ) ;
206
199
}
207
- } , [ selectedAudioDevice ] ) ;
200
+ } , [ ] ) ;
208
201
209
202
// Handle device change events.
210
203
useEffect ( ( ) => {
@@ -232,9 +225,9 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
232
225
? values . streamUrl . replace ( / \/ + $ / , "" )
233
226
: values . streamUrl ,
234
227
prompts : prompts ,
235
- selectedDeviceId : selectedDevice ,
236
- selectedAudioDeviceId : selectedAudioDevice ,
237
- } ) ;
228
+ selectedVideoDeviceId : selectedVideoDevice || "none" ,
229
+ selectedAudioDeviceId : selectedAudioDevice || "none" ,
230
+ } ) ;
238
231
} ;
239
232
240
233
const handlePromptsChange = async ( e : React . ChangeEvent < HTMLInputElement > ) => {
@@ -260,11 +253,18 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
260
253
* @param deviceId
261
254
*/
262
255
const handleCameraSelect = ( deviceId : string ) => {
263
- if ( deviceId !== selectedDevice ) {
264
- setSelectedDevice ( deviceId ) ;
256
+ if ( deviceId !== selectedVideoDevice ) {
257
+ setSelectedVideoDevice ( deviceId ) ;
258
+ }
259
+ } ;
260
+
261
+ const handleMicrophoneSelect = ( deviceId : string ) => {
262
+ if ( deviceId !== selectedAudioDevice ) {
263
+ setSelectedAudioDevice ( deviceId ) ;
265
264
}
266
265
} ;
267
266
267
+
268
268
return (
269
269
< Form { ...form } >
270
270
< form onSubmit = { form . handleSubmit ( handleSubmit ) } autoComplete = "off" >
@@ -300,11 +300,11 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
300
300
< Label > Camera</ Label >
301
301
< Select
302
302
required = { true }
303
- value = { selectedDevice }
303
+ value = { selectedVideoDevice }
304
304
onValueChange = { handleCameraSelect }
305
305
>
306
306
< Select . Trigger className = "w-full mt-2" >
307
- { selectedDevice ? ( videoDevices . find ( ( d ) => d . deviceId === selectedDevice ) ?. label || "None" ) : "None" }
307
+ { selectedVideoDevice ? ( videoDevices . find ( ( d ) => d . deviceId === selectedVideoDevice ) ?. label || "None" ) : "None" }
308
308
</ Select . Trigger >
309
309
< Select . Content >
310
310
{ videoDevices . length === 0 ? (
@@ -324,28 +324,37 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
324
324
325
325
< div className = "mt-4 mb-4" >
326
326
< Label > Microphone</ Label >
327
- < Select value = { selectedAudioDevice } onValueChange = { setSelectedAudioDevice } >
327
+ < Select value = { selectedAudioDevice } onValueChange = { handleMicrophoneSelect } >
328
328
< Select . Trigger className = "w-full mt-2" >
329
329
{ selectedAudioDevice ? ( audioDevices . find ( ( d ) => d . deviceId === selectedAudioDevice ) ?. label || "None" ) : "None" }
330
330
</ Select . Trigger >
331
331
< Select . Content >
332
- { audioDevices . map ( ( device ) => (
333
- < Select . Option key = { device . deviceId } value = { device . deviceId } >
334
- { device . label }
332
+ { audioDevices . length === 0 ? (
333
+ < Select . Option disabled value = "no-devices" >
334
+ No audio devices found
335
335
</ Select . Option >
336
- ) ) }
336
+ ) : (
337
+ audioDevices
338
+ . filter ( ( device ) => device . deviceId !== undefined && device . deviceId != "" )
339
+ . map ( ( device ) => (
340
+ < Select . Option key = { device . deviceId } value = { device . deviceId } >
341
+ { device . label }
342
+ </ Select . Option >
343
+ ) )
344
+ ) }
337
345
</ Select . Content >
338
346
</ Select >
339
347
</ div >
340
348
341
349
< div className = "mt-4 mb-4 grid max-w-sm items-center gap-3" >
342
350
< Label > Comfy Workflows</ Label >
343
351
< Input
344
- id = "video- workflow"
352
+ id = "workflow"
345
353
type = "file"
346
354
accept = ".json"
347
355
multiple
348
356
onChange = { handlePromptsChange }
357
+ required = { true }
349
358
/>
350
359
</ div >
351
360
0 commit comments