完成视频浏览
This commit is contained in:
@@ -150,6 +150,63 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 视频控制栏 */
|
||||
.video-controls {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(to bottom, rgba(0,0,0,0.7), transparent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.video-controls label {
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.video-controls select {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #444;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 视频信息栏 */
|
||||
.video-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
|
||||
color: #ccc;
|
||||
font-size: 12px;
|
||||
z-index: 10;
|
||||
font-family: monospace;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.video-info .info-row {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.video-info .info-label {
|
||||
color: #888;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.video-info .info-value {
|
||||
color: #0f0;
|
||||
}
|
||||
|
||||
#video-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -196,9 +253,28 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-area">
|
||||
<!-- 视频控制栏 -->
|
||||
<div class="video-controls" id="video-controls" style="display: none;">
|
||||
<label>码流类型:</label>
|
||||
<select id="stream-type">
|
||||
<option value="0">主码流(高清)</option>
|
||||
<option value="1">子码流(流畅)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="video-container">
|
||||
<div class="placeholder">👈 从左侧选择一个直播源</div>
|
||||
</div>
|
||||
<!-- 视频信息栏 -->
|
||||
<div class="video-info" id="video-info" style="display: none;">
|
||||
<div class="info-row">
|
||||
<span class="info-label">摄像头ID:</span>
|
||||
<span class="info-value" id="info-camera-id">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">播放地址:</span>
|
||||
<span class="info-value" id="info-url">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -209,6 +285,7 @@
|
||||
// 全局状态
|
||||
let currentVideoNode = null; // 当前播放的节点对象
|
||||
let hls = null; // HLS 实例
|
||||
let currentStreamType = 0; // 当前码流类型
|
||||
|
||||
// 缓存已加载的子节点数据: { parentId: [childrenNodes] }
|
||||
const childrenCache = new Map();
|
||||
@@ -243,13 +320,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点详情(这里主要用来获取视频流地址,但也可以直接用子节点数据,不过为符合 API 设计,单独调用 stream 接口)
|
||||
async function fetchStreamUrl(nodeId) {
|
||||
// 获取视频流地址
|
||||
async function fetchStreamUrl(nodeId, streamType = 0) {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/stream/${nodeId}`);
|
||||
const res = await fetch(`${API_BASE}/stream/${nodeId}?stream_type=${streamType}`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const data = await res.json();
|
||||
return data.url;
|
||||
return await res.json();
|
||||
} catch (err) {
|
||||
console.error(`获取节点 ${nodeId} 的视频地址失败:`, err);
|
||||
return null;
|
||||
@@ -257,20 +333,26 @@
|
||||
}
|
||||
|
||||
// 播放视频
|
||||
async function playVideo(node) {
|
||||
async function playVideo(node, streamType = 0) {
|
||||
if (!node || !node.is_leaf) return;
|
||||
|
||||
// 显示加载占位
|
||||
const container = document.getElementById('video-container');
|
||||
container.innerHTML = '<div class="placeholder">📡 正在加载直播流...</div>';
|
||||
|
||||
// 隐藏信息和控制栏
|
||||
document.getElementById('video-controls').style.display = 'none';
|
||||
document.getElementById('video-info').style.display = 'none';
|
||||
|
||||
// 获取流地址
|
||||
const streamUrl = await fetchStreamUrl(node.id);
|
||||
if (!streamUrl) {
|
||||
const streamData = await fetchStreamUrl(node.id, streamType);
|
||||
if (!streamData || !streamData.url) {
|
||||
container.innerHTML = '<div class="placeholder">❌ 无法获取视频地址,请稍后重试</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const streamUrl = streamData.url;
|
||||
|
||||
// 清理旧播放器
|
||||
if (hls) {
|
||||
hls.destroy();
|
||||
@@ -313,6 +395,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 更新并显示视频信息
|
||||
document.getElementById('info-camera-id').textContent = streamData.cameraIndexCode;
|
||||
document.getElementById('info-url').textContent = streamUrl;
|
||||
document.getElementById('video-info').style.display = 'block';
|
||||
|
||||
// 显示控制栏
|
||||
document.getElementById('video-controls').style.display = 'flex';
|
||||
|
||||
currentVideoNode = node;
|
||||
}
|
||||
|
||||
@@ -388,7 +478,7 @@
|
||||
// 高亮当前选中的节点
|
||||
clearActiveHighlight();
|
||||
contentDiv.classList.add('active');
|
||||
await playVideo(node);
|
||||
await playVideo(node, currentStreamType);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -442,7 +532,14 @@
|
||||
|
||||
// 初始化视频区域(可选:尝试自动播放需用户交互)
|
||||
function initVideoArea() {
|
||||
// 预留
|
||||
// 监听码流类型切换
|
||||
document.getElementById('stream-type').addEventListener('change', async (e) => {
|
||||
currentStreamType = parseInt(e.target.value);
|
||||
// 如果当前有播放的视频,重新加载
|
||||
if (currentVideoNode) {
|
||||
await playVideo(currentVideoNode, currentStreamType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 启动
|
||||
|
||||
Reference in New Issue
Block a user