This commit is contained in:
2025-09-05 20:38:40 +08:00
parent d8ecfd0d91
commit 0b4e6c5285
5 changed files with 54 additions and 22 deletions

View File

@@ -94,3 +94,9 @@ HS_VOICE_TYPE_QINCANG = "BV701_V2_streaming" # 中年男声,用于朗读古
LIBLIB_URL="https://openapi.liblibai.cloud"
LIBLIB_ACCESSKEY="sOCtVLVTNOZkRMajlhzCmg"
LIBLIB_SECRETKEY="PUe8QTRG9i0G9EbpedHmIpLQ0FyxoYY9"
# 科大讯飞
XF_APPID="5b83f8d6"
XF_APISECRET="604fa6cb9c5ab664a0d153fe0ccc6802"
XF_APIKEY="5beb887923204000bfcb402046bb05a6"

View File

@@ -2,12 +2,12 @@ import logging
import os
import uuid
import time
from fastapi import APIRouter, HTTPException, BackgroundTasks, Query, UploadFile, File
from fastapi import APIRouter, HTTPException, BackgroundTasks, Query, UploadFile, File, Form
from pydantic import BaseModel
from typing import Optional
import tempfile
from Util.ObsUtil import ObsUploader
from Config.Config import OBS_BUCKET, OBS_SERVER
from Config.Config import OBS_BUCKET, OBS_SERVER, XF_APPID, XF_APISECRET, XF_APIKEY
from fastapi.responses import StreamingResponse
import requests
@@ -36,19 +36,19 @@ class AudioEvaluationResponse(BaseModel):
# 科大讯飞API配置需要根据实际情况配置
XUNFEI_CONFIG = {
"appid": "your_appid_here",
"api_key": "your_api_key_here",
"api_secret": "your_api_secret_here"
"appid": XF_APPID,
"api_key": XF_APISECRET,
"api_secret": XF_APIKEY
}
@router.post("/evaluate-audio", response_model=AudioEvaluationResponse)
async def evaluate_audio(
background_tasks: BackgroundTasks,
language: str = Query("chinese", description="评测语言: chinese 或 english"),
text: str = Query(..., description="评测文本内容"),
group: str = Query("adult", description="群体类型: adult, youth, pupil"),
check_type: str = Query("common", description="检错严格程度: easy, common, hard"),
grade: str = Query("middle", description="学段: junior, middle, senior"),
language: str = Form("chinese", description="评测语言: chinese 或 english"),
text: str = Form(..., description="评测文本内容"),
group: str = Form("adult", description="群体类型: adult, youth, pupil"),
check_type: str = Form("common", description="检错严格程度: easy, common, hard"),
grade: str = Form("middle", description="学段: junior, middle, senior"),
audio_file: UploadFile = File(...)):
"""
语音评测接口 - 支持中文和英文篇章朗读判分

View File

@@ -104,7 +104,6 @@
<div class="form-group">
<button id="recordBtn" class="btn btn-record">开始录音</button>
<button id="stopBtn" class="btn btn-stop" disabled>停止录音</button>
<button id="evaluateBtn" class="btn" disabled>提交评测</button>
</div>
<div id="status" class="status">准备就绪</div>
@@ -125,7 +124,7 @@
const textInput = document.getElementById('text');
const recordBtn = document.getElementById('recordBtn');
const stopBtn = document.getElementById('stopBtn');
const evaluateBtn = document.getElementById('evaluateBtn');
// 删除提交按钮引用
const statusDiv = document.getElementById('status');
const resultDiv = document.getElementById('result');
const resultContent = document.getElementById('resultContent');
@@ -150,6 +149,24 @@
statusDiv.textContent = '正在获取麦克风权限...';
statusDiv.className = 'status';
// ==== 插入WebSocket认证代码 ====
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${wsProtocol}//${window.location.host}/ws/audio-evaluation?token=${getAuthToken()}`;
const ws = new WebSocket(wsUrl);
// WebSocket事件处理
ws.onopen = () => {
console.log('WebSocket连接已建立');
statusDiv.textContent = 'WebSocket连接已建立准备录音...';
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
statusDiv.textContent = 'WebSocket连接失败请刷新页面重试';
statusDiv.className = 'status error';
};
// ==== 插入结束 ====
// 使用更明确的提示并添加详细的错误处理
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
.catch(err => {
@@ -174,8 +191,10 @@
mediaRecorder.onstop = () => {
audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
statusDiv.textContent = '录音完成,可以提交评测';
evaluateBtn.disabled = false;
statusDiv.textContent = '录音完成,正在自动提交评测...';
// 添加WebSocket关闭逻辑
if (ws) ws.close(1000, '录音已完成');
submitEvaluation();
};
// 添加录音最大时长限制60秒
@@ -218,8 +237,8 @@
}
});
// 提交评测
evaluateBtn.addEventListener('click', async () => {
// 提交评测逻辑提取为独立函数
async function submitEvaluation() {
if (!audioBlob) {
statusDiv.textContent = '请先完成录音';
statusDiv.className = 'status error';
@@ -235,7 +254,6 @@
try {
statusDiv.textContent = '正在提交评测...';
statusDiv.className = 'status';
evaluateBtn.disabled = true;
const formData = new FormData();
formData.append('audio_file', audioBlob, 'recording.webm');
@@ -268,10 +286,8 @@
console.error('评测失败:', error);
statusDiv.textContent = '评测失败: ' + error.message;
statusDiv.className = 'status error';
} finally {
evaluateBtn.disabled = false;
}
});
}
// 显示评测结果
function displayResults(results) {
@@ -313,6 +329,16 @@
resultContent.innerHTML = html;
}
// 在<script>标签内添加getAuthToken实现
function getAuthToken() {
// 实际项目中应从Cookie、localStorage或后端API获取
return 'your_auth_token_here'; // 临时占位,需替换为真实认证逻辑
}
// 页面卸载时关闭连接
window.addEventListener('beforeunload', () => {
if (ws) ws.close(1001, '页面即将关闭');
});
</script>
</body>
</html>