@@ -83,35 +83,58 @@ def _session(root: Path, callbacks: object | None = None) -> Any:
8383 )
8484
8585
86- def _video_models (session : Any ) -> list [dict [str , Any ]]:
86+ _MODEL_TASKS = {
87+ "video" : ("text_to_video" , "image_to_video" ),
88+ "image" : ("text_to_image" , "image_to_image" ),
89+ }
90+
91+
92+ def _is_music (metadata : dict [str , Any ]) -> bool :
93+ return str (metadata .get ("family" ) or "" ).casefold () == "music"
94+
95+
96+ def _models (session : Any , model_kind : str ) -> list [dict [str , Any ]]:
97+ """Translate WanGP model metadata into NodeTool provider models."""
98+ if model_kind not in {"video" , "image" , "tts" , "music" }:
99+ return []
87100 models : list [dict [str , Any ]] = []
88101 for metadata in session .list_model_metadata ():
89- outputs = metadata .get ("outputs" , [])
90- if "video" not in outputs :
102+ outputs = metadata .get ("main_output" , metadata .get ("outputs" , []))
103+ expected_output = "audio" if model_kind in {"tts" , "music" } else model_kind
104+ if expected_output not in outputs :
105+ continue
106+ if model_kind == "music" and not _is_music (metadata ):
107+ continue
108+ if model_kind == "tts" and _is_music (metadata ):
91109 continue
92110 capabilities = metadata .get ("capabilities" , {})
93- supported = [
94- task
95- for task in ("text_to_video" , "image_to_video" )
96- if capabilities .get (task )
97- ]
111+ if model_kind in _MODEL_TASKS :
112+ supported = [
113+ task for task in _MODEL_TASKS [model_kind ] if capabilities .get (task )
114+ ]
115+ elif model_kind == "music" :
116+ supported = ["text_to_music" ] if capabilities .get ("text_to_audio" ) else []
117+ else :
118+ supported = ["text_to_speech" ] if capabilities .get ("text_to_audio" ) else []
98119 if not supported :
99120 continue
100121 model_type = str (metadata .get ("model_type" ) or "" ).strip ()
101122 if not model_type :
102123 continue
103- models .append (
104- {
105- "id" : model_type ,
106- "name" : str (metadata .get ("name" ) or model_type ),
107- "provider" : "wangp" ,
108- "supportedTasks" : supported ,
109- }
110- )
124+ model = {
125+ "id" : model_type ,
126+ "name" : str (metadata .get ("name" ) or model_type ),
127+ "provider" : "wangp" ,
128+ }
129+ if model_kind == "tts" :
130+ model ["capabilities" ] = supported
131+ else :
132+ model ["supportedTasks" ] = supported
133+ models .append (model )
111134 return models
112135
113136
114- def _settings (request : dict [str , Any ]) -> dict [ str , Any ] :
137+ def _model_id (request : dict [str , Any ]) -> str :
115138 params = request .get ("params" )
116139 if not isinstance (params , dict ):
117140 raise ValueError ("Provider generation requires params" )
@@ -121,33 +144,107 @@ def _settings(request: dict[str, Any]) -> dict[str, Any]:
121144 model_type = str (model or "" ).strip ()
122145 if not model_type :
123146 raise ValueError ("Provider generation requires a model id" )
147+ return model_type
148+
149+
150+ def _input_path (request : dict [str , Any ], name : str ) -> Path :
151+ path = Path (str (request .get (name ) or "" )).resolve ()
152+ if not path .is_file ():
153+ raise ValueError (f"{ request .get ('operation' )} requires a readable { name } " )
154+ return path
155+
156+
157+ def _setting_choice_with_flag (
158+ metadata : dict [str , Any ] | None , setting : str , flag : str
159+ ) -> str :
160+ definitions = (metadata or {}).get ("setting_values" , {}).get (
161+ "video_prompt_type" , {}
162+ )
163+ choice_def = definitions .get (setting )
164+ if not isinstance (choice_def , dict ):
165+ return ""
166+ for choice in choice_def .get ("choices" , []):
167+ value = choice .get ("value" , "" ) if isinstance (choice , dict ) else ""
168+ if flag in str (value ):
169+ return str (value )
170+ return ""
171+
172+
173+ def _settings (
174+ request : dict [str , Any ], metadata : dict [str , Any ] | None = None
175+ ) -> dict [str , Any ]:
176+ params = request ["params" ]
177+ operation = str (request .get ("operation" ) or "" )
178+ model_type = _model_id (request )
124179
125180 settings : dict [str , Any ] = {
126181 "model_type" : model_type ,
127- "prompt" : str (params .get ("prompt" ) or "" ),
182+ "prompt" : str (params .get ("text" ) or params . get ( " prompt" ) or "" ),
128183 }
129184 mappings = {
130185 "negativePrompt" : "negative_prompt" ,
131186 "resolution" : "resolution" ,
132187 "guidanceScale" : "guidance_scale" ,
133188 "numInferenceSteps" : "num_inference_steps" ,
134189 "seed" : "seed" ,
190+ "fps" : "force_fps" ,
191+ "scheduler" : "sample_solver" ,
192+ "strength" : "denoising_strength" ,
135193 }
136194 for source , target in mappings .items ():
137195 value = params .get (source )
138196 if value is not None :
139197 settings [target ] = value
198+ if "resolution" not in settings :
199+ width = params .get ("width" , params .get ("targetWidth" ))
200+ height = params .get ("height" , params .get ("targetHeight" ))
201+ if width is not None and height is not None :
202+ settings ["resolution" ] = f"{ int (width )} x{ int (height )} "
140203 if params .get ("numFrames" ) is not None :
141204 settings ["video_length" ] = int (params ["numFrames" ])
142- elif params .get ("durationSeconds" ) is not None :
205+ elif params .get ("durationSeconds" ) is not None and operation . endswith ( "video" ) :
143206 settings ["video_length" ] = f"{ float (params ['durationSeconds' ]):g} s"
144207
145- if request ["operation" ] == "image_to_video" :
146- image_path = Path (str (request .get ("image_path" ) or "" )).resolve ()
147- if not image_path .is_file ():
148- raise ValueError ("image_to_video requires a readable image_path" )
149- settings ["image_start" ] = str (image_path )
150- settings ["image_prompt_type" ] = "S"
208+ if operation in {"image_to_image" , "image_to_video" }:
209+ image_path = str (_input_path (request , "image_path" ))
210+ image_inputs = (metadata or {}).get ("media_inputs" , {}).get ("image" , {})
211+ if image_inputs .get ("start" ) or operation == "image_to_video" :
212+ settings ["image_start" ] = image_path
213+ settings ["image_prompt_type" ] = "S"
214+ elif image_inputs .get ("reference" ):
215+ settings ["image_refs" ] = [image_path ]
216+ elif image_inputs .get ("control" ):
217+ settings ["image_guide" ] = image_path
218+ control_mode = _setting_choice_with_flag (
219+ metadata , "guide_preprocessing" , "V"
220+ ) or _setting_choice_with_flag (metadata , "guide_custom_choices" , "V" )
221+ if control_mode :
222+ settings ["video_prompt_type" ] = control_mode
223+ else :
224+ raise ValueError (f"Model { model_type } does not accept an input image" )
225+
226+ if operation == "text_to_audio" :
227+ style_prompt = str (params .get ("prompt" ) or "" )
228+ lyrics = str (params .get ("lyrics" ) or "" ).strip ()
229+ settings ["prompt" ] = lyrics or "[Instrumental]"
230+ settings ["alt_prompt" ] = style_prompt
231+ if params .get ("durationSeconds" ) is not None :
232+ settings ["duration_seconds" ] = float (params ["durationSeconds" ])
233+
234+ if operation == "tts_encoded" :
235+ if params .get ("referenceText" ) is not None :
236+ settings ["alt_prompt" ] = str (params ["referenceText" ])
237+ elif params .get ("instructions" ) is not None :
238+ settings ["alt_prompt" ] = str (params ["instructions" ])
239+ model_mode = params .get ("voice" ) or params .get ("language" )
240+ if model_mode :
241+ settings ["model_mode" ] = str (model_mode )
242+ if params .get ("speed" ) is not None :
243+ settings ["speech_speed" ] = float (params ["speed" ])
244+ if request .get ("reference_audio_path" ):
245+ settings ["audio_guide" ] = str (
246+ _input_path (request , "reference_audio_path" )
247+ )
151248 return settings
152249
153250
@@ -168,19 +265,21 @@ def on_progress(self, update: Any) -> None:
168265 )
169266
170267
171- def _generated_path (result : Any ) -> str :
268+ def _generated_path (result : Any , media_type : str | None = None ) -> str :
172269 if not getattr (result , "success" , False ):
173270 errors = getattr (result , "errors" , ())
174271 detail = "; " .join (str (error ) for error in errors ) or "generation failed"
175272 raise RuntimeError (detail )
176- for path in getattr (result , "generated_files" , ()):
177- candidate = Path (str (path ))
178- if candidate .is_file ():
179- return str (candidate .resolve ())
180273 for artifact in getattr (result , "artifacts" , ()):
274+ if media_type and getattr (artifact , "media_type" , None ) != media_type :
275+ continue
181276 path = getattr (artifact , "path" , None )
182277 if path and Path (path ).is_file ():
183278 return str (Path (path ).resolve ())
279+ for path in getattr (result , "generated_files" , ()):
280+ candidate = Path (str (path ))
281+ if candidate .is_file ():
282+ return str (candidate .resolve ())
184283 raise RuntimeError ("WanGP completed without a generated media file" )
185284
186285
@@ -196,14 +295,24 @@ def main() -> int:
196295 with contextlib .redirect_stdout (sys .stderr ):
197296 if operation == "models" :
198297 model_type = str (request .get ("model_type" ) or "" )
199- models = _video_models (_session (root )) if model_type == "video" else []
298+ models = _models (_session (root ), model_type )
200299 emitter .send ("result" , {"models" : models })
201300 return 0
202- if operation not in {"text_to_video" , "image_to_video" }:
301+ media_type = {
302+ "text_to_image" : "image" ,
303+ "image_to_image" : "image" ,
304+ "text_to_video" : "video" ,
305+ "image_to_video" : "video" ,
306+ "text_to_audio" : "audio" ,
307+ "tts_encoded" : "audio" ,
308+ }.get (operation )
309+ if media_type is None :
203310 raise ValueError (f"Unsupported provider operation: { operation } " )
204311 callbacks = _Callbacks (emitter )
205- result = _session (root , callbacks ).run_task (_settings (request ))
206- emitter .send ("result" , {"path" : _generated_path (result )})
312+ session = _session (root , callbacks )
313+ metadata = session .get_model_metadata (_model_id (request ))
314+ result = session .run_task (_settings (request , metadata ))
315+ emitter .send ("result" , {"path" : _generated_path (result , media_type )})
207316 return 0
208317
209318
0 commit comments