1
0
Fork 0
forked from Simnation/Main
Main/resources/[jobs]/[police]/[spy-bodycam]/spy-bodycam/web/js/app.js
2025-07-27 04:11:33 +02:00

467 lines
18 KiB
JavaScript

import { GameView } from './gameview.js';
const gameview = new GameView();
let isRecording = false;
let isDashcamRecording = false;
let recordingTimeout;
let dashcamRecordingTimeout;
let dashcamTimer;
$(document).ready(function () {
$('.overlayCont').hide();
$('.currWatchCont').hide();
$('.RecordInfo').hide();
$('.recCont').hide();
$('.askMain').hide();
$('.vidplaycont').hide();
$('#dashcam-recording-ui').hide();
const beepSound = document.getElementById('beep-sound');
const offSound = document.getElementById('off-sound');
function updateTime() {
const date = new Date();
date.setUTCHours(date.getUTCHours() - 5);
const month = ("0" + (date.getMonth() + 1)).slice(-2);
const day = ("0" + date.getDate()).slice(-2);
const hours = ("0" + date.getHours()).slice(-2);
const minutes = ("0" + date.getMinutes()).slice(-2);
const seconds = ("0" + date.getSeconds()).slice(-2);
const gameTime = `${month}-${day} ${hours}:${minutes}:${seconds}-0500`;
$('.bodyDate').html('<i class="fa-solid fa-circle fa-fade bodyIcon"></i>' + gameTime);
}
let interval;
window.addEventListener('message', function (event) {
const data = event.data;
if (data.action === 'open') {
updateTime();
interval = setInterval(updateTime, 1000);
$('.bodyNum').text(data.bodyname);
$('.bodyCallsign').text(data.callsign);
$('.overlayCont').removeClass('popOut').addClass('popIn').show();
beepSound.play();
} else if (data.action === 'close') {
clearInterval(interval);
$('.overlayCont').removeClass('popIn').addClass('popOut').one('animationend', function () {
$(this).hide();
$(this).removeClass('popOut');
});
offSound.play();
}
if (data.action === 'openWatch') {
let typeCam;
let plateText;
let carText;
if (data.debug) {
var controlsHtml = `
<span class="watchInText typeCam">Controls</span>
<br>
<span class="watchInText typeCam">W -> Up</span>
<br>
<span class="watchInText typeCam">S -> Down</span>
<br>
<span class="watchInText typeCam">A -> Left</span>
<br>
<span class="watchInText typeCam">D -> Right</span>
<br>
<span class="watchInText typeCam">Q -> Forward</span>
<br>
<span class="watchInText typeCam">E -> Backward</span>
`;
document.querySelector('.userLaber').innerHTML = controlsHtml;
} else {
if (data.isbodycam) {
typeCam = "BODYCAM";
plateText = "CamID: " + data.bodyId;
carText = "Name: " + data.name;
} else {
typeCam = "DASHCAM";
plateText = "Plate: " + data.bodyId;
carText = "Car: " + data.name;
}
document.querySelector('.typeCam').textContent = typeCam;
document.querySelector('.plateText').textContent = plateText;
document.querySelector('.carText').textContent = carText;
}
$('.backInText').text(data.exitKey);
$('.currWatchCont').fadeIn();
} else if (data.action === 'closeWatch') {
$('.currWatchCont').fadeOut();
}
if (data.action === 'toggle_record') {
clearTimeout(recordingTimeout);
if (!isRecording) {
// Start recording
isRecording = true;
$('.HeadText').html('<i class="fa-solid fa-circle fa-fade bodyIcon" style="color: rgb(173, 8, 8);"></i>' + 'Recording Started');
$('.RecordInfo').fadeIn();
startRecording(data.hook, data.service);
recordingTimeout = setTimeout(() => {
if (isRecording) {
// Stop recording automatically after configured time
isRecording = false;
$('.HeadText').html('<i class="fa-solid fa-circle bodyIcon" style="color: white;"></i>' + 'Recording Stopped');
setTimeout(() => {
$('.RecordInfo').fadeOut();
}, 2000);
stopRecording();
}
}, data.recTiming * 1000);
} else {
// Stop recording
isRecording = false;
$('.HeadText').html('<i class="fa-solid fa-circle bodyIcon" style="color: white;"></i>' + 'Recording Stopped');
clearTimeout(recordingTimeout);
setTimeout(() => {
$('.RecordInfo').fadeOut();
}, 2000);
stopRecording();
}
}
if (data.action === 'toggle_dashcam_record') {
clearTimeout(dashcamRecordingTimeout);
if (!isDashcamRecording) {
// Start dashcam recording
isDashcamRecording = true;
$('#dashcam-recording-ui').show();
$('#dashcam-vehicle-info').text(`${data.vehicleName} - ${data.vehiclePlate}`);
// Start a timer for the dashcam recording
let seconds = 0;
dashcamTimer = setInterval(() => {
seconds++;
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
$('#dashcam-timer').text(`${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`);
}, 1000);
// Start recording using the existing function
startRecording(data.hook, data.service, data.recTiming, true);
// Auto-stop after the configured time
dashcamRecordingTimeout = setTimeout(() => {
if (isDashcamRecording) {
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
clearInterval(dashcamTimer);
stopRecording(true);
}
}, data.recTiming * 1000);
} else {
// Stop dashcam recording
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
clearTimeout(dashcamRecordingTimeout);
clearInterval(dashcamTimer);
stopRecording(true);
}
}
if (data.action === 'stop_dashcam_recording') {
if (isDashcamRecording) {
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
clearTimeout(dashcamRecordingTimeout);
clearInterval(dashcamTimer);
stopRecording(true);
}
}
if (data.action === 'cancel_rec_force') {
if (isRecording) {
// Force stop recording
isRecording = false;
$('.HeadText').html('<i class="fa-solid fa-circle bodyIcon" style="color: white;"></i>' + 'Recording Stopped');
clearTimeout(recordingTimeout);
setTimeout(() => {
$('.RecordInfo').fadeOut();
}, 1000);
stopRecording();
}
if (isDashcamRecording) {
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
clearTimeout(dashcamRecordingTimeout);
clearInterval(dashcamTimer);
stopRecording(true);
}
}
// RECORDS SHOWING
if (data.action === 'show_records') {
$('.recInnerScroll').empty();
$('.recDesc').text(`${data.jobTitle} Database`);
if (Array.isArray(data.recordData) && data.recordData.length > 0) {
$.each(data.recordData, function (index, record) {
var recordType = record.recordtype || 'bodycam';
var recordIcon = recordType === 'dashcam' ? 'fa-car' : 'fa-video';
var camBox = $(
`
<div class="camBox">
<div class="camInfo">
<div class="camTitle">${record.playername}<span class="camStreet"> [${record.street}]</span></div>
<div class="camDesc">${recordType.toUpperCase()} | Date: ${record.date}</div>
</div>
<div class="camIcons">
<div class="camShow" data-stored="${record.videolink}"><i class="fa-solid fa-eye"></i></div>
${data.isBoss ? `<div class="camDelete" data-url="${record.videolink}"><i class="fa-solid fa-trash"></i></div>` : ''}
</div>
</div>
`
);
$('.recInnerScroll').append(camBox);
});
$('.recCont').show();
} else {
$('.recInnerScroll').html('<h1 class="noRecAv">No records available</h1>');
$('.recCont').show();
}
}
// Refresh Records
if (data.action === 'refreshrec') {
$('.recInnerScroll').empty();
if (Array.isArray(data.recordData) && data.recordData.length > 0) {
$.each(data.recordData, function (index, record) {
var recordType = record.recordtype || 'bodycam';
var recordIcon = recordType === 'dashcam' ? 'fa-car' : 'fa-video';
var camBox = $(
`
<div class="camBox">
<div class="camInfo">
<div class="camTitle">${record.playername}<span class="camStreet"> [${record.street}]</span></div>
<div class="camDesc">${recordType.toUpperCase()} | Date: ${record.date}</div>
</div>
<div class="camIcons">
<div class="camShow" data-stored="${record.videolink}"><i class="fa-solid fa-eye"></i></div>
${data.isBoss ? `<div class="camDelete" data-url="${record.videolink}"><i class="fa-solid fa-trash"></i></div>` : ''}
</div>
</div>
`
);
$('.recInnerScroll').append(camBox);
});
$('.recCont').show();
} else {
$('.recInnerScroll').html('<h1 class="noRecAv">No records available</h1>');
$('.recCont').show();
}
}
});
let deleteUrl = '';
// SEARCH AND DATE FILTERS :)
function updateVisibility(searchText) {
$('.camBox').each(function () {
var camTitleText = $(this).find('.camTitle').text().toLowerCase();
var camDescDate = $(this).find('.camDesc').text().trim().replace(/.*Date: /, '');
if ((searchText === '' || camTitleText.includes(searchText)) &&
($(this).css('display') !== 'none' || camDescDate === $('.selectedDate').val())
) {
$(this).show();
}
else if ((searchText === '' || camTitleText.includes(searchText)) &&
($('.selectedDate').val() === '')
) {
$(this).show();
} else {
$(this).hide();
}
});
}
$('.searchInput').on('input', function () {
var searchText = $(this).val().toLowerCase();
updateVisibility(searchText);
});
$('.selectedDate').on('change', function () {
var selectedDate = $(this).val();
$('.camBox').each(function () {
var camDescDate = $(this).find('.camDesc').text().trim().replace(/.*Date: /, '');
if (selectedDate === '') {
$(this).show();
} else if (selectedDate === camDescDate) {
$(this).show();
} else {
$(this).hide();
}
});
var searchText = $('.searchInput').val().toLowerCase();
updateVisibility(searchText);
});
$(document).on('click', '.camDelete', function () {
const camBox = $(this).closest('.camBox');
deleteUrl = $(this).data('url');
$('.askMain').fadeIn();
});
$(document).on('click', '.askBtn:nth-child(1)', function () {
$('.askMain').fadeOut();
if (deleteUrl) {
$.post(`https://${GetParentResourceName()}/deleteVideo`, JSON.stringify({
vidurl: deleteUrl
}));
}
});
$(document).on('click', '.askBtn:nth-child(2)', function () {
$('.askMain').fadeOut();
});
$(document).on('click', '.camShow', function () {
var videoUrl = $(this).data('stored');
$('.vidPlayer').attr('src', videoUrl);
$('.vidplaycont').show();
});
$(document).on('click', '.vidPlayerIcon', function () {
$('.vidplaycont').hide();
$('.vidPlayer').attr('src', '');
});
$(document).on('click', function (event) {
if ($(event.target).is('.vidplaycont')) {
$('.vidplaycont').hide();
$('.vidPlayer').attr('src', '');
}
});
$(document).on('keydown', function (e) {
if (e.which === 27) {
$('.askMain').hide();
$('.searchInput').val('');
$('.selectedDate').val('');
$('.recCont').hide();
$('.vidplaycont').hide();
$('.vidPlayer').attr('src', '');
$.post(`https://${GetParentResourceName()}/closeRecUI`, '{}');
}
});
});
let mediaRecorder;
let audioStream;
const canvasElement = document.querySelector('canvas');
async function uploadBlob(videoBlob, hook, service, isDashcam = false) {
$.post(`https://${GetParentResourceName()}/exitBodyCam`, '{}');
const formData = new FormData();
try {
let response, responseData;
if (service === 'fivemanage') {
formData.append('video', videoBlob);
response = await fetch('https://api.fivemanage.com/api/video', {
method: 'POST',
headers: {
Authorization: hook,
},
body: formData,
});
if (!response.ok) {
throw new Error(`Failed to upload video to FiveManage: ${response.status}`);
}
responseData = await response.json();
$.post(`https://${GetParentResourceName()}/videoLog`, JSON.stringify({
vidurl: responseData.url,
isDashcam: isDashcam
}));
} else if (service === 'fivemerr') {
formData.append('file', videoBlob, 'video.webm');
response = await fetch('https://api.fivemerr.com/v1/media/videos', {
method: 'POST',
headers: {
Authorization: hook,
},
body: formData,
});
if (!response.ok) {
throw new Error(`Failed to upload video to Fivemerr: ${response.status}`);
}
responseData = await response.json();
$.post(`https://${GetParentResourceName()}/videoLog`, JSON.stringify({
vidurl: responseData.url,
isDashcam: isDashcam
}));
} else if (service === 'discord') {
formData.append('file', videoBlob, 'video.webm');
response = await fetch(hook, {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error(`Failed to upload video to Discord: ${response.status}`);
}
responseData = await response.json();
$.post(`https://${GetParentResourceName()}/videoLog`, JSON.stringify({
vidurl: responseData.attachments[0].url,
isDashcam: isDashcam
}));
}
} catch (error) {
console.error('^1[ERROR]:^3', error.message);
}
}
async function startMicrophoneCapture() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
return stream;
} catch (error) {
console.error('Microphone capture failed. Recording video only.');
return null;
}
}
async function startRecording(hook, service, recordTime, isDashcam = false) {
audioStream = await startMicrophoneCapture();
if (!isRecording && !isDashcamRecording) {
if (audioStream) {
audioStream.getAudioTracks().forEach(track => track.stop());
}
return;
}
console.log(isDashcam ? 'Dashcam Recording Started' : 'Bodycam Recording Started');
const gameView = gameview.createGameView(canvasElement);
const canvasStream = canvasElement.captureStream(30);
// Combine audio and video streams
const combinedStream = new MediaStream([
...canvasStream.getVideoTracks(),
...(audioStream ? audioStream.getAudioTracks() : [])
]);
const videoChunks = [];
window.gameView = gameView;
mediaRecorder = new MediaRecorder(combinedStream, { mimeType: 'video/webm;codecs=vp9' });
mediaRecorder.start();
mediaRecorder.ondataavailable = (e) => e.data.size > 0 && videoChunks.push(e.data);
mediaRecorder.onstop = async () => {
const videoBlob = new Blob(videoChunks, { type: 'video/webm' });
if (videoBlob.size > 0) {
uploadBlob(videoBlob, hook, service, isDashcam);
}
if (audioStream) {
audioStream.getAudioTracks().forEach(track => track.stop());
}
};
}
function stopRecording(isDashcam = false) {
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
if (isDashcam) {
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
} else {
isRecording = false;
}
}