import 'package:em2rp/services/text_to_speech_service.dart'; import 'package:em2rp/services/cloud_text_to_speech_service.dart'; import 'package:em2rp/utils/debug_log.dart'; /// Service hybride intelligent pour le Text-to-Speech /// Essaie d'abord Web Speech API (gratuit, rapide), puis fallback vers Cloud TTS class SmartTextToSpeechService { static bool _initialized = false; static bool _webSpeechWorks = true; // Optimiste par défaut static int _webSpeechFailures = 0; static const int _maxFailuresBeforeSwitch = 2; /// Initialiser le service static Future initialize() async { if (_initialized) return; try { DebugLog.info('[SmartTTS] Initializing...'); // Initialiser Web Speech API await TextToSpeechService.initialize(); // Pré-charger les phrases courantes pour Cloud TTS en arrière-plan // (ne bloque pas l'initialisation) Future.delayed(const Duration(seconds: 2), () { CloudTextToSpeechService.preloadCommonPhrases(); }); _initialized = true; DebugLog.info('[SmartTTS] ✓ Initialized (Web Speech preferred)'); } catch (e) { DebugLog.error('[SmartTTS] Initialization error', e); _initialized = true; // Continuer quand même } } /// Lire un texte à haute voix avec stratégie intelligente static Future speak(String text) async { if (!_initialized) { await initialize(); } // Si Web Speech a échoué plusieurs fois, utiliser directement Cloud TTS if (!_webSpeechWorks || _webSpeechFailures >= _maxFailuresBeforeSwitch) { return _speakWithCloudTTS(text); } // Essayer Web Speech d'abord try { await _speakWithWebSpeech(text); // Si succès, réinitialiser le compteur d'échecs _webSpeechFailures = 0; } catch (e) { DebugLog.warning('[SmartTTS] Web Speech failed ($e), trying Cloud TTS...'); _webSpeechFailures++; // Si trop d'échecs, basculer vers Cloud TTS par défaut if (_webSpeechFailures >= _maxFailuresBeforeSwitch) { DebugLog.info('[SmartTTS] Switching to Cloud TTS as primary'); _webSpeechWorks = false; } // Fallback vers Cloud TTS await _speakWithCloudTTS(text); } } /// Utiliser Web Speech API static Future _speakWithWebSpeech(String text) async { DebugLog.info('[SmartTTS] → Trying Web Speech API'); // Timeout pour détecter si ça ne marche pas await Future.any([ TextToSpeechService.speak(text), Future.delayed(const Duration(seconds: 3), () { throw Exception('Web Speech timeout'); }), ]); DebugLog.info('[SmartTTS] ✓ Web Speech succeeded'); } /// Utiliser Cloud TTS static Future _speakWithCloudTTS(String text) async { DebugLog.info('[SmartTTS] → Using Cloud TTS'); try { await CloudTextToSpeechService.speak(text); DebugLog.info('[SmartTTS] ✓ Cloud TTS succeeded'); } catch (e) { DebugLog.error('[SmartTTS] ✗ Cloud TTS failed', e); // En dernier recours, réessayer Web Speech if (!_webSpeechWorks) { DebugLog.info('[SmartTTS] Last resort: trying Web Speech again'); await TextToSpeechService.speak(text); } else { rethrow; } } } /// Arrêter toute lecture en cours static Future stop() async { try { await TextToSpeechService.stop(); CloudTextToSpeechService.stopAll(); } catch (e) { DebugLog.error('[SmartTTS] Error stopping', e); } } /// Vérifier si une lecture est en cours static Future isSpeaking() async { try { return await TextToSpeechService.isSpeaking(); } catch (e) { return false; } } /// Forcer l'utilisation de Cloud TTS (pour tests ou préférence utilisateur) static void forceCloudTTS() { DebugLog.info('[SmartTTS] Forced to use Cloud TTS'); _webSpeechWorks = false; _webSpeechFailures = _maxFailuresBeforeSwitch; } /// Forcer l'utilisation de Web Speech (pour tests ou préférence utilisateur) static void forceWebSpeech() { DebugLog.info('[SmartTTS] Forced to use Web Speech'); _webSpeechWorks = true; _webSpeechFailures = 0; } /// Réinitialiser la stratégie (utile pour tests) static void resetStrategy() { DebugLog.info('[SmartTTS] Strategy reset'); _webSpeechWorks = true; _webSpeechFailures = 0; } /// Obtenir le statut actuel static Map getStatus() { return { 'initialized': _initialized, 'webSpeechWorks': _webSpeechWorks, 'failures': _webSpeechFailures, 'currentStrategy': _webSpeechWorks ? 'Web Speech (primary)' : 'Cloud TTS (primary)', }; } /// Nettoyer les ressources static Future dispose() async { try { await TextToSpeechService.dispose(); CloudTextToSpeechService.clearCache(); } catch (e) { DebugLog.error('[SmartTTS] Error disposing', e); } } }