diff --git a/scene_state.json b/scene_state.json
new file mode 100644
index 0000000..261b13d
--- /dev/null
+++ b/scene_state.json
@@ -0,0 +1,3 @@
+{
+ "currentSceneIndex": 2
+}
\ No newline at end of file
diff --git a/server.js b/server.js
index f1a3202..95bdf6f 100644
--- a/server.js
+++ b/server.js
@@ -86,27 +86,58 @@ app.delete('/api/messages/clear', async (req, res) => {
const connectedClients = new Map();
// 场景轮询系统
-// 场景轮询系统
+// 场景轮询系统 - 添加持久化
+// 删除这行:const fs = require('fs'); // 重复声明,需要删除
+const sceneStateFile = path.join(__dirname, 'scene_state.json');
+
+// 从文件加载场景状态
+function loadSceneState() {
+ try {
+ if (fs.existsSync(sceneStateFile)) {
+ const data = fs.readFileSync(sceneStateFile, 'utf8');
+ const state = JSON.parse(data);
+ currentSceneIndex = state.currentSceneIndex || 0;
+ console.log(`从文件加载场景状态: ${currentSceneIndex} (${scenes[currentSceneIndex].name})`);
+ } else {
+ console.log('场景状态文件不存在,使用默认值: 0');
+ }
+ } catch (error) {
+ console.error('加载场景状态失败:', error);
+ currentSceneIndex = 0;
+ }
+}
+
+// 保存场景状态到文件
+function saveSceneState() {
+ try {
+ const state = { currentSceneIndex };
+ fs.writeFileSync(sceneStateFile, JSON.stringify(state, null, 2));
+ console.log(`场景状态已保存: ${currentSceneIndex}`);
+ } catch (error) {
+ console.error('保存场景状态失败:', error);
+ }
+}
+
let currentSceneIndex = 0;
const scenes = [
{
name: '起床',
- defaultVideo: '8-4-bd-2.mp4',
- interactionVideo: '8-4-sh.mp4',
+ defaultVideo: 'qc-bd-4.mp4',
+ interactionVideo: 'qc-sh-4.mp4',
tag: 'wakeup',
apiKey: 'bot-20250724150616-xqpz8' // 起床场景的API key
},
{
name: '开车',
- defaultVideo: '8-4-kc-bd.mp4',
- interactionVideo: '8-4-kc-sh.mp4',
+ defaultVideo: 'kc-bd-3.mp4',
+ interactionVideo: 'kc-sh-3.mp4',
tag: 'driving',
apiKey: 'bot-20250623140339-r8f8b' // 开车场景的API key
},
{
name: '喝茶',
- defaultVideo: '8-4-hc-bd.mp4',
- interactionVideo: '8-4-hc-sh.mp4',
+ defaultVideo: 'hc-bd-3.mp4',
+ interactionVideo: 'hc-sh-3(1).mp4',
tag: 'tea',
apiKey: 'bot-20250804180724-4dgtk' // 喝茶场景的API key
}
@@ -117,11 +148,34 @@ function getCurrentScene() {
return scenes[currentSceneIndex];
}
-// 切换到下一个场景
+// 切换到下一个场景 - 改进版
function switchToNextScene() {
+ const previousIndex = currentSceneIndex;
+ const previousScene = scenes[previousIndex].name;
+
currentSceneIndex = (currentSceneIndex + 1) % scenes.length;
- console.log(`切换到场景: ${getCurrentScene().name}`);
- return getCurrentScene();
+ const newScene = getCurrentScene();
+
+ console.log(`场景切换: ${previousScene}(${previousIndex}) → ${newScene.name}(${currentSceneIndex})`);
+
+ // 保存状态到文件
+ saveSceneState();
+
+ return newScene;
+}
+
+// 在服务器启动时加载场景状态
+async function initializeServer() {
+ try {
+ // 加载场景状态
+ loadSceneState();
+
+ await messageHistory.initialize();
+ console.log('消息历史初始化完成');
+ console.log(`当前场景: ${getCurrentScene().name} (索引: ${currentSceneIndex})`);
+ } catch (error) {
+ console.error('初始化服务器失败:', error);
+ }
}
// 视频映射配置 - 动态更新
@@ -185,8 +239,13 @@ app.get('/api/current-scene', (req, res) => {
// 获取视频映射
app.get('/api/video-mapping', (req, res) => {
- // videoMapping = getCurrentScene()
- res.json({ mapping: videoMapping });
+ const currentMapping = getVideoMapping();
+ const dynamicMapping = {
+ 'default': currentMapping.defaultVideo,
+ '8-4-sh': currentMapping.interactionVideo,
+ 'tag': currentMapping.tag
+ };
+ res.json({ mapping: dynamicMapping });
});
// 获取默认视频
@@ -203,7 +262,8 @@ io.on('connection', (socket) => {
connectedClients.set(socket.id, {
socket: socket,
currentVideo: getDefaultVideo(),
- isInInteraction: false
+ isInInteraction: false,
+ hasTriggeredSceneSwitch: false // 添加这个标志
});
// 处理WebRTC信令 - 用于传输视频流
@@ -344,11 +404,25 @@ io.on('connection', (socket) => {
// 处理用户关闭连接事件
socket.on('user-disconnect', () => {
+ console.log('=== 场景切换开始 ===');
console.log('用户主动关闭连接:', socket.id);
+ console.log('切换前场景:', getCurrentScene().name, '(索引:', currentSceneIndex, ')');
// 切换到下一个场景
const newScene = switchToNextScene();
- console.log(`场景已切换到: ${newScene.name}`);
+ console.log('切换后场景:', newScene.name, '(索引:', currentSceneIndex, ')');
+
+ // 检查是否已经处理过场景切换
+ const client = connectedClients.get(socket.id);
+ if (client && client.hasTriggeredSceneSwitch) {
+ console.log('场景切换已处理,跳过重复触发');
+ return;
+ }
+
+ // 标记已处理场景切换
+ if (client) {
+ client.hasTriggeredSceneSwitch = true;
+ }
// 更新videoMapping
const newMapping = getVideoMapping();
@@ -356,20 +430,17 @@ io.on('connection', (socket) => {
videoMapping['8-4-sh'] = newMapping.interactionVideo;
videoMapping['tag'] = newMapping.tag;
- console.log('videoMapping已更新:', videoMapping);
-
- // 清理客户端状态
- const client = connectedClients.get(socket.id);
- if (client) {
- client.currentVideo = newMapping.defaultVideo;
- client.isInInteraction = false;
- }
-
// 广播场景切换事件给所有客户端
io.emit('scene-switched', {
- scene: newScene,
- mapping: newMapping,
- from: socket.id
+ scene: newScene,
+ mapping: {
+ defaultVideo: newMapping.defaultVideo,
+ interactionVideo: newMapping.interactionVideo,
+ tag: newMapping.tag,
+ 'default': newMapping.defaultVideo,
+ '8-4-sh': newMapping.interactionVideo
+ },
+ from: socket.id
});
});
diff --git a/src/index.html b/src/index.html
deleted file mode 100644
index 0809ed4..0000000
--- a/src/index.html
+++ /dev/null
@@ -1,408 +0,0 @@
-
-
-
-
-
- Soulmate In Parallels - 壹和零人工智能
-
-
-
-
-
-
-
-
- WebRTC 音频通话
- 实时播放录制视频,支持文本和语音输入
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 未选择视频
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 1a16f3f..d4c9ce6 100644
--- a/src/index.js
+++ b/src/index.js
@@ -86,6 +86,10 @@ class WebRTCChat {
this.recordedVideoBuffer = document.getElementById('recordedVideoBuffer'); // 新增缓冲视频元素
this.videoLoading = document.getElementById('videoLoading'); // 加载指示器
+ // 头像和视频容器元素
+ this.avatarContainer = document.getElementById('avatarContainer');
+ this.videoContainer = document.getElementById('videoContainer');
+
// 当前活跃的视频元素标识
this.activeVideoElement = 'main'; // 'main' 或 'buffer'
@@ -122,7 +126,7 @@ class WebRTCChat {
});
this.socket.on('disconnect', () => {
- this.updateStatus('与服务器断开连接', 'disconnected');
+ this.connectionStatus.style.display = 'none';
this.logMessage('与服务器断开连接', 'error');
});
@@ -153,9 +157,21 @@ class WebRTCChat {
// 场景切换处理
this.socket.on('scene-switched', (data) => {
- this.logMessage(`场景已切换到: ${data.currentScene}`, 'info');
- // 移除自动清除缓存和播放视频的逻辑
- // 现在依赖页面刷新来处理缓存清除
+ this.logMessage(`场景已切换到: ${data.scene.name}`, 'info');
+
+ // 更新视频映射
+ this.videoMapping = data.mapping;
+ this.interactionVideo = data.mapping.interactionVideo;
+ this.defaultVideo = data.mapping.defaultVideo;
+
+ // 立即切换到新场景的默认视频
+ this.switchVideoStream(this.defaultVideo, 'scene-change');
+
+ console.log('场景切换完成,新的视频配置:', {
+ defaultVideo: this.defaultVideo,
+ interactionVideo: this.interactionVideo,
+ tag: data.mapping.tag
+ });
});
}
@@ -832,7 +848,12 @@ class WebRTCChat {
this.stopButton.disabled = false;
// 显示结束通话按钮
- this.stopButton.classList.add('show');
+ this.stopButton.style.display = 'block';
+
+ // 隐藏头像,显示视频
+ if (this.videoContainer) {
+ this.videoContainer.classList.add('calling');
+ }
this.updateAudioStatus('已连接', 'connected');
this.logMessage('音频通话已开始', 'success');
@@ -883,6 +904,15 @@ class WebRTCChat {
this.peerConnection.close();
this.peerConnection = null;
}
+
+ // 隐藏结束通话按钮
+ this.stopButton.style.display = 'none';
+ this.stopButton.disabled = true;
+
+ // 显示头像,隐藏视频
+ if (this.videoContainer) {
+ this.videoContainer.classList.remove('calling');
+ }
// 直接刷新页面清除所有缓存
console.log('通话已结束,正在刷新页面清除缓存...');
@@ -1185,6 +1215,10 @@ class WebRTCChat {
updateStatus(message, type) {
this.connectionStatus.textContent = message;
this.connectionStatus.className = `status ${type}`;
+ // 显示状态元素(仅在连接时显示)
+ if (type === 'connected') {
+ this.connectionStatus.style.display = 'block';
+ }
}
updateAudioStatus(message, type) {
@@ -1219,12 +1253,12 @@ class WebRTCChat {
// 添加图标切换方法
switchToCallingIcon() {
const callIcon = document.getElementById('callIcon');
- const callingIcon = document.getElementById('callingIcon');
+ const callingText = document.getElementById('callingText');
const startButton = this.startButton;
- if (callIcon && callingIcon && startButton) {
+ if (callIcon && callingText && startButton) {
callIcon.style.display = 'none';
- callingIcon.style.display = 'block';
+ callingText.style.display = 'block';
startButton.classList.add('calling');
startButton.title = '通话中...';
}
@@ -1232,12 +1266,12 @@ class WebRTCChat {
switchToDefaultIcon() {
const callIcon = document.getElementById('callIcon');
- const callingIcon = document.getElementById('callingIcon');
+ const callingText = document.getElementById('callingText');
const startButton = this.startButton;
- if (callIcon && callingIcon && startButton) {
+ if (callIcon && callingText && startButton) {
callIcon.style.display = 'block';
- callingIcon.style.display = 'none';
+ callingText.style.display = 'none';
startButton.classList.remove('calling');
startButton.title = '开始通话';
startButton.disabled = false;
diff --git a/src/tx.png b/src/tx.png
new file mode 100644
index 0000000..93ae887
Binary files /dev/null and b/src/tx.png differ
diff --git a/videos/0-1.mp4 b/videos/0-1.mp4
deleted file mode 100644
index db78dc1..0000000
Binary files a/videos/0-1.mp4 and /dev/null differ
diff --git a/videos/2.mp4 b/videos/2.mp4
deleted file mode 100644
index 152d359..0000000
Binary files a/videos/2.mp4 and /dev/null differ
diff --git a/videos/8-4-bd-1.mp4 b/videos/8-4-bd-1.mp4
deleted file mode 100644
index 50e4c81..0000000
Binary files a/videos/8-4-bd-1.mp4 and /dev/null differ
diff --git a/videos/8-4-bd-2.mp4 b/videos/8-4-bd-2.mp4
deleted file mode 100644
index c7b765d..0000000
Binary files a/videos/8-4-bd-2.mp4 and /dev/null differ
diff --git a/videos/8-4-bd.mp4 b/videos/8-4-bd.mp4
deleted file mode 100644
index 7c89f8b..0000000
Binary files a/videos/8-4-bd.mp4 and /dev/null differ
diff --git a/videos/8-4-hc-bd.mp4 b/videos/8-4-hc-bd.mp4
deleted file mode 100644
index 28b93bc..0000000
Binary files a/videos/8-4-hc-bd.mp4 and /dev/null differ
diff --git a/videos/8-4-hc-sh.mp4 b/videos/8-4-hc-sh.mp4
deleted file mode 100644
index 5e5cb89..0000000
Binary files a/videos/8-4-hc-sh.mp4 and /dev/null differ
diff --git a/videos/8-4-kc-bd.mp4 b/videos/8-4-kc-bd.mp4
deleted file mode 100644
index 5e66bbb..0000000
Binary files a/videos/8-4-kc-bd.mp4 and /dev/null differ
diff --git a/videos/8-4-kc-sh.mp4 b/videos/8-4-kc-sh.mp4
deleted file mode 100644
index cf0155d..0000000
Binary files a/videos/8-4-kc-sh.mp4 and /dev/null differ
diff --git a/videos/8-4-sh-3.mp4 b/videos/8-4-sh-3.mp4
deleted file mode 100644
index f10c741..0000000
Binary files a/videos/8-4-sh-3.mp4 and /dev/null differ
diff --git a/videos/8-4-sh.mp4 b/videos/8-4-sh.mp4
deleted file mode 100644
index 3ca5fa3..0000000
Binary files a/videos/8-4-sh.mp4 and /dev/null differ
diff --git a/videos/dj.mp4 b/videos/dj.mp4
new file mode 100644
index 0000000..5a5dee8
Binary files /dev/null and b/videos/dj.mp4 differ
diff --git a/videos/hc-bd-3.mp4 b/videos/hc-bd-3.mp4
new file mode 100644
index 0000000..a5cc81b
Binary files /dev/null and b/videos/hc-bd-3.mp4 differ
diff --git a/videos/hc-sh-3(1).mp4 b/videos/hc-sh-3(1).mp4
new file mode 100644
index 0000000..59b9756
Binary files /dev/null and b/videos/hc-sh-3(1).mp4 differ
diff --git a/videos/hc-sh-3.mp4 b/videos/hc-sh-3.mp4
new file mode 100644
index 0000000..fd45a4b
Binary files /dev/null and b/videos/hc-sh-3.mp4 differ
diff --git a/videos/kc-bd-3.mp4 b/videos/kc-bd-3.mp4
new file mode 100644
index 0000000..a41e2c8
Binary files /dev/null and b/videos/kc-bd-3.mp4 differ
diff --git a/videos/kc-sh-3.mp4 b/videos/kc-sh-3.mp4
new file mode 100644
index 0000000..1df24ae
Binary files /dev/null and b/videos/kc-sh-3.mp4 differ
diff --git a/videos/qc-bd-4.mp4 b/videos/qc-bd-4.mp4
new file mode 100644
index 0000000..0652faa
Binary files /dev/null and b/videos/qc-bd-4.mp4 differ
diff --git a/videos/qc-sh-4.mp4 b/videos/qc-sh-4.mp4
new file mode 100644
index 0000000..2fb6c92
Binary files /dev/null and b/videos/qc-sh-4.mp4 differ