'commit'
This commit is contained in:
@@ -17,7 +17,11 @@ const AudioState = {
|
|||||||
audioChunks: [], // 存储接收到的音频块
|
audioChunks: [], // 存储接收到的音频块
|
||||||
audioQueue: [], // 音频队列,用于流式播放
|
audioQueue: [], // 音频队列,用于流式播放
|
||||||
isStreamPlaying: false, // 是否正在流式播放
|
isStreamPlaying: false, // 是否正在流式播放
|
||||||
currentAudioIndex: 0 // 当前播放的音频索引
|
currentAudioIndex: 0, // 当前播放的音频索引
|
||||||
|
streamAudioElement: null, // 流式播放音频元素
|
||||||
|
totalPlayedTime: 0, // 新增:累计播放时间
|
||||||
|
estimatedTotalDuration: 0, // 新增:估算总时长
|
||||||
|
currentChunkDuration: 0 // 新增:当前块时长
|
||||||
},
|
},
|
||||||
websocket: {
|
websocket: {
|
||||||
connection: null,
|
connection: null,
|
||||||
@@ -56,7 +60,7 @@ const UIController = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新按钮状态
|
// 更新录音按钮状态
|
||||||
updateRecordingButtons(isRecording) {
|
updateRecordingButtons(isRecording) {
|
||||||
this.toggleElement('recordingIndicator', isRecording);
|
this.toggleElement('recordingIndicator', isRecording);
|
||||||
this.toggleElement('startRecordBtn', !isRecording);
|
this.toggleElement('startRecordBtn', !isRecording);
|
||||||
@@ -92,12 +96,46 @@ const UIController = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 添加总进度更新方法
|
||||||
|
updateTotalProgress() {
|
||||||
|
// 根据实际业务逻辑计算总进度
|
||||||
|
const totalProgress = AudioState.playback.totalPlayedChunks / AudioState.playback.totalChunks * 100;
|
||||||
|
this.updateProgress(totalProgress);
|
||||||
|
},
|
||||||
|
|
||||||
// 更新时间显示
|
// 更新时间显示
|
||||||
updateTimeDisplay(currentTime, duration) {
|
updateTimeDisplay(currentTime, duration) {
|
||||||
const timeDisplay = document.getElementById('audioTime');
|
const timeDisplay = document.getElementById('audioTime');
|
||||||
if (timeDisplay) {
|
if (timeDisplay) {
|
||||||
timeDisplay.textContent = `${Utils.formatTime(currentTime)} / ${Utils.formatTime(duration)}`;
|
timeDisplay.textContent = `${Utils.formatTime(currentTime)} / ${Utils.formatTime(duration)}`;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 显示错误通知
|
||||||
|
showErrorNotification(message) {
|
||||||
|
// 创建或显示错误通知元素
|
||||||
|
let notification = document.getElementById('audioErrorNotification');
|
||||||
|
if (!notification) {
|
||||||
|
notification = document.createElement('div');
|
||||||
|
notification.id = 'audioErrorNotification';
|
||||||
|
notification.style.position = 'fixed';
|
||||||
|
notification.style.bottom = '20px';
|
||||||
|
notification.style.left = '50%';
|
||||||
|
notification.style.transform = 'translateX(-50%)';
|
||||||
|
notification.style.backgroundColor = 'rgba(255, 0, 0, 0.8)';
|
||||||
|
notification.style.color = 'white';
|
||||||
|
notification.style.padding = '10px 20px';
|
||||||
|
notification.style.borderRadius = '4px';
|
||||||
|
notification.style.zIndex = '1000';
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
}
|
||||||
|
notification.textContent = message;
|
||||||
|
notification.style.display = 'block';
|
||||||
|
|
||||||
|
// 3秒后自动隐藏
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.style.display = 'none';
|
||||||
|
}, 3000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,9 +163,15 @@ const WebSocketManager = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 连接关闭
|
// 连接关闭
|
||||||
AudioState.websocket.connection.onclose = () => {
|
AudioState.websocket.connection.onclose = (event) => {
|
||||||
console.log('WebSocket连接已关闭');
|
console.log('WebSocket连接已关闭,代码:', event.code, '原因:', event.reason);
|
||||||
AudioState.websocket.isConnected = false;
|
AudioState.websocket.isConnected = false;
|
||||||
|
|
||||||
|
// 添加自动重连逻辑
|
||||||
|
if (!AudioState.websocket.isClosing) {
|
||||||
|
console.log('尝试重新连接WebSocket...');
|
||||||
|
setTimeout(() => WebSocketManager.initConnection(), 3000);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 连接错误
|
// 连接错误
|
||||||
@@ -392,13 +436,35 @@ const AudioPlayer = {
|
|||||||
UIController.updateTimeDisplay(currentTime, duration);
|
UIController.updateTimeDisplay(currentTime, duration);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 在AudioPlayer.initStreamPlayer方法中添加暂停事件监听
|
// 流式播放进度更新 - 新增方法
|
||||||
|
updateStreamProgress() {
|
||||||
|
if (!AudioState.playback.streamAudioElement) return;
|
||||||
|
|
||||||
|
// 计算当前音频块播放进度
|
||||||
|
const currentTime = AudioState.playback.streamAudioElement.currentTime;
|
||||||
|
const duration = AudioState.playback.streamAudioElement.duration || AudioState.playback.currentChunkDuration;
|
||||||
|
|
||||||
|
// 更新进度条
|
||||||
|
const progress = (currentTime / duration) * 100;
|
||||||
|
UIController.updateProgress(progress);
|
||||||
|
|
||||||
|
// 更新时间显示(累计已播放时间 + 当前块播放时间)
|
||||||
|
const totalPlayedTime = AudioState.playback.totalPlayedTime + currentTime;
|
||||||
|
UIController.updateTimeDisplay(totalPlayedTime, AudioState.playback.estimatedTotalDuration);
|
||||||
|
},
|
||||||
|
|
||||||
// 初始化流式播放器
|
// 初始化流式播放器
|
||||||
initStreamPlayer() {
|
initStreamPlayer() {
|
||||||
// 创建新的音频元素用于流式播放
|
// 创建新的音频元素用于流式播放
|
||||||
if (!AudioState.playback.streamAudioElement) {
|
if (!AudioState.playback.streamAudioElement) {
|
||||||
AudioState.playback.streamAudioElement = new Audio();
|
AudioState.playback.streamAudioElement = new Audio();
|
||||||
|
|
||||||
|
// 添加暂停按钮点击事件绑定
|
||||||
|
const playBtn = document.getElementById('playAudioBtn');
|
||||||
|
if (playBtn) {
|
||||||
|
playBtn.onclick = () => AudioPlayer.togglePlay();
|
||||||
|
}
|
||||||
|
|
||||||
// 监听音频结束事件
|
// 监听音频结束事件
|
||||||
AudioState.playback.streamAudioElement.addEventListener('ended', () => {
|
AudioState.playback.streamAudioElement.addEventListener('ended', () => {
|
||||||
// 当前音频播放完毕,处理队列中的下一个音频
|
// 当前音频播放完毕,处理队列中的下一个音频
|
||||||
@@ -424,6 +490,11 @@ const AudioPlayer = {
|
|||||||
UIController.updatePlayButton(false); // 更新按钮状态
|
UIController.updatePlayButton(false); // 更新按钮状态
|
||||||
this.processAudioQueue();
|
this.processAudioQueue();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 添加进度更新事件监听 - 新增代码
|
||||||
|
AudioState.playback.streamAudioElement.addEventListener('timeupdate', () => {
|
||||||
|
this.updateStreamProgress();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -449,15 +520,29 @@ const AudioPlayer = {
|
|||||||
const audioUrl = URL.createObjectURL(audioBlob);
|
const audioUrl = URL.createObjectURL(audioBlob);
|
||||||
AudioState.playback.streamAudioElement.src = audioUrl;
|
AudioState.playback.streamAudioElement.src = audioUrl;
|
||||||
|
|
||||||
|
// 添加当前音频块时长跟踪
|
||||||
|
AudioState.playback.currentChunkStartTime = Date.now();
|
||||||
|
AudioState.playback.currentChunkDuration = audioBlob.size / 1024 / 16; // 估算时长
|
||||||
|
|
||||||
// 修改AudioPlayer.processAudioQueue方法中的播放部分
|
// 修改AudioPlayer.processAudioQueue方法中的播放部分
|
||||||
AudioState.playback.streamAudioElement.play()
|
AudioState.playback.streamAudioElement.play()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('开始播放音频块');
|
console.log('开始播放音频块');
|
||||||
// 关键修复:更新播放按钮状态为播放中
|
// 关键修复:更新播放按钮状态为播放中
|
||||||
UIController.updatePlayButton(true);
|
UIController.updatePlayButton(true);
|
||||||
|
|
||||||
|
// 更新总进度 - 修改前:this.updateTotalProgress();
|
||||||
|
UIController.updateTotalProgress(); // 修改为直接调用UIController方法
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('播放音频块失败:', error);
|
console.error('播放音频块失败:', error);
|
||||||
|
// 添加失败后状态重置
|
||||||
|
AudioState.playback.isStreamPlaying = false;
|
||||||
|
UIController.updatePlayButton(false);
|
||||||
|
|
||||||
|
// 添加用户可见的错误提示
|
||||||
|
UIController.showErrorNotification('音频播放失败,请重试');
|
||||||
|
|
||||||
this.processAudioQueue();
|
this.processAudioQueue();
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -598,6 +683,8 @@ const RecordingManager = {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (AudioState.recording.isRecording) {
|
if (AudioState.recording.isRecording) {
|
||||||
console.log('达到最大录音时长,自动停止录音');
|
console.log('达到最大录音时长,自动停止录音');
|
||||||
|
// 添加用户提示
|
||||||
|
alert('已达到最大录音时长');
|
||||||
this.stopRecording();
|
this.stopRecording();
|
||||||
}
|
}
|
||||||
}, AudioState.recording.maxDuration);
|
}, AudioState.recording.maxDuration);
|
||||||
@@ -624,8 +711,16 @@ const RecordingManager = {
|
|||||||
|
|
||||||
// 等待WebSocket连接建立
|
// 等待WebSocket连接建立
|
||||||
waitForConnection() {
|
waitForConnection() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const maxRetries = 30; // 最多等待3秒
|
||||||
|
let retries = 0;
|
||||||
|
|
||||||
const checkConnection = () => {
|
const checkConnection = () => {
|
||||||
|
if (retries >= maxRetries) {
|
||||||
|
reject(new Error('WebSocket连接超时'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('检查WebSocket连接状态:', AudioState.websocket.isConnected);
|
console.log('检查WebSocket连接状态:', AudioState.websocket.isConnected);
|
||||||
if (AudioState.websocket.isConnected &&
|
if (AudioState.websocket.isConnected &&
|
||||||
AudioState.websocket.connection &&
|
AudioState.websocket.connection &&
|
||||||
@@ -634,6 +729,7 @@ const RecordingManager = {
|
|||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
console.log('WebSocket连接未建立,等待...');
|
console.log('WebSocket连接未建立,等待...');
|
||||||
|
retries++;
|
||||||
setTimeout(checkConnection, 100);
|
setTimeout(checkConnection, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -706,23 +802,23 @@ function initializeApp() {
|
|||||||
// 初始化流式播放器
|
// 初始化流式播放器
|
||||||
AudioPlayer.initStreamPlayer();
|
AudioPlayer.initStreamPlayer();
|
||||||
|
|
||||||
// 检查DOM是否已就绪
|
// 统一使用DOMContentLoaded事件绑定
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', bindEventsAndInitialize);
|
||||||
EventBinder.bindEvents();
|
|
||||||
console.log('学伴录音功能初始化完成(DOMContentLoaded)');
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// DOM已经加载完成,直接绑定事件
|
bindEventsAndInitialize();
|
||||||
EventBinder.bindEvents();
|
|
||||||
console.log('学伴录音功能初始化完成(直接执行)');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bindEventsAndInitialize() {
|
||||||
|
EventBinder.bindEvents();
|
||||||
|
console.log('学伴录音功能初始化完成');
|
||||||
|
}
|
||||||
|
|
||||||
// 立即执行初始化
|
// 立即执行初始化
|
||||||
initializeApp();
|
initializeApp();
|
||||||
|
|
||||||
// 同时保留原有的DOMContentLoaded事件作为备用
|
// 移除重复的DOMContentLoaded和load事件绑定
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
EventBinder.bindEvents();
|
EventBinder.bindEvents();
|
||||||
console.log('学伴录音功能备用初始化完成');
|
console.log('学伴录音功能备用初始化完成');
|
||||||
@@ -736,5 +832,20 @@ window.addEventListener('load', () => {
|
|||||||
|
|
||||||
// 页面关闭时关闭WebSocket连接
|
// 页面关闭时关闭WebSocket连接
|
||||||
window.addEventListener('beforeunload', () => {
|
window.addEventListener('beforeunload', () => {
|
||||||
WebSocketManager.closeConnection();
|
WebSocketManager.closeConnection(true);
|
||||||
|
|
||||||
|
// 清理音频元素
|
||||||
|
if (AudioState.playback.streamAudioElement) {
|
||||||
|
// 移除所有事件监听器
|
||||||
|
const oldElement = AudioState.playback.streamAudioElement;
|
||||||
|
const newElement = new Audio();
|
||||||
|
AudioState.playback.streamAudioElement = newElement;
|
||||||
|
oldElement.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理URL对象
|
||||||
|
if (AudioState.playback.audioUrl) {
|
||||||
|
URL.revokeObjectURL(AudioState.playback.audioUrl);
|
||||||
|
AudioState.playback.audioUrl = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user