mirror of https://github.com/jetkvm/kvm.git
Cleanup: remove polling fallback for /audio/mute status
This commit is contained in:
parent
97bcb3c1ea
commit
32055f5762
|
@ -2,7 +2,7 @@ import { MdOutlineContentPasteGo, MdVolumeOff, MdVolumeUp, MdGraphicEq } from "r
|
|||
import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu";
|
||||
import { FaKeyboard } from "react-icons/fa6";
|
||||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||
import { Fragment, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Fragment, useCallback, useRef } from "react";
|
||||
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
|
@ -21,7 +21,7 @@ import ExtensionPopover from "@/components/popovers/ExtensionPopover";
|
|||
import AudioControlPopover from "@/components/popovers/AudioControlPopover";
|
||||
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
|
||||
import { useAudioEvents } from "@/hooks/useAudioEvents";
|
||||
import api from "@/api";
|
||||
|
||||
|
||||
// Type for microphone error
|
||||
interface MicrophoneError {
|
||||
|
@ -83,35 +83,10 @@ export default function Actionbar({
|
|||
);
|
||||
|
||||
// Use WebSocket-based audio events for real-time updates
|
||||
const { audioMuted, isConnected } = useAudioEvents();
|
||||
const { audioMuted } = useAudioEvents();
|
||||
|
||||
// Fallback to polling if WebSocket is not connected
|
||||
const [fallbackMuted, setFallbackMuted] = useState(false);
|
||||
useEffect(() => {
|
||||
if (!isConnected) {
|
||||
// Load initial state
|
||||
api.GET("/audio/mute").then(async resp => {
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
setFallbackMuted(!!data.muted);
|
||||
}
|
||||
});
|
||||
|
||||
// Fallback polling when WebSocket is not available
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.GET("/audio/mute");
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
setFallbackMuted(!!data.muted);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [isConnected]);
|
||||
|
||||
// Use WebSocket data when available, fallback to polling data otherwise
|
||||
const isMuted = isConnected && audioMuted !== null ? audioMuted : fallbackMuted;
|
||||
// Use WebSocket data exclusively - no polling fallback
|
||||
const isMuted = audioMuted ?? false; // Default to false if WebSocket data not available yet
|
||||
|
||||
return (
|
||||
<Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
|
||||
|
|
|
@ -41,23 +41,7 @@ interface AudioConfig {
|
|||
FrameSize: string;
|
||||
}
|
||||
|
||||
interface AudioMetrics {
|
||||
frames_received: number;
|
||||
frames_dropped: number;
|
||||
bytes_processed: number;
|
||||
last_frame_time: string;
|
||||
connection_drops: number;
|
||||
average_latency: string;
|
||||
}
|
||||
|
||||
interface MicrophoneMetrics {
|
||||
frames_sent: number;
|
||||
frames_dropped: number;
|
||||
bytes_processed: number;
|
||||
last_frame_time: string;
|
||||
connection_drops: number;
|
||||
average_latency: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -94,11 +78,7 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo
|
|||
isConnected: wsConnected
|
||||
} = useAudioEvents();
|
||||
|
||||
// Fallback state for when WebSocket is not connected
|
||||
const [fallbackMuted, setFallbackMuted] = useState(false);
|
||||
const [fallbackMetrics, setFallbackMetrics] = useState<AudioMetrics | null>(null);
|
||||
const [fallbackMicMetrics, setFallbackMicMetrics] = useState<MicrophoneMetrics | null>(null);
|
||||
const [fallbackConnected, setFallbackConnected] = useState(false);
|
||||
// WebSocket-only implementation - no fallback polling
|
||||
|
||||
// Microphone state from props
|
||||
const {
|
||||
|
@ -115,11 +95,11 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo
|
|||
isToggling,
|
||||
} = microphone;
|
||||
|
||||
// Use WebSocket data when available, fallback to polling data otherwise
|
||||
const isMuted = wsConnected && audioMuted !== null ? audioMuted : fallbackMuted;
|
||||
const metrics = wsConnected && audioMetrics !== null ? audioMetrics : fallbackMetrics;
|
||||
const micMetrics = wsConnected && microphoneMetrics !== null ? microphoneMetrics : fallbackMicMetrics;
|
||||
const isConnected = wsConnected ? wsConnected : fallbackConnected;
|
||||
// Use WebSocket data exclusively - no polling fallback
|
||||
const isMuted = audioMuted ?? false;
|
||||
const metrics = audioMetrics;
|
||||
const micMetrics = microphoneMetrics;
|
||||
const isConnected = wsConnected;
|
||||
|
||||
// Audio level monitoring - enable only when popover is open and microphone is active to save resources
|
||||
const analysisEnabled = (open ?? true) && isMicrophoneActive;
|
||||
|
@ -150,34 +130,15 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo
|
|||
}
|
||||
}, [configsLoaded]);
|
||||
|
||||
// Optimize fallback polling - only run when WebSocket is not connected
|
||||
// WebSocket-only implementation - sync microphone state when needed
|
||||
useEffect(() => {
|
||||
if (!wsConnected && !configsLoaded) {
|
||||
// Load state once if configs aren't loaded yet
|
||||
loadAudioState();
|
||||
}
|
||||
|
||||
if (!wsConnected) {
|
||||
loadAudioMetrics();
|
||||
loadMicrophoneMetrics();
|
||||
|
||||
// Reduced frequency for fallback polling (every 3 seconds instead of 2)
|
||||
const metricsInterval = setInterval(() => {
|
||||
if (!wsConnected) { // Double-check to prevent unnecessary requests
|
||||
loadAudioMetrics();
|
||||
loadMicrophoneMetrics();
|
||||
}
|
||||
}, 3000);
|
||||
return () => clearInterval(metricsInterval);
|
||||
}
|
||||
|
||||
// Always sync microphone state, but debounce it
|
||||
const syncTimeout = setTimeout(() => {
|
||||
syncMicrophoneState();
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(syncTimeout);
|
||||
}, [wsConnected, syncMicrophoneState, configsLoaded]);
|
||||
}, [syncMicrophoneState]);
|
||||
|
||||
const loadAudioConfigurations = async () => {
|
||||
try {
|
||||
|
@ -203,60 +164,14 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo
|
|||
}
|
||||
};
|
||||
|
||||
const loadAudioState = async () => {
|
||||
try {
|
||||
// Load mute state only (configurations are loaded separately)
|
||||
const muteResp = await api.GET("/audio/mute");
|
||||
if (muteResp.ok) {
|
||||
const muteData = await muteResp.json();
|
||||
setFallbackMuted(!!muteData.muted);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load audio state:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadAudioMetrics = async () => {
|
||||
try {
|
||||
const resp = await api.GET("/audio/metrics");
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
setFallbackMetrics(data);
|
||||
// Consider connected if API call succeeds, regardless of frame count
|
||||
setFallbackConnected(true);
|
||||
} else {
|
||||
setFallbackConnected(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load audio metrics:", error);
|
||||
setFallbackConnected(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const loadMicrophoneMetrics = async () => {
|
||||
try {
|
||||
const resp = await api.GET("/microphone/metrics");
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
setFallbackMicMetrics(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load microphone metrics:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleMute = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const resp = await api.POST("/audio/mute", { muted: !isMuted });
|
||||
if (resp.ok) {
|
||||
// WebSocket will handle the state update, but update fallback for immediate feedback
|
||||
if (!wsConnected) {
|
||||
setFallbackMuted(!isMuted);
|
||||
}
|
||||
if (!resp.ok) {
|
||||
console.error("Failed to toggle mute:", resp.statusText);
|
||||
}
|
||||
// WebSocket will handle the state update automatically
|
||||
} catch (error) {
|
||||
console.error("Failed to toggle mute:", error);
|
||||
} finally {
|
||||
|
|
4
web.go
4
web.go
|
@ -159,10 +159,6 @@ func setupRouter() *gin.Engine {
|
|||
protected.POST("/storage/upload", handleUploadHttp)
|
||||
}
|
||||
|
||||
protected.GET("/audio/mute", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"muted": audio.IsAudioMuted()})
|
||||
})
|
||||
|
||||
protected.POST("/audio/mute", func(c *gin.Context) {
|
||||
type muteReq struct {
|
||||
Muted bool `json:"muted"`
|
||||
|
|
Loading…
Reference in New Issue