1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-07-27 04:11:33 +02:00
parent 901a510704
commit 48b64f3e4a
23 changed files with 345 additions and 72 deletions

View file

@ -1,5 +0,0 @@
fx_version 'cerulean'
games { 'gta5' }
author 'DTPL 3D <3d@dtpl.cz>'
description 'Touchdown Car Rental'
version '1.0.1'

View file

@ -1,21 +0,0 @@
<CodeWalkerProject>
<Name>New CodeWalker Project</Name>
<Version value="1" />
<YmapFilenames>
<Item>dtpl_carrent.ymap</Item>
<Item>hei_ap1_03_strm_2.ymap</Item>
</YmapFilenames>
<YtypFilenames>
<Item>dtpl_carrent.ytyp</Item>
</YtypFilenames>
<YbnFilenames />
<YndFilenames />
<YnvFilenames />
<TrainsFilenames />
<ScenarioFilenames />
<AudioRelFilenames />
<YdrFilenames />
<YddFilenames />
<YftFilenames />
<YtdFilenames />
</CodeWalkerProject>

View file

@ -9,6 +9,8 @@ local PlyInCarCam = false
local bcamstate = false
local carCam = false
local onRec = false
local isRecording = false
local isDashcamRecording = false
-- for prop and ped
local propNetID = nil
@ -767,22 +769,88 @@ RegisterNetEvent('spy-bodycam:client:deleteDecoyPed',function(plyId)
end)
RegisterNetEvent('spy-bodycam:client:startRec',function(webhook,serviceUsed)
SendNUIMessage({
action = "toggle_record",
hook = webhook,
service = serviceUsed,
recTiming = Config.RecordTime,
})
if Config.ForceViewCam then
SetTimecycleModifier(Config.CameraEffect.bodycam)
SetTimecycleModifierStrength(0.5)
CreateThread(function()
onRec = true
while onRec do
SetFollowPedCamViewMode(4)
Wait(0)
end
end)
if isRecording then
SendNUIMessage({action = "cancel_rec_force"})
isRecording = false
NotifyPlayer('Recording stopped!', 'success', 2500)
if Config.ForceViewCam then
onRec = false
SetFollowPedCamViewMode(1)
SetTimecycleModifier('default')
SetTimecycleModifierStrength(1.0)
end
else
SendNUIMessage({
action = "toggle_record",
hook = webhook,
service = serviceUsed,
recTiming = Config.RecordTime,
})
isRecording = true
NotifyPlayer('Recording started!', 'success', 2500)
if Config.ForceViewCam then
SetTimecycleModifier(Config.CameraEffect.bodycam)
SetTimecycleModifierStrength(0.5)
CreateThread(function()
onRec = true
while onRec do
SetFollowPedCamViewMode(4)
Wait(0)
end
end)
end
end
end)
RegisterNetEvent('spy-bodycam:client:startDashcamRec', function(webhook, serviceUsed, netId)
if isDashcamRecording then
SendNUIMessage({action = "stop_dashcam_recording"})
isDashcamRecording = false
NotifyPlayer('Dashcam recording stopped!', 'success', 2500)
if Config.ForceViewCam then
onRec = false
SetFollowPedCamViewMode(1)
SetTimecycleModifier('default')
SetTimecycleModifierStrength(1.0)
end
else
local veh = NetworkGetEntityFromNetworkId(netId)
if not DoesEntityExist(veh) then
NotifyPlayer('Vehicle not found!', 'error', 2500)
return
end
-- Get vehicle info for recording overlay
local carPlate = GetVehicleNumberPlateText(veh)
local carName = GetVehDisplayName(GetEntityModel(veh))
-- Send message to NUI to start recording
SendNUIMessage({
action = "toggle_dashcam_record",
hook = webhook,
service = serviceUsed,
recTiming = Config.RecordTime,
vehicleId = netId,
vehiclePlate = carPlate,
vehicleName = carName
})
isDashcamRecording = true
NotifyPlayer('Dashcam recording started!', 'success', 2500)
if Config.ForceViewCam then
SetTimecycleModifier(Config.CameraEffect.dashcam)
SetTimecycleModifierStrength(0.5)
CreateThread(function()
onRec = true
while onRec do
SetFollowPedCamViewMode(4)
Wait(0)
end
end)
end
end
end)
@ -805,6 +873,39 @@ RegisterNetEvent('spy-bodycam:client:refreshRecords',function(records,isBoss)
})
end)
-- Register hotkeys for recording if enabled in config
if Config.EnableRecordingHotkeys then
-- Bodycam recording hotkey
RegisterKeyMapping('bodycamrecord', 'Start/Stop bodycam recording', 'keyboard', Config.RecordHotkey)
RegisterCommand('bodycamrecord', function()
if bcamstate then -- Only if bodycam is active
TriggerServerEvent('spy-bodycam:server:toggleRecording')
else
NotifyPlayer('Bodycam not activated!', 'error', 2500)
end
end)
-- Dashcam recording hotkey
RegisterKeyMapping('dashcamrecord', 'Start/Stop dashcam recording', 'keyboard', Config.DashcamHotkey)
RegisterCommand('dashcamrecord', function()
if IsPedInAnyVehicle(cache.ped, false) then
local veh = GetVehiclePedIsIn(cache.ped, false)
if isCarAuth(veh) then
local netId = NetworkGetNetworkIdFromEntity(veh)
if GlobalState.CarsOnBodycam[netId] then
TriggerServerEvent('spy-bodycam:server:toggleDashcamRecording', netId)
else
NotifyPlayer('Dashcam not activated in this vehicle!', 'error', 2500)
end
else
NotifyPlayer('Vehicle not authorized for dashcam!', 'error', 2500)
end
else
NotifyPlayer('You need to be in a vehicle to use dashcam!', 'error', 2500)
end
end)
end
RegisterKeyMapping('bodycamexit', 'Exit bodycam spectate', 'keyboard', Config.ExitCamKey)
RegisterCommand('bodycamexit', function()
if PlyInCam or PlyInCarCam then
@ -827,7 +928,7 @@ RegisterNUICallback('videoLog', function(data, cb)
local videoUrl = data.vidurl
local pos = GetEntityCoords(cache.ped)
local s1, s2 = GetStreetNameAtCoord(pos.x, pos.y, pos.z)
TriggerServerEvent('spy-bodycam:server:logVideoDetails', videoUrl,GetStreetNameFromHashKey(s1))
TriggerServerEvent('spy-bodycam:server:logVideoDetails', videoUrl, GetStreetNameFromHashKey(s1), data.isDashcam)
end
end)
@ -1041,6 +1142,7 @@ function PlayWatchAnim(ped,isNet)
Citizen.Wait(10)
end
local prop = CreateObject(tabletprop, x, y, z + 0.2, isNet, true, false)
AttachEntityToEntity(prop, ped, GetPedBoneIndex(ped, 28422), -0.05, 0.0, 0.0, 0.0, 0.0, 0.0,
AttachEntityToEntity(prop, ped, GetPedBoneIndex(ped, 28422), -0.05, 0.0, 0.0, 0.0, 0.0, 0.0, true, true, false, true, 1, true)
local animDict = 'amb@code_human_in_bus_passenger_idles@female@tablet@idle_a'
RequestAnimDict(animDict)
@ -1221,3 +1323,5 @@ function HasItemsCheck(itemname)
end

View file

@ -21,10 +21,13 @@ Config.Dependency = {
UseProgress = 'qb', -- qb | ox | esx
UseMenu = 'qb', -- qb | ox | esx
UseNotify = 'qb', -- qb | ox | esx
UseAppearance = 'illenium', -- qb | illenium | false
UseAppearance = 'illenium', -- qb | illenium | false
}
Config.ExitCamKey = 'BACK'
Config.RecordHotkey = 'F9' -- Hotkey for bodycam recording
Config.DashcamHotkey = 'F10' -- Hotkey for dashcam recording
Config.EnableRecordingHotkeys = true -- Enable/disable hotkey functionality
Config.CameraEffect = {
bodycam = 'Island_CCTV_ChannelFuzz',

View file

@ -161,13 +161,70 @@ Citizen.CreateThread(function()
end
end)
RegisterNetEvent('spy-bodycam:server:logVideoDetails', function(videoUrl,streetName)
-- Handle bodycam recording toggle
RegisterNetEvent('spy-bodycam:server:toggleRecording', function()
local src = source
if PlayerOnBodycam[src] then
local defwebhook
if Upload.ServiceUsed == 'discord' then
local jobKey
if Config.Framework == 'qb' then
local Player = QBCore.Functions.GetPlayer(src)
jobKey = Player.PlayerData.job.name
else
local xPlayer = ESX.GetPlayerFromId(src)
jobKey = xPlayer.getJob().name
end
if Upload.JobUploads[jobKey] then
defwebhook = Upload.JobUploads[jobKey].webhook
else
defwebhook = Upload.DefaultUploads.webhook
end
elseif Upload.ServiceUsed == 'fivemanage' or Upload.ServiceUsed == 'fivemerr' then
defwebhook = Upload.Token
end
TriggerClientEvent('spy-bodycam:client:startRec', src, defwebhook, Upload.ServiceUsed)
else
NotifyPlayerSv('Bodycam not turned on!', 'error', 3000, src)
end
end)
-- Handle dashcam recording toggle
RegisterNetEvent('spy-bodycam:server:toggleDashcamRecording', function(netId)
local src = source
if CarsOnBodycam[netId] then
local defwebhook
if Upload.ServiceUsed == 'discord' then
local jobKey
if Config.Framework == 'qb' then
local Player = QBCore.Functions.GetPlayer(src)
jobKey = Player.PlayerData.job.name
else
local xPlayer = ESX.GetPlayerFromId(src)
jobKey = xPlayer.getJob().name
end
if Upload.JobUploads[jobKey] then
defwebhook = Upload.JobUploads[jobKey].webhook
else
defwebhook = Upload.DefaultUploads.webhook
end
elseif Upload.ServiceUsed == 'fivemanage' or Upload.ServiceUsed == 'fivemerr' then
defwebhook = Upload.Token
end
TriggerClientEvent('spy-bodycam:client:startDashcamRec', src, defwebhook, Upload.ServiceUsed, netId)
else
NotifyPlayerSv('Dashcam not turned on!', 'error', 3000, src)
end
end)
RegisterNetEvent('spy-bodycam:server:logVideoDetails', function(videoUrl, streetName, isDashcam)
local src = source
local offName
local offJob
local offRank
local jobKey
local date = os.date('%Y-%m-%d')
local recordType = isDashcam and "dashcam" or "bodycam"
if Config.Framework == 'qb' then
local Player = QBCore.Functions.GetPlayer(src)
@ -184,12 +241,13 @@ RegisterNetEvent('spy-bodycam:server:logVideoDetails', function(videoUrl,streetN
end
---SQL UPLOAD
MySQL.Async.execute('INSERT INTO spy_bodycam (job, videolink, street, date, playername) VALUES (@job, @videolink, @street, @date, @playername)', {
MySQL.Async.execute('INSERT INTO spy_bodycam (job, videolink, street, date, playername, recordtype) VALUES (@job, @videolink, @street, @date, @playername, @recordtype)', {
['@job'] = jobKey,
['@videolink'] = videoUrl,
['@street'] = streetName,
['@date'] = date,
['@playername'] = offName
['@playername'] = offName,
['@recordtype'] = recordType
}, function(rowsChanged) end)
if Upload.DiscordLogs.Enabled then
@ -204,7 +262,7 @@ RegisterNetEvent('spy-bodycam:server:logVideoDetails', function(videoUrl,streetN
end
local embedData = {
{
title = Upload.DiscordLogs.Title,
title = Upload.DiscordLogs.Title .. (isDashcam and " (Dashcam)" or " (Bodycam)"),
color = 16761035,
fields = {
{ name = "Name:", value = offName, inline = false },
@ -212,7 +270,7 @@ RegisterNetEvent('spy-bodycam:server:logVideoDetails', function(videoUrl,streetN
{ name = "Job Rank:", value = offRank, inline = false },
},
footer = {
text = "Date: " .. os.date("!%Y-%m-%d %H:%M:%S", os.time()),
text = "Date: " .. os.date("!%Y-%m-%d %H:%M:%S", os.time()),
icon_url = "https://i.imgur.com/CuSyeZT.png",
},
author = author
@ -370,4 +428,3 @@ AddEventHandler('onResourceStart', function(resourceName)
checkForUpdates()
end
end)

View file

@ -79,6 +79,15 @@
</div>
</div>
</div>
<!-- Dashcam Recording UI -->
<div id="dashcam-recording-ui">
<div class="dashcam-header">
<div class="dashcam-recording-indicator"></div>
<span>DASHCAM RECORDING</span>
</div>
<div id="dashcam-vehicle-info">Police Cruiser - ABC123</div>
<div id="dashcam-timer">00:00</div>
</div>
<audio id="beep-sound" src="audio/on_sound.mp3"></audio>
<audio id="off-sound" src="audio/off_sound.mp3"></audio>
<script src="js/jquery-3.7.1.min.js"></script>

View file

@ -1,7 +1,10 @@
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();
@ -10,6 +13,7 @@ $(document).ready(function () {
$('.recCont').hide();
$('.askMain').hide();
$('.vidplaycont').hide();
$('#dashcam-recording-ui').hide();
const beepSound = document.getElementById('beep-sound');
const offSound = document.getElementById('off-sound');
@ -98,7 +102,7 @@ $(document).ready(function () {
startRecording(data.hook, data.service);
recordingTimeout = setTimeout(() => {
if (isRecording) {
// Stop recording automatically after 30 seconds
// Stop recording automatically after configured time
isRecording = false;
$('.HeadText').html('<i class="fa-solid fa-circle bodyIcon" style="color: white;"></i>' + 'Recording Stopped');
setTimeout(() => {
@ -118,6 +122,53 @@ $(document).ready(function () {
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
@ -129,6 +180,13 @@ $(document).ready(function () {
}, 1000);
stopRecording();
}
if (isDashcamRecording) {
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
clearTimeout(dashcamRecordingTimeout);
clearInterval(dashcamTimer);
stopRecording(true);
}
}
// RECORDS SHOWING
@ -138,16 +196,18 @@ $(document).ready(function () {
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">Date: ${record.date}</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"><i class="fa-solid fa-trash"></i></div>` : ''}
${data.isBoss ? `<div class="camDelete" data-url="${record.videolink}"><i class="fa-solid fa-trash"></i></div>` : ''}
</div>
</div>
`
@ -166,16 +226,18 @@ $(document).ready(function () {
$('.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">Date: ${record.date}</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"><i class="fa-solid fa-trash"></i></div>` : ''}
${data.isBoss ? `<div class="camDelete" data-url="${record.videolink}"><i class="fa-solid fa-trash"></i></div>` : ''}
</div>
</div>
`
@ -196,7 +258,7 @@ $(document).ready(function () {
function updateVisibility(searchText) {
$('.camBox').each(function () {
var camTitleText = $(this).find('.camTitle').text().toLowerCase();
var camDescDate = $(this).find('.camDesc').text().trim().replace('Date: ', '');
var camDescDate = $(this).find('.camDesc').text().trim().replace(/.*Date: /, '');
if ((searchText === '' || camTitleText.includes(searchText)) &&
($(this).css('display') !== 'none' || camDescDate === $('.selectedDate').val())
) {
@ -220,7 +282,7 @@ $(document).ready(function () {
$('.selectedDate').on('change', function () {
var selectedDate = $(this).val();
$('.camBox').each(function () {
var camDescDate = $(this).find('.camDesc').text().trim().replace('Date: ', '');
var camDescDate = $(this).find('.camDesc').text().trim().replace(/.*Date: /, '');
if (selectedDate === '') {
$(this).show();
} else if (selectedDate === camDescDate) {
@ -235,7 +297,7 @@ $(document).ready(function () {
$(document).on('click', '.camDelete', function () {
const camBox = $(this).closest('.camBox');
deleteUrl = camBox.find('.camShow').data('stored');
deleteUrl = $(this).data('url');
$('.askMain').fadeIn();
});
@ -287,7 +349,7 @@ let mediaRecorder;
let audioStream;
const canvasElement = document.querySelector('canvas');
async function uploadBlob(videoBlob, hook, service) {
async function uploadBlob(videoBlob, hook, service, isDashcam = false) {
$.post(`https://${GetParentResourceName()}/exitBodyCam`, '{}');
const formData = new FormData();
try {
@ -306,7 +368,8 @@ async function uploadBlob(videoBlob, hook, service) {
}
responseData = await response.json();
$.post(`https://${GetParentResourceName()}/videoLog`, JSON.stringify({
vidurl: responseData.url
vidurl: responseData.url,
isDashcam: isDashcam
}));
} else if (service === 'fivemerr') {
formData.append('file', videoBlob, 'video.webm');
@ -322,7 +385,8 @@ async function uploadBlob(videoBlob, hook, service) {
}
responseData = await response.json();
$.post(`https://${GetParentResourceName()}/videoLog`, JSON.stringify({
vidurl: responseData.url
vidurl: responseData.url,
isDashcam: isDashcam
}));
} else if (service === 'discord') {
formData.append('file', videoBlob, 'video.webm');
@ -335,7 +399,8 @@ async function uploadBlob(videoBlob, hook, service) {
}
responseData = await response.json();
$.post(`https://${GetParentResourceName()}/videoLog`, JSON.stringify({
vidurl: responseData.attachments[0].url
vidurl: responseData.attachments[0].url,
isDashcam: isDashcam
}));
}
} catch (error) {
@ -353,15 +418,16 @@ async function startMicrophoneCapture() {
}
}
async function startRecording(hook, service) {
async function startRecording(hook, service, recordTime, isDashcam = false) {
audioStream = await startMicrophoneCapture();
if (!isRecording){
if (!isRecording && !isDashcamRecording) {
if (audioStream) {
audioStream.getAudioTracks().forEach(track => track.stop());
}
return
return;
}
console.log('Video Recording Started');
console.log(isDashcam ? 'Dashcam Recording Started' : 'Bodycam Recording Started');
const gameView = gameview.createGameView(canvasElement);
const canvasStream = canvasElement.captureStream(30);
@ -379,7 +445,7 @@ async function startRecording(hook, service) {
mediaRecorder.onstop = async () => {
const videoBlob = new Blob(videoChunks, { type: 'video/webm' });
if (videoBlob.size > 0) {
uploadBlob(videoBlob, hook, service);
uploadBlob(videoBlob, hook, service, isDashcam);
}
if (audioStream) {
audioStream.getAudioTracks().forEach(track => track.stop());
@ -387,8 +453,15 @@ async function startRecording(hook, service) {
};
}
function stopRecording() {
function stopRecording(isDashcam = false) {
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
if (isDashcam) {
isDashcamRecording = false;
$('#dashcam-recording-ui').hide();
} else {
isRecording = false;
}
}

View file

@ -449,3 +449,56 @@
font-size: 3rem;
font-family: "Fjalla One", sans-serif;
}
/* Dashcam Recording UI */
#dashcam-recording-ui {
position: absolute;
top: 20px;
left: 20px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 15px;
border-radius: 5px;
font-family: "Play", sans-serif;
display: none;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
border: 1px solid rgba(255, 0, 0, 0.3);
min-width: 250px;
}
.dashcam-header {
display: flex;
align-items: center;
margin-bottom: 10px;
font-weight: bold;
font-size: 16px;
}
#dashcam-vehicle-info {
font-size: 14px;
margin-bottom: 5px;
color: #f0f0f0;
}
#dashcam-timer {
font-size: 20px;
font-weight: bold;
color: #ff3333;
text-align: center;
}
.dashcam-recording-indicator {
width: 12px;
height: 12px;
background-color: red;
border-radius: 50%;
display: inline-block;
margin-right: 10px;
animation: blink 1s infinite;
}
@keyframes blink {
0% { opacity: 1; }
50% { opacity: 0; }
100% { opacity: 1; }
}