Add Batch Download and Delete to Videos table

timelapse-frames-through-websocket
Moe 2022-06-29 18:06:15 -07:00
parent 2680e0a9ea
commit 36943fa192
6 changed files with 216 additions and 77 deletions

View File

@ -254,6 +254,9 @@
"Can Edit Monitor": "Can Edit Monitor",
"Can Delete Videos": "Can Delete Videos",
"Delete Video": "Delete Video",
"Delete Videos": "Delete Videos",
"Batch Download": "Batch Download",
"batchDownloadText": "Do you want to download the selected files?",
"Delete Timelapse Frame": "Delete Timelapse Frame",
"Can View Videos and Events": "Can View Videos and Events",
"Can Delete Videos and Events": "Can Delete Videos and Events",
@ -483,6 +486,7 @@
"FixVideoMsg": "Do you want to fix this video? You cannot undo this action.",
"DeleteVideoMsg": "Do you want to delete this video? You cannot recover it.",
"DeleteThisMsg": "Do you want to delete this? You cannot recover it.",
"DeleteTheseMsg": "Do you want to delete these? You cannot recover them.",
"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

@ -812,6 +812,12 @@ function downloadJSON(jsonData,filename){
.attr('download',filename)
[0].click()
}
function downloadFile(downloadUrl,fileName){
var a = document.createElement('a')
a.href = downloadUrl
a.download = fileName
a.click()
}
function convertKbToHumanSize(theNumber){
var amount = theNumber / 1048576
var unit = amount / 1000 >= 1000 ? 'TB' : amount >= 1000 ? 'GB' : 'MB'

View File

@ -254,10 +254,7 @@ $(document).ready(function(e){
}
function downloadTimelapseVideo(data){
var downloadUrl = buildFileBinUrl(data)
var a = document.createElement('a')
a.href = downloadUrl
a.download = data.name
a.click()
downloadFile(downloadUrl,data.name)
}
function onTimelapseVideoBuildComplete(data){
var saveBuiltVideo = dashboardOptions().switches.timelapseSaveBuiltVideo

View File

@ -398,32 +398,35 @@ function loadVideoData(video){
loadedVideosInMemory[`${video.mid}${video.time}`] = video
}
function getVideos(options,callback){
options = options ? options : {}
var requestQueries = []
var monitorId = options.monitorId
var limit = options.limit || 300
var eventStartTime
var eventEndTime
// var startDate = options.startDate
// var endDate = options.endDate
if(options.startDate){
eventStartTime = formattedTimeForFilename(options.startDate,false)
requestQueries.push(`start=${eventStartTime}`)
}
if(options.endDate){
eventEndTime = formattedTimeForFilename(options.endDate,false)
requestQueries.push(`end=${eventEndTime}`)
}
$.getJSON(`${getApiPrefix(`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){
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${limit}`]).join('&')}`,function(eventData){
var newVideos = applyDataListToVideos(videos,eventData)
newVideos = applyTimelapseFramesListToVideos(newVideos,timelapseFrames,'timelapseFrames',true)
$.each(newVideos,function(n,video){
loadVideoData(video)
return new Promise((resolve,reject) => {
options = options ? options : {}
var requestQueries = []
var monitorId = options.monitorId
var limit = options.limit || 300
var eventStartTime
var eventEndTime
// var startDate = options.startDate
// var endDate = options.endDate
if(options.startDate){
eventStartTime = formattedTimeForFilename(options.startDate,false)
requestQueries.push(`start=${eventStartTime}`)
}
if(options.endDate){
eventEndTime = formattedTimeForFilename(options.endDate,false)
requestQueries.push(`end=${eventEndTime}`)
}
$.getJSON(`${getApiPrefix(`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){
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${limit}`]).join('&')}`,function(eventData){
var newVideos = applyDataListToVideos(videos,eventData)
newVideos = applyTimelapseFramesListToVideos(newVideos,timelapseFrames,'timelapseFrames',true)
$.each(newVideos,function(n,video){
loadVideoData(video)
})
if(callback)callback({videos: newVideos, frames: timelapseFrames});
resolve({videos: newVideos, frames: timelapseFrames})
})
callback({videos: newVideos, frames: timelapseFrames})
})
})
})
@ -452,6 +455,32 @@ function getEvents(options,callback){
callback(eventData)
})
}
function deleteVideo(video,callback){
return new Promise((resolve,reject) => {
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
console.log(videoEndpoint)
$.getJSON(videoEndpoint + '/delete',function(data){
if(callback)callback(data)
resolve(data)
})
})
}
async function deleteVideos(videos){
for (let i = 0; i < videos.length; i++) {
var video = videos[i];
await deleteVideo(video)
}
}
function downloadVideo(video){
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
downloadFile(videoEndpoint,video.filename)
}
async function downloadVideos(videos){
for (let i = 0; i < videos.length; i++) {
var video = videos[i];
await downloadVideo(video)
}
}
onWebSocketEvent(function(d){
switch(d.f){
case'video_delete':
@ -490,14 +519,14 @@ $(document).ready(function(){
timeInward = timeInward < 0 ? 0 : timeInward
createVideoPlayerTab(video,timeInward)
})
.on('click','.delete-video',function(){
.on('click','.delete-video',function(e){
e.preventDefault()
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var ext = video.filename.split('.')
ext = ext[ext.length - 1]
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
$.confirm.create({
title: lang["Delete Video"] + ' : ' + video.filename,
body: `${lang.DeleteVideoMsg}<br><br><div class="row"><video class="video_video" autoplay loop controls><source src="${videoEndpoint}" type="video/${ext}"></video></div>`,
@ -506,6 +535,7 @@ $(document).ready(function(){
class: 'btn-danger btn-sm'
},
clickCallback: function(){
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
$.getJSON(videoEndpoint + '/delete',function(data){
if(data.ok){
console.log('Video Deleted')
@ -514,6 +544,7 @@ $(document).ready(function(){
}
})
}
})
});
return false;
})
})

View File

@ -4,7 +4,8 @@ $(document).ready(function(e){
var dateSelector = theEnclosure.find('.date_selector')
var videosTableDrawArea = $('#videosTable_draw_area')
var videosTablePreviewArea = $('#videosTable_preview_area')
var loadedVideosInMemory = {};
var loadedVideosTable = [];
var redrawTimeout;
function openVideosTableView(monitorId,startDate,endDate){
drawVideosTableViewElements(monitorId,startDate,endDate)
}
@ -37,59 +38,91 @@ $(document).ready(function(e){
monitorsList.change(function(){
drawVideosTableViewElements()
})
function drawVideosTableViewElements(selectedMonitor,startDate,endDate){
async function drawVideosTableViewElements(usePreloadedData){
var dateRange = getSelectedTime(false)
if(!startDate)startDate = dateRange.startDate
if(!endDate)endDate = dateRange.endDate
if(!selectedMonitor)selectedMonitor = monitorsList.val()
var startDate = dateRange.startDate
var endDate = dateRange.endDate
var monitorId = monitorsList.val()
var queryString = ['start=' + startDate,'end=' + endDate,'limit=0']
var frameIconsHtml = ''
var apiURL = getApiPrefix('videos') + '/' + selectedMonitor;
var videosTableData = []
loadedVideosInMemory = {}
$.getJSON(apiURL + '?' + queryString.join('&'),function(data){
videosTableDrawArea.bootstrapTable('destroy')
videosTableDrawArea.bootstrapTable({
pagination: true,
search: true,
columns: [
{
field: 'monitorName',
title: lang['Monitor']
},
{
field: 'time',
title: lang['Time Created']
},
{
field: 'size',
title: ''
},
{
field: 'buttons',
title: ''
}
],
data: data.videos.map((file) => {
return {
monitorName: `<b>${loadedMonitors[file.mid]?.name || file.mid}</b>`,
time: `
<div><b>${lang.Started}</b> ${formattedTime(file.start, 'DD-MM-YYYY hh:mm:ss AA')}</div>
<div><b>${lang.Ended}</b> ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}</div>
`,
size: convertKbToHumanSize(file.size),
buttons: `
<a class="btn btn-sm btn-primary" href="${file.href}" download title="${lang.Download}"><i class="fa fa-download"></i></a>
<a class="btn btn-sm btn-primary preview-video" href="${file.href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
`,
}
})
var apiURL = getApiPrefix('videos') + '/' + monitorId;
if(!usePreloadedData){
loadedVideosTable = (await getVideos({
monitorId,
startDate,
endDate,
})).videos;
$.each(loadedVideosTable,function(n,v){
loadedVideosInMemory[`${monitorId}${v.time}`]
})
}
videosTableDrawArea.bootstrapTable('destroy')
videosTableDrawArea.bootstrapTable({
pagination: true,
search: true,
columns: [
{
field: 'mid',
title: '',
checkbox: true,
formatter: () => {
return {
checked: false
}
},
},
{
field: 'time',
title: lang['Time Created'],
},
{
field: 'end',
title: lang['Ended']
},
{
field: 'size',
title: ''
},
{
field: 'buttons',
title: ''
}
],
data: loadedVideosTable.map((file) => {
return {
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'),
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="${file.href}" download title="${lang.Download}"><i class="fa fa-download"></i></a>
<a class="btn btn-sm btn-primary preview-video" href="${file.href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
<a class="btn btn-sm btn-danger delete-video" href="${file.href}" title="${lang.Delete}"><i class="fa fa-trash-o"></i></a>
</div>
`,
}
})
})
}
function drawPreviewVideo(href){
videosTablePreviewArea.html(`<video class="video_video" style="width:100%" autoplay controls preload loop src="${href}"></video>`)
}
function getSelectedRows(){
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 filename = rowInfo.attr('data-filename')
rowsSelected.push({
mid: monitorId,
ke: groupKey,
filename: filename,
})
})
return rowsSelected
}
$('body')
.on('click','.open-videosTable',function(e){
e.preventDefault()
@ -108,6 +141,58 @@ $(document).ready(function(e){
drawPreviewVideo(href)
return false;
})
.on('click','.delete-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
$.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','.download-selected-videos',function(e){
e.preventDefault()
var videos = getSelectedRows()
console.log(videos)
$.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':
if(tabTree.name === 'videosTableView' && monitorsList.val() === data.mid){
var videoIndexToRemove = loadedVideosTable.findIndex(row => new Date(row.time).getTime() === new Date(data.time).getTime())
if(videoIndexToRemove !== -1){
loadedVideosTable.splice(videoIndexToRemove, 1);
delete(loadedVideosInMemory[`${data.mid}${data.time}`])
clearTimeout(redrawTimeout)
redrawTimeout = setTimeout(function(){
drawVideosTableViewElements(true)
},2000)
}
}
break;
}
})
addOnTabOpen('videosTableView', function () {
drawMonitorListToSelector(monitorsList)
drawVideosTableViewElements()

View File

@ -12,5 +12,21 @@
drawBlock(theBlock)
}) %>
</div>
<div style="margin-bottom: 50px;"></div>
<h4 class="sticky-bar p-3 d-flex flex-row">
<div>
<div class="dropdown">
<button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="monitorsListOptions" data-bs-toggle="dropdown" aria-expanded="false">
<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 download-selected-videos cursor-pointer"><%- lang.Download %></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>
</ul>
</div>
</div>
</h4>
<script src="<%-window.libURL%>assets/js/bs5.videosTable.js"></script>
</main>