@@ -69,13 +69,25 @@ def _apply_transformers_patches_once():
6969 except Exception as e :
7070 print (f"⚠️ Warning: Could not apply Transformers patches: { e } " )
7171
72- # Numba/Librosa compatibility check DEFERRED to first engine use.
73- # The numba_compat module imports librosa (~0.7s) to test JIT compilation,
74- # which is too expensive for startup. Instead, we defer the test until
75- # an engine actually needs librosa. On Python 3.13+ we preemptively set
76- # the safe env var since numba JIT is known to be problematic there.
72+ # Numba/Librosa compatibility check at startup.
73+ # On Python 3.13+ we preemptively disable JIT (known incompatible).
74+ # On NumPy 2.x we run the full librosa JIT test (~0.7s) because numba's
75+ # @guvectorize crashes on some hardware with NumPy 2.x — this must happen
76+ # before any engine lazy-loads librosa/numba, or the env var is too late.
77+ # On NumPy 1.x we skip the test entirely (not affected).
7778if sys .version_info >= (3 , 13 ):
7879 os .environ .setdefault ('NUMBA_DISABLE_JIT' , '1' )
80+ else :
81+ try :
82+ import numpy as _np
83+ if int (_np .__version__ .split ('.' )[0 ]) >= 2 :
84+ numba_compat_path = os .path .join (os .path .dirname (__file__ ), "utils" , "compatibility" , "numba_compat.py" )
85+ _spec = importlib .util .spec_from_file_location ("numba_compat_module" , numba_compat_path )
86+ _numba_compat = importlib .util .module_from_spec (_spec )
87+ _spec .loader .exec_module (_numba_compat )
88+ _numba_compat .setup_numba_compatibility (quick_startup = False , verbose = False )
89+ except Exception :
90+ pass
7991
8092# TorchCodec note: Removed torchcodec dependency to eliminate FFmpeg system requirement
8193# torchaudio.load() works fine with fallback backends (soundfile, scipy)
@@ -345,10 +357,10 @@ async def get_available_languages_endpoint(request):
345357 # Fallback list
346358 return web .json_response ({"languages" : ["en" , "de" , "fr" , "ja" , "es" , "it" , "pt" , "th" , "no" ], "error" : str (e )})
347359
348- @PromptServer .instance .routes .post ("/api/tts-audio-suite/settings" )
349- async def set_inline_tag_settings_endpoint (request ):
350- """API endpoint to receive settings from frontend for inline edit tags and restore VC"""
351- print ("🔧 Settings endpoint called" ) # Immediate print to verify endpoint is reached
360+ @PromptServer .instance .routes .post ("/api/tts-audio-suite/settings" )
361+ async def set_inline_tag_settings_endpoint (request ):
362+ """API endpoint to receive settings from frontend for inline edit tags and restore VC"""
363+ print ("🔧 Settings endpoint called" ) # Immediate print to verify endpoint is reached
352364 try :
353365 data = await request .json ()
354366 precision = data .get ("precision" , "auto" )
@@ -375,45 +387,45 @@ async def set_inline_tag_settings_endpoint(request):
375387 edit_post_processor_module .set_inline_tag_settings (precision = precision , device = device , vc_engine = vc_engine , cosyvoice_variant = cosyvoice_variant )
376388
377389 return web .json_response ({"status" : "success" , "precision" : precision , "device" : device , "vc_engine" : vc_engine , "cosyvoice_variant" : cosyvoice_variant })
378- except Exception as e :
379- print (f"⚠️ Error setting inline tag settings: { e } " )
380- return web .json_response ({"status" : "error" , "error" : str (e )})
381-
382- @PromptServer .instance .routes .get ("/api/tts-audio-suite/voice-preview" )
383- async def get_voice_preview_endpoint (request ):
384- """
385- Stream selected Character Voices dropdown audio for browser preview playback.
386-
387- Query params:
388- - voice_name: exact dropdown key from get_available_voices()
389- """
390- try :
391- voice_name = request .query .get ("voice_name" , "" ).strip ()
392- if not voice_name or voice_name == "none" :
393- return web .json_response ({"error" : "voice_name is required and cannot be 'none'" }, status = 400 )
394-
395- # Load voice discovery directly by file path to avoid package import issues
396- voice_discovery_path = os .path .join (os .path .dirname (__file__ ), "utils" , "voice" , "discovery.py" )
397- spec = importlib .util .spec_from_file_location ("voice_discovery_module" , voice_discovery_path )
398- voice_discovery_module = importlib .util .module_from_spec (spec )
399- spec .loader .exec_module (voice_discovery_module )
400-
401- # Use cached discovery for fast preview playback.
402- voice_discovery_module .get_available_voices (force_refresh = False )
403- audio_path , _ = voice_discovery_module .load_voice_reference (voice_name )
404-
405- if not audio_path or not os .path .exists (audio_path ):
406- return web .json_response ({"error" : f"Voice file not found: { voice_name } " }, status = 404 )
407-
408- # Direct stream of resolved local audio file.
409- response = web .FileResponse (path = audio_path )
410- response .headers ["Cache-Control" ] = "no-store, no-cache, must-revalidate"
411- return response
412- except Exception as e :
413- print (f"⚠️ Error serving voice preview audio: { e } " )
414- return web .json_response ({"error" : str (e )}, status = 500 )
415- except Exception as e :
416- print (f"⚠️ Could not setup API routes: { e } " )
390+ except Exception as e :
391+ print (f"⚠️ Error setting inline tag settings: { e } " )
392+ return web .json_response ({"status" : "error" , "error" : str (e )})
393+
394+ @PromptServer .instance .routes .get ("/api/tts-audio-suite/voice-preview" )
395+ async def get_voice_preview_endpoint (request ):
396+ """
397+ Stream selected Character Voices dropdown audio for browser preview playback.
398+
399+ Query params:
400+ - voice_name: exact dropdown key from get_available_voices()
401+ """
402+ try :
403+ voice_name = request .query .get ("voice_name" , "" ).strip ()
404+ if not voice_name or voice_name == "none" :
405+ return web .json_response ({"error" : "voice_name is required and cannot be 'none'" }, status = 400 )
406+
407+ # Load voice discovery directly by file path to avoid package import issues
408+ voice_discovery_path = os .path .join (os .path .dirname (__file__ ), "utils" , "voice" , "discovery.py" )
409+ spec = importlib .util .spec_from_file_location ("voice_discovery_module" , voice_discovery_path )
410+ voice_discovery_module = importlib .util .module_from_spec (spec )
411+ spec .loader .exec_module (voice_discovery_module )
412+
413+ # Use cached discovery for fast preview playback.
414+ voice_discovery_module .get_available_voices (force_refresh = False )
415+ audio_path , _ = voice_discovery_module .load_voice_reference (voice_name )
416+
417+ if not audio_path or not os .path .exists (audio_path ):
418+ return web .json_response ({"error" : f"Voice file not found: { voice_name } " }, status = 404 )
419+
420+ # Direct stream of resolved local audio file.
421+ response = web .FileResponse (path = audio_path )
422+ response .headers ["Cache-Control" ] = "no-store, no-cache, must-revalidate"
423+ return response
424+ except Exception as e :
425+ print (f"⚠️ Error serving voice preview audio: { e } " )
426+ return web .json_response ({"error" : str (e )}, status = 500 )
427+ except Exception as e :
428+ print (f"⚠️ Could not setup API routes: { e } " )
417429
418430# Setup API routes when extension loads
419431setup_api_routes ()
0 commit comments