Files
urbanLifeline/urbanLifelineWeb/example/jitsi-meet/useJitsiTranscription.js

161 lines
5.5 KiB
JavaScript
Raw Normal View History

2025-12-20 18:52:33 +08:00
// 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
};
}