Shinobi/web/assets/js/bs5.videosTable.js

443 lines
19 KiB
JavaScript

$(document).ready(function(e){
var theEnclosure = $('#tab-videosTableView')
var monitorsList = theEnclosure.find('.monitors_list')
var dateSelector = theEnclosure.find('.date_selector')
var videosTableDrawArea = $('#videosTable_draw_area')
var videosTablePreviewArea = $('#videosTable_preview_area')
var objectTagSearchField = $('#videosTable_tag_search')
var cloudVideoCheckSwitch = $('#videosTable_cloudVideos')
var sideLinkListBox = $('#side-menu-link-videosTableView ul')
var loadedVideosTable = [];
var redrawTimeout;
var frameUrlCache = {}
var frameUrlCacheTimeouts = {}
async function getSnapshotFromVideoTimeFrame(monitorId,startDate,endDate){
const frameUrlCacheId = `${monitorId}${startDate}${endDate}`
if(typeof frameUrlCache[frameUrlCacheId] === 'string'){
return frameUrlCache[frameUrlCacheId]
}else{
const frame = (await getTimelapseFrames(monitorId,startDate,endDate,1))[0]
const href = frame && frame.href ? frame.href : ''
frameUrlCache[frameUrlCacheId] = `${href}`
frameUrlCacheTimeouts[frameUrlCacheId] = setTimeout(() => {
delete(frameUrlCache[frameUrlCacheId])
delete(frameUrlCacheTimeouts[frameUrlCacheId])
},1000 * 60 * 15)
return href
}
}
function loadFramesForVideosInView(){
videosTableDrawArea.find('.video-thumbnail').each(async (n,imgEl) => {
const el = $(imgEl)
const monitorId = el.attr('data-mid')
const startDate = el.attr('data-time')
const endDate = el.attr('data-end')
const imgBlock = el.find('.video-thumbnail-img-block')
const href = await getSnapshotFromVideoTimeFrame(monitorId,startDate,endDate)
imgBlock.find('img').attr('src',href)
})
}
window.openVideosTableView = function(monitorId){
drawMonitorListToSelector(monitorsList,null,null,true)
monitorsList.val(monitorId)
drawVideosTableViewElements()
openTab(`videosTableView`,{})
}
loadDateRangePicker(dateSelector,{
onChange: function(start, end, label) {
drawVideosTableViewElements()
}
})
monitorsList.change(function(){
drawVideosTableViewElements()
})
objectTagSearchField.change(function(){
drawVideosTableViewElements()
})
cloudVideoCheckSwitch.change(function(){
drawVideosTableViewElements()
})
async function drawVideosTableViewElements(usePreloadedData){
var dateRange = getSelectedTime(dateSelector)
var searchQuery = objectTagSearchField.val() || null
var startDate = dateRange.startDate
var endDate = dateRange.endDate
var monitorId = monitorsList.val()
var wantsArchivedVideo = getVideoSetSelected() === 'archive'
var wantCloudVideo = wantCloudVideos()
var frameIconsHtml = ''
if(!usePreloadedData){
loadedVideosTable = (await getVideos({
monitorId,
startDate,
endDate,
searchQuery,
archived: wantsArchivedVideo,
customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
})).videos;
$.each(loadedVideosTable,function(n,v){
loadedVideosInMemory[`${monitorId}${v.time}${v.type}`]
})
}
// for (let i = 0; i < loadedVideosTable.length; i++) {
// const file = loadedVideosTable[i]
// const frameUrl = await getSnapshotFromVideoTimeFrame(file.mid,file.time,file.end);
// file.frameUrl = frameUrl
// }
videosTableDrawArea.bootstrapTable('destroy')
videosTableDrawArea.bootstrapTable({
onPostBody: loadFramesForVideosInView,
onPageChange: () => {
setTimeout(() => {
loadFramesForVideosInView()
},500)
},
pagination: true,
search: true,
columns: [
{
field: 'mid',
title: '',
checkbox: true,
formatter: () => {
return {
checked: false
}
},
},
{
field: 'image',
title: '',
},
{
field: 'Monitor',
title: '',
},
{
field: 'time',
title: lang['Time'],
},
{
field: 'objects',
title: lang['Objects Found']
},
{
field: 'tags',
title: ''
},
{
field: 'size',
title: ''
},
{
field: 'buttons',
title: ''
}
],
data: loadedVideosTable.map((file) => {
var isLocalVideo = !wantCloudVideo
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`
var loadedMonitor = loadedMonitors[file.mid]
return {
image: `<div class="video-thumbnail" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-end="${file.end}" data-filename="${file.filename}">
<div class="video-thumbnail-img-block">
<img class="pop-image cursor-pointer" style="min-width: 100px;min-height: 75px;">
</div>
<div class="video-thumbnail-buttons d-flex">
<a class="video-thumbnail-button-cell open-snapshot p-3">
<i class="fa fa-camera"></i>
</a>
<a class="video-thumbnail-button-cell preview-video p-3" href="${href}" title="${lang.Play}">
<i class="fa fa-play"></i>
</a>
</div>
</div>`,
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
mid: file.mid,
time: `
<div>${timeAgo(file.time)}</div>
<div><small><b>${lang.Start} :</b> ${formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>
<div><small><b>${lang.End} :</b> ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>`,
objects: file.objects,
tags: `
${file.ext ? `<span class="badge badge-${file.ext ==='webm' ? `primary` : 'danger'}">${file.ext}</span>` : ''}
${!isLocalVideo ? `<span class="badge badge-success">${file.type}</span>` : ''}
`,
size: convertKbToHumanSize(file.size),
buttons: `
<div class="row-info btn-group" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-filename="${file.filename}" data-status="${file.status}" data-type="${file.type}">
<a class="btn btn-sm btn-default btn-monitor-status-color open-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
${isLocalVideo && permissionCheck('video_delete',file.mid) ? `<a class="btn btn-sm btn-${file.archive === 1 ? `success status-archived` : `default`} archive-video" title="${lang.Archive}"><i class="fa fa-${file.archive === 1 ? `lock` : `unlock-alt`}"></i></a>` : ''}
<div class="dropdown d-inline-block">
<a class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" data-bs-reference="parent">
<i class="fa fa-ellipsis-v" aria-hidden="true"></i>
</a>
<ul class="dropdown-menu ${definitions.Theme.isDark ? 'dropdown-menu-dark bg-dark' : ''} shadow-lg">
${buildDefaultVideoMenuItems(file)}
</ul>
</div>
</div>
`,
}
})
})
}
function drawPreviewVideo(href){
videosTablePreviewArea.html(`<video class="video_video" style="width:100%" autoplay controls preload loop src="${href}"></video>`)
}
function getSelectedRows(getLoadedRows){
var rowsSelected = []
videosTableDrawArea.find('[name="btSelectItem"]:checked').each(function(n,checkbox){
var rowInfo = $(checkbox).parents('tr').find('.row-info')
var monitorId = rowInfo.attr('data-mid')
var groupKey = rowInfo.attr('data-ke')
var time = rowInfo.attr('data-time')
var filename = rowInfo.attr('data-filename')
var type = rowInfo.attr('data-type')
rowsSelected.push(getLoadedRows ? loadedVideosInMemory[`${monitorId}${time}${type}`] : {
mid: monitorId,
ke: groupKey,
time: time,
filename: filename,
})
})
return rowsSelected
}
function getVideoSetSelected(){
return cloudVideoCheckSwitch.val()
}
function wantCloudVideos(){
const isChecked = getVideoSetSelected() === 'cloud'
return isChecked
}
function drawCompressedVideoProgressBar(data){
var videoUrl = buildNewFileLink(data)
var html = `<li data-mid="${data.mid}" data-ke="${data.mid}" data-name="${data.name}" title="${data.name}">
<div class="text-white cursor-pointer d-flex flex-row" style="align-items: center;justify-content: center;">
<span class="dot shadow mr-2 dot-orange"></span>
<div class="row-status">
${lang.Compressing}...
</div>
<div class="flex-grow-1 px-2 pt-1">
<div class="progress" style="height:18px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: ${data.percent}%;">${data.percent}%</div>
</div>
</div>
<div style="display:none;" class="download-button pr-2">
<a class="badge badge-sm badge-default open-video" href="${videoUrl}"><i class="fa fa-play"></i></a>
</div>
<div style="display:none;" class="download-button pr-2">
<a class="badge badge-sm badge-success" download href="${videoUrl}"><i class="fa fa-download"></i></a>
</div>
<div style="display:none;" class="download-button">
<a class="badge badge-sm badge-dark remove-row"><i class="fa fa-times"></i></a>
</div>
</div>
</li>`
sideLinkListBox.append(html)
}
function buildNewFileLink(data){
return apiBaseUrl + '/videos/' + data.ke + '/' + data.mid + '/' + data.name
}
function downloadCompressedVideo(data){
var downloadUrl = buildNewFileLink(data)
downloadFile(downloadUrl,data.name)
}
$('body')
.on('click','.open-videosTable',function(e){
e.preventDefault()
var monitorId = getRowsMonitorId(this)
openVideosTableView(monitorId)
return false;
});
sideLinkListBox
.on('click','.remove-row',function(){
var el = $(this).parents('[data-mid]')
el.remove()
});
theEnclosure
.on('click','.preview-video',function(e){
e.preventDefault()
var el = $(this)
var rowEl = $(this).parents('[data-mid]')
var monitorId = rowEl.attr('data-mid')
var videoTime = rowEl.attr('data-time')
var type = rowEl.attr('data-type')
var video = loadedVideosInMemory[`${monitorId}${videoTime}${type}`]
var href = el.attr('href')
setPreviewedVideoHighlight(el,videosTableDrawArea)
drawPreviewVideo(href)
setVideoStatus(video)
return false;
})
.on('click','.zip-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows(true)
zipVideosAndDownloadWithConfirm(videos)
return false;
})
.on('click','.refresh-data',function(e){
e.preventDefault()
drawVideosTableViewElements()
return false;
})
.on('click','.open-snapshot',function(e){
e.preventDefault()
var href = $(this).parents('.video-thumbnail').find('img').click()
return false;
})
.on('click','.delete-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
if(videos.length === 0)return;
$.confirm.create({
title: lang["Delete Videos"],
body: `${lang.DeleteTheseMsg}`,
clickOptions: {
title: '<i class="fa fa-trash-o"></i> ' + lang.Delete,
class: 'btn-danger btn-sm'
},
clickCallback: function(){
deleteVideos(videos).then(() => {
console.log(`Done Deleting Rows!`)
})
}
});
return false;
})
.on('click','.compress-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
if(videos.length === 0)return;
$.confirm.create({
title: lang["Compress Videos"],
body: `${lang.CompressTheseMsg}`,
clickOptions: {
title: '<i class="fa fa-compress"></i> ' + lang.Compress,
class: 'btn-primary btn-sm'
},
clickCallback: function(){
compressVideos(videos).then(() => {
console.log(`Done Sending Compression Request!`)
})
}
});
return false;
})
.on('click','.archive-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
if(videos.length === 0)return;
$.confirm.create({
title: lang["Archive Videos"],
body: `${lang.ArchiveTheseMsg}`,
clickOptions: {
title: '<i class="fa fa-lock"></i> ' + lang.Archive,
class: 'btn-primary btn-sm'
},
clickCallback: function(){
archiveVideos(videos).then(() => {
console.log(`Done Archiving Rows!`)
})
}
});
return false;
})
.on('click','.download-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
if(videos.length === 0)return;
$.confirm.create({
title: lang["Batch Download"],
body: `${lang.batchDownloadText}`,
clickOptions: {
title: '<i class="fa fa-check"></i> ' + lang.Yes,
class: 'btn-success btn-sm'
},
clickCallback: function(){
downloadVideos(videos)
}
});
return false;
})
.on('click','.pop-img',function(e){
e.preventDefault()
var videos = getSelectedRows()
if(videos.length === 0)return;
$.confirm.create({
title: lang["Batch Download"],
body: `${lang.batchDownloadText}`,
clickOptions: {
title: '<i class="fa fa-check"></i> ' + lang.Yes,
class: 'btn-success btn-sm'
},
clickCallback: function(){
downloadVideos(videos)
}
});
return false;
})
onWebSocketEvent((data) => {
switch(data.f){
case'video_delete':
case'video_delete_cloud':
if(tabTree.name === 'videosTableView'){
var videoIndexToRemove = loadedVideosTable.findIndex(row => (!data.type || data.type === row.type) && data.mid === row.mid && new Date(row.time).getTime() === new Date(data.time).getTime())
if(videoIndexToRemove !== -1){
loadedVideosTable.splice(videoIndexToRemove, 1);
delete(loadedVideosInMemory[`${data.mid}${data.time}${data.type}`])
clearTimeout(redrawTimeout)
redrawTimeout = setTimeout(function(){
drawVideosTableViewElements(true)
},2000)
}
}
break;
case'video_compress_started':
console.log(`Compressing Video...`,data)
break;
case'video_compress_completed':
if(data.success){
var progressItem = sideLinkListBox.find(`[data-mid="${data.mid}"][data-ke="${data.mid}"][data-name="${data.name}"]`)
var saveBuiltVideo = dashboardOptions().switches.saveCompressedVideo
if(saveBuiltVideo === 1){
downloadCompressedVideo(data)
progressItem.remove()
console.log(`Downloaded!`,data)
}else if(!data.automated){
progressItem.find('.row-status').text(`${lang.Done}!`)
progressItem.find('.dot').removeClass('dot-orange').addClass('dot-green')
progressItem.find('.download-button').show()
progressItem.find('.progress-bar').css('width',`100%`).text(`100%`)
}else if(data.automated){
progressItem.remove()
}
}
break;
case'video_compress_percent':
var progressItem = sideLinkListBox.find(`[data-mid="${data.mid}"][data-ke="${data.mid}"][data-name="${data.name}"]`)
data.percent = data.percent > 100 ? 100 : data.percent
if(progressItem.length === 0){
drawCompressedVideoProgressBar(data)
}else{
progressItem = sideLinkListBox.find(`[data-mid="${data.mid}"][data-ke="${data.mid}"][data-name="${data.name}"]`)
progressItem.find('.progress-bar').css('width',`${data.percent}%`).text(`${data.percent}%`)
}
console.log(data)
break;
}
})
addOnTabOpen('videosTableView', function () {
drawMonitorListToSelector(monitorsList,null,null,true)
drawVideosTableViewElements()
})
addOnTabReopen('videosTableView', function () {
var theSelected = `${monitorsList.val()}`
drawMonitorListToSelector(monitorsList,null,null,true)
monitorsList.val(theSelected)
})
addOnTabAway('videosTableView', function () {
videosTablePreviewArea.find('video')[0].pause()
})
})