Update Cloud Uploaders

audio-only-output
Moe 2022-12-21 19:26:45 +00:00
parent f956271e5e
commit c548692754
18 changed files with 2800 additions and 461 deletions

View File

@ -441,6 +441,7 @@
"Endpoint Address": "Endpoint Address",
"Custom Endpoint": "Custom Endpoint",
"Bucket": "Bucket",
"Bucket ID": "Bucket ID",
"Region": "Region",
"Use Global Amazon S3 Video Storage": "Use Global Amazon S3 Video Storage",
"Use Global Wasabi Hot Cloud Storage Video Storage": "Use Global Wasabi Hot Cloud Storage Video Storage",

View File

@ -6,4 +6,11 @@ module.exports = async function(s,config){
await addColumn('Monitors',[
{name: 'tags', length: 500, type: 'string'},
])
await addColumn('Cloud Videos',[
{name: 'type', type: 'string', length: 15, defaultTo: 's3'},
{name: 'ext', type: 'string', length: 10, defaultTo: 'mp4'},
])
await addColumn('Cloud Timelapse Frames',[
{name: 'type', type: 'string', length: 15, defaultTo: 's3'},
])
}

View File

@ -95,12 +95,12 @@ module.exports = function(s,config,lang,app,io){
})
}
}
s.deleteTimelapseFrameFromCloud = function(e){
s.deleteTimelapseFrameFromCloud = function(e,cloudType){
// e = video object
s.checkDetails(e)
var frameSelector = {
ke: e.ke,
mid: e.id,
type: cloudType,
time: new Date(e.time),
}
s.knexQuery({
@ -118,7 +118,7 @@ module.exports = function(s,config,lang,app,io){
where: frameSelector,
limit: 1
},function(){
s.onDeleteTimelapseFrameFromCloudExtensionsRunner(e,r)
s.onDeleteTimelapseFrameFromCloudExtensionsRunner(e,details.type || r.type || 's3',r)
})
}else{
// console.log('Delete Failed',e)

View File

@ -1,6 +1,30 @@
var fs = require('fs');
// https://us-east-1.console.aws.amazon.com/iamv2/home#/users
const fs = require('fs');
const { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
module.exports = function(s,config,lang){
//Amazon S3
const genericRequest = async (groupKey,requestOptions) => {
const response = {ok: true}
try {
await s.group[groupKey].aws_s3.send(requestOptions);
} catch (err) {
console.error('AMZ genericRequest',groupKey,requestOptions)
response.ok = false
response.err = err
}
return response;
};
const deleteObject = async (groupKey,options) => {
return await genericRequest(groupKey,new DeleteObjectCommand(options))
};
const uploadObject = async (groupKey,options) => {
return await genericRequest(groupKey,new PutObjectCommand(options))
};
const getObject = async (groupKey,options) => {
// returns createReadStream
return await s.group[groupKey].aws_s3.send(new GetObjectCommand(options))
};
function beforeAccountSave(d){
//d = save event
d.formDetails.aws_use_global=d.d.aws_use_global
@ -29,7 +53,7 @@ module.exports = function(s,config,lang){
userDetails = Object.assign(userDetails,config.cloudUploaders.AmazonS3)
}
//Amazon S3
if(!s.group[e.ke].aws &&
if(
!s.group[e.ke].aws_s3 &&
userDetails.aws_s3 !== '0' &&
userDetails.aws_accessKeyId !== ''&&
@ -45,17 +69,16 @@ module.exports = function(s,config,lang){
if(userDetails.aws_s3_dir !== ''){
userDetails.aws_s3_dir = s.checkCorrectPathEnding(userDetails.aws_s3_dir)
}
s.group[e.ke].aws = new require("aws-sdk")
s.group[e.ke].aws.config = new s.group[e.ke].aws.Config({
accessKeyId: userDetails.aws_accessKeyId,
secretAccessKey: userDetails.aws_secretAccessKey,
s.group[e.ke].aws_s3 = new S3Client({
credentials: {
accessKeyId: userDetails.aws_accessKeyId,
secretAccessKey: userDetails.aws_secretAccessKey,
},
region: userDetails.aws_region
})
s.group[e.ke].aws_s3 = new s.group[e.ke].aws.S3();
});
}
}
function unloadGroupApp(user){
s.group[user.ke].aws = null
s.group[user.ke].aws_s3 = null
}
function deleteVideo(e,video,callback){
@ -68,55 +91,55 @@ module.exports = function(s,config,lang){
if(!videoDetails.location){
videoDetails.location = video.href.split('.amazonaws.com')[1]
}
if(videoDetails.type !== 's3'){
if(video.type !== 's3'){
callback()
return
}
try{
s.group[video.ke].aws_s3.deleteObject({
Bucket: s.group[video.ke].init.aws_s3_bucket,
Key: videoDetails.location,
}, function(err, data) {
if (err) console.log(err);
callback()
});
}catch(err){
console.log('Amazon S3 DELETE Error',err)
deleteObject(video.ke,{
Bucket: s.group[video.ke].init.aws_s3_bucket,
Key: videoDetails.location,
}).then((response) => {
if (response.err){
console.error('Amazon S3 DELETE Error')
console.error(err);
}
callback()
}
});
}
function uploadVideo(e,k){
function uploadVideo(e,k,insertQuery){
//e = video object
//k = temporary values
if(!k)k={};
//cloud saver - amazon s3
if(s.group[e.ke].aws_s3 && s.group[e.ke].init.use_aws_s3 !== '0' && s.group[e.ke].init.aws_s3_save === '1'){
var ext = k.filename.split('.')
ext = ext[ext.length - 1]
var fileStream = fs.createReadStream(k.dir+k.filename);
const groupKey = insertQuery.ke
if(s.group[groupKey].aws_s3 && s.group[groupKey].init.use_aws_s3 !== '0' && s.group[groupKey].init.aws_s3_save === '1'){
const filename = `${s.formattedTime(insertQuery.time)}.${insertQuery.ext}`
var fileStream = fs.createReadStream(k.dir+filename);
fileStream.on('error', function (err) {
console.error(err)
})
var saveLocation = s.group[e.ke].init.aws_s3_dir+e.ke+'/'+e.mid+'/'+k.filename
s.group[e.ke].aws_s3.upload({
Bucket: s.group[e.ke].init.aws_s3_bucket,
var saveLocation = s.group[groupKey].init.aws_s3_dir+groupKey+'/'+e.mid+'/'+filename
uploadObject(groupKey,{
Bucket: s.group[groupKey].init.aws_s3_bucket,
Key: saveLocation,
Body: fileStream,
ContentType: 'video/'+ext
},function(err,data){
if(err){
s.userLog(e,{type:lang['Amazon S3 Upload Error'],msg:err})
ContentType: 'video/'+e.ext
}).then((response) => {
if(response.err){
s.userLog(e,{type:lang['Amazon S3 Upload Error'],msg:response.err})
}
if(s.group[e.ke].init.aws_s3_log === '1' && data && data.Location){
if(s.group[groupKey].init.aws_s3_log === '1' && response.ok){
s.knexQuery({
action: "insert",
table: "Cloud Videos",
insert: {
mid: e.mid,
ke: e.ke,
time: k.startTime,
ke: groupKey,
ext: insertQuery.ext,
time: insertQuery.time,
status: 1,
type : 's3',
details: s.s({
type : 's3',
location : saveLocation
}),
size: k.filesize,
@ -124,13 +147,13 @@ module.exports = function(s,config,lang){
href: ''
}
})
s.setCloudDiskUsedForGroup(e.ke,{
s.setCloudDiskUsedForGroup(groupKey,{
amount: k.filesizeMB,
storageType: 's3'
})
s.purgeCloudDiskForGroup(e,'s3')
}
})
});
}
}
function onInsertTimelapseFrame(monitorObject,queryInfo,filePath){
@ -141,17 +164,16 @@ module.exports = function(s,config,lang){
console.error(err)
})
var saveLocation = s.group[e.ke].init.aws_s3_dir + e.ke + '/' + e.mid + '_timelapse/' + queryInfo.filename
s.group[e.ke].aws_s3.upload({
uploadObject(e.ke,{
Bucket: s.group[e.ke].init.aws_s3_bucket,
Key: saveLocation,
Body: fileStream,
ACL:'public-read',
ContentType:'image/jpeg'
},function(err,data){
if(err){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
}).then((response) => {
if(response.err){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:response.err})
}
if(s.group[e.ke].init.aws_s3_log === '1' && data && data.Location){
if(s.group[e.ke].init.aws_s3_log === '1' && response.ok){
s.knexQuery({
action: "insert",
table: "Cloud Timelapse Frames",
@ -160,12 +182,12 @@ module.exports = function(s,config,lang){
ke: queryInfo.ke,
time: queryInfo.time,
filename: queryInfo.filename,
type : 's3',
details: s.s({
type : 's3',
location : saveLocation
}),
size: queryInfo.size,
href: data.Location
href: ''
}
})
s.setCloudDiskUsedForGroup(e.ke,{
@ -184,30 +206,32 @@ module.exports = function(s,config,lang){
}catch(err){
var frameDetails = frame.details
}
if(frameDetails.type !== 's3'){
if(video.type !== 's3'){
callback()
return
}
if(!frameDetails.location){
frameDetails.location = frame.href.split(locationUrl)[1]
}
s.group[e.ke].aws_s3.deleteObject({
deleteObject(e.ke,{
Bucket: s.group[e.ke].init.aws_s3_bucket,
Key: frameDetails.location,
}, function(err, data) {
if (err) console.log(err);
}).then((response) => {
if (response.err){
console.error('Amazon S3 DELETE Error')
console.error(err);
}
callback()
});
}
function onGetVideoData(video){
async function onGetVideoData(video){
const videoDetails = s.parseJSON(video.details)
return new Promise((resolve, reject) => {
const saveLocation = videoDetails.location
var fileStream = s.group[video.ke].aws_s3.getObject({
Bucket: s.group[video.ke].init.aws_s3_bucket,
Key: saveLocation,
}).createReadStream();
resolve(fileStream)
})
const saveLocation = videoDetails.location
var fileStream = await getObject(video.ke,{
Bucket: s.group[video.ke].init.aws_s3_bucket,
Key: saveLocation,
});
return fileStream.Body
}
//amazon s3
s.addCloudUploader({
@ -219,8 +243,8 @@ module.exports = function(s,config,lang){
cloudDiskUseStartupExtensions: cloudDiskUseStartup,
beforeAccountSave: beforeAccountSave,
onAccountSave: cloudDiskUseStartup,
onInsertTimelapseFrame: onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud,
onInsertTimelapseFrame: (() => {}) || onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: (() => {}) || onDeleteTimelapseFrameFromCloud,
onGetVideoData
})
//return fields that will appear in settings

View File

@ -1,5 +1,6 @@
const fs = require('fs');
const { Readable } = require('stream');
const B2 = require('backblaze-b2')
module.exports = function(s,config,lang){
//Backblaze B2
var beforeAccountSaveForBackblazeB2 = function(d){
@ -37,7 +38,6 @@ module.exports = function(s,config,lang){
userDetails.bb_b2_bucket !== '' &&
userDetails.bb_b2_save === '1'
){
var B2 = require('backblaze-b2')
if(!userDetails.bb_b2_dir || userDetails.bb_b2_dir === '/'){
userDetails.bb_b2_dir = ''
}
@ -48,37 +48,21 @@ module.exports = function(s,config,lang){
// console.log(err)
s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.stack || err.data || err})
}
var createB2Connection = function(){
var b2 = new B2({
async function createB2Connection(){
const b2 = new B2({
accountId: userDetails.bb_b2_accountId,
applicationKey: userDetails.bb_b2_applicationKey
})
b2.authorize().then(function(resp){
s.group[e.ke].bb_b2_downloadUrl = resp.downloadUrl
b2.listBuckets().then(function(resp){
var buckets = resp.buckets
var bucketN = -2
if(!buckets){
s.userLog({mid:'$USER',ke:e.ke},{type: lang['Backblaze Error'],msg: lang['Not Authorized']})
return
}
buckets.forEach(function(item,n){
if(item.bucketName === userDetails.bb_b2_bucket){
bucketN = n
}
})
if(bucketN > -1){
s.group[e.ke].bb_b2_bucketId = buckets[bucketN].bucketId
}else{
b2.createBucket(
userDetails.bb_b2_bucket,
'allPublic'
).then(function(resp){
s.group[e.ke].bb_b2_bucketId = resp.bucketId
}).catch(backblazeErr)
}
}).catch(backblazeErr)
}).catch(backblazeErr)
});
const bucketName = userDetails.bb_b2_bucket
try{
const authResponse = await b2.authorize();
const getBucketResponse = await b2.getBucket({bucketName: bucketName})
const bucketId = getBucketResponse.data.buckets[0].bucketId
s.group[e.ke].bb_b2_bucketId = bucketId
}catch(err){
console.error('b2.authorize',err)
backblazeErr(err)
}
s.group[e.ke].bb_b2 = b2
}
createB2Connection()
@ -99,13 +83,19 @@ module.exports = function(s,config,lang){
}catch(err){
var videoDetails = video.details
}
if(video.type !== 'b2'){
callback()
return
}
s.group[e.ke].bb_b2.deleteFileVersion({
fileId: videoDetails.fileId,
fileName: videoDetails.fileName
}).then(function(resp){
// console.log('deleteFileVersion',resp)
callback()
}).catch(function(err){
console.log('deleteFileVersion',err)
callback()
})
}
var uploadVideoToBackblazeB2 = function(e,k){
@ -113,28 +103,31 @@ module.exports = function(s,config,lang){
//k = temporary values
if(!k)k={};
//cloud saver - Backblaze B2
if(s.group[e.ke].bb_b2 && s.group[e.ke].init.use_bb_b2 !== '0' && s.group[e.ke].init.bb_b2_save === '1'){
var backblazeErr = function(err){
// console.log(err)
s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.data})
const theGroup = s.group[e.ke]
if(theGroup.bb_b2 && theGroup.init.use_bb_b2 !== '0' && theGroup.init.bb_b2_save === '1'){
function backblazeErr(err){
s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err})
s.debugLog(err)
}
fs.readFile(k.dir+k.filename,function(err,data){
var backblazeSavePath = s.group[e.ke].init.bb_b2_dir+e.ke+'/'+e.mid+'/'+k.filename
var backblazeSavePath = theGroup.init.bb_b2_dir+e.ke+'/'+e.mid+'/'+k.filename
var getUploadUrl = function(bucketId,callback){
s.group[e.ke].bb_b2.getUploadUrl(bucketId).then(function(resp){
theGroup.bb_b2.getUploadUrl(bucketId).then(function(resp){
callback(resp)
}).catch(backblazeErr)
}
getUploadUrl(s.group[e.ke].bb_b2_bucketId,function(req){
s.group[e.ke].bb_b2.uploadFile({
uploadUrl: req.uploadUrl,
uploadAuthToken: req.authorizationToken,
getUploadUrl(theGroup.bb_b2_bucketId,function(req){
const uploadUrl = req.data.uploadUrl
const authorizationToken = req.data.authorizationToken
theGroup.bb_b2.uploadFile({
uploadUrl: uploadUrl,
uploadAuthToken: authorizationToken,
filename: backblazeSavePath,
data: data,
onUploadProgress: null
}).then(function(resp){
if(s.group[e.ke].init.bb_b2_log === '1' && resp.fileId){
var backblazeDownloadUrl = s.group[e.ke].bb_b2_downloadUrl + '/file/' + s.group[e.ke].init.bb_b2_bucket + '/' + backblazeSavePath
const uploadResponse = resp.data
if(theGroup.init.bb_b2_log === '1' && uploadResponse.fileId){
s.knexQuery({
action: "insert",
table: "Cloud Videos",
@ -143,11 +136,11 @@ module.exports = function(s,config,lang){
ke: e.ke,
time: k.startTime,
status: 1,
type : 'b2',
details: s.s({
type : 'b2',
bucketId : resp.bucketId,
fileId : resp.fileId,
fileName : resp.fileName
bucketId : uploadResponse.bucketId,
fileId : uploadResponse.fileId,
fileName : uploadResponse.fileName
}),
size: k.filesize,
end: k.endTime,
@ -183,6 +176,7 @@ module.exports = function(s,config,lang){
const fileStream = Readable.from(response.data);
resolve(fileStream)
}).catch((err) => {
s.debugLog(err)
reject(err)
});
})
@ -225,7 +219,7 @@ module.exports = function(s,config,lang){
},
{
"hidden": true,
"field": lang.Bucket,
"field": lang['Bucket ID'],
"name": "detail=bb_b2_bucket",
"placeholder": "Example : slippery-seal",
"form-group-class": "autosave_bb_b2_input autosave_bb_b2_1",

View File

@ -115,10 +115,12 @@ module.exports = (s,config,lang,app,io) => {
s.group[user.ke].googleDrive = null
s.group[user.ke].googleDriveOAuth2Client = null
}
var deleteVideoFromGoogleDrive = function(groupKey,video,callback){
var deleteVideoFromGoogleDrive = function(e,video,callback){
// e = user
const groupKey = e.ke
var videoDetails = s.parseJSON(video.details)
if(videoDetails.type !== 'googd'){
if(video.type !== 'googd'){
callback()
return
}
s.group[groupKey].googleDrive.files.delete({
@ -168,8 +170,8 @@ module.exports = (s,config,lang,app,io) => {
ke: e.ke,
time: k.startTime,
status: 1,
type: 'googd',
details: s.s({
type: 'googd',
id: data.id
}),
size: k.filesize,
@ -221,8 +223,8 @@ module.exports = (s,config,lang,app,io) => {
mid: queryInfo.mid,
ke: queryInfo.ke,
time: queryInfo.time,
type : 'googd',
details: s.s({
type : 'googd',
id : data.id,
}),
size: queryInfo.size,
@ -241,7 +243,7 @@ module.exports = (s,config,lang,app,io) => {
var onDeleteTimelapseFrameFromCloud = function(e,frame,callback){
// e = user
var frameDetails = s.parseJSON(frame.details)
if(frameDetails.type !== 'googd'){
if(frame.type !== 'googd'){
return
}
s.group[e.ke].googleDrive.files.delete({
@ -255,9 +257,6 @@ module.exports = (s,config,lang,app,io) => {
// e = user
var videoDetails = s.parseJSON(video.details)
const fileId = videoDetails.id
if(videoDetails.type !== 'googd'){
return
}
return new Promise((resolve, reject) => {
s.group[video.ke].googleDrive.files
.get({fileId, alt: 'media'}, {responseType: 'stream'})
@ -296,8 +295,8 @@ module.exports = (s,config,lang,app,io) => {
cloudDiskUseStartupExtensions: cloudDiskUseStartupForGoogleDrive,
beforeAccountSave: beforeAccountSaveForGoogleDrive,
onAccountSave: cloudDiskUseStartupForGoogleDrive,
onInsertTimelapseFrame: onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud,
onInsertTimelapseFrame: (() => {}) || onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: (() => {}) || onDeleteTimelapseFrameFromCloud,
onGetVideoData: onGetVideoData
})
return {

View File

@ -1,15 +1,37 @@
var fs = require('fs');
// https://us-east-1.console.aws.amazon.com/iamv2/home#/users
const fs = require('fs');
const { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
module.exports = function(s,config,lang){
//Wasabi Hot Cloud Storage
var beforeAccountSaveForWasabiHotCloudStorage = function(d){
const genericRequest = async (groupKey,requestOptions) => {
const response = {ok: true}
try {
await s.group[groupKey].whcs.send(requestOptions);
} catch (err) {
console.error('AMZ genericRequest',groupKey,requestOptions)
response.ok = false
response.err = err
}
return response;
};
const deleteObject = async (groupKey,options) => {
return await genericRequest(groupKey,new DeleteObjectCommand(options))
};
const uploadObject = async (groupKey,options) => {
return await genericRequest(groupKey,new PutObjectCommand(options))
};
const getObject = async (groupKey,options) => {
// returns createReadStream
return await s.group[groupKey].whcs.send(new GetObjectCommand(options))
};
function beforeAccountSave(d){
//d = save event
d.formDetails.whcs_use_global=d.d.whcs_use_global
d.formDetails.use_whcs=d.d.use_whcs
}
var cloudDiskUseStartupForWasabiHotCloudStorage = function(group,userDetails){
group.cloudDiskUse = group.cloudDiskUse || {}
group.cloudDiskUse['whcs'] = group.cloudDiskUse['whcs'] || {}
group.cloudDiskUse['whcs'].name = 'Wasabi Hot Cloud Storage'
function cloudDiskUseStartup(group,userDetails){
group.cloudDiskUse['whcs'].name = 'S3-Based Network Storage'
group.cloudDiskUse['whcs'].sizeLimitCheck = (userDetails.use_whcs_size_limit === '1')
if(!userDetails.whcs_size_limit || userDetails.whcs_size_limit === ''){
group.cloudDiskUse['whcs'].sizeLimit = 10000
@ -17,9 +39,9 @@ module.exports = function(s,config,lang){
group.cloudDiskUse['whcs'].sizeLimit = parseFloat(userDetails.whcs_size_limit)
}
}
var loadWasabiHotCloudStorageForUser = function(e){
function loadGroupApp(e){
// e = user
var userDetails = s.parseJSON(e.details)
var userDetails = JSON.parse(e.details)
if(userDetails.whcs_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WasabiHotCloudStorage){
// {
// whcs_accessKeyId: "",
@ -30,8 +52,9 @@ module.exports = function(s,config,lang){
// }
userDetails = Object.assign(userDetails,config.cloudUploaders.WasabiHotCloudStorage)
}
//Wasabi Hot Cloud Storage
if(!s.group[e.ke].whcs &&
//S3-Based Network Storage
if(
!s.group[e.ke].whcs &&
userDetails.whcs !== '0' &&
userDetails.whcs_accessKeyId !== ''&&
userDetails.whcs_secretAccessKey &&
@ -41,119 +64,98 @@ module.exports = function(s,config,lang){
if(!userDetails.whcs_dir || userDetails.whcs_dir === '/'){
userDetails.whcs_dir = ''
}
if(userDetails.whcs_dir !== ''){
if(userDetails.whcs_dir){
userDetails.whcs_dir = s.checkCorrectPathEnding(userDetails.whcs_dir)
}
if(userDetails.use_whcs_endpoint_select && userDetails.use_whcs_endpoint_select !== ''){
userDetails.whcs_endpoint = userDetails.use_whcs_endpoint_select
}
if(!userDetails.whcs_endpoint || userDetails.whcs_endpoint === ''){
if(!userDetails.whcs_endpoint ){
userDetails.whcs_endpoint = 's3.wasabisys.com'
}
var whcs_region = null
if(userDetails.whcs_region && userDetails.whcs_region !== ''){
whcs_region = userDetails.whcs_region
}
var endpointSplit = userDetails.whcs_endpoint.split('.')
if(endpointSplit.length > 2){
endpointSplit.shift()
}
var locationUrl = endpointSplit.join('.')
var AWS = new require("aws-sdk")
s.group[e.ke].whcs = AWS
var wasabiEndpoint = new AWS.Endpoint(userDetails.whcs_endpoint)
s.group[e.ke].whcs.config = new s.group[e.ke].whcs.Config({
endpoint: wasabiEndpoint,
accessKeyId: userDetails.whcs_accessKeyId,
secretAccessKey: userDetails.whcs_secretAccessKey,
region: whcs_region
})
s.group[e.ke].whcs = new s.group[e.ke].whcs.S3();
s.group[e.ke].whcs = new S3Client({
endpoint: userDetails.whcs_endpoint,
credentials: {
accessKeyId: userDetails.whcs_accessKeyId,
secretAccessKey: userDetails.whcs_secretAccessKey,
},
region: userDetails.whcs_region || null
});
}
}
var unloadWasabiHotCloudStorageForUser = function(user){
function unloadGroupApp(user){
s.group[user.ke].whcs = null
}
var deleteVideoFromWasabiHotCloudStorage = function(e,video,callback){
function deleteVideo(e,video,callback){
// e = user
try{
var videoDetails = JSON.parse(video.details)
}catch(err){
var videoDetails = video.details
}
if(!videoDetails.location){
videoDetails.location = video.href.split(locationUrl)[1]
}
if(videoDetails.type !== 'whcs'){
if(video.type !== 'whcs'){
callback()
return
}
s.group[e.ke].whcs.deleteObject({
Bucket: s.group[e.ke].init.whcs_bucket,
deleteObject(video.ke,{
Bucket: s.group[video.ke].init.whcs_bucket,
Key: videoDetails.location,
}, function(err, data) {
if (err) console.log(err);
}).then((response) => {
if (response.err){
console.error('S3-Based Network Storage DELETE Error')
console.error(err);
}
callback()
});
}
var uploadVideoToWasabiHotCloudStorage = function(e,k){
function uploadVideo(e,k,insertQuery){
//e = video object
//k = temporary values
if(!k)k={};
//cloud saver - Wasabi Hot Cloud Storage
if(s.group[e.ke].whcs && s.group[e.ke].init.use_whcs !== '0' && s.group[e.ke].init.whcs_save === '1'){
var ext = k.filename.split('.')
ext = ext[ext.length - 1]
var fileStream = fs.createReadStream(k.dir+k.filename);
//cloud saver - S3-Based Network Storage
const groupKey = insertQuery.ke
if(s.group[groupKey].whcs && s.group[groupKey].init.use_whcs !== '0' && s.group[groupKey].init.whcs_save === '1'){
const filename = `${s.formattedTime(insertQuery.time)}.${insertQuery.ext}`
var fileStream = fs.createReadStream(k.dir+filename);
fileStream.on('error', function (err) {
console.error(err)
})
var bucketName = s.group[e.ke].init.whcs_bucket
var saveLocation = s.group[e.ke].init.whcs_dir+e.ke+'/'+e.mid+'/'+k.filename
// gcp does not support multipart. Set queueSize to 1 and a big enough partSize
var options = s.group[e.ke].whcs.endpoint.href.includes("https://storage.googleapis.com") ? {
queueSize: 1,
partSize: 300 * 1024 * 1024
} : {}
s.group[e.ke].whcs.upload({
Bucket: bucketName,
var saveLocation = s.group[groupKey].init.whcs_dir+groupKey+'/'+e.mid+'/'+filename
uploadObject(groupKey,{
Bucket: s.group[groupKey].init.whcs_bucket,
Key: saveLocation,
Body: fileStream,
ContentType: 'video/'+ext
},options,function(err,data){
if(err){
console.error(err)
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
ContentType: 'video/'+e.ext
}).then((response) => {
if(response.err){
s.userLog(e,{type:lang['S3-Based Network Storage Upload Error'],msg:response.err})
}
if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){
var cloudLink = data.Location
cloudLink = fixCloudianUrl(e,cloudLink)
if(s.group[groupKey].init.whcs_log === '1' && response.ok){
s.knexQuery({
action: "insert",
table: "Cloud Videos",
insert: {
mid: e.mid,
ke: e.ke,
time: k.startTime,
ke: groupKey,
ext: insertQuery.ext,
time: insertQuery.time,
status: 1,
type : 'whcs',
details: s.s({
type : 'whcs',
location : saveLocation
}),
size: k.filesize,
end: k.endTime,
href: cloudLink
href: ''
}
})
s.setCloudDiskUsedForGroup(e.ke,{
amount : k.filesizeMB,
storageType : 'whcs'
s.setCloudDiskUsedForGroup(groupKey,{
amount: k.filesizeMB,
storageType: 'whcs'
})
s.purgeCloudDiskForGroup(e,'whcs')
}
})
});
}
}
var onInsertTimelapseFrame = function(monitorObject,queryInfo,filePath){
function onInsertTimelapseFrame(monitorObject,queryInfo,filePath){
var e = monitorObject
if(s.group[e.ke].whcs && s.group[e.ke].init.use_whcs !== '0' && s.group[e.ke].init.whcs_save === '1'){
var fileStream = fs.createReadStream(filePath)
@ -161,17 +163,16 @@ module.exports = function(s,config,lang){
console.error(err)
})
var saveLocation = s.group[e.ke].init.whcs_dir + e.ke + '/' + e.mid + '_timelapse/' + queryInfo.filename
s.group[e.ke].whcs.upload({
uploadObject(e.ke,{
Bucket: s.group[e.ke].init.whcs_bucket,
Key: saveLocation,
Body: fileStream,
ACL:'public-read',
ContentType:'image/jpeg'
},function(err,data){
if(err){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
}).then((response) => {
if(response.err){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:response.err})
}
if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){
if(s.group[e.ke].init.whcs_log === '1' && response.ok){
s.knexQuery({
action: "insert",
table: "Cloud Timelapse Frames",
@ -179,13 +180,13 @@ module.exports = function(s,config,lang){
mid: queryInfo.mid,
ke: queryInfo.ke,
time: queryInfo.time,
filename: queryInfo.filename,
filename: queryInfo.filename,
type : 'whcs',
details: s.s({
type : 'whcs',
location : saveLocation
}),
size: queryInfo.size,
href: data.Location
href: ''
}
})
s.setCloudDiskUsedForGroup(e.ke,{
@ -197,68 +198,55 @@ module.exports = function(s,config,lang){
})
}
}
var onDeleteTimelapseFrameFromCloud = function(e,frame,callback){
function onDeleteTimelapseFrameFromCloud(e,frame,callback){
// e = user
try{
var frameDetails = JSON.parse(frame.details)
}catch(err){
var frameDetails = frame.details
}
if(frameDetails.type !== 'whcs'){
if(video.type !== 'whcs'){
callback()
return
}
if(!frameDetails.location){
frameDetails.location = frame.href.split(locationUrl)[1]
}
s.group[e.ke].whcs.deleteObject({
deleteObject(e.ke,{
Bucket: s.group[e.ke].init.whcs_bucket,
Key: frameDetails.location,
}, function(err, data) {
if (err) console.log(err);
}).then((response) => {
if (response.err){
console.error('S3-Based Network Storage DELETE Error')
console.error(err);
}
callback()
});
}
var fixCloudianUrl = function(e,cloudLink){
if(cloudLink.indexOf('http') === -1){
var bucketName = s.group[e.ke].init.whcs_bucket
var endPointSplit = s.group[e.ke].init.whcs_endpoint.split('://')
endPoint = endPointSplit[1] || endPointSplit[0]
var protocol = `https`
if(endPointSplit[1])protocol = endPointSplit[0]
var cloudLinkPrefix = `${protocol}://${bucketName}.${endPoint}`
var truncatedLink = cloudLink.substring(0, bucketName.length + 3)
if(truncatedLink.indexOf(`${bucketName}/`) > -1){
cloudLink = cloudLink.replace(`${bucketName}/`,'')
}
cloudLink = s.checkCorrectPathEnding(cloudLinkPrefix) + cloudLink
}
return cloudLink
}
function onGetVideoData(video){
async function onGetVideoData(video){
const videoDetails = s.parseJSON(video.details)
return new Promise((resolve, reject) => {
const saveLocation = videoDetails.location
var fileStream = s.group[video.ke].whcs.getObject({
Bucket: s.group[video.ke].init.whcs_bucket,
Key: saveLocation,
}).createReadStream();
resolve(fileStream)
})
const saveLocation = videoDetails.location
var fileStream = await getObject(video.ke,{
Bucket: s.group[video.ke].init.whcs_bucket,
Key: saveLocation,
});
return fileStream.Body
}
//wasabi
//S3-Based Network Storage
s.addCloudUploader({
name: 'whcs',
loadGroupAppExtender: loadWasabiHotCloudStorageForUser,
unloadGroupAppExtender: unloadWasabiHotCloudStorageForUser,
insertCompletedVideoExtender: uploadVideoToWasabiHotCloudStorage,
deleteVideoFromCloudExtensions: deleteVideoFromWasabiHotCloudStorage,
cloudDiskUseStartupExtensions: cloudDiskUseStartupForWasabiHotCloudStorage,
beforeAccountSave: beforeAccountSaveForWasabiHotCloudStorage,
onAccountSave: cloudDiskUseStartupForWasabiHotCloudStorage,
onInsertTimelapseFrame: onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud,
loadGroupAppExtender: loadGroupApp,
unloadGroupAppExtender: unloadGroupApp,
insertCompletedVideoExtender: uploadVideo,
deleteVideoFromCloudExtensions: deleteVideo,
cloudDiskUseStartupExtensions: cloudDiskUseStartup,
beforeAccountSave: beforeAccountSave,
onAccountSave: cloudDiskUseStartup,
onInsertTimelapseFrame: (() => {}) || onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: (() => {}) || onDeleteTimelapseFrameFromCloud,
onGetVideoData
})
//return fields that will appear in settings
return {
"evaluation": "details.use_whcs !== '0'",
"name": lang["S3-Based Network Storage"],
@ -285,35 +273,11 @@ module.exports = function(s,config,lang){
},
{
"hidden": true,
"name": "detail=use_whcs_endpoint_select",
"selector":"h_whcs_endpoint",
"field": lang.Endpoint,
"description": "",
"default": "",
"example": "",
"fieldType": "select",
"possible": [
{
"name": "Custom Endpoint",
"value": ""
},
{
"name": lang['Wasabi Hot Cloud Storage'],
"value": "s3.wasabisys.com"
}
]
},
{
"hidden": true,
"field": lang['Endpoint Address'],
"name": "detail=whcs_endpoint",
"placeholder": "s3.wasabisys.com",
"form-group-class": "autosave_whcs_input autosave_whcs_1",
"form-group-class-pre-layer":"h_whcs_endpoint_input h_whcs_endpoint_",
"description": "",
"default": "",
"example": "",
"possible": ""
"field": lang['Endpoint Address'],
"name": "detail=whcs_endpoint",
"placeholder": "s3.wasabisys.com",
"form-group-class": "autosave_whcs_input autosave_whcs_1",
"form-group-class-pre-layer":"h_whcs_endpoint_input h_whcs_endpoint_"
},
{
"hidden": true,
@ -357,7 +321,7 @@ module.exports = function(s,config,lang){
"description": "",
"default": "",
"example": "",
"possible": [
"possible": [
{
"name": lang['No Region'],
"value": ""
@ -434,7 +398,7 @@ module.exports = function(s,config,lang){
"name": "South America 1",
"value": "sa-east-1"
}
]
]
},
{
"hidden": true,

View File

@ -57,6 +57,10 @@ module.exports = function(s,config,lang){
}catch(err){
var videoDetails = video.details
}
if(video.type !== 'webdav'){
callback()
return
}
if(!videoDetails.location){
var prefix = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[groupKey].init.webdav_url),s.group[groupKey].init.webdav_user,s.group[groupKey].init.webdav_pass)
videoDetails.location = video.href.replace(prefix,'')
@ -88,8 +92,8 @@ module.exports = function(s,config,lang){
ke: e.ke,
time: k.startTime,
status: 1,
type : 'webdav',
details: s.s({
type : 'webdav',
location : webdavUploadDir + k.filename
}),
size: k.filesize,
@ -172,8 +176,8 @@ module.exports = function(s,config,lang){
ke: queryInfo.ke,
time: queryInfo.time,
filename: queryInfo.filename,
type : 'webdav',
details: s.s({
type : 'webdav',
location : saveLocation
}),
size: queryInfo.size,
@ -195,7 +199,8 @@ module.exports = function(s,config,lang){
}catch(err){
var frameDetails = frame.details
}
if(frameDetails.type !== 'webdav'){
if(frame.type !== 'webdav'){
callback()
return
}
if(!frameDetails.location){
@ -223,8 +228,8 @@ module.exports = function(s,config,lang){
cloudDiskUseStartupExtensions: cloudDiskUseStartupForWebDav,
beforeAccountSave: beforeAccountSaveForWebDav,
onAccountSave: cloudDiskUseStartupForWebDav,
onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud,
onInsertTimelapseFrame: () => {},
onDeleteTimelapseFrameFromCloud: () => {},
onGetVideoData
})
return {

View File

@ -331,22 +331,18 @@ module.exports = function(s,config,lang){
}
if(s.deleteVideoFromCloudExtensions[storageType]){
s.deleteVideoFromCloudExtensions[storageType](e,video,function(){
s.tx({
s.tx(Object.assign({
f: 'video_delete_cloud',
mid: e.mid,
ke: e.ke,
time: e.time,
end: e.end
},'GRP_'+e.ke);
},video),'GRP_'+e.ke);
})
}
}
s.deleteVideoFromCloud = function(e){
s.deleteVideoFromCloud = function(e,cloudType){
// e = video object
s.checkDetails(e)
const whereQuery = {
ke: e.ke,
mid: e.mid,
type: cloudType,
time: new Date(e.time),
}
s.knexQuery({
@ -363,7 +359,7 @@ module.exports = function(s,config,lang){
table: "Cloud Videos",
where: whereQuery
},(err) => {
s.deleteVideoFromCloudExtensionsRunner(e,details.type || 's3',r)
s.deleteVideoFromCloudExtensionsRunner(e,details.type || r.type || 's3',r)
})
}else{
// console.log('Delete Failed',e)

View File

@ -1379,6 +1379,7 @@ module.exports = function(s,config,lang,app,io){
where: [
['ke','=',groupKey],
['mid','=',req.params.id],
['type','=', s.getPostData(req,'type') || 's3'],
['time','=',time]
],
limit: 1
@ -1386,7 +1387,7 @@ module.exports = function(s,config,lang,app,io){
if(r&&r[0]){
r = r[0]
const videoDetails = JSON.parse(r.details)
const storageType = videoDetails.type
const storageType = r.type || videoDetails.type
const onGetVideoData = s.cloudDiskUseOnGetVideoDataExtensions[storageType]
if(onGetVideoData){
onGetVideoData(r).then((dataPipe) => {
@ -1803,15 +1804,17 @@ module.exports = function(s,config,lang,app,io){
videoSet = 'Cloud Videos'
break;
}
const whereQuery = [
['ke','=',groupKey],
['mid','=',req.params.id],
['time','=',time]
]
if(videoParam === 'cloudVideos')whereQuery.push(['type','=',s.getPostData(req,'type') || 's3']);
s.knexQuery({
action: "select",
columns: "*",
table: videoSet,
where: [
['ke','=',groupKey],
['mid','=',req.params.id],
['time','=',time]
],
where: whereQuery,
limit: 1
},async (err,r) => {
if(r && r[0]){
@ -1884,7 +1887,7 @@ module.exports = function(s,config,lang,app,io){
response.ok = true;
switch(videoParam){
case'cloudVideos':
s.deleteVideoFromCloud(r,details.type || 's3')
s.deleteVideoFromCloud(r,details.type || r.type || 's3')
break;
default:
s.deleteVideo(r)

2585
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,8 @@
},
"homepage": "https://gitlab.com/Shinobi-Systems/Shinobi#readme",
"dependencies": {
"@aws-sdk/client-s3": "^3.226.0",
"async": "^3.2.2",
"aws-sdk": "^2.1030.0",
"backblaze-b2": "^0.9.12",
"body-parser": "^1.19.0",
"bson": "^4.6.1",

View File

@ -26,7 +26,7 @@ $(document).ready(function(e){
$.getJSON(apiURL + '?' + queryString.join('&'),function(data){
$.each(data.videos,function(n,v){
if(v.status !== 0){
loadedVideosInMemory[`${v.mid}${v.time}`] = Object.assign({},v)
loadedVideosInMemory[`${v.mid}${v.time}${v.type}`] = Object.assign({},v)
var loadedMonitor = loadedMonitors[v.mid]
if(loadedMonitor){
v.title = loadedMonitor.name+' - '+(parseInt(v.size)/1048576).toFixed(2)+'mb';
@ -53,7 +53,7 @@ $(document).ready(function(e){
eventLimit: true,
events: calendarData,
eventClick: function(v){
var video = loadedVideosInMemory[`${v.mid}${v.time}`]
var video = loadedVideosInMemory[`${v.mid}${v.time}${v.type}`]
var href = video.href
createVideoPlayerTab(Object.assign({},video,{href: href}))
$(this).css('border-color', 'red');

View File

@ -1067,7 +1067,7 @@ $(document).ready(function(e){
d.mid = d.id || d.mid
var monitorId = d.mid
var videoTime = d.time
loadedVideosInMemory[`${monitorId}${videoTime}`] = d
loadedVideosInMemory[`${monitorId}${videoTime}${d.type}`] = d
if(liveGridElements[monitorId] && liveGridElements[monitorId].streamElement)drawVideoCardToMiniList(monitorId,createVideoLinks(d),false)
break;
case'monitor_watch_off':case'monitor_stopping':

View File

@ -250,7 +250,7 @@ $(document).ready(function(){
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var video = loadedVideosInMemory[`${monitorId}${videoTime}${undefined}`]
openTab('studio')
loadVideoIntoSlicer(video)
return false;

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}" data-ke="${video.ke}">
var baseHtml = `<main class="container page-tab tab-videoPlayer" id="tab-${newTabId}" video-id="${video.mid}${video.time}${video.type}" data-time="${video.time}" data-mid="${video.mid}" data-ke="${video.ke}" data-type="${video.type}">
<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>

View File

@ -278,7 +278,7 @@ function createVideoRow(row,classOverride){
}
var videoEndpoint = getApiPrefix(`videos`) + '/' + row.mid + '/' + row.filename
return `
<div class="video-row ${classOverride ? classOverride : `col-md-12 col-lg-6 mb-3`} search-row" data-mid="${row.mid}" data-time="${row.time}" data-time-formed="${new Date(row.time)}">
<div class="video-row ${classOverride ? classOverride : `col-md-12 col-lg-6 mb-3`} search-row" data-mid="${row.mid}" data-time="${row.time}" data-type="${row.type}" data-time-formed="${new Date(row.time)}">
<div class="video-time-card shadow-lg px-0 btn-default">
<div class="card-header">
<div class="${definitions.Theme.isDark ? 'text-white' : ''}">
@ -441,7 +441,7 @@ function drawVideoRowsToList(targetElement,rows){
function loadVideosData(newVideos){
$.each(newVideos,function(n,video){
delete(video.f)
loadedVideosInMemory[`${video.mid}${video.time}`] = video
loadedVideosInMemory[`${video.mid}${video.time}${video.type}`] = video
})
}
function loadEventsData(videoEvents){
@ -486,7 +486,10 @@ function getVideos(options,callback){
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${limit}`]).join('&')}`,function(eventData){
var theEvents = eventData.events || eventData;
var newVideos = applyDataListToVideos(videos,theEvents)
newVideos = applyTimelapseFramesListToVideos(newVideos,timelapseFrames.frames || timelapseFrames,'timelapseFrames',true)
newVideos = applyTimelapseFramesListToVideos(newVideos,timelapseFrames.frames || timelapseFrames,'timelapseFrames',true).map((video) => {
video.videoSet = customVideoSet
return video
})
loadEventsData(theEvents)
loadVideosData(newVideos)
if(callback)callback({videos: newVideos, frames: timelapseFrames});
@ -634,16 +637,16 @@ async function unarchiveVideos(videos){
}
}
function buildDefaultVideoMenuItems(file,options){
var href = file.href
var isLocalVideo = !file.videoSet || file.videoSet === 'videos'
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`
options = options ? options : {play: true}
return `
<li><a class="dropdown-item" href="${href}" download>${lang.Download}</a></li>
${options.play ? `<li><a class="dropdown-item open-video" href="${href}">${lang.Play}</a></li>` : ``}
${options.play ? `<li><a class="dropdown-item mark-unread" href="${href}">${lang.Play}</a></li>` : ``}
<li><hr class="dropdown-divider"></li>
${permissionCheck('video_delete',file.mid) ? `<li><a class="dropdown-item open-video-studio" href="${href}">${lang.Slice}</a></li>` : ``}
${isLocalVideo && permissionCheck('video_delete',file.mid) ? `<li><a class="dropdown-item open-video-studio" href="${href}">${lang.Slice}</a></li>` : ``}
${permissionCheck('video_delete',file.mid) ? `<li><a class="dropdown-item delete-video" href="${href}">${lang.Delete}</a></li>` : ``}
${permissionCheck('video_delete',file.mid) ? `<li><a class="dropdown-item compress-video" href="${href}">${lang.Compress}</a></li>` : ``}
${isLocalVideo && permissionCheck('video_delete',file.mid) ? `<li><a class="dropdown-item compress-video" href="${href}">${lang.Compress}</a></li>` : ``}
`
}
function setVideoStatus(video,toStatus){
@ -659,10 +662,10 @@ function setVideoStatus(video,toStatus){
onWebSocketEvent(function(d){
switch(d.f){
case'video_edit':case'video_archive':
var video = loadedVideosInMemory[`${d.mid}${d.time}`]
var video = loadedVideosInMemory[`${d.mid}${d.time}${d.type}`]
if(video){
let filename = `${formattedTimeForFilename(convertTZ(d.time),false,`YYYY-MM-DDTHH-mm-ss`)}.${video.ext || 'mp4'}`
loadedVideosInMemory[`${d.mid}${d.time}`].status = d.status
loadedVideosInMemory[`${d.mid}${d.time}${d.type}`].status = d.status
$(`[data-mid="${d.mid}"][data-filename="${filename}"]`).attr('data-status',d.status);
}
break;
@ -685,7 +688,7 @@ $(document).ready(function(){
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var video = loadedVideosInMemory[`${monitorId}${videoTime}${undefined}`]
createVideoPlayerTab(video)
setVideoStatus(video)
return false;
@ -695,7 +698,7 @@ $(document).ready(function(){
var monitorId = el.attr('data-mid')
var videoTime = el.attr('video-time-seeked-video-position')
var timeInward = (parseInt(el.attr('video-slice-seeked')) / 1000) - 2
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var video = loadedVideosInMemory[`${monitorId}${videoTime}${undefined}`]
timeInward = timeInward < 0 ? 0 : timeInward
createVideoPlayerTab(video,timeInward)
})
@ -704,23 +707,27 @@ $(document).ready(function(){
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var type = el.attr('data-type')
var video = loadedVideosInMemory[`${monitorId}${videoTime}${type}`]
var videoSet = video.videoSet
var ext = video.filename.split('.')
ext = ext[ext.length - 1]
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
var isCloudVideo = videoSet === 'cloudVideos'
var videoEndpoint = getApiPrefix(videoSet || 'videos') + '/' + video.mid + '/' + video.filename
var endpointType = isCloudVideo ? `?type=${video.type}` : ''
$.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>`,
body: `${lang.DeleteVideoMsg}<br><br><div class="row"><video class="video_video" autoplay loop controls><source src="${videoEndpoint}${endpointType}" type="video/${ext}"></video></div>`,
clickOptions: {
title: '<i class="fa fa-trash-o"></i> ' + lang.Delete,
class: 'btn-danger btn-sm'
},
clickCallback: function(){
$.getJSON(videoEndpoint + '/delete',function(data){
$.getJSON(videoEndpoint + '/delete' + endpointType,function(data){
if(data.ok){
console.log('Video Deleted')
}else{
console.log('Video Not Deleted',data,videoEndpoint)
console.log('Video Not Deleted',data,videoEndpoint + endpointType)
}
})
}
@ -732,7 +739,7 @@ $(document).ready(function(){
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var video = loadedVideosInMemory[`${monitorId}${videoTime}${undefined}`]
var ext = video.filename.split('.')
ext = ext[ext.length - 1]
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
@ -755,7 +762,7 @@ $(document).ready(function(){
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var unarchive = $(this).hasClass('status-archived')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var video = loadedVideosInMemory[`${monitorId}${videoTime}${undefined}`]
var ext = video.filename.split('.')
ext = ext[ext.length - 1]
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename
@ -781,7 +788,7 @@ $(document).ready(function(){
var el = $(this).parents('[data-mid]')
var monitorId = el.attr('data-mid')
var videoTime = el.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var video = loadedVideosInMemory[`${monitorId}${videoTime}${undefined}`]
var ext = video.filename.split('.')
ext = ext[ext.length - 1]
var videoEndpoint = getApiPrefix(`videos`) + '/' + video.mid + '/' + video.filename

View File

@ -61,6 +61,7 @@ $(document).ready(function(e){
var endDate = dateRange.endDate
var monitorId = monitorsList.val()
var wantsArchivedVideo = getVideoSetSelected() === 'archive'
var wantCloudVideo = wantCloudVideos()
var frameIconsHtml = ''
if(!usePreloadedData){
loadedVideosTable = (await getVideos({
@ -69,10 +70,10 @@ $(document).ready(function(e){
endDate,
searchQuery,
archived: wantsArchivedVideo,
customVideoSet: wantCloudVideos() ? 'cloudVideos' : null,
customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
})).videos;
$.each(loadedVideosTable,function(n,v){
loadedVideosInMemory[`${monitorId}${v.time}`]
loadedVideosInMemory[`${monitorId}${v.time}${v.type}`]
})
}
// for (let i = 0; i < loadedVideosTable.length; i++) {
@ -131,7 +132,8 @@ $(document).ready(function(e){
}
],
data: loadedVideosTable.map((file) => {
var href = file.href
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}">
@ -154,12 +156,13 @@ $(document).ready(function(e){
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}">
<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>
${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>` : ''}
${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>
@ -185,7 +188,8 @@ $(document).ready(function(e){
var groupKey = rowInfo.attr('data-ke')
var time = rowInfo.attr('data-time')
var filename = rowInfo.attr('data-filename')
rowsSelected.push(getLoadedRows ? loadedVideosInMemory[`${monitorId}${time}`] : {
var type = rowInfo.attr('data-type')
rowsSelected.push(getLoadedRows ? loadedVideosInMemory[`${monitorId}${time}${type}`] : {
mid: monitorId,
ke: groupKey,
time: time,
@ -257,7 +261,8 @@ $(document).ready(function(e){
var rowEl = $(this).parents('[data-mid]')
var monitorId = rowEl.attr('data-mid')
var videoTime = rowEl.attr('data-time')
var video = loadedVideosInMemory[`${monitorId}${videoTime}`]
var type = rowEl.attr('data-type')
var video = loadedVideosInMemory[`${monitorId}${videoTime}${type}`]
var href = el.attr('href')
setPreviewedVideoHighlight(el,videosTableDrawArea)
drawPreviewVideo(href)
@ -374,11 +379,12 @@ $(document).ready(function(e){
onWebSocketEvent((data) => {
switch(data.f){
case'video_delete':
case'video_delete_cloud':
if(tabTree.name === 'videosTableView'){
var videoIndexToRemove = loadedVideosTable.findIndex(row => data.mid === row.mid && new Date(row.time).getTime() === new Date(data.time).getTime())
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}`])
delete(loadedVideosInMemory[`${data.mid}${data.time}${data.type}`])
clearTimeout(redrawTimeout)
redrawTimeout = setTimeout(function(){
drawVideosTableViewElements(true)