diff --git a/src/composables/useMonitorLive.js b/src/composables/useMonitorLive.js new file mode 100644 index 0000000..108cb3a --- /dev/null +++ b/src/composables/useMonitorLive.js @@ -0,0 +1,93 @@ +import { computed, reactive } from 'vue' +import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live' + +export function useMonitorLive({ getDockId, ui, getLivePlayer }) { + const live = reactive({ session: null, dockId: '', playUrl: '', heartbeatTimer: null }) + + const livePhaseText = computed(() => { + if (!live.session) return '待机' + const phase = live.session.phase + if (phase === 'streaming') return live.playUrl.startsWith('fake://') ? '模拟直播' : '直播中' + if (phase === 'starting') return '启动中' + if (phase === 'reconnecting') return '重连中' + if (phase === 'stopping') return '停止中' + if (phase === 'failed') return '启动失败' + return '已停止' + }) + + const canFullscreenLive = computed(() => !!live.playUrl && !live.playUrl.startsWith('fake://')) + + function toggleLive() { + if (live.session) closeLive() + else openLive() + } + + function fullscreenLive() { + if (!canFullscreenLive.value) { + ui.toast('当前无可全屏播放的视频流') + return + } + const ok = getLivePlayer()?.requestFullscreen?.() + if (!ok) ui.toast('当前浏览器不支持全屏') + } + + async function openLive() { + const dockId = getDockId?.() + if (!dockId) return + try { + const result = await joinLive(dockId) + live.session = result.session + live.dockId = dockId + scheduleLiveHeartbeat(result.leaseExpiresAt) + if (result.session.phase === 'streaming') await refreshPlayURL() + } catch (e) { + ui.toast(e.message || '打开直播失败') + } + } + + async function refreshPlayURL() { + if (!live.dockId) return + const play = await getLivePlayURL(live.dockId) + live.playUrl = play.playUrl || '' + } + + function scheduleLiveHeartbeat(expiresAt) { + window.clearTimeout(live.heartbeatTimer) + const delay = Math.max(5000, (Number(expiresAt) * 1000 - Date.now()) / 2) + live.heartbeatTimer = window.setTimeout(async () => { + try { + const result = await heartbeatLive(live.dockId, live.session.id) + live.session = result.session + scheduleLiveHeartbeat(result.leaseExpiresAt) + if (result.session.phase === 'streaming' && !live.playUrl) await refreshPlayURL() + } catch (e) { + ui.toast(e.message || '直播观看已结束') + await closeLive(false) + } + }, delay) + } + + async function closeLive(sendRequest = true) { + window.clearTimeout(live.heartbeatTimer) + const session = live.session + const dockId = live.dockId + live.session = null + live.dockId = '' + live.playUrl = '' + if (sendRequest && session && dockId) { + try { + await leaveLive(dockId, session.id) + } catch (_) {} + } + } + + return { + live, + livePhaseText, + canFullscreenLive, + toggleLive, + fullscreenLive, + openLive, + closeLive, + } +} diff --git a/src/views/MonitorView/MonitorView.vue b/src/views/MonitorView/MonitorView.vue index 595e608..aee0e79 100644 --- a/src/views/MonitorView/MonitorView.vue +++ b/src/views/MonitorView/MonitorView.vue @@ -90,8 +90,8 @@ import MonitorDeviceList from '@/components/MonitorDeviceList.vue' import DockStatusDialog from '@/components/DockStatusDialog.vue' import CommandProgressFloat from '@/components/CommandProgressFloat.vue' import MonitorMapChrome from '@/components/MonitorMapChrome.vue' -import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live' import { useCommandProgress } from '@/composables/useCommandProgress' +import { useMonitorLive } from '@/composables/useMonitorLive' const DEVICE_SOURCE = 'monitor-devices' @@ -118,8 +118,19 @@ const userStore = useUserStore() const isAdmin = computed(() => userStore.isAdmin()) const selectedId = ref(null) -const live = reactive({ session: null, dockId: '', playUrl: '', heartbeatTimer: null }) const detailPanelRef = ref(null) +const { + live, + livePhaseText, + canFullscreenLive, + toggleLive, + fullscreenLive, + closeLive, +} = useMonitorLive({ + getDockId: () => selectedDock.value?.dockId, + ui, + getLivePlayer: () => detailPanelRef.value?.livePlayerRef, +}) const filter = ref('all') const search = ref('') @@ -689,18 +700,6 @@ const droneMissionState = computed(() => { return record.value.status || '停放待命' }) -const livePhaseText = computed(() => { - if (!live.session) return '待机' - const phase = live.session.phase - if (phase === 'streaming') return live.playUrl.startsWith('fake://') ? '模拟直播' : '直播中' - if (phase === 'starting') return '启动中' - if (phase === 'reconnecting') return '重连中' - if (phase === 'stopping') return '停止中' - if (phase === 'failed') return '启动失败' - return '已停止' -}) -const canFullscreenLive = computed(() => !!live.playUrl && !live.playUrl.startsWith('fake://')) - const detailHeader = computed(() => { const asset = selectedAsset.value if (!asset) return null @@ -791,71 +790,6 @@ function onDetailCommand({ title, desc }) { runCommand(title, desc) } -function toggleLive() { - if (live.session) closeLive() - else openLive() -} - -function fullscreenLive() { - if (!canFullscreenLive.value) { - ui.toast('当前无可全屏播放的视频流') - return - } - const ok = detailPanelRef.value?.livePlayerRef?.requestFullscreen?.() - if (!ok) ui.toast('当前浏览器不支持全屏') -} - -async function openLive() { - const dockId = selectedDock.value?.dockId - if (!dockId) return - try { - const result = await joinLive(dockId) - live.session = result.session - live.dockId = dockId - scheduleLiveHeartbeat(result.leaseExpiresAt) - if (result.session.phase === 'streaming') await refreshPlayURL() - } catch (e) { - ui.toast(e.message || '打开直播失败') - } -} - -async function refreshPlayURL() { - if (!live.dockId) return - const play = await getLivePlayURL(live.dockId) - live.playUrl = play.playUrl || '' -} - -function scheduleLiveHeartbeat(expiresAt) { - window.clearTimeout(live.heartbeatTimer) - const delay = Math.max(5000, (Number(expiresAt) * 1000 - Date.now()) / 2) - live.heartbeatTimer = window.setTimeout(async () => { - try { - const result = await heartbeatLive(live.dockId, live.session.id) - live.session = result.session - scheduleLiveHeartbeat(result.leaseExpiresAt) - if (result.session.phase === 'streaming' && !live.playUrl) await refreshPlayURL() - } catch (e) { - ui.toast(e.message || '直播观看已结束') - await closeLive(false) - } - }, delay) -} - -async function closeLive(sendRequest = true) { - window.clearTimeout(live.heartbeatTimer) - const session = live.session - const dockId = live.dockId - live.session = null - live.dockId = '' - live.playUrl = '' - if (sendRequest && session && dockId) { - try { - await leaveLive(dockId, session.id) - } catch (_) {} - } -} - - function displayWithUnit(value, unit, digits) { if (value == null || value === '') return '--' const num = Number(value)