mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-09-13 02:54:26 +08:00
fix: 优化视频切换流畅性;修复 OTG HID 功能无法一次保存成功和页面未即刻生效问题
This commit is contained in:
@@ -1312,18 +1312,17 @@ async function switchToWebRTC(codec: VideoMode = 'h264') {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Connect WebRTC with retry
|
||||
let retries = 3
|
||||
// Step 3: Connect WebRTC with retry (backoff between retries)
|
||||
const MAX_ATTEMPTS = 3
|
||||
const RETRY_DELAYS = [200, 800]
|
||||
let success = false
|
||||
while (retries > 0 && !success) {
|
||||
success = await connectWebRTCSerial('switchToWebRTC')
|
||||
if (!success) {
|
||||
retries--
|
||||
if (retries > 0) {
|
||||
console.log(`[WebRTC] Connection failed, retrying (${retries} attempts left)`)
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
}
|
||||
for (let attempt = 0; attempt < MAX_ATTEMPTS && !success; attempt++) {
|
||||
if (attempt > 0) {
|
||||
const delay = RETRY_DELAYS[attempt - 1] ?? RETRY_DELAYS[RETRY_DELAYS.length - 1]
|
||||
console.log(`[WebRTC] Connection failed, retrying in ${delay}ms (${MAX_ATTEMPTS - attempt} attempts left)`)
|
||||
await new Promise(resolve => setTimeout(resolve, delay))
|
||||
}
|
||||
success = await connectWebRTCSerial('switchToWebRTC')
|
||||
}
|
||||
if (success) {
|
||||
toast.success(t('console.webrtcConnected'), {
|
||||
@@ -1526,10 +1525,22 @@ watch(() => webrtc.state.value, (newState, oldState) => {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// Handle direct 'failed' state (ICE or DTLS failure)
|
||||
// Allow one automatic retry before marking as failed, consistent with
|
||||
// the disconnected->reconnect path that allows 2 failures.
|
||||
if (newState === 'failed' && videoMode.value !== 'mjpeg') {
|
||||
webrtcReconnectFailures += 1
|
||||
if (webrtcReconnectFailures >= 1) {
|
||||
if (webrtcReconnectFailures >= 2) {
|
||||
markWebRTCFailure(t('console.webrtcFailed'))
|
||||
} else {
|
||||
webrtcReconnectTimeout = setTimeout(async () => {
|
||||
if (videoMode.value !== 'mjpeg' && webrtc.state.value !== 'connected') {
|
||||
const success = await connectWebRTCSerial('auto reconnect after failed')
|
||||
if (!success) {
|
||||
markWebRTCFailure(t('console.webrtcFailed'))
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -2155,6 +2166,10 @@ async function activateConsoleView() {
|
||||
isConsoleActive.value = true
|
||||
registerInteractionListeners()
|
||||
|
||||
// REST snapshot: returning from Settings (or other routes) may have missed WS device_info
|
||||
void systemStore.fetchAllStates()
|
||||
void configStore.refreshHid().then(() => syncMouseModeFromConfig()).catch(() => {})
|
||||
|
||||
// Ensure HID WebSocket is connected when console becomes active
|
||||
if (!hidWs.connected.value) {
|
||||
hidWs.connect().catch(() => {})
|
||||
|
||||
@@ -974,33 +974,29 @@ async function saveConfig() {
|
||||
saved.value = false
|
||||
|
||||
try {
|
||||
// Save only config related to the active section
|
||||
const savePromises: Promise<unknown>[] = []
|
||||
// Save only config related to the active section.
|
||||
// Sequential awaits: backend ConfigStore uses read-modify-write; parallel PATCH
|
||||
// requests could overwrite each other's section (last writer wins on full JSON).
|
||||
|
||||
// Video config (including encoder and WebRTC/STUN/TURN settings)
|
||||
if (activeSection.value === 'video') {
|
||||
savePromises.push(
|
||||
configStore.updateVideo({
|
||||
device: config.value.video_device || undefined,
|
||||
format: config.value.video_format || undefined,
|
||||
width: config.value.video_width,
|
||||
height: config.value.video_height,
|
||||
fps: toConfigFps(config.value.video_fps),
|
||||
})
|
||||
)
|
||||
// Save Stream/Encoder and STUN/TURN config together
|
||||
savePromises.push(
|
||||
configStore.updateStream({
|
||||
encoder: config.value.encoder_backend as any,
|
||||
stun_server: config.value.stun_server || undefined,
|
||||
turn_server: config.value.turn_server || undefined,
|
||||
turn_username: config.value.turn_username || undefined,
|
||||
turn_password: config.value.turn_password || undefined,
|
||||
})
|
||||
)
|
||||
await configStore.updateVideo({
|
||||
device: config.value.video_device || undefined,
|
||||
format: config.value.video_format || undefined,
|
||||
width: config.value.video_width,
|
||||
height: config.value.video_height,
|
||||
fps: toConfigFps(config.value.video_fps),
|
||||
})
|
||||
await configStore.updateStream({
|
||||
encoder: config.value.encoder_backend as any,
|
||||
stun_server: config.value.stun_server || undefined,
|
||||
turn_server: config.value.turn_server || undefined,
|
||||
turn_username: config.value.turn_username || undefined,
|
||||
turn_password: config.value.turn_password || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
// HID config
|
||||
// HID config (includes MSD enable — same gadget; must not race with updateHid)
|
||||
if (activeSection.value === 'hid') {
|
||||
if (!isHidFunctionSelectionValid.value || !isOtgEndpointBudgetValid.value) {
|
||||
return
|
||||
@@ -1024,24 +1020,20 @@ async function saveConfig() {
|
||||
hidUpdate.otg_functions = { ...config.value.hid_otg_functions }
|
||||
hidUpdate.otg_keyboard_leds = config.value.hid_otg_keyboard_leds
|
||||
}
|
||||
savePromises.push(configStore.updateHid(hidUpdate))
|
||||
savePromises.push(
|
||||
configStore.updateMsd({
|
||||
enabled: config.value.msd_enabled,
|
||||
})
|
||||
)
|
||||
await configStore.updateHid(hidUpdate)
|
||||
await configStore.updateMsd({
|
||||
enabled: config.value.msd_enabled,
|
||||
})
|
||||
}
|
||||
|
||||
// MSD config
|
||||
if (activeSection.value === 'msd') {
|
||||
savePromises.push(
|
||||
configStore.updateMsd({
|
||||
msd_dir: config.value.msd_dir || undefined,
|
||||
})
|
||||
)
|
||||
await configStore.updateMsd({
|
||||
msd_dir: config.value.msd_dir || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
await Promise.all(savePromises)
|
||||
await loadConfig()
|
||||
saved.value = true
|
||||
setTimeout(() => (saved.value = false), 2000)
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user