From 36943fa192127d074acff6457be7d935e7ea5778 Mon Sep 17 00:00:00 2001 From: Moe Date: Wed, 29 Jun 2022 18:06:15 -0700 Subject: [PATCH] Add Batch Download and Delete to Videos table --- languages/en_CA.json | 4 + web/assets/js/bs5.dashboard-base.js | 6 + web/assets/js/bs5.timelapseViewer.js | 5 +- web/assets/js/bs5.videos.js | 87 ++++++++----- web/assets/js/bs5.videosTable.js | 175 +++++++++++++++++++------- web/pages/blocks/home/videosTable.ejs | 16 +++ 6 files changed, 216 insertions(+), 77 deletions(-) diff --git a/languages/en_CA.json b/languages/en_CA.json index 2687c8fc..36698da5 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -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.", diff --git a/web/assets/js/bs5.dashboard-base.js b/web/assets/js/bs5.dashboard-base.js index f3860b06..f6c0aa9b 100644 --- a/web/assets/js/bs5.dashboard-base.js +++ b/web/assets/js/bs5.dashboard-base.js @@ -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' diff --git a/web/assets/js/bs5.timelapseViewer.js b/web/assets/js/bs5.timelapseViewer.js index 94d92074..25e652ea 100644 --- a/web/assets/js/bs5.timelapseViewer.js +++ b/web/assets/js/bs5.timelapseViewer.js @@ -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 diff --git a/web/assets/js/bs5.videos.js b/web/assets/js/bs5.videos.js index 0dfd5ebf..97cec906 100644 --- a/web/assets/js/bs5.videos.js +++ b/web/assets/js/bs5.videos.js @@ -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}

`, @@ -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; }) }) diff --git a/web/assets/js/bs5.videosTable.js b/web/assets/js/bs5.videosTable.js index 4db680c4..27d43717 100644 --- a/web/assets/js/bs5.videosTable.js +++ b/web/assets/js/bs5.videosTable.js @@ -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: `${loadedMonitors[file.mid]?.name || file.mid}`, - time: ` -
${lang.Started} ${formattedTime(file.start, 'DD-MM-YYYY hh:mm:ss AA')}
-
${lang.Ended} ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}
- `, - size: convertKbToHumanSize(file.size), - buttons: ` - - - `, - } - }) + 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: ` +
+ + + +
+ `, + } }) }) } function drawPreviewVideo(href){ videosTablePreviewArea.html(``) } + 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: ' ' + 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: ' ' + 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() diff --git a/web/pages/blocks/home/videosTable.ejs b/web/pages/blocks/home/videosTable.ejs index 2e8eb446..4a6b3513 100644 --- a/web/pages/blocks/home/videosTable.ejs +++ b/web/pages/blocks/home/videosTable.ejs @@ -12,5 +12,21 @@ drawBlock(theBlock) }) %> +
+