Files
urbanLifeline/urbanLifelineWeb/example/jitsi-meet/useJitsiTranscription.js
2025-12-20 18:52:33 +08:00

161 lines
5.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
};
}