mirror of https://github.com/jetkvm/kvm.git
Compare commits
3 Commits
b165137f79
...
f91620170d
| Author | SHA1 | Date |
|---|---|---|
|
|
f91620170d | |
|
|
6e1b84f39b | |
|
|
6078cdab66 |
|
|
@ -601,39 +601,58 @@ export default function KvmIdRoute() {
|
||||||
}
|
}
|
||||||
}, [peerConnectionState, cleanupAndStopReconnecting]);
|
}, [peerConnectionState, cleanupAndStopReconnecting]);
|
||||||
|
|
||||||
|
const microphoneRequestInProgress = useRef(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!audioTransceiver || !peerConnection) return;
|
if (!audioTransceiver || !peerConnection) return;
|
||||||
|
|
||||||
if (microphoneEnabled) {
|
if (microphoneEnabled) {
|
||||||
navigator.mediaDevices?.getUserMedia({
|
if (microphoneRequestInProgress.current) return;
|
||||||
audio: {
|
|
||||||
echoCancellation: true,
|
const currentTrack = audioTransceiver.sender.track;
|
||||||
noiseSuppression: true,
|
if (currentTrack) {
|
||||||
autoGainControl: true,
|
currentTrack.stop();
|
||||||
channelCount: 2,
|
}
|
||||||
}
|
|
||||||
}).then((stream) => {
|
const requestMicrophone = () => {
|
||||||
const audioTrack = stream.getAudioTracks()[0];
|
microphoneRequestInProgress.current = true;
|
||||||
if (audioTrack && audioTransceiver.sender) {
|
navigator.mediaDevices?.getUserMedia({
|
||||||
audioTransceiver.sender.replaceTrack(audioTrack);
|
audio: {
|
||||||
}
|
echoCancellation: true,
|
||||||
}).catch(() => {
|
noiseSuppression: true,
|
||||||
setMicrophoneEnabled(false);
|
autoGainControl: true,
|
||||||
});
|
channelCount: 2,
|
||||||
|
}
|
||||||
|
}).then((stream) => {
|
||||||
|
microphoneRequestInProgress.current = false;
|
||||||
|
const audioTrack = stream.getAudioTracks()[0];
|
||||||
|
if (audioTrack && audioTransceiver.sender) {
|
||||||
|
const handleTrackEnded = () => {
|
||||||
|
console.warn('Microphone track ended unexpectedly, attempting to restart...');
|
||||||
|
if (audioTransceiver.sender.track === audioTrack) {
|
||||||
|
audioTransceiver.sender.replaceTrack(null);
|
||||||
|
setTimeout(requestMicrophone, 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
audioTrack.addEventListener('ended', handleTrackEnded, { once: true });
|
||||||
|
audioTransceiver.sender.replaceTrack(audioTrack);
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
microphoneRequestInProgress.current = false;
|
||||||
|
console.error('Failed to get microphone:', err);
|
||||||
|
setMicrophoneEnabled(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
requestMicrophone();
|
||||||
} else {
|
} else {
|
||||||
|
microphoneRequestInProgress.current = false;
|
||||||
if (audioTransceiver.sender.track) {
|
if (audioTransceiver.sender.track) {
|
||||||
audioTransceiver.sender.track.stop();
|
audioTransceiver.sender.track.stop();
|
||||||
audioTransceiver.sender.replaceTrack(null);
|
audioTransceiver.sender.replaceTrack(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [microphoneEnabled, audioTransceiver, peerConnection]);
|
}, [microphoneEnabled, audioTransceiver, peerConnection, setMicrophoneEnabled]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!audioTransceiver || !peerConnection || !audioInputAutoEnable || microphoneEnabled) return;
|
|
||||||
if (isSecureContext()) {
|
|
||||||
setMicrophoneEnabled(true);
|
|
||||||
}
|
|
||||||
}, [audioInputAutoEnable, audioTransceiver, peerConnection, microphoneEnabled]);
|
|
||||||
|
|
||||||
// Cleanup effect
|
// Cleanup effect
|
||||||
const { clearInboundRtpStats, clearCandidatePairStats } = useRTCStore();
|
const { clearInboundRtpStats, clearCandidatePairStats } = useRTCStore();
|
||||||
|
|
@ -806,15 +825,46 @@ export default function KvmIdRoute() {
|
||||||
});
|
});
|
||||||
}, [rpcDataChannel?.readyState, send, setHdmiState]);
|
}, [rpcDataChannel?.readyState, send, setHdmiState]);
|
||||||
|
|
||||||
// Load audio input auto-enable preference from backend
|
const [audioInputAutoEnableLoaded, setAudioInputAutoEnableLoaded] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
send("getAudioInputAutoEnable", {}, (resp: JsonRpcResponse) => {
|
send("getAudioInputAutoEnable", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
setAudioInputAutoEnable(resp.result as boolean);
|
setAudioInputAutoEnable(resp.result as boolean);
|
||||||
|
setAudioInputAutoEnableLoaded(true);
|
||||||
});
|
});
|
||||||
}, [rpcDataChannel?.readyState, send, setAudioInputAutoEnable]);
|
}, [rpcDataChannel?.readyState, send, setAudioInputAutoEnable]);
|
||||||
|
|
||||||
|
const autoEnableAppliedRef = useRef(false);
|
||||||
|
const audioInputAutoEnableValueRef = useRef(audioInputAutoEnable);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
audioInputAutoEnableValueRef.current = audioInputAutoEnable;
|
||||||
|
}, [audioInputAutoEnable]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!audioTransceiver || !peerConnection || microphoneEnabled) return;
|
||||||
|
if (!audioInputAutoEnableLoaded || autoEnableAppliedRef.current) return;
|
||||||
|
|
||||||
|
if (audioInputAutoEnableValueRef.current && isSecureContext()) {
|
||||||
|
autoEnableAppliedRef.current = true;
|
||||||
|
send("setAudioInputEnabled", { enabled: true }, (resp: JsonRpcResponse) => {
|
||||||
|
if ("error" in resp) {
|
||||||
|
console.error("Failed to auto-enable audio input:", resp.error);
|
||||||
|
} else {
|
||||||
|
setMicrophoneEnabled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [audioTransceiver, peerConnection, audioInputAutoEnableLoaded, microphoneEnabled, setMicrophoneEnabled, send]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!peerConnection) {
|
||||||
|
autoEnableAppliedRef.current = false;
|
||||||
|
setAudioInputAutoEnableLoaded(false);
|
||||||
|
}
|
||||||
|
}, [peerConnection]);
|
||||||
|
|
||||||
const [needLedState, setNeedLedState] = useState(true);
|
const [needLedState, setNeedLedState] = useState(true);
|
||||||
|
|
||||||
// request keyboard led state from the device
|
// request keyboard led state from the device
|
||||||
|
|
|
||||||
4
web.go
4
web.go
|
|
@ -814,7 +814,7 @@ func handleSendWOLMagicPacket(c *gin.Context) {
|
||||||
inputMacAddr := c.Param("mac-addr")
|
inputMacAddr := c.Param("mac-addr")
|
||||||
macAddr, err := net.ParseMAC(inputMacAddr)
|
macAddr, err := net.ParseMAC(inputMacAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn().Err(err).Str("sendWol", inputMacAddr).Msg("Invalid mac address provided")
|
logger.Warn().Err(err).Str("inputMacAddr", inputMacAddr).Msg("Invalid MAC address provided")
|
||||||
c.String(http.StatusBadRequest, "Invalid mac address provided")
|
c.String(http.StatusBadRequest, "Invalid mac address provided")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -822,7 +822,7 @@ func handleSendWOLMagicPacket(c *gin.Context) {
|
||||||
macAddrString := macAddr.String()
|
macAddrString := macAddr.String()
|
||||||
err = rpcSendWOLMagicPacket(macAddrString)
|
err = rpcSendWOLMagicPacket(macAddrString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn().Err(err).Str("sendWOL", macAddrString).Msg("Failed to send WOL magic packet")
|
logger.Warn().Err(err).Str("macAddrString", macAddrString).Msg("Failed to send WOL magic packet")
|
||||||
c.String(http.StatusInternalServerError, "Failed to send WOL to %s: %v", macAddrString, err)
|
c.String(http.StatusInternalServerError, "Failed to send WOL to %s: %v", macAddrString, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue