Merge branch 'archive-videos' into 'dev'

Archive Feature

See merge request Shinobi-Systems/Shinobi!379
refactor-sql-commands-on-startup
Moe 2022-08-20 18:00:22 +00:00
commit 6908a3a757
16 changed files with 448 additions and 21 deletions

View File

@ -8222,6 +8222,10 @@ module.exports = function(s,config,lang){
"name": lang.Local,
"value": "local"
},
{
"name": lang.Archive,
"value": "archive"
},
{
"name": lang.Cloud,
"value": "cloud"

View File

@ -331,6 +331,7 @@
"Start": "Start",
"End": "End",
"Archive": "Archive",
"Unarchive": "Unarchive",
"Email Details": "Email Details",
"Delete Matches": "Delete Matches",
"Delete selected": "Delete selected",
@ -495,15 +496,19 @@
"Delete Filter": "Delete Filter",
"confirmDeleteFilter": "Do you want to delete this filter? You cannot recover it.",
"Fix Video": "Fix Video",
"Archived": "Archived",
"Archive Videos": "Archive Videos",
"Compress Videos": "Compress Videos",
"Compress Completed Videos": "Compress Completed Videos",
"compressCompletedVideosFieldText": "Automatically compress videos to WebM once recorded. Doing this requires a powerful CPU or you must allow a large amount of time to allow compression. The rate in which videos are added to the database can't be faster than the rate for compression.",
"FixVideoMsg": "Do you want to fix this video? This will create a new file and overwrite the old one. You cannot undo this action.",
"DeleteVideoMsg": "Do you want to delete this video? You cannot recover it.",
"CompressVideoMsg": "Do you want to compress this video? The original will be moved to your FileBin. Videos that are already completed compressing will be skipped if already queued.",
"ArchiveVideoMsg": "Do you want to Archive this video? This video won't be deleted by the automated cleanup processes.",
"DeleteThisMsg": "Do you want to delete this? You cannot recover it.",
"DeleteTheseMsg": "Do you want to delete these? You cannot recover them.",
"CompressTheseMsg": "Do you want to compress these? The originals will be moved to your FileBin. Videos that are already WebM will be skipped. Videos that are already completed compressing will be skipped if ordered to compress again.",
"ArchiveTheseMsg": "Do you want to Archive these? They won't be deleted by the automated cleanup processes.",
"dropBoxSuccess": "Success! Files saved to your Dropbox.",
"API Key Deleted": "API Key Deleted",
"APIKeyDeletedText": "Key has been deleted. It will no longer work.",

View File

@ -142,7 +142,7 @@ function beginProcessing(){
const whereQuery = [
['ke','=',v.ke],
['status','!=',"0"],
['details','NOT LIKE','%"archived":"1"%'],
['archive','!=',`1`],
]
b.where.forEach(function(condition){
if(condition.p1 === 'ke'){condition.p3 = v.ke}
@ -203,6 +203,7 @@ function beginProcessing(){
const groupKey = v.ke;
const whereQuery = [
['ke','=',v.ke],
['archive','!=',`1`],
['time','<', sqlDate(days+' DAY')],
addedQueries
]
@ -261,6 +262,7 @@ function beginProcessing(){
table: "Monitors",
where: [
['ke','=',v.ke],
['archive','!=',`1`],
]
})
const monitorRows = monitorsResponse.rows
@ -314,7 +316,7 @@ function beginProcessing(){
where: [
['ke','=',v.ke],
['status','!=','0'],
['details','NOT LIKE','%"archived":"1"%'],
['archive','!=',`1`],
['time','<', sqlDate('10 MINUTE')],
]
},(err,evs) => {
@ -384,6 +386,7 @@ function beginProcessing(){
const groupKey = v.ke;
const whereQuery = [
['ke','=',v.ke],
['archive','!=',`1`],
['time','<', sqlDate(daysOldForDeletion+' DAY')],
]
const selectResponse = await knexQueryPromise({
@ -455,6 +458,7 @@ function beginProcessing(){
table: "Events",
where: [
['ke','=',v.ke],
['archive','!=',`1`],
['time','<', sqlDate(daysOldForDeletion + ' DAY')],
]
},(err,rrr) => {
@ -505,6 +509,7 @@ function beginProcessing(){
table: "Files",
where: [
['ke','=',v.ke],
['archive','!=',`1`],
['time','<', sqlDate(daysOldForDeletion + ' DAY')],
]
},(err,files) => {

View File

@ -213,7 +213,7 @@ module.exports = function(s,config){
whereQuery.push(monitorRestrictions)
}
if(options.archived){
whereQuery.push(['details','LIKE',`%"archived":"1"%`])
whereQuery.push(['archive','=',`1`])
}
if(options.filename){
whereQuery.push(['filename','=',options.filename])

View File

@ -163,11 +163,34 @@ module.exports = function(s,config,lang,app,io){
})
})
}
function archiveFileBinEntry(file,unarchive){
return new Promise((resolve) => {
s.knexQuery({
action: "update",
table: 'Files',
update: {
archive: unarchive ? '0' : 1
},
where: {
ke: file.ke,
mid: file.mid,
name: file.name,
}
},function(err){
resolve({
ok: !err,
err: err,
archived: !unarchive
})
})
})
}
s.getFileBinDirectory = getFileBinDirectory
s.getFileBinEntry = getFileBinEntry
s.insertFileBinEntry = insertFileBinEntry
s.updateFileBinEntry = updateFileBinEntry
s.deleteFileBinEntry = deleteFileBinEntry
s.archiveFileBinEntry = archiveFileBinEntry
/**
* API : Get fileBin file rows
*/
@ -187,6 +210,7 @@ module.exports = function(s,config,lang,app,io){
startTimeOperator: req.query.startOperator,
endTimeOperator: req.query.endOperator,
limit: req.query.limit,
archived: req.query.archived,
endIsStartTo: true,
noFormat: true,
noCount: true,
@ -265,4 +289,67 @@ module.exports = function(s,config,lang,app,io){
})
},res,req);
});
/**
* API : Modify fileBin File
*/
app.get(config.webPaths.apiPrefix+':auth/fileBin/:ke/:id/:file/:mode', function (req,res){
let response = { ok: false };
res.setHeader('Content-Type', 'application/json');
s.auth(req.params,function(user){
const monitorId = req.params.id
const groupKey = req.params.ke
const filename = req.params.file
const {
monitorPermissions,
monitorRestrictions,
} = s.getMonitorsPermitted(user.details,monitorId)
const {
isRestricted,
isRestrictedApiKey,
apiKeyPermissions,
} = s.checkPermission(user);
if(
isRestrictedApiKey && apiKeyPermissions.delete_videos_disallowed ||
isRestricted && !monitorPermissions[`${monitorId}_video_delete`]
){
s.closeJsonResponse(res,{ok: false, msg: lang['Not Authorized']});
return
}
s.knexQuery({
action: "select",
columns: "*",
table: 'Files',
where: [
['ke','=',groupKey],
['mid','=',monitorId],
['name','=',filename]
],
limit: 1
},async (err,r) => {
if(r && r[0]){
const file = r[0];
var details = s.parseJSON(r.details) || {}
switch(req.params.mode){
case'archive':
response.ok = true
const unarchive = s.getPostData(req,'unarchive') == '1';
const archiveResponse = await archiveFileBinEntry(file,unarchive)
response.ok = archiveResponse.ok
response.archived = archiveResponse.archived
break;
case'delete':
response.ok = true;
await s.deleteFileBinEntry(file)
break;
default:
response.msg = user.lang.modifyVideoText1;
break;
}
}else{
response.msg = user.lang['No such file'];
}
s.closeJsonResponse(res,response);
})
},res,req);
})
}

View File

@ -176,6 +176,64 @@ module.exports = function(s,config){
}catch(err){
console.log(err)
}
try{
s.databaseEngine.schema.table('Videos', table => {
table.tinyint('archive',1).defaultTo(0)
table.string('saveDir',255).defaultTo('')
}).then(() => {
console.log(`archive and saveDir added to Videos table`)
}).catch((err) => {
if(err && err.code !== 'ER_DUP_FIELDNAME'){
console.log('error')
console.log(err)
}
})
}catch(err){
console.log(err)
}
try{
s.databaseEngine.schema.table('Timelapse Frames', table => {
table.tinyint('archive',1).defaultTo(0)
table.string('saveDir',255).defaultTo('')
}).then(() => {
console.log(`archive and saveDir added to Timelapse Frames table`)
}).catch((err) => {
if(err && err.code !== 'ER_DUP_FIELDNAME'){
console.log('error')
console.log(err)
}
})
}catch(err){
console.log(err)
}
try{
s.databaseEngine.schema.table('Events', table => {
table.tinyint('archive',1).defaultTo(0)
}).then(() => {
console.log(`archive added to Events table`)
}).catch((err) => {
if(err && err.code !== 'ER_DUP_FIELDNAME'){
console.log('error')
console.log(err)
}
})
}catch(err){
console.log(err)
}
try{
s.databaseEngine.schema.table('Files', table => {
table.tinyint('archive',1).defaultTo(0)
}).then(() => {
console.log(`archive added to Files table`)
}).catch((err) => {
if(err && err.code !== 'ER_DUP_FIELDNAME'){
console.log('error')
console.log(err)
}
})
}catch(err){
console.log(err)
}
delete(s.preQueries)
}
}

