fix: recursive polling, candiate video reconnecting
- made all polling to 6s - fixed candidate video being reconnected in interval
This commit is contained in:
parent
78ed98bc43
commit
ab00516c42
@ -663,11 +663,11 @@ public function getPollData($id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up stale active_peers (> 15 seconds)
|
// Clean up stale active_peers (> 30 seconds)
|
||||||
$activePeers = $interview->active_peers ?? [];
|
$activePeers = $interview->active_peers ?? [];
|
||||||
$now = now()->timestamp;
|
$now = now()->timestamp;
|
||||||
$validPeers = array_values(array_filter($activePeers, function ($p) use ($now) {
|
$validPeers = array_values(array_filter($activePeers, function ($p) use ($now) {
|
||||||
return ($now - ($p['last_seen'] ?? 0)) < 15;
|
return ($now - ($p['last_seen'] ?? 0)) < 30;
|
||||||
}));
|
}));
|
||||||
if (count($validPeers) !== count($activePeers)) {
|
if (count($validPeers) !== count($activePeers)) {
|
||||||
$interview->update(['active_peers' => $validPeers]);
|
$interview->update(['active_peers' => $validPeers]);
|
||||||
@ -726,12 +726,13 @@ public function registerPeerHeartbeat(Request $request, $id)
|
|||||||
$role = $request->input('role', $user ? ($user->is_admin ? 'admin' : 'interviewer') : 'candidate');
|
$role = $request->input('role', $user ? ($user->is_admin ? 'admin' : 'interviewer') : 'candidate');
|
||||||
$userId = $user ? $user->id : 0;
|
$userId = $user ? $user->id : 0;
|
||||||
|
|
||||||
|
$interview->refresh();
|
||||||
$activePeers = $interview->active_peers ?? [];
|
$activePeers = $interview->active_peers ?? [];
|
||||||
$now = now()->timestamp;
|
$now = now()->timestamp;
|
||||||
|
|
||||||
$updatedPeers = [];
|
$updatedPeers = [];
|
||||||
foreach ($activePeers as $p) {
|
foreach ($activePeers as $p) {
|
||||||
if (($now - ($p['last_seen'] ?? 0)) < 15) {
|
if (($now - ($p['last_seen'] ?? 0)) < 30) {
|
||||||
if ($action === 'leave' && ($p['peer_id'] ?? '') === $peerId) {
|
if ($action === 'leave' && ($p['peer_id'] ?? '') === $peerId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1135,7 +1135,7 @@ class CandidateProctoring {
|
|||||||
const objectDetector = await this.withAmdLoaderDisabled(() =>
|
const objectDetector = await this.withAmdLoaderDisabled(() =>
|
||||||
this.createModel(vision.ObjectDetector, fileset, {
|
this.createModel(vision.ObjectDetector, fileset, {
|
||||||
modelAssetPath: OBJECT_MODEL_URL,
|
modelAssetPath: OBJECT_MODEL_URL,
|
||||||
runningMode: 'VIDEO', maxResults: 10, scoreThreshold: 0.15,
|
runningMode: 'VIDEO', maxResults: 10, scoreThreshold: 0.35,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.phoneDetector = new PhoneDetector(objectDetector);
|
this.phoneDetector = new PhoneDetector(objectDetector);
|
||||||
|
|||||||
@ -271,26 +271,12 @@ export function saveCanvasDrawing(force = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Candidate Chat Polling & Sending ---
|
// --- Candidate Chat Polling & Sending ---
|
||||||
export function pollCandidateChat() {
|
export function pollCandidateChat(data) {
|
||||||
const interviewId = document.body.dataset.interviewId;
|
|
||||||
if (!interviewId) return;
|
|
||||||
|
|
||||||
fetch(`/candidate/poll/${interviewId}`, {
|
|
||||||
headers: { 'Accept': 'application/json' }
|
|
||||||
})
|
|
||||||
.then(r => {
|
|
||||||
if (!r.ok) return null;
|
|
||||||
const contentType = r.headers.get('content-type');
|
|
||||||
if (!contentType || !contentType.includes('application/json')) return null;
|
|
||||||
return r.json();
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
const chatHistory = document.getElementById('candidate-chat-history');
|
const chatHistory = document.getElementById('candidate-chat-history');
|
||||||
if (!chatHistory) return;
|
if (!chatHistory) return;
|
||||||
|
|
||||||
if (data.status === 'blocked' || data.is_expired) {
|
if (data.status === 'blocked' || data.is_expired) {
|
||||||
window.location.reload();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,8 +304,6 @@ export function pollCandidateChat() {
|
|||||||
chatHistory.innerHTML = html;
|
chatHistory.innerHTML = html;
|
||||||
chatHistory.scrollTop = chatHistory.scrollHeight;
|
chatHistory.scrollTop = chatHistory.scrollHeight;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendCandidateMessage() {
|
export function sendCandidateMessage() {
|
||||||
@ -345,7 +329,13 @@ export function sendCandidateMessage() {
|
|||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
pollCandidateChat();
|
fetch(`/candidate/poll/${interviewId}`, {
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data) pollCandidateChat(data);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,10 +962,6 @@ export function initCandidateRoom() {
|
|||||||
// Start Candidate Camera
|
// Start Candidate Camera
|
||||||
startCandidateCameraStream();
|
startCandidateCameraStream();
|
||||||
|
|
||||||
// Chat polling
|
|
||||||
setInterval(pollCandidateChat, 600);
|
|
||||||
pollCandidateChat();
|
|
||||||
|
|
||||||
// Heartbeat & Polling
|
// Heartbeat & Polling
|
||||||
if (candidateHeartbeatIntervalTimer) clearInterval(candidateHeartbeatIntervalTimer);
|
if (candidateHeartbeatIntervalTimer) clearInterval(candidateHeartbeatIntervalTimer);
|
||||||
sendCandidatePeerHeartbeat();
|
sendCandidatePeerHeartbeat();
|
||||||
@ -985,7 +971,6 @@ export function initCandidateRoom() {
|
|||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (!interviewId) return;
|
if (!interviewId) return;
|
||||||
sendCandidatePeerHeartbeat('heartbeat');
|
|
||||||
|
|
||||||
fetch('/candidate/poll/' + interviewId, {
|
fetch('/candidate/poll/' + interviewId, {
|
||||||
headers: { 'Accept': 'application/json' }
|
headers: { 'Accept': 'application/json' }
|
||||||
@ -994,6 +979,8 @@ export function initCandidateRoom() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
|
pollCandidateChat(data);
|
||||||
|
|
||||||
if (data.active_peers && Array.isArray(data.active_peers)) {
|
if (data.active_peers && Array.isArray(data.active_peers)) {
|
||||||
const activePeerIds = new Set(data.active_peers.map(p => p.peer_id));
|
const activePeerIds = new Set(data.active_peers.map(p => p.peer_id));
|
||||||
|
|
||||||
|
|||||||
@ -606,7 +606,7 @@ export function updateCandidateStatusIcons(isMicOn, isCamOn, isJoined = true) {
|
|||||||
const screenWrapper = document.getElementById('screen-wrapper');
|
const screenWrapper = document.getElementById('screen-wrapper');
|
||||||
const joinedPill = document.getElementById('candidate-joined-pill');
|
const joinedPill = document.getElementById('candidate-joined-pill');
|
||||||
|
|
||||||
if (!isJoined) {
|
if (!isJoined && !candidateStreamConnected) {
|
||||||
if (joinedPill) {
|
if (joinedPill) {
|
||||||
joinedPill.className = 'pill gray inline-flex items-center gap-1.5 font-semibold text-[11px] px-2.5 py-0.5 rounded-full border border-slate-700 text-slate-400 bg-slate-800/60';
|
joinedPill.className = 'pill gray inline-flex items-center gap-1.5 font-semibold text-[11px] px-2.5 py-0.5 rounded-full border border-slate-700 text-slate-400 bg-slate-800/60';
|
||||||
joinedPill.innerHTML = '<i class="fa-solid fa-circle text-[8px] text-slate-500"></i> Not Joined';
|
joinedPill.innerHTML = '<i class="fa-solid fa-circle text-[8px] text-slate-500"></i> Not Joined';
|
||||||
@ -692,13 +692,18 @@ export function pollReviewData() {
|
|||||||
const currentUserId = window.currentUserId || 0;
|
const currentUserId = window.currentUserId || 0;
|
||||||
const myPeerId = 'interviewer_' + currentInterviewId + '_' + currentUserId;
|
const myPeerId = 'interviewer_' + currentInterviewId + '_' + currentUserId;
|
||||||
|
|
||||||
|
const candVid = document.getElementById('interviewer-cand-video');
|
||||||
|
const candidateStreamConnected = candVid && candVid.srcObject && (candVid.srcObject.active || (candVid.srcObject.getTracks && candVid.srcObject.getTracks().length > 0));
|
||||||
|
|
||||||
const candidatePeer = (data.active_peers && Array.isArray(data.active_peers))
|
const candidatePeer = (data.active_peers && Array.isArray(data.active_peers))
|
||||||
? data.active_peers.find(p => p.role === 'candidate' || (p.peer_id && p.peer_id.startsWith('cand_')))
|
? data.active_peers.find(p => p.role === 'candidate' || (p.peer_id && p.peer_id.startsWith('cand_')))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (candidatePeer) {
|
const isCandidateJoined = Boolean(candidatePeer) || Boolean(candidateStreamConnected);
|
||||||
const isMicOn = isTrueVal(candidatePeer.mic_on);
|
|
||||||
const isCamOn = isTrueVal(candidatePeer.cam_on);
|
if (isCandidateJoined) {
|
||||||
|
const isMicOn = candidatePeer ? isTrueVal(candidatePeer.mic_on) : true;
|
||||||
|
const isCamOn = candidatePeer ? isTrueVal(candidatePeer.cam_on) : true;
|
||||||
updateCandidateStatusIcons(isMicOn, isCamOn, true);
|
updateCandidateStatusIcons(isMicOn, isCamOn, true);
|
||||||
} else {
|
} else {
|
||||||
updateCandidateStatusIcons(false, false, false);
|
updateCandidateStatusIcons(false, false, false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user