Add Batch Download and Delete to Videos table
parent
2680e0a9ea
commit
36943fa192
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue