server_recording_api_sync.py 10.3 KB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AIfeng/2025-01-02 10:27:06
服务端录音API - 同步版本
基于eman-Fay-main-copy项目的同步实现模式
"""

import json
import time
from flask import Flask, request, jsonify, render_template
from flask_socketio import SocketIO, emit
from threading import Thread
import logging

from recorder_sync import RecorderSync, get_global_recorder, create_global_recorder, destroy_global_recorder
from utils import util
from utils import config_util as cfg

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__, template_folder='web')
app.config['SECRET_KEY'] = 'voice_test_sync_secret_key'
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')

class VoiceTestRecorder(RecorderSync):
    """语音测试录音器"""
    
    def __init__(self, fay):
        super().__init__(fay)
        self.socketio = None
        # 设置状态变化回调
        self.set_status_change_callback(self._on_status_change)
        
    def set_socketio(self, socketio_instance):
        """设置SocketIO实例"""
        self.socketio = socketio_instance
    
    def _on_status_change(self, status):
        """状态变化回调"""
        if self.socketio:
            self.socketio.emit('message', {
                'type': 'status',
                'data': status
            })
            util.log(1, f"状态已更新: 录音={status['is_reading']}, 处理={status['is_processing']}")
        
    def on_speaking(self, text):
        """语音识别结果回调"""
        util.log(1, f"识别结果: {text}")
        
        # 发送识别结果到Web客户端
        if self.socketio:
            self.socketio.emit('message', {
                'type': 'recognition_result',
                'data': {
                    'text': text,
                    'timestamp': time.time(),
                    'username': self.username
                }
            })
            
        # 发送日志
        if self.socketio:
            self.socketio.emit('message', {
                'type': 'log',
                'message': f'识别结果: {text}',
                'level': 'success'
            })

class TestFay:
    """测试用的Fay实例"""
    
    def __init__(self):
        self.sound_query = None
        
    def on_interact(self, interact):
        """处理交互事件"""
        util.log(1, f"收到交互: {interact}")

# 全局变量
test_fay = TestFay()
recorder = None

@app.route('/')
def index():
    """主页"""
    return render_template('voice_test_sync.html')

@app.route('/api/status')
def get_status():
    """获取系统状态"""
    global recorder
    
    if recorder:
        status = recorder.get_status()
        return jsonify({
            'success': True,
            'data': status
        })
    else:
        return jsonify({
            'success': False,
            'message': '录音器未初始化'
        })

@app.route('/api/devices')
def get_devices():
    """获取音频设备列表"""
    global recorder
    
    try:
        if not recorder:
            # 创建临时录音器获取设备列表
            temp_recorder = VoiceTestRecorder(test_fay)
            devices = temp_recorder.list_audio_devices()
        else:
            devices = recorder.list_audio_devices()
            
        return jsonify({
            'success': True,
            'data': devices
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        })

@app.route('/api/start_recording', methods=['POST'])
def start_recording():
    """开始录音"""
    global recorder
    
    try:
        data = request.get_json() or {}
        device_index = data.get('device_index')
        
        if not recorder:
            recorder = create_global_recorder(test_fay)
            if isinstance(recorder, VoiceTestRecorder):
                recorder.set_socketio(socketio)
        
        # 设置设备索引
        if device_index is not None:
            recorder.device_index = int(device_index)
            
        # 开始录音
        success = recorder.start_recording()
        
        if success:
            return jsonify({
                'success': True,
                'message': '录音已开始'
            })
        else:
            return jsonify({
                'success': False,
                'message': '录音启动失败'
            })
            
    except Exception as e:
        util.log(3, f"开始录音时出错: {e}")
        return jsonify({
            'success': False,
            'message': str(e)
        })

@app.route('/api/stop_recording', methods=['POST'])
def stop_recording():
    """停止录音"""
    global recorder
    
    try:
        if recorder:
            recorder.stop_recording()
            return jsonify({
                'success': True,
                'message': '录音已停止'
            })
        else:
            return jsonify({
                'success': False,
                'message': '录音器未初始化'
            })
            
    except Exception as e:
        util.log(3, f"停止录音时出错: {e}")
        return jsonify({
            'success': False,
            'message': str(e)
        })

@socketio.on('connect')
def handle_connect():
    """WebSocket连接建立"""
    util.log(1, f"客户端已连接: {request.sid}")
    
    # 发送连接成功消息
    emit('message', {
        'type': 'log',
        'message': 'WebSocket连接已建立',
        'level': 'success'
    })
    
    # 发送当前状态
    if recorder:
        status = recorder.get_status()
        emit('message', {
            'type': 'status',
            'data': status
        })

@socketio.on('disconnect')
def handle_disconnect():
    """WebSocket连接断开"""
    util.log(1, f"客户端已断开: {request.sid}")

@socketio.on('message')
def handle_message(data):
    """处理WebSocket消息"""
    global recorder
    
    try:
        message_type = data.get('type')
        util.log(1, f"收到消息: {message_type}")
        
        if message_type == 'start_recording':
            device_index = data.get('device_index')
            
            if not recorder:
                recorder = VoiceTestRecorder(test_fay)
                recorder.set_socketio(socketio)
            
            # 设置设备索引
            if device_index is not None:
                recorder.device_index = int(device_index)
                
            # 开始录音
            success = recorder.start_recording()
            
            if success:
                emit('message', {
                    'type': 'log',
                    'message': '录音已开始',
                    'level': 'success'
                })
            else:
                emit('message', {
                    'type': 'log',
                    'message': '录音启动失败',
                    'level': 'error'
                })
                
        elif message_type == 'stop_recording':
            if recorder:
                recorder.stop_recording()
                emit('message', {
                    'type': 'log',
                    'message': '录音已停止',
                    'level': 'success'
                })
            else:
                emit('message', {
                    'type': 'log',
                    'message': '录音器未初始化',
                    'level': 'error'
                })
                
        elif message_type == 'get_devices':
            try:
                if not recorder:
                    temp_recorder = VoiceTestRecorder(test_fay)
                    devices = temp_recorder.list_audio_devices()
                else:
                    devices = recorder.list_audio_devices()
                    
                emit('message', {
                    'type': 'devices',
                    'data': devices
                })
            except Exception as e:
                emit('message', {
                    'type': 'log',
                    'message': f'获取设备列表失败: {e}',
                    'level': 'error'
                })
                
        elif message_type == 'test_connection':
            emit('message', {
                'type': 'log',
                'message': '连接测试成功',
                'level': 'success'
            })
            
            # 发送当前状态
            if recorder:
                status = recorder.get_status()
                emit('message', {
                    'type': 'status',
                    'data': status
                })
        else:
            emit('message', {
                'type': 'log',
                'message': f'未知消息类型: {message_type}',
                'level': 'warning'
            })
            
    except Exception as e:
        util.log(3, f"处理WebSocket消息时出错: {e}")
        emit('message', {
            'type': 'log',
            'message': f'处理消息时出错: {e}',
            'level': 'error'
        })

def status_broadcast_thread():
    """状态广播线程"""
    global recorder
    
    while True:
        try:
            if recorder:
                status = recorder.get_status()
                socketio.emit('message', {
                    'type': 'status',
                    'data': status
                })
            time.sleep(1)  # 每秒广播一次状态
        except Exception as e:
            util.log(3, f"状态广播线程出错: {e}")
            time.sleep(5)

def cleanup():
    """清理资源"""
    global recorder
    if recorder:
        recorder.stop_recording()
        destroy_global_recorder()
        recorder = None
    util.log(1, "资源清理完成")

if __name__ == '__main__':
    try:
        util.log(1, "启动语音测试服务器 - 同步模式")
        
        # 启动状态广播线程
        broadcast_thread = Thread(target=status_broadcast_thread, daemon=True)
        broadcast_thread.start()
        
        # 启动服务器
        socketio.run(
            app, 
            host='0.0.0.0', 
            port=5050, 
            debug=False,
            use_reloader=False
        )
        
    except KeyboardInterrupt:
        util.log(1, "收到中断信号,正在关闭服务器...")
    except Exception as e:
        util.log(3, f"服务器运行时出错: {e}")
    finally:
        cleanup()
        util.log(1, "语音测试服务器已关闭")