UNPKG

nvr-js

Version:

A simple, lightweight, but very functional NVR aimed at 24/7 recording using nodejs

283 lines (252 loc) 6.69 kB
/* eslint-disable no-unused-vars */ /* eslint-disable no-undef */ const SearchTimebufferHours = 2; let Segments = []; function StartStats() { const Update = () => { $.ajax({ type: 'GET', url: '/systeminfo', dataType: 'json', success: function (data) { $('#CPU').html(data.CPU + '%'); $('#RAM').html(data.MEM.usedMemPercentage + '%'); $('#DISK').html(data.DISK.usedPercentage + '%'); } }); }; Update(); setInterval(() => { Update(); }, 5000); } function StartTimeline(ID, Name) { const contents = $('#scrub').html(); const copy = $('<div style="padding:5px" id="' + ID + '"></div>'); copy.append(contents); copy.dialog({ width: 855, height: 630, modal: true, title: Name + ' Timeline Viewer', close: function () { const VideoElement = copy.find('video'); const VE5 = $(VideoElement)[0]; VE5.pause(); VE5.remove(); } }); const TimelineDiv = copy.find('#timeline'); const TL = $(TimelineDiv)[0]; const Groups = new vis.DataSet([ { content: 'Video', id: 'Video', value: 1 }, { content: 'Events', id: 'Events', value: 2 } ]); const Items = new vis.DataSet([ { id: 1, content: 'Display Fix', start: '2000-01-01' } ]); const Options = { start: dayjs().subtract(1, 'hour').toDate(), end: dayjs().add(15, 'minutes').toDate(), groupOrder: function (a, b) { return a.value - b.value; }, height: 150, editable: false, groupEditable: false, stack: false, rollingMode: { follow: false, offset: 0.5 } }; // create a Timeline const timeline = new vis.Timeline(TL, Items, Groups, Options); Items.remove(1); let CommitTimeout; let isMoving = false; timeline.on('rangechange', (event) => { clearTimeout(CommitTimeout); isMoving = true; }); timeline.on('rangechanged', (event) => { CommitTimeout = setTimeout(() => { const Start = dayjs(event.start) .subtract(SearchTimebufferHours, 'hour') .unix(); const End = dayjs(event.end).add(SearchTimebufferHours, 'hour').unix(); GetSegmentsAndEvents(timeline, Items, Start, End, ID); isMoving = false; }, 500); }); timeline.on('click', (event) => { if (!isMoving) { timeline.setCurrentTime(event.time); LoadAndPosition(timeline, event.time, copy, ID); } isMoving = false; }); } let VideoFile; function LoadAndPosition(Timeline, Date, Copy, ID) { const VideoElement = Copy.find('video'); const VE5 = $(VideoElement)[0]; const Time = dayjs(Date).unix(); const MatchedSegments = Segments.filter( (S) => S.Start <= Time && S.End >= Time )[0]; const VideoStart = MatchedSegments.Start; const URL = '/segments/' + ID + '/' + MatchedSegments.FileName; let StartTime = Time - VideoStart; if (StartTime < 0) { StartTime = 0; } if (VideoFile === undefined || VideoFile !== URL) { if (!VE5.paused) { VE5.pause(); } VideoElement.off('timeupdate'); VideoElement.on('timeupdate', (event) => { const Date = dayjs .unix(MatchedSegments.Start) .add(VE5.currentTime, 'second') .toDate(); Timeline.setCurrentTime(Date); }); VideoElement.one('canplay', () => { VE5.play().then((R) => { VE5.currentTime = StartTime; }); }); VE5.src = '/segments/' + ID + '/' + MatchedSegments.FileName; VideoFile = URL; } else { VE5.currentTime = StartTime; if (VE5.paused) { VE5.play(); } } } function EventSort(a, b) { return a.Start - b.Start; } function GetSegmentsAndEvents(Timeline, DataSet, Start, End, ID) { $.getJSON('/geteventdata/' + ID + '/' + Start + '/' + End, function (data) { data.segments.sort(EventSort); Segments = data.segments; DataSet.clear(); for (let i = 0; i < data.segments.length; i++) { const Seg = data.segments[i]; const Start = dayjs.unix(Seg.Start); const End = dayjs.unix(Seg.End); DataSet.add({ start: Start.toDate(), end: End.toDate(), type: 'background', group: 'Video', content: Start.format('YYYY-MM-DD HH:mm:ss'), style: 'background-color: rgba(0,0,0,0.5);color: white;border-radius: 6px;', fileName: Seg.FileName, cameraId: Seg.CameraID, segmentId: Seg.SegmentID }); } for (let i = 0; i < data.events.length; i++) { const Event = data.events[i]; const Start = dayjs.unix(Event.Date); DataSet.add({ start: Start.toDate(), group: 'Events', content: Event.Name, style: 'background-color: orangered;color: white;border-radius: 6px;' }); } Timeline.redraw(); }); } function StartLive(ID, Name, Codec) { let buffer; let socket; const contents = $('#liveView').html(); const copy = $('<div style="padding:5px" id="' + ID + '"></div>'); copy.append(contents); const VideoElement = copy.find('video'); const VE5 = $(VideoElement)[0]; copy.dialog({ width: 520, height: 410, title: Name + ' (Live)', close: function () { socket.disconnect(); VE5.pause(); VE5.remove(); }, buttons: { 'Full Screen': function () { goFullscreen(VE5); }, Snapshot: function () { const canvas = document.createElement('canvas'); canvas.width = VE5.videoWidth; canvas.height = VE5.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(VE5, 0, 0, canvas.width, canvas.height); Canvas2Image.saveAsJPEG(canvas); canvas.remove(); } } }); if (!MediaSource.isTypeSupported(Codec)) { alert('Unsupported mime type'); return; } const mediaSource = new MediaSource(); const DataURL = URL.createObjectURL(mediaSource); VE5.src = DataURL; mediaSource.addEventListener('sourceopen', function (e) { buffer = mediaSource.addSourceBuffer(Codec); buffer.mode = 'sequence'; buffer.addEventListener('updateend', function (e) { if ( mediaSource.duration !== Number.POSITIVE_INFINITY && VE5.currentTime === 0 && mediaSource.duration > 0 ) { VE5.currentTime = mediaSource.duration - 1; mediaSource.duration = Number.POSITIVE_INFINITY; } VE5.play(); }); socket = io('/', { path: '/streams/' + ID }); socket.on('segment', function (data) { data = new Uint8Array(data); buffer.appendBuffer(data); }); }); } function goFullscreen(element) { if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullScreen) { element.webkitRequestFullScreen(); } } function Login() { const Data = { password: $('#Password').val(), username: $('#Username').val() }; $.ajax({ type: 'POST', url: '/login', data: JSON.stringify(Data), contentType: 'application/json; charset=utf-8', success: function () { document.location = '/dashboard'; }, error: function () { alert('Could not login. This may be due to incorrect login details'); } }); }