a
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
录音后端服务 V2 - 集成语音转文字
|
||||
"""
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
from flask import Flask, request, jsonify, send_from_directory
|
||||
from flask_cors import CORS
|
||||
from werkzeug.utils import secure_filename
|
||||
import os
|
||||
import ssl
|
||||
from datetime import datetime
|
||||
|
||||
# 导入转写模块 - 使用 Vosk
|
||||
from transcribe_api import transcribe_audio
|
||||
|
||||
MODEL_PATH = '/home/ubuntu/nanocode/audio-recorder/models/cn-model'
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
# 配置
|
||||
UPLOAD_FOLDER = '/home/ubuntu/nanocode/audio-recorder/uploads'
|
||||
ALLOWED_EXTENSIONS = {'webm', 'mp4', 'wav', 'mp3', 'ogg'}
|
||||
MAX_CONTENT_LENGTH = 100 * 1024 * 1024
|
||||
|
||||
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
|
||||
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||
|
||||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
|
||||
@app.route('/api/upload', methods=['POST'])
|
||||
def upload_audio():
|
||||
"""接收音频文件上传"""
|
||||
if 'audio' not in request.files:
|
||||
return jsonify({'error': '没有文件'}), 400
|
||||
|
||||
file = request.files['audio']
|
||||
if file.filename == '':
|
||||
return jsonify({'error': '文件名为空'}), 400
|
||||
|
||||
if file and allowed_file(file.filename):
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
filename = f"recording_{timestamp}.webm"
|
||||
filepath = os.path.join(UPLOAD_FOLDER, filename)
|
||||
|
||||
file.save(filepath)
|
||||
file_size = os.path.getsize(filepath)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'filename': filename,
|
||||
'filepath': filepath,
|
||||
'size': file_size,
|
||||
'size_human': f"{file_size / 1024:.1f} KB",
|
||||
'timestamp': timestamp
|
||||
}), 200
|
||||
|
||||
return jsonify({'error': '不支持的文件类型'}), 400
|
||||
|
||||
|
||||
@app.route('/api/transcribe', methods=['POST'])
|
||||
def transcribe():
|
||||
"""语音转文字"""
|
||||
if 'audio' not in request.files:
|
||||
return jsonify({'error': '没有音频文件'}), 400
|
||||
|
||||
file = request.files['audio']
|
||||
if file.filename == '':
|
||||
return jsonify({'error': '文件名为空'}), 400
|
||||
|
||||
try:
|
||||
# 保存上传的音频
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
filename = f"recording_{timestamp}.webm"
|
||||
filepath = os.path.join(UPLOAD_FOLDER, filename)
|
||||
file.save(filepath)
|
||||
|
||||
print(f"🎵 收到音频: {filename}, 大小: {os.path.getsize(filepath)} bytes")
|
||||
|
||||
# 转写
|
||||
text, error = transcribe_audio(filepath)
|
||||
|
||||
if error:
|
||||
print(f"❌ 转写失败: {error}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': error,
|
||||
'filename': filename
|
||||
}), 500
|
||||
|
||||
print(f"✅ 转写成功: {text[:50]}...")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'text': text or '(未识别到语音)',
|
||||
'filename': filename,
|
||||
'model_type': 'vosk' if os.path.exists(MODEL_PATH) else 'mock'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 服务器错误: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
@app.route('/api/recordings', methods=['GET'])
|
||||
def list_recordings():
|
||||
"""列出所有录音文件"""
|
||||
recordings = []
|
||||
|
||||
for filename in sorted(os.listdir(UPLOAD_FOLDER), reverse=True):
|
||||
if allowed_file(filename):
|
||||
filepath = os.path.join(UPLOAD_FOLDER, filename)
|
||||
stat = os.stat(filepath)
|
||||
recordings.append({
|
||||
'filename': filename,
|
||||
'size': stat.st_size,
|
||||
'size_human': f"{stat.st_size / 1024:.1f} KB",
|
||||
'created': datetime.fromtimestamp(stat.st_mtime).isoformat()
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'count': len(recordings),
|
||||
'recordings': recordings
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/recordings/<filename>', methods=['GET'])
|
||||
def get_recording(filename):
|
||||
"""获取单个录音文件"""
|
||||
return send_from_directory(UPLOAD_FOLDER, filename)
|
||||
|
||||
|
||||
@app.route('/api/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""健康检查"""
|
||||
model_exists = os.path.exists(MODEL_PATH)
|
||||
return jsonify({
|
||||
'status': 'ok',
|
||||
'model_loaded': model_exists,
|
||||
'model_path': MODEL_PATH,
|
||||
'upload_folder': UPLOAD_FOLDER,
|
||||
'recordings_count': len([f for f in os.listdir(UPLOAD_FOLDER) if allowed_file(f)])
|
||||
})
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return send_from_directory('../frontend', 'index.html')
|
||||
|
||||
|
||||
@app.route('/<path:path>')
|
||||
def static_files(path):
|
||||
return send_from_directory('../frontend', path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
context.load_cert_chain(
|
||||
'/home/ubuntu/nanocode/audio-recorder/cert.pem',
|
||||
'/home/ubuntu/nanocode/audio-recorder/key.pem'
|
||||
)
|
||||
|
||||
print(f"🎙️ HTTPS 录音服务 V2 启动中...")
|
||||
print(f"📁 上传目录: {UPLOAD_FOLDER}")
|
||||
print(f"🔤 模型路径: {MODEL_PATH}")
|
||||
print(f"🔒 HTTPS 地址: https://129.211.173.87:8891")
|
||||
print(f"⚠️ 提示: {'✅ 模型已安装' if os.path.exists(MODEL_PATH) else '⚠️ 使用模拟转写,请安装模型'}")
|
||||
|
||||
app.run(
|
||||
host='0.0.0.0',
|
||||
port=8891,
|
||||
ssl_context=context,
|
||||
debug=False
|
||||
)
|
||||
Reference in New Issue
Block a user