let video_list = {}; //element_id -- socket let run_list = {}; //element_id -- runtag(替换berror) var channel_list = null; const fourViewButton = document.getElementById('fourView'); const nineViewButton = document.getElementById('nineView'); //页面即将卸载时执行 window.addEventListener('beforeunload', function (event) { // 关闭所有 WebSocket 连接或执行其他清理操作 for(let key in video_list){ delete run_list[key]; video_list[key].close(); delete video_list[key]; } }); //页面加载时执行 document.addEventListener('DOMContentLoaded', async function() { console.log('DOM fully loaded and parsed'); // 发送请求获取额外数据 --- 这个接口功能有点大了---暂时只是更新通道树2024-7-29 try { let response = await fetch('/api/channel/list'); if (!response.ok) { throw new Error('Network response was not ok'); } channel_list = await response.json(); // 遍历输出每个元素的信息 let area_name = "" let html = ''; html += ''; } area_name = `${channel.area_name}`; html += `
  • ${area_name}`; html += ''; html += '
  • '; } html += ''; const treeView = document.getElementById('treeView'); treeView.innerHTML = html generateVideoNodes(4); } catch (error) { console.error('Failed to fetch data:', error); } }); //视频窗口 document.getElementById('fourView').addEventListener('click', function() { if (fourViewButton.classList.contains('btn-primary')) { return; // 如果按钮已经是选中状态,直接返回 } const videoGrid = document.getElementById('videoGrid'); videoGrid.classList.remove('nine'); videoGrid.classList.add('four'); generateVideoNodes(4); //更新按钮点击状态 fourViewButton.classList.remove('btn-secondary'); fourViewButton.classList.add('btn-primary'); nineViewButton.classList.remove('btn-primary'); nineViewButton.classList.add('btn-secondary'); }); document.getElementById('nineView').addEventListener('click', function() { if (nineViewButton.classList.contains('btn-primary')) { return; // 如果按钮已经是选中状态,直接返回 } const videoGrid = document.getElementById('videoGrid'); videoGrid.classList.remove('four'); videoGrid.classList.add('nine'); generateVideoNodes(9); //更新按钮点击状态 fourViewButton.classList.remove('btn-primary'); fourViewButton.classList.add('btn-secondary'); nineViewButton.classList.remove('btn-secondary'); nineViewButton.classList.add('btn-primary'); }); function generateVideoNodes(count) { //在这里显示视频-初始化 //结束在播放的socket for(let key in video_list){ delete run_list[key]; video_list[key].close(); delete video_list[key]; } //切换窗口布局 const videoGrid = document.getElementById('videoGrid'); let html = ''; for (let i = 0; i < count; i++) { let frameWidth = count === 4 ? 'calc(50% - 10px)' : 'calc(33.33% - 10px)'; html += `
    Video Stream ${i+1}
    Video Stream
    `; } videoGrid.innerHTML = html; //获取视频接口 const url = `/api/viewlist?count=${count}`; fetch(url) .then(response => response.json()) .then(data => { console.log('Success:', data); clist = data.clist; elist = data.elist; nlist = data.nlist; for(let i=0;i { console.error('Error:', error); }); } function toggleFullScreen(id) { console.log('toggleFullScreen'); const videoFrame = document.querySelector(`[data-frame-id="${id}"]`); if (!document.fullscreenElement) { videoFrame.requestFullscreen().catch(err => { alert(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`); }); } else { document.exitFullscreen(); }; } function closeVideo(id) { const titleElement = document.querySelector(`[data-frame-id="${id}"] .video-title`); if (titleElement.textContent === `Video Stream ${Number(id)+1}`) { showModal('当前视频窗口未播放视频。'); return; }; console.log('closeVideo'); //发送视频链接接口 const url = '/api/close_stream'; const data = {"element_id":id}; // 发送 POST 请求 fetch(url, { method: 'POST', // 指定请求方法为 POST headers: { 'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON }, body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串 }) .then(response => response.json()) // 将响应解析为 JSON .then(data => { console.log('Success:', data); const istatus = data.status; if(istatus === 0){ showModal(data.msg); // 使用 Modal 显示消息 return; } else{ const videoFrame = document.querySelector(`[data-frame-id="${id}"] .video-area img`); const titleElement = document.querySelector(`[data-frame-id="${id}"] .video-title`); run_list[id] = false; video_list[id].close(); videoFrame.src = ''; // 清空画面 videoFrame.style.display = 'none'; // 停止播放时隐藏 img 元素 titleElement.textContent = `Video Stream ${id+1}`; removeErrorMessage(videoFrame) } }) .catch((error) => { showModal(`Error: ${error.message}`); // 使用 Modal 显示错误信息 return; }); } function allowDrop(event) { event.preventDefault(); } function drag(event) { event.dataTransfer.setData("text", event.target.dataset.nodeId); event.dataTransfer.setData("name", event.target.dataset.nodeName); } function drop(event) { event.preventDefault(); const nodeId = event.dataTransfer.getData("text"); const nodeName = event.dataTransfer.getData("name"); const frameId = event.currentTarget.dataset.frameId; //需要判断下当前窗口是否已经在播放视频 const imgElement = document.getElementById(`video-${frameId}`); const titleElement = document.querySelector(`[data-frame-id="${frameId}"] .video-title`); if (titleElement.textContent !== `Video Stream ${Number(frameId)+1}`) { showModal('请先关闭当前窗口视频,然后再播放新的视频。'); return; }; //发送视频链接接口 const url = '/api/start_stream'; const data = {"channel_id":nodeId,"element_id":frameId}; // 发送 POST 请求 fetch(url, { method: 'POST', // 指定请求方法为 POST headers: { 'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON }, body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串 }) .then(response => response.json()) // 将响应解析为 JSON .then(data => { const istatus = data.status; if(istatus === 0){ showModal(data.msg); // 使用 Modal 显示消息 return; } else{ //获取视频流 connectToStream(frameId,nodeId,nodeName); } }) .catch((error) => { showModal(`Error: ${error.message}`); // 使用 Modal 显示错误信息 return; }); //console.log('retrun 只是把fetch结束,这里的代码还是会执行'); } function connectToStream(element_id,channel_id,channel_name) { console.log("开始连接视频",channel_id); // 设置视频区域的标题 const titleElement = document.querySelector(`[data-frame-id="${element_id}"] .video-title`); titleElement.textContent = channel_name; //获取视频 const imgElement = document.getElementById(`video-${element_id}`); imgElement.alt = `Stream ${channel_name}`; const streamUrl = `ws://${window.location.host}/api/ws/video_feed/${channel_id}`; let berror = false; function connect() { const socket = new WebSocket(streamUrl); video_list[element_id] = socket; run_list[element_id] = true; imgElement.style.display = 'block'; berror_state = false; // 处理连接打开事件 socket.onopen = () => { console.log('WebSocket connection established'); }; socket.onmessage = function(event) { const reader = new FileReader(); reader.readAsArrayBuffer(event.data); reader.onload = () => { const arrayBuffer = reader.result; const decoder = new TextDecoder("utf-8"); const decodedData = decoder.decode(arrayBuffer); if (decodedData === "video_error") { //video_error displayErrorMessage(imgElement, "该视频源未获取到画面,请检查,默认每隔2分钟将重新链接视频源。"); berror_state = true; } else if(decodedData === "client_error"){ //client_error run_list[element_id] = false; displayErrorMessage(imgElement, "该通道节点数据存在问题,请重启或联系技术支持!"); socket.close(); // 停止连接 berror_state = true; } else { if(berror_state){ removeErrorMessage(imgElement); berror_state = false; } // 释放旧的对象URL if (imgElement.src) { URL.revokeObjectURL(imgElement.src); } //blob = new Blob([arrayBuffer], { type: 'image/jpeg' }); imgElement.src = URL.createObjectURL(event.data); } }; //图片显示方案二 // if (imgElement.src) { // URL.revokeObjectURL(imgElement.src); // } // imgElement.src = URL.createObjectURL(event.data); }; socket.onclose = function() { if(run_list[element_id]){ console.log(`尝试重新连接... Channel ID: ${channel_id}`); setTimeout(connect, 1000*10); // 尝试在10秒后重新连接 } }; socket.onerror = function() { console.log(`WebSocket错误,Channel ID: ${channel_id}`); socket.close(); }; }; connect(); } function displayErrorMessage(imgElement, message) { removeErrorMessage(imgElement) imgElement.style.display = 'none'; // 隐藏图片 const errorElement = document.createElement('div'); errorElement.textContent = message; errorElement.classList.add('error-message'); imgElement.parentNode.appendChild(errorElement); } function removeErrorMessage(imgElement) { const errorElement = imgElement.parentNode.querySelector('.error-message'); if (errorElement) { imgElement.parentNode.removeChild(errorElement); } }