app添加工作经历开发
This commit is contained in:
@@ -28,27 +28,57 @@ export function useTTSPlayer(wsUrl) {
|
||||
let activePlayId = 0
|
||||
|
||||
const speak = (text) => {
|
||||
console.log('🎤 TTS speak function called');
|
||||
console.log('📝 Text to synthesize:', text ? text.substring(0, 100) + '...' : 'No text');
|
||||
console.log('🔗 WebSocket URL:', wsUrl);
|
||||
|
||||
currentPlayId++
|
||||
const myPlayId = currentPlayId
|
||||
console.log('🆔 Play ID:', myPlayId);
|
||||
|
||||
reset()
|
||||
pendingText = text
|
||||
activePlayId = myPlayId
|
||||
|
||||
console.log('✅ Speak function setup complete');
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
console.log('⏸️ TTS pause called');
|
||||
console.log('🔊 AudioContext state:', audioContext.state);
|
||||
console.log('🔊 Is speaking before pause:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused before pause:', isPaused.value);
|
||||
|
||||
if (audioContext.state === 'running') {
|
||||
audioContext.suspend()
|
||||
isPaused.value = true
|
||||
isSpeaking.value = false
|
||||
// 不要设置 isSpeaking.value = false,保持当前状态
|
||||
console.log('✅ Audio paused successfully');
|
||||
} else {
|
||||
console.log('⚠️ AudioContext is not running, cannot pause');
|
||||
}
|
||||
|
||||
console.log('🔊 Is speaking after pause:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused after pause:', isPaused.value);
|
||||
}
|
||||
|
||||
const resume = () => {
|
||||
console.log('▶️ TTS resume called');
|
||||
console.log('🔊 AudioContext state:', audioContext.state);
|
||||
console.log('🔊 Is speaking before resume:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused before resume:', isPaused.value);
|
||||
|
||||
if (audioContext.state === 'suspended') {
|
||||
audioContext.resume()
|
||||
isPaused.value = false
|
||||
isSpeaking.value = true
|
||||
console.log('✅ Audio resumed successfully');
|
||||
} else {
|
||||
console.log('⚠️ AudioContext is not suspended, cannot resume');
|
||||
}
|
||||
|
||||
console.log('🔊 Is speaking after resume:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused after resume:', isPaused.value);
|
||||
}
|
||||
|
||||
const cancelAudio = () => {
|
||||
@@ -89,16 +119,42 @@ export function useTTSPlayer(wsUrl) {
|
||||
|
||||
const initWebSocket = () => {
|
||||
const thisPlayId = currentPlayId
|
||||
console.log('🔌 Initializing WebSocket connection');
|
||||
console.log('🔗 WebSocket URL:', wsUrl);
|
||||
console.log('🆔 This play ID:', thisPlayId);
|
||||
|
||||
socket = new WebSocket(wsUrl)
|
||||
socket.binaryType = 'arraybuffer'
|
||||
|
||||
// 设置心跳检测,避免超时
|
||||
const heartbeatInterval = setInterval(() => {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({ type: 'ping' }));
|
||||
}
|
||||
}, 30000); // 每30秒发送一次心跳
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log('✅ WebSocket connection opened');
|
||||
if (pendingText && thisPlayId === activePlayId) {
|
||||
const seepdText = extractSpeechText(pendingText)
|
||||
console.log('📤 Sending text to TTS server:', seepdText.substring(0, 100) + '...');
|
||||
socket.send(seepdText)
|
||||
pendingText = null
|
||||
} else {
|
||||
console.log('❌ No pending text or play ID mismatch');
|
||||
console.log('📝 Pending text exists:', !!pendingText);
|
||||
console.log('🆔 Play ID match:', thisPlayId === activePlayId);
|
||||
}
|
||||
}
|
||||
|
||||
socket.onerror = (error) => {
|
||||
console.error('❌ WebSocket error:', error);
|
||||
}
|
||||
|
||||
socket.onclose = (event) => {
|
||||
console.log('🔌 WebSocket connection closed:', event.code, event.reason);
|
||||
clearInterval(heartbeatInterval);
|
||||
}
|
||||
|
||||
socket.onmessage = async (e) => {
|
||||
if (thisPlayId !== activePlayId) return // 忽略旧播放的消息
|
||||
@@ -106,13 +162,19 @@ export function useTTSPlayer(wsUrl) {
|
||||
if (typeof e.data === 'string') {
|
||||
try {
|
||||
const msg = JSON.parse(e.data)
|
||||
console.log('📨 TTS server message:', msg);
|
||||
if (msg.status === 'complete') {
|
||||
console.log('✅ TTS synthesis completed');
|
||||
isComplete.value = true
|
||||
// 计算剩余播放时间,确保播放完整
|
||||
const remainingTime = Math.max(0, (playTime - audioContext.currentTime) * 1000);
|
||||
console.log('⏱️ Remaining play time:', remainingTime + 'ms');
|
||||
setTimeout(() => {
|
||||
if (thisPlayId === activePlayId) {
|
||||
console.log('🔇 TTS playback finished, setting isSpeaking to false');
|
||||
isSpeaking.value = false
|
||||
}
|
||||
}, (playTime - audioContext.currentTime) * 1000)
|
||||
}, remainingTime + 500) // 额外500ms缓冲时间
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[TTSPlayer] 文本消息:', e.data)
|
||||
@@ -156,8 +218,10 @@ export function useTTSPlayer(wsUrl) {
|
||||
}
|
||||
|
||||
const playBuffer = (audioBuffer) => {
|
||||
console.log('🎵 playBuffer called, duration:', audioBuffer.duration + 's');
|
||||
if (!isSpeaking.value) {
|
||||
playTime = audioContext.currentTime
|
||||
console.log('🎵 Starting new audio playback at time:', playTime);
|
||||
}
|
||||
const source = audioContext.createBufferSource()
|
||||
source.buffer = audioBuffer
|
||||
@@ -166,6 +230,12 @@ export function useTTSPlayer(wsUrl) {
|
||||
sourceNodes.push(source)
|
||||
playTime += audioBuffer.duration
|
||||
isSpeaking.value = true
|
||||
console.log('🎵 Audio scheduled, new playTime:', playTime);
|
||||
|
||||
// 添加音频播放结束监听
|
||||
source.onended = () => {
|
||||
console.log('🎵 Audio buffer finished playing');
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -203,6 +273,10 @@ export function useTTSPlayer(wsUrl) {
|
||||
}
|
||||
|
||||
function extractSpeechText(markdown) {
|
||||
console.log('🔍 extractSpeechText called');
|
||||
console.log('📝 Input markdown length:', markdown ? markdown.length : 0);
|
||||
console.log('📝 Input markdown preview:', markdown ? markdown.substring(0, 200) + '...' : 'No markdown');
|
||||
|
||||
const jobRegex = /``` job-json\s*({[\s\S]*?})\s*```/g;
|
||||
const jobs = [];
|
||||
let match;
|
||||
@@ -219,11 +293,16 @@ function extractSpeechText(markdown) {
|
||||
firstJobStartIndex = match.index;
|
||||
}
|
||||
lastJobEndIndex = jobRegex.lastIndex;
|
||||
console.log('✅ Found job:', job.jobTitle);
|
||||
} catch (e) {
|
||||
console.warn('JSON 解析失败', e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('📊 Jobs found:', jobs.length);
|
||||
console.log('📍 First job start index:', firstJobStartIndex);
|
||||
console.log('📍 Last job end index:', lastJobEndIndex);
|
||||
|
||||
// 提取引导语(第一个 job-json 之前的文字)
|
||||
const guideText = firstJobStartIndex > 0 ?
|
||||
markdown.slice(0, firstJobStartIndex).trim() :
|
||||
@@ -234,6 +313,9 @@ function extractSpeechText(markdown) {
|
||||
markdown.slice(lastJobEndIndex).trim() :
|
||||
'';
|
||||
|
||||
console.log('📝 Guide text:', guideText);
|
||||
console.log('📝 Ending text:', endingText);
|
||||
|
||||
// 岗位信息格式化为语音文本
|
||||
const jobTexts = jobs.map((job, index) => {
|
||||
return `第 ${index + 1} 个岗位,岗位名称是:${job.jobTitle},公司是:${job.companyName},薪资:${job.salary},地点:${job.location},学历要求:${job.education},经验要求:${job.experience}。`;
|
||||
@@ -245,5 +327,10 @@ function extractSpeechText(markdown) {
|
||||
finalTextParts.push(...jobTexts);
|
||||
if (endingText) finalTextParts.push(endingText);
|
||||
|
||||
return finalTextParts.join('\n');
|
||||
const finalText = finalTextParts.join('\n');
|
||||
console.log('🎤 Final TTS text length:', finalText.length);
|
||||
console.log('🎤 Final TTS text preview:', finalText.substring(0, 200) + '...');
|
||||
console.log('🎤 Final TTS text parts count:', finalTextParts.length);
|
||||
|
||||
return finalText;
|
||||
}
|
Reference in New Issue
Block a user