| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- import { useState, useCallback, useEffect } from 'react';
- import { useAgoraSTT } from '../AgoraSTTProvider';
- import { toast } from 'sonner';
- export interface TranscriptionResult {
- text: string;
- isFinal: boolean;
- timestamp: number;
- confidence?: number;
- }
- export interface AgoraSTTState {
- isConnected: boolean;
- isRecording: boolean;
- isTranscribing: boolean;
- isConnecting: boolean;
- error: string | null;
- transcriptionResults: TranscriptionResult[];
- currentTranscription: string;
- microphonePermission: 'granted' | 'denied' | 'prompt';
- }
- export interface UseAgoraSTTManagerResult {
- state: AgoraSTTState;
- joinChannel: (userId: string, channel: string, userName: string) => Promise<void>;
- leaveChannel: () => Promise<void>;
- startRecording: (languages: { value: string; label: string }[]) => Promise<void>;
- stopRecording: () => Promise<void>;
- clearTranscriptions: () => void;
- }
- export const useAgoraSTTManager = (): UseAgoraSTTManagerResult => {
- const { sttManager } = useAgoraSTT();
- const [state, setState] = useState<AgoraSTTState>({
- isConnected: false,
- isRecording: false,
- isTranscribing: false,
- isConnecting: false,
- error: null,
- transcriptionResults: [],
- currentTranscription: '',
- microphonePermission: 'prompt'
- });
- const updateState = useCallback((updates: Partial<AgoraSTTState>) => {
- setState(prev => ({ ...prev, ...updates }));
- }, []);
- const setError = useCallback((error: string | null) => {
- updateState({ error });
- }, [updateState]);
- const joinChannel = useCallback(async (userId: string, channel: string, userName: string): Promise<void> => {
- try {
- updateState({ error: null, isConnecting: true });
- // 初始化 STT 管理器
- await sttManager.init({ userId, channel, userName });
- updateState({
- isConnected: true,
- isConnecting: false,
- error: null
- });
- toast.success('成功加入频道');
- } catch (error) {
- const errorMessage = error instanceof Error ? error.message : '未知错误';
- setError(`加入频道失败: ${errorMessage}`);
- updateState({ isConnecting: false });
- toast.error('加入频道失败');
- }
- }, [sttManager, updateState, setError]);
- const leaveChannel = useCallback(async (): Promise<void> => {
- try {
- await sttManager.destroy();
- updateState({
- isConnected: false,
- isRecording: false,
- isTranscribing: false,
- currentTranscription: ''
- });
- toast.success('已离开频道');
- } catch (error) {
- console.error('离开频道失败:', error);
- toast.error('离开频道失败');
- }
- }, [sttManager, updateState]);
- const checkMicrophonePermission = useCallback(async (): Promise<boolean> => {
- try {
- const permissionStatus = await navigator.permissions.query({ name: 'microphone' as PermissionName });
- updateState({ microphonePermission: permissionStatus.state as 'granted' | 'denied' | 'prompt' });
- if (permissionStatus.state === 'denied') {
- setError('麦克风权限已被拒绝,请在浏览器设置中启用');
- return false;
- }
- return true;
- } catch (error) {
- console.warn('Microphone permission API not supported');
- return true;
- }
- }, [updateState, setError]);
- const requestMicrophonePermission = useCallback(async (): Promise<boolean> => {
- try {
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
- stream.getTracks().forEach(track => track.stop());
- updateState({ microphonePermission: 'granted' });
- return true;
- } catch (error) {
- updateState({ microphonePermission: 'denied' });
- setError('麦克风权限请求被拒绝,请允许访问麦克风');
- return false;
- }
- }, [updateState, setError]);
- const startRecording = useCallback(async (languages: { value: string; label: string }[]): Promise<void> => {
- if (!state.isConnected) {
- setError('未连接到 Agora 频道');
- return;
- }
- // 检查麦克风权限
- if (!(await checkMicrophonePermission())) {
- return;
- }
- // 如果权限是prompt状态,请求权限
- if (state.microphonePermission === 'prompt') {
- if (!(await requestMicrophonePermission())) {
- return;
- }
- }
- try {
- // 转换语言格式为 STT 管理器期望的格式
- const sttLanguages = languages.map(lang => ({
- source: lang.value,
- target: []
- }));
- // 开始转录
- await sttManager.startTranscription({ languages: sttLanguages });
- updateState({
- isRecording: true,
- isTranscribing: true,
- error: null
- });
- toast.success('开始录音');
- } catch (error) {
- const errorMessage = error instanceof Error ? error.message : '未知错误';
- setError(`开始录音失败: ${errorMessage}`);
- toast.error('开始录音失败');
- }
- }, [state.isConnected, state.microphonePermission, sttManager, checkMicrophonePermission, requestMicrophonePermission, updateState, setError]);
- const stopRecording = useCallback(async (): Promise<void> => {
- try {
- await sttManager.stopTranscription();
- updateState({
- isRecording: false,
- isTranscribing: false
- });
- toast.success('停止录音');
- } catch (error) {
- console.error('停止录音失败:', error);
- toast.error('停止录音失败');
- }
- }, [sttManager, updateState]);
- const clearTranscriptions = useCallback((): void => {
- updateState({
- transcriptionResults: [],
- currentTranscription: ''
- });
- }, [updateState]);
- // 监听转录结果
- useEffect(() => {
- // TODO: 实现监听 STT 管理器的转录结果事件
- // sttManager.on('transcription', (result) => {
- // const newResult: TranscriptionResult = {
- // text: result.text,
- // isFinal: result.isFinal,
- // timestamp: Date.now(),
- // confidence: result.confidence
- // };
- //
- // setState(prev => ({
- // ...prev,
- // transcriptionResults: [...prev.transcriptionResults, newResult],
- // currentTranscription: result.isFinal ? '' : result.text
- // }));
- // });
- // 临时模拟转录结果
- if (state.isRecording && state.isConnected) {
- const interval = setInterval(() => {
- if (Math.random() > 0.7) {
- const newResult: TranscriptionResult = {
- text: `模拟转录文本 ${Date.now()}`,
- isFinal: Math.random() > 0.8,
- timestamp: Date.now(),
- confidence: Math.random() * 0.5 + 0.5
- };
- setState(prev => ({
- ...prev,
- transcriptionResults: [...prev.transcriptionResults, newResult],
- currentTranscription: newResult.isFinal ? '' : newResult.text
- }));
- }
- }, 2000);
- return () => clearInterval(interval);
- }
- }, [state.isRecording, state.isConnected]);
- return {
- state,
- joinChannel,
- leaveChannel,
- startRecording,
- stopRecording,
- clearTranscriptions
- };
- };
|