View File

@ -350,7 +350,6 @@ module.exports = function(s,config,lang,app,io){
const timeNow = new Date()
const fileStats = await fs.promises.stat(finalMp4OutputLocation)
const details = {
video: true,
start: frames[0].time,
end: frames[frames.length - 1].time,
}

View File

@ -221,7 +221,7 @@ module.exports = (s,config,lang) => {
where: [
['ke','=',groupKey],
['status','!=','0'],
['details','NOT LIKE',`%"archived":"1"%`],
['archive','!=',`1`],
['details','LIKE',`%"dir":"${storage.value}"%`],
],
orderBy: ['time','asc'],
@ -266,7 +266,7 @@ module.exports = (s,config,lang) => {
where: [
['ke','=',groupKey],
['status','!=','0'],
['details','NOT LIKE',`%"archived":"1"%`],
['archive','!=',`1`],
['details','NOT LIKE',`%"dir"%`],
],
orderBy: ['time','asc'],
@ -298,7 +298,7 @@ module.exports = (s,config,lang) => {
table: "Timelapse Frames",
where: [
['ke','=',groupKey],
['details','NOT LIKE',`%"archived":"1"%`],
['archive','!=',`1`],
],
orderBy: ['time','asc'],
limit: 3
@ -326,6 +326,7 @@ module.exports = (s,config,lang) => {
table: "Files",
where: [
['ke','=',groupKey],
['archive','!=',`1`],
],
orderBy: ['time','asc'],
limit: 1
@ -408,7 +409,6 @@ module.exports = (s,config,lang) => {
table: "Cloud Timelapse Frames",
where: [
['ke','=',groupKey],
['details','NOT LIKE',`%"archived":"1"%`],
],
orderBy: ['time','asc'],
limit: 3

View File

@ -479,6 +479,61 @@ module.exports = (s,config,lang) => {
}
})
}
function archiveVideo(video,unarchive){
return new Promise((resolve) => {
s.knexQuery({
action: "update",
table: 'Videos',
update: {
archive: unarchive ? '0' : 1
},
where: {
ke: video.ke,
mid: video.mid,
time: video.time,
}
},function(errVideos){
s.knexQuery({
action: "update",
table: 'Events',
update: {
archive: unarchive ? '0' : 1
},
where: [
['ke','=',video.ke],
['mid','=',video.mid],
['time','>=',video.time],
['time','<=',video.end],
]
},function(errEvents){
s.knexQuery({
action: "update",
table: 'Timelapse Frames',
update: {
archive: unarchive ? '0' : 1
},
limit: 1,
where: [
['ke','=',video.ke],
['mid','=',video.mid],
['time','>=',video.time],
['time','<=',video.end],
]
},function(errTimelapseFrames){
resolve({
ok: !errVideos && !errEvents && !errTimelapseFrames,
err: errVideos || errEvents || errTimelapseFrames ? {
errVideos,
errEvents,
errTimelapseFrames,
} : undefined,
archived: !unarchive
})
})
})
})
})
}
return {
reEncodeVideoAndReplace,
stitchMp4Files,
@ -488,5 +543,6 @@ module.exports = (s,config,lang) => {
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent,
reEncodeVideoAndBinOriginal,
reEncodeVideoAndBinOriginalAddToQueue,
archiveVideo,
}
}

View File

@ -31,6 +31,7 @@ module.exports = function(s,config,lang,app,io){
destroySubstreamProcess,
} = require('./monitor/utils.js')(s,config,lang)
const {
archiveVideo,
reEncodeVideoAndReplace,
reEncodeVideoAndBinOriginalAddToQueue,
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent,
@ -1141,6 +1142,7 @@ module.exports = function(s,config,lang,app,io){
endTimeOperator: req.query.endOperator,
noLimit: req.query.noLimit,
limit: req.query.limit,
archived: req.query.archived,
endIsStartTo: true,
parseRowDetails: true,
noFormat: true,
@ -1811,6 +1813,13 @@ module.exports = function(s,config,lang,app,io){
const originalFileName = `${s.formattedTime(r.time)+'.'+r.ext}`
var details = s.parseJSON(r.details) || {}
switch(req.params.mode){
case'archive':
response.ok = true
const unarchive = s.getPostData(req,'unarchive') == '1';
const archiveResponse = await archiveVideo(r,unarchive)
response.ok = archiveResponse.ok
response.archived = archiveResponse.archived
break;
case'fix':
await reEncodeVideoAndReplace(r)
break;

View File

@ -1,4 +1,28 @@
.video-thumbnail {
position: relative;
overflow: hidden;
}
.video-thumbnail img {
height: 75px;
border-radius: 10px;
}
.video-thumbnail-buttons {
position: absolute;
height: 100%;
width:100%;
top:0;
left:0;
}
.video-thumbnail-buttons > .video-thumbnail-button-cell {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
text-shadow: 0 0 15px #333;
cursor: pointer;
}
.video-thumbnail-buttons > .video-thumbnail-button-cell:hover {
background: rgba(0,0,0,0.6);
color: #fff;
}

View File

@ -4,7 +4,7 @@ $(document).ready(function(e){
var dateSelector = theEnclosure.find('.date_selector')
var fileBinDrawArea = $('#fileBin_draw_area')
var fileBinPreviewArea = $('#fileBin_preview_area')
var loadedVideosInMemory = {};
var loadedFilesInMemory = {};
function openFileBinView(monitorId,startDate,endDate){
drawFileBinViewElements(monitorId,startDate,endDate)
}
@ -37,6 +37,10 @@ $(document).ready(function(e){
monitorsList.change(function(){
drawFileBinViewElements()
})
function loadFileData(video){
delete(video.f)
loadedFilesInMemory[`${video.mid}${video.name}`] = video
}
function drawFileBinViewElements(selectedMonitor,startDate,endDate){
var dateRange = getSelectedTime(false)
if(!startDate)startDate = dateRange.startDate
@ -46,8 +50,11 @@ $(document).ready(function(e){
var frameIconsHtml = ''
var apiURL = getApiPrefix('fileBin') + '/' + selectedMonitor;
var fileBinData = []
loadedVideosInMemory = {}
loadedFilesInMemory = {}
$.getJSON(apiURL + '?' + queryString.join('&'),function(data){
$.each(data.files,function(n,file){
loadFileData(file)
})
fileBinDrawArea.bootstrapTable('destroy')
fileBinDrawArea.bootstrapTable({
pagination: true,
@ -76,6 +83,7 @@ $(document).ready(function(e){
],
data: data.files.map((file) => {
var href = getApiPrefix('fileBin') + '/' + selectedMonitor + '/' + file.name
var isVideo = file.name.includes('.mp4') || file.name.includes('.webm')
return {
monitorName: `<b>${loadedMonitors[file.mid]?.name || file.mid}</b>`,
name: file.name,
@ -86,8 +94,11 @@ $(document).ready(function(e){
`,
size: convertKbToHumanSize(file.size),
buttons: `
<a class="btn btn-sm btn-primary" href="${href}" download title="${lang.Download}"><i class="fa fa-download"></i></a>
${file.details.video ? `<a class="btn btn-sm btn-primary preview-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>` : ``}
<div class="row-info" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-name="${file.name}">
<a class="btn btn-sm btn-primary" href="${href}" download title="${lang.Download}"><i class="fa fa-download"></i></a>
${isVideo ? `<a class="btn btn-sm btn-primary preview-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>` : ``}
${permissionCheck('video_delete',file.mid) ? `<a class="btn btn-sm btn-${file.archive === 1 ? `success status-archived` : `default`} archive-file" title="${lang.Archive}"><i class="fa fa-${file.archive === 1 ? `lock` : `unlock-alt`}"></i></a>` : ''}
</div>
`,
}
})
@ -97,6 +108,24 @@ $(document).ready(function(e){
function drawPreviewVideo(href){
fileBinPreviewArea.html(`<video class="video_video" style="width:100%" autoplay controls preload loop src="${href}"></video>`)
}
function archiveFile(video,unarchive){
return archiveVideo(video,unarchive,true)
}
async function archiveFiles(videos){
for (let i = 0; i < videos.length; i++) {
var video = videos[i];
await archiveFile(video,false)
}
}
function unarchiveFile(video){
return archiveFile(video,true)
}
async function unarchiveFiles(videos){
for (let i = 0; i < videos.length; i++) {
var video = videos[i];
await unarchiveFile(video)
}
}
$('body')
.on('click','.open-fileBin-video',function(e){
e.preventDefault()
@ -112,6 +141,21 @@ $(document).ready(function(e){
drawPreviewVideo(href)
return false;
})
.on('click','.archive-file',function(e){
e.preventDefault()
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var filename = el.attr('data-name')
var unarchive = $(this).hasClass('status-archived')
var file = loadedFilesInMemory[`${monitorId}${filename}`]
if(!file)return console.log(`No File`,monitorId,filename,unarchive,file);
if(unarchive){
unarchiveFile(file)
}else{
archiveFile(file)
}
return false;
})
addOnTabOpen('fileBinView', function () {
drawMonitorListToSelector(monitorsList)
drawFileBinViewElements()

View File

@ -34,7 +34,7 @@ $(document).ready(function(){
})
eventMatrixHtml += `</div>`
}
var baseHtml = `<main class="container page-tab tab-videoPlayer" id="tab-${newTabId}" video-id="${video.mid}${video.time}" data-time="${video.time}" data-mid="${video.mid}">
var baseHtml = `<main class="container page-tab tab-videoPlayer" id="tab-${newTabId}" video-id="${video.mid}${video.time}" data-time="${video.time}" data-mid="${video.mid}" data-ke="${video.ke}">
<div class="my-3 ${definitions.Theme.isDark ? 'bg-dark text-white' : 'bg-light text-dark'} rounded shadow-sm">
<div class="p-3">
<h6 class="video-title border-bottom-dotted border-bottom-dark pb-2 mb-0">${tabLabel}</h6>
@ -60,6 +60,7 @@ $(document).ready(function(){
<div class="btn-group btn-group-justified">
<a class="btn btn-sm btn-success" download href="${videoUrl}"><i class="fa fa-download"></i> ${lang.Download}</a>
${permissionCheck('video_delete',video.mid) ? `<a class="btn btn-sm btn-danger delete-video"><i class="fa fa-trash-o"></i> ${lang.Delete}</a>` : ''}
${permissionCheck('video_delete',video.mid) ? `<a class="btn btn-sm btn-${video.archive === 1 ? `success status-archived` : `default`} archive-video" title="${lang.Archive}"><i class="fa fa-${video.archive === 1 ? `lock` : `unlock-alt`}"></i> <span>${video.archive === 1 ? lang.Unarchive : lang.Archive}</span></a>` : ''}
</div>
</div>
</div>

View File

@ -441,6 +441,7 @@ function getVideos(options,callback){
var searchQuery = options.searchQuery
var requestQueries = []
var monitorId = options.monitorId
var archived = options.archived
var customVideoSet = options.customVideoSet
var limit = options.limit || 300
var eventStartTime
@ -458,6 +459,9 @@ function getVideos(options,callback){
if(searchQuery){
requestQueries.push(`search=${searchQuery}`)
}
if(archived){
requestQueries.push(`archived=1`)
}
$.getJSON(`${getApiPrefix(customVideoSet ? customVideoSet : searchQuery ? `videosByEventTag` : `videos`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(data){
var videos = data.videos
$.getJSON(`${getApiPrefix(`timelapse`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(timelapseFrames){
@ -548,6 +552,64 @@ async function compressVideos(videos){
await compressVideo(video)
}
}
function getArchiveButtons(video,isFileBin){
return $(`[data-mid="${video.mid}"][data-ke="${video.ke}"][data-time="${video.time}"] .archive-${isFileBin ? `file` : 'video'}`)
}
var currentlyArchiving = {}
function archiveVideo(video,unarchive,isFileBin){
return new Promise((resolve) => {
var videoEndpoint = getApiPrefix(isFileBin ? `fileBin` : `videos`) + '/' + video.mid + '/' + (isFileBin ? video.name : video.filename)
// var currentlyArchived = video.archive === 1
if(currentlyArchiving[videoEndpoint]){
resolve({ok: false})
return;
}
currentlyArchiving[videoEndpoint] = true
$.getJSON(videoEndpoint + '/archive' + `${unarchive ? `?unarchive=1` : ''}`,function(data){
if(data.ok){
var archiveButtons = getArchiveButtons(video,isFileBin)
var classToRemove = 'btn-default'
var classToAdd = 'btn-success status-archived'
var iconToRemove = 'fa-unlock-alt'
var iconToAdd = 'fa-lock'
var elTitle = `${lang.Unarchive}`
if(!data.archived){
console.log('Video Unarchived',unarchive)
classToRemove = 'btn-success status-archived'
classToAdd = 'btn-default'
iconToRemove = 'fa-lock'
iconToAdd = 'fa-unlock-alt'
elTitle = `${lang.Archive}`
}else{
console.log('Video Archived',unarchive)
}
archiveButtons.removeClass(classToRemove).addClass(classToAdd).attr('title',elTitle)
archiveButtons.find('i').removeClass(iconToRemove).addClass(iconToAdd)
archiveButtons.find('span').text(elTitle)
video.archive = data.archived ? 1 : 0
}else{
console.log('Video Archive status unchanged',data,videoEndpoint)
}
delete(currentlyArchiving[videoEndpoint])
resolve(data)
})
})
}
async function archiveVideos(videos){
for (let i = 0; i < videos.length; i++) {
var video = videos[i];
await archiveVideo(video)
}
}
function unarchiveVideo(video){
return archiveVideo(video,true)
}
async function unarchiveVideos(videos){
for (let i = 0; i < videos.length; i++) {
var video = videos[i];
await unarchiveVideo(video)
}
}
onWebSocketEvent(function(d){
switch(d.f){
case'video_delete':
@ -641,6 +703,33 @@ $(document).ready(function(){
});
return false;
})
.on('click','.archive-video',function(e){
e.preventDefault()
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var unarchive = $(this).hasClass('status-archived')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var ext = video.filename.split('.')
ext = ext[ext.length - 1]
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
if(unarchive){
unarchiveVideo(video)
}else{
// $.confirm.create({
// title: lang["Archive"] + ' : ' + video.filename,
// body: `${lang.ArchiveVideoMsg}<br><br><div class="row"><video class="video_video" autoplay loop controls><source src="${videoEndpoint}" type="video/${ext}"></video></div>`,
// clickOptions: {
// title: '<i class="fa fa-lock"></i> ' + lang.Archive,
// class: 'btn-primary btn-sm'
// },
// clickCallback: function(){
archiveVideo(video)
// }
// });
}
return false;
})
.on('click','.fix-video',function(e){
e.preventDefault()
var el = $(this).parents('[data-mid]')

View File

@ -32,8 +32,9 @@ $(document).ready(function(e){
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)
imgEl.innerHTML = href ? `<img class="pop-image cursor-pointer" src="${href}">` : ''
imgBlock.html(`<img class="pop-image cursor-pointer" src="${href}">`)
})
}
function openVideosTableView(monitorId,startDate,endDate){
@ -80,6 +81,7 @@ $(document).ready(function(e){
var startDate = dateRange.startDate
var endDate = dateRange.endDate
var monitorId = monitorsList.val()
var wantsArchivedVideo = getVideoSetSelected() === 'archive'
var frameIconsHtml = ''
if(!usePreloadedData){
loadedVideosTable = (await getVideos({
@ -87,6 +89,7 @@ $(document).ready(function(e){
startDate,
endDate,
searchQuery,
archived: wantsArchivedVideo,
customVideoSet: wantCloudVideos() ? 'cloudVideos' : null,
})).videos;
$.each(loadedVideosTable,function(n,v){
@ -140,7 +143,7 @@ $(document).ready(function(e){
title: lang['Objects Found']
},
{
field: 'ext',
field: 'tags',
title: ''
},
{
@ -156,21 +159,34 @@ $(document).ready(function(e){
var href = getFullOrigin(true) + file.href
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}"></div>`,
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">
</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: formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA'),
end: formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA'),
ext: file.ext,
tags: `
<span class="badge badge-${file.ext ==='webm' ? `primary` : 'danger'}">${file.ext}</span>
`,
objects: file.objects,
size: convertKbToHumanSize(file.size),
buttons: `
<div class="row-info" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-filename="${file.filename}">
<a class="btn btn-sm btn-primary" href="${href}" download title="${lang.Download}"><i class="fa fa-download"></i></a>
<a class="btn btn-sm btn-primary preview-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
<a class="btn btn-sm btn-default open-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
${permissionCheck('video_delete',file.mid) ? `<a class="btn btn-sm btn-danger delete-video" href="${href}" title="${lang.Delete}"><i class="fa fa-trash-o"></i></a>` : ''}
${permissionCheck('video_delete',file.mid) ? `<a class="btn btn-sm btn-warning compress-video" href="${href}" title="${lang.Compress}"><i class="fa fa-compress"></i></a>` : ''}
${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>
`,
}
@ -195,8 +211,11 @@ $(document).ready(function(e){
})
return rowsSelected
}
function getVideoSetSelected(){
return cloudVideoCheckSwitch.val()
}
function wantCloudVideos(){
const isChecked = cloudVideoCheckSwitch.val() === 'cloud'
const isChecked = getVideoSetSelected() === 'cloud'
return isChecked
}
function drawCompressedVideoProgressBar(data){
@ -255,6 +274,11 @@ $(document).ready(function(e){
drawPreviewVideo(href)
return false;
})
.on('click','.open-snapshot',function(e){
e.preventDefault()
var href = $(this).parents('.video-thumbnail-img-block').find('img').click()
return false;
})
.on('click','.delete-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
@ -287,7 +311,26 @@ $(document).ready(function(e){
},
clickCallback: function(){
compressVideos(videos).then(() => {
console.log(`Done Deleting Rows!`)
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!`)
})
}
});

View File

@ -21,8 +21,11 @@
<i class="fa fa-bars"></i>
</button>
<ul class="dropdown-menu <%- `${define.Theme.isDark ? 'dropdown-menu-dark bg-dark text-white' : 'bg-light text-dark'}` %> shadow-lg" aria-labelledby="monitorsListOptions">
<li><a class="dropdown-item compress-selected-videos cursor-pointer"><%- lang.Compress %></a></li>
<li><a class="dropdown-item download-selected-videos cursor-pointer"><%- lang.Download %></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item archive-selected-videos cursor-pointer"><%- lang.Archive %></a></li>
<li><a class="dropdown-item unarchive-selected-videos cursor-pointer"><%- lang.Unarchive %></a></li>
<li><a class="dropdown-item compress-selected-videos cursor-pointer"><%- lang.Compress %></a></li>
<!-- <li><a class="dropdown-item merge-selected-videos cursor-pointer"><%- lang.Merge %></a></li> -->
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item delete-selected-videos cursor-pointer"><%- lang.Delete %></a></li>