Spaces:
Sleeping
Sleeping
| """ | |
| TTS Cache using Redis or in-memory fallback | |
| """ | |
| import hashlib | |
| import logging | |
| import os | |
| from typing import Optional | |
| logger = logging.getLogger(__name__) | |
| # Try to import redis, fallback to in-memory cache | |
| try: | |
| import redis.asyncio as redis | |
| REDIS_AVAILABLE = True | |
| except ImportError: | |
| REDIS_AVAILABLE = False | |
| logger.warning("Redis not available, using in-memory cache") | |
| class TTSCache: | |
| def __init__(self): | |
| self.ttl = 86400 * 7 # 7 days | |
| self.redis_client = None | |
| self.memory_cache: dict[str, str] = {} | |
| self.max_memory_items = 1000 | |
| # Try to connect to Redis | |
| redis_url = os.environ.get("REDIS_URL", "redis://localhost:6379") | |
| if REDIS_AVAILABLE: | |
| try: | |
| self.redis_client = redis.from_url(redis_url, decode_responses=True) | |
| logger.info(f"Redis cache initialized: {redis_url}") | |
| except Exception as e: | |
| logger.warning(f"Redis connection failed, using memory cache: {e}") | |
| self.redis_client = None | |
| def _key(self, text: str) -> str: | |
| """Generate cache key from text hash""" | |
| return f"tts:{hashlib.md5(text.encode()).hexdigest()}" | |
| async def get(self, text: str) -> Optional[str]: | |
| """Get cached audio (base64) for text""" | |
| key = self._key(text) | |
| # Try Redis first | |
| if self.redis_client: | |
| try: | |
| result = await self.redis_client.get(key) | |
| if result: | |
| logger.debug(f"Redis cache hit for key: {key}") | |
| return result | |
| except Exception as e: | |
| logger.warning(f"Redis get failed: {e}") | |
| # Fallback to memory cache | |
| result = self.memory_cache.get(key) | |
| if result: | |
| logger.debug(f"Memory cache hit for key: {key}") | |
| return result | |
| async def set(self, text: str, audio_b64: str): | |
| """Cache audio (base64) for text""" | |
| key = self._key(text) | |
| # Try Redis first | |
| if self.redis_client: | |
| try: | |
| await self.redis_client.setex(key, self.ttl, audio_b64) | |
| logger.debug(f"Cached to Redis: {key}") | |
| return | |
| except Exception as e: | |
| logger.warning(f"Redis set failed: {e}") | |
| # Fallback to memory cache with LRU eviction | |
| if len(self.memory_cache) >= self.max_memory_items: | |
| # Remove oldest item (simple FIFO, not true LRU) | |
| oldest_key = next(iter(self.memory_cache)) | |
| del self.memory_cache[oldest_key] | |
| logger.debug(f"Evicted from memory cache: {oldest_key}") | |
| self.memory_cache[key] = audio_b64 | |
| logger.debug(f"Cached to memory: {key}") | |
| async def clear(self): | |
| """Clear all cached items""" | |
| self.memory_cache.clear() | |
| if self.redis_client: | |
| try: | |
| # Clear only TTS keys | |
| async for key in self.redis_client.scan_iter("tts:*"): | |
| await self.redis_client.delete(key) | |
| except Exception as e: | |
| logger.warning(f"Redis clear failed: {e}") | |