mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-07-29 06:31:45 +08:00
del: MJPEG /HTTTP 视频模式删除网页连接统计功能
This commit is contained in:
@@ -66,6 +66,7 @@ const props = defineProps<{
|
||||
showTerminal?: boolean
|
||||
showComputerUse?: boolean
|
||||
}>()
|
||||
const showStats = computed(() => (props.videoMode ?? 'mjpeg') !== 'mjpeg')
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggleFullscreen'): void
|
||||
@@ -196,6 +197,7 @@ const RIGHT_FIXED_PX = 120
|
||||
const collapsibleItems = computed(() => {
|
||||
const items = ITEM_SPECS.slice(3).filter(item => {
|
||||
if (item.id === 'msd' && !showMsd.value) return false
|
||||
if (item.id === 'stats' && !showStats.value) return false
|
||||
if (item.id === 'terminal' && props.showTerminal === false) return false
|
||||
return true
|
||||
})
|
||||
@@ -244,6 +246,12 @@ const isVisible = (id: CollapsibleItem) => visibleSet.value.has(id)
|
||||
const hasOverflow = computed(() => {
|
||||
return collapsibleItems.value.some(i => !visibleSet.value.has(i.id))
|
||||
})
|
||||
const hasLeftOverflow = computed(() => {
|
||||
return collapsibleItems.value.some(i => i.side === 'left' && !visibleSet.value.has(i.id))
|
||||
})
|
||||
const hasRightOverflow = computed(() => {
|
||||
return collapsibleItems.value.some(i => i.side === 'right' && !visibleSet.value.has(i.id))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -469,10 +477,10 @@ const hasOverflow = computed(() => {
|
||||
{{ t('actionbar.paste') }}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator v-if="(!isVisible('msd') || !isVisible('atx') || !isVisible('paste')) && (!isVisible('stats') || (props.showTerminal !== false && !isVisible('terminal')) || !isVisible('settings'))" />
|
||||
<DropdownMenuSeparator v-if="hasLeftOverflow && hasRightOverflow" />
|
||||
|
||||
<!-- Stats -->
|
||||
<DropdownMenuItem v-if="!isVisible('stats')" @click="openFromOverflow(() => emit('toggleStats'))">
|
||||
<DropdownMenuItem v-if="showStats && !isVisible('stats')" @click="openFromOverflow(() => emit('toggleStats'))">
|
||||
<BarChart3 class="h-4 w-4 mr-2" />
|
||||
{{ t('actionbar.stats') }}
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -17,11 +17,6 @@ const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
videoMode: 'mjpeg' | 'h264' | 'h265' | 'vp8' | 'vp9'
|
||||
// MJPEG stats
|
||||
mjpegFps?: number
|
||||
wsLatency?: number
|
||||
// WebRTC stats
|
||||
webrtcStats?: WebRTCStats
|
||||
}>()
|
||||
|
||||
@@ -51,9 +46,6 @@ let lastBytesReceived = 0
|
||||
let lastPacketsLost = 0
|
||||
let lastTimestamp = 0
|
||||
|
||||
// Is WebRTC mode
|
||||
const isWebRTC = computed(() => props.videoMode !== 'mjpeg')
|
||||
|
||||
function formatTime(ts: number): string {
|
||||
const date = new Date(ts * 1000)
|
||||
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||
@@ -256,14 +248,13 @@ function addDataPoint() {
|
||||
timestamps.value.shift()
|
||||
}
|
||||
|
||||
if (isWebRTC.value && props.webrtcStats) {
|
||||
if (props.webrtcStats) {
|
||||
const jitter = (props.webrtcStats.jitter || 0) * 1000
|
||||
jitterHistory.value.push(jitter)
|
||||
|
||||
const rtt = (props.webrtcStats.roundTripTime || 0) * 1000
|
||||
delayHistory.value.push(rtt)
|
||||
|
||||
// Packet loss delta
|
||||
const currentLost = props.webrtcStats.packetsLost || 0
|
||||
const lostDelta = lastPacketsLost > 0 ? Math.max(0, currentLost - lastPacketsLost) : 0
|
||||
lastPacketsLost = currentLost
|
||||
@@ -284,11 +275,10 @@ function addDataPoint() {
|
||||
lastBytesReceived = currentBytes
|
||||
lastTimestamp = currentTime
|
||||
} else {
|
||||
// MJPEG mode
|
||||
jitterHistory.value.push(0)
|
||||
delayHistory.value.push(props.wsLatency || 0)
|
||||
delayHistory.value.push(0)
|
||||
packetLossHistory.value.push(0)
|
||||
fpsHistory.value.push(props.mjpegFps || 0)
|
||||
fpsHistory.value.push(0)
|
||||
bitrateHistory.value.push(0)
|
||||
}
|
||||
|
||||
@@ -335,7 +325,7 @@ function formatCandidateType(type: string): string {
|
||||
|
||||
// Current stats for header display
|
||||
const currentStats = computed(() => {
|
||||
if (isWebRTC.value && props.webrtcStats) {
|
||||
if (props.webrtcStats) {
|
||||
const lastBitrate = bitrateHistory.value[bitrateHistory.value.length - 1]
|
||||
const bitrate = lastBitrate !== undefined ? lastBitrate : 0
|
||||
return {
|
||||
@@ -356,8 +346,8 @@ const currentStats = computed(() => {
|
||||
}
|
||||
return {
|
||||
jitter: 0,
|
||||
delay: props.wsLatency || 0,
|
||||
fps: props.mjpegFps || 0,
|
||||
delay: 0,
|
||||
fps: 0,
|
||||
resolution: '-',
|
||||
bitrate: '0',
|
||||
packetsLost: 0,
|
||||
@@ -422,7 +412,7 @@ onUnmounted(() => {
|
||||
<div class="flex items-center gap-2">
|
||||
<SheetTitle class="text-base">{{ t('stats.title') }}</SheetTitle>
|
||||
<span class="text-xs px-2 py-0.5 rounded bg-slate-100 dark:bg-slate-800 text-muted-foreground">
|
||||
{{ isWebRTC ? 'WebRTC' : 'MJPEG' }}
|
||||
WebRTC
|
||||
</span>
|
||||
</div>
|
||||
</SheetHeader>
|
||||
@@ -438,7 +428,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- Network Stability (Jitter) -->
|
||||
<div class="space-y-2" v-if="isWebRTC">
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<h4 class="text-sm font-medium">{{ t('stats.stability') }}</h4>
|
||||
</div>
|
||||
@@ -462,7 +452,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- Playback Delay -->
|
||||
<div class="space-y-2" v-if="isWebRTC">
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<h4 class="text-sm font-medium">{{ t('stats.delay') }}</h4>
|
||||
<span class="text-xs text-muted-foreground">
|
||||
@@ -489,7 +479,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- Packet Loss -->
|
||||
<div class="space-y-2" v-if="isWebRTC">
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<h4 class="text-sm font-medium">{{ t('stats.packetLoss') }}</h4>
|
||||
<span class="text-xs text-muted-foreground">
|
||||
@@ -543,7 +533,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- Additional Stats -->
|
||||
<div class="space-y-3 pt-2 border-t border-slate-200 dark:border-slate-800" v-if="isWebRTC">
|
||||
<div class="space-y-3 pt-2 border-t border-slate-200 dark:border-slate-800">
|
||||
<h4 class="text-sm font-medium">{{ t('stats.additional') }}</h4>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="rounded-lg bg-slate-50 dark:bg-slate-900/50 p-3">
|
||||
|
||||
@@ -1101,8 +1101,6 @@ export default {
|
||||
},
|
||||
stats: {
|
||||
title: 'Connection Stats',
|
||||
webrtcMode: 'WebRTC Real-time Stats',
|
||||
mjpegMode: 'MJPEG Real-time Stats',
|
||||
current: 'Current Status',
|
||||
video: 'Video',
|
||||
videoDesc: 'Video stream from server to client.',
|
||||
@@ -1122,7 +1120,6 @@ export default {
|
||||
fps: 'FPS',
|
||||
resolution: 'Resolution',
|
||||
packetsLost: 'Packets Lost',
|
||||
wsLatency: 'WS Latency',
|
||||
connection: 'Connection Info',
|
||||
connectionType: 'Connection Type',
|
||||
transport: 'Transport',
|
||||
|
||||
@@ -1100,8 +1100,6 @@ export default {
|
||||
},
|
||||
stats: {
|
||||
title: '连接统计',
|
||||
webrtcMode: 'WebRTC 实时统计',
|
||||
mjpegMode: 'MJPEG 实时统计',
|
||||
current: '当前状态',
|
||||
video: '视频',
|
||||
videoDesc: '从服务器到客户端的视频流。',
|
||||
@@ -1121,7 +1119,6 @@ export default {
|
||||
fps: '帧率',
|
||||
resolution: '分辨率',
|
||||
packetsLost: '丢包数',
|
||||
wsLatency: 'WS 延迟',
|
||||
connection: '连接信息',
|
||||
connectionType: '连接类型',
|
||||
transport: '传输协议',
|
||||
|
||||
@@ -171,6 +171,7 @@ function syncMouseModeFromConfig() {
|
||||
const virtualKeyboardVisible = ref(false)
|
||||
const virtualKeyboardAttached = ref(true)
|
||||
const statsSheetOpen = ref(false)
|
||||
const showConnectionStats = computed(() => videoMode.value !== 'mjpeg')
|
||||
const virtualKeyboardConsumerEnabled = computed(() => {
|
||||
const hid = configStore.hid
|
||||
if (!hid) return true
|
||||
@@ -207,6 +208,18 @@ const videoStatus = computed<'connected' | 'connecting' | 'disconnected' | 'erro
|
||||
return 'disconnected'
|
||||
})
|
||||
|
||||
function openStatsSheet() {
|
||||
if (showConnectionStats.value) {
|
||||
statsSheetOpen.value = true
|
||||
}
|
||||
}
|
||||
|
||||
watch(showConnectionStats, (canShow) => {
|
||||
if (!canShow) {
|
||||
statsSheetOpen.value = false
|
||||
}
|
||||
})
|
||||
|
||||
function getResolutionShortName(width: number, height: number): string {
|
||||
if (height === 2160 || (height === 2160 && width === 4096)) return '4K'
|
||||
if (height === 1440) return '2K'
|
||||
@@ -2835,7 +2848,7 @@ onUnmounted(() => {
|
||||
:show-terminal="showTerminal"
|
||||
:show-computer-use="showComputerUse"
|
||||
@toggle-fullscreen="toggleFullscreen"
|
||||
@toggle-stats="statsSheetOpen = true"
|
||||
@toggle-stats="openStatsSheet"
|
||||
@toggle-virtual-keyboard="handleToggleVirtualKeyboard"
|
||||
@toggle-mouse-mode="handleToggleMouseMode"
|
||||
@update:video-mode="handleVideoModeChange"
|
||||
@@ -3086,10 +3099,8 @@ onUnmounted(() => {
|
||||
:debug-mode="false"
|
||||
/>
|
||||
<StatsSheet
|
||||
v-if="showConnectionStats"
|
||||
v-model:open="statsSheetOpen"
|
||||
:video-mode="videoMode"
|
||||
:mjpeg-fps="backendFps"
|
||||
:ws-latency="0"
|
||||
:webrtc-stats="webrtc.stats.value"
|
||||
/>
|
||||
<Dialog v-if="showTerminal" v-model:open="showTerminalDialog">
|
||||
|
||||
Reference in New Issue
Block a user