tts_cache.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. """
  2. TTS音频缓存管理器模块
  3. 提供音频数据的本地缓存功能,避免重复调用TTS API
  4. """
  5. import threading
  6. import time
  7. import hashlib
  8. import pickle
  9. from pathlib import Path
  10. from utils.logger import logger
  11. class TTSCache:
  12. """TTS音频缓存管理器"""
  13. def __init__(self, cache_file: str = "tts_cache/tts_cache.pkl"):
  14. self.cache_file = Path(cache_file)
  15. self.cache_file.parent.mkdir(parents=True, exist_ok=True)
  16. self.cache_data = self._load_cache_data()
  17. self._lock = threading.Lock()
  18. def _load_cache_data(self):
  19. """加载缓存数据"""
  20. if self.cache_file.exists():
  21. try:
  22. with open(self.cache_file, 'rb') as f:
  23. return pickle.load(f)
  24. except Exception as e:
  25. logger.info(f"[缓存] 加载缓存数据失败: {e}")
  26. return {}
  27. def _save_cache_data(self):
  28. """保存缓存数据"""
  29. try:
  30. with open(self.cache_file, 'wb') as f:
  31. pickle.dump(self.cache_data, f)
  32. except Exception as e:
  33. logger.info(f"[缓存] 保存缓存数据失败: {e}")
  34. def _get_cache_key(self, text: str, voice: str = None) -> str:
  35. """生成缓存键"""
  36. if voice is None:
  37. from config.config.settings import config
  38. voice = config.TTS_VOICE
  39. # 使用文本内容和语音参数的哈希作为缓存键
  40. cache_data = f"{text.strip()}_{voice}"
  41. return hashlib.md5(cache_data.encode('utf-8')).hexdigest()
  42. def get_cached_audio(self, text: str, voice: str = None) -> bytes:
  43. """获取缓存的音频数据"""
  44. with self._lock:
  45. cache_key = self._get_cache_key(text, voice)
  46. if cache_key in self.cache_data:
  47. cache_info = self.cache_data[cache_key]
  48. audio_data = cache_info.get('audio_data')
  49. if audio_data:
  50. logger.info(f"[缓存] 命中缓存: {text[:20]}...")
  51. return audio_data
  52. else:
  53. # 删除损坏的缓存记录
  54. del self.cache_data[cache_key]
  55. self._save_cache_data()
  56. return None
  57. def save_audio_cache(self, text: str, audio_data: bytes, voice: str = None):
  58. """保存音频数据到缓存"""
  59. with self._lock:
  60. cache_key = self._get_cache_key(text, voice)
  61. try:
  62. # 更新缓存数据
  63. if voice is None:
  64. from config.config.settings import config
  65. voice = config.TTS_VOICE
  66. self.cache_data[cache_key] = {
  67. 'text': text,
  68. 'voice': voice,
  69. 'audio_data': audio_data,
  70. 'size': len(audio_data),
  71. 'created_time': time.time()
  72. }
  73. self._save_cache_data()
  74. logger.info(
  75. f"[缓存] 已缓存: {text[:20]}... (大小: {len(audio_data)} bytes)")
  76. except Exception as e:
  77. logger.info(f"[缓存] 保存缓存失败: {e}")
  78. def clear_cache(self, max_age_days: int = 30):
  79. """清理过期缓存"""
  80. with self._lock:
  81. current_time = time.time()
  82. expired_keys = []
  83. for cache_key, cache_info in self.cache_data.items():
  84. age_days = (current_time -
  85. cache_info['created_time']) / (24 * 3600)
  86. if age_days > max_age_days:
  87. expired_keys.append(cache_key)
  88. for cache_key in expired_keys:
  89. del self.cache_data[cache_key]
  90. if expired_keys:
  91. self._save_cache_data()
  92. logger.info(f"[缓存] 清理了 {len(expired_keys)} 个过期缓存")
  93. def get_cache_stats(self):
  94. """获取缓存统计信息"""
  95. with self._lock:
  96. total_size = sum(info['size']
  97. for info in self.cache_data.values())
  98. return {
  99. 'total_entries': len(self.cache_data),
  100. 'total_size_bytes': total_size,
  101. 'total_size_mb': total_size / (1024 * 1024)
  102. }
  103. def clear_all_cache(self):
  104. """清空所有缓存"""
  105. with self._lock:
  106. self.cache_data.clear()
  107. self._save_cache_data()
  108. logger.info("[缓存] 已清空所有缓存")
  109. def get_cache_info(self, text: str, voice: str = None):
  110. """获取指定文本的缓存信息"""
  111. with self._lock:
  112. cache_key = self._get_cache_key(text, voice)
  113. if cache_key in self.cache_data:
  114. cache_info = self.cache_data[cache_key].copy()
  115. # 不返回音频数据,只返回元信息
  116. cache_info.pop('audio_data', None)
  117. return cache_info
  118. return None
  119. # 全局缓存实例
  120. _tts_cache = TTSCache()
  121. def get_cache_instance():
  122. """获取缓存实例"""
  123. return _tts_cache
  124. def get_cached_audio(text: str, voice: str = None) -> bytes:
  125. """获取缓存的音频数据"""
  126. return _tts_cache.get_cached_audio(text, voice)
  127. def save_audio_cache(text: str, audio_data: bytes, voice: str = None):
  128. """保存音频数据到缓存"""
  129. _tts_cache.save_audio_cache(text, audio_data, voice)
  130. def clear_cache(max_age_days: int = 30):
  131. """清理过期缓存"""
  132. _tts_cache.clear_cache(max_age_days)
  133. def get_cache_stats():
  134. """获取缓存统计信息"""
  135. return _tts_cache.get_cache_stats()
  136. def clear_all_cache():
  137. """清空所有缓存"""
  138. _tts_cache.clear_all_cache()
  139. def get_cache_info(text: str, voice: str = None):
  140. """获取指定文本的缓存信息"""
  141. return _tts_cache.get_cache_info(text, voice)