直播页:弹幕 WebSocket 自动重连与失败提示
Made-with: Cursor
This commit is contained in:
@@ -53,6 +53,7 @@
|
||||
/>
|
||||
<button type="button" class="live-dm-send" @click="sendDm">发送</button>
|
||||
</div>
|
||||
<p v-if="dmHint" class="live-dm-hint">{{ dmHint }}</p>
|
||||
</section>
|
||||
|
||||
<section class="live-block live-block--divider" aria-label="外链直播间">
|
||||
@@ -86,9 +87,13 @@ const qualityOptions = LIVE_QUALITY_OPTIONS
|
||||
const captureQualityPref = ref('source')
|
||||
const liveInfoLine = ref('')
|
||||
const dmDraft = ref('')
|
||||
const dmHint = ref('')
|
||||
const dmItems = ref([])
|
||||
let dmIdSeq = 0
|
||||
let dmWs = null
|
||||
let dmIntentionalClose = false
|
||||
let dmReconnectTimer = null
|
||||
let dmReconnectAttempt = 0
|
||||
|
||||
const enterUrl = computed(() => (rawLiveUrl.value || '').trim())
|
||||
|
||||
@@ -185,11 +190,42 @@ function pushDmLine(text) {
|
||||
}, 12000)
|
||||
}
|
||||
|
||||
function scheduleDmReconnect() {
|
||||
if (dmIntentionalClose) return
|
||||
if (dmReconnectTimer) return
|
||||
const delay = Math.min(2500 * Math.pow(1.4, dmReconnectAttempt), 28000)
|
||||
dmReconnectTimer = window.setTimeout(() => {
|
||||
dmReconnectTimer = null
|
||||
if (dmIntentionalClose) return
|
||||
connectDanmaku()
|
||||
}, delay)
|
||||
}
|
||||
|
||||
function connectDanmaku() {
|
||||
if (dmIntentionalClose) return
|
||||
try {
|
||||
dmWs?.close()
|
||||
} catch (_) {}
|
||||
dmWs = new WebSocket(liveDanmakuWsURL())
|
||||
dmWs = null
|
||||
const url = liveDanmakuWsURL()
|
||||
dmHint.value = dmReconnectAttempt > 0 ? `弹幕重连中(第 ${dmReconnectAttempt + 1} 次)…` : '弹幕通道连接中…'
|
||||
try {
|
||||
dmWs = new WebSocket(url)
|
||||
} catch (e) {
|
||||
dmHint.value = '无法创建弹幕连接,请检查网络或地址配置'
|
||||
dmReconnectAttempt += 1
|
||||
scheduleDmReconnect()
|
||||
return
|
||||
}
|
||||
dmWs.onopen = () => {
|
||||
dmReconnectAttempt = 0
|
||||
dmHint.value = ''
|
||||
}
|
||||
dmWs.onerror = () => {
|
||||
if (!dmIntentionalClose) {
|
||||
dmHint.value = '弹幕 WebSocket 异常(多为网关未放行,见下方说明)'
|
||||
}
|
||||
}
|
||||
dmWs.onmessage = (ev) => {
|
||||
let j
|
||||
try {
|
||||
@@ -203,18 +239,32 @@ function connectDanmaku() {
|
||||
}
|
||||
dmWs.onclose = () => {
|
||||
dmWs = null
|
||||
if (dmIntentionalClose) return
|
||||
dmHint.value = '弹幕已断开,自动重连中…'
|
||||
dmReconnectAttempt += 1
|
||||
scheduleDmReconnect()
|
||||
}
|
||||
}
|
||||
|
||||
function sendDm() {
|
||||
const t = dmDraft.value.trim()
|
||||
if (!t) return
|
||||
if (!dmWs || dmWs.readyState !== WebSocket.OPEN) return
|
||||
if (!dmWs || dmWs.readyState !== WebSocket.OPEN) {
|
||||
dmHint.value =
|
||||
'弹幕未连接,无法发送。请确认:① 已部署含弹幕接口的 API;② Nginx 已为 /api/web/live/danmaku/ws 配置 WebSocket 反代(Upgrade);③ 与直播信令使用同一域名/网关。'
|
||||
return
|
||||
}
|
||||
try {
|
||||
dmWs.send(JSON.stringify({ text: t }))
|
||||
dmDraft.value = ''
|
||||
dmHint.value = ''
|
||||
} catch (_) {
|
||||
dmHint.value = '发送失败,请稍后重试或刷新页面'
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
dmIntentionalClose = false
|
||||
loadCaptureQualityPref()
|
||||
loadHomepage()
|
||||
await nextTick()
|
||||
@@ -230,6 +280,11 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
dmIntentionalClose = true
|
||||
if (dmReconnectTimer) {
|
||||
clearTimeout(dmReconnectTimer)
|
||||
dmReconnectTimer = null
|
||||
}
|
||||
if (liveInfoTimer) {
|
||||
clearInterval(liveInfoTimer)
|
||||
liveInfoTimer = null
|
||||
@@ -397,6 +452,14 @@ onUnmounted(() => {
|
||||
.live-dm-send:hover {
|
||||
background: rgba(0, 212, 255, 0.25);
|
||||
}
|
||||
.live-dm-hint {
|
||||
max-width: 480px;
|
||||
margin: 10px auto 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
color: #ffb86c;
|
||||
text-align: left;
|
||||
}
|
||||
.live-video-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
Reference in New Issue
Block a user