190 lines
5.9 KiB
Python
190 lines
5.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
import json
|
||
import time
|
||
from typing import Dict, Tuple
|
||
from pathlib import Path
|
||
from aliyunsdkcore.acs_exception.exceptions import ClientException, ServerException
|
||
from aliyunsdkcore.client import AcsClient
|
||
from aliyunsdkcore.request import CommonRequest
|
||
from Config import *
|
||
from Config.Config import ALY_AK, ALY_SK
|
||
|
||
# 服务常量配置
|
||
CONFIG = {
|
||
"REGION_ID": "cn-shanghai",
|
||
"PRODUCT": "nls-filetrans",
|
||
"DOMAIN": "filetrans.cn-shanghai.aliyuncs.com",
|
||
"API_VERSION": "2018-08-17",
|
||
"MAX_RETRIES": 20,
|
||
"POLL_INTERVAL": 10
|
||
}
|
||
|
||
|
||
class TranscriptionError(Exception):
|
||
"""自定义语音识别异常基类"""
|
||
pass
|
||
|
||
|
||
class APIConnectionError(TranscriptionError):
|
||
"""API连接异常"""
|
||
pass
|
||
|
||
|
||
class TaskSubmissionError(TranscriptionError):
|
||
"""任务提交异常"""
|
||
pass
|
||
|
||
|
||
class TaskTimeoutError(TranscriptionError):
|
||
"""任务超时异常"""
|
||
pass
|
||
|
||
|
||
def submit_transcription_task(client: AcsClient, app_key: str, file_link: str) -> str:
|
||
"""提交语音识别任务
|
||
|
||
:param client: 阿里云客户端实例
|
||
:param app_key: 应用密钥
|
||
:param file_link: 音频文件URL
|
||
:return: 任务ID
|
||
:raises TaskSubmissionError: 任务提交失败时抛出
|
||
"""
|
||
task_config = {
|
||
"appkey": app_key,
|
||
"file_link": file_link,
|
||
"version": "4.0",
|
||
"enable_words": False
|
||
}
|
||
|
||
request = CommonRequest()
|
||
request.set_domain(CONFIG["DOMAIN"])
|
||
request.set_version(CONFIG["API_VERSION"])
|
||
request.set_product(CONFIG["PRODUCT"])
|
||
request.set_action_name("SubmitTask")
|
||
request.set_method('POST')
|
||
request.add_body_params("Task", json.dumps(task_config))
|
||
|
||
try:
|
||
response = client.do_action_with_exception(request)
|
||
response_data = json.loads(response)
|
||
|
||
if response_data.get("StatusText") != "SUCCESS":
|
||
raise TaskSubmissionError(f"任务提交失败: {response_data.get('StatusText', '未知错误')}")
|
||
|
||
return response_data["TaskId"]
|
||
|
||
except (ServerException, ClientException) as e:
|
||
raise APIConnectionError(f"API连接异常: {str(e)}") from e
|
||
except KeyError as e:
|
||
raise TaskSubmissionError("响应缺少TaskId字段") from e
|
||
|
||
|
||
def poll_transcription_result(client: AcsClient, task_id: str) -> Dict:
|
||
"""轮询识别任务结果
|
||
|
||
:param client: 阿里云客户端实例
|
||
:param task_id: 任务ID
|
||
:return: 识别结果字典
|
||
:raises TaskTimeoutError: 超时未完成时抛出
|
||
"""
|
||
request = CommonRequest()
|
||
request.set_domain(CONFIG["DOMAIN"])
|
||
request.set_version(CONFIG["API_VERSION"])
|
||
request.set_product(CONFIG["PRODUCT"])
|
||
request.set_action_name("GetTaskResult")
|
||
request.set_method('GET')
|
||
request.add_query_param("TaskId", task_id)
|
||
|
||
retries = 0
|
||
while retries < CONFIG["MAX_RETRIES"]:
|
||
try:
|
||
response = client.do_action_with_exception(request)
|
||
result = json.loads(response)
|
||
status = result.get("StatusText", "")
|
||
|
||
if status == "SUCCESS":
|
||
return result.get("Result", {})
|
||
if status in ("RUNNING", "QUEUEING"):
|
||
time.sleep(CONFIG["POLL_INTERVAL"])
|
||
retries += 1
|
||
else:
|
||
raise TaskSubmissionError(f"识别失败,状态: {status}")
|
||
|
||
except (ServerException, ClientException) as e:
|
||
raise APIConnectionError(f"查询异常: {str(e)}") from e
|
||
|
||
raise TaskTimeoutError(f"超过最大重试次数({CONFIG['MAX_RETRIES']}),任务未完成")
|
||
|
||
|
||
def save_transcription_result(result: Dict, output_path: Path) -> Path:
|
||
"""保存识别结果到文件
|
||
|
||
:param result: 识别结果字典
|
||
:param output_path: 输出文件路径
|
||
:return: 实际保存路径
|
||
:raises IOError: 文件保存失败时抛出
|
||
"""
|
||
if not result.get('Sentences'):
|
||
raise ValueError("识别结果为空")
|
||
|
||
try:
|
||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||
output_path.write_text(str(result['Sentences']), encoding='utf-8')
|
||
return output_path.resolve()
|
||
except (IOError, PermissionError) as e:
|
||
raise IOError(f"文件保存失败: {str(e)}") from e
|
||
|
||
|
||
def transcribe_audio_file(
|
||
access_key_id: str,
|
||
access_key_secret: str,
|
||
app_key: str,
|
||
audio_url: str,
|
||
output_path: Path = Path("识别结果.txt")
|
||
) -> Tuple[bool, Path, str]:
|
||
"""语音识别主流程
|
||
|
||
:param access_key_id: 阿里云访问密钥ID
|
||
:param access_key_secret: 阿里云访问密钥
|
||
:param app_key: 应用密钥
|
||
:param audio_url: 音频文件URL
|
||
:param output_path: 输出文件路径
|
||
:return: (成功标志, 输出路径, 错误信息)
|
||
"""
|
||
client = AcsClient(access_key_id, access_key_secret, CONFIG["REGION_ID"])
|
||
|
||
try:
|
||
task_id = submit_transcription_task(client, app_key, audio_url)
|
||
print(f"任务已提交,ID: {task_id}")
|
||
|
||
result = poll_transcription_result(client, task_id)
|
||
saved_path = save_transcription_result(result, output_path)
|
||
|
||
return True, saved_path, ""
|
||
|
||
except TranscriptionError as e:
|
||
return False, Path(), str(e)
|
||
except Exception as e:
|
||
return False, Path(), f"未处理的异常: {str(e)}"
|
||
|
||
def ShiBie(audio_url, output_path):
|
||
# 配置参数(应通过环境变量或配置文件获取)
|
||
CREDENTIALS = {
|
||
"access_key_id": ALY_AK,
|
||
"access_key_secret": ALY_SK,
|
||
"app_key": "OIpiw501l4o6MYEe",
|
||
"audio_url":audio_url,
|
||
"output_path": output_path
|
||
}
|
||
|
||
# 执行识别
|
||
success, path, error = transcribe_audio_file(**CREDENTIALS)
|
||
|
||
if success:
|
||
print(f"✅ 识别成功,文件已保存至: {path}")
|
||
else:
|
||
print(f"❌ 识别失败: {error}")
|
||
exit(1)
|
||
|
||
|