|
|
|
|
|
import requests |
|
|
from requests.adapters import HTTPAdapter |
|
|
from urllib3.util.retry import Retry |
|
|
from typing import Optional, Dict, Any |
|
|
import ssl |
|
|
import logging |
|
|
from elevenlabs.client import ElevenLabs |
|
|
from io import BytesIO |
|
|
import time |
|
|
|
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
class TLSAdapter(HTTPAdapter): |
|
|
"""自定義 TLS 適配器解決 SSL 協議問題""" |
|
|
def init_poolmanager(self, *args, **kwargs): |
|
|
ctx = ssl.create_default_context() |
|
|
ctx.set_ciphers('DEFAULT@SECLEVEL=1') |
|
|
ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 |
|
|
kwargs['ssl_context'] = ctx |
|
|
return super().init_poolmanager(*args, **kwargs) |
|
|
|
|
|
|
|
|
def create_retry_session(): |
|
|
"""建立具有重試機制的 Session""" |
|
|
session = requests.Session() |
|
|
retry = Retry( |
|
|
total=5, |
|
|
backoff_factor=1, |
|
|
status_forcelist=[500, 502, 503, 504], |
|
|
allowed_methods=["POST"] |
|
|
) |
|
|
adapter = HTTPAdapter(max_retries=retry) |
|
|
session.mount("https://", adapter) |
|
|
return session |
|
|
|
|
|
|
|
|
def transcribe_audio( |
|
|
api_key: str, |
|
|
file_path: str, |
|
|
language_code: Optional[str] = None, |
|
|
diarize: bool = False, |
|
|
max_retries: int = 5, |
|
|
timeout: int = 600 |
|
|
) -> Optional[Dict[str, Any]]: |
|
|
""" |
|
|
使用 ElevenLabs API 將音訊轉換為文字,包含重試機制 |
|
|
|
|
|
Args: |
|
|
api_key: ElevenLabs API 金鑰 |
|
|
file_path: 音訊檔案路徑 |
|
|
language_code: 語言代碼(可選,使用 ISO-639-1 或 ISO-639-3 格式) |
|
|
diarize: 是否啟用說話者辨識(限制音訊長度最長 8 分鐘) |
|
|
max_retries: 最大重試次數 |
|
|
timeout: 請求超時時間(秒) |
|
|
""" |
|
|
|
|
|
client = ElevenLabs( |
|
|
api_key=api_key, |
|
|
) |
|
|
|
|
|
for attempt in range(max_retries): |
|
|
try: |
|
|
|
|
|
with open(file_path, 'rb') as audio_file: |
|
|
audio_data = BytesIO(audio_file.read()) |
|
|
|
|
|
|
|
|
params = { |
|
|
"file": audio_data, |
|
|
"model_id": "scribe_v1", |
|
|
"diarize": diarize, |
|
|
"tag_audio_events": True, |
|
|
"timestamps_granularity": "word" |
|
|
} |
|
|
|
|
|
|
|
|
if language_code and language_code.strip(): |
|
|
params["language_code"] = language_code.strip() |
|
|
|
|
|
|
|
|
response = client.speech_to_text.convert(**params) |
|
|
|
|
|
|
|
|
if hasattr(response, 'text'): |
|
|
language_code = getattr( |
|
|
response, 'language_code', None |
|
|
) |
|
|
language_prob = getattr( |
|
|
response, 'language_probability', None |
|
|
) |
|
|
return { |
|
|
'text': response.text, |
|
|
'language_code': language_code, |
|
|
'language_probability': language_prob |
|
|
} |
|
|
return response |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"第 {attempt + 1} 次嘗試失敗:{str(e)}") |
|
|
if attempt < max_retries - 1: |
|
|
wait_time = min((attempt + 1) * 5, 30) |
|
|
logger.info(f"{wait_time} 秒後重試...") |
|
|
time.sleep(wait_time) |
|
|
else: |
|
|
logger.error("已達最大重試次數,轉換失敗") |
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|