161 lines
5.5 KiB
JavaScript
161 lines
5.5 KiB
JavaScript
// src/composables/useJitsiTranscription.ts
|
||
import { ref } from 'vue';
|
||
import { XunfeiTranscription } from '@/utils/xunfei';
|
||
|
||
export function useJitsiTranscription(meetingId: string) {
|
||
const isRecording = ref(false);
|
||
const currentSpeaker = ref<string | null>(null);
|
||
let mediaRecorder: MediaRecorder | null = null;
|
||
let xunfeiWs: XunfeiTranscription | null = null;
|
||
|
||
// 初始化Jitsi API
|
||
const initJitsiApi = (domain: string, roomName: string) => {
|
||
const api = new JitsiMeetExternalAPI(domain, {
|
||
roomName: roomName,
|
||
width: '100%',
|
||
height: '100%',
|
||
parentNode: document.querySelector('#jitsi-container'),
|
||
userInfo: {
|
||
displayName: '张三',
|
||
email: 'zhangsan@example.com'
|
||
}
|
||
});
|
||
|
||
// 监听主讲人变化(核心:识别说话人)
|
||
api.addEventListener('dominantSpeakerChanged', (event) => {
|
||
const speakerId = event.id;
|
||
console.log('主讲人切换:', speakerId);
|
||
|
||
// 获取说话人信息
|
||
api.getParticipantsInfo().then(participants => {
|
||
const speaker = participants.find(p => p.participantId === speakerId);
|
||
if (speaker) {
|
||
currentSpeaker.value = speaker.displayName;
|
||
console.log('当前说话人:', speaker.displayName);
|
||
|
||
// 开始录制该说话人的音频
|
||
startRecording(speakerId, speaker.displayName);
|
||
}
|
||
});
|
||
});
|
||
|
||
// 监听音频轨道添加
|
||
api.addEventListener('trackAdded', (event) => {
|
||
if (event.track.getType() === 'audio') {
|
||
console.log('音频轨道添加:', event.track.getParticipantId());
|
||
}
|
||
});
|
||
|
||
return api;
|
||
};
|
||
|
||
// 开始录制音频
|
||
const startRecording = async (speakerId: string, speakerName: string) => {
|
||
try {
|
||
// 获取会议音频流
|
||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||
|
||
mediaRecorder = new MediaRecorder(stream, {
|
||
mimeType: 'audio/webm'
|
||
});
|
||
|
||
let audioChunks: Blob[] = [];
|
||
|
||
mediaRecorder.ondataavailable = (event) => {
|
||
if (event.data.size > 0) {
|
||
audioChunks.push(event.data);
|
||
}
|
||
};
|
||
|
||
// 每3秒发送一次音频数据进行转写
|
||
mediaRecorder.onstop = async () => {
|
||
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
|
||
|
||
// 发送到讯飞/阿里云进行转写
|
||
const transcription = await transcribeAudio(audioBlob, speakerId, speakerName);
|
||
|
||
// 保存转录结果
|
||
await saveTranscription(meetingId, {
|
||
speakerId: speakerId,
|
||
speakerName: speakerName,
|
||
content: transcription.text,
|
||
confidence: transcription.confidence,
|
||
startTime: new Date().toISOString(),
|
||
endTime: new Date().toISOString()
|
||
});
|
||
|
||
audioChunks = [];
|
||
};
|
||
|
||
mediaRecorder.start();
|
||
isRecording.value = true;
|
||
|
||
// 每3秒停止并重新开始,实现分段录制
|
||
setInterval(() => {
|
||
if (mediaRecorder && mediaRecorder.state === 'recording') {
|
||
mediaRecorder.stop();
|
||
setTimeout(() => mediaRecorder?.start(), 100);
|
||
}
|
||
}, 3000);
|
||
|
||
} catch (error) {
|
||
console.error('录制失败:', error);
|
||
}
|
||
};
|
||
|
||
// 使用讯飞实时转写
|
||
const transcribeAudio = async (audioBlob: Blob, speakerId: string, speakerName: string) => {
|
||
// 连接讯飞WebSocket
|
||
if (!xunfeiWs) {
|
||
xunfeiWs = new XunfeiTranscription({
|
||
appId: 'YOUR_APP_ID',
|
||
apiKey: 'YOUR_API_KEY',
|
||
onResult: (result) => {
|
||
console.log('实时转写结果:', result);
|
||
|
||
// 实时保存到数据库
|
||
saveTranscription(meetingId, {
|
||
speakerId: speakerId,
|
||
speakerName: speakerName,
|
||
content: result.text,
|
||
confidence: result.confidence,
|
||
startTime: result.startTime,
|
||
endTime: result.endTime,
|
||
isFinal: result.isFinal
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 发送音频数据
|
||
const arrayBuffer = await audioBlob.arrayBuffer();
|
||
xunfeiWs.send(arrayBuffer);
|
||
|
||
return {
|
||
text: '转写结果(异步返回)',
|
||
confidence: 0.95
|
||
};
|
||
};
|
||
|
||
// 保存转录结果到后端
|
||
const saveTranscription = async (meetingId: string, data: any) => {
|
||
try {
|
||
await fetch('/api/workcase/meeting/transcription/save', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
meetingId: meetingId,
|
||
...data
|
||
})
|
||
});
|
||
} catch (error) {
|
||
console.error('保存转录失败:', error);
|
||
}
|
||
};
|
||
|
||
return {
|
||
initJitsiApi,
|
||
isRecording,
|
||
currentSpeaker
|
||
};
|
||
} |