o
    i'                     @   sp   d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlmZm	Z	m
Z
 d dlmZ e  G dd dZdS )    N)OptionalListTuple)load_dotenvc                   @   s   e Zd Zdd ZdedefddZdee fddZd	edee fd
dZdede	fddZ
ddedee dee fddZdS )
TTSServicec                 C   s   t jt jt jt| _|  | _t dd}t dd}| 	|| _
| 	|| _d| _td | js;td ntd| j  | j
rMt j| j
sWtd	| j
 d
 ntd| j
  | jrddnd| _| jrptd dS td dS )zk
        Initializes the Piper TTS service with automatic path correction and absolute resolution.
        PIPER_MODEL_PATHz&assets/models/en_US-lessac-medium.onnxPIPER_CONFIG_PATHz+assets/models/en_US-lessac-medium.onnx.jsonFu!   
🔍 Piper TTS Diagnostic Check:u8      - Binary: ❌ NOT FOUND (Check PIPER_EXECUTABLE_PATH)u      - Binary: ✅ FOUND at u%      - Model:  ❌ NOT FOUND (Checked: )u      - Model:  ✅ FOUND at Tu$   🚀 Piper TTS Service Fully Active
u9   ⚠️ Piper TTS Service Disabled (Missing requirements)
N)ospathdirnameabspath__file__base_dir_resolve_piper_executable
piper_pathgetenv_make_absolute
model_pathconfig_path	availableprintexists)self	model_env
config_env r   C/var/www/eduai.edurigo.com/roleplay/testing/services/tts_service.py__init__   s$   

zTTSService.__init__r   returnc                 C   s(   |sdS t j|r|S t j| j|S )z@Converts relative paths to absolute paths based on project root. )r
   r   isabsjoinr   )r   r   r   r   r   r   2   s   zTTSService._make_absolutec                    s   ddg}t d}|r9t j|r%||vr$t j|r$t |t jr$|S nt|r9t	fdd|D s9S dD ]}t|  rSt	 fdd|D sS   S q;dD ] t j rmt  t jrm |vrm   S qVd	S )
z
        Strategically searches for the piper binary in common production paths.
        STRICTLY blocks /usr/bin/piper to avoid the Ubuntu GTK conflict.
        z/usr/bin/piperz
/bin/piperPIPER_EXECUTABLE_PATHc                 3       | ]}| v V  qd S Nr   .0b)resolvedr   r   	<genexpr>I       z7TTSService._resolve_piper_executable.<locals>.<genexpr>)z	piper-ttspiperc                 3   r$   r%   r   r&   )r   r   r   r*   O   r+   )z/usr/local/bin/piper-ttsz/usr/local/bin/piperz/opt/piper/piperz/opt/piper/piper/piperN)
r
   r   r   r!   r   accessX_OKshutilwhichany)r   	BLACKLISTenv_pathnamer   )r   r)   r   r   8   s(   
"

"z$TTSService._resolve_piper_executabler   c                 C   s   |d }t j|r|S dS )z7Returns the .json config path for a model if it exists.z.jsonN)r
   r   r   )r   r   r   r   r   r   _get_config_pathZ   s   zTTSService._get_config_pathvoicec              	      s  t j| jdd}t j|sdS ||dd|ddg}td|}t|dkrB|	d|dd	  |	d|dd	  t
t|}|D ]}t j|| d
}t j|rf|| |f  S qK|g| }h d  fdd|D }|D ]/}t j|d| d}	t|	}
|
r|
d }td| dt j|  || |f  S q{| jrt j| jrtd| dt| dt j| j  | j| jfS tt j|d}|r|d }tdt j|  || |fS dS )z
        Ultra-robust resolution for Piper voice models.
        Tries exact match, naming variants, and globbing to find the best possible file.
        assetsmodels)NN_-z[-_]   Nz.onnx>	   low-polyengbinuslowhighr6   mediumc                    s(   g | ]}|   vrt|d kr|qS )   )lowerlen)r'   t	stopwordsr   r   
<listcomp>   s   ( z3TTSService._resolve_voice_model.<locals>.<listcomp>*z*.onnxr   u   🎙️ Fuzzy match found for 'z': u   ⚠️ Piper voice 'z' not found (tried z, variants + glob). Falling back to default: u!   ⚠️ Using emergency fallback: )r
   r   r"   r   r   replaceresplitrG   appendlistdictfromkeysr5   globr   basenamer   r   )r   r6   
models_dirvariantspartsvr   search_termstermpatternmatchesmatchavailable_modelsfallbackr   rI   r   _resolve_voice_model_   sJ   



(zTTSService._resolve_voice_modelNtextc                 C   s  | j s	td dS |r| std dS |r+| |\}}|s*td| d dS n| j}| j}|dddd }d}z}ztjd	d
d}|j	}W d   n1 sXw   Y  | j
d|d|g}|rttj|rt|d|g td|pyd d|dd  d tj|tjtjtjddd}	|	j|dd\}
}|	jdkrtd|	j d|  W W |rtj|rzt| W dS  ty   Y dS w dS dS tj|r(tj|dkr(t|d=}t| d}tdt| d |W  d   W W |rtj|rzt| W S  ty   Y S w S S 1 s#w   Y  td W W |rLtj|rNzt| W dS  tyK   Y dS w dS dS  tjy   td  Y W |r|tj|r~zt| W dS  ty{   Y dS w dS dS  ty } z1td!t|  W Y d}~W |rtj|rzt| W dS  ty   Y dS w dS dS d}~ww |rtj|rzt| W w  ty   Y w w w w )"a  
        Generates high-quality audio base64 string using the specified Piper voice model.
        Thread-safe and optimized using temporary file isolation.

        Args:
            text: The text to synthesize.
            voice: Optional Piper voice model name (e.g. "en_US-hfc_female-medium").
                   If provided, uses that model; otherwise uses the default model.
        uE   ⚠️ TTS skipped: Service not available (Check binary/model paths).Nu(   ⚠️ TTS skipped: Empty text provided.u!   ⚠️ TTS skipped: Voice model 'z' not found in assets/models/."r    'z.wavF)suffixdeletez--modelz--output_filez--configu   🎙️ Generating voice (defaultz) for text: 2   z...Tzutf-8)stdinstdoutstderrrb   encoding   )inputtimeoutr   u    ❌ Piper Execution Error (Code z): rbu"   ✅ Voice generated successfully (z chars)u"   ❌ Piper generated an empty file.u(   ❌ Piper process timed out (30s limit).u'   ❌ Exception in Piper TTS processing: )r   r   stripra   r   r   rM   tempfileNamedTemporaryFiler4   r   r
   r   r   extend
subprocessPopenPIPEcommunicate
returncoderemove	Exceptiongetsizeopenbase64	b64encodereaddecoderG   TimeoutExpiredstr)r   rb   r6   active_modelactive_config
clean_texttemp_wav_pathtemp_wavcommandprocessrj   rk   
audio_fileb64er   r   r   generate_voice_base64   s   
"	
 	z TTSService.generate_voice_base64r%   )__name__
__module____qualname__r   r   r   r   r   r5   tuplera   r   r   r   r   r   r      s    $"$<r   )r
   r~   ru   rr   r/   rN   rT   typingr   r   r   dotenvr   r   r   r   r   r   <module>   s    