开播页:预览放大;信令重连上限与健康检查提示 502
Made-with: Cursor
This commit is contained in:
@@ -57,6 +57,14 @@ function liveWsURLPublish(token) {
|
||||
|
||||
const defaultIce = [{ urls: 'stun:stun.l.google.com:19302' }]
|
||||
|
||||
const MAX_SIGNAL_RECONNECT = 15
|
||||
|
||||
function healthCheckUrl() {
|
||||
if (apiBase) return `${apiBase}/api/health`
|
||||
if (typeof window !== 'undefined') return `${window.location.origin}/api/health`
|
||||
return '/api/health'
|
||||
}
|
||||
|
||||
function buildCameraConstraints(publishKey, videoDeviceId) {
|
||||
const preset = QUALITY_MEDIA[publishKey] || QUALITY_MEDIA.source
|
||||
const dev = videoDeviceId ? { deviceId: { exact: videoDeviceId } } : {}
|
||||
@@ -130,6 +138,7 @@ export function startPublishing(opts = {}) {
|
||||
let reconnectTimer = null
|
||||
let reconnectAttempt = 0
|
||||
let wsGen = 0
|
||||
let reconnectStopped = false
|
||||
|
||||
function clearReconnectTimer() {
|
||||
if (reconnectTimer) {
|
||||
@@ -254,20 +263,36 @@ export function startPublishing(opts = {}) {
|
||||
}
|
||||
|
||||
function scheduleReconnect() {
|
||||
if (closedByLocal) return
|
||||
if (closedByLocal || reconnectStopped) return
|
||||
clearReconnectTimer()
|
||||
const delay = Math.min(2000 * Math.pow(1.45, reconnectAttempt), 28000)
|
||||
reconnectAttempt += 1
|
||||
onStatus(`信令断开,${Math.round(delay / 1000)} 秒后重连(${reconnectAttempt})…`)
|
||||
reconnectTimer = window.setTimeout(() => {
|
||||
if (reconnectAttempt > MAX_SIGNAL_RECONNECT) {
|
||||
reconnectStopped = true
|
||||
onStatus(
|
||||
`信令已重试 ${MAX_SIGNAL_RECONNECT} 次仍失败。多为 API 返回 502(进程未启动/崩溃)或 Nginx 未正确反代到 Go。请检查服务与 \`/api/web/live/ws\` 的 WebSocket 配置,修复后刷新本页再点「开始直播」。`
|
||||
)
|
||||
return
|
||||
}
|
||||
const delay = Math.min(2000 * Math.pow(1.45, reconnectAttempt - 1), 28000)
|
||||
onStatus(`信令断开,约 ${Math.round(delay / 1000)} 秒后重试(${reconnectAttempt}/${MAX_SIGNAL_RECONNECT})…`)
|
||||
reconnectTimer = window.setTimeout(async () => {
|
||||
reconnectTimer = null
|
||||
if (closedByLocal) return
|
||||
if (closedByLocal || reconnectStopped) return
|
||||
try {
|
||||
const r = await fetch(healthCheckUrl(), { method: 'GET', cache: 'no-store' })
|
||||
if (!r.ok) {
|
||||
onStatus(`API 不可用(HTTP ${r.status}),多为网关 502,推迟重试…`)
|
||||
}
|
||||
} catch (_) {
|
||||
onStatus('无法访问健康检查接口,请确认网络与域名。')
|
||||
}
|
||||
if (closedByLocal || reconnectStopped) return
|
||||
openSignalingSocket()
|
||||
}, delay)
|
||||
}
|
||||
|
||||
function openSignalingSocket() {
|
||||
if (closedByLocal) return
|
||||
if (closedByLocal || reconnectStopped) return
|
||||
const myGen = ++wsGen
|
||||
clearReconnectTimer()
|
||||
if (ws) {
|
||||
@@ -321,6 +346,7 @@ export function startPublishing(opts = {}) {
|
||||
|
||||
function stop() {
|
||||
closedByLocal = true
|
||||
reconnectStopped = true
|
||||
wsGen += 1
|
||||
clearReconnectTimer()
|
||||
if (ws) {
|
||||
|
||||
@@ -133,7 +133,7 @@ onBeforeRouteLeave(() => {
|
||||
|
||||
<style scoped>
|
||||
.live-broadcast {
|
||||
max-width: 720px;
|
||||
max-width: min(1200px, 100%);
|
||||
}
|
||||
.status {
|
||||
color: #409eff;
|
||||
@@ -160,21 +160,23 @@ onBeforeRouteLeave(() => {
|
||||
}
|
||||
.preview-wrap {
|
||||
position: relative;
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
max-width: 1100px;
|
||||
}
|
||||
.preview-main {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
min-height: 320px;
|
||||
max-height: min(85vh, 900px);
|
||||
border-radius: 8px;
|
||||
background: #000;
|
||||
object-fit: contain;
|
||||
}
|
||||
.preview-pip {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 12px;
|
||||
width: min(28%, 200px);
|
||||
right: 14px;
|
||||
bottom: 14px;
|
||||
width: min(32%, 280px);
|
||||
aspect-ratio: 4 / 3;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #409eff;
|
||||
|
||||
Reference in New Issue
Block a user